refactor(transpiler): split the monolithic dart transformer
fixes #24 The new architecture conforms with the Traceur architecture.
This commit is contained in:
parent
92375c0281
commit
64d3cc68f0
|
@ -0,0 +1,68 @@
|
|||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||
|
||||
import {
|
||||
PROPERTY_METHOD_ASSIGNMENT,
|
||||
MEMBER_EXPRESSION,
|
||||
THIS_EXPRESSION,
|
||||
BINARY_EXPRESSION
|
||||
} from 'traceur/src/syntax/trees/ParseTreeType';
|
||||
|
||||
import {CONSTRUCTOR} from 'traceur/src/syntax/PredefinedName';
|
||||
|
||||
import {propName} from 'traceur/src/staticsemantics/PropName';
|
||||
|
||||
import {ClassFieldParseTree} from '../ast/class_field';
|
||||
|
||||
/**
|
||||
* Transforms class declaration:
|
||||
* - rename constructor (name of the class - default Dart constructor)
|
||||
*
|
||||
*/
|
||||
|
||||
export class ClassTransformer extends ParseTreeTransformer {
|
||||
/**
|
||||
* @param {ClassDeclaration} tree
|
||||
* @returns {ParseTree}
|
||||
*/
|
||||
transformClassDeclaration(tree) {
|
||||
var className = tree.name.identifierToken.toString();
|
||||
var argumentTypesMap = {};
|
||||
var fields = [];
|
||||
|
||||
tree.elements.forEach(function(elementTree) {
|
||||
if (elementTree.type === PROPERTY_METHOD_ASSIGNMENT &&
|
||||
!elementTree.isStatic &&
|
||||
propName(elementTree) === CONSTRUCTOR) {
|
||||
|
||||
// Store constructor argument types,
|
||||
// so that we can use them to set the types of simple-assigned fields.
|
||||
elementTree.parameterList.parameters.forEach(function(p) {
|
||||
var binding = p.parameter.binding;
|
||||
if (binding.identifierToken) {
|
||||
argumentTypesMap[binding.identifierToken.value] = p.typeAnnotation;
|
||||
}
|
||||
});
|
||||
|
||||
// Rename "constructor" to the class name.
|
||||
elementTree.name.literalToken.value = className;
|
||||
|
||||
// Collect all fields, defined in the constructor.
|
||||
elementTree.body.statements.forEach(function(statement) {
|
||||
if (statement.expression.type === BINARY_EXPRESSION &&
|
||||
statement.expression.operator.type === '=' &&
|
||||
statement.expression.left.type === MEMBER_EXPRESSION &&
|
||||
statement.expression.left.operand.type === THIS_EXPRESSION) {
|
||||
|
||||
var typeAnnotation = argumentTypesMap[statement.expression.left.memberName.value] || null;
|
||||
fields.push(new ClassFieldParseTree(tree.location, statement.expression.left.memberName, typeAnnotation));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add the field definitions to the beginning of the class.
|
||||
tree.elements = fields.concat(tree.elements);
|
||||
|
||||
return super(tree);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import {MultiTransformer} from 'traceur/src/codegeneration/MultiTransformer';
|
||||
import {UniqueIdentifierGenerator} from 'traceur/src/codegeneration/UniqueIdentifierGenerator';
|
||||
import {options} from 'traceur/src/Options';
|
||||
|
||||
import {ClassTransformer} from './ClassTransformer';
|
||||
import {InstanceOfTransformer} from './InstanceOfTransformer';
|
||||
import {MultiVarTransformer} from './MultiVarTransformer';
|
||||
import {StrictEqualityTransformer} from './StrictEqualityTransformer';
|
||||
|
||||
/**
|
||||
* Transforms ES6 + annotations to Dart code.
|
||||
*/
|
||||
export class DartTransformer extends MultiTransformer {
|
||||
constructor(reporter, idGenerator = new UniqueIdentifierGenerator()) {
|
||||
super(reporter, options.validate);
|
||||
|
||||
var append = (transformer) => {
|
||||
this.append((tree) => {
|
||||
return new transformer(idGenerator, reporter).transformAny(tree);
|
||||
});
|
||||
};
|
||||
|
||||
append(MultiVarTransformer);
|
||||
append(InstanceOfTransformer);
|
||||
append(StrictEqualityTransformer);
|
||||
append(ClassTransformer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import {INSTANCEOF} from 'traceur/src/syntax/TokenType';
|
||||
|
||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||
|
||||
/**
|
||||
* Transforms `a instanceof b` to `a is b`,
|
||||
*/
|
||||
export class InstanceOfTransformer extends ParseTreeTransformer {
|
||||
/**
|
||||
* @param {BinaryExpression} tree
|
||||
* @return {ParseTree}
|
||||
*/
|
||||
transformBinaryExpression(tree) {
|
||||
tree.left = this.transformAny(tree.left);
|
||||
tree.right = this.transformAny(tree.right);
|
||||
|
||||
if (tree.operator.type === 'instanceof') {
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
tree.operator.type = 'is';
|
||||
return tree;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import {VariableStatement, VariableDeclarationList} from 'traceur/src/syntax/trees/ParseTrees';
|
||||
|
||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||
|
||||
/**
|
||||
* Transforms `var a, b;` to `var a; var b;`
|
||||
*/
|
||||
export class MultiVarTransformer extends ParseTreeTransformer {
|
||||
// Individual item transformer can return an array of items.
|
||||
// This is used in `transformVariableStatement`.
|
||||
// Otherwise this is copy/pasted from `ParseTreeTransformer`.
|
||||
transformList(list) {
|
||||
var transformedList = [];
|
||||
var transformedItem = null;
|
||||
|
||||
for (var i = 0, ii = list.length; i < ii; i++) {
|
||||
transformedItem = this.transformAny(list[i]);
|
||||
if (Array.isArray(transformedItem)) {
|
||||
transformedList = transformedList.concat(transformedItem);
|
||||
} else {
|
||||
transformedList.push(transformedItem);
|
||||
}
|
||||
}
|
||||
|
||||
return transformedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VariableStatement} tree
|
||||
* @returns {ParseTree}
|
||||
*/
|
||||
transformVariableStatement(tree) {
|
||||
var declarations = tree.declarations.declarations;
|
||||
|
||||
if (declarations.length === 1 || declarations.length === 0) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Multiple var declaration, we will split it into multiple statements.
|
||||
// TODO(vojta): We can leave the multi-definition as long as they are all the same type/untyped.
|
||||
return declarations.map(function(declaration) {
|
||||
return new VariableStatement(tree.location, new VariableDeclarationList(tree.location,
|
||||
tree.declarations.declarationType, [declaration]));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
createCallExpression,
|
||||
createIdentifierExpression,
|
||||
createArgumentList} from 'traceur/src/codegeneration/ParseTreeFactory';
|
||||
|
||||
import {
|
||||
EQUAL_EQUAL_EQUAL,
|
||||
NOT_EQUAL_EQUAL
|
||||
} from 'traceur/src/syntax/TokenType';
|
||||
|
||||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||
|
||||
/**
|
||||
* Transforms:
|
||||
* - `a === b` to `indentical(a, b)`,
|
||||
* - `a !== b` to `!identical(a, b)`
|
||||
*/
|
||||
export class StrictEqualityTransformer extends ParseTreeTransformer {
|
||||
/**
|
||||
* @param {BinaryExpression} tree
|
||||
* @return {ParseTree}
|
||||
*/
|
||||
transformBinaryExpression(tree) {
|
||||
tree.left = this.transformAny(tree.left);
|
||||
tree.right = this.transformAny(tree.right);
|
||||
|
||||
if (tree.operator.type === EQUAL_EQUAL_EQUAL) {
|
||||
// `a === b` -> `identical(a, b)`
|
||||
return createCallExpression(createIdentifierExpression('identical'),
|
||||
createArgumentList([tree.left, tree.right]));
|
||||
}
|
||||
|
||||
if (tree.operator.type === NOT_EQUAL_EQUAL) {
|
||||
// `a !== b` -> `!identical(a, b)`
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
return createCallExpression(createIdentifierExpression('!identical'),
|
||||
createArgumentList([tree.left, tree.right]));
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {Compiler as TraceurCompiler} from 'traceur/src/Compiler';
|
||||
import {ClassTransformer} from './dart_class_transformer';
|
||||
import {DartTransformer} from './codegeneration/DartTransformer';
|
||||
import {DartTreeWriter} from './dart_writer';
|
||||
import {CollectingErrorReporter} from 'traceur/src/util/CollectingErrorReporter';
|
||||
import {Parser} from './parser';
|
||||
|
@ -16,8 +16,11 @@ export class Compiler extends TraceurCompiler {
|
|||
|
||||
transform(tree, moduleName = undefined) {
|
||||
if (this.options_.outputLanguage.toLowerCase() === 'dart') {
|
||||
var transformer = new ClassTransformer();
|
||||
return transformer.transformAny(tree);
|
||||
var errorReporter = new CollectingErrorReporter();
|
||||
var transformer = new DartTransformer(errorReporter);
|
||||
var transformedTree = transformer.transform(tree);
|
||||
this.throwIfErrors(errorReporter);
|
||||
return transformedTree;
|
||||
} else {
|
||||
return super(tree, moduleName);
|
||||
}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer';
|
||||
import {createVariableStatement, createCallExpression, createIdentifierExpression, createArgumentList} from 'traceur/src/codegeneration/ParseTreeFactory';
|
||||
|
||||
// var token = traceur.syntax.TokenType;
|
||||
// var CONSTRUCTOR = token.CONSTRUCTOR;
|
||||
|
||||
import {PROPERTY_METHOD_ASSIGNMENT, MEMBER_EXPRESSION, THIS_EXPRESSION, BINARY_EXPRESSION} from 'traceur/src/syntax/trees/ParseTreeType';
|
||||
import {EQUAL_EQUAL_EQUAL, NOT_EQUAL_EQUAL} from 'traceur/src/syntax/TokenType';
|
||||
import {CONSTRUCTOR} from 'traceur/src/syntax/PredefinedName';
|
||||
|
||||
import {VariableStatement, VariableDeclarationList} from 'traceur/src/syntax/trees/ParseTrees';
|
||||
|
||||
import {propName} from 'traceur/src/staticsemantics/PropName';
|
||||
|
||||
import {ClassFieldParseTree} from './ast/class_field';
|
||||
|
||||
// - rename constructor (name of the class - default Dart constructor)
|
||||
export class ClassTransformer extends ParseTreeTransformer {
|
||||
// Transform multi-var declarations, into multiple statements:
|
||||
// var x, y;
|
||||
// ==>
|
||||
// var x;
|
||||
// var y;
|
||||
// TODO(vojta): move this into a separate transformer.
|
||||
|
||||
// Individual item transformer can return an array of items.
|
||||
// This is used in `transformVariableStatement`.
|
||||
// Otherwise this is copy/pasted from `ParseTreeTransformer`.
|
||||
transformList(list) {
|
||||
var transformedList = [];
|
||||
var transformedItem = null;
|
||||
|
||||
for (var i = 0, ii = list.length; i < ii; i++) {
|
||||
transformedItem = this.transformAny(list[i]);
|
||||
if (Array.isArray(transformedItem)) {
|
||||
transformedList = transformedList.concat(transformedItem);
|
||||
} else {
|
||||
transformedList.push(transformedItem);
|
||||
}
|
||||
}
|
||||
|
||||
return transformedList;
|
||||
}
|
||||
|
||||
transformVariableStatement(tree) {
|
||||
var declarations = tree.declarations.declarations;
|
||||
|
||||
if (declarations.length === 1 || declarations.length === 0) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Multiple var declaration, we will split it into multiple statements.
|
||||
// TODO(vojta): We can leave the multi-definition as long as they are all the same type/untyped.
|
||||
return declarations.map(function(declaration) {
|
||||
return new VariableStatement(tree.location, new VariableDeclarationList(tree.location,
|
||||
tree.declarations.declarationType, [declaration]));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Transform triple equals into identical() call.
|
||||
// TODO(vojta): move to a separate transformer
|
||||
transformBinaryExpression(tree) {
|
||||
tree.left = this.transformAny(tree.left);
|
||||
tree.right = this.transformAny(tree.right);
|
||||
if (tree.operator.type === 'instanceof') {
|
||||
// a instanceof b -> a is b
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
tree.operator.type = 'is';
|
||||
return tree;
|
||||
} else if (tree.operator.type === EQUAL_EQUAL_EQUAL) {
|
||||
// a === b -> identical(a, b)
|
||||
return createCallExpression(createIdentifierExpression('identical'), createArgumentList([tree.left, tree.right]));
|
||||
} else if (tree.operator.type === NOT_EQUAL_EQUAL) {
|
||||
// a !== b -> !identical(a, b)
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
return createCallExpression(createIdentifierExpression('!identical'), createArgumentList([tree.left, tree.right]));
|
||||
} else {
|
||||
return tree;
|
||||
}
|
||||
};
|
||||
|
||||
transformClassDeclaration(tree) {
|
||||
var className = tree.name.identifierToken.toString();
|
||||
var argumentTypesMap = {};
|
||||
var fields = [];
|
||||
|
||||
tree.elements.forEach(function(elementTree) {
|
||||
if (elementTree.type === PROPERTY_METHOD_ASSIGNMENT &&
|
||||
!elementTree.isStatic &&
|
||||
propName(elementTree) === CONSTRUCTOR) {
|
||||
|
||||
// Store constructor argument types,
|
||||
// so that we can use them to set the types of simple-assigned fields.
|
||||
elementTree.parameterList.parameters.forEach(function(p) {
|
||||
var binding = p.parameter.binding;
|
||||
if (binding.identifierToken) {
|
||||
argumentTypesMap[binding.identifierToken.value] = p.typeAnnotation;
|
||||
}
|
||||
});
|
||||
|
||||
// Rename "constructor" to the class name.
|
||||
elementTree.name.literalToken.value = className;
|
||||
|
||||
// Collect all fields, defined in the constructor.
|
||||
elementTree.body.statements.forEach(function(statement) {
|
||||
if (statement.expression.type === BINARY_EXPRESSION &&
|
||||
statement.expression.operator.type === '=' &&
|
||||
statement.expression.left.type === MEMBER_EXPRESSION &&
|
||||
statement.expression.left.operand.type === THIS_EXPRESSION) {
|
||||
|
||||
var typeAnnotation = argumentTypesMap[statement.expression.left.memberName.value] || null;
|
||||
fields.push(new ClassFieldParseTree(tree.location, statement.expression.left.memberName, typeAnnotation));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add the field definitions to the begining of the class.
|
||||
tree.elements = fields.concat(tree.elements);
|
||||
|
||||
return super(tree);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue