From c964a0fa8dcc0c7a1196ce9fc5d26e92ed0d1905 Mon Sep 17 00:00:00 2001 From: joeduffy Date: Wed, 11 Jan 2017 07:43:00 -0800 Subject: [PATCH] Use explicit type coercions This uses explicit type coercions when creating interface types with object literals. This helps the compiler catch some additional errors, such as mistyped property names (of which we had two instances, for load location expressions ("key" vs "name")). --- tools/mujs/lib/ast/expressions.ts | 7 +++- tools/mujs/lib/compiler/transform.ts | 62 ++++++++++++++-------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/tools/mujs/lib/ast/expressions.ts b/tools/mujs/lib/ast/expressions.ts index 62dda9ee1..7456a6fa6 100644 --- a/tools/mujs/lib/ast/expressions.ts +++ b/tools/mujs/lib/ast/expressions.ts @@ -67,8 +67,11 @@ export type ObjectLiteralInitializerKind = "ObjectLiteralInitializer"; // TODO(joe): figure out how to load/store elements and maps. Possibly just use intrinsic functions. +export interface LoadExpression extends Expression { +} + // Loads a location's address, producing a pointer that can be dereferenced. -export interface LoadLocationExpression extends Expression { +export interface LoadLocationExpression extends LoadExpression { kind: LoadLocationExpressionKind; object?: Expression; // the `this` object, in the case of class properties. name: Identifier; // the name of the member to load. @@ -78,7 +81,7 @@ export type LoadLocationExpressionKind = "LoadLocationExpression"; // Dynamically loads either a variable or function, by name, from an object. // TODO(joe): I'm unsure if we should permit assigning to functions by name; I think we'll need to for Python/Ruby/etc. -export interface LoadDynamicExpression extends Expression { +export interface LoadDynamicExpression extends LoadExpression { kind: LoadDynamicExpressionKind; object: Expression; // the object to load a property from. name: Expression; // the name of the property to load. diff --git a/tools/mujs/lib/compiler/transform.ts b/tools/mujs/lib/compiler/transform.ts index 2c821771d..0689c2359 100644 --- a/tools/mujs/lib/compiler/transform.ts +++ b/tools/mujs/lib/compiler/transform.ts @@ -268,7 +268,7 @@ export class Transpiler { moduleName = moduleName.substring(0, moduleExtIndex); } - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.moduleKind, name: ident(moduleName), members: members, @@ -440,7 +440,7 @@ export class Transpiler { if (statement.kind === ast.blockKind) { return statement; } - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.blockKind, statements: [ statement ], }); @@ -518,7 +518,7 @@ export class Transpiler { } let mods: ts.ModifierFlags = ts.getCombinedModifierFlags(node); - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.classKind, name: name, access: access, @@ -861,7 +861,7 @@ export class Transpiler { /** Control flow statements **/ private transformBreakStatement(node: ts.BreakStatement): ast.BreakStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.breakStatementKind, label: object.maybeUndefined(node.label, this.transformIdentifier), }); @@ -876,7 +876,7 @@ export class Transpiler { } private transformContinueStatement(node: ts.ContinueStatement): ast.ContinueStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.continueStatementKind, label: object.maybeUndefined(node.label, this.transformIdentifier), }); @@ -903,7 +903,7 @@ export class Transpiler { ], }); - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.whileStatementKind, test: { kind: ast.boolLiteralKind, @@ -930,7 +930,7 @@ export class Transpiler { } private transformIfStatement(node: ts.IfStatement): ast.IfStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.ifStatementKind, condition: this.transformExpression(node.expression), consequent: this.transformStatement(node.thenStatement), @@ -939,7 +939,7 @@ export class Transpiler { } private transformReturnStatement(node: ts.ReturnStatement): ast.ReturnStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.returnStatementKind, expression: object.maybeUndefined(node.expression, this.transformExpression), }); @@ -950,7 +950,7 @@ export class Transpiler { } private transformThrowStatement(node: ts.ThrowStatement): ast.ThrowStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.throwStatementKind, expression: this.transformExpression(node.expression), }); @@ -960,8 +960,8 @@ export class Transpiler { return contract.fail("NYI"); } - private transformWhileStatement(node: ts.WhileStatement): ast.Statement { - return this.copyLocation(node, { + private transformWhileStatement(node: ts.WhileStatement): ast.WhileStatement { + return this.copyLocation(node, { kind: ast.whileStatementKind, test: this.transformExpression(node.expression), body: this.transformStatementAsBlock(node.statement), @@ -972,7 +972,7 @@ export class Transpiler { private transformBlock(node: ts.Block): ast.Block { // TODO(joe): map directives. - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.blockKind, statements: node.statements.map((stmt: ts.Statement) => this.transformStatement(stmt)), }); @@ -981,26 +981,26 @@ export class Transpiler { private transformDebuggerStatement(node: ts.DebuggerStatement): ast.Statement { // The debugger statement in ECMAScript can be used to trip a breakpoint. We don't have the equivalent in Mu at // the moment, so we simply produce an empty statement in its place. - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.emptyStatementKind, }); } private transformEmptyStatement(node: ts.EmptyStatement): ast.EmptyStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.emptyStatementKind, }); } private transformExpressionStatement(node: ts.ExpressionStatement): ast.ExpressionStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.expressionStatementKind, expression: this.transformExpression(node.expression), }); } private transformLabeledStatement(node: ts.LabeledStatement): ast.LabeledStatement { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.labeledStatementKind, label: this.transformIdentifier(node.label), statement: this.transformStatement(node.statement), @@ -1118,7 +1118,7 @@ export class Transpiler { // TODO: finish binary operator mapping; for any left that are unsupported, introduce a real error message. return contract.fail(`Unsupported binary operator: ${ts.SyntaxKind[node.operatorToken.kind]}`); } - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.binaryOperatorExpressionKind, operator: operator, left: this.transformExpression(node.left), @@ -1137,7 +1137,7 @@ export class Transpiler { curr = binary.left; } expressions.unshift(this.transformExpression(curr)); - return { + return { kind: ast.sequenceExpressionKind, expressions: expressions, }; @@ -1152,7 +1152,7 @@ export class Transpiler { } private transformConditionalExpression(node: ts.ConditionalExpression): ast.ConditionalExpression { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.conditionalExpressionKind, condition: this.transformExpression(node.condition), consequent: this.transformExpression(node.whenTrue), @@ -1164,21 +1164,21 @@ export class Transpiler { return contract.fail("NYI"); } - private transformElementAccessExpression(node: ts.ElementAccessExpression): ast.Expression { + private transformElementAccessExpression(node: ts.ElementAccessExpression): ast.LoadExpression { let object: ast.Expression = this.transformExpression(node.expression); if (node.argumentExpression) { switch (node.argumentExpression.kind) { case ts.SyntaxKind.Identifier: - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.loadLocationExpressionKind, object: object, - key: this.transformIdentifier(node.argumentExpression), + name: this.transformIdentifier(node.argumentExpression), }); default: - return this.copyLocation(node, { - kind: ast.loadDynamicExpressionKind, + return this.copyLocation(node, { + kind: ast.loadDynamicExpressionKind, object: object, - key: this.transformExpression(node.argumentExpression), + name: this.transformExpression(node.argumentExpression), }); } } @@ -1212,7 +1212,7 @@ export class Transpiler { private transformPostfixUnaryExpression(node: ts.PostfixUnaryExpression): ast.UnaryOperatorExpression { let operator: ast.UnaryOperator | undefined = postfixUnaryOperators.get(node.operator); contract.assert(!!(operator = operator!)); - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.unaryOperatorExpressionKind, postfix: true, operator: operator, @@ -1223,7 +1223,7 @@ export class Transpiler { private transformPrefixUnaryExpression(node: ts.PrefixUnaryExpression): ast.UnaryOperatorExpression { let operator: ast.UnaryOperator | undefined = prefixUnaryOperators.get(node.operator); contract.assert(!!(operator = operator!)); - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.unaryOperatorExpressionKind, postfix: false, operator: operator, @@ -1293,7 +1293,7 @@ export class Transpiler { private transformBooleanLiteral(node: ts.BooleanLiteral): ast.BoolLiteral { contract.assert(node.kind === ts.SyntaxKind.FalseKeyword || node.kind === ts.SyntaxKind.TrueKeyword); - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.boolLiteralKind, raw: node.getText(), value: (node.kind === ts.SyntaxKind.TrueKeyword), @@ -1305,14 +1305,14 @@ export class Transpiler { } private transformNullLiteral(node: ts.NullLiteral): ast.NullLiteral { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.nullLiteralKind, raw: node.getText(), }); } private transformNumericLiteral(node: ts.NumericLiteral): ast.NumberLiteral { - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.numberLiteralKind, raw: node.text, value: Number(node.text), @@ -1327,7 +1327,7 @@ export class Transpiler { // TODO: we need to dynamically populate the resulting object with ECMAScript-style string functions. It's not // yet clear how to do this in a way that facilitates inter-language interoperability. This is especially // challenging because most use of such functions will be entirely dynamic. - return this.copyLocation(node, { + return this.copyLocation(node, { kind: ast.stringLiteralKind, raw: node.text, value: node.text,