feat(transpiler): constructor and typed field semantics
fixes #11 (constructor and typed field semantics) fixes #42 (Should we infer class property types from ctor args ?) fixes #17 (number (js) should map to num (dart)) Closes #45
This commit is contained in:
parent
fd0c2d8063
commit
089a2f1b62
|
@ -12,7 +12,7 @@ export class ChangeDetection {
|
||||||
}
|
}
|
||||||
|
|
||||||
detectChanges():int {
|
detectChanges():int {
|
||||||
var current:Record = _rootWatchGroup._headRecord;
|
var current:Record = _rootWatchGroup.headRecord;
|
||||||
var count:number = 0;
|
var count:number = 0;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
if (current.check()) {
|
if (current.check()) {
|
||||||
|
|
|
@ -2,11 +2,11 @@ import {ProtoRecord, Record} from './record';
|
||||||
import {FIELD} from 'facade/lang';
|
import {FIELD} from 'facade/lang';
|
||||||
|
|
||||||
export class ProtoWatchGroup {
|
export class ProtoWatchGroup {
|
||||||
@FIELD('final _headRecord:ProtoRecord')
|
@FIELD('final headRecord:ProtoRecord')
|
||||||
@FIELD('final _tailRecord:ProtoRecord')
|
@FIELD('final tailRecord:ProtoRecord')
|
||||||
constructor() {
|
constructor() {
|
||||||
this._headRecord = null;
|
this.headRecord = null;
|
||||||
this._tailRecord = null;
|
this.tailRecord = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +29,7 @@ export class ProtoWatchGroup {
|
||||||
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
||||||
var head:Record = null;
|
var head:Record = null;
|
||||||
var tail:Record = null;
|
var tail:Record = null;
|
||||||
var proto:ProtoRecord = this._headRecord;
|
var proto:ProtoRecord = this.headRecord;
|
||||||
|
|
||||||
while(proto != null) {
|
while(proto != null) {
|
||||||
tail = proto.instantiate(watchGroup);
|
tail = proto.instantiate(watchGroup);
|
||||||
|
@ -37,14 +37,14 @@ export class ProtoWatchGroup {
|
||||||
proto = proto.next;
|
proto = proto.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
proto = this._headRecord;
|
proto = this.headRecord;
|
||||||
while(proto != null) {
|
while(proto != null) {
|
||||||
proto.instantiateComplete();
|
proto.instantiateComplete();
|
||||||
proto = proto.next;
|
proto = proto.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
watchGroup._headRecord = head;
|
watchGroup.headRecord = head;
|
||||||
watchGroup._tailRecord = tail;
|
watchGroup.tailRecord = tail;
|
||||||
return watchGroup;
|
return watchGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ export class ProtoWatchGroup {
|
||||||
export class WatchGroup {
|
export class WatchGroup {
|
||||||
@FIELD('final protoWatchGroup:ProtoWatchGroup')
|
@FIELD('final protoWatchGroup:ProtoWatchGroup')
|
||||||
@FIELD('final dispatcher:WatchGroupDispatcher')
|
@FIELD('final dispatcher:WatchGroupDispatcher')
|
||||||
@FIELD('final _headRecord:Record')
|
@FIELD('final headRecord:Record')
|
||||||
@FIELD('final _tailRecord:Record')
|
@FIELD('final tailRecord:Record')
|
||||||
constructor(protoWatchGroup:ProtoWatchGroup, dispatcher:WatchGroupDispatcher) {
|
constructor(protoWatchGroup:ProtoWatchGroup, dispatcher:WatchGroupDispatcher) {
|
||||||
this.protoWatchGroup = protoWatchGroup;
|
this.protoWatchGroup = protoWatchGroup;
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this._headRecord = null;
|
this.headRecord = null;
|
||||||
this._tailRecord = null;
|
this.tailRecord = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {describe, it, expect} from 'test_lib/test_lib';
|
import {describe, it, expect} from 'test_lib/test_lib';
|
||||||
|
import {CONST} from './fixtures/annotations';
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
// Define fields
|
// Define fields
|
||||||
|
@ -13,6 +14,28 @@ class Foo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubFoo extends Foo {
|
||||||
|
constructor(a, b) {
|
||||||
|
this.c = 3;
|
||||||
|
super(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Const {
|
||||||
|
@CONST
|
||||||
|
constructor(a:number) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubConst extends Const {
|
||||||
|
@CONST
|
||||||
|
constructor(a:number, b:number) {
|
||||||
|
super(a);
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('classes', function() {
|
describe('classes', function() {
|
||||||
it('should work', function() {
|
it('should work', function() {
|
||||||
|
@ -22,5 +45,21 @@ export function main() {
|
||||||
expect(foo.b).toBe(3);
|
expect(foo.b).toBe(3);
|
||||||
expect(foo.sum()).toBe(5);
|
expect(foo.sum()).toBe(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('@CONST should be transpiled to a const constructor', function() {
|
||||||
|
var subConst = new SubConst(1, 2);
|
||||||
|
expect(subConst.a).toBe(1);
|
||||||
|
expect(subConst.b).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('inheritance', function() {
|
||||||
|
it('should support super call', function () {
|
||||||
|
var subFoo = new SubFoo(1, 2);
|
||||||
|
expect(subFoo.a).toBe(1);
|
||||||
|
expect(subFoo.b).toBe(2);
|
||||||
|
expect(subFoo.c).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ class Provide {
|
||||||
const Provide(this.token);
|
const Provide(this.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CONST {
|
||||||
|
const CONST();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this api does not yet return an array as we don't have
|
// TODO: this api does not yet return an array as we don't have
|
||||||
// a nice array wrapper for Dart
|
// a nice array wrapper for Dart
|
||||||
readFirstAnnotation(clazz) {
|
readFirstAnnotation(clazz) {
|
||||||
|
|
|
@ -10,6 +10,8 @@ export class Provide {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CONST {
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this api does not yet return an array as we don't have
|
// TODO: this api does not yet return an array as we don't have
|
||||||
// a nice array wrapper for Dart
|
// a nice array wrapper for Dart
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import {ParseTree} from 'traceur/src/syntax/trees/ParseTree';
|
|
||||||
|
|
||||||
var CLASS_FIELD = 'CLASS_FIELD';
|
|
||||||
|
|
||||||
export class ClassFieldParseTree extends ParseTree {
|
|
||||||
constructor(location, identifier, typeAnnotation) {
|
|
||||||
this.location = location;
|
|
||||||
this.identifier = identifier;
|
|
||||||
this.typeAnnotation = typeAnnotation;
|
|
||||||
}
|
|
||||||
get type() {
|
|
||||||
return CLASS_FIELD;
|
|
||||||
}
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitClassField(this);
|
|
||||||
}
|
|
||||||
transform(transformer) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +1,44 @@
|
||||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
import {ParseTreeTransformer} from './ParseTreeTransformer';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PROPERTY_METHOD_ASSIGNMENT,
|
BINARY_EXPRESSION,
|
||||||
|
CALL_EXPRESSION,
|
||||||
|
IDENTIFIER_EXPRESSION,
|
||||||
MEMBER_EXPRESSION,
|
MEMBER_EXPRESSION,
|
||||||
THIS_EXPRESSION,
|
PROPERTY_METHOD_ASSIGNMENT,
|
||||||
BINARY_EXPRESSION
|
SUPER_EXPRESSION,
|
||||||
|
THIS_EXPRESSION
|
||||||
} from 'traceur/src/syntax/trees/ParseTreeType';
|
} from 'traceur/src/syntax/trees/ParseTreeType';
|
||||||
|
|
||||||
|
import {EQUAL} from 'traceur/src/syntax/TokenType';
|
||||||
|
|
||||||
import {CONSTRUCTOR} from 'traceur/src/syntax/PredefinedName';
|
import {CONSTRUCTOR} from 'traceur/src/syntax/PredefinedName';
|
||||||
|
|
||||||
import {propName} from 'traceur/src/staticsemantics/PropName';
|
import {propName} from 'traceur/src/staticsemantics/PropName';
|
||||||
|
|
||||||
import {ClassFieldParseTree} from '../ast/class_field';
|
import {
|
||||||
|
BinaryExpression,
|
||||||
|
BindingIdentifier,
|
||||||
|
IdentifierExpression
|
||||||
|
} from 'traceur/src/syntax/trees/ParseTrees';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClassFieldDeclaration,
|
||||||
|
PropertyConstructorAssignment
|
||||||
|
} from '../syntax/trees/ParseTrees';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms class declaration:
|
* Transforms class declaration:
|
||||||
* - rename constructor (name of the class - default Dart constructor)
|
* - rename constructor to the name of the class (default Dart constructor),
|
||||||
*
|
* - class fields are extracted from `this.field = expression;` in the ctor body,
|
||||||
|
* - `@CONST` annotations on the ctor result in a const constructor & final fields in Dart,
|
||||||
|
* - const constructor body is converted to an initializerList
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class ClassTransformer extends ParseTreeTransformer {
|
export class ClassTransformer extends ParseTreeTransformer {
|
||||||
|
constructor(idGenerator, reporter) {
|
||||||
|
this.reporter_ = reporter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ClassDeclaration} tree
|
* @param {ClassDeclaration} tree
|
||||||
* @returns {ParseTree}
|
* @returns {ParseTree}
|
||||||
|
@ -28,12 +47,17 @@ export class ClassTransformer extends ParseTreeTransformer {
|
||||||
var className = tree.name.identifierToken.toString();
|
var className = tree.name.identifierToken.toString();
|
||||||
var argumentTypesMap = {};
|
var argumentTypesMap = {};
|
||||||
var fields = [];
|
var fields = [];
|
||||||
|
var isConst;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
tree.elements.forEach(function(elementTree) {
|
tree.elements.forEach(function(elementTree, index) {
|
||||||
if (elementTree.type === PROPERTY_METHOD_ASSIGNMENT &&
|
if (elementTree.type === PROPERTY_METHOD_ASSIGNMENT &&
|
||||||
!elementTree.isStatic &&
|
!elementTree.isStatic &&
|
||||||
propName(elementTree) === CONSTRUCTOR) {
|
propName(elementTree) === CONSTRUCTOR) {
|
||||||
|
|
||||||
|
isConst = elementTree.annotations.some((annotation) =>
|
||||||
|
annotation.name.identifierToken.value === 'CONST');
|
||||||
|
|
||||||
// Store constructor argument types,
|
// Store constructor argument types,
|
||||||
// so that we can use them to set the types of simple-assigned fields.
|
// so that we can use them to set the types of simple-assigned fields.
|
||||||
elementTree.parameterList.parameters.forEach(function(p) {
|
elementTree.parameterList.parameters.forEach(function(p) {
|
||||||
|
@ -48,15 +72,54 @@ export class ClassTransformer extends ParseTreeTransformer {
|
||||||
|
|
||||||
// Collect all fields, defined in the constructor.
|
// Collect all fields, defined in the constructor.
|
||||||
elementTree.body.statements.forEach(function(statement) {
|
elementTree.body.statements.forEach(function(statement) {
|
||||||
if (statement.expression.type === BINARY_EXPRESSION &&
|
var exp = statement.expression;
|
||||||
statement.expression.operator.type === '=' &&
|
if (exp.type === BINARY_EXPRESSION &&
|
||||||
statement.expression.left.type === MEMBER_EXPRESSION &&
|
exp.operator.type === EQUAL &&
|
||||||
statement.expression.left.operand.type === THIS_EXPRESSION) {
|
exp.left.type === MEMBER_EXPRESSION &&
|
||||||
|
exp.left.operand.type === THIS_EXPRESSION) {
|
||||||
|
|
||||||
var typeAnnotation = argumentTypesMap[statement.expression.left.memberName.value] || null;
|
var typeAnnotation;
|
||||||
fields.push(new ClassFieldParseTree(tree.location, statement.expression.left.memberName, typeAnnotation));
|
|
||||||
|
if (exp.right.type === IDENTIFIER_EXPRESSION) {
|
||||||
|
// `this.field = variable;`
|
||||||
|
// we can infer the type of the field from the variable when it is a typed arg
|
||||||
|
var varName = exp.right.getStringValue();
|
||||||
|
typeAnnotation = argumentTypesMap[varName] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldName = exp.left.memberName.value;
|
||||||
|
var lvalue = new BindingIdentifier(tree.location, fieldName);
|
||||||
|
fields.push(new ClassFieldDeclaration(tree.location, lvalue, typeAnnotation, isConst));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Compute the initializer list
|
||||||
|
var initializerList = [];
|
||||||
|
var superCall = that._extractSuperCall(elementTree.body);
|
||||||
|
if (isConst) {
|
||||||
|
initializerList = that._extractFieldInitializers(elementTree.body);
|
||||||
|
if (elementTree.body.statements.length > 0) {
|
||||||
|
that.reporter_.reportError(
|
||||||
|
elementTree.location,
|
||||||
|
'Const constructor body can only contain field initialization & super call');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (superCall) initializerList.push(superCall);
|
||||||
|
|
||||||
|
// Replace the `PROPERTY_METHOD_ASSIGNMENT` with a Dart specific
|
||||||
|
// `PROPERTY_CONSTRUCTOR_ASSIGNMENT`
|
||||||
|
tree.elements[index] = new PropertyConstructorAssignment(
|
||||||
|
elementTree.location,
|
||||||
|
elementTree.isStatic,
|
||||||
|
elementTree.functionKind,
|
||||||
|
elementTree.name,
|
||||||
|
elementTree.parameterList,
|
||||||
|
elementTree.typeAnnotation,
|
||||||
|
elementTree.annotations,
|
||||||
|
elementTree.body,
|
||||||
|
isConst,
|
||||||
|
initializerList
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,5 +127,58 @@ export class ClassTransformer extends ParseTreeTransformer {
|
||||||
tree.elements = fields.concat(tree.elements);
|
tree.elements = fields.concat(tree.elements);
|
||||||
|
|
||||||
return super(tree);
|
return super(tree);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract field initialization (`this.field = <expression>;`) from the body of the constructor.
|
||||||
|
* The init statements are removed from the body statements and returned as an array.
|
||||||
|
*/
|
||||||
|
_extractFieldInitializers(body) {
|
||||||
|
var statements = [];
|
||||||
|
var fieldInitializers = [];
|
||||||
|
body.statements.forEach(function(statement) {
|
||||||
|
var exp = statement.expression;
|
||||||
|
if (exp.type === BINARY_EXPRESSION &&
|
||||||
|
exp.operator.type === EQUAL &&
|
||||||
|
exp.left.type === MEMBER_EXPRESSION &&
|
||||||
|
exp.left.operand.type === THIS_EXPRESSION) {
|
||||||
|
// `this.field = exp` -> `field = exp`
|
||||||
|
// todo(vicb): check for `this.` on rhs, not allowed in Dart
|
||||||
|
// -> remove if possible (arguments), throw otherwise.
|
||||||
|
var fieldName = exp.left.memberName.value;
|
||||||
|
fieldInitializers.push(new BinaryExpression(
|
||||||
|
statement.location,
|
||||||
|
new IdentifierExpression(statement.location, fieldName),
|
||||||
|
EQUAL,
|
||||||
|
exp.right
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
statements.push(statement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
body.statements = statements;
|
||||||
|
return fieldInitializers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the super call (`super(<arg list>)`) from the body of the constructor.
|
||||||
|
* When found the super call statement is removed from the body statements and returned.
|
||||||
|
*/
|
||||||
|
_extractSuperCall(body) {
|
||||||
|
var statements = [];
|
||||||
|
var superCall = null;
|
||||||
|
|
||||||
|
body.statements.forEach(function (statement) {
|
||||||
|
if (statement.expression.type === CALL_EXPRESSION &&
|
||||||
|
statement.expression.operand.type === SUPER_EXPRESSION) {
|
||||||
|
superCall = statement.expression;
|
||||||
|
} else {
|
||||||
|
statements.push(statement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
body.statements = statements;
|
||||||
|
return superCall;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {INSTANCEOF} from 'traceur/src/syntax/TokenType';
|
import {INSTANCEOF} from 'traceur/src/syntax/TokenType';
|
||||||
|
|
||||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
import {ParseTreeTransformer} from './ParseTreeTransformer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms `a instanceof b` to `a is b`,
|
* Transforms `a instanceof b` to `a is b`,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {VariableStatement, VariableDeclarationList} from 'traceur/src/syntax/trees/ParseTrees';
|
import {VariableStatement, VariableDeclarationList} from 'traceur/src/syntax/trees/ParseTrees';
|
||||||
|
|
||||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
import {ParseTreeTransformer} from './ParseTreeTransformer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms `var a, b;` to `var a; var b;`
|
* Transforms `var a, b;` to `var a; var b;`
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {
|
||||||
|
ParseTreeTransformer as TraceurParseTreeTransformer
|
||||||
|
} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClassFieldDeclaration,
|
||||||
|
PropertyConstructorAssignment
|
||||||
|
} from '../syntax/trees/ParseTrees'
|
||||||
|
|
||||||
|
export class ParseTreeTransformer extends TraceurParseTreeTransformer {
|
||||||
|
transformClassFieldDeclaration(tree) {
|
||||||
|
var lvalue = this.transformAny(tree.lvalue);
|
||||||
|
var typeAnnotation = this.transformAny(tree.typeAnnotation);
|
||||||
|
if (lvalue === tree.lvalue && typeAnnotation === tree.typeAnnotation) {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
return new ClassFieldDeclaration(tree.location, lvalue, typeAnnotation, initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
transformPropertyConstructorAssignment(tree) {
|
||||||
|
tree = super.transformPropertyMethodAssignment(tree);
|
||||||
|
var initializerList = this.transformList(tree.initializerList);
|
||||||
|
if (initializerList === tree.initializerList) {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PropertyConstructorAssignment(tree.location, tree.isStatic, tree.functionKind,
|
||||||
|
tree.name, tree.parameterList, tree.typeAnnotation, tree.annotations, tree.body, tree.isConst,
|
||||||
|
initializerList);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
import {
|
import {
|
||||||
|
createArgumentList,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
createIdentifierExpression,
|
createIdentifierExpression
|
||||||
createArgumentList} from 'traceur/src/codegeneration/ParseTreeFactory';
|
} from 'traceur/src/codegeneration/ParseTreeFactory';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EQUAL_EQUAL_EQUAL,
|
EQUAL_EQUAL_EQUAL,
|
||||||
NOT_EQUAL_EQUAL
|
NOT_EQUAL_EQUAL
|
||||||
} from 'traceur/src/syntax/TokenType';
|
} from 'traceur/src/syntax/TokenType';
|
||||||
|
|
||||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
import {ParseTreeTransformer} from './ParseTreeTransformer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms:
|
* Transforms:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Compiler as TraceurCompiler} from 'traceur/src/Compiler';
|
import {Compiler as TraceurCompiler} from 'traceur/src/Compiler';
|
||||||
import {DartTransformer} from './codegeneration/DartTransformer';
|
import {DartTransformer} from './codegeneration/DartTransformer';
|
||||||
import {DartTreeWriter} from './dart_writer';
|
import {DartParseTreeWriter} from './outputgeneration/DartParseTreeWriter';
|
||||||
import {CollectingErrorReporter} from 'traceur/src/util/CollectingErrorReporter';
|
import {CollectingErrorReporter} from 'traceur/src/util/CollectingErrorReporter';
|
||||||
import {Parser} from './parser';
|
import {Parser} from './parser';
|
||||||
import {SourceFile} from 'traceur/src/syntax/SourceFile';
|
import {SourceFile} from 'traceur/src/syntax/SourceFile';
|
||||||
|
@ -28,7 +28,7 @@ export class Compiler extends TraceurCompiler {
|
||||||
|
|
||||||
write(tree, outputName = undefined, sourceRoot = undefined) {
|
write(tree, outputName = undefined, sourceRoot = undefined) {
|
||||||
if (this.options_.outputLanguage.toLowerCase() === 'dart') {
|
if (this.options_.outputLanguage.toLowerCase() === 'dart') {
|
||||||
var writer = new DartTreeWriter(this.options_.moduleName, outputName);
|
var writer = new DartParseTreeWriter(this.options_.moduleName, outputName);
|
||||||
writer.visitAny(tree);
|
writer.visitAny(tree);
|
||||||
return writer.toString();
|
return writer.toString();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,22 @@
|
||||||
import {CONSTRUCTOR, FROM} from 'traceur/src/syntax/PredefinedName';
|
import {CONSTRUCTOR, FROM} from 'traceur/src/syntax/PredefinedName';
|
||||||
import {EQUAL_EQUAL_EQUAL, OPEN_PAREN, CLOSE_PAREN, IMPORT, SEMI_COLON, STAR, OPEN_CURLY, CLOSE_CURLY, COMMA, AT, EQUAL, COLON} from 'traceur/src/syntax/TokenType';
|
import {
|
||||||
|
AT,
|
||||||
|
CLOSE_CURLY,
|
||||||
|
CLOSE_PAREN,
|
||||||
|
COLON,
|
||||||
|
COMMA,
|
||||||
|
EQUAL,
|
||||||
|
EQUAL_EQUAL_EQUAL,
|
||||||
|
IMPORT,
|
||||||
|
OPEN_CURLY,
|
||||||
|
OPEN_PAREN,
|
||||||
|
SEMI_COLON,
|
||||||
|
STAR
|
||||||
|
} from 'traceur/src/syntax/TokenType';
|
||||||
|
|
||||||
import {ParseTreeWriter as JavaScriptParseTreeWriter, ObjectLiteralExpression} from 'traceur/src/outputgeneration/ParseTreeWriter';
|
import {ParseTreeWriter as JavaScriptParseTreeWriter, ObjectLiteralExpression} from 'traceur/src/outputgeneration/ParseTreeWriter';
|
||||||
|
|
||||||
export class DartTreeWriter extends JavaScriptParseTreeWriter {
|
export class DartParseTreeWriter extends JavaScriptParseTreeWriter {
|
||||||
constructor(moduleName, outputPath) {
|
constructor(moduleName, outputPath) {
|
||||||
super(outputPath);
|
super(outputPath);
|
||||||
this.libName = moduleName.replace(/\//g, '.');
|
this.libName = moduleName.replace(/\//g, '.');
|
||||||
|
@ -104,20 +117,42 @@ export class DartTreeWriter extends JavaScriptParseTreeWriter {
|
||||||
this.visitAny(tree.body);
|
this.visitAny(tree.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {PropertyMethodAssignment} tree
|
||||||
|
*/
|
||||||
|
visitPropertyConstructorAssignment(tree) {
|
||||||
|
this.writeAnnotations_(tree.annotations);
|
||||||
|
|
||||||
|
if (tree.isConst) {
|
||||||
|
this.write_('const');
|
||||||
|
this.writeSpace_();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.writeType_(tree.typeAnnotation);
|
||||||
|
this.visitAny(tree.name);
|
||||||
|
this.write_(OPEN_PAREN);
|
||||||
|
this.visitAny(tree.parameterList);
|
||||||
|
this.write_(CLOSE_PAREN);
|
||||||
|
if (tree.initializerList.length > 0) {
|
||||||
|
this.write_(COLON);
|
||||||
|
this.writeSpace_();
|
||||||
|
this.writeList_(tree.initializerList, ', ');
|
||||||
|
}
|
||||||
|
if (tree.isConst) {
|
||||||
|
this.write_(SEMI_COLON);
|
||||||
|
} else {
|
||||||
|
this.writeSpace_();
|
||||||
|
this.visitAny(tree.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
normalizeType_(typeName) {
|
normalizeType_(typeName) {
|
||||||
if (typeName === 'number') {
|
switch (typeName) {
|
||||||
return 'num';
|
case 'number': return 'num';
|
||||||
|
case 'boolean': return 'bool';
|
||||||
|
case 'string': return 'String';
|
||||||
|
default: return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeName === 'boolean') {
|
|
||||||
return 'bool';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeName === 'string') {
|
|
||||||
return 'String';
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTION/METHOD ARGUMENTS
|
// FUNCTION/METHOD ARGUMENTS
|
||||||
|
@ -148,14 +183,23 @@ export class DartTreeWriter extends JavaScriptParseTreeWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitClassField(tree) {
|
visitClassFieldDeclaration(tree) {
|
||||||
|
if (tree.isFinal) {
|
||||||
|
// `final <type> name;` or `final name;` for untyped variable
|
||||||
|
this.write_('final');
|
||||||
|
this.writeSpace_();
|
||||||
this.writeType_(tree.typeAnnotation);
|
this.writeType_(tree.typeAnnotation);
|
||||||
|
} else {
|
||||||
if (!tree.typeAnnotation) {
|
// `<type> name;` or `var name;`
|
||||||
|
if (tree.typeAnnotation) {
|
||||||
|
this.writeType_(tree.typeAnnotation);
|
||||||
|
} else {
|
||||||
this.write_('var');
|
this.write_('var');
|
||||||
|
this.writeSpace_();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.write_(tree.identifier);
|
this.write_(tree.lvalue.getStringValue());
|
||||||
this.write_(SEMI_COLON);
|
this.write_(SEMI_COLON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +224,7 @@ export class DartTreeWriter extends JavaScriptParseTreeWriter {
|
||||||
visitExportDeclaration(tree) {
|
visitExportDeclaration(tree) {
|
||||||
if (tree.declaration.moduleSpecifier) {
|
if (tree.declaration.moduleSpecifier) {
|
||||||
this.write_('export');
|
this.write_('export');
|
||||||
|
this.writeSpace_();
|
||||||
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
|
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
|
||||||
this.write_(SEMI_COLON);
|
this.write_(SEMI_COLON);
|
||||||
} else {
|
} else {
|
|
@ -0,0 +1,2 @@
|
||||||
|
export var CLASS_FIELD_DECLARATION = 'CLASS_FIELD_DECLARATION';
|
||||||
|
export var PROPERTY_CONSTRUCTOR_ASSIGNMENT = 'PROPERTY_CONSTRUCTOR_ASSIGNMENT';
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {ParseTree} from 'traceur/src/syntax/trees/ParseTree';
|
||||||
|
|
||||||
|
import {PropertyMethodAssignment} from 'traceur/src/syntax/trees/ParseTrees';
|
||||||
|
|
||||||
|
import * as ParseTreeType from './ParseTreeType';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property declaration
|
||||||
|
*/
|
||||||
|
export class ClassFieldDeclaration extends ParseTree {
|
||||||
|
constructor(location, lvalue, typeAnnotation, isFinal) {
|
||||||
|
this.location = location;
|
||||||
|
this.lvalue = lvalue;
|
||||||
|
this.typeAnnotation = typeAnnotation;
|
||||||
|
this.isFinal = isFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return CLASS_FIELD_DECLARATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitClassFieldDeclaration(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(transformer) {
|
||||||
|
return transformer.transformClassFieldDeclaration(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var CLASS_FIELD_DECLARATION = ParseTreeType.CLASS_FIELD_DECLARATION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor
|
||||||
|
*/
|
||||||
|
export class PropertyConstructorAssignment extends PropertyMethodAssignment {
|
||||||
|
/**
|
||||||
|
* @param {SourceRange} location
|
||||||
|
* @param {boolean} isStatic
|
||||||
|
* @param {Token} functionKind
|
||||||
|
* @param {ParseTree} name
|
||||||
|
* @param {FormalParameterList} parameterList
|
||||||
|
* @param {ParseTree} typeAnnotation
|
||||||
|
* @param {Array.<ParseTree>} annotations
|
||||||
|
* @param {FunctionBody} body
|
||||||
|
* @param {boolean} isConst
|
||||||
|
* @param {ParseTree} initializerList
|
||||||
|
*/
|
||||||
|
constructor(location, isStatic, functionKind, name, parameterList, typeAnnotation, annotations,
|
||||||
|
body, isConst, initializerList) {
|
||||||
|
super(location, isStatic, functionKind, name, parameterList, typeAnnotation, annotations,
|
||||||
|
body);
|
||||||
|
this.isConst = isConst;
|
||||||
|
this.initializerList = initializerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ParseTreeTransformer} transformer
|
||||||
|
*/
|
||||||
|
transform(transformer) {
|
||||||
|
return transformer.transformPropertyConstructorAssignment(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ParseTreeVisitor} visitor
|
||||||
|
*/
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitPropertyConstructorAssignment(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {ParseTreeType}
|
||||||
|
*/
|
||||||
|
get type() {
|
||||||
|
return PROPERTY_CONSTRUCTOR_ASSIGNMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var PROPERTY_CONSTRUCTOR_ASSIGNMENT = ParseTreeType.PROPERTY_CONSTRUCTOR_ASSIGNMENT;
|
||||||
|
|
Loading…
Reference in New Issue