UNPKG

74.7 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
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
46function DEFNODE(type, props, methods, base) {
47 if (typeof base === "undefined") base = AST_Node;
48 props = props ? props.split(/\s+/) : [];
49 var self_props = props;
50 if (base && base.PROPS) props = props.concat(base.PROPS);
51 var code = [
52 "return function AST_", type, "(props){",
53 "if(props){",
54 ];
55 props.forEach(function(prop) {
56 code.push("this.", prop, "=props.", prop, ";");
57 });
58 code.push("}");
59 var proto = Object.create(base && base.prototype);
60 if (methods.initialize || proto.initialize) code.push("this.initialize();");
61 code.push("};");
62 var ctor = new Function(code.join(""))();
63 ctor.prototype = proto;
64 ctor.prototype.CTOR = ctor;
65 ctor.prototype.TYPE = ctor.TYPE = type;
66 if (base) {
67 ctor.BASE = base;
68 base.SUBCLASSES.push(ctor);
69 }
70 ctor.DEFMETHOD = function(name, method) {
71 this.prototype[name] = method;
72 };
73 ctor.PROPS = props;
74 ctor.SELF_PROPS = self_props;
75 ctor.SUBCLASSES = [];
76 for (var name in methods) if (HOP(methods, name)) {
77 if (/^\$/.test(name)) {
78 ctor[name.substr(1)] = methods[name];
79 } else {
80 ctor.DEFMETHOD(name, methods[name]);
81 }
82 }
83 if (typeof exports !== "undefined") exports["AST_" + type] = ctor;
84 return ctor;
85}
86
87var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw", {
88}, null);
89
90var AST_Node = DEFNODE("Node", "start end", {
91 _clone: function(deep) {
92 if (deep) {
93 var self = this.clone();
94 return self.transform(new TreeTransformer(function(node) {
95 if (node !== self) {
96 return node.clone(true);
97 }
98 }));
99 }
100 return new this.CTOR(this);
101 },
102 clone: function(deep) {
103 return this._clone(deep);
104 },
105 $documentation: "Base class of all AST nodes",
106 $propdoc: {
107 start: "[AST_Token] The first token of this node",
108 end: "[AST_Token] The last token of this node"
109 },
110 walk: function(visitor) {
111 visitor.visit(this);
112 },
113 _validate: function() {
114 if (this.TYPE == "Node") throw new Error("should not instantiate AST_Node");
115 },
116 validate: function() {
117 var ctor = this.CTOR;
118 do {
119 ctor.prototype._validate.call(this);
120 } while (ctor = ctor.BASE);
121 },
122 validate_ast: function() {
123 var marker = {};
124 this.walk(new TreeWalker(function(node) {
125 if (node.validate_visited === marker) {
126 throw new Error(string_template("cannot reuse {type} from [{file}:{line},{col}]", {
127 type: "AST_" + node.TYPE,
128 file: node.start.file,
129 line: node.start.line,
130 col: node.start.col,
131 }));
132 }
133 node.validate_visited = marker;
134 }));
135 },
136}, null);
137
138(AST_Node.log_function = function(fn, verbose) {
139 if (typeof fn != "function") {
140 AST_Node.info = AST_Node.warn = noop;
141 return;
142 }
143 var printed = Object.create(null);
144 AST_Node.info = verbose ? function(text, props) {
145 log("INFO: " + string_template(text, props));
146 } : noop;
147 AST_Node.warn = function(text, props) {
148 log("WARN: " + string_template(text, props));
149 };
150
151 function log(msg) {
152 if (printed[msg]) return;
153 printed[msg] = true;
154 fn(msg);
155 }
156})();
157
158var restore_transforms = [];
159AST_Node.enable_validation = function() {
160 AST_Node.disable_validation();
161 (function validate_transform(ctor) {
162 ctor.SUBCLASSES.forEach(validate_transform);
163 if (!HOP(ctor.prototype, "transform")) return;
164 var transform = ctor.prototype.transform;
165 ctor.prototype.transform = function(tw, in_list) {
166 var node = transform.call(this, tw, in_list);
167 if (node instanceof AST_Node) {
168 node.validate();
169 } else if (!(node === null || in_list && List.is_op(node))) {
170 throw new Error("invalid transformed value: " + node);
171 }
172 return node;
173 };
174 restore_transforms.push(function() {
175 ctor.prototype.transform = transform;
176 });
177 })(this);
178};
179
180AST_Node.disable_validation = function() {
181 var restore;
182 while (restore = restore_transforms.pop()) restore();
183};
184
185/* -----[ statements ]----- */
186
187var AST_Statement = DEFNODE("Statement", null, {
188 $documentation: "Base class of all statements",
189 _validate: function() {
190 if (this.TYPE == "Statement") throw new Error("should not instantiate AST_Statement");
191 },
192});
193
194var AST_Debugger = DEFNODE("Debugger", null, {
195 $documentation: "Represents a debugger statement",
196}, AST_Statement);
197
198var AST_Directive = DEFNODE("Directive", "quote value", {
199 $documentation: "Represents a directive, like \"use strict\";",
200 $propdoc: {
201 quote: "[string?] the original quote character",
202 value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
203 },
204 _validate: function() {
205 if (this.quote != null) {
206 if (typeof this.quote != "string") throw new Error("quote must be string");
207 if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
208 }
209 if (typeof this.value != "string") throw new Error("value must be string");
210 },
211}, AST_Statement);
212
213var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
214 $documentation: "The empty statement (empty block or simply a semicolon)"
215}, AST_Statement);
216
217function is_statement(node) {
218 return node instanceof AST_Statement
219 && !(node instanceof AST_ClassExpression)
220 && !(node instanceof AST_LambdaExpression);
221}
222
223function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
224 multiple = multiple ? "contain" : "be";
225 if (!(value instanceof AST_Node)) throw new Error(prop + " must " + multiple + " AST_Node");
226 if (value instanceof AST_DefaultValue) throw new Error(prop + " cannot " + multiple + " AST_DefaultValue");
227 if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured");
228 if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole");
229 if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread");
230 if (is_statement(value)) throw new Error(prop + " cannot " + multiple + " AST_Statement");
231 if (value instanceof AST_SymbolDeclaration) {
232 throw new Error(prop + " cannot " + multiple + " AST_SymbolDeclaration");
233 }
234}
235
236function must_be_expression(node, prop) {
237 validate_expression(node[prop], prop);
238}
239
240var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
241 $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
242 $propdoc: {
243 body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
244 },
245 walk: function(visitor) {
246 var node = this;
247 visitor.visit(node, function() {
248 node.body.walk(visitor);
249 });
250 },
251 _validate: function() {
252 must_be_expression(this, "body");
253 },
254}, AST_Statement);
255
256var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", {
257 $documentation: "Base class for all statements introducing a lexical scope",
258 $propdoc: {
259 enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
260 functions: "[Dictionary/S] like `variables`, but only lists function declarations",
261 parent_scope: "[AST_Scope?/S] link to the parent scope",
262 variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
263 },
264 clone: function(deep) {
265 var node = this._clone(deep);
266 if (this.enclosed) node.enclosed = this.enclosed.slice();
267 if (this.functions) node.functions = this.functions.clone();
268 if (this.variables) node.variables = this.variables.clone();
269 return node;
270 },
271 pinned: function() {
272 return this.resolve().pinned();
273 },
274 resolve: function() {
275 return this.parent_scope.resolve();
276 },
277 _validate: function() {
278 if (this.TYPE == "BlockScope") throw new Error("should not instantiate AST_BlockScope");
279 if (this.parent_scope == null) return;
280 if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
281 if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
282 },
283}, AST_Statement);
284
285function walk_body(node, visitor) {
286 node.body.forEach(function(node) {
287 node.walk(visitor);
288 });
289}
290
291var AST_Block = DEFNODE("Block", "body", {
292 $documentation: "A body of statements (usually braced)",
293 $propdoc: {
294 body: "[AST_Statement*] an array of statements"
295 },
296 walk: function(visitor) {
297 var node = this;
298 visitor.visit(node, function() {
299 walk_body(node, visitor);
300 });
301 },
302 _validate: function() {
303 if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block");
304 this.body.forEach(function(node) {
305 if (!is_statement(node)) throw new Error("body must contain AST_Statement");
306 });
307 },
308}, AST_BlockScope);
309
310var AST_BlockStatement = DEFNODE("BlockStatement", null, {
311 $documentation: "A block statement",
312}, AST_Block);
313
314var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
315 $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
316 $propdoc: {
317 body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
318 },
319 _validate: function() {
320 if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody");
321 if (!is_statement(this.body)) throw new Error("body must be AST_Statement");
322 },
323}, AST_BlockScope);
324
325var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
326 $documentation: "Statement with a label",
327 $propdoc: {
328 label: "[AST_Label] a label definition"
329 },
330 walk: function(visitor) {
331 var node = this;
332 visitor.visit(node, function() {
333 node.label.walk(visitor);
334 node.body.walk(visitor);
335 });
336 },
337 clone: function(deep) {
338 var node = this._clone(deep);
339 if (deep) {
340 var label = node.label;
341 var def = this.label;
342 node.walk(new TreeWalker(function(node) {
343 if (node instanceof AST_LoopControl) {
344 if (!node.label || node.label.thedef !== def) return;
345 node.label.thedef = label;
346 label.references.push(node);
347 return true;
348 }
349 if (node instanceof AST_Scope) return true;
350 }));
351 }
352 return node;
353 },
354 _validate: function() {
355 if (!(this.label instanceof AST_Label)) throw new Error("label must be AST_Label");
356 },
357}, AST_StatementWithBody);
358
359var AST_IterationStatement = DEFNODE("IterationStatement", null, {
360 $documentation: "Internal class. All loops inherit from it.",
361 _validate: function() {
362 if (this.TYPE == "IterationStatement") throw new Error("should not instantiate AST_IterationStatement");
363 },
364}, AST_StatementWithBody);
365
366var AST_DWLoop = DEFNODE("DWLoop", "condition", {
367 $documentation: "Base class for do/while statements",
368 $propdoc: {
369 condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
370 },
371 _validate: function() {
372 if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
373 must_be_expression(this, "condition");
374 },
375}, AST_IterationStatement);
376
377var AST_Do = DEFNODE("Do", null, {
378 $documentation: "A `do` statement",
379 walk: function(visitor) {
380 var node = this;
381 visitor.visit(node, function() {
382 node.body.walk(visitor);
383 node.condition.walk(visitor);
384 });
385 }
386}, AST_DWLoop);
387
388var AST_While = DEFNODE("While", null, {
389 $documentation: "A `while` statement",
390 walk: function(visitor) {
391 var node = this;
392 visitor.visit(node, function() {
393 node.condition.walk(visitor);
394 node.body.walk(visitor);
395 });
396 }
397}, AST_DWLoop);
398
399var AST_For = DEFNODE("For", "init condition step", {
400 $documentation: "A `for` statement",
401 $propdoc: {
402 init: "[AST_Node?] the `for` initialization code, or null if empty",
403 condition: "[AST_Node?] the `for` termination clause, or null if empty",
404 step: "[AST_Node?] the `for` update clause, or null if empty"
405 },
406 walk: function(visitor) {
407 var node = this;
408 visitor.visit(node, function() {
409 if (node.init) node.init.walk(visitor);
410 if (node.condition) node.condition.walk(visitor);
411 if (node.step) node.step.walk(visitor);
412 node.body.walk(visitor);
413 });
414 },
415 _validate: function() {
416 if (this.init != null) {
417 if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
418 if (is_statement(this.init) && !(this.init instanceof AST_Definitions)) {
419 throw new Error("init cannot be AST_Statement");
420 }
421 }
422 if (this.condition != null) must_be_expression(this, "condition");
423 if (this.step != null) must_be_expression(this, "step");
424 },
425}, AST_IterationStatement);
426
427var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
428 $documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
429 $propdoc: {
430 init: "[AST_Node] the assignment target during iteration",
431 object: "[AST_Node] the object to iterate over"
432 },
433 walk: function(visitor) {
434 var node = this;
435 visitor.visit(node, function() {
436 node.init.walk(visitor);
437 node.object.walk(visitor);
438 node.body.walk(visitor);
439 });
440 },
441 _validate: function() {
442 if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration");
443 if (this.init instanceof AST_Definitions) {
444 if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
445 } else {
446 validate_destructured(this.init, function(node) {
447 if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
448 throw new Error("init must be assignable: " + node.TYPE);
449 }
450 });
451 }
452 must_be_expression(this, "object");
453 },
454}, AST_IterationStatement);
455
456var AST_ForIn = DEFNODE("ForIn", null, {
457 $documentation: "A `for ... in` statement",
458}, AST_ForEnumeration);
459
460var AST_ForOf = DEFNODE("ForOf", null, {
461 $documentation: "A `for ... of` statement",
462}, AST_ForEnumeration);
463
464var AST_ForAwaitOf = DEFNODE("ForAwaitOf", null, {
465 $documentation: "A `for await ... of` statement",
466}, AST_ForOf);
467
468var AST_With = DEFNODE("With", "expression", {
469 $documentation: "A `with` statement",
470 $propdoc: {
471 expression: "[AST_Node] the `with` expression"
472 },
473 walk: function(visitor) {
474 var node = this;
475 visitor.visit(node, function() {
476 node.expression.walk(visitor);
477 node.body.walk(visitor);
478 });
479 },
480 _validate: function() {
481 must_be_expression(this, "expression");
482 },
483}, AST_StatementWithBody);
484
485/* -----[ scope and functions ]----- */
486
487var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
488 $documentation: "Base class for all statements introducing a lexical scope",
489 $propdoc: {
490 uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
491 uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
492 },
493 pinned: function() {
494 return this.uses_eval || this.uses_with;
495 },
496 resolve: return_this,
497 _validate: function() {
498 if (this.TYPE == "Scope") throw new Error("should not instantiate AST_Scope");
499 },
500}, AST_Block);
501
502var AST_Toplevel = DEFNODE("Toplevel", "globals", {
503 $documentation: "The toplevel scope",
504 $propdoc: {
505 globals: "[Dictionary/S] a map of name ---> SymbolDef for all undeclared names",
506 },
507 wrap: function(name) {
508 var body = this.body;
509 return parse([
510 "(function(exports){'$ORIG';})(typeof ",
511 name,
512 "=='undefined'?(",
513 name,
514 "={}):",
515 name,
516 ");"
517 ].join(""), {
518 filename: "wrap=" + JSON.stringify(name)
519 }).transform(new TreeTransformer(function(node) {
520 if (node instanceof AST_Directive && node.value == "$ORIG") {
521 return List.splice(body);
522 }
523 }));
524 },
525 enclose: function(args_values) {
526 if (typeof args_values != "string") args_values = "";
527 var index = args_values.indexOf(":");
528 if (index < 0) index = args_values.length;
529 var body = this.body;
530 return parse([
531 "(function(",
532 args_values.slice(0, index),
533 '){"$ORIG"})(',
534 args_values.slice(index + 1),
535 ")"
536 ].join(""), {
537 filename: "enclose=" + JSON.stringify(args_values)
538 }).transform(new TreeTransformer(function(node) {
539 if (node instanceof AST_Directive && node.value == "$ORIG") {
540 return List.splice(body);
541 }
542 }));
543 }
544}, AST_Scope);
545
546var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
547 $documentation: "Base class for functions",
548 $propdoc: {
549 argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
550 length_read: "[boolean/S] whether length property of this function is accessed",
551 rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
552 uses_arguments: "[boolean/S] whether this function accesses the arguments array",
553 },
554 each_argname: function(visit) {
555 var tw = new TreeWalker(function(node) {
556 if (node instanceof AST_DefaultValue) {
557 node.name.walk(tw);
558 return true;
559 }
560 if (node instanceof AST_DestructuredKeyVal) {
561 node.value.walk(tw);
562 return true;
563 }
564 if (node instanceof AST_SymbolFunarg) visit(node);
565 });
566 this.argnames.forEach(function(argname) {
567 argname.walk(tw);
568 });
569 if (this.rest) this.rest.walk(tw);
570 },
571 walk: function(visitor) {
572 var node = this;
573 visitor.visit(node, function() {
574 if (node.name) node.name.walk(visitor);
575 node.argnames.forEach(function(argname) {
576 argname.walk(visitor);
577 });
578 if (node.rest) node.rest.walk(visitor);
579 walk_body(node, visitor);
580 });
581 },
582 _validate: function() {
583 if (this.TYPE == "Lambda") throw new Error("should not instantiate AST_Lambda");
584 this.argnames.forEach(function(node) {
585 validate_destructured(node, function(node) {
586 if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
587 }, true);
588 });
589 if (this.rest != null) validate_destructured(this.rest, function(node) {
590 if (!(node instanceof AST_SymbolFunarg)) throw new Error("rest must be AST_SymbolFunarg");
591 });
592 },
593}, AST_Scope);
594
595var AST_Accessor = DEFNODE("Accessor", null, {
596 $documentation: "A getter/setter function",
597 _validate: function() {
598 if (this.name != null) throw new Error("name must be null");
599 },
600}, AST_Lambda);
601
602var AST_LambdaExpression = DEFNODE("LambdaExpression", "inlined", {
603 $documentation: "Base class for function expressions",
604 $propdoc: {
605 inlined: "[boolean/S] whether this function has been inlined",
606 },
607 _validate: function() {
608 if (this.TYPE == "LambdaExpression") throw new Error("should not instantiate AST_LambdaExpression");
609 },
610}, AST_Lambda);
611
612function is_arrow(node) {
613 return node instanceof AST_Arrow || node instanceof AST_AsyncArrow;
614}
615
616function is_async(node) {
617 return node instanceof AST_AsyncArrow
618 || node instanceof AST_AsyncDefun
619 || node instanceof AST_AsyncFunction
620 || node instanceof AST_AsyncGeneratorDefun
621 || node instanceof AST_AsyncGeneratorFunction;
622}
623
624function is_generator(node) {
625 return node instanceof AST_AsyncGeneratorDefun
626 || node instanceof AST_AsyncGeneratorFunction
627 || node instanceof AST_GeneratorDefun
628 || node instanceof AST_GeneratorFunction;
629}
630
631function walk_lambda(node, tw) {
632 if (is_arrow(node) && node.value) {
633 node.value.walk(tw);
634 } else {
635 walk_body(node, tw);
636 }
637}
638
639var AST_Arrow = DEFNODE("Arrow", "value", {
640 $documentation: "An arrow function expression",
641 $propdoc: {
642 value: "[AST_Node?] simple return expression, or null if using function body.",
643 },
644 walk: function(visitor) {
645 var node = this;
646 visitor.visit(node, function() {
647 node.argnames.forEach(function(argname) {
648 argname.walk(visitor);
649 });
650 if (node.rest) node.rest.walk(visitor);
651 if (node.value) {
652 node.value.walk(visitor);
653 } else {
654 walk_body(node, visitor);
655 }
656 });
657 },
658 _validate: function() {
659 if (this.name != null) throw new Error("name must be null");
660 if (this.uses_arguments) throw new Error("uses_arguments must be false");
661 if (this.value != null) {
662 must_be_expression(this, "value");
663 if (this.body.length) throw new Error("body must be empty if value exists");
664 }
665 },
666}, AST_LambdaExpression);
667
668var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", {
669 $documentation: "An asynchronous arrow function expression",
670 $propdoc: {
671 value: "[AST_Node?] simple return expression, or null if using function body.",
672 },
673 walk: function(visitor) {
674 var node = this;
675 visitor.visit(node, function() {
676 node.argnames.forEach(function(argname) {
677 argname.walk(visitor);
678 });
679 if (node.rest) node.rest.walk(visitor);
680 if (node.value) {
681 node.value.walk(visitor);
682 } else {
683 walk_body(node, visitor);
684 }
685 });
686 },
687 _validate: function() {
688 if (this.name != null) throw new Error("name must be null");
689 if (this.uses_arguments) throw new Error("uses_arguments must be false");
690 if (this.value != null) {
691 must_be_expression(this, "value");
692 if (this.body.length) throw new Error("body must be empty if value exists");
693 }
694 },
695}, AST_LambdaExpression);
696
697var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
698 $documentation: "An asynchronous function expression",
699 $propdoc: {
700 name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
701 },
702 _validate: function() {
703 if (this.name != null) {
704 if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
705 }
706 },
707}, AST_LambdaExpression);
708
709var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
710 $documentation: "An asynchronous generator function expression",
711 $propdoc: {
712 name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
713 },
714 _validate: function() {
715 if (this.name != null) {
716 if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
717 }
718 },
719}, AST_LambdaExpression);
720
721var AST_Function = DEFNODE("Function", "name", {
722 $documentation: "A function expression",
723 $propdoc: {
724 name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
725 },
726 _validate: function() {
727 if (this.name != null) {
728 if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
729 }
730 },
731}, AST_LambdaExpression);
732
733var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", {
734 $documentation: "A generator function expression",
735 $propdoc: {
736 name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
737 },
738 _validate: function() {
739 if (this.name != null) {
740 if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
741 }
742 },
743}, AST_LambdaExpression);
744
745var AST_LambdaDefinition = DEFNODE("LambdaDefinition", "inlined name", {
746 $documentation: "Base class for function definitions",
747 $propdoc: {
748 inlined: "[boolean/S] whether this function has been inlined",
749 name: "[AST_SymbolDefun] the name of this function",
750 },
751 _validate: function() {
752 if (this.TYPE == "LambdaDefinition") throw new Error("should not instantiate AST_LambdaDefinition");
753 if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
754 },
755}, AST_Lambda);
756
757var AST_AsyncDefun = DEFNODE("AsyncDefun", null, {
758 $documentation: "An asynchronous function definition",
759}, AST_LambdaDefinition);
760
761var AST_AsyncGeneratorDefun = DEFNODE("AsyncGeneratorDefun", null, {
762 $documentation: "An asynchronous generator function definition",
763}, AST_LambdaDefinition);
764
765var AST_Defun = DEFNODE("Defun", null, {
766 $documentation: "A function definition",
767}, AST_LambdaDefinition);
768
769var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, {
770 $documentation: "A generator function definition",
771}, AST_LambdaDefinition);
772
773/* -----[ classes ]----- */
774
775var AST_Class = DEFNODE("Class", "extends name properties", {
776 $documentation: "Base class for class literals",
777 $propdoc: {
778 extends: "[AST_Node?] the super class, or null if not specified",
779 properties: "[AST_ClassProperty*] array of class properties",
780 },
781 walk: function(visitor) {
782 var node = this;
783 visitor.visit(node, function() {
784 if (node.name) node.name.walk(visitor);
785 if (node.extends) node.extends.walk(visitor);
786 node.properties.forEach(function(prop) {
787 prop.walk(visitor);
788 });
789 });
790 },
791 _validate: function() {
792 if (this.TYPE == "Class") throw new Error("should not instantiate AST_Class");
793 if (this.extends != null) must_be_expression(this, "extends");
794 this.properties.forEach(function(node) {
795 if (!(node instanceof AST_ClassProperty)) throw new Error("properties must contain AST_ClassProperty");
796 });
797 },
798}, AST_BlockScope);
799
800var AST_DefClass = DEFNODE("DefClass", null, {
801 $documentation: "A class definition",
802 $propdoc: {
803 name: "[AST_SymbolDefClass] the name of this class",
804 },
805 _validate: function() {
806 if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass");
807 },
808}, AST_Class);
809
810var AST_ClassExpression = DEFNODE("ClassExpression", null, {
811 $documentation: "A class expression",
812 $propdoc: {
813 name: "[AST_SymbolClass?] the name of this class, or null if not specified",
814 },
815 _validate: function() {
816 if (this.name != null) {
817 if (!(this.name instanceof AST_SymbolClass)) throw new Error("name must be AST_SymbolClass");
818 }
819 },
820}, AST_Class);
821
822var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
823 $documentation: "Base class for `class` properties",
824 $propdoc: {
825 key: "[string|AST_Node] property name (AST_Node for computed property)",
826 private: "[boolean] whether this is a private property",
827 static: "[boolean] whether this is a static property",
828 value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
829 },
830 walk: function(visitor) {
831 var node = this;
832 visitor.visit(node, function() {
833 if (node.key instanceof AST_Node) node.key.walk(visitor);
834 if (node.value) node.value.walk(visitor);
835 });
836 },
837 _validate: function() {
838 if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
839 if (typeof this.key != "string") {
840 if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
841 must_be_expression(this, "key");
842 }
843 if(this.value != null) {
844 if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
845 }
846 },
847});
848
849var AST_ClassField = DEFNODE("ClassField", null, {
850 $documentation: "A `class` field",
851 _validate: function() {
852 if(this.value != null) must_be_expression(this, "value");
853 },
854}, AST_ClassProperty);
855
856var AST_ClassGetter = DEFNODE("ClassGetter", null, {
857 $documentation: "A `class` getter",
858 _validate: function() {
859 if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
860 },
861}, AST_ClassProperty);
862
863var AST_ClassSetter = DEFNODE("ClassSetter", null, {
864 $documentation: "A `class` setter",
865 _validate: function() {
866 if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
867 },
868}, AST_ClassProperty);
869
870var AST_ClassMethod = DEFNODE("ClassMethod", null, {
871 $documentation: "A `class` method",
872 _validate: function() {
873 if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
874 if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow");
875 if (this.value.name != null) throw new Error("name of class method's lambda must be null");
876 },
877}, AST_ClassProperty);
878
879/* -----[ JUMPS ]----- */
880
881var AST_Jump = DEFNODE("Jump", null, {
882 $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)",
883 _validate: function() {
884 if (this.TYPE == "Jump") throw new Error("should not instantiate AST_Jump");
885 },
886}, AST_Statement);
887
888var AST_Exit = DEFNODE("Exit", "value", {
889 $documentation: "Base class for “exits” (`return` and `throw`)",
890 $propdoc: {
891 value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
892 },
893 walk: function(visitor) {
894 var node = this;
895 visitor.visit(node, function() {
896 if (node.value) node.value.walk(visitor);
897 });
898 },
899 _validate: function() {
900 if (this.TYPE == "Exit") throw new Error("should not instantiate AST_Exit");
901 },
902}, AST_Jump);
903
904var AST_Return = DEFNODE("Return", null, {
905 $documentation: "A `return` statement",
906 _validate: function() {
907 if (this.value != null) must_be_expression(this, "value");
908 },
909}, AST_Exit);
910
911var AST_Throw = DEFNODE("Throw", null, {
912 $documentation: "A `throw` statement",
913 _validate: function() {
914 must_be_expression(this, "value");
915 },
916}, AST_Exit);
917
918var AST_LoopControl = DEFNODE("LoopControl", "label", {
919 $documentation: "Base class for loop control statements (`break` and `continue`)",
920 $propdoc: {
921 label: "[AST_LabelRef?] the label, or null if none",
922 },
923 walk: function(visitor) {
924 var node = this;
925 visitor.visit(node, function() {
926 if (node.label) node.label.walk(visitor);
927 });
928 },
929 _validate: function() {
930 if (this.TYPE == "LoopControl") throw new Error("should not instantiate AST_LoopControl");
931 if (this.label != null) {
932 if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef");
933 }
934 },
935}, AST_Jump);
936
937var AST_Break = DEFNODE("Break", null, {
938 $documentation: "A `break` statement"
939}, AST_LoopControl);
940
941var AST_Continue = DEFNODE("Continue", null, {
942 $documentation: "A `continue` statement"
943}, AST_LoopControl);
944
945/* -----[ IF ]----- */
946
947var AST_If = DEFNODE("If", "condition alternative", {
948 $documentation: "A `if` statement",
949 $propdoc: {
950 condition: "[AST_Node] the `if` condition",
951 alternative: "[AST_Statement?] the `else` part, or null if not present"
952 },
953 walk: function(visitor) {
954 var node = this;
955 visitor.visit(node, function() {
956 node.condition.walk(visitor);
957 node.body.walk(visitor);
958 if (node.alternative) node.alternative.walk(visitor);
959 });
960 },
961 _validate: function() {
962 must_be_expression(this, "condition");
963 if (this.alternative != null) {
964 if (!is_statement(this.alternative)) throw new Error("alternative must be AST_Statement");
965 }
966 },
967}, AST_StatementWithBody);
968
969/* -----[ SWITCH ]----- */
970
971var AST_Switch = DEFNODE("Switch", "expression", {
972 $documentation: "A `switch` statement",
973 $propdoc: {
974 expression: "[AST_Node] the `switch` “discriminant”"
975 },
976 walk: function(visitor) {
977 var node = this;
978 visitor.visit(node, function() {
979 node.expression.walk(visitor);
980 walk_body(node, visitor);
981 });
982 },
983 _validate: function() {
984 must_be_expression(this, "expression");
985 this.body.forEach(function(node) {
986 if (!(node instanceof AST_SwitchBranch)) throw new Error("body must be AST_SwitchBranch[]");
987 });
988 },
989}, AST_Block);
990
991var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
992 $documentation: "Base class for `switch` branches",
993 _validate: function() {
994 if (this.TYPE == "SwitchBranch") throw new Error("should not instantiate AST_SwitchBranch");
995 },
996}, AST_Block);
997
998var AST_Default = DEFNODE("Default", null, {
999 $documentation: "A `default` switch branch",
1000}, AST_SwitchBranch);
1001
1002var AST_Case = DEFNODE("Case", "expression", {
1003 $documentation: "A `case` switch branch",
1004 $propdoc: {
1005 expression: "[AST_Node] the `case` expression"
1006 },
1007 walk: function(visitor) {
1008 var node = this;
1009 visitor.visit(node, function() {
1010 node.expression.walk(visitor);
1011 walk_body(node, visitor);
1012 });
1013 },
1014 _validate: function() {
1015 must_be_expression(this, "expression");
1016 },
1017}, AST_SwitchBranch);
1018
1019/* -----[ EXCEPTIONS ]----- */
1020
1021var AST_Try = DEFNODE("Try", "bcatch bfinally", {
1022 $documentation: "A `try` statement",
1023 $propdoc: {
1024 bcatch: "[AST_Catch?] the catch block, or null if not present",
1025 bfinally: "[AST_Finally?] the finally block, or null if not present"
1026 },
1027 walk: function(visitor) {
1028 var node = this;
1029 visitor.visit(node, function() {
1030 walk_body(node, visitor);
1031 if (node.bcatch) node.bcatch.walk(visitor);
1032 if (node.bfinally) node.bfinally.walk(visitor);
1033 });
1034 },
1035 _validate: function() {
1036 if (this.bcatch != null) {
1037 if (!(this.bcatch instanceof AST_Catch)) throw new Error("bcatch must be AST_Catch");
1038 }
1039 if (this.bfinally != null) {
1040 if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
1041 }
1042 },
1043}, AST_Block);
1044
1045var AST_Catch = DEFNODE("Catch", "argname", {
1046 $documentation: "A `catch` node; only makes sense as part of a `try` statement",
1047 $propdoc: {
1048 argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present",
1049 },
1050 walk: function(visitor) {
1051 var node = this;
1052 visitor.visit(node, function() {
1053 if (node.argname) node.argname.walk(visitor);
1054 walk_body(node, visitor);
1055 });
1056 },
1057 _validate: function() {
1058 if (this.argname != null) validate_destructured(this.argname, function(node) {
1059 if (!(node instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
1060 });
1061 },
1062}, AST_Block);
1063
1064var AST_Finally = DEFNODE("Finally", null, {
1065 $documentation: "A `finally` node; only makes sense as part of a `try` statement"
1066}, AST_Block);
1067
1068/* -----[ VAR ]----- */
1069
1070var AST_Definitions = DEFNODE("Definitions", "definitions", {
1071 $documentation: "Base class for `var` nodes (variable declarations/initializations)",
1072 $propdoc: {
1073 definitions: "[AST_VarDef*] array of variable definitions"
1074 },
1075 walk: function(visitor) {
1076 var node = this;
1077 visitor.visit(node, function() {
1078 node.definitions.forEach(function(defn) {
1079 defn.walk(visitor);
1080 });
1081 });
1082 },
1083 _validate: function() {
1084 if (this.TYPE == "Definitions") throw new Error("should not instantiate AST_Definitions");
1085 if (this.definitions.length < 1) throw new Error("must have at least one definition");
1086 },
1087}, AST_Statement);
1088
1089var AST_Const = DEFNODE("Const", null, {
1090 $documentation: "A `const` statement",
1091 _validate: function() {
1092 this.definitions.forEach(function(node) {
1093 if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
1094 validate_destructured(node.name, function(node) {
1095 if (!(node instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst");
1096 });
1097 });
1098 },
1099}, AST_Definitions);
1100
1101var AST_Let = DEFNODE("Let", null, {
1102 $documentation: "A `let` statement",
1103 _validate: function() {
1104 this.definitions.forEach(function(node) {
1105 if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
1106 validate_destructured(node.name, function(node) {
1107 if (!(node instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet");
1108 });
1109 });
1110 },
1111}, AST_Definitions);
1112
1113var AST_Var = DEFNODE("Var", null, {
1114 $documentation: "A `var` statement",
1115 _validate: function() {
1116 this.definitions.forEach(function(node) {
1117 if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
1118 validate_destructured(node.name, function(node) {
1119 if (!(node instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
1120 });
1121 });
1122 },
1123}, AST_Definitions);
1124
1125var AST_VarDef = DEFNODE("VarDef", "name value", {
1126 $documentation: "A variable declaration; only appears in a AST_Definitions node",
1127 $propdoc: {
1128 name: "[AST_Destructured|AST_SymbolVar] name of the variable",
1129 value: "[AST_Node?] initializer, or null of there's no initializer",
1130 },
1131 walk: function(visitor) {
1132 var node = this;
1133 visitor.visit(node, function() {
1134 node.name.walk(visitor);
1135 if (node.value) node.value.walk(visitor);
1136 });
1137 },
1138 _validate: function() {
1139 if (this.value != null) must_be_expression(this, "value");
1140 },
1141});
1142
1143/* -----[ OTHER ]----- */
1144
1145var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
1146 $documentation: "An `export` statement",
1147 $propdoc: {
1148 body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
1149 },
1150 walk: function(visitor) {
1151 var node = this;
1152 visitor.visit(node, function() {
1153 node.body.walk(visitor);
1154 });
1155 },
1156 _validate: function() {
1157 if (!(this.body instanceof AST_DefClass
1158 || this.body instanceof AST_Definitions
1159 || this.body instanceof AST_LambdaDefinition)) {
1160 throw new Error("body must be AST_DefClass, AST_Definitions or AST_LambdaDefinition");
1161 }
1162 },
1163}, AST_Statement);
1164
1165var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
1166 $documentation: "An `export default` statement",
1167 $propdoc: {
1168 body: "[AST_Node] the default export",
1169 },
1170 walk: function(visitor) {
1171 var node = this;
1172 visitor.visit(node, function() {
1173 node.body.walk(visitor);
1174 });
1175 },
1176 _validate: function() {
1177 if (!(this.body instanceof AST_DefClass || this.body instanceof AST_LambdaDefinition)) {
1178 must_be_expression(this, "body");
1179 }
1180 },
1181}, AST_Statement);
1182
1183var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", {
1184 $documentation: "An `export ... from '...'` statement",
1185 $propdoc: {
1186 aliases: "[string*] array of aliases to export",
1187 keys: "[string*] array of keys to import",
1188 path: "[string] the path to import module",
1189 quote: "[string?] the original quote character",
1190 },
1191 _validate: function() {
1192 if (this.aliases.length != this.keys.length) {
1193 throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
1194 }
1195 this.aliases.forEach(function(name) {
1196 if (typeof name != "string") throw new Error("aliases must contain string");
1197 });
1198 this.keys.forEach(function(name) {
1199 if (typeof name != "string") throw new Error("keys must contain string");
1200 });
1201 if (typeof this.path != "string") throw new Error("path must be string");
1202 if (this.quote != null) {
1203 if (typeof this.quote != "string") throw new Error("quote must be string");
1204 if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
1205 }
1206 },
1207}, AST_Statement);
1208
1209var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
1210 $documentation: "An `export { ... }` statement",
1211 $propdoc: {
1212 properties: "[AST_SymbolExport*] array of aliases to export",
1213 },
1214 walk: function(visitor) {
1215 var node = this;
1216 visitor.visit(node, function() {
1217 node.properties.forEach(function(prop) {
1218 prop.walk(visitor);
1219 });
1220 });
1221 },
1222 _validate: function() {
1223 this.properties.forEach(function(prop) {
1224 if (!(prop instanceof AST_SymbolExport)) throw new Error("properties must contain AST_SymbolExport");
1225 });
1226 },
1227}, AST_Statement);
1228
1229var AST_Import = DEFNODE("Import", "all default path properties quote", {
1230 $documentation: "An `import` statement",
1231 $propdoc: {
1232 all: "[AST_SymbolImport?] the imported namespace, or null if not specified",
1233 default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified",
1234 path: "[string] the path to import module",
1235 properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified",
1236 quote: "[string?] the original quote character",
1237 },
1238 walk: function(visitor) {
1239 var node = this;
1240 visitor.visit(node, function() {
1241 if (node.all) node.all.walk(visitor);
1242 if (node.default) node.default.walk(visitor);
1243 if (node.properties) node.properties.forEach(function(prop) {
1244 prop.walk(visitor);
1245 });
1246 });
1247 },
1248 _validate: function() {
1249 if (this.all != null) {
1250 if (!(this.all instanceof AST_SymbolImport)) throw new Error("all must be AST_SymbolImport");
1251 if (this.properties != null) throw new Error("cannot import both * and {} in the same statement");
1252 }
1253 if (this.default != null) {
1254 if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport");
1255 if (this.default.key !== "") throw new Error("invalid default key: " + this.default.key);
1256 }
1257 if (typeof this.path != "string") throw new Error("path must be string");
1258 if (this.properties != null) this.properties.forEach(function(node) {
1259 if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport");
1260 });
1261 if (this.quote != null) {
1262 if (typeof this.quote != "string") throw new Error("quote must be string");
1263 if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
1264 }
1265 },
1266}, AST_Statement);
1267
1268var AST_DefaultValue = DEFNODE("DefaultValue", "name value", {
1269 $documentation: "A default value declaration",
1270 $propdoc: {
1271 name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable",
1272 value: "[AST_Node] value to assign if variable is `undefined`",
1273 },
1274 walk: function(visitor) {
1275 var node = this;
1276 visitor.visit(node, function() {
1277 node.name.walk(visitor);
1278 node.value.walk(visitor);
1279 });
1280 },
1281 _validate: function() {
1282 must_be_expression(this, "value");
1283 },
1284});
1285
1286function must_be_expressions(node, prop, allow_spread, allow_hole) {
1287 node[prop].forEach(function(node) {
1288 validate_expression(node, prop, true, allow_spread, allow_hole);
1289 });
1290}
1291
1292var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
1293 $documentation: "A function call expression",
1294 $propdoc: {
1295 args: "[AST_Node*] array of arguments",
1296 expression: "[AST_Node] expression to invoke as function",
1297 optional: "[boolean] whether the expression is optional chaining",
1298 pure: "[string/S] marker for side-effect-free call expression",
1299 terminal: "[boolean] whether the chain has ended",
1300 },
1301 walk: function(visitor) {
1302 var node = this;
1303 visitor.visit(node, function() {
1304 node.expression.walk(visitor);
1305 node.args.forEach(function(arg) {
1306 arg.walk(visitor);
1307 });
1308 });
1309 },
1310 _validate: function() {
1311 must_be_expression(this, "expression");
1312 must_be_expressions(this, "args", true);
1313 },
1314});
1315
1316var AST_New = DEFNODE("New", null, {
1317 $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties",
1318 _validate: function() {
1319 if (this.optional) throw new Error("optional must be false");
1320 if (this.terminal) throw new Error("terminal must be false");
1321 },
1322}, AST_Call);
1323
1324var AST_Sequence = DEFNODE("Sequence", "expressions", {
1325 $documentation: "A sequence expression (comma-separated expressions)",
1326 $propdoc: {
1327 expressions: "[AST_Node*] array of expressions (at least two)"
1328 },
1329 walk: function(visitor) {
1330 var node = this;
1331 visitor.visit(node, function() {
1332 node.expressions.forEach(function(expr) {
1333 expr.walk(visitor);
1334 });
1335 });
1336 },
1337 _validate: function() {
1338 if (this.expressions.length < 2) throw new Error("expressions must contain multiple elements");
1339 must_be_expressions(this, "expressions");
1340 },
1341});
1342
1343function root_expr(prop) {
1344 while (prop instanceof AST_PropAccess) prop = prop.expression;
1345 return prop;
1346}
1347
1348var AST_PropAccess = DEFNODE("PropAccess", "expression optional property terminal", {
1349 $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
1350 $propdoc: {
1351 expression: "[AST_Node] the “container” expression",
1352 optional: "[boolean] whether the expression is optional chaining",
1353 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",
1354 terminal: "[boolean] whether the chain has ended",
1355 },
1356 get_property: function() {
1357 var p = this.property;
1358 if (p instanceof AST_Constant) return p.value;
1359 if (p instanceof AST_UnaryPrefix && p.operator == "void" && p.expression instanceof AST_Constant) return;
1360 return p;
1361 },
1362 _validate: function() {
1363 if (this.TYPE == "PropAccess") throw new Error("should not instantiate AST_PropAccess");
1364 must_be_expression(this, "expression");
1365 },
1366});
1367
1368var AST_Dot = DEFNODE("Dot", null, {
1369 $documentation: "A dotted property access expression",
1370 walk: function(visitor) {
1371 var node = this;
1372 visitor.visit(node, function() {
1373 node.expression.walk(visitor);
1374 });
1375 },
1376 _validate: function() {
1377 if (typeof this.property != "string") throw new Error("property must be string");
1378 },
1379}, AST_PropAccess);
1380
1381var AST_Sub = DEFNODE("Sub", null, {
1382 $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
1383 walk: function(visitor) {
1384 var node = this;
1385 visitor.visit(node, function() {
1386 node.expression.walk(visitor);
1387 node.property.walk(visitor);
1388 });
1389 },
1390 _validate: function() {
1391 must_be_expression(this, "property");
1392 },
1393}, AST_PropAccess);
1394
1395var AST_Spread = DEFNODE("Spread", "expression", {
1396 $documentation: "Spread expression in array/object literals or function calls",
1397 $propdoc: {
1398 expression: "[AST_Node] expression to be expanded",
1399 },
1400 walk: function(visitor) {
1401 var node = this;
1402 visitor.visit(node, function() {
1403 node.expression.walk(visitor);
1404 });
1405 },
1406 _validate: function() {
1407 must_be_expression(this, "expression");
1408 },
1409});
1410
1411var AST_Unary = DEFNODE("Unary", "operator expression", {
1412 $documentation: "Base class for unary expressions",
1413 $propdoc: {
1414 operator: "[string] the operator",
1415 expression: "[AST_Node] expression that this unary operator applies to"
1416 },
1417 walk: function(visitor) {
1418 var node = this;
1419 visitor.visit(node, function() {
1420 node.expression.walk(visitor);
1421 });
1422 },
1423 _validate: function() {
1424 if (this.TYPE == "Unary") throw new Error("should not instantiate AST_Unary");
1425 if (typeof this.operator != "string") throw new Error("operator must be string");
1426 must_be_expression(this, "expression");
1427 },
1428});
1429
1430var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
1431 $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
1432}, AST_Unary);
1433
1434var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
1435 $documentation: "Unary postfix expression, i.e. `i++`"
1436}, AST_Unary);
1437
1438var AST_Binary = DEFNODE("Binary", "operator left right", {
1439 $documentation: "Binary expression, i.e. `a + b`",
1440 $propdoc: {
1441 left: "[AST_Node] left-hand side expression",
1442 operator: "[string] the operator",
1443 right: "[AST_Node] right-hand side expression"
1444 },
1445 walk: function(visitor) {
1446 var node = this;
1447 visitor.visit(node, function() {
1448 node.left.walk(visitor);
1449 node.right.walk(visitor);
1450 });
1451 },
1452 _validate: function() {
1453 if (!(this instanceof AST_Assign)) must_be_expression(this, "left");
1454 if (typeof this.operator != "string") throw new Error("operator must be string");
1455 must_be_expression(this, "right");
1456 },
1457});
1458
1459var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
1460 $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
1461 $propdoc: {
1462 condition: "[AST_Node]",
1463 consequent: "[AST_Node]",
1464 alternative: "[AST_Node]"
1465 },
1466 walk: function(visitor) {
1467 var node = this;
1468 visitor.visit(node, function() {
1469 node.condition.walk(visitor);
1470 node.consequent.walk(visitor);
1471 node.alternative.walk(visitor);
1472 });
1473 },
1474 _validate: function() {
1475 must_be_expression(this, "condition");
1476 must_be_expression(this, "consequent");
1477 must_be_expression(this, "alternative");
1478 },
1479});
1480
1481var AST_Assign = DEFNODE("Assign", null, {
1482 $documentation: "An assignment expression — `a = b + 5`",
1483 _validate: function() {
1484 if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="');
1485 if (this.left instanceof AST_Destructured) {
1486 if (this.operator != "=") throw new Error("invalid destructuring operator: " + this.operator);
1487 validate_destructured(this.left, function(node) {
1488 if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
1489 throw new Error("left must be assignable: " + node.TYPE);
1490 }
1491 });
1492 }
1493 },
1494}, AST_Binary);
1495
1496var AST_Await = DEFNODE("Await", "expression", {
1497 $documentation: "An await expression",
1498 $propdoc: {
1499 expression: "[AST_Node] expression with Promise to resolve on",
1500 },
1501 walk: function(visitor) {
1502 var node = this;
1503 visitor.visit(node, function() {
1504 node.expression.walk(visitor);
1505 });
1506 },
1507 _validate: function() {
1508 must_be_expression(this, "expression");
1509 },
1510});
1511
1512var AST_Yield = DEFNODE("Yield", "expression nested", {
1513 $documentation: "A yield expression",
1514 $propdoc: {
1515 expression: "[AST_Node?] return value for iterator, or null if undefined",
1516 nested: "[boolean] whether to iterate over expression as generator",
1517 },
1518 walk: function(visitor) {
1519 var node = this;
1520 visitor.visit(node, function() {
1521 if (node.expression) node.expression.walk(visitor);
1522 });
1523 },
1524 _validate: function() {
1525 if (this.expression != null) {
1526 must_be_expression(this, "expression");
1527 } else if (this.nested) {
1528 throw new Error("yield* must contain expression");
1529 }
1530 },
1531});
1532
1533/* -----[ LITERALS ]----- */
1534
1535var AST_Array = DEFNODE("Array", "elements", {
1536 $documentation: "An array literal",
1537 $propdoc: {
1538 elements: "[AST_Node*] array of elements"
1539 },
1540 walk: function(visitor) {
1541 var node = this;
1542 visitor.visit(node, function() {
1543 node.elements.forEach(function(element) {
1544 element.walk(visitor);
1545 });
1546 });
1547 },
1548 _validate: function() {
1549 must_be_expressions(this, "elements", true, true);
1550 },
1551});
1552
1553var AST_Destructured = DEFNODE("Destructured", "rest", {
1554 $documentation: "Base class for destructured literal",
1555 $propdoc: {
1556 rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent",
1557 },
1558 _validate: function() {
1559 if (this.TYPE == "Destructured") throw new Error("should not instantiate AST_Destructured");
1560 },
1561});
1562
1563function validate_destructured(node, check, allow_default) {
1564 if (node instanceof AST_DefaultValue && allow_default) return validate_destructured(node.name, check);
1565 if (node instanceof AST_Destructured) {
1566 if (node.rest != null) validate_destructured(node.rest, check);
1567 if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) {
1568 if (!(node instanceof AST_Hole)) validate_destructured(node, check, true);
1569 });
1570 if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) {
1571 validate_destructured(prop.value, check, true);
1572 });
1573 }
1574 check(node);
1575}
1576
1577var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
1578 $documentation: "A destructured array literal",
1579 $propdoc: {
1580 elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements",
1581 },
1582 walk: function(visitor) {
1583 var node = this;
1584 visitor.visit(node, function() {
1585 node.elements.forEach(function(element) {
1586 element.walk(visitor);
1587 });
1588 if (node.rest) node.rest.walk(visitor);
1589 });
1590 },
1591}, AST_Destructured);
1592
1593var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
1594 $documentation: "A key: value destructured property",
1595 $propdoc: {
1596 key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
1597 value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value",
1598 },
1599 walk: function(visitor) {
1600 var node = this;
1601 visitor.visit(node, function() {
1602 if (node.key instanceof AST_Node) node.key.walk(visitor);
1603 node.value.walk(visitor);
1604 });
1605 },
1606 _validate: function() {
1607 if (typeof this.key != "string") {
1608 if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
1609 must_be_expression(this, "key");
1610 }
1611 if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
1612 },
1613});
1614
1615var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
1616 $documentation: "A destructured object literal",
1617 $propdoc: {
1618 properties: "[AST_DestructuredKeyVal*] array of properties",
1619 },
1620 walk: function(visitor) {
1621 var node = this;
1622 visitor.visit(node, function() {
1623 node.properties.forEach(function(prop) {
1624 prop.walk(visitor);
1625 });
1626 if (node.rest) node.rest.walk(visitor);
1627 });
1628 },
1629 _validate: function() {
1630 this.properties.forEach(function(node) {
1631 if (!(node instanceof AST_DestructuredKeyVal)) throw new Error("properties must be AST_DestructuredKeyVal[]");
1632 });
1633 },
1634}, AST_Destructured);
1635
1636var AST_Object = DEFNODE("Object", "properties", {
1637 $documentation: "An object literal",
1638 $propdoc: {
1639 properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties"
1640 },
1641 walk: function(visitor) {
1642 var node = this;
1643 visitor.visit(node, function() {
1644 node.properties.forEach(function(prop) {
1645 prop.walk(visitor);
1646 });
1647 });
1648 },
1649 _validate: function() {
1650 this.properties.forEach(function(node) {
1651 if (!(node instanceof AST_ObjectProperty || node instanceof AST_Spread)) {
1652 throw new Error("properties must contain AST_ObjectProperty and/or AST_Spread only");
1653 }
1654 });
1655 },
1656});
1657
1658var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
1659 $documentation: "Base class for literal object properties",
1660 $propdoc: {
1661 key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
1662 value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
1663 },
1664 walk: function(visitor) {
1665 var node = this;
1666 visitor.visit(node, function() {
1667 if (node.key instanceof AST_Node) node.key.walk(visitor);
1668 node.value.walk(visitor);
1669 });
1670 },
1671 _validate: function() {
1672 if (this.TYPE == "ObjectProperty") throw new Error("should not instantiate AST_ObjectProperty");
1673 if (typeof this.key != "string") {
1674 if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
1675 must_be_expression(this, "key");
1676 }
1677 if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
1678 },
1679});
1680
1681var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
1682 $documentation: "A key: value object property",
1683 _validate: function() {
1684 must_be_expression(this, "value");
1685 },
1686}, AST_ObjectProperty);
1687
1688var AST_ObjectMethod = DEFNODE("ObjectMethod", null, {
1689 $documentation: "A key(){} object property",
1690 _validate: function() {
1691 if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
1692 if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow");
1693 if (this.value.name != null) throw new Error("name of object method's lambda must be null");
1694 },
1695}, AST_ObjectKeyVal);
1696
1697var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
1698 $documentation: "An object setter property",
1699 _validate: function() {
1700 if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
1701 },
1702}, AST_ObjectProperty);
1703
1704var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
1705 $documentation: "An object getter property",
1706 _validate: function() {
1707 if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
1708 },
1709}, AST_ObjectProperty);
1710
1711var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
1712 $documentation: "Base class for all symbols",
1713 $propdoc: {
1714 name: "[string] name of this symbol",
1715 scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
1716 thedef: "[SymbolDef/S] the definition of this symbol"
1717 },
1718 _validate: function() {
1719 if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol");
1720 if (typeof this.name != "string") throw new Error("name must be string");
1721 },
1722});
1723
1724var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
1725 $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
1726}, AST_Symbol);
1727
1728var AST_SymbolConst = DEFNODE("SymbolConst", null, {
1729 $documentation: "Symbol defining a constant",
1730}, AST_SymbolDeclaration);
1731
1732var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
1733 $documentation: "Symbol defined by an `import` statement",
1734 $propdoc: {
1735 key: "[string] the original `export` name",
1736 },
1737 _validate: function() {
1738 if (typeof this.key != "string") throw new Error("key must be string");
1739 },
1740}, AST_SymbolConst);
1741
1742var AST_SymbolLet = DEFNODE("SymbolLet", null, {
1743 $documentation: "Symbol defining a lexical-scoped variable",
1744}, AST_SymbolDeclaration);
1745
1746var AST_SymbolVar = DEFNODE("SymbolVar", null, {
1747 $documentation: "Symbol defining a variable",
1748}, AST_SymbolDeclaration);
1749
1750var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
1751 $documentation: "Symbol naming a function argument",
1752}, AST_SymbolVar);
1753
1754var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
1755 $documentation: "Symbol defining a function",
1756}, AST_SymbolDeclaration);
1757
1758var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
1759 $documentation: "Symbol naming a function expression",
1760}, AST_SymbolDeclaration);
1761
1762var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
1763 $documentation: "Symbol defining a class",
1764}, AST_SymbolConst);
1765
1766var AST_SymbolClass = DEFNODE("SymbolClass", null, {
1767 $documentation: "Symbol naming a class expression",
1768}, AST_SymbolConst);
1769
1770var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
1771 $documentation: "Symbol naming the exception in catch",
1772}, AST_SymbolDeclaration);
1773
1774var AST_Label = DEFNODE("Label", "references", {
1775 $documentation: "Symbol naming a label (declaration)",
1776 $propdoc: {
1777 references: "[AST_LoopControl*] a list of nodes referring to this label"
1778 },
1779 initialize: function() {
1780 this.references = [];
1781 this.thedef = this;
1782 }
1783}, AST_Symbol);
1784
1785var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
1786 $documentation: "Reference to some symbol (not definition/declaration)",
1787}, AST_Symbol);
1788
1789var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
1790 $documentation: "Reference in an `export` statement",
1791 $propdoc: {
1792 alias: "[string] the `export` alias",
1793 },
1794 _validate: function() {
1795 if (typeof this.alias != "string") throw new Error("alias must be string");
1796 },
1797}, AST_SymbolRef);
1798
1799var AST_LabelRef = DEFNODE("LabelRef", null, {
1800 $documentation: "Reference to a label symbol",
1801}, AST_Symbol);
1802
1803var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
1804 $documentation: "Base class for `super` & `this`",
1805 _validate: function() {
1806 if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity");
1807 },
1808}, AST_Symbol);
1809
1810var AST_Super = DEFNODE("Super", null, {
1811 $documentation: "The `super` symbol",
1812 _validate: function() {
1813 if (this.name !== "super") throw new Error('name must be "super"');
1814 },
1815}, AST_ObjectIdentity);
1816
1817var AST_This = DEFNODE("This", null, {
1818 $documentation: "The `this` symbol",
1819 _validate: function() {
1820 if (this.TYPE == "This" && this.name !== "this") throw new Error('name must be "this"');
1821 },
1822}, AST_ObjectIdentity);
1823
1824var AST_NewTarget = DEFNODE("NewTarget", null, {
1825 $documentation: "The `new.target` symbol",
1826 initialize: function() {
1827 this.name = "new.target";
1828 },
1829 _validate: function() {
1830 if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name);
1831 },
1832}, AST_This);
1833
1834var AST_Template = DEFNODE("Template", "expressions strings tag", {
1835 $documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
1836 $propdoc: {
1837 expressions: "[AST_Node*] the placeholder expressions",
1838 strings: "[string*] the raw text segments",
1839 tag: "[AST_Node] tag function, or null if absent",
1840 },
1841 walk: function(visitor) {
1842 var node = this;
1843 visitor.visit(node, function() {
1844 if (node.tag) node.tag.walk(visitor);
1845 node.expressions.forEach(function(expr) {
1846 expr.walk(visitor);
1847 });
1848 });
1849 },
1850 _validate: function() {
1851 if (this.expressions.length + 1 != this.strings.length) {
1852 throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)");
1853 }
1854 must_be_expressions(this, "expressions");
1855 this.strings.forEach(function(string) {
1856 if (typeof string != "string") throw new Error("strings must contain string");
1857 });
1858 if (this.tag != null) must_be_expression(this, "tag");
1859 },
1860});
1861
1862var AST_Constant = DEFNODE("Constant", null, {
1863 $documentation: "Base class for all constants",
1864 _validate: function() {
1865 if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
1866 },
1867});
1868
1869var AST_String = DEFNODE("String", "quote value", {
1870 $documentation: "A string literal",
1871 $propdoc: {
1872 quote: "[string?] the original quote character",
1873 value: "[string] the contents of this string",
1874 },
1875 _validate: function() {
1876 if (this.quote != null) {
1877 if (typeof this.quote != "string") throw new Error("quote must be string");
1878 if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
1879 }
1880 if (typeof this.value != "string") throw new Error("value must be string");
1881 },
1882}, AST_Constant);
1883
1884var AST_Number = DEFNODE("Number", "value", {
1885 $documentation: "A number literal",
1886 $propdoc: {
1887 value: "[number] the numeric value",
1888 },
1889 _validate: function() {
1890 if (typeof this.value != "number") throw new Error("value must be number");
1891 if (!isFinite(this.value)) throw new Error("value must be finite");
1892 if (this.value < 0) throw new Error("value cannot be negative");
1893 },
1894}, AST_Constant);
1895
1896var AST_BigInt = DEFNODE("BigInt", "value", {
1897 $documentation: "A BigInt literal",
1898 $propdoc: {
1899 value: "[string] the numeric representation",
1900 },
1901 _validate: function() {
1902 if (typeof this.value != "string") throw new Error("value must be string");
1903 if (this.value[0] == "-") throw new Error("value cannot be negative");
1904 },
1905}, AST_Constant);
1906
1907var AST_RegExp = DEFNODE("RegExp", "value", {
1908 $documentation: "A regexp literal",
1909 $propdoc: {
1910 value: "[RegExp] the actual regexp"
1911 },
1912 _validate: function() {
1913 if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp");
1914 },
1915}, AST_Constant);
1916
1917var AST_Atom = DEFNODE("Atom", null, {
1918 $documentation: "Base class for atoms",
1919 _validate: function() {
1920 if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom");
1921 },
1922}, AST_Constant);
1923
1924var AST_Null = DEFNODE("Null", null, {
1925 $documentation: "The `null` atom",
1926 value: null
1927}, AST_Atom);
1928
1929var AST_NaN = DEFNODE("NaN", null, {
1930 $documentation: "The impossible value",
1931 value: 0/0
1932}, AST_Atom);
1933
1934var AST_Undefined = DEFNODE("Undefined", null, {
1935 $documentation: "The `undefined` value",
1936 value: function(){}()
1937}, AST_Atom);
1938
1939var AST_Hole = DEFNODE("Hole", null, {
1940 $documentation: "A hole in an array",
1941 value: function(){}()
1942}, AST_Atom);
1943
1944var AST_Infinity = DEFNODE("Infinity", null, {
1945 $documentation: "The `Infinity` value",
1946 value: 1/0
1947}, AST_Atom);
1948
1949var AST_Boolean = DEFNODE("Boolean", null, {
1950 $documentation: "Base class for booleans",
1951 _validate: function() {
1952 if (this.TYPE == "Boolean") throw new Error("should not instantiate AST_Boolean");
1953 },
1954}, AST_Atom);
1955
1956var AST_False = DEFNODE("False", null, {
1957 $documentation: "The `false` atom",
1958 value: false
1959}, AST_Boolean);
1960
1961var AST_True = DEFNODE("True", null, {
1962 $documentation: "The `true` atom",
1963 value: true
1964}, AST_Boolean);
1965
1966/* -----[ TreeWalker ]----- */
1967
1968function TreeWalker(callback) {
1969 this.callback = callback;
1970 this.directives = Object.create(null);
1971 this.stack = [];
1972}
1973TreeWalker.prototype = {
1974 visit: function(node, descend) {
1975 this.push(node);
1976 var done = this.callback(node, descend || noop);
1977 if (!done && descend) descend();
1978 this.pop();
1979 },
1980 parent: function(n) {
1981 return this.stack[this.stack.length - 2 - (n || 0)];
1982 },
1983 push: function(node) {
1984 if (node instanceof AST_Lambda) {
1985 this.directives = Object.create(this.directives);
1986 } else if (node instanceof AST_Directive && !this.directives[node.value]) {
1987 this.directives[node.value] = node;
1988 }
1989 this.stack.push(node);
1990 },
1991 pop: function() {
1992 var node = this.stack.pop();
1993 if (node instanceof AST_Lambda) {
1994 this.directives = Object.getPrototypeOf(this.directives);
1995 }
1996 },
1997 self: function() {
1998 return this.stack[this.stack.length - 1];
1999 },
2000 find_parent: function(type) {
2001 var stack = this.stack;
2002 for (var i = stack.length; --i >= 0;) {
2003 var x = stack[i];
2004 if (x instanceof type) return x;
2005 }
2006 },
2007 has_directive: function(type) {
2008 var dir = this.directives[type];
2009 if (dir) return dir;
2010 var node = this.stack[this.stack.length - 1];
2011 if (node instanceof AST_Scope) {
2012 for (var i = 0; i < node.body.length; ++i) {
2013 var st = node.body[i];
2014 if (!(st instanceof AST_Directive)) break;
2015 if (st.value == type) return st;
2016 }
2017 }
2018 },
2019 loopcontrol_target: function(node) {
2020 var stack = this.stack;
2021 if (node.label) for (var i = stack.length; --i >= 0;) {
2022 var x = stack[i];
2023 if (x instanceof AST_LabeledStatement && x.label.name == node.label.name)
2024 return x.body;
2025 } else for (var i = stack.length; --i >= 0;) {
2026 var x = stack[i];
2027 if (x instanceof AST_IterationStatement
2028 || node instanceof AST_Break && x instanceof AST_Switch)
2029 return x;
2030 }
2031 },
2032 in_boolean_context: function() {
2033 var self = this.self();
2034 for (var i = 0, p; p = this.parent(i); i++) {
2035 if (p instanceof AST_Conditional && p.condition === self
2036 || p instanceof AST_DWLoop && p.condition === self
2037 || p instanceof AST_For && p.condition === self
2038 || p instanceof AST_If && p.condition === self
2039 || p instanceof AST_Return && p.in_bool
2040 || p instanceof AST_Sequence && p.tail_node() !== self
2041 || p instanceof AST_SimpleStatement
2042 || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
2043 return true;
2044 }
2045 if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
2046 || p instanceof AST_Conditional
2047 || p.tail_node() === self) {
2048 self = p;
2049 } else if (p instanceof AST_Return) {
2050 for (var call, fn = p; call = this.parent(++i); fn = call) {
2051 if (call.TYPE == "Call") {
2052 if (!(fn instanceof AST_Lambda) || fn.name) return false;
2053 } else if (fn instanceof AST_Lambda) {
2054 return false;
2055 }
2056 }
2057 } else {
2058 return false;
2059 }
2060 }
2061 }
2062};