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 {Compiler as TraceurCompiler} from 'traceur/src/Compiler';
|
||||||
import {ClassTransformer} from './dart_class_transformer';
|
import {DartTransformer} from './codegeneration/DartTransformer';
|
||||||
import {DartTreeWriter} from './dart_writer';
|
import {DartTreeWriter} from './dart_writer';
|
||||||
import {CollectingErrorReporter} from 'traceur/src/util/CollectingErrorReporter';
|
import {CollectingErrorReporter} from 'traceur/src/util/CollectingErrorReporter';
|
||||||
import {Parser} from './parser';
|
import {Parser} from './parser';
|
||||||
|
@ -16,8 +16,11 @@ export class Compiler extends TraceurCompiler {
|
||||||
|
|
||||||
transform(tree, moduleName = undefined) {
|
transform(tree, moduleName = undefined) {
|
||||||
if (this.options_.outputLanguage.toLowerCase() === 'dart') {
|
if (this.options_.outputLanguage.toLowerCase() === 'dart') {
|
||||||
var transformer = new ClassTransformer();
|
var errorReporter = new CollectingErrorReporter();
|
||||||
return transformer.transformAny(tree);
|
var transformer = new DartTransformer(errorReporter);
|
||||||
|
var transformedTree = transformer.transform(tree);
|
||||||
|
this.throwIfErrors(errorReporter);
|
||||||
|
return transformedTree;
|
||||||
} else {
|
} else {
|
||||||
return super(tree, moduleName);
|
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