Map new expressions

This change adds a new AST node kind to represent "new" expressions.
Originally I had thought we could simply represent these as calls to
a constructor, however, there is a sizeable semantic difference and so
it's better for this node kind to be distinct; for example, we will
most likely permit allocation of objects without explicit constructors.

In addition, I've added a "complex" scenario test, which is just a
Point class.  It has a constructor, two properties, and an instance
method that performs arithmetic and allocates a new instance.  It works!
This commit is contained in:
joeduffy 2017-01-11 08:06:11 -08:00
parent 00e0c00ac9
commit 2d5ec61aeb
8 changed files with 739 additions and 6 deletions

View file

@ -91,12 +91,23 @@ export type LoadDynamicExpressionKind = "LoadDynamicExpression";
/** Functions **/
// Invokes a function.
export interface InvokeFunctionExpression extends Expression {
kind: InvokeFunctionExpressionKind;
function: Expression; // a function to invoke (of a func type).
export interface CallExpression extends Expression {
arguments?: Expression[]; // the list of arguments in sequential order.
}
// Allocates a new object and calls its constructor.
export interface NewExpression extends CallExpression {
kind: NewExpressionKind;
type: Identifier; // the object type to allocate.
}
export const newExpressionKind = "NewExpression";
export type NewExpressionKind = "NewExpression";
// Invokes a function.
export interface InvokeFunctionExpression extends CallExpression {
kind: InvokeFunctionExpressionKind;
function: Expression; // a function to invoke (of a func type).
}
export const invokeFunctionExpressionKind = "InvokeFunctionExpression";
export type InvokeFunctionExpressionKind = "InvokeFunctionExpression";

View file

@ -51,6 +51,7 @@ export type NodeKind =
expressions.ObjectLiteralInitializerKind |
expressions.LoadLocationExpressionKind |
expressions.LoadDynamicExpressionKind |
expressions.NewExpressionKind |
expressions.InvokeFunctionExpressionKind |
expressions.LambdaExpressionKind |
expressions.UnaryOperatorExpressionKind |

View file

@ -1241,8 +1241,21 @@ export class Transpiler {
});
}
private transformNewExpression(node: ts.NewExpression): ast.Expression {
return contract.fail("NYI");
private transformNewExpression(node: ts.NewExpression): ast.NewExpression {
// Only "new T(..)" constructors, where T is an identifier referring to a type, are permitted.
let ty: ast.Identifier;
if (node.expression.kind === ts.SyntaxKind.Identifier) {
ty = this.transformIdentifier(<ts.Identifier>node.expression);
}
else {
return contract.fail("New T(..) expression must have an identifier T");
}
return <ast.NewExpression>{
kind: ast.newExpressionKind,
type: ty,
arguments: node.arguments.map((expr: ts.Expression) => this.transformExpression(expr)),
};
}
private transformOmittedExpression(node: ts.OmittedExpression): ast.Expression {

View file

@ -20,6 +20,9 @@ let testCases: string[] = [
"modules/func_exp_def_1",
"modules/class_1",
"modules/class_exp_1",
// These are not quite real-world-code, but they are more complex "integration" style tests.
"scenarios/point",
];
describe("outputs", () => {

View file

@ -0,0 +1,4 @@
{
"name": "scenarios/point"
}

View file

@ -0,0 +1,678 @@
{
"name": "scenarios/point",
"modules": {
"index": {
"kind": "Module",
"name": {
"kind": "Identifier",
"ident": "index"
},
"members": {
"Point": {
"kind": "Class",
"name": {
"kind": "Identifier",
"ident": "Point",
"loc": {
"file": "index.ts",
"start": {
"line": 4,
"column": 13
},
"end": {
"line": 4,
"column": 18
}
}
},
"access": "public",
"members": {
".ctor": {
"kind": "ClassMethod",
"name": {
"kind": "Identifier",
"ident": ".ctor"
},
"access": "public",
"parameters": [
{
"kind": "LocalVariable",
"name": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 8,
"column": 16
},
"end": {
"line": 8,
"column": 17
}
}
},
"type": "TODO"
},
{
"kind": "LocalVariable",
"name": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 8,
"column": 27
},
"end": {
"line": 8,
"column": 28
}
}
},
"type": "TODO"
}
],
"body": {
"kind": "Block",
"statements": [
{
"kind": "ExpressionStatement",
"expression": {
"kind": "BinaryOperatorExpression",
"operator": "=",
"left": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"name": {
"kind": "Identifier",
"ident": ".this"
},
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 8
},
"end": {
"line": 9,
"column": 12
}
}
},
"name": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 13
},
"end": {
"line": 9,
"column": 14
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 8
},
"end": {
"line": 9,
"column": 14
}
}
},
"right": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 17
},
"end": {
"line": 9,
"column": 18
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 8
},
"end": {
"line": 9,
"column": 18
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 9,
"column": 8
},
"end": {
"line": 9,
"column": 19
}
}
},
{
"kind": "ExpressionStatement",
"expression": {
"kind": "BinaryOperatorExpression",
"operator": "=",
"left": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"name": {
"kind": "Identifier",
"ident": ".this"
},
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 8
},
"end": {
"line": 10,
"column": 12
}
}
},
"name": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 13
},
"end": {
"line": 10,
"column": 14
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 8
},
"end": {
"line": 10,
"column": 14
}
}
},
"right": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 17
},
"end": {
"line": 10,
"column": 18
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 8
},
"end": {
"line": 10,
"column": 18
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 10,
"column": 8
},
"end": {
"line": 10,
"column": 19
}
}
}
],
"loc": {
"file": "index.ts",
"start": {
"line": 8,
"column": 38
},
"end": {
"line": 11,
"column": 5
}
}
},
"returnType": "TODO",
"static": false,
"abstract": false,
"loc": {
"file": "index.ts",
"start": {
"line": 8,
"column": 4
},
"end": {
"line": 11,
"column": 5
}
}
},
"add": {
"kind": "ClassMethod",
"name": {
"kind": "Identifier",
"ident": "add",
"loc": {
"file": "index.ts",
"start": {
"line": 13,
"column": 11
},
"end": {
"line": 13,
"column": 14
}
}
},
"access": "public",
"parameters": [
{
"kind": "LocalVariable",
"name": {
"kind": "Identifier",
"ident": "other",
"loc": {
"file": "index.ts",
"start": {
"line": 13,
"column": 15
},
"end": {
"line": 13,
"column": 20
}
}
},
"type": "TODO"
}
],
"body": {
"kind": "Block",
"statements": [
{
"kind": "ReturnStatement",
"expression": {
"kind": "NewExpression",
"type": {
"kind": "Identifier",
"ident": "Point",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 19
},
"end": {
"line": 14,
"column": 24
}
}
},
"arguments": [
{
"kind": "BinaryOperatorExpression",
"operator": "+",
"left": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"name": {
"kind": "Identifier",
"ident": ".this"
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 25
},
"end": {
"line": 14,
"column": 29
}
}
},
"name": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 30
},
"end": {
"line": 14,
"column": 31
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 25
},
"end": {
"line": 14,
"column": 31
}
}
},
"right": {
"kind": "LoadLocationExpression",
"object": {
"kind": "Identifier",
"ident": "other",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 34
},
"end": {
"line": 14,
"column": 39
}
}
},
"name": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 40
},
"end": {
"line": 14,
"column": 41
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 34
},
"end": {
"line": 14,
"column": 41
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 25
},
"end": {
"line": 14,
"column": 41
}
}
},
{
"kind": "BinaryOperatorExpression",
"operator": "+",
"left": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"name": {
"kind": "Identifier",
"ident": ".this"
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 43
},
"end": {
"line": 14,
"column": 47
}
}
},
"name": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 48
},
"end": {
"line": 14,
"column": 49
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 43
},
"end": {
"line": 14,
"column": 49
}
}
},
"right": {
"kind": "LoadLocationExpression",
"object": {
"kind": "Identifier",
"ident": "other",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 52
},
"end": {
"line": 14,
"column": 57
}
}
},
"name": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 58
},
"end": {
"line": 14,
"column": 59
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 52
},
"end": {
"line": 14,
"column": 59
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 43
},
"end": {
"line": 14,
"column": 59
}
}
}
]
},
"loc": {
"file": "index.ts",
"start": {
"line": 14,
"column": 8
},
"end": {
"line": 14,
"column": 61
}
}
}
],
"loc": {
"file": "index.ts",
"start": {
"line": 13,
"column": 36
},
"end": {
"line": 15,
"column": 5
}
}
},
"returnType": "TODO",
"static": false,
"abstract": false,
"loc": {
"file": "index.ts",
"start": {
"line": 13,
"column": 4
},
"end": {
"line": 15,
"column": 5
}
}
},
"x": {
"kind": "ClassProperty",
"name": {
"kind": "Identifier",
"ident": "x",
"loc": {
"file": "index.ts",
"start": {
"line": 5,
"column": 20
},
"end": {
"line": 5,
"column": 21
}
}
},
"access": "public",
"readonly": true,
"static": false,
"type": "TODO"
},
"y": {
"kind": "ClassProperty",
"name": {
"kind": "Identifier",
"ident": "y",
"loc": {
"file": "index.ts",
"start": {
"line": 6,
"column": 20
},
"end": {
"line": 6,
"column": 21
}
}
},
"access": "public",
"readonly": true,
"static": false,
"type": "TODO"
}
},
"abstract": false,
"loc": {
"file": "index.ts",
"start": {
"line": 4,
"column": 0
},
"end": {
"line": 16,
"column": 1
}
}
}
},
"loc": {
"file": "index.ts",
"start": {
"line": 4,
"column": 0
},
"end": {
"line": 18,
"column": 0
}
}
}
}
}

View file

@ -0,0 +1,17 @@
// This is a simple test case that exports a class with a number of interesting facets: namely, a constructor, some
// member variables, and some member methods.
export class Point {
public readonly x: number;
public readonly y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
public add(other: Point): Point {
return new Point(this.x + other.x, this.y + other.y);
}
}

View file

@ -0,0 +1,6 @@
{
"files": [
"index.ts"
]
}