UNPKG

55.6 kBJavaScriptView Raw
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
46import {
47 HOP,
48 MAP,
49 noop
50} from "./utils/index.js";
51import { parse } from "./parse.js";
52
53function DEFNODE(type, props, methods, base = AST_Node) {
54 if (!props) props = [];
55 else props = props.split(/\s+/);
56 var self_props = props;
57 if (base && base.PROPS)
58 props = props.concat(base.PROPS);
59 var code = "return function AST_" + type + "(props){ if (props) { ";
60 for (var i = props.length; --i >= 0;) {
61 code += "this." + props[i] + " = props." + props[i] + ";";
62 }
63 const proto = base && Object.create(base.prototype);
64 if (proto && proto.initialize || (methods && methods.initialize))
65 code += "this.initialize();";
66 code += "}";
67 code += "this.flags = 0;";
68 code += "}";
69 var ctor = new Function(code)();
70 if (proto) {
71 ctor.prototype = proto;
72 ctor.BASE = base;
73 }
74 if (base) base.SUBCLASSES.push(ctor);
75 ctor.prototype.CTOR = ctor;
76 ctor.prototype.constructor = ctor;
77 ctor.PROPS = props || null;
78 ctor.SELF_PROPS = self_props;
79 ctor.SUBCLASSES = [];
80 if (type) {
81 ctor.prototype.TYPE = ctor.TYPE = type;
82 }
83 if (methods) for (i in methods) if (HOP(methods, i)) {
84 if (i[0] === "$") {
85 ctor[i.substr(1)] = methods[i];
86 } else {
87 ctor.prototype[i] = methods[i];
88 }
89 }
90 ctor.DEFMETHOD = function(name, method) {
91 this.prototype[name] = method;
92 };
93 return ctor;
94}
95
96var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw quote end", {
97}, null);
98
99var AST_Node = DEFNODE("Node", "start end", {
100 _clone: function(deep) {
101 if (deep) {
102 var self = this.clone();
103 return self.transform(new TreeTransformer(function(node) {
104 if (node !== self) {
105 return node.clone(true);
106 }
107 }));
108 }
109 return new this.CTOR(this);
110 },
111 clone: function(deep) {
112 return this._clone(deep);
113 },
114 $documentation: "Base class of all AST nodes",
115 $propdoc: {
116 start: "[AST_Token] The first token of this node",
117 end: "[AST_Token] The last token of this node"
118 },
119 _walk: function(visitor) {
120 return visitor._visit(this);
121 },
122 walk: function(visitor) {
123 return this._walk(visitor); // not sure the indirection will be any help
124 },
125 _children_backwards: () => {}
126}, null);
127
128/* -----[ statements ]----- */
129
130var AST_Statement = DEFNODE("Statement", null, {
131 $documentation: "Base class of all statements",
132});
133
134var AST_Debugger = DEFNODE("Debugger", null, {
135 $documentation: "Represents a debugger statement",
136}, AST_Statement);
137
138var AST_Directive = DEFNODE("Directive", "value quote", {
139 $documentation: "Represents a directive, like \"use strict\";",
140 $propdoc: {
141 value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
142 quote: "[string] the original quote character"
143 },
144}, AST_Statement);
145
146var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
147 $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
148 $propdoc: {
149 body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
150 },
151 _walk: function(visitor) {
152 return visitor._visit(this, function() {
153 this.body._walk(visitor);
154 });
155 },
156 _children_backwards(push) {
157 push(this.body);
158 }
159}, AST_Statement);
160
161function walk_body(node, visitor) {
162 const body = node.body;
163 for (var i = 0, len = body.length; i < len; i++) {
164 body[i]._walk(visitor);
165 }
166}
167
168function clone_block_scope(deep) {
169 var clone = this._clone(deep);
170 if (this.block_scope) {
171 // TODO this is sometimes undefined during compression.
172 // But it should always have a value!
173 clone.block_scope = this.block_scope.clone();
174 }
175 return clone;
176}
177
178var AST_Block = DEFNODE("Block", "body block_scope", {
179 $documentation: "A body of statements (usually braced)",
180 $propdoc: {
181 body: "[AST_Statement*] an array of statements",
182 block_scope: "[AST_Scope] the block scope"
183 },
184 _walk: function(visitor) {
185 return visitor._visit(this, function() {
186 walk_body(this, visitor);
187 });
188 },
189 _children_backwards(push) {
190 let i = this.body.length;
191 while (i--) push(this.body[i]);
192 },
193 clone: clone_block_scope
194}, AST_Statement);
195
196var AST_BlockStatement = DEFNODE("BlockStatement", null, {
197 $documentation: "A block statement",
198}, AST_Block);
199
200var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
201 $documentation: "The empty statement (empty block or simply a semicolon)"
202}, AST_Statement);
203
204var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
205 $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
206 $propdoc: {
207 body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
208 }
209}, AST_Statement);
210
211var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
212 $documentation: "Statement with a label",
213 $propdoc: {
214 label: "[AST_Label] a label definition"
215 },
216 _walk: function(visitor) {
217 return visitor._visit(this, function() {
218 this.label._walk(visitor);
219 this.body._walk(visitor);
220 });
221 },
222 _children_backwards(push) {
223 push(this.body);
224 push(this.label);
225 },
226 clone: function(deep) {
227 var node = this._clone(deep);
228 if (deep) {
229 var label = node.label;
230 var def = this.label;
231 node.walk(new TreeWalker(function(node) {
232 if (node instanceof AST_LoopControl
233 && node.label && node.label.thedef === def) {
234 node.label.thedef = label;
235 label.references.push(node);
236 }
237 }));
238 }
239 return node;
240 }
241}, AST_StatementWithBody);
242
243var AST_IterationStatement = DEFNODE("IterationStatement", "block_scope", {
244 $documentation: "Internal class. All loops inherit from it.",
245 $propdoc: {
246 block_scope: "[AST_Scope] the block scope for this iteration statement."
247 },
248 clone: clone_block_scope
249}, AST_StatementWithBody);
250
251var AST_DWLoop = DEFNODE("DWLoop", "condition", {
252 $documentation: "Base class for do/while statements",
253 $propdoc: {
254 condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
255 }
256}, AST_IterationStatement);
257
258var AST_Do = DEFNODE("Do", null, {
259 $documentation: "A `do` statement",
260 _walk: function(visitor) {
261 return visitor._visit(this, function() {
262 this.body._walk(visitor);
263 this.condition._walk(visitor);
264 });
265 },
266 _children_backwards(push) {
267 push(this.condition);
268 push(this.body);
269 }
270}, AST_DWLoop);
271
272var AST_While = DEFNODE("While", null, {
273 $documentation: "A `while` statement",
274 _walk: function(visitor) {
275 return visitor._visit(this, function() {
276 this.condition._walk(visitor);
277 this.body._walk(visitor);
278 });
279 },
280 _children_backwards(push) {
281 push(this.body);
282 push(this.condition);
283 },
284}, AST_DWLoop);
285
286var AST_For = DEFNODE("For", "init condition step", {
287 $documentation: "A `for` statement",
288 $propdoc: {
289 init: "[AST_Node?] the `for` initialization code, or null if empty",
290 condition: "[AST_Node?] the `for` termination clause, or null if empty",
291 step: "[AST_Node?] the `for` update clause, or null if empty"
292 },
293 _walk: function(visitor) {
294 return visitor._visit(this, function() {
295 if (this.init) this.init._walk(visitor);
296 if (this.condition) this.condition._walk(visitor);
297 if (this.step) this.step._walk(visitor);
298 this.body._walk(visitor);
299 });
300 },
301 _children_backwards(push) {
302 push(this.body);
303 if (this.step) push(this.step);
304 if (this.condition) push(this.condition);
305 if (this.init) push(this.init);
306 },
307}, AST_IterationStatement);
308
309var AST_ForIn = DEFNODE("ForIn", "init object", {
310 $documentation: "A `for ... in` statement",
311 $propdoc: {
312 init: "[AST_Node] the `for/in` initialization code",
313 object: "[AST_Node] the object that we're looping through"
314 },
315 _walk: function(visitor) {
316 return visitor._visit(this, function() {
317 this.init._walk(visitor);
318 this.object._walk(visitor);
319 this.body._walk(visitor);
320 });
321 },
322 _children_backwards(push) {
323 push(this.body);
324 if (this.object) push(this.object);
325 if (this.init) push(this.init);
326 },
327}, AST_IterationStatement);
328
329var AST_ForOf = DEFNODE("ForOf", "await", {
330 $documentation: "A `for ... of` statement",
331}, AST_ForIn);
332
333var AST_With = DEFNODE("With", "expression", {
334 $documentation: "A `with` statement",
335 $propdoc: {
336 expression: "[AST_Node] the `with` expression"
337 },
338 _walk: function(visitor) {
339 return visitor._visit(this, function() {
340 this.expression._walk(visitor);
341 this.body._walk(visitor);
342 });
343 },
344 _children_backwards(push) {
345 push(this.body);
346 push(this.expression);
347 },
348}, AST_StatementWithBody);
349
350/* -----[ scope and functions ]----- */
351
352var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
353 $documentation: "Base class for all statements introducing a lexical scope",
354 $propdoc: {
355 variables: "[Map/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
356 functions: "[Map/S] like `variables`, but only lists function declarations",
357 uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
358 uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
359 parent_scope: "[AST_Scope?/S] link to the parent scope",
360 enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
361 cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
362 },
363 get_defun_scope: function() {
364 var self = this;
365 while (self.is_block_scope()) {
366 self = self.parent_scope;
367 }
368 return self;
369 },
370 clone: function(deep, toplevel) {
371 var node = this._clone(deep);
372 if (deep && this.variables && toplevel && !this._block_scope) {
373 node.figure_out_scope({}, {
374 toplevel: toplevel,
375 parent_scope: this.parent_scope
376 });
377 } else {
378 if (this.variables) node.variables = new Map(this.variables);
379 if (this.functions) node.functions = new Map(this.functions);
380 if (this.enclosed) node.enclosed = this.enclosed.slice();
381 if (this._block_scope) node._block_scope = this._block_scope;
382 }
383 return node;
384 },
385 pinned: function() {
386 return this.uses_eval || this.uses_with;
387 }
388}, AST_Block);
389
390var AST_Toplevel = DEFNODE("Toplevel", "globals", {
391 $documentation: "The toplevel scope",
392 $propdoc: {
393 globals: "[Map/S] a map of name -> SymbolDef for all undeclared names",
394 },
395 wrap_commonjs: function(name) {
396 var body = this.body;
397 var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");";
398 wrapped_tl = parse(wrapped_tl);
399 wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) {
400 if (node instanceof AST_Directive && node.value == "$ORIG") {
401 return MAP.splice(body);
402 }
403 }));
404 return wrapped_tl;
405 },
406 wrap_enclose: function(args_values) {
407 if (typeof args_values != "string") args_values = "";
408 var index = args_values.indexOf(":");
409 if (index < 0) index = args_values.length;
410 var body = this.body;
411 return parse([
412 "(function(",
413 args_values.slice(0, index),
414 '){"$ORIG"})(',
415 args_values.slice(index + 1),
416 ")"
417 ].join("")).transform(new TreeTransformer(function(node) {
418 if (node instanceof AST_Directive && node.value == "$ORIG") {
419 return MAP.splice(body);
420 }
421 }));
422 }
423}, AST_Scope);
424
425var AST_Expansion = DEFNODE("Expansion", "expression", {
426 $documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
427 $propdoc: {
428 expression: "[AST_Node] the thing to be expanded"
429 },
430 _walk: function(visitor) {
431 return visitor._visit(this, function() {
432 this.expression.walk(visitor);
433 });
434 },
435 _children_backwards(push) {
436 push(this.expression);
437 },
438});
439
440var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator async", {
441 $documentation: "Base class for functions",
442 $propdoc: {
443 name: "[AST_SymbolDeclaration?] the name of this function",
444 argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
445 uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
446 is_generator: "[boolean] is this a generator method",
447 async: "[boolean] is this method async",
448 },
449 args_as_names: function () {
450 var out = [];
451 for (var i = 0; i < this.argnames.length; i++) {
452 if (this.argnames[i] instanceof AST_Destructuring) {
453 out.push(...this.argnames[i].all_symbols());
454 } else {
455 out.push(this.argnames[i]);
456 }
457 }
458 return out;
459 },
460 _walk: function(visitor) {
461 return visitor._visit(this, function() {
462 if (this.name) this.name._walk(visitor);
463 var argnames = this.argnames;
464 for (var i = 0, len = argnames.length; i < len; i++) {
465 argnames[i]._walk(visitor);
466 }
467 walk_body(this, visitor);
468 });
469 },
470 _children_backwards(push) {
471 let i = this.body.length;
472 while (i--) push(this.body[i]);
473
474 i = this.argnames.length;
475 while (i--) push(this.argnames[i]);
476
477 if (this.name) push(this.name);
478 },
479}, AST_Scope);
480
481var AST_Accessor = DEFNODE("Accessor", null, {
482 $documentation: "A setter/getter function. The `name` property is always null."
483}, AST_Lambda);
484
485var AST_Function = DEFNODE("Function", null, {
486 $documentation: "A function expression"
487}, AST_Lambda);
488
489var AST_Arrow = DEFNODE("Arrow", null, {
490 $documentation: "An ES6 Arrow function ((a) => b)"
491}, AST_Lambda);
492
493var AST_Defun = DEFNODE("Defun", null, {
494 $documentation: "A function definition"
495}, AST_Lambda);
496
497/* -----[ DESTRUCTURING ]----- */
498var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
499 $documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
500 $propdoc: {
501 "names": "[AST_Node*] Array of properties or elements",
502 "is_array": "[Boolean] Whether the destructuring represents an object or array"
503 },
504 _walk: function(visitor) {
505 return visitor._visit(this, function() {
506 this.names.forEach(function(name) {
507 name._walk(visitor);
508 });
509 });
510 },
511 _children_backwards(push) {
512 let i = this.names.length;
513 while (i--) push(this.names[i]);
514 },
515 all_symbols: function() {
516 var out = [];
517 this.walk(new TreeWalker(function (node) {
518 if (node instanceof AST_Symbol) {
519 out.push(node);
520 }
521 }));
522 return out;
523 }
524});
525
526var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
527 $documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
528 $propdoc: {
529 template_string: "[AST_TemplateString] The template string",
530 prefix: "[AST_Node] The prefix, which will get called."
531 },
532 _walk: function(visitor) {
533 return visitor._visit(this, function () {
534 this.prefix._walk(visitor);
535 this.template_string._walk(visitor);
536 });
537 },
538 _children_backwards(push) {
539 push(this.template_string);
540 push(this.prefix);
541 },
542});
543
544var AST_TemplateString = DEFNODE("TemplateString", "segments", {
545 $documentation: "A template string literal",
546 $propdoc: {
547 segments: "[AST_Node*] One or more segments, starting with AST_TemplateSegment. AST_Node may follow AST_TemplateSegment, but each AST_Node must be followed by AST_TemplateSegment."
548 },
549 _walk: function(visitor) {
550 return visitor._visit(this, function() {
551 this.segments.forEach(function(seg) {
552 seg._walk(visitor);
553 });
554 });
555 },
556 _children_backwards(push) {
557 let i = this.segments.length;
558 while (i--) push(this.segments[i]);
559 }
560});
561
562var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", {
563 $documentation: "A segment of a template string literal",
564 $propdoc: {
565 value: "Content of the segment",
566 raw: "Raw content of the segment"
567 }
568});
569
570/* -----[ JUMPS ]----- */
571
572var AST_Jump = DEFNODE("Jump", null, {
573 $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
574}, AST_Statement);
575
576var AST_Exit = DEFNODE("Exit", "value", {
577 $documentation: "Base class for “exits” (`return` and `throw`)",
578 $propdoc: {
579 value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
580 },
581 _walk: function(visitor) {
582 return visitor._visit(this, this.value && function() {
583 this.value._walk(visitor);
584 });
585 },
586 _children_backwards(push) {
587 if (this.value) push(this.value);
588 },
589}, AST_Jump);
590
591var AST_Return = DEFNODE("Return", null, {
592 $documentation: "A `return` statement"
593}, AST_Exit);
594
595var AST_Throw = DEFNODE("Throw", null, {
596 $documentation: "A `throw` statement"
597}, AST_Exit);
598
599var AST_LoopControl = DEFNODE("LoopControl", "label", {
600 $documentation: "Base class for loop control statements (`break` and `continue`)",
601 $propdoc: {
602 label: "[AST_LabelRef?] the label, or null if none",
603 },
604 _walk: function(visitor) {
605 return visitor._visit(this, this.label && function() {
606 this.label._walk(visitor);
607 });
608 },
609 _children_backwards(push) {
610 if (this.label) push(this.label);
611 },
612}, AST_Jump);
613
614var AST_Break = DEFNODE("Break", null, {
615 $documentation: "A `break` statement"
616}, AST_LoopControl);
617
618var AST_Continue = DEFNODE("Continue", null, {
619 $documentation: "A `continue` statement"
620}, AST_LoopControl);
621
622var AST_Await = DEFNODE("Await", "expression", {
623 $documentation: "An `await` statement",
624 $propdoc: {
625 expression: "[AST_Node] the mandatory expression being awaited",
626 },
627 _walk: function(visitor) {
628 return visitor._visit(this, function() {
629 this.expression._walk(visitor);
630 });
631 },
632 _children_backwards(push) {
633 push(this.expression);
634 },
635});
636
637var AST_Yield = DEFNODE("Yield", "expression is_star", {
638 $documentation: "A `yield` statement",
639 $propdoc: {
640 expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
641 is_star: "[Boolean] Whether this is a yield or yield* statement"
642 },
643 _walk: function(visitor) {
644 return visitor._visit(this, this.expression && function() {
645 this.expression._walk(visitor);
646 });
647 },
648 _children_backwards(push) {
649 if (this.expression) push(this.expression);
650 }
651});
652
653/* -----[ IF ]----- */
654
655var AST_If = DEFNODE("If", "condition alternative", {
656 $documentation: "A `if` statement",
657 $propdoc: {
658 condition: "[AST_Node] the `if` condition",
659 alternative: "[AST_Statement?] the `else` part, or null if not present"
660 },
661 _walk: function(visitor) {
662 return visitor._visit(this, function() {
663 this.condition._walk(visitor);
664 this.body._walk(visitor);
665 if (this.alternative) this.alternative._walk(visitor);
666 });
667 },
668 _children_backwards(push) {
669 if (this.alternative) {
670 push(this.alternative);
671 }
672 push(this.body);
673 push(this.condition);
674 }
675}, AST_StatementWithBody);
676
677/* -----[ SWITCH ]----- */
678
679var AST_Switch = DEFNODE("Switch", "expression", {
680 $documentation: "A `switch` statement",
681 $propdoc: {
682 expression: "[AST_Node] the `switch` “discriminant”"
683 },
684 _walk: function(visitor) {
685 return visitor._visit(this, function() {
686 this.expression._walk(visitor);
687 walk_body(this, visitor);
688 });
689 },
690 _children_backwards(push) {
691 let i = this.body.length;
692 while (i--) push(this.body[i]);
693 push(this.expression);
694 }
695}, AST_Block);
696
697var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
698 $documentation: "Base class for `switch` branches",
699}, AST_Block);
700
701var AST_Default = DEFNODE("Default", null, {
702 $documentation: "A `default` switch branch",
703}, AST_SwitchBranch);
704
705var AST_Case = DEFNODE("Case", "expression", {
706 $documentation: "A `case` switch branch",
707 $propdoc: {
708 expression: "[AST_Node] the `case` expression"
709 },
710 _walk: function(visitor) {
711 return visitor._visit(this, function() {
712 this.expression._walk(visitor);
713 walk_body(this, visitor);
714 });
715 },
716 _children_backwards(push) {
717 let i = this.body.length;
718 while (i--) push(this.body[i]);
719 push(this.expression);
720 },
721}, AST_SwitchBranch);
722
723/* -----[ EXCEPTIONS ]----- */
724
725var AST_Try = DEFNODE("Try", "bcatch bfinally", {
726 $documentation: "A `try` statement",
727 $propdoc: {
728 bcatch: "[AST_Catch?] the catch block, or null if not present",
729 bfinally: "[AST_Finally?] the finally block, or null if not present"
730 },
731 _walk: function(visitor) {
732 return visitor._visit(this, function() {
733 walk_body(this, visitor);
734 if (this.bcatch) this.bcatch._walk(visitor);
735 if (this.bfinally) this.bfinally._walk(visitor);
736 });
737 },
738 _children_backwards(push) {
739 if (this.bfinally) push(this.bfinally);
740 if (this.bcatch) push(this.bcatch);
741 let i = this.body.length;
742 while (i--) push(this.body[i]);
743 },
744}, AST_Block);
745
746var AST_Catch = DEFNODE("Catch", "argname", {
747 $documentation: "A `catch` node; only makes sense as part of a `try` statement",
748 $propdoc: {
749 argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
750 },
751 _walk: function(visitor) {
752 return visitor._visit(this, function() {
753 if (this.argname) this.argname._walk(visitor);
754 walk_body(this, visitor);
755 });
756 },
757 _children_backwards(push) {
758 let i = this.body.length;
759 while (i--) push(this.body[i]);
760 if (this.argname) push(this.argname);
761 },
762}, AST_Block);
763
764var AST_Finally = DEFNODE("Finally", null, {
765 $documentation: "A `finally` node; only makes sense as part of a `try` statement"
766}, AST_Block);
767
768/* -----[ VAR/CONST ]----- */
769
770var AST_Definitions = DEFNODE("Definitions", "definitions", {
771 $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
772 $propdoc: {
773 definitions: "[AST_VarDef*] array of variable definitions"
774 },
775 _walk: function(visitor) {
776 return visitor._visit(this, function() {
777 var definitions = this.definitions;
778 for (var i = 0, len = definitions.length; i < len; i++) {
779 definitions[i]._walk(visitor);
780 }
781 });
782 },
783 _children_backwards(push) {
784 let i = this.definitions.length;
785 while (i--) push(this.definitions[i]);
786 },
787}, AST_Statement);
788
789var AST_Var = DEFNODE("Var", null, {
790 $documentation: "A `var` statement"
791}, AST_Definitions);
792
793var AST_Let = DEFNODE("Let", null, {
794 $documentation: "A `let` statement"
795}, AST_Definitions);
796
797var AST_Const = DEFNODE("Const", null, {
798 $documentation: "A `const` statement"
799}, AST_Definitions);
800
801var AST_VarDef = DEFNODE("VarDef", "name value", {
802 $documentation: "A variable declaration; only appears in a AST_Definitions node",
803 $propdoc: {
804 name: "[AST_Destructuring|AST_SymbolConst|AST_SymbolLet|AST_SymbolVar] name of the variable",
805 value: "[AST_Node?] initializer, or null of there's no initializer"
806 },
807 _walk: function(visitor) {
808 return visitor._visit(this, function() {
809 this.name._walk(visitor);
810 if (this.value) this.value._walk(visitor);
811 });
812 },
813 _children_backwards(push) {
814 if (this.value) push(this.value);
815 push(this.name);
816 },
817});
818
819var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
820 $documentation: "The part of the export/import statement that declare names from a module.",
821 $propdoc: {
822 foreign_name: "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)",
823 name: "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module."
824 },
825 _walk: function (visitor) {
826 return visitor._visit(this, function() {
827 this.foreign_name._walk(visitor);
828 this.name._walk(visitor);
829 });
830 },
831 _children_backwards(push) {
832 push(this.name);
833 push(this.foreign_name);
834 },
835});
836
837var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
838 $documentation: "An `import` statement",
839 $propdoc: {
840 imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
841 imported_names: "[AST_NameMapping*] The names of non-default imported variables",
842 module_name: "[AST_String] String literal describing where this module came from",
843 },
844 _walk: function(visitor) {
845 return visitor._visit(this, function() {
846 if (this.imported_name) {
847 this.imported_name._walk(visitor);
848 }
849 if (this.imported_names) {
850 this.imported_names.forEach(function(name_import) {
851 name_import._walk(visitor);
852 });
853 }
854 this.module_name._walk(visitor);
855 });
856 },
857 _children_backwards(push) {
858 push(this.module_name);
859 if (this.imported_names) {
860 let i = this.imported_names.length;
861 while (i--) push(this.imported_names[i]);
862 }
863 if (this.imported_name) push(this.imported_name);
864 },
865});
866
867var AST_ImportMeta = DEFNODE("ImportMeta", null, {
868 $documentation: "A reference to import.meta",
869});
870
871var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
872 $documentation: "An `export` statement",
873 $propdoc: {
874 exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
875 exported_value: "[AST_Node?] An exported value",
876 exported_names: "[AST_NameMapping*?] List of exported names",
877 module_name: "[AST_String?] Name of the file to load exports from",
878 is_default: "[Boolean] Whether this is the default exported value of this module"
879 },
880 _walk: function (visitor) {
881 return visitor._visit(this, function () {
882 if (this.exported_definition) {
883 this.exported_definition._walk(visitor);
884 }
885 if (this.exported_value) {
886 this.exported_value._walk(visitor);
887 }
888 if (this.exported_names) {
889 this.exported_names.forEach(function(name_export) {
890 name_export._walk(visitor);
891 });
892 }
893 if (this.module_name) {
894 this.module_name._walk(visitor);
895 }
896 });
897 },
898 _children_backwards(push) {
899 if (this.module_name) push(this.module_name);
900 if (this.exported_names) {
901 let i = this.exported_names.length;
902 while (i--) push(this.exported_names[i]);
903 }
904 if (this.exported_value) push(this.exported_value);
905 if (this.exported_definition) push(this.exported_definition);
906 }
907}, AST_Statement);
908
909/* -----[ OTHER ]----- */
910
911var AST_Call = DEFNODE("Call", "expression args optional _annotations", {
912 $documentation: "A function call expression",
913 $propdoc: {
914 expression: "[AST_Node] expression to invoke as function",
915 args: "[AST_Node*] array of arguments",
916 optional: "[boolean] whether this is an optional call (IE ?.() )",
917 _annotations: "[number] bitfield containing information about the call"
918 },
919 initialize() {
920 if (this._annotations == null) this._annotations = 0;
921 },
922 _walk(visitor) {
923 return visitor._visit(this, function() {
924 var args = this.args;
925 for (var i = 0, len = args.length; i < len; i++) {
926 args[i]._walk(visitor);
927 }
928 this.expression._walk(visitor); // TODO why do we need to crawl this last?
929 });
930 },
931 _children_backwards(push) {
932 let i = this.args.length;
933 while (i--) push(this.args[i]);
934 push(this.expression);
935 },
936});
937
938var AST_New = DEFNODE("New", null, {
939 $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
940}, AST_Call);
941
942var AST_Sequence = DEFNODE("Sequence", "expressions", {
943 $documentation: "A sequence expression (comma-separated expressions)",
944 $propdoc: {
945 expressions: "[AST_Node*] array of expressions (at least two)"
946 },
947 _walk: function(visitor) {
948 return visitor._visit(this, function() {
949 this.expressions.forEach(function(node) {
950 node._walk(visitor);
951 });
952 });
953 },
954 _children_backwards(push) {
955 let i = this.expressions.length;
956 while (i--) push(this.expressions[i]);
957 },
958});
959
960var AST_PropAccess = DEFNODE("PropAccess", "expression property optional", {
961 $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
962 $propdoc: {
963 expression: "[AST_Node] the “container” expression",
964 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",
965
966 optional: "[boolean] whether this is an optional property access (IE ?.)"
967 }
968});
969
970var AST_Dot = DEFNODE("Dot", "quote", {
971 $documentation: "A dotted property access expression",
972 $propdoc: {
973 quote: "[string] the original quote character when transformed from AST_Sub",
974 },
975 _walk: function(visitor) {
976 return visitor._visit(this, function() {
977 this.expression._walk(visitor);
978 });
979 },
980 _children_backwards(push) {
981 push(this.expression);
982 },
983}, AST_PropAccess);
984
985var AST_Sub = DEFNODE("Sub", null, {
986 $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
987 _walk: function(visitor) {
988 return visitor._visit(this, function() {
989 this.expression._walk(visitor);
990 this.property._walk(visitor);
991 });
992 },
993 _children_backwards(push) {
994 push(this.property);
995 push(this.expression);
996 },
997}, AST_PropAccess);
998
999var AST_Chain = DEFNODE("Chain", "expression", {
1000 $documentation: "A chain expression like a?.b?.(c)?.[d]",
1001 $propdoc: {
1002 expression: "[AST_Call|AST_Dot|AST_Sub] chain element."
1003 },
1004 _walk: function (visitor) {
1005 return visitor._visit(this, function() {
1006 this.expression._walk(visitor);
1007 });
1008 },
1009 _children_backwards(push) {
1010 push(this.expression);
1011 },
1012});
1013
1014var AST_Unary = DEFNODE("Unary", "operator expression", {
1015 $documentation: "Base class for unary expressions",
1016 $propdoc: {
1017 operator: "[string] the operator",
1018 expression: "[AST_Node] expression that this unary operator applies to"
1019 },
1020 _walk: function(visitor) {
1021 return visitor._visit(this, function() {
1022 this.expression._walk(visitor);
1023 });
1024 },
1025 _children_backwards(push) {
1026 push(this.expression);
1027 },
1028});
1029
1030var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
1031 $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
1032}, AST_Unary);
1033
1034var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
1035 $documentation: "Unary postfix expression, i.e. `i++`"
1036}, AST_Unary);
1037
1038var AST_Binary = DEFNODE("Binary", "operator left right", {
1039 $documentation: "Binary expression, i.e. `a + b`",
1040 $propdoc: {
1041 left: "[AST_Node] left-hand side expression",
1042 operator: "[string] the operator",
1043 right: "[AST_Node] right-hand side expression"
1044 },
1045 _walk: function(visitor) {
1046 return visitor._visit(this, function() {
1047 this.left._walk(visitor);
1048 this.right._walk(visitor);
1049 });
1050 },
1051 _children_backwards(push) {
1052 push(this.right);
1053 push(this.left);
1054 },
1055});
1056
1057var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
1058 $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
1059 $propdoc: {
1060 condition: "[AST_Node]",
1061 consequent: "[AST_Node]",
1062 alternative: "[AST_Node]"
1063 },
1064 _walk: function(visitor) {
1065 return visitor._visit(this, function() {
1066 this.condition._walk(visitor);
1067 this.consequent._walk(visitor);
1068 this.alternative._walk(visitor);
1069 });
1070 },
1071 _children_backwards(push) {
1072 push(this.alternative);
1073 push(this.consequent);
1074 push(this.condition);
1075 },
1076});
1077
1078var AST_Assign = DEFNODE("Assign", null, {
1079 $documentation: "An assignment expression — `a = b + 5`",
1080}, AST_Binary);
1081
1082var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
1083 $documentation: "A default assignment expression like in `(a = 3) => a`"
1084}, AST_Binary);
1085
1086/* -----[ LITERALS ]----- */
1087
1088var AST_Array = DEFNODE("Array", "elements", {
1089 $documentation: "An array literal",
1090 $propdoc: {
1091 elements: "[AST_Node*] array of elements"
1092 },
1093 _walk: function(visitor) {
1094 return visitor._visit(this, function() {
1095 var elements = this.elements;
1096 for (var i = 0, len = elements.length; i < len; i++) {
1097 elements[i]._walk(visitor);
1098 }
1099 });
1100 },
1101 _children_backwards(push) {
1102 let i = this.elements.length;
1103 while (i--) push(this.elements[i]);
1104 },
1105});
1106
1107var AST_Object = DEFNODE("Object", "properties", {
1108 $documentation: "An object literal",
1109 $propdoc: {
1110 properties: "[AST_ObjectProperty*] array of properties"
1111 },
1112 _walk: function(visitor) {
1113 return visitor._visit(this, function() {
1114 var properties = this.properties;
1115 for (var i = 0, len = properties.length; i < len; i++) {
1116 properties[i]._walk(visitor);
1117 }
1118 });
1119 },
1120 _children_backwards(push) {
1121 let i = this.properties.length;
1122 while (i--) push(this.properties[i]);
1123 },
1124});
1125
1126var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
1127 $documentation: "Base class for literal object properties",
1128 $propdoc: {
1129 key: "[string|AST_Node] property name. For ObjectKeyVal this is a string. For getters, setters and computed property this is an AST_Node.",
1130 value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
1131 },
1132 _walk: function(visitor) {
1133 return visitor._visit(this, function() {
1134 if (this.key instanceof AST_Node)
1135 this.key._walk(visitor);
1136 this.value._walk(visitor);
1137 });
1138 },
1139 _children_backwards(push) {
1140 push(this.value);
1141 if (this.key instanceof AST_Node) push(this.key);
1142 }
1143});
1144
1145var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
1146 $documentation: "A key: value object property",
1147 $propdoc: {
1148 quote: "[string] the original quote character"
1149 },
1150 computed_key() {
1151 return this.key instanceof AST_Node;
1152 }
1153}, AST_ObjectProperty);
1154
1155var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", {
1156 $propdoc: {
1157 quote: "[string|undefined] the original quote character, if any",
1158 static: "[boolean] whether this is a static setter (classes only)"
1159 },
1160 $documentation: "An object setter property",
1161 computed_key() {
1162 return !(this.key instanceof AST_SymbolMethod);
1163 }
1164}, AST_ObjectProperty);
1165
1166var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
1167 $propdoc: {
1168 quote: "[string|undefined] the original quote character, if any",
1169 static: "[boolean] whether this is a static getter (classes only)"
1170 },
1171 $documentation: "An object getter property",
1172 computed_key() {
1173 return !(this.key instanceof AST_SymbolMethod);
1174 }
1175}, AST_ObjectProperty);
1176
1177var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator async", {
1178 $propdoc: {
1179 quote: "[string|undefined] the original quote character, if any",
1180 static: "[boolean] is this method static (classes only)",
1181 is_generator: "[boolean] is this a generator method",
1182 async: "[boolean] is this method async",
1183 },
1184 $documentation: "An ES6 concise method inside an object or class",
1185 computed_key() {
1186 return !(this.key instanceof AST_SymbolMethod);
1187 }
1188}, AST_ObjectProperty);
1189
1190var AST_Class = DEFNODE("Class", "name extends properties", {
1191 $propdoc: {
1192 name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
1193 extends: "[AST_Node]? optional parent class",
1194 properties: "[AST_ObjectProperty*] array of properties"
1195 },
1196 $documentation: "An ES6 class",
1197 _walk: function(visitor) {
1198 return visitor._visit(this, function() {
1199 if (this.name) {
1200 this.name._walk(visitor);
1201 }
1202 if (this.extends) {
1203 this.extends._walk(visitor);
1204 }
1205 this.properties.forEach((prop) => prop._walk(visitor));
1206 });
1207 },
1208 _children_backwards(push) {
1209 let i = this.properties.length;
1210 while (i--) push(this.properties[i]);
1211 if (this.extends) push(this.extends);
1212 if (this.name) push(this.name);
1213 },
1214}, AST_Scope /* TODO a class might have a scope but it's not a scope */);
1215
1216var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
1217 $documentation: "A class property",
1218 $propdoc: {
1219 static: "[boolean] whether this is a static key",
1220 quote: "[string] which quote is being used"
1221 },
1222 _walk: function(visitor) {
1223 return visitor._visit(this, function() {
1224 if (this.key instanceof AST_Node)
1225 this.key._walk(visitor);
1226 if (this.value instanceof AST_Node)
1227 this.value._walk(visitor);
1228 });
1229 },
1230 _children_backwards(push) {
1231 if (this.value instanceof AST_Node) push(this.value);
1232 if (this.key instanceof AST_Node) push(this.key);
1233 },
1234 computed_key() {
1235 return !(this.key instanceof AST_SymbolClassProperty);
1236 }
1237}, AST_ObjectProperty);
1238
1239var AST_DefClass = DEFNODE("DefClass", null, {
1240 $documentation: "A class definition",
1241}, AST_Class);
1242
1243var AST_ClassExpression = DEFNODE("ClassExpression", null, {
1244 $documentation: "A class expression."
1245}, AST_Class);
1246
1247var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
1248 $propdoc: {
1249 name: "[string] name of this symbol",
1250 scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
1251 thedef: "[SymbolDef/S] the definition of this symbol"
1252 },
1253 $documentation: "Base class for all symbols"
1254});
1255
1256var AST_NewTarget = DEFNODE("NewTarget", null, {
1257 $documentation: "A reference to new.target"
1258});
1259
1260var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
1261 $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
1262}, AST_Symbol);
1263
1264var AST_SymbolVar = DEFNODE("SymbolVar", null, {
1265 $documentation: "Symbol defining a variable",
1266}, AST_SymbolDeclaration);
1267
1268var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
1269 $documentation: "Base class for block-scoped declaration symbols"
1270}, AST_SymbolDeclaration);
1271
1272var AST_SymbolConst = DEFNODE("SymbolConst", null, {
1273 $documentation: "A constant declaration"
1274}, AST_SymbolBlockDeclaration);
1275
1276var AST_SymbolLet = DEFNODE("SymbolLet", null, {
1277 $documentation: "A block-scoped `let` declaration"
1278}, AST_SymbolBlockDeclaration);
1279
1280var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
1281 $documentation: "Symbol naming a function argument",
1282}, AST_SymbolVar);
1283
1284var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
1285 $documentation: "Symbol defining a function",
1286}, AST_SymbolDeclaration);
1287
1288var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
1289 $documentation: "Symbol in an object defining a method",
1290}, AST_Symbol);
1291
1292var AST_SymbolClassProperty = DEFNODE("SymbolClassProperty", null, {
1293 $documentation: "Symbol for a class property",
1294}, AST_Symbol);
1295
1296var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
1297 $documentation: "Symbol naming a function expression",
1298}, AST_SymbolDeclaration);
1299
1300var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
1301 $documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
1302}, AST_SymbolBlockDeclaration);
1303
1304var AST_SymbolClass = DEFNODE("SymbolClass", null, {
1305 $documentation: "Symbol naming a class's name. Lexically scoped to the class."
1306}, AST_SymbolDeclaration);
1307
1308var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
1309 $documentation: "Symbol naming the exception in catch",
1310}, AST_SymbolBlockDeclaration);
1311
1312var AST_SymbolImport = DEFNODE("SymbolImport", null, {
1313 $documentation: "Symbol referring to an imported name",
1314}, AST_SymbolBlockDeclaration);
1315
1316var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
1317 $documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
1318}, AST_Symbol);
1319
1320var AST_Label = DEFNODE("Label", "references", {
1321 $documentation: "Symbol naming a label (declaration)",
1322 $propdoc: {
1323 references: "[AST_LoopControl*] a list of nodes referring to this label"
1324 },
1325 initialize: function() {
1326 this.references = [];
1327 this.thedef = this;
1328 }
1329}, AST_Symbol);
1330
1331var AST_SymbolRef = DEFNODE("SymbolRef", null, {
1332 $documentation: "Reference to some symbol (not definition/declaration)",
1333}, AST_Symbol);
1334
1335var AST_SymbolExport = DEFNODE("SymbolExport", null, {
1336 $documentation: "Symbol referring to a name to export",
1337}, AST_SymbolRef);
1338
1339var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", null, {
1340 $documentation: "A symbol exported from this module, but it is used in the other module, and its real name is irrelevant for this module's purposes",
1341}, AST_Symbol);
1342
1343var AST_LabelRef = DEFNODE("LabelRef", null, {
1344 $documentation: "Reference to a label symbol",
1345}, AST_Symbol);
1346
1347var AST_This = DEFNODE("This", null, {
1348 $documentation: "The `this` symbol",
1349}, AST_Symbol);
1350
1351var AST_Super = DEFNODE("Super", null, {
1352 $documentation: "The `super` symbol",
1353}, AST_This);
1354
1355var AST_Constant = DEFNODE("Constant", null, {
1356 $documentation: "Base class for all constants",
1357 getValue: function() {
1358 return this.value;
1359 }
1360});
1361
1362var AST_String = DEFNODE("String", "value quote", {
1363 $documentation: "A string literal",
1364 $propdoc: {
1365 value: "[string] the contents of this string",
1366 quote: "[string] the original quote character"
1367 }
1368}, AST_Constant);
1369
1370var AST_Number = DEFNODE("Number", "value literal", {
1371 $documentation: "A number literal",
1372 $propdoc: {
1373 value: "[number] the numeric value",
1374 literal: "[string] numeric value as string (optional)"
1375 }
1376}, AST_Constant);
1377
1378var AST_BigInt = DEFNODE("BigInt", "value", {
1379 $documentation: "A big int literal",
1380 $propdoc: {
1381 value: "[string] big int value"
1382 }
1383}, AST_Constant);
1384
1385var AST_RegExp = DEFNODE("RegExp", "value", {
1386 $documentation: "A regexp literal",
1387 $propdoc: {
1388 value: "[RegExp] the actual regexp",
1389 }
1390}, AST_Constant);
1391
1392var AST_Atom = DEFNODE("Atom", null, {
1393 $documentation: "Base class for atoms",
1394}, AST_Constant);
1395
1396var AST_Null = DEFNODE("Null", null, {
1397 $documentation: "The `null` atom",
1398 value: null
1399}, AST_Atom);
1400
1401var AST_NaN = DEFNODE("NaN", null, {
1402 $documentation: "The impossible value",
1403 value: 0/0
1404}, AST_Atom);
1405
1406var AST_Undefined = DEFNODE("Undefined", null, {
1407 $documentation: "The `undefined` value",
1408 value: (function() {}())
1409}, AST_Atom);
1410
1411var AST_Hole = DEFNODE("Hole", null, {
1412 $documentation: "A hole in an array",
1413 value: (function() {}())
1414}, AST_Atom);
1415
1416var AST_Infinity = DEFNODE("Infinity", null, {
1417 $documentation: "The `Infinity` value",
1418 value: 1/0
1419}, AST_Atom);
1420
1421var AST_Boolean = DEFNODE("Boolean", null, {
1422 $documentation: "Base class for booleans",
1423}, AST_Atom);
1424
1425var AST_False = DEFNODE("False", null, {
1426 $documentation: "The `false` atom",
1427 value: false
1428}, AST_Boolean);
1429
1430var AST_True = DEFNODE("True", null, {
1431 $documentation: "The `true` atom",
1432 value: true
1433}, AST_Boolean);
1434
1435/* -----[ Walk function ]---- */
1436
1437/**
1438 * Walk nodes in depth-first search fashion.
1439 * Callback can return `walk_abort` symbol to stop iteration.
1440 * It can also return `true` to stop iteration just for child nodes.
1441 * Iteration can be stopped and continued by passing the `to_visit` argument,
1442 * which is given to the callback in the second argument.
1443 **/
1444function walk(node, cb, to_visit = [node]) {
1445 const push = to_visit.push.bind(to_visit);
1446 while (to_visit.length) {
1447 const node = to_visit.pop();
1448 const ret = cb(node, to_visit);
1449
1450 if (ret) {
1451 if (ret === walk_abort) return true;
1452 continue;
1453 }
1454
1455 node._children_backwards(push);
1456 }
1457 return false;
1458}
1459
1460function walk_parent(node, cb, initial_stack) {
1461 const to_visit = [node];
1462 const push = to_visit.push.bind(to_visit);
1463 const stack = initial_stack ? initial_stack.slice() : [];
1464 const parent_pop_indices = [];
1465
1466 let current;
1467
1468 const info = {
1469 parent: (n = 0) => {
1470 if (n === -1) {
1471 return current;
1472 }
1473
1474 // [ p1 p0 ] [ 1 0 ]
1475 if (initial_stack && n >= stack.length) {
1476 n -= stack.length;
1477 return initial_stack[
1478 initial_stack.length - (n + 1)
1479 ];
1480 }
1481
1482 return stack[stack.length - (1 + n)];
1483 },
1484 };
1485
1486 while (to_visit.length) {
1487 current = to_visit.pop();
1488
1489 while (
1490 parent_pop_indices.length &&
1491 to_visit.length == parent_pop_indices[parent_pop_indices.length - 1]
1492 ) {
1493 stack.pop();
1494 parent_pop_indices.pop();
1495 }
1496
1497 const ret = cb(current, info);
1498
1499 if (ret) {
1500 if (ret === walk_abort) return true;
1501 continue;
1502 }
1503
1504 const visit_length = to_visit.length;
1505
1506 current._children_backwards(push);
1507
1508 // Push only if we're going to traverse the children
1509 if (to_visit.length > visit_length) {
1510 stack.push(current);
1511 parent_pop_indices.push(visit_length - 1);
1512 }
1513 }
1514
1515 return false;
1516}
1517
1518const walk_abort = Symbol("abort walk");
1519
1520/* -----[ TreeWalker ]----- */
1521
1522class TreeWalker {
1523 constructor(callback) {
1524 this.visit = callback;
1525 this.stack = [];
1526 this.directives = Object.create(null);
1527 }
1528
1529 _visit(node, descend) {
1530 this.push(node);
1531 var ret = this.visit(node, descend ? function() {
1532 descend.call(node);
1533 } : noop);
1534 if (!ret && descend) {
1535 descend.call(node);
1536 }
1537 this.pop();
1538 return ret;
1539 }
1540
1541 parent(n) {
1542 return this.stack[this.stack.length - 2 - (n || 0)];
1543 }
1544
1545 push(node) {
1546 if (node instanceof AST_Lambda) {
1547 this.directives = Object.create(this.directives);
1548 } else if (node instanceof AST_Directive && !this.directives[node.value]) {
1549 this.directives[node.value] = node;
1550 } else if (node instanceof AST_Class) {
1551 this.directives = Object.create(this.directives);
1552 if (!this.directives["use strict"]) {
1553 this.directives["use strict"] = node;
1554 }
1555 }
1556 this.stack.push(node);
1557 }
1558
1559 pop() {
1560 var node = this.stack.pop();
1561 if (node instanceof AST_Lambda || node instanceof AST_Class) {
1562 this.directives = Object.getPrototypeOf(this.directives);
1563 }
1564 }
1565
1566 self() {
1567 return this.stack[this.stack.length - 1];
1568 }
1569
1570 find_parent(type) {
1571 var stack = this.stack;
1572 for (var i = stack.length; --i >= 0;) {
1573 var x = stack[i];
1574 if (x instanceof type) return x;
1575 }
1576 }
1577
1578 has_directive(type) {
1579 var dir = this.directives[type];
1580 if (dir) return dir;
1581 var node = this.stack[this.stack.length - 1];
1582 if (node instanceof AST_Scope && node.body) {
1583 for (var i = 0; i < node.body.length; ++i) {
1584 var st = node.body[i];
1585 if (!(st instanceof AST_Directive)) break;
1586 if (st.value == type) return st;
1587 }
1588 }
1589 }
1590
1591 loopcontrol_target(node) {
1592 var stack = this.stack;
1593 if (node.label) for (var i = stack.length; --i >= 0;) {
1594 var x = stack[i];
1595 if (x instanceof AST_LabeledStatement && x.label.name == node.label.name)
1596 return x.body;
1597 } else for (var i = stack.length; --i >= 0;) {
1598 var x = stack[i];
1599 if (x instanceof AST_IterationStatement
1600 || node instanceof AST_Break && x instanceof AST_Switch)
1601 return x;
1602 }
1603 }
1604}
1605
1606// Tree transformer helpers.
1607class TreeTransformer extends TreeWalker {
1608 constructor(before, after) {
1609 super();
1610 this.before = before;
1611 this.after = after;
1612 }
1613}
1614
1615const _PURE = 0b00000001;
1616const _INLINE = 0b00000010;
1617const _NOINLINE = 0b00000100;
1618
1619export {
1620 AST_Accessor,
1621 AST_Array,
1622 AST_Arrow,
1623 AST_Assign,
1624 AST_Atom,
1625 AST_Await,
1626 AST_BigInt,
1627 AST_Binary,
1628 AST_Block,
1629 AST_BlockStatement,
1630 AST_Boolean,
1631 AST_Break,
1632 AST_Call,
1633 AST_Case,
1634 AST_Catch,
1635 AST_Chain,
1636 AST_Class,
1637 AST_ClassExpression,
1638 AST_ClassProperty,
1639 AST_ConciseMethod,
1640 AST_Conditional,
1641 AST_Const,
1642 AST_Constant,
1643 AST_Continue,
1644 AST_Debugger,
1645 AST_Default,
1646 AST_DefaultAssign,
1647 AST_DefClass,
1648 AST_Definitions,
1649 AST_Defun,
1650 AST_Destructuring,
1651 AST_Directive,
1652 AST_Do,
1653 AST_Dot,
1654 AST_DWLoop,
1655 AST_EmptyStatement,
1656 AST_Exit,
1657 AST_Expansion,
1658 AST_Export,
1659 AST_False,
1660 AST_Finally,
1661 AST_For,
1662 AST_ForIn,
1663 AST_ForOf,
1664 AST_Function,
1665 AST_Hole,
1666 AST_If,
1667 AST_Import,
1668 AST_ImportMeta,
1669 AST_Infinity,
1670 AST_IterationStatement,
1671 AST_Jump,
1672 AST_Label,
1673 AST_LabeledStatement,
1674 AST_LabelRef,
1675 AST_Lambda,
1676 AST_Let,
1677 AST_LoopControl,
1678 AST_NameMapping,
1679 AST_NaN,
1680 AST_New,
1681 AST_NewTarget,
1682 AST_Node,
1683 AST_Null,
1684 AST_Number,
1685 AST_Object,
1686 AST_ObjectGetter,
1687 AST_ObjectKeyVal,
1688 AST_ObjectProperty,
1689 AST_ObjectSetter,
1690 AST_PrefixedTemplateString,
1691 AST_PropAccess,
1692 AST_RegExp,
1693 AST_Return,
1694 AST_Scope,
1695 AST_Sequence,
1696 AST_SimpleStatement,
1697 AST_Statement,
1698 AST_StatementWithBody,
1699 AST_String,
1700 AST_Sub,
1701 AST_Super,
1702 AST_Switch,
1703 AST_SwitchBranch,
1704 AST_Symbol,
1705 AST_SymbolBlockDeclaration,
1706 AST_SymbolCatch,
1707 AST_SymbolClass,
1708 AST_SymbolClassProperty,
1709 AST_SymbolConst,
1710 AST_SymbolDeclaration,
1711 AST_SymbolDefClass,
1712 AST_SymbolDefun,
1713 AST_SymbolExport,
1714 AST_SymbolExportForeign,
1715 AST_SymbolFunarg,
1716 AST_SymbolImport,
1717 AST_SymbolImportForeign,
1718 AST_SymbolLambda,
1719 AST_SymbolLet,
1720 AST_SymbolMethod,
1721 AST_SymbolRef,
1722 AST_SymbolVar,
1723 AST_TemplateSegment,
1724 AST_TemplateString,
1725 AST_This,
1726 AST_Throw,
1727 AST_Token,
1728 AST_Toplevel,
1729 AST_True,
1730 AST_Try,
1731 AST_Unary,
1732 AST_UnaryPostfix,
1733 AST_UnaryPrefix,
1734 AST_Undefined,
1735 AST_Var,
1736 AST_VarDef,
1737 AST_While,
1738 AST_With,
1739 AST_Yield,
1740
1741 // Walkers
1742 TreeTransformer,
1743 TreeWalker,
1744 walk,
1745 walk_abort,
1746 walk_body,
1747 walk_parent,
1748
1749 // annotations
1750 _INLINE,
1751 _NOINLINE,
1752 _PURE,
1753};