File: /home/frenchy/www/french-american.org/releases/20210421135930Z/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;
}
);