Skip to content
Browse files

Experimenting with using Traceur as a parser for JSHint

  • Loading branch information...
1 parent 94e186d commit 6717030c09d3e163ec8bfeab3fa877aeb53718cf @valueof valueof committed May 13, 2011
Showing with 18,087 additions and 0 deletions.
  1. +12 −0 demo/demo.html
  2. +11 −0 src/jshint.js
  3. +189 −0 src/traceur.js
  4. +315 −0 src/traceur/ClassAnalyzer.js
  5. +70 −0 src/traceur/ErrorReporter.js
  6. +288 −0 src/traceur/FreeVariableChecker.js
  7. +94 −0 src/traceur/ModuleAnalyzer.js
  8. +41 −0 src/traceur/MutedErrorReporter.js
  9. +65 −0 src/traceur/ObjectMap.js
  10. +43 −0 src/traceur/SourcePosition.js
  11. +32 −0 src/traceur/SourceRange.js
  12. +63 −0 src/traceur/StringBuilder.js
  13. +326 −0 src/traceur/VariableBinder.js
  14. +1,216 −0 src/traceur/codegeneration/ParseTreeFactory.js
  15. +1,154 −0 src/traceur/codegeneration/ParseTreeTransformer.js
  16. +1,079 −0 src/traceur/codegeneration/ParseTreeWriter.js
  17. +72 −0 src/traceur/codegeneration/generator/TryState.js
  18. +315 −0 src/traceur/semantics/ClassAnalyzer.js
  19. +288 −0 src/traceur/semantics/FreeVariableChecker.js
  20. +94 −0 src/traceur/semantics/ModuleAnalyzer.js
  21. +326 −0 src/traceur/semantics/VariableBinder.js
  22. +214 −0 src/traceur/semantics/symbols/AggregateSymbol.js
  23. +43 −0 src/traceur/semantics/symbols/ClassSymbol.js
  24. +40 −0 src/traceur/semantics/symbols/ExportSymbol.js
  25. +50 −0 src/traceur/semantics/symbols/FieldSymbol.js
  26. +42 −0 src/traceur/semantics/symbols/GetAccessor.js
  27. +74 −0 src/traceur/semantics/symbols/MemberSymbol.js
  28. +50 −0 src/traceur/semantics/symbols/MethodSymbol.js
  29. +111 −0 src/traceur/semantics/symbols/ModuleSymbol.js
  30. +127 −0 src/traceur/semantics/symbols/Project.js
  31. +54 −0 src/traceur/semantics/symbols/PropertyAccessor.js
  32. +43 −0 src/traceur/semantics/symbols/PropertySymbol.js
  33. +56 −0 src/traceur/semantics/symbols/RequiresSymbol.js
  34. +43 −0 src/traceur/semantics/symbols/SetAccessor.js
  35. +123 −0 src/traceur/semantics/symbols/Symbol.js
  36. +33 −0 src/traceur/semantics/symbols/SymbolType.js
  37. +42 −0 src/traceur/semantics/symbols/TraitSymbol.js
  38. +214 −0 src/traceur/symbols/AggregateSymbol.js
  39. +43 −0 src/traceur/symbols/ClassSymbol.js
  40. +40 −0 src/traceur/symbols/ExportSymbol.js
  41. +50 −0 src/traceur/symbols/FieldSymbol.js
  42. +42 −0 src/traceur/symbols/GetAccessor.js
  43. +74 −0 src/traceur/symbols/MemberSymbol.js
  44. +50 −0 src/traceur/symbols/MethodSymbol.js
  45. +111 −0 src/traceur/symbols/ModuleSymbol.js
  46. +127 −0 src/traceur/symbols/Project.js
  47. +54 −0 src/traceur/symbols/PropertyAccessor.js
  48. +43 −0 src/traceur/symbols/PropertySymbol.js
  49. +56 −0 src/traceur/symbols/RequiresSymbol.js
  50. +43 −0 src/traceur/symbols/SetAccessor.js
  51. +123 −0 src/traceur/symbols/Symbol.js
  52. +33 −0 src/traceur/symbols/SymbolType.js
  53. +42 −0 src/traceur/symbols/TraitSymbol.js
  54. +43 −0 src/traceur/syntax/IdentifierToken.js
  55. +136 −0 src/traceur/syntax/Keywords.js
  56. +133 −0 src/traceur/syntax/LineNumberTable.js
  57. +54 −0 src/traceur/syntax/LiteralToken.js
  58. +942 −0 src/traceur/syntax/ParseTreeValidator.js
  59. +667 −0 src/traceur/syntax/ParseTreeVisitor.js
  60. +3,403 −0 src/traceur/syntax/Parser.js
  61. +103 −0 src/traceur/syntax/PredefinedName.js
  62. +968 −0 src/traceur/syntax/Scanner.js
  63. +40 −0 src/traceur/syntax/SourceFile.js
  64. +61 −0 src/traceur/syntax/Token.js
  65. +144 −0 src/traceur/syntax/TokenType.js
  66. +42 −0 src/traceur/syntax/trees/NullTree.js
  67. +298 −0 src/traceur/syntax/trees/ParseTree.js
  68. +117 −0 src/traceur/syntax/trees/ParseTreeType.js
  69. +965 −0 src/traceur/syntax/trees/ParseTrees.js
  70. +146 −0 src/traceur/syntax/trees/StateMachine.js
  71. +564 −0 src/traceur/traits.js
  72. +70 −0 src/traceur/util/ErrorReporter.js
  73. +41 −0 src/traceur/util/MutedErrorReporter.js
  74. +65 −0 src/traceur/util/ObjectMap.js
  75. +43 −0 src/traceur/util/SourcePosition.js
  76. +32 −0 src/traceur/util/SourceRange.js
  77. +63 −0 src/traceur/util/StringBuilder.js
  78. +564 −0 src/traceur/util/traits.js
View
12 demo/demo.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+
+<html lang="en">
+ <head>
+ <title>JSHint Demo</title>
+ <script src="../src/traceur.js"></script>
+ <script src="../src/jshint.js"></script>
+ </head>
+
+ <body>
+ </body>
+</html>
View
11 src/jshint.js
@@ -0,0 +1,11 @@
+var JSHINT = (function () {
+ 'use strict';
+
+ return function (source) {
+ var sourceFile = new traceur.syntax.SourceFile('jshint', source),
+ parser = new traceur.syntax.Parser({}, sourceFile);
+
+ window.parser = parser;
+ return parser.parseProgram(true);
+ };
+}());
View
189 src/traceur.js
@@ -0,0 +1,189 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var traceur = (function() {
+ 'use strict';
+
+ /**
+ * Builds an object structure for the provided namespace path,
+ * ensuring that names that already exist are not overwritten. For
+ * example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * @param {string} name Name of the object that this file defines.
+ * @private
+ */
+ function exportPath(name) {
+ var parts = name.split('.');
+ var cur = traceur;
+
+ for (var part; parts.length && (part = parts.shift());) {
+ if (part in cur) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+ return cur;
+ };
+
+ /**
+ * @param {string} name
+ * @param {!Function} fun
+ */
+ function define(name, fun) {
+ var obj = exportPath(name);
+ var exports = fun();
+ for (var propertyName in exports) {
+ // Maybe we should check the prototype chain here? The current usage
+ // pattern is always using an object literal so we only care about own
+ // properties.
+ var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
+ propertyName);
+ if (propertyDescriptor)
+ Object.defineProperty(obj, propertyName, propertyDescriptor);
+ }
+ }
+
+ function assert(b) {
+ if (!b)
+ throw Error('Assertion failed');
+ }
+
+ // Cached path to the current script file in an HTML hosting environment.
+ var path;
+
+ // Use comma expression to use global eval.
+ var global = ('global', eval)('this');
+
+ // Allow script before this one to define a global importScript function.
+ var importScript = global.importScript || function(file) {
+ if (!path) {
+ // Find path to this js file
+ var scripts = document.querySelectorAll('script');
+ var src = scripts[scripts.length - 1].src;
+ path = src.substring(0, src.lastIndexOf('/') + 1);
+ path += 'traceur/';
+ }
+
+ document.write('<script src="' + path + file + '"></script>');
+ };
+
+ var uidCounter = 0;
+
+ /**
+ * Returns a new unique ID.
+ * @return {number}
+ */
+ function getUid() {
+ return ++uidCounter;
+ }
+
+ global.traceur = {
+ define: define,
+ assert: assert,
+ getUid: getUid
+ };
+
+ var scripts = [
+ 'util/ObjectMap.js',
+ 'util/SourceRange.js',
+ 'util/SourcePosition.js',
+ 'syntax/Token.js',
+ 'syntax/TokenType.js',
+ 'syntax/LiteralToken.js',
+ 'syntax/IdentifierToken.js',
+ 'syntax/Keywords.js',
+ 'syntax/LineNumberTable.js',
+ 'syntax/SourceFile.js',
+ 'syntax/Scanner.js',
+ 'syntax/PredefinedName.js',
+ 'syntax/trees/ParseTreeType.js',
+ 'syntax/trees/ParseTree.js',
+ 'syntax/trees/NullTree.js',
+ 'syntax/trees/ParseTrees.js',
+ 'util/ErrorReporter.js',
+ 'util/MutedErrorReporter.js',
+ 'syntax/Parser.js',
+ 'syntax/ParseTreeVisitor.js',
+ 'util/StringBuilder.js',
+ 'semantics/VariableBinder.js',
+ 'semantics/symbols/SymbolType.js',
+ 'semantics/symbols/Symbol.js',
+ 'semantics/symbols/MemberSymbol.js',
+ 'semantics/symbols/MethodSymbol.js',
+ 'semantics/symbols/ModuleSymbol.js',
+ 'semantics/symbols/ExportSymbol.js',
+ 'semantics/symbols/FieldSymbol.js',
+ 'semantics/symbols/PropertyAccessor.js',
+ 'semantics/symbols/GetAccessor.js',
+ 'semantics/symbols/SetAccessor.js',
+ 'semantics/symbols/PropertySymbol.js',
+ 'semantics/symbols/AggregateSymbol.js',
+ 'semantics/symbols/ClassSymbol.js',
+ 'semantics/symbols/Project.js',
+ 'semantics/symbols/TraitSymbol.js',
+ 'semantics/symbols/RequiresSymbol.js',
+ 'semantics/ClassAnalyzer.js',
+ 'codegeneration/ParseTreeWriter.js',
+ 'syntax/ParseTreeValidator.js',
+ 'codegeneration/ParseTreeFactory.js',
+ 'codegeneration/ParseTreeTransformer.js',
+/* 'codegeneration/AlphaRenamer.js',
+ 'codegeneration/DestructuringTransformer.js',
+ 'codegeneration/DefaultParametersTransformer.js',
+ 'codegeneration/RestParameterTransformer.js',
+ 'codegeneration/SpreadTransformer.js',
+ 'codegeneration/UniqueIdentifierGenerator.js',
+ 'codegeneration/ForEachTransformer.js',
+ 'codegeneration/ModuleTransformer.js',
+ 'codegeneration/FunctionTransformer.js',
+ 'codegeneration/ClassTransformer.js',
+ 'codegeneration/BlockBindingTransformer.js',
+ 'codegeneration/generator/ForInTransformPass.js',
+ 'codegeneration/generator/State.js',
+ 'codegeneration/generator/FallThroughState.js', */
+ 'codegeneration/generator/TryState.js',
+/* 'codegeneration/generator/BreakState.js',
+ 'codegeneration/generator/CatchState.js',
+ 'codegeneration/generator/ConditionalState.js',
+ 'codegeneration/generator/ContinueState.js',
+ 'codegeneration/generator/EndState.js',
+ 'codegeneration/generator/FinallyFallThroughState.js',
+ 'codegeneration/generator/FinallyState.js',
+ 'codegeneration/generator/SwitchState.js',
+ 'codegeneration/generator/YieldState.js',
+ 'codegeneration/generator/StateAllocator.js', */
+ 'syntax/trees/StateMachine.js',
+/* 'codegeneration/generator/BreakContinueTransformer.js',
+ 'codegeneration/generator/CPSTransformer.js',
+ 'codegeneration/generator/GeneratorTransformer.js',
+ 'codegeneration/generator/AsyncTransformer.js',
+ 'codegeneration/GeneratorTransformPass.js', */
+ 'semantics/FreeVariableChecker.js'
+/* 'codegeneration/ProgramTransformer.js',
+ 'codegeneration/ProjectWriter.js',
+ 'codegeneration/module/ModuleVisitor.js',
+ 'codegeneration/module/ModuleDefinitionVisitor.js',
+ 'codegeneration/module/ExportVisitor.js',
+ 'codegeneration/module/ModuleDeclarationVisitor.js',
+ 'codegeneration/module/ValidationVisitor.js',
+ 'semantics/ModuleAnalyzer.js',
+ 'codegeneration/Compiler.js',
+ 'runtime.js',
+ 'util/traits.js' */
+ ];
+ scripts.forEach(importScript);
+
+ return global.traceur;
+})();
View
315 src/traceur/ClassAnalyzer.js
@@ -0,0 +1,315 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the 'License');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an 'AS IS' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('semantics', function() {
+ 'use strict';
+
+ var PredefinedName = traceur.syntax.PredefinedName;
+ var SourceFile = traceur.syntax.SourceFile;
+ var Token = traceur.syntax.Token;
+ var TokenType = traceur.syntax.TokenType;
+ var ClassDeclaration = traceur.syntax.trees.ClassDeclaration;
+ var ExportDeclaration = traceur.syntax.trees.ExportDeclaration;
+ var FieldDeclaration = traceur.syntax.trees.FieldDeclaration;
+ var FunctionDeclaration = traceur.syntax.trees.FunctionDeclaration;
+ var GetAccessor = traceur.syntax.trees.GetAccessor;
+ var MixinResolve = traceur.syntax.trees.MixinResolve;
+ var Mixin = traceur.syntax.trees.Mixin;
+ var ModuleDefinition = traceur.syntax.trees.ModuleDefinition;
+ var ParseTree = traceur.syntax.trees.ParseTree;
+ var ParseTreeType = traceur.syntax.trees.ParseTreeType;
+ var Program = traceur.syntax.trees.Program;
+ var RequiresMember = traceur.syntax.trees.RequiresMember;
+ var SetAccessor = traceur.syntax.trees.SetAccessor;
+ var TraitDeclaration = traceur.syntax.trees.TraitDeclaration;
+ var VariableDeclaration = traceur.syntax.trees.VariableDeclaration;
+ var VariableStatement = traceur.syntax.trees.VariableStatement;
+ var ErrorReporter = traceur.util.ErrorReporter;
+ var ClassSymbol = traceur.semantics.symbols.ClassSymbol;
+ var TraitSymbol = traceur.semantics.symbols.TraitSymbol;
+ var SymbolType = traceur.semantics.symbols.SymbolType;
+ var MethodSymbol = traceur.semantics.symbols.MethodSymbol;
+ var PropertySymbol = traceur.semantics.symbols.PropertySymbol;
+ var FieldSymbol = traceur.semantics.symbols.FieldSymbol;
+ var AggregateSymbol = traceur.semantics.symbols.AggregateSymbol;
+ var GetAccessor = traceur.semantics.symbols.GetAccessor;
+ var SetAccessor = traceur.semantics.symbols.SetAccessor;
+ var RequiresSymbol = traceur.semantics.symbols.RequiresSymbol;
+
+ /**
+ * Analyzes a class or trait and creates an AggregateSymbol. This class just
+ * collects data for use later by ClassTransformer. The analysis isn't much
+ * more than duplicate member checking--so it could quite possibly be folded
+ * into ClassTransformer.
+ *
+ * @param {ErrorReporter} reporter
+ * @constructor
+ */
+ function ClassAnalyzer(reporter) {
+ this.reporter_ = reporter;
+ }
+
+ /**
+ * Analyzes a class and creates a ClassSymbol
+ * @param {ErrorReporter} reporter
+ * @param {ClassDeclaration} tree
+ * @return {ClassSymbol}
+ */
+ ClassAnalyzer.analyzeClass = function(reporter, tree) {
+ return new ClassAnalyzer(reporter).declareAggregate_(tree, ClassSymbol);
+ }
+
+ /**
+ * Analyzes a trait and creates a TraitSymbol
+ * @param {ErrorReporter} reporter
+ * @param {TraitDeclaration} tree
+ * @return {TraitSymbol}
+ */
+ ClassAnalyzer.analyzeTrait = function(reporter, tree) {
+ return new ClassAnalyzer(reporter).declareAggregate_(tree, TraitSymbol);
+ }
+
+ ClassAnalyzer.prototype = {
+ /**
+ * @param {ClassDeclaration|TraitDeclaration} tree
+ * @param {Function} symbolType
+ * @return {AggregateSymbol}
+ */
+ declareAggregate_: function(tree, symbolType) {
+ var symbol = new symbolType(tree.name.value, tree);
+ this.declareAggregateMembers_(symbol);
+ return symbol;
+ },
+
+ /** @param {AggregateSymbol} symbol */
+ declareAggregateMembers_: function(symbol) {
+ symbol.tree.elements.forEach(function(memberTree) {
+ this.declareAggregateMember_(symbol, memberTree);
+ }, this);
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {ParseTree} memberTree
+ */
+ declareAggregateMember_: function(aggregate, memberTree) {
+ switch (memberTree.type) {
+ case ParseTreeType.FUNCTION_DECLARATION:
+ this.declareFunctionMember_(aggregate, memberTree);
+ break;
+ case ParseTreeType.FIELD_DECLARATION:
+ this.declareFieldMembers_(aggregate, memberTree);
+ break;
+ case ParseTreeType.GET_ACCESSOR:
+ this.declareAccessor_(aggregate, memberTree, 'get', GetAccessor);
+ break;
+ case ParseTreeType.SET_ACCESSOR:
+ this.declareAccessor_(aggregate, memberTree, 'set', SetAccessor);
+ break;
+ case ParseTreeType.MIXIN:
+ aggregate.mixins.push(memberTree);
+ break;
+ case ParseTreeType.REQUIRES_MEMBER:
+ this.declareRequiresMember_(aggregate, memberTree);
+ break;
+ default:
+ throw new Error('Unrecognized parse tree in class declaration:' + memberTree.type);
+ }
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {RequiresMember} tree
+ */
+ declareRequiresMember_: function(aggregate, tree) {
+ var name = tree.name.value;
+ if (!this.checkForDuplicateMemberDeclaration_(aggregate, tree, name, false)) {
+ new RequiresSymbol(tree, name, aggregate);
+ }
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {FieldDeclaration} tree
+ */
+ declareFieldMembers_: function(aggregate, tree) {
+ tree.declarations.forEach(function(declarationTree) {
+ this.declareFieldMember_(aggregate, tree, declarationTree);
+ }, this);
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {FieldDeclaration} field
+ * @param {VariableDeclaration} tree
+ */
+ declareFieldMember_: function(aggregate, field, tree) {
+ var name = null;
+ switch (tree.lvalue.type) {
+ case ParseTreeType.IDENTIFIER_EXPRESSION:
+ name = tree.lvalue.identifierToken.value;
+ break;
+ default:
+ // TODO: Should destructuring be allowed in a field declaration?
+ this.reportError_(tree, 'Cannot use destructuring in a field declaration');
+ break;
+ }
+ if (PredefinedName.NEW == name) {
+ this.reportError_(tree, 'Cannot name a field "new"');
+ return;
+ }
+ if (!this.checkForDuplicateMemberDeclaration_(aggregate, tree, name, field.isStatic)) {
+ new FieldSymbol(field, tree, name, aggregate);
+ }
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {string} kind
+ * @param {Function} ctor
+ * @param {ParseTree} tree
+ */
+ declareAccessor_: function(aggregate, tree, kind, ctor) {
+ var name = this.getPropertyName_(tree, tree.propertyName);
+ if (name == null) {
+ return;
+ }
+ var property = this.getOrCreateProperty_(aggregate, name, tree, tree.isStatic);
+ if (property == null) {
+ return;
+ }
+ if (property[kind] != null) {
+ this.reportError_(tree, 'Duplicate "%s" accessor "%s"', kind, name);
+ this.reportRelatedError_(property[kind].tree);
+ return;
+ }
+ property[kind] = new ctor(property, tree);
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {string} name
+ * @param {ParseTree} tree
+ * @param {boolean} isStatic
+ * @return {PropertySymbol}
+ */
+ getOrCreateProperty_: function(aggregate, name, tree, isStatic) {
+ if (isStatic && !aggregate.hasStaticMember(name) ||
+ !isStatic && !aggregate.hasInstanceMember(name)) {
+ return new PropertySymbol(tree, name, aggregate, isStatic);
+ }
+ var member = isStatic ? aggregate.getStaticMember(name) : aggregate.getInstanceMember(name);
+ if (member.type != SymbolType.PROPERTY) {
+ this.reportDuplicateMember_(aggregate, tree, name);
+ return null;
+ }
+ return member;
+ },
+
+ /**
+ * @param {ParseTree} tree
+ * @param {Token} propertyName
+ * @return {string}
+ */
+ getPropertyName_: function(tree, propertyName) {
+ var name;
+ switch (propertyName.type) {
+ case TokenType.IDENTIFIER:
+ name = propertyName.value;
+ break;
+ case TokenType.STRING:
+ name = propertyName.value;
+ break;
+ case TokenType.NUMBER:
+ throw new Error('TODO: Property with numeric names');
+ default:
+ throw new Error('Unexpected property name type');
+ }
+ if (name == PredefinedName.NEW) {
+ this.reportError_(tree, 'Cannot name a property "new"');
+ return null;
+ }
+ return name;
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {FunctionDeclaration} tree
+ * @return {MethodSymbol}
+ */
+ declareFunctionMember_: function(aggregate, tree) {
+ // TODO: validate super constructor call
+ var name = tree.name.value;
+ if (!this.checkForDuplicateMemberDeclaration_(aggregate, tree, name, tree.isStatic)) {
+ return new MethodSymbol(tree, name, aggregate, tree.isStatic);
+ }
+ return null;
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {ParseTree} tree
+ * @param {string} name
+ * @param {boolean} isStatic
+ * @return {boolean}
+ */
+ checkForDuplicateMemberDeclaration_: function(aggregate, tree, name, isStatic) {
+ if (isStatic && aggregate.hasStaticMember(name) ||
+ !isStatic && aggregate.hasInstanceMember(name)) {
+ this.reportDuplicateMember_(aggregate, tree, name);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * @param {AggregateSymbol} aggregate
+ * @param {ParseTree} tree
+ * @param {string} name
+ */
+ reportDuplicateMember_: function(aggregate, tree, name) {
+ this.reportError_(tree, 'Duplicate member "%s"', name);
+ this.reportRelatedError_(aggregate.getInstanceMember(name));
+ },
+
+ /**
+ * @param {Symbol|ParseTree} treeOrSymbol
+ * @param {string} format
+ * @param {...Object} var_args
+ */
+ reportError_: function(treeOrSymbol, format, var_args) {
+ if (treeOrSymbol instanceof Symbol) {
+ treeOrSymbol = treeOrSymbol.tree;
+ }
+ var args = Array.prototype.slice.call(arguments, 2);
+ this.reporter_.reportError(treeOrSymbol.location.start, format, args);
+ },
+
+ /** @param {Symbol|ParseTree} treeOrSymbol*/
+ reportRelatedError_: function(treeOrSymbol) {
+ var msg = 'Location related to previous error';
+ if (treeOrSymbol instanceof Symbol) {
+ symbol.getRelatedLocations().forEach(function(loc) {
+ this.reportError_(loc, msg);
+ }, this);
+ } else {
+ this.reportError_(tree, msg);
+ }
+ }
+ };
+
+ return {
+ ClassAnalyzer: ClassAnalyzer
+ };
+});
View
70 src/traceur/ErrorReporter.js
@@ -0,0 +1,70 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ /**
+ * A conduit for reporting errors and warnings to the user using the Firebug
+ * console API.
+ */
+ function ErrorReporter() {}
+
+ ErrorReporter.prototype = {
+ hadError_: false,
+
+ /**
+ * @param {traceur.util.SourcePosition} location
+ * @param {string} format
+ */
+ reportError: function(location, format, var_args) {
+ this.hadError_ = true;
+ var args = Array.prototype.slice.call(arguments, 2);
+ this.reportMessageInternal(location, 'error', format, args);
+ },
+
+ /**
+ * @param {traceur.util.SourcePosition} location
+ * @param {string} format
+ */
+ reportWarning: function(location, format, var_args) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ this.reportMessageInternal(location, 'warn', format, args);
+ },
+
+ /**
+ * @param {traceur.util.SourcePosition} location
+ * @param {string} kind
+ * @param {string} format
+ * @param {Array} args
+ */
+ reportMessageInternal: function(location, kind, format, args) {
+ if (location)
+ format = location + ': ' + format;
+ console[kind].apply(console, [format].concat(args));
+ },
+
+ hadError: function() {
+ return this.hadError_;
+ },
+
+ clearError: function() {
+ this.hadError_ = false;
+ }
+ };
+
+ return {
+ ErrorReporter: ErrorReporter
+ };
+});
View
288 src/traceur/FreeVariableChecker.js
@@ -0,0 +1,288 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var global = this;
+traceur.define('semantics', function() {
+ 'use strict';
+
+ var TokenType = traceur.syntax.TokenType;
+ var ParseTreeVisitor = traceur.syntax.ParseTreeVisitor;
+ var IdentifierExpression = traceur.syntax.trees.IdentifierExpression;
+ var IdentifierToken = traceur.syntax.IdentifierToken;
+ var ParseTreeType = traceur.syntax.trees.ParseTreeType;
+ var SourcePosition = traceur.syntax.SourcePosition;
+ var PredefinedName = traceur.syntax.PredefinedName;
+
+ /**
+ * Finds the identifiers that are not bound in a program. Run this after all
+ * module imports have been resolved.
+ *
+ * This is run after all transformations to simplify the analysis. In
+ * particular we can ignore:
+ * - module imports
+ * - block scope (let/const)
+ * - foreach
+ * - generators
+ * - destructuring/rest
+ * - classes/traits
+ * as all of these nodes will have been replaced. We assume that synthetic
+ * variables (generated by Traceur) will bind correctly, so we don't worry
+ * about binding them as well as user defined variables.
+ *
+ * @param {ErrorReporter} reporter
+ * @extends {ParseTreeVisitor}
+ * @constructor
+ */
+ function FreeVariableChecker(reporter) {
+ ParseTreeVisitor.call(this);
+ this.reporter_ = reporter;
+ }
+
+ /**
+ * Represents the link in the scope chain.
+ * @param {Scope} parent The parent scope, or null if top level scope.
+ * @constructor
+ */
+ function Scope(parent) {
+ this.parent = parent;
+ this.references = Object.create(null);
+ this.declarations = Object.create(null);
+ }
+
+ /**
+ * Gets the name of an identifier expression or token
+ * @param {IdentifierExpression|IdentifierToken|string} name
+ * @returns {string}
+ */
+ function getVariableName(name) {
+ if (name instanceof IdentifierExpression) {
+ name = name.identifierToken;
+ }
+ if (name instanceof IdentifierToken) {
+ name = name.value;
+ }
+ return name;
+ }
+
+ function getIdentifier(tree) {
+ while (tree.type == ParseTreeType.PAREN_EXPRESSION) {
+ tree = tree.expression;
+ }
+ if (tree.type == ParseTreeType.IDENTIFIER_EXPRESSION) {
+ return tree;
+ }
+ return null;
+ }
+
+ /**
+ * Checks the program for free variables, and reports an error when it
+ * encounters any.
+ *
+ * @param {ErrorReporter} reporter
+ * @param {Program} tree
+ */
+ FreeVariableChecker.checkProgram = function(reporter, tree) {
+ new FreeVariableChecker(reporter).visitProgram(tree, global);
+ }
+
+ var proto = ParseTreeVisitor.prototype;
+ FreeVariableChecker.prototype = {
+ __proto__: proto,
+
+ /** Current scope (block, program) */
+ scope_: null,
+
+ /**
+ * Pushes a scope.
+ * @return {Scope}
+ */
+ pushScope_: function() {
+ return this.scope_ = new Scope(this.scope_);
+ },
+
+ /**
+ * Pops scope, tracks proper matching of push_/pop_ operations.
+ * @param {Scope} scope
+ */
+ pop_: function(scope) {
+ if (this.scope_ != scope) {
+ throw new Error('FreeVariableChecker scope mismatch');
+ }
+
+ this.validateScope_();
+
+ this.scope_ = scope.parent;
+ },
+
+ visitBlock: function(tree) {
+ // block scope was already dealt with
+ this.visitStatements_(tree.statements);
+ },
+
+ visitProgram: function(tree, global) {
+ var scope = this.pushScope_();
+
+ // Declare variables from the global scope.
+ // TODO(jmesserly): this should be done through the module loaders, and by
+ // providing the user the option to import URLs like '@dom', but for now
+ // just bind against everything in the global scope.
+ var object = global;
+ while (object) {
+ Object.getOwnPropertyNames(object).forEach(this.declareVariable_, this);
+ object = Object.getPrototypeOf(object);
+ }
+
+ this.visitStatements_(tree.programElements);
+
+ this.pop_(scope);
+ },
+
+ visitStatements_: function(statements) {
+ statements.forEach(function(s) {
+ if (s.type == ParseTreeType.FUNCTION_DECLARATION) {
+ // Declare the function's name in the outer scope.
+ // We need to do this here, and not inside visitFunctionDeclaration,
+ // because function expressions shouldn't have their names added. Only
+ // in statement contexts does this happen.
+ this.declareVariable_(s.name);
+ }
+ this.visitAny(s);
+ }, this);
+ },
+
+ visitFunctionDeclaration: function(tree) {
+ var scope = this.pushScope_();
+
+ // Declare the function name, 'arguments' and formal parameters inside the
+ // function
+ this.declareVariable_(tree.name);
+ this.declareVariable_(PredefinedName.ARGUMENTS);
+ tree.formalParameterList.parameters.forEach(this.declareVariable_, this);
+
+ this.visitAny(tree.functionBody);
+
+ this.pop_(scope);
+ },
+
+ visitGetAccessor: function(tree) {
+ var scope = this.pushScope_();
+
+ this.visitAny(tree.body);
+
+ this.pop_(scope);
+ },
+
+ visitSetAccessor: function(tree) {
+ var scope = this.pushScope_();
+
+ this.declareVariable_(tree.parameter);
+ this.visitAny(tree.body);
+
+ this.pop_(scope);
+ },
+
+ visitCatch: function(tree) {
+ var scope = this.pushScope_();
+
+ this.declareVariable_(tree.exceptionName);
+
+ this.visitAny(tree.catchBody);
+
+ this.pop_(scope);
+ },
+
+ visitVariableDeclarationList: function(tree) {
+ if (tree.declarationType != TokenType.VAR) {
+ throw new Error('let and const should have been rewritten');
+ }
+
+ tree.declarations.forEach(function(d) {
+ this.declareVariable_(d.lvalue);
+ this.visitAny(d.initializer);
+ }, this);
+ },
+
+ visitIdentifierExpression: function(tree) {
+ var name = getVariableName(tree);
+ var scope = this.scope_;
+ if (!(name in scope.references)) {
+ scope.references[name] = tree.location;
+ }
+ },
+
+ declareVariable_: function(tree) {
+ var name = getVariableName(tree);
+ if (name) {
+ var scope = this.scope_;
+ if (!(name in scope.declarations)) {
+ scope.declarations[name] = tree.location;
+ }
+ }
+ },
+
+ /**
+ * Once we've visited the body of a scope, we check that all variables were
+ * declared. If they haven't been, we promote the references to the parent
+ * scope (because ES can close over variables, as well as reference them
+ * before declaration).
+ *
+ * At the top level scope we issue errors for any remaining free variables.
+ */
+ validateScope_: function() {
+ var scope = this.scope_;
+
+ // Promote any unresolved references to the parent scope.
+ var errors = [];
+ for (var name in scope.references) {
+ if (!(name in scope.declarations)) {
+ var location = scope.references[name];
+ if (!scope.parent) {
+ if (!location) {
+ // If location is null, it means we're getting errors from code we
+ // generated. This is an internal error.
+ throw new Error('generated variable ' + name + ' is not defined');
+ }
+
+ // If we're at the top level scope, then issue an error for
+ // remaining free variables.
+ errors.push([location.start, '%s is not defined', name]);
+ } else if (!(name in scope.parent.references)) {
+ scope.parent.references[name] = location;
+ }
+ }
+ }
+
+ if (errors.length) {
+ // Issue errors in source order.
+ errors.sort(function(x, y) { return x[0].offset - y[0].offset; });
+ errors.forEach(function(e) { this.reportError_.apply(this, e); }, this);
+ }
+ },
+
+ /**
+ * @param {SourcePosition} start location
+ * @param {string} format
+ * @param {...Object} var_args
+ */
+ reportError_: function(location, format, var_args) {
+ var args = Array.prototype.slice.call(arguments);
+ args[0] = location;
+ this.reporter_.reportError.apply(this.reporter_, args);
+ }
+ };
+
+ return {
+ FreeVariableChecker: FreeVariableChecker
+ };
+});
View
94 src/traceur/ModuleAnalyzer.js
@@ -0,0 +1,94 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('semantics', function() {
+ 'use strict';
+
+ var ModuleDefinitionVisitor = traceur.codegeneration.module.ModuleDefinitionVisitor;
+ var ExportVisitor = traceur.codegeneration.module.ExportVisitor;
+ var ModuleDeclarationVisitor = traceur.codegeneration.module.ModuleDeclarationVisitor;
+ var ValidationVisitor = traceur.codegeneration.module.ValidationVisitor;
+
+ // TODO(arv): import
+ // TODO(arv): Validate that there are no free variables
+ // TODO(arv): Validate that the exported reference exists
+
+ /**
+ * Builds up all module symbols and validates them.
+ *
+ * @param {ErrorReporter} reporter
+ * @param {Project} project
+ * @constructor
+ */
+ function ModuleAnalyzer(reporter, project) {
+ this.reporter_ = reporter;
+ this.project_ = project;
+ }
+
+ ModuleAnalyzer.prototype = {
+ /**
+ * @return {void}
+ */
+ analyze: function() {
+ var root = this.project_.getRootModule();
+ var visitor = new ModuleDefinitionVisitor(this.reporter_, root);
+ this.project_.getSourceTrees().forEach(visitor.visitAny, visitor);
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ExportVisitor(this.reporter_, root);
+ this.project_.getSourceTrees().forEach(visitor.visitAny, visitor);
+ }
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ModuleDeclarationVisitor(this.reporter_, root);
+ this.project_.getSourceTrees().forEach(visitor.visitAny, visitor);
+ }
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ValidationVisitor(this.reporter_, root);
+ this.project_.getSourceTrees().forEach(visitor.visitAny, visitor);
+ }
+ },
+
+ /**
+ * @param {SourceFile} sourceFile
+ * @return {void}
+ */
+ analyzeFile: function(sourceFile) {
+ var root = this.project_.getRootModule();
+ var visitor = new ModuleDefinitionVisitor(this.reporter_, root);
+ var tree = this.project_.getParseTree(sourceFile);
+ visitor.visitAny(tree);
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ExportVisitor(this.reporter_, root);
+ visitor.visitAny(tree);
+ }
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ModuleDeclarationVisitor(this.reporter_, root);
+ visitor.visitAny(tree);
+ }
+
+ if (!this.reporter_.hadError()) {
+ visitor = new ValidationVisitor(this.reporter_, root);
+ visitor.visitAny(tree);
+ }
+ }
+ };
+
+ return {
+ ModuleAnalyzer: ModuleAnalyzer
+ };
+});
View
41 src/traceur/MutedErrorReporter.js
@@ -0,0 +1,41 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ var ErrorReporter = traceur.util.ErrorReporter;
+
+ /**
+ * An error reporter that doesn't output errors; it just records
+ * whether an error occurred.
+ *
+ * <p>{@code MutedErrorReporter} instances are used by the parser to
+ * observe whether speculative parses fail before committing to
+ * parsing them.
+ */
+ function MutedErrorReporter() {}
+
+ MutedErrorReporter.prototype = {
+ __proto__: ErrorReporter.prototype,
+
+ reportMessageInternal: function(location, message) {
+ // message.dropOn(floor);
+ }
+ };
+
+ return {
+ MutedErrorReporter: MutedErrorReporter
+ };
+});
View
65 src/traceur/ObjectMap.js
@@ -0,0 +1,65 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ /**
+ * A simple O(1) object map. It requires that the key object have a
+ * {@code uid} property.
+ */
+ function ObjectMap() {
+ this.keys_ = Object.create(null);
+ this.values_ = Object.create(null);
+ }
+
+ ObjectMap.prototype = {
+ put: function(key, value) {
+ var uid = key.uid;
+ this.keys_[uid] = key;
+ this.values_[uid] = value;
+ },
+ get: function(key) {
+ return this.values_[key.uid];
+ },
+ containsKey: function(key) {
+ return key.uid in this.keys_;
+ },
+ addAll: function(other) {
+ for (var uid in other.keys_) {
+ this.keys_[uid] = other.keys_[uid];
+ this.values_[uid] = other.values_[uid];
+ }
+ },
+ keys: function() {
+ return Object.keys(this.keys_).map(function(uid) {
+ return this.keys_[uid];
+ }, this);
+ },
+ values: function() {
+ return Object.keys(this.values_).map(function(uid) {
+ return this.values_[uid];
+ }, this);
+ },
+ remove: function(key) {
+ var uid = key.uid;
+ delete this.keys_[uid];
+ delete this.values_[uid];
+ }
+ };
+
+ return {
+ ObjectMap: ObjectMap
+ };
+});
View
43 src/traceur/SourcePosition.js
@@ -0,0 +1,43 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ /**
+ * A position in a source string - includes offset, line and column.
+ * @param {SourceFile} source
+ * @param {number} offset
+ * @param {number} line
+ * @param {number} column
+ * @constructor
+ */
+ function SourcePosition(source, offset, line, column) {
+ this.source = source;
+ this.offset = offset;
+ this.line = line;
+ this.column = column;
+ }
+
+ SourcePosition.prototype = {
+ toString: function() {
+ return (this.source ? this.source.name : '') +
+ '(' + (this.line + 1) + ', ' + (this.column + 1) + ')';
+ }
+ };
+
+ return {
+ SourcePosition: SourcePosition
+ };
+});
View
32 src/traceur/SourceRange.js
@@ -0,0 +1,32 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ /**
+ * A range of positions in a source string.
+ * @param {SourcePosition} start Start is inclusive.
+ * @param {SourcePosition} end End is exclusive.
+ * @constructor
+ */
+ function SourceRange(start, end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ return {
+ SourceRange: SourceRange
+ };
+});
View
63 src/traceur/StringBuilder.js
@@ -0,0 +1,63 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('util', function() {
+ 'use strict';
+
+ /**
+ * Builds strings by appending them.
+ * @constructor
+ */
+ function StringBuilder() {
+ this.strings_ = [];
+ this.length = 0;
+ }
+
+ StringBuilder.prototype = {
+ append: function(str) {
+ str = str.toString();
+ this.length += str.length;
+ this.strings_.push(str);
+ return this;
+ },
+
+ toString: function() {
+ return this.strings_.join('');
+ },
+
+ // Instead of supporting charAt and deleteCharAt, implement lastChar and
+ // deleteLastChar. These can be implemented in constant time with no
+ // additional data structures
+
+ lastChar: function() {
+ var last = this.strings_[this.strings_.length - 1];
+ if (last) {
+ last = last[last.length - 1];
+ }
+ return last;
+ },
+
+ deleteLastChar: function() {
+ var lastString = this.strings_.length - 1;
+ var last = this.strings_[lastString];
+ if (last) {
+ this.strings_[lastString] = last.substring(0, last.length - 1);
+ }
+ }
+ };
+
+ return {
+ StringBuilder: StringBuilder
+ };
+});
View
326 src/traceur/VariableBinder.js
@@ -0,0 +1,326 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('semantics', function() {
+ 'use strict';
+
+ var IdentifierToken = traceur.syntax.IdentifierToken;
+ var ParseTreeType = traceur.syntax.ParseTreeType;
+ var ParseTreeVisitor = traceur.syntax.ParseTreeVisitor;
+ var TokenType = traceur.syntax.TokenType;
+ var Block = traceur.syntax.trees.Block;
+ var Catch = traceur.syntax.trees.Catch;
+ var ForInStatement = traceur.syntax.trees.ForInStatement;
+ var ForStatement = traceur.syntax.trees.ForStatement;
+ var FunctionDeclaration = traceur.syntax.trees.FunctionDeclaration;
+ var ObjectPatternField = traceur.syntax.trees.ObjectPatternField;
+ var ParseTree = traceur.syntax.trees.ParseTree;
+ var ParseTreeType = traceur.syntax.trees.ParseTreeType;
+ var VariableDeclarationList = traceur.syntax.trees.VariableDeclarationList;
+ var VariableDeclaration = traceur.syntax.trees.VariableDeclaration;
+
+ /**
+ * Finds the identifiers that are bound in a given scope. Identifiers
+ * can be bound by function declarations, formal parameter lists,
+ * variable declarations, and catch headers.
+ * @param {boolean} inFunctionScope
+ * @param {Block=} scope
+ * @extends {ParseTreeVisitor}
+ * @constructor
+ */
+ function VariableBinder(includeFunctionScope, scope) {
+ ParseTreeVisitor.call(this);
+
+ // Should we include:
+ // * all "var" declarations
+ // * all block scoped declarations occurring in the top level function block.
+ this.includeFunctionScope_ = includeFunctionScope;
+
+ // Block within which we are looking for declarations:
+ // * block scoped declaration occurring in this block.
+ // If function != null this refers to the top level function block.
+ this.scope_ = scope || null;
+
+ // Block currently being processed
+ this.block_ = null;
+
+ this.identifiers_ = Object.create(null);
+ }
+
+ // TODO: Add entry more entry points:
+ // for..in statment
+ // for statement
+
+ /**
+ * Gets the identifiers bound in {@code tree}. The tree should be a block
+ * statement. This means if {@code tree} is:
+ *
+ * <pre>
+ * { function f(x) { var y; } }
+ * </pre>
+ *
+ * Then only {@code "f"} is bound; {@code "x"} and {@code "y"} are bound in
+ * the separate lexical scope of {@code f}. Note that only const/let bound
+ * variables (such as {@code "f"} in this example) are returned. Variables
+ * declared with "var" are only returned when {@code includeFunctionScope} is
+ * set to true.
+ *
+ * If {@code tree} was instead:
+ * <pre>
+ * { var z = function f(x) { var y; }; }
+ * </pre>
+ *
+ * Then only {@code "z"} is bound
+ *
+ * @param {Block} tree
+ * @param {boolean=} includeFunctionScope
+ * @return {Object}
+ */
+ VariableBinder.variablesInBlock = function(tree,
+ includeFunctionScope) {
+ var binder = new VariableBinder(includeFunctionScope, tree);
+ binder.visitAny(tree);
+ return binder.identifiers_;
+ };
+
+ /**
+ * Gets the identifiers bound in the context of a function,
+ * {@code tree}, other than the function name itself. For example, if
+ * {@code tree} is:
+ *
+ * <pre>
+ * function f(x) { var y; f(); }
+ * </pre>
+ *
+ * Then a set containing only {@code "x"} and {@code "y"} is returned. Note
+ * that we treat {@code "f"} as free in the body of {@code f}, because
+ * AlphaRenamer uses this fact to determine if the function name is shadowed
+ * by another name in the body of the function.
+ *
+ * <p>Only identifiers that are bound <em>throughout</em> the
+ * specified tree are returned, for example:
+ *
+ * <pre>
+ * function f() {
+ * try {
+ * } catch (x) {
+ * function g(y) { }
+ * }
+ * }
+ * </pre>
+ *
+ * Reports nothing as being bound, because {@code "x"} is only bound in the
+ * scope of the catch block; {@code "g"} is let bound to the catch block, and
+ * {@code "y"} is only bound in the scope of {@code g}.
+ *
+ * <p>{@code "arguments"} is only reported as bound if it is
+ * explicitly bound in the function. If it is not explicitly bound,
+ * {@code "arguments"} is implicitly bound during function
+ * invocation.
+ *
+ * @param {FunctionDeclaration} tree
+ * @return {Object}
+ */
+ VariableBinder.variablesInFunction = function(tree) {
+ var binder = new VariableBinder(true, tree.functionBody);
+ binder.bindVariablesInFunction_(tree);
+ return binder.identifiers_;
+ };
+
+ var proto = ParseTreeVisitor.prototype;
+ VariableBinder.prototype = {
+ __proto__: proto,
+
+ /** @param {FunctionDeclaration} tree */
+ bindVariablesInFunction_: function(tree) {
+ var parameters = tree.formalParameterList.parameters;
+ for (var i = 0; i < parameters.length; i++) {
+ this.bindParameter_(parameters[i]);
+ }
+ this.visitAny(tree.functionBody);
+ },
+
+ /** @param {Block} tree */
+ visitBlock: function(tree) {
+ // Save and set current block
+ var parentBlock = this.block_;
+ this.block_ = tree;
+
+ // visit the statements
+ tree.statements.forEach(function(s) {
+ if (s.type == ParseTreeType.FUNCTION_DECLARATION) {
+ this.bindFunctionDeclaration_(s);
+ } else {
+ this.visitAny(s);
+ }
+ }, this);
+
+ // restore current block
+ this.block_ = parentBlock;
+ },
+
+ /** @param {FunctionDeclaration} tree */
+ bindFunctionDeclaration_: function(tree) {
+ // functions follow the binding rules of 'let'
+ if (tree.name != null && this.block_ == this.scope_) {
+ this.bind_(tree.name);
+ }
+ // We don't recurse into function bodies, because they create
+ // their own lexical scope.
+ },
+
+ /** @param {FunctionDeclaration} tree */
+ visitFunctionDeclaration: function(tree) {
+ // We don't recurse into function bodies, because they create
+ // their own lexical scope.
+ },
+
+ /** @param {ForEachStatement} tree */
+ visitForEachStatement: function(tree) {
+ throw new Error('foreach statements should be transformed before this pass');
+ },
+
+ /** @param {ForInStatement} tree */
+ visitForInStatement: function(tree) {
+ if (tree.initializer != null &&
+ tree.initializer.type == ParseTreeType.VARIABLE_DECLARATION_LIST) {
+ this.visitForDeclarations_(tree.initializer);
+ } else {
+ this.visitAny(tree.initializer);
+ }
+
+ // visit the rest of the for..in statement
+ this.visitAny(tree.collection);
+ this.visitAny(tree.body);
+ },
+
+ /** @param {ForStatement} tree */
+ visitForStatement: function(tree) {
+ if (tree.initializer != null &&
+ tree.initializer.type == ParseTreeType.VARIABLE_DECLARATION_LIST) {
+ this.visitForDeclarations_(tree.initializer);
+ } else {
+ this.visitAny(tree.initializer);
+ }
+
+ // visit the rest of the for statement
+ this.visitAny(tree.condition);
+ this.visitAny(tree.increment);
+ this.visitAny(tree.body);
+ },
+
+ /** @param {VariableDeclarationList} declarations */
+ visitForDeclarations_: function(declarations) {
+ if (declarations.declarationType == TokenType.VAR) {
+ this.visitAny(declarations);
+ } else {
+ // let and const declare in the nested scope, not the outer scope
+ // so we need to bypass them (but walk the initializers)
+ var decls = declarations.declarations;
+ for (var i = 0; i < decls.length; i++) {
+ // skipping lvalue, visit only initializer
+ this.visitAny(decls[i].initializer);
+ }
+ }
+ },
+
+ /** @param {VariableDeclarationList} tree */
+ visitVariableDeclarationList: function(tree) {
+ // "var" variables are bound if we are scanning the whole function only
+ // "let/const" are bound if (we are scanning block scope or function) AND
+ // the scope currently processed is the scope we care about
+ // (either the block scope being scanned or the top level function scope)
+ if ((tree.declarationType == TokenType.VAR && this.includeFunctionScope_) ||
+ (tree.declarationType != TokenType.VAR && this.block_ == this.scope_)) {
+ // declare the variables
+ proto.visitVariableDeclarationList.call(this, tree);
+ } else {
+ // skipping let/const declarations in nested blocks
+ var decls = tree.declarations;
+ for (var i = 0; i < decls.length; i++) {
+ this.visitAny(decls[i].initializer);
+ }
+ }
+ },
+
+ /** @param {VariableDeclaration} tree */
+ visitVariableDeclaration: function(tree) {
+ this.bindVariableDeclaration_(tree.lvalue);
+ proto.visitVariableDeclaration.call(this, tree);
+ },
+
+ /** @param {IdentifierToken} identifier */
+ bind_: function(identifier) {
+ this.identifiers_[identifier.value] = true;
+ },
+
+ /** @param {ParseTree} parameter */
+ bindParameter_: function(parameter) {
+ if (parameter.isRestParameter()) {
+ this.bind_(parameter.identifier);
+ } else {
+ // Formal parameters are otherwise like variable
+ // declarations--identifier expressions and patterns
+ this.bindVariableDeclaration_(parameter);
+ }
+ },
+
+ /** @param {ParseTree} parameter */
+ bindVariableDeclaration_: function(tree) {
+ switch (tree.type) {
+ case ParseTreeType.IDENTIFIER_EXPRESSION:
+ this.bind_(tree.identifierToken);
+ break;
+
+ case ParseTreeType.ARRAY_PATTERN:
+ var i = tree.elements;
+ for (var i = 0; i < elements.length; i++) {
+ this.bindVariableDeclaration_(elements[i]);
+ }
+ break;
+
+ case ParseTreeType.SPREAD_PATTERN_ELEMENT:
+ this.bindVariableDeclaration_(tree.lvalue);
+ break;
+
+ case ParseTreeType.OBJECT_PATTERN:
+ var fields = tree.fields;
+ for (var i = 0; i < fields.length; i++) {
+ this.bindVariableDeclaration_(fields[i]);
+ }
+ break;
+
+ case ParseTreeType.OBJECT_PATTERN_FIELD:
+ var field = tree;
+ if (field.element == null) {
+ this.bind_(field.identifier);
+ } else {
+ this.bindVariableDeclaration_(field.element);
+ }
+ break;
+
+ case ParseTreeType.PAREN_EXPRESSION:
+ this.bindVariableDeclaration_(tree.expression);
+ break;
+
+ default:
+ throw new Error('unreachable');
+ }
+ }
+ };
+
+ return {
+ VariableBinder: VariableBinder
+ };
+});
View
1,216 src/traceur/codegeneration/ParseTreeFactory.js
@@ -0,0 +1,1216 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the 'License');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an 'AS IS' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+traceur.define('codegeneration', function() {
+ 'use strict';
+
+ var IdentifierToken = traceur.syntax.IdentifierToken;
+ var LiteralToken = traceur.syntax.LiteralToken;
+ var ParseTreeType = traceur.syntax.ParseTreeType;
+ var PredefinedName = traceur.syntax.PredefinedName;
+ var Token = traceur.syntax.Token;
+ var TokenType = traceur.syntax.TokenType;
+
+ var ParseTree = traceur.syntax.trees.ParseTree;
+ var ParseTreeType = traceur.syntax.trees.ParseTreeType;
+
+ var ArgumentList = traceur.syntax.trees.ArgumentList;
+ var ArrayLiteralExpression = traceur.syntax.trees.ArrayLiteralExpression;
+ var ArrayPattern = traceur.syntax.trees.ArrayPattern;
+ var BinaryOperator = traceur.syntax.trees.BinaryOperator;
+ var Block = traceur.syntax.trees.Block;
+ var BreakStatement = traceur.syntax.trees.BreakStatement;
+ var CallExpression = traceur.syntax.trees.CallExpression;
+ var CaseClause = traceur.syntax.trees.CaseClause;
+ var Catch = traceur.syntax.trees.Catch;
+ var ClassDeclaration = traceur.syntax.trees.ClassDeclaration;
+ var CommaExpression = traceur.syntax.trees.CommaExpression;
+ var ConditionalExpression = traceur.syntax.trees.ConditionalExpression;
+ var ContinueStatement = traceur.syntax.trees.ContinueStatement;
+ var DefaultClause = traceur.syntax.trees.DefaultClause;
+ var DefaultParameter = traceur.syntax.trees.DefaultParameter;
+ var DoWhileStatement = traceur.syntax.trees.DoWhileStatement;
+ var EmptyStatement = traceur.syntax.trees.EmptyStatement;
+ var ExpressionStatement = traceur.syntax.trees.ExpressionStatement;
+ var FieldDeclaration = traceur.syntax.trees.FieldDeclaration;
+ var Finally = traceur.syntax.trees.Finally;
+ var ForEachStatement = traceur.syntax.trees.ForEachStatement;
+ var ForInStatement = traceur.syntax.trees.ForInStatement;
+ var ForStatement = traceur.syntax.trees.ForStatement;
+ var FormalParameterList = traceur.syntax.trees.FormalParameterList;
+ var FunctionDeclaration = traceur.syntax.trees.FunctionDeclaration;
+ var GetAccessor = traceur.syntax.trees.GetAccessor;
+ var IdentifierExpression = traceur.syntax.trees.IdentifierExpression;
+ var IfStatement = traceur.syntax.trees.IfStatement;
+ var LabelledStatement = traceur.syntax.trees.LabelledStatement;
+ var LiteralExpression = traceur.syntax.trees.LiteralExpression;
+ var MemberExpression = traceur.syntax.trees.MemberExpression;
+ var MemberLookupExpression = traceur.syntax.trees.MemberLookupExpression;
+ var Mixin = traceur.syntax.trees.Mixin;
+ var MixinResolveList = traceur.syntax.trees.MixinResolveList;
+ var NewExpression = traceur.syntax.trees.NewExpression;
+ var ObjectLiteralExpression = traceur.syntax.trees.ObjectLiteralExpression;
+ var ObjectPattern = traceur.syntax.trees.ObjectPattern;
+ var ObjectPatternField = traceur.syntax.trees.ObjectPatternField;
+ var ParenExpression = traceur.syntax.trees.ParenExpression;
+ var PostfixExpression = traceur.syntax.trees.PostfixExpression;
+ var Program = traceur.syntax.trees.Program;
+ var PropertyNameAssignment = traceur.syntax.trees.PropertyNameAssignment;
+ var RestParameter = traceur.syntax.trees.RestParameter;
+ var ReturnStatement = traceur.syntax.trees.ReturnStatement;
+ var YieldStatement = traceur.syntax.trees.YieldStatement;
+ var SetAccessor = traceur.syntax.trees.SetAccessor;
+ var SpreadExpression = traceur.syntax.trees.SpreadExpression;
+ var SpreadPatternElement = traceur.syntax.trees.SpreadPatternElement;
+ var SwitchStatement = traceur.syntax.trees.SwitchStatement;
+ var ThisExpression = traceur.syntax.trees.ThisExpression;
+ var ThrowStatement = traceur.syntax.trees.ThrowStatement;
+ var TraitDeclaration = traceur.syntax.trees.TraitDeclaration;
+ var TryStatement = traceur.syntax.trees.TryStatement;
+ var UnaryExpression = traceur.syntax.trees.UnaryExpression;
+ var VariableDeclarationList = traceur.syntax.trees.VariableDeclarationList;
+ var VariableDeclaration = traceur.syntax.trees.VariableDeclaration;
+ var VariableStatement = traceur.syntax.trees.VariableStatement;
+ var WhileStatement = traceur.syntax.trees.WhileStatement;
+ var WithStatement = traceur.syntax.trees.WithStatement;
+
+ // Helpers so we can use these on Arguments objects.
+ var slice = Array.prototype.slice.call.bind(Array.prototype.slice);
+ var map = Array.prototype.map.call.bind(Array.prototype.map);
+
+ // Tokens
+
+ /**
+ * @param {TokenType} operator
+ * @return {Token}
+ */
+ function createOperatorToken(operator) {
+ return new Token(operator, null);
+ }
+
+ /**
+ * @param {string} identifier
+ * @return {IdentifierToken}
+ */
+ function createIdentifierToken(identifier) {
+ return new IdentifierToken(null, identifier);
+ }
+
+ /**
+ * @param {string} propertyName
+ * @return {Token}
+ */
+ function createPropertyNameToken(propertyName) {
+ // TODO: properties with non identifier names
+ return createIdentifierToken(propertyName);
+ }
+
+ function createStringLiteralToken(value) {
+ // TODO: escape string literal token
+ return new LiteralToken(TokenType.STRING, '"' + value + '"', null);
+ }
+
+ function createBooleanLiteralToken(value) {
+ return new Token(value ? TokenType.TRUE : TokenType.FALSE, null);
+ }
+
+ function createNullLiteralToken() {
+ return new LiteralToken(TokenType.NULL, 'null', null);
+ }
+
+
+ function createNumberLiteralToken(value) {
+ return new LiteralToken(TokenType.NUMBER, String(value), null);
+ }
+
+ // Token lists
+
+ /**
+ * @return {Array.<string>}
+ */
+ function createEmptyParameters() {
+ return [];
+ }
+
+ /**
+ * @param {IdentifierToken|FormalParameterList} parameter
+ * @return {Array.<string>}
+ */
+ function createParameters(parameter) {
+ if (parameter instanceof IdentifierToken)
+ return [parameter.value];
+
+ var builder = [];
+
+ parameter.parameters.forEach(function(parameter) {
+ if (!parameter.isRestParameter()) {
+ // TODO: array and object patterns
+ builder.push(parameter.identifierToken.value);
+ }
+ });
+
+ return builder;
+ }
+
+ /**
+ * Either creates an array from the arguments, or if the first argument is an
+ * array, creates a new array with its elements followed by the other
+ * arguments.
+ *
+ * TODO(jmesserly): this API is a bit goofy. Can we replace it with something
+ * simpler? In most use cases, square brackets could replace calls to this.
+ *
+ * @param {Array.<ParseTree>|ParseTree} statementsOrHead
+ * @param {...ParseTree} var_args
+ * @return {Array.<ParseTree>}
+ */
+ function createStatementList(statementsOrHead, var_args) {
+ if (statementsOrHead instanceof Array) {
+ var result = statementsOrHead.slice();
+ result.push.apply(result, slice(arguments, 1));
+ return result;
+ }
+ return slice(arguments);
+ }
+
+ /**
+ * TODO(arv): Make this less overloaded.
+ *
+ * @param {string|number|IdentifierToken|Array.<string>} arg0
+ * @param {...string} var_args
+ * @return {FormalParameterList}
+ */
+ function createParameterList(arg0, var_args) {
+ if (typeof arg0 == 'string') {
+ // var_args of strings
+ var parameterList = map(arguments, createIdentifierExpression);
+ return new FormalParameterList(null, parameterList);
+ }
+
+ if (typeof arg0 == 'number')
+ return createParameterListHelper(arg0, false);
+
+ if (arg0 instanceof IdentifierToken) {
+ return new FormalParameterList(
+ null, [createIdentifierExpression(arg0)]);
+ }
+
+ // Array.<string>
+ var builder = arg0.map(createIdentifierExpression);
+ return new FormalParameterList(null, builder);
+ }
+
+ /**
+ * Helper for building parameter lists with and without rest params.
+ * @param {number} numberOfParameters
+ * @param {boolean} hasRestParams
+ * @return {FormalParameterList}
+ */
+ function createParameterListHelper(numberOfParameters, hasRestParams) {
+ var builder = [];
+
+ for (var index = 0; index < numberOfParameters; index++) {
+ var parameterName = PredefinedName.getParameterName(index);
+ var isRestParameter = index == numberOfParameters - 1 && hasRestParams;
+ builder.push(
+ isRestParameter ?
+ createRestParameter(parameterName) :
+ createIdentifierExpression(parameterName));
+ }
+
+ return new FormalParameterList(null, builder);
+ }
+
+ /**
+ * @param {number} numberOfParameters
+ * @return {FormalParameterList}
+ */
+ function createParameterListWithRestParams(numberOfParameters) {
+ return createParameterListHelper(numberOfParameters, true);
+ }
+
+ /**
+ * Creates an expression that refers to the {@code index}-th
+ * parameter by its predefined name.
+ *
+ * @see PredefinedName#getParameterName
+ *
+ * @param {number} index
+ * @return {IdentifierExpression}
+ */
+ function createParameterReference(index) {
+ return createIdentifierExpression(PredefinedName.getParameterName(index));
+ }
+
+ /**
+ * @return {FormalParameterList}
+ */
+ function createEmptyParameterList() {
+ return new FormalParameterList(null, []);
+ }
+
+ // Tree Lists
+
+ function createEmptyList() {
+ // TODO(arv): Remove
+ return [];
+ }
+
+ // Trees
+
+ /**
+ * @param {Array.<ParseTree>|ParseTree|number} numberListOrFirst
+ * @param {...ParseTree} var_args
+ * @return {ArgumentList}
+ */
+ function createArgumentList(numberListOrFirst, var_args) {
+ if (typeof numberListOrFirst == 'number') {
+ return createArgumentListFromParameterList(
+ createParameterList(numberListOrFirst));
+ }
+
+ var list;
+ if (numberListOrFirst instanceof Array)
+ list = numberListOrFirst;
+ else
+ list = slice(arguments);
+
+ return new ArgumentList(null, list);
+ }
+
+ /**
+ * @param {FormalParameterList} formalParameterList
+ * @return {ArgumentList}
+ */
+ function createArgumentListFromParameterList(formalParameterList) {
+ var builder = formalParameterList.parameters.map(function(parameter) {
+ if (parameter.isRestParameter()) {
+ return createSpreadExpression(
+ createIdentifierExpression(
+ parameter.identifier));
+ } else {
+ // TODO: implement pattern -> array, object literal translation
+ return parameter;
+ }
+ });
+
+ return new ArgumentList(null, builder);
+ }
+
+ /**
+ * @return {ArgumentList}
+ */
+ function createEmptyArgumentList() {
+ return new ArgumentList(null, createEmptyList());
+ }
+
+ /**
+ * @param {Array.<ParseTree>} list
+ * @return {ArrayLiteralExpression}
+ */
+ function createArrayLiteralExpression(list) {
+ return new ArrayLiteralExpression(null, list);
+ }
+
+ /**
+ * @return {ArrayLiteralExpression}
+ */
+ function createEmptyArrayLiteralExpression() {
+ return createArrayLiteralExpression(createEmptyList());
+ }
+
+ /**
+ * @param {Array.<ParseTree>} list
+ * @return {ArrayPattern}
+ */
+ function createArrayPattern(list) {
+ return new ArrayPattern(null, list);
+ }
+
+ /**
+ * @param {ParseTree} lhs
+ * @param {ParseTree} rhs
+ * @return {BinaryOperator}
+ */
+ function createAssignmentExpression(lhs, rhs) {
+ return new BinaryOperator(null, lhs,
+ createOperatorToken(TokenType.EQUAL), rhs);
+ }
+
+ /**
+ * @return {BinaryOperator}
+ */
+ function createBinaryOperator(left, operator, right) {
+ return new BinaryOperator(null, left, operator, right);
+ }
+
+ /**
+ * @return {EmptyStatement}
+ */
+ function createEmptyStatement() {
+ return new EmptyStatement(null);
+ }
+
+ /**
+ * @return {Block}
+ */
+ function createEmptyBlock() {
+ return createBlock(createEmptyList());
+ }
+
+ /**
+ * @param {Array.<ParseTree>|ParseTree} statements
+ * @param {...ParseTree} var_args
+ * @return {Block}
+ */
+ function createBlock(statements) {
+ if (statements instanceof ParseTree)
+ statements = slice(arguments);
+ return new Block(null, statements);
+ }
+
+ /**
+ * @param {Array.<ParseTree>|ParseTree} statements
+ * @param {...ParseTree} var_args
+ * @return {ParseTree}
+ */
+ function createScopedStatements(statements) {
+ if (statements instanceof ParseTree)
+ statements = slice(arguments);
+ return createScopedBlock(createBlock(statements));
+ }
+
+ /**
+ * @param {Block} block
+ * @return {ParseTree}
+ */
+ function createScopedBlock(block) {
+ return createExpressionStatement(createScopedExpression(block));
+ }
+
+ /**
+ * @param {Block} block
+ * @return {CallExpression}
+ */
+ function createScopedExpression(block) {
+ return createCallCall(
+ createParenExpression(
+ createFunctionExpression(createEmptyParameterList(), block)),
+ createThisExpression());
+ }
+
+ /**
+ * @param {ParseTree} operand
+ * @param {ArgumentList=} opt_args
+ * @return {CallExpression}
+ */
+ function createCallExpression(operand, opt_args) {
+ var args = opt_args || createEmptyArgumentList();
+ return new CallExpression(null, operand, args);
+ }
+
+ /**
+ * @param {ParseTree} func
+ * @param {ParseTree} thisTree
+ * @return {CallExpression}
+ */
+ function createBoundCall(func, thisTree) {
+ return createCallExpression(
+ createMemberExpression(
+ func.type == ParseTreeType.FUNCTION_DECLARATION ?
+ createParenExpression(func) :
+ func,
+ PredefinedName.BIND),
+ createArgumentList(thisTree));
+ }
+
+ /**
+ * @param {string} aggregateName
+ * @param {string} propertyName
+ * @return {CallExpression}
+ */
+ function createLookupGetter(aggregateName, propertyName) {
+ // TODO(arv): Use ES5 method instead of relying on propriatary extensions.
+ return createCallExpression(
+ createMemberExpression(
+ aggregateName,
+ PredefinedName.PROTOTYPE,
+ PredefinedName.LOOKUP_GETTER),
+ createArgumentList(createStringLiteral(propertyName)));
+ }
+
+ /**
+ * @return {BreakStatement}
+ */
+ function createBreakStatement() {
+ return new BreakStatement(null, null);
+ }
+
+ // function.call(this, arguments)
+ /**
+ * @param {ParseTree} func
+ * @param {ParseTree} thisExpression
+ * @param {ParseTree|Array.<ParseTree>} args
+ * @param {...ParseTree} var_args
+ * @return {CallExpression}
+ */
+ function createCallCall(func, thisExpression, args, var_args) {
+ if (args instanceof ParseTree)
+ args = slice(arguments, 2);
+
+ var builder = [];
+
+ builder.push(thisExpression);
+ builder.push.apply(builder, args);
+
+ return createCallExpression(
+ createMemberExpression(func, PredefinedName.CALL),
+ createArgumentList(builder));
+ }
+
+ /**
+ * @param {ParseTree} func
+ * @param {ParseTree} thisExpression
+ * @param {...ParseTree} var_args
+ * @return {ParseTree}
+ */
+ function createCallCallStatement(func, thisExpression, var_args) {
+ var args = slice(arguments, 2);
+ return createExpressionStatement(
+ createCallCall(func, thisExpression, args));
+ }
+
+ /**
+ * @param {ParseTree} expression
+ * @param {Array.<ParseTree>} statements
+ * @return {CaseClause}
+ */
+ function createCaseClause(expression, statements) {
+ return new CaseClause(null, expression, statements);
+ }
+
+ /**
+ * @param {IdentifierToken} exceptionName
+ * @param {ParseTree} catchBody
+ * @return {Catch}
+ */
+ function createCatch(exceptionName, catchBody) {
+ return new Catch(null, exceptionName, catchBody);
+ }
+
+ /**
+ * @param {IdentifierToken} name
+ * @param {ParseTree} superClass
+ * @param {Array.<ParseTree>} elements
+ * @return {ClassDeclaration}
+ */
+ function createClassDeclaration(name, superClass, elements) {
+ return new ClassDeclaration(null, name, superClass, elements);
+ }
+
+ /**
+ * @param {Array.<ParseTree>} expressions
+ * @return {CommaExpression}
+ */
+ function createCommaExpression(expressions) {
+ return new CommaExpression(null, expressions);
+ }
+
+ /**
+ * @param {ParseTree} condition
+ * @param {ParseTree} left
+ * @param {ParseTree} right
+ * @return {ConditionalExpression}
+ */
+ function createConditionalExpression(condition, left, right) {
+ return new ConditionalExpression(null, condition, left, right);
+ }
+
+ /**
+ * @return {ContinueStatement}
+ */
+ function createContinueStatement() {
+ return new ContinueStatement(null, null);
+ }
+
+ /**
+ * @param {Array.<ParseTree>} statements
+ * @return {DefaultClause}
+ */
+ function createDefaultClause(statements) {
+ return new DefaultClause(null, statements);
+ }
+
+ /**
+ * @param {IdentifierExpression} identifier
+ * @param {ParseTree} expression
+ * @return {DefaultParameter}
+ */
+ function createDefaultParameter(identifier, expression) {
+ return new DefaultParameter(null, identifier, expression);
+ }
+
+ /**
+ * @param {ParseTree} body
+ * @param {ParseTree} condition
+ * @return {DoWhileStatement}
+ */
+ function createDoWhileStatement(body, condition) {
+ return new DoWhileStatement(null, body, condition);
+ }
+
+ /**
+ * @param {ParseTree} lhs
+ * @param {ParseTree} rhs
+ * @return {ExpressionStatement}
+ */
+ function createAssignmentStatement(lhs, rhs) {
+ return createExpressionStatement(createAssignmentExpression(lhs, rhs));
+ }
+
+ /**
+ * @param {ParseTree} operand
+ * @param {ArgumentList=} opt_args
+ * @return {ExpressionStatement}
+ */
+ function createCallStatement(operand, opt_args) {
+ if (opt_args) {
+ return createExpressionStatement(
+ createCallExpression(operand, opt_args));
+ }
+ return createExpressionStatement(createCallExpression(operand));
+ }
+
+ /**
+ * @param {ParseTree} expression
+ * @return {ExpressionStatement}
+ */
+ function createExpressionStatement(expression) {
+ return new ExpressionStatement(null, expression);
+ }
+
+ /**
+ * @param {boolean} isStatic
+ * @param {boolean} isConst
+ * @param {Array.<VariableDeclaration} expression
+ * @return {FieldDeclaration}
+ */
+ function createFieldDeclaration(isStatic, isConst, declarations) {
+ return new FieldDeclaration(null, isStatic, isConst, declarations);
+ }
+
+ /**
+ * @param {ParseTree} block
+ * @return {Finally}
+ */
+ function createFinally(block) {
+ return new Finally(null, block);
+ }
+
+ /**
+ * @param {VariableDeclarationList} initializer
+ * @param {ParseTree} collection
+ * @param {ParseTree} body
+ * @return {ForEachStatement}
+ */
+ function createForEachStatement(initializer, collection, body) {
+ return new ForEachStatement(null, initializer, collection, body);
+ }
+
+ /**
+ * @param {ParseTree} initializer
+ * @param {ParseTree} collection
+ * @param {ParseTree} body
+ * @return {ForInStatement}
+ */
+ function createForInStatement(initializer, collection, body) {
+ return new ForInStatement(null, initializer, collection, body);
+ }
+
+ /**
+ * @param {ParseTree} variables
+ * @param {ParseTree} condition
+ * @param {ParseTree} increment
+ * @param {ParseTree} body
+ * @return {ForStatement}
+ */
+ function createForStatement(variables, condition, increment, body) {
+ return new ForStatement(null, variables, condition, increment, body);
+ }
+
+ /**
+ * @param {Array.<string>|FormalParameterList} formalParameterList
+ * @param {Block} functionBody
+ * @return {FunctionDeclaration}
+ */
+ function createFunctionExpressionFormals(formalParameters, functionBody) {
+ if (formalParameters instanceof Array)
+ formalParameters = createParameterList(formalParameters);
+ return new FunctionDeclaration(null, null, false, formalParameters,
+ functionBody);
+ }
+
+ /**
+ * @param {string|IdentifierToken} name
+ * @param {FormalParameterList} formalParameterList
+ * @param {Block} functionBody
+ * @return {FunctionDeclaration}
+ */
+