Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / handlebars / node_modules / uglify-js / lib / ast.js
1 /***********************************************************************
2
3   A JavaScript tokenizer / parser / beautifier / compressor.
4   https://github.com/mishoo/UglifyJS2
5
6   -------------------------------- (C) ---------------------------------
7
8                            Author: Mihai Bazon
9                          <mihai.bazon@gmail.com>
10                        http://mihai.bazon.net/blog
11
12   Distributed under the BSD license:
13
14     Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16     Redistribution and use in source and binary forms, with or without
17     modification, are permitted provided that the following conditions
18     are met:
19
20         * Redistributions of source code must retain the above
21           copyright notice, this list of conditions and the following
22           disclaimer.
23
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.
28
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
40     SUCH DAMAGE.
41
42  ***********************************************************************/
43
44 "use strict";
45
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] + ";";
56     }
57     var proto = base && new base;
58     if (proto && proto.initialize || (methods && methods.initialize))
59         code += "this.initialize();";
60     code += "}}";
61     var ctor = new Function(code)();
62     if (proto) {
63         ctor.prototype = proto;
64         ctor.BASE = base;
65     }
66     if (base) base.SUBCLASSES.push(ctor);
67     ctor.prototype.CTOR = ctor;
68     ctor.PROPS = props || null;
69     ctor.SELF_PROPS = self_props;
70     ctor.SUBCLASSES = [];
71     if (type) {
72         ctor.prototype.TYPE = ctor.TYPE = type;
73     }
74     if (methods) for (i in methods) if (HOP(methods, i)) {
75         if (/^\$/.test(i)) {
76             ctor[i.substr(1)] = methods[i];
77         } else {
78             ctor.prototype[i] = methods[i];
79         }
80     }
81     ctor.DEFMETHOD = function(name, method) {
82         this.prototype[name] = method;
83     };
84     if (typeof exports !== "undefined") {
85         exports["AST_" + type] = ctor;
86     }
87     return ctor;
88 };
89
90 var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
91 }, null);
92
93 var AST_Node = DEFNODE("Node", "start end", {
94     clone: function() {
95         return new this.CTOR(this);
96     },
97     $documentation: "Base class of all AST nodes",
98     $propdoc: {
99         start: "[AST_Token] The first token of this node",
100         end: "[AST_Token] The last token of this node"
101     },
102     _walk: function(visitor) {
103         return visitor._visit(this);
104     },
105     walk: function(visitor) {
106         return this._walk(visitor); // not sure the indirection will be any help
107     }
108 }, null);
109
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));
114 };
115
116 /* -----[ statements ]----- */
117
118 var AST_Statement = DEFNODE("Statement", null, {
119     $documentation: "Base class of all statements",
120 });
121
122 var AST_Debugger = DEFNODE("Debugger", null, {
123     $documentation: "Represents a debugger statement",
124 }, AST_Statement);
125
126 var AST_Directive = DEFNODE("Directive", "value scope quote", {
127     $documentation: "Represents a directive, like \"use strict\";",
128     $propdoc: {
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"
132     },
133 }, AST_Statement);
134
135 var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
136     $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
137     $propdoc: {
138         body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
139     },
140     _walk: function(visitor) {
141         return visitor._visit(this, function(){
142             this.body._walk(visitor);
143         });
144     }
145 }, AST_Statement);
146
147 function walk_body(node, visitor) {
148     var body = node.body;
149     if (body instanceof AST_Statement) {
150         body._walk(visitor);
151     }
152     else for (var i = 0, len = body.length; i < len; i++) {
153         body[i]._walk(visitor);
154     }
155 };
156
157 var AST_Block = DEFNODE("Block", "body", {
158     $documentation: "A body of statements (usually bracketed)",
159     $propdoc: {
160         body: "[AST_Statement*] an array of statements"
161     },
162     _walk: function(visitor) {
163         return visitor._visit(this, function(){
164             walk_body(this, visitor);
165         });
166     }
167 }, AST_Statement);
168
169 var AST_BlockStatement = DEFNODE("BlockStatement", null, {
170     $documentation: "A block statement",
171 }, AST_Block);
172
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);
177     }
178 }, AST_Statement);
179
180 var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
181     $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
182     $propdoc: {
183         body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
184     },
185     _walk: function(visitor) {
186         return visitor._visit(this, function(){
187             this.body._walk(visitor);
188         });
189     }
190 }, AST_Statement);
191
192 var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
193     $documentation: "Statement with a label",
194     $propdoc: {
195         label: "[AST_Label] a label definition"
196     },
197     _walk: function(visitor) {
198         return visitor._visit(this, function(){
199             this.label._walk(visitor);
200             this.body._walk(visitor);
201         });
202     }
203 }, AST_StatementWithBody);
204
205 var AST_IterationStatement = DEFNODE("IterationStatement", null, {
206     $documentation: "Internal class.  All loops inherit from it."
207 }, AST_StatementWithBody);
208
209 var AST_DWLoop = DEFNODE("DWLoop", "condition", {
210     $documentation: "Base class for do/while statements",
211     $propdoc: {
212         condition: "[AST_Node] the loop condition.  Should not be instanceof AST_Statement"
213     }
214 }, AST_IterationStatement);
215
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);
222         });
223     }
224 }, AST_DWLoop);
225
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);
232         });
233     }
234 }, AST_DWLoop);
235
236 var AST_For = DEFNODE("For", "init condition step", {
237     $documentation: "A `for` statement",
238     $propdoc: {
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"
242     },
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);
249         });
250     }
251 }, AST_IterationStatement);
252
253 var AST_ForIn = DEFNODE("ForIn", "init name object", {
254     $documentation: "A `for ... in` statement",
255     $propdoc: {
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"
259     },
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);
265         });
266     }
267 }, AST_IterationStatement);
268
269 var AST_With = DEFNODE("With", "expression", {
270     $documentation: "A `with` statement",
271     $propdoc: {
272         expression: "[AST_Node] the `with` expression"
273     },
274     _walk: function(visitor) {
275         return visitor._visit(this, function(){
276             this.expression._walk(visitor);
277             this.body._walk(visitor);
278         });
279     }
280 }, AST_StatementWithBody);
281
282 /* -----[ scope and functions ]----- */
283
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",
286     $propdoc: {
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)",
295     },
296 }, AST_Block);
297
298 var AST_Toplevel = DEFNODE("Toplevel", "globals", {
299     $documentation: "The toplevel scope",
300     $propdoc: {
301         globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
302     },
303     wrap_enclose: function(arg_parameter_pairs) {
304         var self = this;
305         var args = [];
306         var parameters = [];
307
308         arg_parameter_pairs.forEach(function(pair) {
309             var splitAt = pair.lastIndexOf(":");
310
311             args.push(pair.substr(0, splitAt));
312             parameters.push(pair.substr(splitAt + 1));
313         });
314
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);
320             }
321         }));
322         return wrapped_tl;
323     },
324     wrap_commonjs: function(name, export_all) {
325         var self = this;
326         var to_export = [];
327         if (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);
333                 }
334             }));
335         }
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) {
341                   case "$ORIG":
342                     return MAP.splice(self.body);
343                   case "$EXPORTS":
344                     var body = [];
345                     to_export.forEach(function(sym){
346                         body.push(new AST_SimpleStatement({
347                             body: new AST_Assign({
348                                 left: new AST_Sub({
349                                     expression: new AST_SymbolRef({ name: "exports" }),
350                                     property: new AST_String({ value: sym.name }),
351                                 }),
352                                 operator: "=",
353                                 right: new AST_SymbolRef(sym),
354                             }),
355                         }));
356                     });
357                     return MAP.splice(body);
358                 }
359             }
360         }));
361         return wrapped_tl;
362     }
363 }, AST_Scope);
364
365 var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
366     $documentation: "Base class for functions",
367     $propdoc: {
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"
371     },
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);
378             }
379             walk_body(this, visitor);
380         });
381     }
382 }, AST_Scope);
383
384 var AST_Accessor = DEFNODE("Accessor", null, {
385     $documentation: "A setter/getter function.  The `name` property is always null."
386 }, AST_Lambda);
387
388 var AST_Function = DEFNODE("Function", null, {
389     $documentation: "A function expression"
390 }, AST_Lambda);
391
392 var AST_Defun = DEFNODE("Defun", null, {
393     $documentation: "A function definition"
394 }, AST_Lambda);
395
396 /* -----[ JUMPS ]----- */
397
398 var AST_Jump = DEFNODE("Jump", null, {
399     $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
400 }, AST_Statement);
401
402 var AST_Exit = DEFNODE("Exit", "value", {
403     $documentation: "Base class for “exits” (`return` and `throw`)",
404     $propdoc: {
405         value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
406     },
407     _walk: function(visitor) {
408         return visitor._visit(this, this.value && function(){
409             this.value._walk(visitor);
410         });
411     }
412 }, AST_Jump);
413
414 var AST_Return = DEFNODE("Return", null, {
415     $documentation: "A `return` statement"
416 }, AST_Exit);
417
418 var AST_Throw = DEFNODE("Throw", null, {
419     $documentation: "A `throw` statement"
420 }, AST_Exit);
421
422 var AST_LoopControl = DEFNODE("LoopControl", "label", {
423     $documentation: "Base class for loop control statements (`break` and `continue`)",
424     $propdoc: {
425         label: "[AST_LabelRef?] the label, or null if none",
426     },
427     _walk: function(visitor) {
428         return visitor._visit(this, this.label && function(){
429             this.label._walk(visitor);
430         });
431     }
432 }, AST_Jump);
433
434 var AST_Break = DEFNODE("Break", null, {
435     $documentation: "A `break` statement"
436 }, AST_LoopControl);
437
438 var AST_Continue = DEFNODE("Continue", null, {
439     $documentation: "A `continue` statement"
440 }, AST_LoopControl);
441
442 /* -----[ IF ]----- */
443
444 var AST_If = DEFNODE("If", "condition alternative", {
445     $documentation: "A `if` statement",
446     $propdoc: {
447         condition: "[AST_Node] the `if` condition",
448         alternative: "[AST_Statement?] the `else` part, or null if not present"
449     },
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);
455         });
456     }
457 }, AST_StatementWithBody);
458
459 /* -----[ SWITCH ]----- */
460
461 var AST_Switch = DEFNODE("Switch", "expression", {
462     $documentation: "A `switch` statement",
463     $propdoc: {
464         expression: "[AST_Node] the `switch` “discriminant”"
465     },
466     _walk: function(visitor) {
467         return visitor._visit(this, function(){
468             this.expression._walk(visitor);
469             walk_body(this, visitor);
470         });
471     }
472 }, AST_Block);
473
474 var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
475     $documentation: "Base class for `switch` branches",
476 }, AST_Block);
477
478 var AST_Default = DEFNODE("Default", null, {
479     $documentation: "A `default` switch branch",
480 }, AST_SwitchBranch);
481
482 var AST_Case = DEFNODE("Case", "expression", {
483     $documentation: "A `case` switch branch",
484     $propdoc: {
485         expression: "[AST_Node] the `case` expression"
486     },
487     _walk: function(visitor) {
488         return visitor._visit(this, function(){
489             this.expression._walk(visitor);
490             walk_body(this, visitor);
491         });
492     }
493 }, AST_SwitchBranch);
494
495 /* -----[ EXCEPTIONS ]----- */
496
497 var AST_Try = DEFNODE("Try", "bcatch bfinally", {
498     $documentation: "A `try` statement",
499     $propdoc: {
500         bcatch: "[AST_Catch?] the catch block, or null if not present",
501         bfinally: "[AST_Finally?] the finally block, or null if not present"
502     },
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);
508         });
509     }
510 }, AST_Block);
511
512 var AST_Catch = DEFNODE("Catch", "argname", {
513     $documentation: "A `catch` node; only makes sense as part of a `try` statement",
514     $propdoc: {
515         argname: "[AST_SymbolCatch] symbol for the exception"
516     },
517     _walk: function(visitor) {
518         return visitor._visit(this, function(){
519             this.argname._walk(visitor);
520             walk_body(this, visitor);
521         });
522     }
523 }, AST_Block);
524
525 var AST_Finally = DEFNODE("Finally", null, {
526     $documentation: "A `finally` node; only makes sense as part of a `try` statement"
527 }, AST_Block);
528
529 /* -----[ VAR/CONST ]----- */
530
531 var AST_Definitions = DEFNODE("Definitions", "definitions", {
532     $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
533     $propdoc: {
534         definitions: "[AST_VarDef*] array of variable definitions"
535     },
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);
541             }
542         });
543     }
544 }, AST_Statement);
545
546 var AST_Var = DEFNODE("Var", null, {
547     $documentation: "A `var` statement"
548 }, AST_Definitions);
549
550 var AST_Const = DEFNODE("Const", null, {
551     $documentation: "A `const` statement"
552 }, AST_Definitions);
553
554 var AST_VarDef = DEFNODE("VarDef", "name value", {
555     $documentation: "A variable declaration; only appears in a AST_Definitions node",
556     $propdoc: {
557         name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
558         value: "[AST_Node?] initializer, or null of there's no initializer"
559     },
560     _walk: function(visitor) {
561         return visitor._visit(this, function(){
562             this.name._walk(visitor);
563             if (this.value) this.value._walk(visitor);
564         });
565     }
566 });
567
568 /* -----[ OTHER ]----- */
569
570 var AST_Call = DEFNODE("Call", "expression args", {
571     $documentation: "A function call expression",
572     $propdoc: {
573         expression: "[AST_Node] expression to invoke as function",
574         args: "[AST_Node*] array of arguments"
575     },
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);
582             }
583         });
584     }
585 });
586
587 var AST_New = DEFNODE("New", null, {
588     $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
589 }, AST_Call);
590
591 var AST_Seq = DEFNODE("Seq", "car cdr", {
592     $documentation: "A sequence expression (two comma-separated expressions)",
593     $propdoc: {
594         car: "[AST_Node] first element in sequence",
595         cdr: "[AST_Node] second element in sequence"
596     },
597     $cons: function(x, y) {
598         var seq = new AST_Seq(x);
599         seq.car = x;
600         seq.cdr = y;
601         return seq;
602     },
603     $from_array: function(array) {
604         if (array.length == 0) return null;
605         if (array.length == 1) return array[0].clone();
606         var list = null;
607         for (var i = array.length; --i >= 0;) {
608             list = AST_Seq.cons(array[i], list);
609         }
610         var p = list;
611         while (p) {
612             if (p.cdr && !p.cdr.cdr) {
613                 p.cdr = p.cdr.car;
614                 break;
615             }
616             p = p.cdr;
617         }
618         return list;
619     },
620     to_array: function() {
621         var p = this, a = [];
622         while (p) {
623             a.push(p.car);
624             if (p.cdr && !(p.cdr instanceof AST_Seq)) {
625                 a.push(p.cdr);
626                 break;
627             }
628             p = p.cdr;
629         }
630         return a;
631     },
632     add: function(node) {
633         var p = this;
634         while (p) {
635             if (!(p.cdr instanceof AST_Seq)) {
636                 var cell = AST_Seq.cons(p.cdr, node);
637                 return p.cdr = cell;
638             }
639             p = p.cdr;
640         }
641     },
642     len: function() {
643         if (this.cdr instanceof AST_Seq) {
644             return this.cdr.len() + 1;
645         } else {
646             return 2;
647         }
648     },
649     _walk: function(visitor) {
650         return visitor._visit(this, function(){
651             this.car._walk(visitor);
652             if (this.cdr) this.cdr._walk(visitor);
653         });
654     }
655 });
656
657 var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
658     $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
659     $propdoc: {
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"
662     }
663 });
664
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);
670         });
671     }
672 }, AST_PropAccess);
673
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);
680         });
681     }
682 }, AST_PropAccess);
683
684 var AST_Unary = DEFNODE("Unary", "operator expression", {
685     $documentation: "Base class for unary expressions",
686     $propdoc: {
687         operator: "[string] the operator",
688         expression: "[AST_Node] expression that this unary operator applies to"
689     },
690     _walk: function(visitor) {
691         return visitor._visit(this, function(){
692             this.expression._walk(visitor);
693         });
694     }
695 });
696
697 var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
698     $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
699 }, AST_Unary);
700
701 var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
702     $documentation: "Unary postfix expression, i.e. `i++`"
703 }, AST_Unary);
704
705 var AST_Binary = DEFNODE("Binary", "left operator right", {
706     $documentation: "Binary expression, i.e. `a + b`",
707     $propdoc: {
708         left: "[AST_Node] left-hand side expression",
709         operator: "[string] the operator",
710         right: "[AST_Node] right-hand side expression"
711     },
712     _walk: function(visitor) {
713         return visitor._visit(this, function(){
714             this.left._walk(visitor);
715             this.right._walk(visitor);
716         });
717     }
718 });
719
720 var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
721     $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
722     $propdoc: {
723         condition: "[AST_Node]",
724         consequent: "[AST_Node]",
725         alternative: "[AST_Node]"
726     },
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);
732         });
733     }
734 });
735
736 var AST_Assign = DEFNODE("Assign", null, {
737     $documentation: "An assignment expression — `a = b + 5`",
738 }, AST_Binary);
739
740 /* -----[ LITERALS ]----- */
741
742 var AST_Array = DEFNODE("Array", "elements", {
743     $documentation: "An array literal",
744     $propdoc: {
745         elements: "[AST_Node*] array of elements"
746     },
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);
752             }
753         });
754     }
755 });
756
757 var AST_Object = DEFNODE("Object", "properties", {
758     $documentation: "An object literal",
759     $propdoc: {
760         properties: "[AST_ObjectProperty*] array of properties"
761     },
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);
767             }
768         });
769     }
770 });
771
772 var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
773     $documentation: "Base class for literal object properties",
774     $propdoc: {
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."
777     },
778     _walk: function(visitor) {
779         return visitor._visit(this, function(){
780             this.value._walk(visitor);
781         });
782     }
783 });
784
785 var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
786     $documentation: "A key: value object property",
787     $propdoc: {
788         quote: "[string] the original quote character"
789     }
790 }, AST_ObjectProperty);
791
792 var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
793     $documentation: "An object setter property",
794 }, AST_ObjectProperty);
795
796 var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
797     $documentation: "An object getter property",
798 }, AST_ObjectProperty);
799
800 var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
801     $propdoc: {
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"
805     },
806     $documentation: "Base class for all symbols",
807 });
808
809 var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
810     $documentation: "The name of a property accessor (setter/getter function)"
811 }, AST_Symbol);
812
813 var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
814     $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
815 }, AST_Symbol);
816
817 var AST_SymbolVar = DEFNODE("SymbolVar", null, {
818     $documentation: "Symbol defining a variable",
819 }, AST_SymbolDeclaration);
820
821 var AST_SymbolConst = DEFNODE("SymbolConst", null, {
822     $documentation: "A constant declaration"
823 }, AST_SymbolDeclaration);
824
825 var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
826     $documentation: "Symbol naming a function argument",
827 }, AST_SymbolVar);
828
829 var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
830     $documentation: "Symbol defining a function",
831 }, AST_SymbolDeclaration);
832
833 var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
834     $documentation: "Symbol naming a function expression",
835 }, AST_SymbolDeclaration);
836
837 var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
838     $documentation: "Symbol naming the exception in catch",
839 }, AST_SymbolDeclaration);
840
841 var AST_Label = DEFNODE("Label", "references", {
842     $documentation: "Symbol naming a label (declaration)",
843     $propdoc: {
844         references: "[AST_LoopControl*] a list of nodes referring to this label"
845     },
846     initialize: function() {
847         this.references = [];
848         this.thedef = this;
849     }
850 }, AST_Symbol);
851
852 var AST_SymbolRef = DEFNODE("SymbolRef", null, {
853     $documentation: "Reference to some symbol (not definition/declaration)",
854 }, AST_Symbol);
855
856 var AST_LabelRef = DEFNODE("LabelRef", null, {
857     $documentation: "Reference to a label symbol",
858 }, AST_Symbol);
859
860 var AST_This = DEFNODE("This", null, {
861     $documentation: "The `this` symbol",
862 }, AST_Symbol);
863
864 var AST_Constant = DEFNODE("Constant", null, {
865     $documentation: "Base class for all constants",
866     getValue: function() {
867         return this.value;
868     }
869 });
870
871 var AST_String = DEFNODE("String", "value quote", {
872     $documentation: "A string literal",
873     $propdoc: {
874         value: "[string] the contents of this string",
875         quote: "[string] the original quote character"
876     }
877 }, AST_Constant);
878
879 var AST_Number = DEFNODE("Number", "value literal", {
880     $documentation: "A number literal",
881     $propdoc: {
882         value: "[number] the numeric value",
883         literal: "[string] numeric value as string (optional)"
884     }
885 }, AST_Constant);
886
887 var AST_RegExp = DEFNODE("RegExp", "value", {
888     $documentation: "A regexp literal",
889     $propdoc: {
890         value: "[RegExp] the actual regexp"
891     }
892 }, AST_Constant);
893
894 var AST_Atom = DEFNODE("Atom", null, {
895     $documentation: "Base class for atoms",
896 }, AST_Constant);
897
898 var AST_Null = DEFNODE("Null", null, {
899     $documentation: "The `null` atom",
900     value: null
901 }, AST_Atom);
902
903 var AST_NaN = DEFNODE("NaN", null, {
904     $documentation: "The impossible value",
905     value: 0/0
906 }, AST_Atom);
907
908 var AST_Undefined = DEFNODE("Undefined", null, {
909     $documentation: "The `undefined` value",
910     value: (function(){}())
911 }, AST_Atom);
912
913 var AST_Hole = DEFNODE("Hole", null, {
914     $documentation: "A hole in an array",
915     value: (function(){}())
916 }, AST_Atom);
917
918 var AST_Infinity = DEFNODE("Infinity", null, {
919     $documentation: "The `Infinity` value",
920     value: 1/0
921 }, AST_Atom);
922
923 var AST_Boolean = DEFNODE("Boolean", null, {
924     $documentation: "Base class for booleans",
925 }, AST_Atom);
926
927 var AST_False = DEFNODE("False", null, {
928     $documentation: "The `false` atom",
929     value: false
930 }, AST_Boolean);
931
932 var AST_True = DEFNODE("True", null, {
933     $documentation: "The `true` atom",
934     value: true
935 }, AST_Boolean);
936
937 /* -----[ TreeWalker ]----- */
938
939 function TreeWalker(callback) {
940     this.visit = callback;
941     this.stack = [];
942     this.directives = Object.create(null);
943 };
944 TreeWalker.prototype = {
945     _visit: function(node, descend) {
946         this.push(node);
947         var ret = this.visit(node, descend ? function(){
948             descend.call(node);
949         } : noop);
950         if (!ret && descend) {
951             descend.call(node);
952         }
953         this.pop(node);
954         return ret;
955     },
956     parent: function(n) {
957         return this.stack[this.stack.length - 2 - (n || 0)];
958     },
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;
964         }
965         this.stack.push(node);
966     },
967     pop: function(node) {
968         this.stack.pop();
969         if (node instanceof AST_Lambda) {
970             this.directives = Object.getPrototypeOf(this.directives);
971         }
972     },
973     self: function() {
974         return this.stack[this.stack.length - 1];
975     },
976     find_parent: function(type) {
977         var stack = this.stack;
978         for (var i = stack.length; --i >= 0;) {
979             var x = stack[i];
980             if (x instanceof type) return x;
981         }
982     },
983     has_directive: function(type) {
984         var dir = this.directives[type];
985         if (dir) return dir;
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;
992             }
993         }
994     },
995     in_boolean_context: function() {
996         var stack = this.stack;
997         var i = stack.length, self = stack[--i];
998         while (i > 0) {
999             var p = 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))
1005             {
1006                 return true;
1007             }
1008             if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
1009                 return false;
1010             self = p;
1011         }
1012     },
1013     loopcontrol_target: function(label) {
1014         var stack = this.stack;
1015         if (label) for (var i = stack.length; --i >= 0;) {
1016             var x = stack[i];
1017             if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
1018                 return x.body;
1019             }
1020         } else for (var i = stack.length; --i >= 0;) {
1021             var x = stack[i];
1022             if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
1023                 return x;
1024         }
1025     }
1026 };