Skip to content

Commit

Permalink
Merge pull request #108 from bloomberg/isolated-declarations-evaluator
Browse files Browse the repository at this point in the history
Isolated declarations evaluator
  • Loading branch information
dragomirtitian authored Nov 14, 2023
2 parents 9dc09ca + fca6fb1 commit 35ccc67
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 200 deletions.
96 changes: 8 additions & 88 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import {
createDiagnosticForNodeFromMessageChain,
createDiagnosticMessageChainFromDiagnostic,
createEmptyExports,
createEvaluator,
createFileDiagnostic,
createGetCanonicalFileName,
createGetSymbolWalker,
Expand Down Expand Up @@ -1467,6 +1468,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
var checkBinaryExpression = createCheckBinaryExpression();
var emitResolver = createResolver();
var nodeBuilder = createNodeBuilder();
var evaluate = createEvaluator({
evaluateElementAccessExpression,
evaluateEntityNameExpression,
onNumericLiteral: checkGrammarNumericLiteral,
});

var globals = createSymbolTable();
var undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String);
Expand Down Expand Up @@ -38437,7 +38443,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isConstContext(node) || isTemplateLiteralContext(node) || someType(getContextualType(node, /*contextFlags*/ undefined) || unknownType, isTemplateLiteralContextualType)) {
return getTemplateLiteralType(texts, types);
}
const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluateTemplateExpression(node);
const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluate(node);
return evaluated ? getFreshTypeOfLiteralType(getStringLiteralType(evaluated)) : stringType;
}

Expand Down Expand Up @@ -44829,79 +44835,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return value;
}

function evaluate(expr: Expression, location?: Declaration): string | number | undefined {
switch (expr.kind) {
case SyntaxKind.PrefixUnaryExpression:
const value = evaluate((expr as PrefixUnaryExpression).operand, location);
if (typeof value === "number") {
switch ((expr as PrefixUnaryExpression).operator) {
case SyntaxKind.PlusToken:
return value;
case SyntaxKind.MinusToken:
return -value;
case SyntaxKind.TildeToken:
return ~value;
}
}
break;
case SyntaxKind.BinaryExpression:
const left = evaluate((expr as BinaryExpression).left, location);
const right = evaluate((expr as BinaryExpression).right, location);
if (typeof left === "number" && typeof right === "number") {
switch ((expr as BinaryExpression).operatorToken.kind) {
case SyntaxKind.BarToken:
return left | right;
case SyntaxKind.AmpersandToken:
return left & right;
case SyntaxKind.GreaterThanGreaterThanToken:
return left >> right;
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
return left >>> right;
case SyntaxKind.LessThanLessThanToken:
return left << right;
case SyntaxKind.CaretToken:
return left ^ right;
case SyntaxKind.AsteriskToken:
return left * right;
case SyntaxKind.SlashToken:
return left / right;
case SyntaxKind.PlusToken:
return left + right;
case SyntaxKind.MinusToken:
return left - right;
case SyntaxKind.PercentToken:
return left % right;
case SyntaxKind.AsteriskAsteriskToken:
return left ** right;
}
}
else if (
(typeof left === "string" || typeof left === "number") &&
(typeof right === "string" || typeof right === "number") &&
(expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken
) {
return "" + left + right;
}
break;
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return (expr as StringLiteralLike).text;
case SyntaxKind.TemplateExpression:
return evaluateTemplateExpression(expr as TemplateExpression, location);
case SyntaxKind.NumericLiteral:
checkGrammarNumericLiteral(expr as NumericLiteral);
return +(expr as NumericLiteral).text;
case SyntaxKind.ParenthesizedExpression:
return evaluate((expr as ParenthesizedExpression).expression, location);
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
return evaluateEntityNameExpression(expr as EntityNameExpression, location);
case SyntaxKind.ElementAccessExpression:
return evaluateElementAccessExpression(expr as ElementAccessExpression, location);
}
return undefined;
}

function evaluateEntityNameExpression(expr: EntityNameExpression, location?: Declaration) {
if (
isIdentifier(expr) && isInfinityOrNaNString(expr.escapedText) &&
Expand All @@ -44923,7 +44856,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
return getEnumMemberValue(enumMember);
}
if (compilerOptions.isolatedDeclarations && isConstantVariable(symbol)) {
if (!compilerOptions.isolatedDeclarations && isConstantVariable(symbol)) {
const declaration = symbol.valueDeclaration;
if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) {
return evaluate(declaration.initializer, declaration);
Expand Down Expand Up @@ -44967,19 +44900,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return getEnumMemberValue(declaration as EnumMember);
}

function evaluateTemplateExpression(expr: TemplateExpression, location?: Declaration) {
let result = expr.head.text;
for (const span of expr.templateSpans) {
const value = evaluate(span.expression, location);
if (value === undefined) {
return undefined;
}
result += value;
result += span.literal.text;
}
return result;
}

function checkEnumDeclaration(node: EnumDeclaration) {
addLazyDiagnostic(() => checkEnumDeclarationWorker(node));
}
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/transformers/declarations/emit-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AnyImportSyntax,
appendIfUnique,
ComputedPropertyName,
createEvaluator,
Debug,
Declaration,
DeclarationName,
Expand Down Expand Up @@ -90,9 +91,6 @@ import {
EmitDeclarationNodeLinks,
EmitDeclarationSymbol,
} from "./emit-binder";
import {
makeEvaluator,
} from "./evaluator";
import {
IsolatedEmitHost,
IsolatedEmitResolver,
Expand Down Expand Up @@ -125,7 +123,7 @@ export function createEmitDeclarationResolver(file: SourceFile, host: IsolatedEm
}
return undefined;
}
const evaluate = makeEvaluator({
const evaluate = createEvaluator({
evaluateElementAccessExpression(expr, location) {
// We only resolve names in the current enum declaration
if (!location || !isEnumDeclaration(location)) return undefined;
Expand Down
108 changes: 0 additions & 108 deletions src/compiler/transformers/declarations/evaluator.ts

This file was deleted.

101 changes: 101 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ import {
SyntaxKind,
SyntaxList,
TaggedTemplateExpression,
TemplateExpression,
TemplateLiteral,
TemplateLiteralLikeNode,
TemplateLiteralToken,
Expand Down Expand Up @@ -10620,3 +10621,103 @@ export function determineIfDeclarationIsVisible(node: Node, isDeclarationVisible
return false;
}
}

/** @internal */
export interface EvaluationResolver {
evaluateEntityNameExpression(expr: EntityNameExpression, location: Declaration | undefined): string | number | undefined;
evaluateElementAccessExpression(expr: ElementAccessExpression, location: Declaration | undefined): string | number | undefined;
onNumericLiteral(expr: NumericLiteral): void;
}

/** @internal */
export function createEvaluator({ evaluateElementAccessExpression, evaluateEntityNameExpression, onNumericLiteral }: EvaluationResolver) {
function evaluate(expr: TemplateExpression, location?: Declaration): string;
function evaluate(expr: Expression, location?: Declaration): string | number | undefined;
function evaluate(expr: Expression, location?: Declaration): string | number | undefined {
switch (expr.kind) {
case SyntaxKind.PrefixUnaryExpression:
const value = evaluate((expr as PrefixUnaryExpression).operand, location);
if (typeof value === "number") {
switch ((expr as PrefixUnaryExpression).operator) {
case SyntaxKind.PlusToken:
return value;
case SyntaxKind.MinusToken:
return -value;
case SyntaxKind.TildeToken:
return ~value;
}
}
break;
case SyntaxKind.BinaryExpression:
const left = evaluate((expr as BinaryExpression).left, location);
const right = evaluate((expr as BinaryExpression).right, location);
if (typeof left === "number" && typeof right === "number") {
switch ((expr as BinaryExpression).operatorToken.kind) {
case SyntaxKind.BarToken:
return left | right;
case SyntaxKind.AmpersandToken:
return left & right;
case SyntaxKind.GreaterThanGreaterThanToken:
return left >> right;
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
return left >>> right;
case SyntaxKind.LessThanLessThanToken:
return left << right;
case SyntaxKind.CaretToken:
return left ^ right;
case SyntaxKind.AsteriskToken:
return left * right;
case SyntaxKind.SlashToken:
return left / right;
case SyntaxKind.PlusToken:
return left + right;
case SyntaxKind.MinusToken:
return left - right;
case SyntaxKind.PercentToken:
return left % right;
case SyntaxKind.AsteriskAsteriskToken:
return left ** right;
}
}
else if (
(typeof left === "string" || typeof left === "number") &&
(typeof right === "string" || typeof right === "number") &&
(expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken
) {
return "" + left + right;
}
break;
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return (expr as StringLiteralLike).text;
case SyntaxKind.TemplateExpression:
return evaluateTemplateExpression(expr as TemplateExpression, location);
case SyntaxKind.NumericLiteral:
onNumericLiteral(expr as NumericLiteral);
return +(expr as NumericLiteral).text;
case SyntaxKind.ParenthesizedExpression:
return evaluate((expr as ParenthesizedExpression).expression, location);
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
return evaluateEntityNameExpression(expr as EntityNameExpression, location);
case SyntaxKind.ElementAccessExpression:
return evaluateElementAccessExpression(expr as ElementAccessExpression, location);
}
return undefined;
}

function evaluateTemplateExpression(expr: TemplateExpression, location?: Declaration) {
let result = expr.head.text;
for (const span of expr.templateSpans) {
const value = evaluate(span.expression, location);
if (value === undefined) {
return undefined;
}
result += value;
result += span.literal.text;
}
return result;
}

return evaluate;
}
Loading

0 comments on commit 35ccc67

Please sign in to comment.