1 /***********************************************************************
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
6 -------------------------------- (C) ---------------------------------
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
12 Distributed under the BSD license:
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 ***********************************************************************/
46 function DEFNODE(type, props, methods, base) {
47 if (arguments.length < 4) base = AST_Node;
48 if (!props) props = [];
49 else props = props.split(/\s+/);
50 var self_props = props;
51 if (base && base.PROPS)
52 props = props.concat(base.PROPS);
53 var code = "return function AST_" + type + "(props){ if (props) { ";
54 for (var i = props.length; --i >= 0;) {
55 code += "this." + props[i] + " = props." + props[i] + ";";
57 var proto = base && new base;
58 if (proto && proto.initialize || (methods && methods.initialize))
59 code += "this.initialize();";
61 var ctor = new Function(code)();
63 ctor.prototype = proto;
66 if (base) base.SUBCLASSES.push(ctor);
67 ctor.prototype.CTOR = ctor;
68 ctor.PROPS = props || null;
69 ctor.SELF_PROPS = self_props;
72 ctor.prototype.TYPE = ctor.TYPE = type;
74 if (methods) for (i in methods) if (HOP(methods, i)) {
76 ctor[i.substr(1)] = methods[i];
78 ctor.prototype[i] = methods[i];
81 ctor.DEFMETHOD = function(name, method) {
82 this.prototype[name] = method;
84 if (typeof exports !== "undefined") {
85 exports["AST_" + type] = ctor;
90 var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
93 var AST_Node = DEFNODE("Node", "start end", {
95 return new this.CTOR(this);
97 $documentation: "Base class of all AST nodes",
99 start: "[AST_Token] The first token of this node",
100 end: "[AST_Token] The last token of this node"
102 _walk: function(visitor) {
103 return visitor._visit(this);
105 walk: function(visitor) {
106 return this._walk(visitor); // not sure the indirection will be any help
110 AST_Node.warn_function = null;
111 AST_Node.warn = function(txt, props) {
112 if (AST_Node.warn_function)
113 AST_Node.warn_function(string_template(txt, props));
116 /* -----[ statements ]----- */
118 var AST_Statement = DEFNODE("Statement", null, {
119 $documentation: "Base class of all statements",
122 var AST_Debugger = DEFNODE("Debugger", null, {
123 $documentation: "Represents a debugger statement",
126 var AST_Directive = DEFNODE("Directive", "value scope quote", {
127 $documentation: "Represents a directive, like \"use strict\";",
129 value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
130 scope: "[AST_Scope/S] The scope that this directive affects",
131 quote: "[string] the original quote character"
135 var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
136 $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
138 body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
140 _walk: function(visitor) {
141 return visitor._visit(this, function(){
142 this.body._walk(visitor);
147 function walk_body(node, visitor) {
148 var body = node.body;
149 if (body instanceof AST_Statement) {
152 else for (var i = 0, len = body.length; i < len; i++) {
153 body[i]._walk(visitor);
157 var AST_Block = DEFNODE("Block", "body", {
158 $documentation: "A body of statements (usually bracketed)",
160 body: "[AST_Statement*] an array of statements"
162 _walk: function(visitor) {
163 return visitor._visit(this, function(){
164 walk_body(this, visitor);
169 var AST_BlockStatement = DEFNODE("BlockStatement", null, {
170 $documentation: "A block statement",
173 var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
174 $documentation: "The empty statement (empty block or simply a semicolon)",
175 _walk: function(visitor) {
176 return visitor._visit(this);
180 var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
181 $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
183 body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
185 _walk: function(visitor) {
186 return visitor._visit(this, function(){
187 this.body._walk(visitor);
192 var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
193 $documentation: "Statement with a label",
195 label: "[AST_Label] a label definition"
197 _walk: function(visitor) {
198 return visitor._visit(this, function(){
199 this.label._walk(visitor);
200 this.body._walk(visitor);
203 }, AST_StatementWithBody);
205 var AST_IterationStatement = DEFNODE("IterationStatement", null, {
206 $documentation: "Internal class. All loops inherit from it."
207 }, AST_StatementWithBody);
209 var AST_DWLoop = DEFNODE("DWLoop", "condition", {
210 $documentation: "Base class for do/while statements",
212 condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
214 }, AST_IterationStatement);
216 var AST_Do = DEFNODE("Do", null, {
217 $documentation: "A `do` statement",
218 _walk: function(visitor) {
219 return visitor._visit(this, function(){
220 this.body._walk(visitor);
221 this.condition._walk(visitor);
226 var AST_While = DEFNODE("While", null, {
227 $documentation: "A `while` statement",
228 _walk: function(visitor) {
229 return visitor._visit(this, function(){
230 this.condition._walk(visitor);
231 this.body._walk(visitor);
236 var AST_For = DEFNODE("For", "init condition step", {
237 $documentation: "A `for` statement",
239 init: "[AST_Node?] the `for` initialization code, or null if empty",
240 condition: "[AST_Node?] the `for` termination clause, or null if empty",
241 step: "[AST_Node?] the `for` update clause, or null if empty"
243 _walk: function(visitor) {
244 return visitor._visit(this, function(){
245 if (this.init) this.init._walk(visitor);
246 if (this.condition) this.condition._walk(visitor);
247 if (this.step) this.step._walk(visitor);
248 this.body._walk(visitor);
251 }, AST_IterationStatement);
253 var AST_ForIn = DEFNODE("ForIn", "init name object", {
254 $documentation: "A `for ... in` statement",
256 init: "[AST_Node] the `for/in` initialization code",
257 name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
258 object: "[AST_Node] the object that we're looping through"
260 _walk: function(visitor) {
261 return visitor._visit(this, function(){
262 this.init._walk(visitor);
263 this.object._walk(visitor);
264 this.body._walk(visitor);
267 }, AST_IterationStatement);
269 var AST_With = DEFNODE("With", "expression", {
270 $documentation: "A `with` statement",
272 expression: "[AST_Node] the `with` expression"
274 _walk: function(visitor) {
275 return visitor._visit(this, function(){
276 this.expression._walk(visitor);
277 this.body._walk(visitor);
280 }, AST_StatementWithBody);
282 /* -----[ scope and functions ]----- */
284 var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
285 $documentation: "Base class for all statements introducing a lexical scope",
287 directives: "[string*/S] an array of directives declared in this scope",
288 variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
289 functions: "[Object/S] like `variables`, but only lists function declarations",
290 uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
291 uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
292 parent_scope: "[AST_Scope?/S] link to the parent scope",
293 enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
294 cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
298 var AST_Toplevel = DEFNODE("Toplevel", "globals", {
299 $documentation: "The toplevel scope",
301 globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
303 wrap_enclose: function(arg_parameter_pairs) {
308 arg_parameter_pairs.forEach(function(pair) {
309 var splitAt = pair.lastIndexOf(":");
311 args.push(pair.substr(0, splitAt));
312 parameters.push(pair.substr(splitAt + 1));
315 var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
316 wrapped_tl = parse(wrapped_tl);
317 wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
318 if (node instanceof AST_Directive && node.value == "$ORIG") {
319 return MAP.splice(self.body);
324 wrap_commonjs: function(name, export_all) {
328 self.figure_out_scope();
329 self.walk(new TreeWalker(function(node){
330 if (node instanceof AST_SymbolDeclaration && node.definition().global) {
331 if (!find_if(function(n){ return n.name == node.name }, to_export))
332 to_export.push(node);
336 var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
337 wrapped_tl = parse(wrapped_tl);
338 wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
339 if (node instanceof AST_Directive) {
340 switch (node.value) {
342 return MAP.splice(self.body);
345 to_export.forEach(function(sym){
346 body.push(new AST_SimpleStatement({
347 body: new AST_Assign({
349 expression: new AST_SymbolRef({ name: "exports" }),
350 property: new AST_String({ value: sym.name }),
353 right: new AST_SymbolRef(sym),
357 return MAP.splice(body);
365 var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
366 $documentation: "Base class for functions",
368 name: "[AST_SymbolDeclaration?] the name of this function",
369 argnames: "[AST_SymbolFunarg*] array of function arguments",
370 uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
372 _walk: function(visitor) {
373 return visitor._visit(this, function(){
374 if (this.name) this.name._walk(visitor);
375 var argnames = this.argnames;
376 for (var i = 0, len = argnames.length; i < len; i++) {
377 argnames[i]._walk(visitor);
379 walk_body(this, visitor);
384 var AST_Accessor = DEFNODE("Accessor", null, {
385 $documentation: "A setter/getter function. The `name` property is always null."
388 var AST_Function = DEFNODE("Function", null, {
389 $documentation: "A function expression"
392 var AST_Defun = DEFNODE("Defun", null, {
393 $documentation: "A function definition"
396 /* -----[ JUMPS ]----- */
398 var AST_Jump = DEFNODE("Jump", null, {
399 $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
402 var AST_Exit = DEFNODE("Exit", "value", {
403 $documentation: "Base class for “exits” (`return` and `throw`)",
405 value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
407 _walk: function(visitor) {
408 return visitor._visit(this, this.value && function(){
409 this.value._walk(visitor);
414 var AST_Return = DEFNODE("Return", null, {
415 $documentation: "A `return` statement"
418 var AST_Throw = DEFNODE("Throw", null, {
419 $documentation: "A `throw` statement"
422 var AST_LoopControl = DEFNODE("LoopControl", "label", {
423 $documentation: "Base class for loop control statements (`break` and `continue`)",
425 label: "[AST_LabelRef?] the label, or null if none",
427 _walk: function(visitor) {
428 return visitor._visit(this, this.label && function(){
429 this.label._walk(visitor);
434 var AST_Break = DEFNODE("Break", null, {
435 $documentation: "A `break` statement"
438 var AST_Continue = DEFNODE("Continue", null, {
439 $documentation: "A `continue` statement"
442 /* -----[ IF ]----- */
444 var AST_If = DEFNODE("If", "condition alternative", {
445 $documentation: "A `if` statement",
447 condition: "[AST_Node] the `if` condition",
448 alternative: "[AST_Statement?] the `else` part, or null if not present"
450 _walk: function(visitor) {
451 return visitor._visit(this, function(){
452 this.condition._walk(visitor);
453 this.body._walk(visitor);
454 if (this.alternative) this.alternative._walk(visitor);
457 }, AST_StatementWithBody);
459 /* -----[ SWITCH ]----- */
461 var AST_Switch = DEFNODE("Switch", "expression", {
462 $documentation: "A `switch` statement",
464 expression: "[AST_Node] the `switch` “discriminant”"
466 _walk: function(visitor) {
467 return visitor._visit(this, function(){
468 this.expression._walk(visitor);
469 walk_body(this, visitor);
474 var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
475 $documentation: "Base class for `switch` branches",
478 var AST_Default = DEFNODE("Default", null, {
479 $documentation: "A `default` switch branch",
480 }, AST_SwitchBranch);
482 var AST_Case = DEFNODE("Case", "expression", {
483 $documentation: "A `case` switch branch",
485 expression: "[AST_Node] the `case` expression"
487 _walk: function(visitor) {
488 return visitor._visit(this, function(){
489 this.expression._walk(visitor);
490 walk_body(this, visitor);
493 }, AST_SwitchBranch);
495 /* -----[ EXCEPTIONS ]----- */
497 var AST_Try = DEFNODE("Try", "bcatch bfinally", {
498 $documentation: "A `try` statement",
500 bcatch: "[AST_Catch?] the catch block, or null if not present",
501 bfinally: "[AST_Finally?] the finally block, or null if not present"
503 _walk: function(visitor) {
504 return visitor._visit(this, function(){
505 walk_body(this, visitor);
506 if (this.bcatch) this.bcatch._walk(visitor);
507 if (this.bfinally) this.bfinally._walk(visitor);
512 var AST_Catch = DEFNODE("Catch", "argname", {
513 $documentation: "A `catch` node; only makes sense as part of a `try` statement",
515 argname: "[AST_SymbolCatch] symbol for the exception"
517 _walk: function(visitor) {
518 return visitor._visit(this, function(){
519 this.argname._walk(visitor);
520 walk_body(this, visitor);
525 var AST_Finally = DEFNODE("Finally", null, {
526 $documentation: "A `finally` node; only makes sense as part of a `try` statement"
529 /* -----[ VAR/CONST ]----- */
531 var AST_Definitions = DEFNODE("Definitions", "definitions", {
532 $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
534 definitions: "[AST_VarDef*] array of variable definitions"
536 _walk: function(visitor) {
537 return visitor._visit(this, function(){
538 var definitions = this.definitions;
539 for (var i = 0, len = definitions.length; i < len; i++) {
540 definitions[i]._walk(visitor);
546 var AST_Var = DEFNODE("Var", null, {
547 $documentation: "A `var` statement"
550 var AST_Const = DEFNODE("Const", null, {
551 $documentation: "A `const` statement"
554 var AST_VarDef = DEFNODE("VarDef", "name value", {
555 $documentation: "A variable declaration; only appears in a AST_Definitions node",
557 name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
558 value: "[AST_Node?] initializer, or null of there's no initializer"
560 _walk: function(visitor) {
561 return visitor._visit(this, function(){
562 this.name._walk(visitor);
563 if (this.value) this.value._walk(visitor);
568 /* -----[ OTHER ]----- */
570 var AST_Call = DEFNODE("Call", "expression args", {
571 $documentation: "A function call expression",
573 expression: "[AST_Node] expression to invoke as function",
574 args: "[AST_Node*] array of arguments"
576 _walk: function(visitor) {
577 return visitor._visit(this, function(){
578 this.expression._walk(visitor);
579 var args = this.args;
580 for (var i = 0, len = args.length; i < len; i++) {
581 args[i]._walk(visitor);
587 var AST_New = DEFNODE("New", null, {
588 $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
591 var AST_Seq = DEFNODE("Seq", "car cdr", {
592 $documentation: "A sequence expression (two comma-separated expressions)",
594 car: "[AST_Node] first element in sequence",
595 cdr: "[AST_Node] second element in sequence"
597 $cons: function(x, y) {
598 var seq = new AST_Seq(x);
603 $from_array: function(array) {
604 if (array.length == 0) return null;
605 if (array.length == 1) return array[0].clone();
607 for (var i = array.length; --i >= 0;) {
608 list = AST_Seq.cons(array[i], list);
612 if (p.cdr && !p.cdr.cdr) {
620 to_array: function() {
621 var p = this, a = [];
624 if (p.cdr && !(p.cdr instanceof AST_Seq)) {
632 add: function(node) {
635 if (!(p.cdr instanceof AST_Seq)) {
636 var cell = AST_Seq.cons(p.cdr, node);
643 if (this.cdr instanceof AST_Seq) {
644 return this.cdr.len() + 1;
649 _walk: function(visitor) {
650 return visitor._visit(this, function(){
651 this.car._walk(visitor);
652 if (this.cdr) this.cdr._walk(visitor);
657 var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
658 $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
660 expression: "[AST_Node] the “container” expression",
661 property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
665 var AST_Dot = DEFNODE("Dot", null, {
666 $documentation: "A dotted property access expression",
667 _walk: function(visitor) {
668 return visitor._visit(this, function(){
669 this.expression._walk(visitor);
674 var AST_Sub = DEFNODE("Sub", null, {
675 $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
676 _walk: function(visitor) {
677 return visitor._visit(this, function(){
678 this.expression._walk(visitor);
679 this.property._walk(visitor);
684 var AST_Unary = DEFNODE("Unary", "operator expression", {
685 $documentation: "Base class for unary expressions",
687 operator: "[string] the operator",
688 expression: "[AST_Node] expression that this unary operator applies to"
690 _walk: function(visitor) {
691 return visitor._visit(this, function(){
692 this.expression._walk(visitor);
697 var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
698 $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
701 var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
702 $documentation: "Unary postfix expression, i.e. `i++`"
705 var AST_Binary = DEFNODE("Binary", "left operator right", {
706 $documentation: "Binary expression, i.e. `a + b`",
708 left: "[AST_Node] left-hand side expression",
709 operator: "[string] the operator",
710 right: "[AST_Node] right-hand side expression"
712 _walk: function(visitor) {
713 return visitor._visit(this, function(){
714 this.left._walk(visitor);
715 this.right._walk(visitor);
720 var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
721 $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
723 condition: "[AST_Node]",
724 consequent: "[AST_Node]",
725 alternative: "[AST_Node]"
727 _walk: function(visitor) {
728 return visitor._visit(this, function(){
729 this.condition._walk(visitor);
730 this.consequent._walk(visitor);
731 this.alternative._walk(visitor);
736 var AST_Assign = DEFNODE("Assign", null, {
737 $documentation: "An assignment expression — `a = b + 5`",
740 /* -----[ LITERALS ]----- */
742 var AST_Array = DEFNODE("Array", "elements", {
743 $documentation: "An array literal",
745 elements: "[AST_Node*] array of elements"
747 _walk: function(visitor) {
748 return visitor._visit(this, function(){
749 var elements = this.elements;
750 for (var i = 0, len = elements.length; i < len; i++) {
751 elements[i]._walk(visitor);
757 var AST_Object = DEFNODE("Object", "properties", {
758 $documentation: "An object literal",
760 properties: "[AST_ObjectProperty*] array of properties"
762 _walk: function(visitor) {
763 return visitor._visit(this, function(){
764 var properties = this.properties;
765 for (var i = 0, len = properties.length; i < len; i++) {
766 properties[i]._walk(visitor);
772 var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
773 $documentation: "Base class for literal object properties",
775 key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
776 value: "[AST_Node] property value. For setters and getters this is an AST_Function."
778 _walk: function(visitor) {
779 return visitor._visit(this, function(){
780 this.value._walk(visitor);
785 var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
786 $documentation: "A key: value object property",
788 quote: "[string] the original quote character"
790 }, AST_ObjectProperty);
792 var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
793 $documentation: "An object setter property",
794 }, AST_ObjectProperty);
796 var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
797 $documentation: "An object getter property",
798 }, AST_ObjectProperty);
800 var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
802 name: "[string] name of this symbol",
803 scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
804 thedef: "[SymbolDef/S] the definition of this symbol"
806 $documentation: "Base class for all symbols",
809 var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
810 $documentation: "The name of a property accessor (setter/getter function)"
813 var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
814 $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
817 var AST_SymbolVar = DEFNODE("SymbolVar", null, {
818 $documentation: "Symbol defining a variable",
819 }, AST_SymbolDeclaration);
821 var AST_SymbolConst = DEFNODE("SymbolConst", null, {
822 $documentation: "A constant declaration"
823 }, AST_SymbolDeclaration);
825 var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
826 $documentation: "Symbol naming a function argument",
829 var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
830 $documentation: "Symbol defining a function",
831 }, AST_SymbolDeclaration);
833 var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
834 $documentation: "Symbol naming a function expression",
835 }, AST_SymbolDeclaration);
837 var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
838 $documentation: "Symbol naming the exception in catch",
839 }, AST_SymbolDeclaration);
841 var AST_Label = DEFNODE("Label", "references", {
842 $documentation: "Symbol naming a label (declaration)",
844 references: "[AST_LoopControl*] a list of nodes referring to this label"
846 initialize: function() {
847 this.references = [];
852 var AST_SymbolRef = DEFNODE("SymbolRef", null, {
853 $documentation: "Reference to some symbol (not definition/declaration)",
856 var AST_LabelRef = DEFNODE("LabelRef", null, {
857 $documentation: "Reference to a label symbol",
860 var AST_This = DEFNODE("This", null, {
861 $documentation: "The `this` symbol",
864 var AST_Constant = DEFNODE("Constant", null, {
865 $documentation: "Base class for all constants",
866 getValue: function() {
871 var AST_String = DEFNODE("String", "value quote", {
872 $documentation: "A string literal",
874 value: "[string] the contents of this string",
875 quote: "[string] the original quote character"
879 var AST_Number = DEFNODE("Number", "value literal", {
880 $documentation: "A number literal",
882 value: "[number] the numeric value",
883 literal: "[string] numeric value as string (optional)"
887 var AST_RegExp = DEFNODE("RegExp", "value", {
888 $documentation: "A regexp literal",
890 value: "[RegExp] the actual regexp"
894 var AST_Atom = DEFNODE("Atom", null, {
895 $documentation: "Base class for atoms",
898 var AST_Null = DEFNODE("Null", null, {
899 $documentation: "The `null` atom",
903 var AST_NaN = DEFNODE("NaN", null, {
904 $documentation: "The impossible value",
908 var AST_Undefined = DEFNODE("Undefined", null, {
909 $documentation: "The `undefined` value",
910 value: (function(){}())
913 var AST_Hole = DEFNODE("Hole", null, {
914 $documentation: "A hole in an array",
915 value: (function(){}())
918 var AST_Infinity = DEFNODE("Infinity", null, {
919 $documentation: "The `Infinity` value",
923 var AST_Boolean = DEFNODE("Boolean", null, {
924 $documentation: "Base class for booleans",
927 var AST_False = DEFNODE("False", null, {
928 $documentation: "The `false` atom",
932 var AST_True = DEFNODE("True", null, {
933 $documentation: "The `true` atom",
937 /* -----[ TreeWalker ]----- */
939 function TreeWalker(callback) {
940 this.visit = callback;
942 this.directives = Object.create(null);
944 TreeWalker.prototype = {
945 _visit: function(node, descend) {
947 var ret = this.visit(node, descend ? function(){
950 if (!ret && descend) {
956 parent: function(n) {
957 return this.stack[this.stack.length - 2 - (n || 0)];
959 push: function (node) {
960 if (node instanceof AST_Lambda) {
961 this.directives = Object.create(this.directives);
962 } else if (node instanceof AST_Directive) {
963 this.directives[node.value] = this.directives[node.value] ? "up" : true;
965 this.stack.push(node);
967 pop: function(node) {
969 if (node instanceof AST_Lambda) {
970 this.directives = Object.getPrototypeOf(this.directives);
974 return this.stack[this.stack.length - 1];
976 find_parent: function(type) {
977 var stack = this.stack;
978 for (var i = stack.length; --i >= 0;) {
980 if (x instanceof type) return x;
983 has_directive: function(type) {
984 var dir = this.directives[type];
986 var node = this.stack[this.stack.length - 1];
987 if (node instanceof AST_Scope) {
988 for (var i = 0; i < node.body.length; ++i) {
989 var st = node.body[i];
990 if (!(st instanceof AST_Directive)) break;
991 if (st.value == type) return true;
995 in_boolean_context: function() {
996 var stack = this.stack;
997 var i = stack.length, self = stack[--i];
1000 if ((p instanceof AST_If && p.condition === self) ||
1001 (p instanceof AST_Conditional && p.condition === self) ||
1002 (p instanceof AST_DWLoop && p.condition === self) ||
1003 (p instanceof AST_For && p.condition === self) ||
1004 (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self))
1008 if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
1013 loopcontrol_target: function(label) {
1014 var stack = this.stack;
1015 if (label) for (var i = stack.length; --i >= 0;) {
1017 if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
1020 } else for (var i = stack.length; --i >= 0;) {
1022 if (x instanceof AST_Switch || x instanceof AST_IterationStatement)