HEX
Server: Apache
System: Linux webd004.cluster130.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User: frenchy (106757)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/frenchy/www/french-american.org/current/node_modules/uglyfly-js/lib/AST.js
/***********************************************************************
  Copyright 2014      (c) Saair Quaderi <saair.quaderi@gmail.com>
  Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>

  UglyflyJS sourcecode can be found here:
    https://github.com/quaderi/uglyflyjs

  UglyflyJS (by Saair) is a fork of UglifyJS2 (by Mihai Bazon)
  Both libraries are released under the BSD 2-Clause License.
 ***********************************************************************/

/*globals define, module, require */

((typeof define === "function") ? define :
        function () { "use strict"; require('./nd').apply(module, arguments); })(
    "AST",
    [
        //no dependencies
    ],
    function () {
        "use strict";
        /*jslint plusplus: true, nomen: true */
        var AST = {},
            UNDEF = (function () {
                return;
            }()),
            NAN = (function () {
                var num = 0,
                    denom = 0;
                return num / denom;
            }()),
            INFINITY = (function () {
                var num = 1,
                    denom = 0;
                return num / denom;
            }());

        function def_node(type, props, methods, BaseType) {
            var self_props,
                proto,
                NodeType,
                method;
            if (arguments.length < 4) {
                BaseType = AST.Node;
            }
            props = props ? props.split(/\s+/) : [];
            self_props = props;
            if (BaseType && BaseType.PROPS) {
                props = props.concat(BaseType.PROPS);
            }
            proto = BaseType && new BaseType();
            NodeType = (function (props, shouldInit) {
                return function (arg) {
                    /*jslint plusplus: true */
                    var i, prop;
                    for (i = props.length - 1; i >= 0; --i) {
                        prop = props[i];
                        if (arg) {
                            this[prop] = arg[prop];
                        }
                    }
                    if (shouldInit && (typeof this.initialize === "function")) {
                        this.initialize();
                    }
                };
            }(props.slice(), (proto && proto.initialize) || (methods && methods.initialize)));

            if (proto) {
                NodeType.prototype = proto;
                NodeType.BASE = BaseType;
            }
            if (BaseType) {
                BaseType.SUBCLASSES.push(NodeType);
            }
            NodeType.prototype.CTOR = NodeType;
            NodeType.PROPS = props || null;
            NodeType.SELF_PROPS = self_props;
            NodeType.SUBCLASSES = [];
            if (type) {
                NodeType.prototype.TYPE = NodeType.TYPE = type;
            }
            if (methods) {
                for (method in methods) {
                    if (methods.hasOwnProperty(method)) {
                        if (method.charAt(0) === "$") {
                            NodeType[method.substr(1)] = methods[method];
                        } else {
                            NodeType.prototype[method] = methods[method];
                        }
                    }
                }
            }
            NodeType.prototype["instanceof_" + type] = true;
            NodeType.DEFMETHOD = function (name, method) {
                this.prototype[name] = method;
            };
            return NodeType;
        }

        function walk_body(node, visitor) {
            if (node.body instanceof AST.Statement) {
                node.body._walk(visitor);
            } else {
                node.body.forEach(function (stat) {
                    stat._walk(visitor);
                });
            }
        }

        AST.def_node = def_node;

        AST.Token = def_node("Token", "type value line col pos endline endcol endpos nlb comments_before file", {
        }, null);

        AST.Node = def_node("Node", "start end", {
            clone: function () {
                return new this.CTOR(this);
            },
            $documentation: "Base class of all AST nodes",
            $propdoc: {
                start: "[AST.Token] The first token of this node",
                end: "[AST.Token] The last token of this node"
            },
            _walk: function (visitor) {
                return visitor._visit(this);
            },
            walk: function (visitor) {
                return this._walk(visitor); // not sure the indirection will be any help
            }
        }, null);


        /* -----[ statements ]----- */

        AST.Statement = def_node("Statement", null, {
            $documentation: "Base class of all statements"
        });

        AST.Debugger = def_node("Debugger", null, {
            $documentation: "Represents a debugger statement"
        }, AST.Statement);

        AST.Directive = def_node("Directive", "value scope quote", {
            $documentation: "Represents a directive, like \"use strict\";",
            $propdoc: {
                value: "[string] The value of this directive as a plain string (it's not an AST.String!)",
                scope: "[AST.Scope/S] The scope that this directive affects",
                quote: "[string] the original quote character"
            }
        }, AST.Statement);

        AST.SimpleStatement = def_node("SimpleStatement", "body", {
            $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
            $propdoc: {
                body: "[AST.Node] an expression node (should not be instanceof AST.Statement)"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.body._walk(visitor);
                });
            }
        }, AST.Statement);

        AST.Block = def_node("Block", "body", {
            $documentation: "A body of statements (usually bracketed)",
            $propdoc: {
                body: "[AST.Statement*] an array of statements"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    walk_body(this, visitor);
                });
            }
        }, AST.Statement);

        AST.BlockStatement = def_node("BlockStatement", null, {
            $documentation: "A block statement"
        }, AST.Block);

        AST.EmptyStatement = def_node("EmptyStatement", null, {
            $documentation: "The empty statement (empty block or simply a semicolon)",
            _walk: function (visitor) {
                return visitor._visit(this);
            }
        }, AST.Statement);

        AST.StatementWithBody = def_node("StatementWithBody", "body", {
            $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
            $propdoc: {
                body: "[AST.Statement] the body; this should always be present, even if it's an AST.EmptyStatement"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.body._walk(visitor);
                });
            }
        }, AST.Statement);

        AST.LabeledStatement = def_node("LabeledStatement", "label", {
            $documentation: "Statement with a label",
            $propdoc: {
                label: "[AST.Label] a label definition"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.label._walk(visitor);
                    this.body._walk(visitor);
                });
            }
        }, AST.StatementWithBody);

        AST.IterationStatement = def_node("IterationStatement", null, {
            $documentation: "Internal class.  All loops inherit from it."
        }, AST.StatementWithBody);

        AST.DWLoop = def_node("DWLoop", "condition", {
            $documentation: "Base class for do/while statements",
            $propdoc: {
                condition: "[AST.Node] the loop condition.  Should not be instanceof AST.Statement"
            }
        }, AST.IterationStatement);

        AST.Do = def_node("Do", null, {
            $documentation: "A `do` statement",
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.body._walk(visitor);
                    this.condition._walk(visitor);
                });
            }
        }, AST.DWLoop);

        AST.While = def_node("While", null, {
            $documentation: "A `while` statement",
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.condition._walk(visitor);
                    this.body._walk(visitor);
                });
            }
        }, AST.DWLoop);

        AST.For = def_node("For", "init condition step", {
            $documentation: "A `for` statement",
            $propdoc: {
                init: "[AST.Node?] the `for` initialization code, or null if empty",
                condition: "[AST.Node?] the `for` termination clause, or null if empty",
                step: "[AST.Node?] the `for` update clause, or null if empty"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    if (this.init) {
                        this.init._walk(visitor);
                    }
                    if (this.condition) {
                        this.condition._walk(visitor);
                    }
                    if (this.step) {
                        this.step._walk(visitor);
                    }
                    this.body._walk(visitor);
                });
            }
        }, AST.IterationStatement);

        AST.ForIn = def_node("ForIn", "init name object", {
            $documentation: "A `for ... in` statement",
            $propdoc: {
                init: "[AST.Node] the `for/in` initialization code",
                name: "[AST.SymbolRef?] the loop variable, only if `init` is AST.Var",
                object: "[AST.Node] the object that we're looping through"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.init._walk(visitor);
                    this.object._walk(visitor);
                    this.body._walk(visitor);
                });
            }
        }, AST.IterationStatement);

        AST.With = def_node("With", "expression", {
            $documentation: "A `with` statement",
            $propdoc: {
                expression: "[AST.Node] the `with` expression"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                    this.body._walk(visitor);
                });
            }
        }, AST.StatementWithBody);

        /* -----[ scope and functions ]----- */

        AST.Scope = def_node("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
            $documentation: "Base class for all statements introducing a lexical scope",
            $propdoc: {
                directives: "[string*/S] an array of directives declared in this scope",
                variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
                functions: "[Object/S] like `variables`, but only lists function declarations",
                uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
                uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
                parent_scope: "[AST.Scope?/S] link to the parent scope",
                enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
                cname: "[integer/S] current index for mangling variables (used internally by the mangler)"
            }
        }, AST.Block);

        AST.Toplevel = def_node("Toplevel", "globals", {
            $documentation: "The toplevel scope",
            $propdoc: {
                globals: "[Object/S] a map of name -> SymbolDef for all undeclared names"
            }
        }, AST.Scope);

        AST.Lambda = def_node("Lambda", "name argnames uses_arguments", {
            $documentation: "Base class for functions",
            $propdoc: {
                name: "[AST.SymbolDeclaration?] the name of this function",
                argnames: "[AST.SymbolFunarg*] array of function arguments",
                uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    if (this.name) {
                        this.name._walk(visitor);
                    }
                    this.argnames.forEach(function (arg) {
                        arg._walk(visitor);
                    });
                    walk_body(this, visitor);
                });
            }
        }, AST.Scope);

        AST.Accessor = def_node("Accessor", null, {
            $documentation: "A setter/getter function.  The `name` property is always null."
        }, AST.Lambda);

        AST.Function = def_node("Function", null, {
            $documentation: "A function expression"
        }, AST.Lambda);

        AST.Defun = def_node("Defun", null, {
            $documentation: "A function definition"
        }, AST.Lambda);

        /* -----[ JUMPS ]----- */

        AST.Jump = def_node("Jump", null, {
            $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
        }, AST.Statement);

        AST.Exit = def_node("Exit", "value", {
            $documentation: "Base class for “exits” (`return` and `throw`)",
            $propdoc: {
                value: "[AST.Node?] the value returned or thrown by this statement; could be null for AST.Return"
            },
            _walk: function (visitor) {
                return visitor._visit(this, this.value && function () {
                    this.value._walk(visitor);
                });
            }
        }, AST.Jump);

        AST.Return = def_node("Return", null, {
            $documentation: "A `return` statement"
        }, AST.Exit);

        AST.Throw = def_node("Throw", null, {
            $documentation: "A `throw` statement"
        }, AST.Exit);

        AST.LoopControl = def_node("LoopControl", "label", {
            $documentation: "Base class for loop control statements (`break` and `continue`)",
            $propdoc: {
                label: "[AST.LabelRef?] the label, or null if none"
            },
            _walk: function (visitor) {
                return visitor._visit(this, this.label && function () {
                    this.label._walk(visitor);
                });
            }
        }, AST.Jump);

        AST.Break = def_node("Break", null, {
            $documentation: "A `break` statement"
        }, AST.LoopControl);

        AST.Continue = def_node("Continue", null, {
            $documentation: "A `continue` statement"
        }, AST.LoopControl);

        /* -----[ IF ]----- */
        AST.If = def_node("If", "condition alternative", {
            $documentation: "A `if` statement",
            $propdoc: {
                condition: "[AST.Node] the `if` condition",
                alternative: "[AST.Statement?] the `else` part, or null if not present"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.condition._walk(visitor);
                    this.body._walk(visitor);
                    if (this.alternative) {
                        this.alternative._walk(visitor);
                    }
                });
            }
        }, AST.StatementWithBody);

        /* -----[ SWITCH ]----- */

        AST.Switch = def_node("Switch", "expression", {
            $documentation: "A `switch` statement",
            $propdoc: {
                expression: "[AST.Node] the `switch` “discriminant”"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                    walk_body(this, visitor);
                });
            }
        }, AST.Block);

        AST.SwitchBranch = def_node("SwitchBranch", null, {
            $documentation: "Base class for `switch` branches"
        }, AST.Block);

        AST.Default = def_node("Default", null, {
            $documentation: "A `default` switch branch"
        }, AST.SwitchBranch);

        AST.Case = def_node("Case", "expression", {
            $documentation: "A `case` switch branch",
            $propdoc: {
                expression: "[AST.Node] the `case` expression"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                    walk_body(this, visitor);
                });
            }
        }, AST.SwitchBranch);

        /* -----[ EXCEPTIONS ]----- */

        AST.Try = def_node("Try", "bcatch bfinally", {
            $documentation: "A `try` statement",
            $propdoc: {
                bcatch: "[AST.Catch?] the catch block, or null if not present",
                bfinally: "[AST.Finally?] the finally block, or null if not present"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    walk_body(this, visitor);
                    if (this.bcatch) {
                        this.bcatch._walk(visitor);
                    }
                    if (this.bfinally) {
                        this.bfinally._walk(visitor);
                    }
                });
            }
        }, AST.Block);

        AST.Catch = def_node("Catch", "argname", {
            $documentation: "A `catch` node; only makes sense as part of a `try` statement",
            $propdoc: {
                argname: "[AST.SymbolCatch] symbol for the exception"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.argname._walk(visitor);
                    walk_body(this, visitor);
                });
            }
        }, AST.Block);

        AST.Finally = def_node("Finally", null, {
            $documentation: "A `finally` node; only makes sense as part of a `try` statement"
        }, AST.Block);

        /* -----[ VAR/CONST ]----- */

        AST.Definitions = def_node("Definitions", "definitions", {
            $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
            $propdoc: {
                definitions: "[AST.VarDef*] array of variable definitions"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.definitions.forEach(function (def) {
                        def._walk(visitor);
                    });
                });
            }
        }, AST.Statement);

        AST.Var = def_node("Var", null, {
            $documentation: "A `var` statement"
        }, AST.Definitions);

        AST.Const = def_node("Const", null, {
            $documentation: "A `const` statement"
        }, AST.Definitions);

        AST.VarDef = def_node("VarDef", "name value", {
            $documentation: "A variable declaration; only appears in a AST.Definitions node",
            $propdoc: {
                name: "[AST.SymbolVar|AST.SymbolConst] name of the variable",
                value: "[AST.Node?] initializer, or null of there's no initializer"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.name._walk(visitor);
                    if (this.value) {
                        this.value._walk(visitor);
                    }
                });
            }
        });

        /* -----[ OTHER ]----- */

        AST.Call = def_node("Call", "expression args", {
            $documentation: "A function call expression",
            $propdoc: {
                expression: "[AST.Node] expression to invoke as function",
                args: "[AST.Node*] array of arguments"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                    this.args.forEach(function (arg) {
                        arg._walk(visitor);
                    });
                });
            }
        });

        AST.New = def_node("New", null, {
            $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
        }, AST.Call);

        AST.Seq = def_node("Seq", "car cdr", {
            $documentation: "A sequence expression (two comma-separated expressions)",
            $propdoc: {
                car: "[AST.Node] first element in sequence",
                cdr: "[AST.Node] second element in sequence"
            },
            $cons: function (x, y) {
                var seq = new AST.Seq(x);
                seq.car = x;
                seq.cdr = y;
                return seq;
            },
            $from_array: function (array) {
                var list = null,
                    i,
                    p;
                if (array.length === 0) {
                    return null;
                }
                if (array.length === 1) {
                    return array[0].clone();
                }
                for (i = array.length - 1; i >= 0; --i) {
                    list = AST.Seq.cons(array[i], list);
                }
                p = list;
                while (p) {
                    if (p.cdr && !p.cdr.cdr) {
                        p.cdr = p.cdr.car;
                        break;
                    }
                    p = p.cdr;
                }
                return list;
            },
            to_array: function () {
                var p = this,
                    a = [];
                while (p) {
                    a.push(p.car);
                    if (p.cdr && !(p.cdr instanceof AST.Seq)) {
                        a.push(p.cdr);
                        break;
                    }
                    p = p.cdr;
                }
                return a;
            },
            add: function (node) {
                var p = this,
                    cell;
                while (p) {
                    if (!(p.cdr instanceof AST.Seq)) {
                        cell = AST.Seq.cons(p.cdr, node);
                        p.cdr = cell;
                        return cell;
                    }
                    p = p.cdr;
                }
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.car._walk(visitor);
                    if (this.cdr) {
                        this.cdr._walk(visitor);
                    }
                });
            }
        });

        AST.PropAccess = def_node("PropAccess", "expression property", {
            $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
            $propdoc: {
                expression: "[AST.Node] the “container” expression",
                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"
            }
        });

        AST.Dot = def_node("Dot", null, {
            $documentation: "A dotted property access expression",
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                });
            }
        }, AST.PropAccess);

        AST.Sub = def_node("Sub", null, {
            $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                    this.property._walk(visitor);
                });
            }
        }, AST.PropAccess);

        AST.Unary = def_node("Unary", "operator expression", {
            $documentation: "Base class for unary expressions",
            $propdoc: {
                operator: "[string] the operator",
                expression: "[AST.Node] expression that this unary operator applies to"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.expression._walk(visitor);
                });
            }
        });

        AST.UnaryPrefix = def_node("UnaryPrefix", null, {
            $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
        }, AST.Unary);

        AST.UnaryPostfix = def_node("UnaryPostfix", null, {
            $documentation: "Unary postfix expression, i.e. `i++`"
        }, AST.Unary);

        AST.Binary = def_node("Binary", "left operator right", {
            $documentation: "Binary expression, i.e. `a + b`",
            $propdoc: {
                left: "[AST.Node] left-hand side expression",
                operator: "[string] the operator",
                right: "[AST.Node] right-hand side expression"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.left._walk(visitor);
                    this.right._walk(visitor);
                });
            }
        });

        AST.Conditional = def_node("Conditional", "condition consequent alternative", {
            $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
            $propdoc: {
                condition: "[AST.Node]",
                consequent: "[AST.Node]",
                alternative: "[AST.Node]"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.condition._walk(visitor);
                    this.consequent._walk(visitor);
                    this.alternative._walk(visitor);
                });
            }
        });

        AST.Assign = def_node("Assign", null, {
            $documentation: "An assignment expression — `a = b + 5`"
        }, AST.Binary);

        /* -----[ LITERALS ]----- */

        AST.Array = def_node("Array", "elements", {
            $documentation: "An array literal",
            $propdoc: {
                elements: "[AST.Node*] array of elements"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.elements.forEach(function (el) {
                        el._walk(visitor);
                    });
                });
            }
        });

        AST.Object = def_node("Object", "properties", {
            $documentation: "An object literal",
            $propdoc: {
                properties: "[AST.ObjectProperty*] array of properties"
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.properties.forEach(function (prop) {
                        prop._walk(visitor);
                    });
                });
            }
        });

        AST.ObjectProperty = def_node("ObjectProperty", "key value", {
            $documentation: "Base class for literal object properties",
            $propdoc: {
                key: "[string] the property name converted to a string for ObjectKeyVal.  For setters and getters this is an arbitrary AST.Node.",
                value: "[AST.Node] property value.  For setters and getters this is an AST.Function."
            },
            _walk: function (visitor) {
                return visitor._visit(this, function () {
                    this.value._walk(visitor);
                });
            }
        });

        AST.ObjectKeyVal = def_node("ObjectKeyVal", "quote", {
            $documentation: "A key: value object property",
            $propdoc: {
                quote: "[string] the original quote character"
            }
        }, AST.ObjectProperty);

        AST.ObjectSetter = def_node("ObjectSetter", null, {
            $documentation: "An object setter property"
        }, AST.ObjectProperty);

        AST.ObjectGetter = def_node("ObjectGetter", null, {
            $documentation: "An object getter property"
        }, AST.ObjectProperty);

        AST.Symbol = def_node("Symbol", "scope name thedef", {
            $propdoc: {
                name: "[string] name of this symbol",
                scope: "[AST.Scope/S] the current scope (not necessarily the definition scope)",
                thedef: "[SymbolDef/S] the definition of this symbol"
            },
            $documentation: "Base class for all symbols"
        });

        AST.SymbolAccessor = def_node("SymbolAccessor", null, {
            $documentation: "The name of a property accessor (setter/getter function)"
        }, AST.Symbol);

        AST.SymbolDeclaration = def_node("SymbolDeclaration", "init", {
            $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
            $propdoc: {
                init: "[AST.Node*/S] array of initializers for this declaration."
            }
        }, AST.Symbol);

        AST.SymbolVar = def_node("SymbolVar", null, {
            $documentation: "Symbol defining a variable"
        }, AST.SymbolDeclaration);

        AST.SymbolConst = def_node("SymbolConst", null, {
            $documentation: "A constant declaration"
        }, AST.SymbolDeclaration);

        AST.SymbolFunarg = def_node("SymbolFunarg", null, {
            $documentation: "Symbol naming a function argument"
        }, AST.SymbolVar);

        AST.SymbolDefun = def_node("SymbolDefun", null, {
            $documentation: "Symbol defining a function"
        }, AST.SymbolDeclaration);

        AST.SymbolLambda = def_node("SymbolLambda", null, {
            $documentation: "Symbol naming a function expression"
        }, AST.SymbolDeclaration);

        AST.SymbolCatch = def_node("SymbolCatch", null, {
            $documentation: "Symbol naming the exception in catch"
        }, AST.SymbolDeclaration);

        AST.Label = def_node("Label", "references", {
            $documentation: "Symbol naming a label (declaration)",
            $propdoc: {
                references: "[AST.LoopControl*] a list of nodes referring to this label"
            },
            initialize: function () {
                this.references = [];
                this.thedef = this;
            }
        }, AST.Symbol);

        AST.SymbolRef = def_node("SymbolRef", null, {
            $documentation: "Reference to some symbol (not definition/declaration)"
        }, AST.Symbol);

        AST.LabelRef = def_node("LabelRef", null, {
            $documentation: "Reference to a label symbol"
        }, AST.Symbol);

        AST.This = def_node("This", null, {
            $documentation: "The `this` symbol"
        }, AST.Symbol);

        AST.Constant = def_node("Constant", null, {
            $documentation: "Base class for all constants",
            getValue: function () {
                return this.value;
            }
        });

        AST.String = def_node("String", "value quote", {
            $documentation: "A string literal",
            $propdoc: {
                value: "[string] the contents of this string",
                quote: "[string] the original quote character"
            }
        }, AST.Constant);

        AST.Number = def_node("Number", "value", {
            $documentation: "A number literal",
            $propdoc: {
                value: "[number] the numeric value"
            }
        }, AST.Constant);

        AST.RegExp = def_node("RegExp", "value", {
            $documentation: "A regexp literal",
            $propdoc: {
                value: "[RegExp] the actual regexp"
            }
        }, AST.Constant);

        AST.Atom = def_node("Atom", null, {
            $documentation: "Base class for atoms"
        }, AST.Constant);

        AST.Null = def_node("Null", null, {
            $documentation: "The `null` atom",
            value: null
        }, AST.Atom);

        AST.NaN = def_node("NaN", null, {
            $documentation: "The impossible value",
            value: NAN
        }, AST.Atom);

        AST.Undefined = def_node("Undefined", null, {
            $documentation: "The `undefined` value",
            value: UNDEF
        }, AST.Atom);

        AST.Hole = def_node("Hole", null, {
            $documentation: "A hole in an array",
            value: UNDEF
        }, AST.Atom);

        AST.Infinity = def_node("Infinity", null, {
            $documentation: "The `Infinity` value",
            value: INFINITY
        }, AST.Atom);

        AST.Boolean = def_node("Boolean", null, {
            $documentation: "Base class for booleans"
        }, AST.Atom);

        AST.False = def_node("False", null, {
            $documentation: "The `false` atom",
            value: false
        }, AST.Boolean);

        AST.True = def_node("True", null, {
            $documentation: "The `true` atom",
            value: true
        }, AST.Boolean);

        return AST;
    }
);