From 64d3cc68f07725292d448b4a79fe37d0792a9aee Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 30 Sep 2014 16:14:22 +0200 Subject: [PATCH] refactor(transpiler): split the monolithic dart transformer fixes #24 The new architecture conforms with the Traceur architecture. --- .../src/codegeneration/ClassTransformer.js | 68 ++++++++++ .../src/codegeneration/DartTransformer.js | 28 ++++ .../codegeneration/InstanceOfTransformer.js | 25 ++++ .../src/codegeneration/MultiVarTransformer.js | 46 +++++++ .../StrictEqualityTransformer.js | 42 ++++++ tools/transpiler/src/compiler.js | 9 +- .../transpiler/src/dart_class_transformer.js | 124 ------------------ 7 files changed, 215 insertions(+), 127 deletions(-) create mode 100644 tools/transpiler/src/codegeneration/ClassTransformer.js create mode 100644 tools/transpiler/src/codegeneration/DartTransformer.js create mode 100644 tools/transpiler/src/codegeneration/InstanceOfTransformer.js create mode 100644 tools/transpiler/src/codegeneration/MultiVarTransformer.js create mode 100644 tools/transpiler/src/codegeneration/StrictEqualityTransformer.js delete mode 100644 tools/transpiler/src/dart_class_transformer.js diff --git a/tools/transpiler/src/codegeneration/ClassTransformer.js b/tools/transpiler/src/codegeneration/ClassTransformer.js new file mode 100644 index 0000000000..9b872ebdb4 --- /dev/null +++ b/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); + }; +} diff --git a/tools/transpiler/src/codegeneration/DartTransformer.js b/tools/transpiler/src/codegeneration/DartTransformer.js new file mode 100644 index 0000000000..9b88bb1660 --- /dev/null +++ b/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); + } +} diff --git a/tools/transpiler/src/codegeneration/InstanceOfTransformer.js b/tools/transpiler/src/codegeneration/InstanceOfTransformer.js new file mode 100644 index 0000000000..ea22d2a2c4 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/tools/transpiler/src/codegeneration/MultiVarTransformer.js b/tools/transpiler/src/codegeneration/MultiVarTransformer.js new file mode 100644 index 0000000000..b05f0106e4 --- /dev/null +++ b/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])); + }); + } +} \ No newline at end of file diff --git a/tools/transpiler/src/codegeneration/StrictEqualityTransformer.js b/tools/transpiler/src/codegeneration/StrictEqualityTransformer.js new file mode 100644 index 0000000000..622842a76f --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/tools/transpiler/src/compiler.js b/tools/transpiler/src/compiler.js index 04d6b54a05..e3b277421e 100644 --- a/tools/transpiler/src/compiler.js +++ b/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'; @@ -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); } diff --git a/tools/transpiler/src/dart_class_transformer.js b/tools/transpiler/src/dart_class_transformer.js deleted file mode 100644 index d5b373bda9..0000000000 --- a/tools/transpiler/src/dart_class_transformer.js +++ /dev/null @@ -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); - }; -}