Translate member access expressions

This change maps MuJS access expressions to the MuPack/MuIL forms.

As part of this, I've changed the representation in the AST.  It doesn't
make much sense to distinguish between variable, member, and function access.
Instead, all accesses will be uniform, and will evaluate to the proper type.
This commit is contained in:
joeduffy 2017-01-10 16:39:50 -08:00
parent 82657c7cdb
commit 7db6f12d6f
4 changed files with 59 additions and 34 deletions

View file

@ -1,6 +1,6 @@
// Copyright 2016 Marapongo, Inc. All rights reserved.
import {Node} from "./nodes";
import {Identifier, Node} from "./nodes";
import * as statements from "./statements";
import * as symbols from "../symbols";
@ -67,30 +67,21 @@ export type ObjectLiteralInitializerKind = "ObjectLiteralInitializer";
// TODO(joe): figure out how to load/store elements and maps. Possibly just use intrinsic functions.
// Loads a variable's address (module, argument, local, or property), producing a pointer that can be dereferenced.
export interface LoadVariableExpression extends Expression {
kind: LoadVariableExpressionKind;
variable: symbols.VariableToken; // the variable to load from.
object?: Expression; // the `this` object, in the case of class properties.
// Loads a location's address, producing a pointer that can be dereferenced.
export interface LoadLocationExpression extends Expression {
kind: LoadLocationExpressionKind;
object?: Expression; // the `this` object, in the case of class properties.
name: Identifier; // the name of the member to load.
}
export const loadVariableExpressionKind = "LoadVariableExpression";
export type LoadVariableExpressionKind = "LoadVariableExpression";
// Loads a function's address, producing a pointer that can be dereferenced to produce an invocable expression.
export interface LoadFunctionExpression extends Expression {
kind: LoadFunctionExpressionKind;
function: symbols.FunctionToken; // the function to load as a lambda.
object?: Expression; // the `this` object, in the case of class methods.
}
export const loadFunctionExpressionKind = "LoadFunctionExpression";
export type LoadFunctionExpressionKind = "LoadFunctionExpression";
export const loadLocationExpressionKind = "LoadLocationExpression";
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 {
kind: LoadDynamicExpressionKind;
key: Expression; // the name of the property to load (a string expression).
object: Expression; // the object to load a property from.
name: Expression; // the name of the property to load.
}
export const loadDynamicExpressionKind = "LoadDynamicExpression";
export type LoadDynamicExpressionKind = "LoadDynamicExpression";

View file

@ -49,8 +49,7 @@ export type NodeKind =
expressions.StringLiteralKind |
expressions.ObjectLiteralKind |
expressions.ObjectLiteralInitializerKind |
expressions.LoadVariableExpressionKind |
expressions.LoadFunctionExpressionKind |
expressions.LoadLocationExpressionKind |
expressions.LoadDynamicExpressionKind |
expressions.InvokeFunctionExpressionKind |
expressions.LambdaExpressionKind |

View file

@ -618,9 +618,9 @@ export class Transpiler {
contract.requires(!!variable.initializer, "variable", "Expected variable to have an initializer");
return this.copyLocation(variable.node, {
kind: ast.binaryOperatorExpressionKind,
left: <ast.LoadVariableExpression>{
kind: ast.loadVariableExpressionKind,
variable: variable.local.name.ident,
left: <ast.LoadLocationExpression>{
kind: ast.loadLocationExpressionKind,
name: variable.local.name,
},
operator: "=",
right: variable.initializer,
@ -1084,7 +1084,26 @@ export class Transpiler {
}
private transformElementAccessExpression(node: ts.ElementAccessExpression): ast.Expression {
return contract.fail("NYI");
let object: ast.Expression = this.transformExpression(node.expression);
if (node.argumentExpression) {
switch (node.argumentExpression.kind) {
case ts.SyntaxKind.Identifier:
return this.copyLocation(node, {
kind: ast.loadLocationExpressionKind,
object: object,
key: this.transformIdentifier(<ts.Identifier>node.argumentExpression),
});
default:
return this.copyLocation(node, {
kind: ast.loadDynamicExpressionKind,
object: object,
key: this.transformExpression(<ts.Expression>node.argumentExpression),
});
}
}
else {
return object;
}
}
private transformFunctionExpression(node: ts.FunctionExpression): ast.Expression {
@ -1151,8 +1170,14 @@ export class Transpiler {
return contract.fail("NYI");
}
private transformSuperExpression(node: ts.SuperExpression): ast.Expression {
return contract.fail("NYI");
private transformSuperExpression(node: ts.SuperExpression): ast.LoadLocationExpression {
return {
kind: ast.loadLocationExpressionKind,
name: {
kind: ast.identifierKind,
ident: symbols.specialVariableSuper,
},
};
}
private transformTaggedTemplateExpression(node: ts.TaggedTemplateExpression): ast.Expression {
@ -1163,8 +1188,14 @@ export class Transpiler {
return contract.fail("NYI");
}
private transformThisExpression(node: ts.ThisExpression): ast.Expression {
return contract.fail("NYI");
private transformThisExpression(node: ts.ThisExpression): ast.LoadLocationExpression {
return {
kind: ast.loadLocationExpressionKind,
name: {
kind: ast.identifierKind,
ident: symbols.specialVariableThis,
},
};
}
private transformTypeOfExpression(node: ts.TypeOfExpression): ast.Expression {

View file

@ -12,12 +12,16 @@ export type Accessibility = "public" | "private"; // accessibi
export type ClassMemberAccessibility = Accessibility | "protected"; // accessibility modifiers for class members.
// Accessibility modifier constants.
export const publicAccessibility = "public";
export const privateAccessibility = "private";
export const protectedAccessibility = "protected";
export const publicAccessibility: Accessibility = "public";
export const privateAccessibility: Accessibility = "private";
export const protectedAccessibility: ClassMemberAccessibility = "protected";
// Special variable tokens.
export const specialVariableThis: VariableToken = ".this"; // the current object (for class methods).
export const specialVariableSuper: VariableToken = ".super"; // the parent class object (for class methods).
// Special function tokens.
export const specialFunctionEntryPoint = ".main"; // the special package entrypoint function.
export const specialFunctionInitializer = ".init"; // the special module/class initialize function.
export const specialFunctionConstructor = ".ctor"; // the special class instance constructor function.
export const specialFunctionEntryPoint: FunctionToken = ".main"; // the special package entrypoint function.
export const specialFunctionInitializer: FunctionToken = ".init"; // the special module/class initialize function.
export const specialFunctionConstructor: FunctionToken = ".ctor"; // the special class instance constructor function.