diff --git a/modules/facade/src/collection.dart b/modules/facade/src/collection.dart index 5a326f4d30..a9093ac592 100644 --- a/modules/facade/src/collection.dart +++ b/modules/facade/src/collection.dart @@ -1,8 +1,35 @@ library facade.collection; -import 'dart:collection' show HashMap; +import 'dart:collection' show HashMap, IterableBase, Iterator; export 'dart:core' show Map, List, Set; +class MapIterator extends Iterator { + Iterator _iterator; + Map _map; + + MapIterator(Map map) { + this._map = map; + this._iterator = map.keys.iterator; + } + bool moveNext() { + return this._iterator.moveNext(); + } + List get current { + return this._iterator.current != null ? + [this._iterator.current, this._map[this._iterator.current]] : + null; + } +} + +class IterableMap extends IterableBase { + Map _map; + + IterableMap(Map map) { + this._map = map; + } + Iterator get iterator => new MapIterator(this._map); +} + class MapWrapper { static HashMap create() => new HashMap(); static HashMap createFromStringMap(m) => m; @@ -21,6 +48,7 @@ class MapWrapper { static int size(m) {return m.length;} static void delete(m, k) { m.remove(k); } static void clear(m) { m.clear(); } + static Iterable iterable(m) { return new IterableMap(m); } } // TODO: how to export StringMap=Map as a type? diff --git a/modules/facade/src/collection.es6 b/modules/facade/src/collection.es6 index 7369e13bea..3df3e3b375 100644 --- a/modules/facade/src/collection.es6 +++ b/modules/facade/src/collection.es6 @@ -23,6 +23,7 @@ export class MapWrapper { static size(m) {return m.size;} static delete(m, k) { m.delete(k); } static clear(m) { m.clear(); } + static iterable(m) { return m; } } // TODO: cannot export StringMap as a type as Dart does not support renaming types... diff --git a/tools/transpiler/spec/fixtures/facade.dart b/tools/transpiler/spec/fixtures/facade.dart index ffb73b023c..eea66dad1c 100644 --- a/tools/transpiler/spec/fixtures/facade.dart +++ b/tools/transpiler/spec/fixtures/facade.dart @@ -1,3 +1,16 @@ +import 'dart:collection'; + class MapWrapper { -} \ No newline at end of file +} + +/** + * Generic iterable class to test for-of. Provides iteration of the given list. + */ +class IterableList extends IterableBase { + List values; + IterableList(values) { + this.values = values; + } + Iterator get iterator => values.iterator; +} diff --git a/tools/transpiler/spec/fixtures/facade.es6 b/tools/transpiler/spec/fixtures/facade.es6 index c54022ccb5..eb7cfca896 100644 --- a/tools/transpiler/spec/fixtures/facade.es6 +++ b/tools/transpiler/spec/fixtures/facade.es6 @@ -1,3 +1,18 @@ export class MapWrapper { -} \ No newline at end of file +} + +/** + * Generic iterable class to test for-of. Provides iteration of the given array. + */ +export class IterableList { + constructor(values) { + this.values = values; + } + + *[Symbol.iterator]() { + for (var value of this.values) { + yield value; + } + } +} diff --git a/tools/transpiler/spec/for_of_spec.js b/tools/transpiler/spec/for_of_spec.js new file mode 100644 index 0000000000..cd2eb5eb61 --- /dev/null +++ b/tools/transpiler/spec/for_of_spec.js @@ -0,0 +1,51 @@ +import {describe, it, expect} from 'test_lib/test_lib'; +import {ListWrapper, MapWrapper} from 'facade/collection'; +import {IterableList} from './fixtures/facade'; + +export function main() { + describe('for..of', function() { + it('should iterate iterable', function() { + var values = ['a', 'b', 'c']; + var result = ListWrapper.create(); + for (var value of new IterableList(values)) { + ListWrapper.push(result, value); + } + expect(result).toEqual(values); + }); + + it('should iterate iterable without var declaration list', function() { + var values = ['a', 'b', 'c']; + var result = ListWrapper.create(); + var value; + for (value of new IterableList(values)) { + ListWrapper.push(result, value); + } + expect(value).toEqual('c'); + expect(result).toEqual(values); + }); + + it('should iterate maps', function() { + var values = [['a', 1], ['b', 2], ['c', 3]]; + var result = ListWrapper.create(); + var map = MapWrapper.createFromPairs(values); + for (var [key, value] of MapWrapper.iterable(map)) { + ListWrapper.push(result, [key, value]); + } + expect(result).toEqual(values); + }); + + it('should iterate maps without var declaration list', function() { + var values = [['a', 1], ['b', 2], ['c', 3]]; + var result = ListWrapper.create(); + var map = MapWrapper.createFromPairs(values); + var key, value; + for ([key, value] of MapWrapper.iterable(map)) { + ListWrapper.push(result, [key, value]); + } + expect(key).toEqual('c'); + expect(value).toEqual(3); + expect(result).toEqual(values); + }); + }); +} + diff --git a/tools/transpiler/src/codegeneration/DartTransformer.js b/tools/transpiler/src/codegeneration/DartTransformer.js index a35f892d28..a16bb8d1d7 100644 --- a/tools/transpiler/src/codegeneration/DartTransformer.js +++ b/tools/transpiler/src/codegeneration/DartTransformer.js @@ -9,6 +9,8 @@ import {MultiVarTransformer} from './MultiVarTransformer'; import {StrictEqualityTransformer} from './StrictEqualityTransformer'; import {NamedParamsTransformer} from './NamedParamsTransformer'; import {ExportTransformer} from './ExportTransformer'; +import {ForOfTransformer} from './ForOfTransformer'; +import {DestructuringTransformer} from './DestructuringTransformer'; /** * Transforms ES6 + annotations to Dart code. @@ -30,5 +32,7 @@ export class DartTransformer extends MultiTransformer { append(StrictEqualityTransformer); append(ClassTransformer); append(ExportTransformer); + append(ForOfTransformer); + append(DestructuringTransformer); } } diff --git a/tools/transpiler/src/codegeneration/DestructuringTransformer.js b/tools/transpiler/src/codegeneration/DestructuringTransformer.js new file mode 100644 index 0000000000..a6e46e0825 --- /dev/null +++ b/tools/transpiler/src/codegeneration/DestructuringTransformer.js @@ -0,0 +1,41 @@ +import { + COMMA_EXPRESSION, + EXPRESSION_STATEMENT +} from 'traceur/src/syntax/trees/ParseTreeType'; + +import { + DestructuringTransformer as TraceurDestructuringTransformer +} from 'traceur/src/codegeneration/DestructuringTransformer'; + +import { + createExpressionStatement +} from 'traceur/src/codegeneration/ParseTreeFactory'; + +export class DestructuringTransformer extends TraceurDestructuringTransformer { + /** + * Overrides formal parameters to skip processing since they are already handled + * by the NamedParamsTransformer. + */ + transformFormalParameter(tree) { + return tree; + } + + /** + * Converts the comma expressions created by Traceur into multiple statements. + */ + desugarBinding_(bindingTree, statements, declarationType) { + var binding = super.desugarBinding_(bindingTree, statements, declarationType); + for (var i = 0; i < statements.length; i++) { + if (statements[i].type === EXPRESSION_STATEMENT && + statements[i].expression.type === COMMA_EXPRESSION) { + let expression = statements[i].expression; + let expressionStatements = expression.expressions.map(e => { + return createExpressionStatement(e); + }); + + statements.splice(i, 1, ...expressionStatements); + } + } + return binding; + } +} diff --git a/tools/transpiler/src/codegeneration/ForOfTransformer.js b/tools/transpiler/src/codegeneration/ForOfTransformer.js new file mode 100644 index 0000000000..48e3e0891d --- /dev/null +++ b/tools/transpiler/src/codegeneration/ForOfTransformer.js @@ -0,0 +1,16 @@ +import {ParseTreeTransformer} from 'traceur/src/codegeneration/ParseTreeTransformer'; +import {ForInStatement} from 'traceur/src/syntax/trees/ParseTrees'; + +/** + * Transforms for-of into for-in. + */ +export class ForOfTransformer extends ParseTreeTransformer { + /** + * @param {ForOfStatement} tree + * @return {ParseTree} + */ + transformForOfStatement(original) { + var tree = super.transformForOfStatement(original); + return new ForInStatement(tree.location, tree.initializer, tree.collection, tree.body); + } +}