diff --git a/kitcom/internal/tsgo/evaluator/evaluator.go b/kitcom/internal/tsgo/evaluator/evaluator.go deleted file mode 100644 index 5a625e1..0000000 --- a/kitcom/internal/tsgo/evaluator/evaluator.go +++ /dev/null @@ -1,170 +0,0 @@ -package evaluator - -import ( - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" -) - -type Result struct { - Value any - IsSyntacticallyString bool - ResolvedOtherFiles bool - HasExternalReferences bool -} - -func NewResult(value any, isSyntacticallyString bool, resolvedOtherFiles bool, hasExternalReferences bool) Result { - return Result{value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} -} - -type Evaluator func(expr *ast.Node, location *ast.Node) Result - -func NewEvaluator(evaluateEntity Evaluator, outerExpressionsToSkip ast.OuterExpressionKinds) Evaluator { - var evaluate Evaluator - evaluate = func(expr *ast.Node, location *ast.Node) Result { - isSyntacticallyString := false - resolvedOtherFiles := false - hasExternalReferences := false - // It's unclear when/whether we should consider skipping other kinds of outer expressions. - // Type assertions intentionally break evaluation when evaluating literal types, such as: - // type T = `one ${"two" as any} three`; // string - // But it's less clear whether such an assertion should break enum member evaluation: - // enum E { - // A = "one" as any - // } - // SatisfiesExpressions and non-null assertions seem to have even less reason to break - // emitting enum members as literals. However, these expressions also break Babel's - // evaluation (but not esbuild's), and the isolatedModules errors we give depend on - // our evaluation results, so we're currently being conservative so as to issue errors - // on code that might break Babel. - expr = ast.SkipOuterExpressions(expr, outerExpressionsToSkip|ast.OEKParentheses) - switch expr.Kind { - case ast.KindPrefixUnaryExpression: - result := evaluate(expr.AsPrefixUnaryExpression().Operand, location) - resolvedOtherFiles = result.ResolvedOtherFiles - hasExternalReferences = result.HasExternalReferences - if value, ok := result.Value.(jsnum.Number); ok { - switch expr.AsPrefixUnaryExpression().Operator { - case ast.KindPlusToken: - return Result{value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindMinusToken: - return Result{-value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindTildeToken: - return Result{value.BitwiseNOT(), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - } - } - case ast.KindBinaryExpression: - left := evaluate(expr.AsBinaryExpression().Left, location) - right := evaluate(expr.AsBinaryExpression().Right, location) - operator := expr.AsBinaryExpression().OperatorToken.Kind - isSyntacticallyString = (left.IsSyntacticallyString || right.IsSyntacticallyString) && expr.AsBinaryExpression().OperatorToken.Kind == ast.KindPlusToken - resolvedOtherFiles = left.ResolvedOtherFiles || right.ResolvedOtherFiles - hasExternalReferences = left.HasExternalReferences || right.HasExternalReferences - leftNum, leftIsNum := left.Value.(jsnum.Number) - rightNum, rightIsNum := right.Value.(jsnum.Number) - if leftIsNum && rightIsNum { - switch operator { - case ast.KindBarToken: - return Result{leftNum.BitwiseOR(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindAmpersandToken: - return Result{leftNum.BitwiseAND(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindGreaterThanGreaterThanToken: - return Result{leftNum.SignedRightShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindGreaterThanGreaterThanGreaterThanToken: - return Result{leftNum.UnsignedRightShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindLessThanLessThanToken: - return Result{leftNum.LeftShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindCaretToken: - return Result{leftNum.BitwiseXOR(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindAsteriskToken: - return Result{leftNum * rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindSlashToken: - return Result{leftNum / rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindPlusToken: - return Result{leftNum + rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindMinusToken: - return Result{leftNum - rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindPercentToken: - return Result{leftNum.Remainder(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - case ast.KindAsteriskAsteriskToken: - return Result{leftNum.Exponentiate(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - } - } - leftStr, leftIsStr := left.Value.(string) - rightStr, rightIsStr := right.Value.(string) - if (leftIsStr || leftIsNum) && (rightIsStr || rightIsNum) && operator == ast.KindPlusToken { - if leftIsNum { - leftStr = leftNum.String() - } - if rightIsNum { - rightStr = rightNum.String() - } - return Result{leftStr + rightStr, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - } - case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral: - return Result{expr.Text(), true /*isSyntacticallyString*/, false, false} - case ast.KindTemplateExpression: - return evaluateTemplateExpression(expr, location, evaluate) - case ast.KindNumericLiteral: - return Result{jsnum.FromString(expr.Text()), false, false, false} - case ast.KindIdentifier: - return evaluateEntity(expr, location) - case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression: - if ast.IsEntityNameExpression(expr.Expression()) { - return evaluateEntity(expr, location) - } - } - return Result{nil, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences} - } - return evaluate -} - -func evaluateTemplateExpression(expr *ast.Node, location *ast.Node, evaluate Evaluator) Result { - var sb strings.Builder - sb.WriteString(expr.AsTemplateExpression().Head.Text()) - resolvedOtherFiles := false - hasExternalReferences := false - for _, span := range expr.AsTemplateExpression().TemplateSpans.Nodes { - spanResult := evaluate(span.Expression(), location) - if spanResult.Value == nil { - return Result{nil, true /*isSyntacticallyString*/, false, false} - } - sb.WriteString(AnyToString(spanResult.Value)) - sb.WriteString(span.AsTemplateSpan().Literal.Text()) - resolvedOtherFiles = resolvedOtherFiles || spanResult.ResolvedOtherFiles - hasExternalReferences = hasExternalReferences || spanResult.HasExternalReferences - } - return Result{sb.String(), true, resolvedOtherFiles, hasExternalReferences} -} - -func AnyToString(v any) string { - // !!! This function should behave identically to the expression `"" + v` in JS - switch v := v.(type) { - case string: - return v - case jsnum.Number: - return v.String() - case bool: - return core.IfElse(v, "true", "false") - case jsnum.PseudoBigInt: - return v.String() - } - panic("Unhandled case in AnyToString") -} - -func IsTruthy(v any) bool { - // !!! This function should behave identically to the expression `!!v` in JS - switch v := v.(type) { - case string: - return len(v) != 0 - case jsnum.Number: - return v != 0 && !v.IsNaN() - case bool: - return v - case jsnum.PseudoBigInt: - return v != jsnum.PseudoBigInt{} - } - panic("Unhandled case in IsTruthy") -}