Skip to content

Commit

Permalink
refactor(transpiler): split the monolithic dart transformer
Browse files Browse the repository at this point in the history
fix angular#24

The new architecture conforms with the Traceur architecture.
  • Loading branch information
vicb committed Sep 30, 2014
1 parent 92375c0 commit bf329c3
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 127 deletions.
68 changes: 68 additions & 0 deletions tools/transpiler/src/codegeneration/ClassTransformer.js
@@ -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);
};
}
28 changes: 28 additions & 0 deletions tools/transpiler/src/codegeneration/DartTransformer.js
@@ -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);
}
}
25 changes: 25 additions & 0 deletions tools/transpiler/src/codegeneration/InstanceOfTransformer.js
@@ -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;
}
}
46 changes: 46 additions & 0 deletions tools/transpiler/src/codegeneration/MultiVarTransformer.js
@@ -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]));
});
}
}
42 changes: 42 additions & 0 deletions tools/transpiler/src/codegeneration/StrictEqualityTransformer.js
@@ -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;
}
}
9 changes: 6 additions & 3 deletions tools/transpiler/src/compiler.js
@@ -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';
Expand All @@ -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);
}
Expand Down
124 changes: 0 additions & 124 deletions tools/transpiler/src/dart_class_transformer.js

This file was deleted.

0 comments on commit bf329c3

Please sign in to comment.