var types = require(“../lib/types”); var Type = types.Type; var def = Type.def; var or = Type.or; var builtin = types.builtInTypes; var isString = builtin.string; var isNumber = builtin.number; var isBoolean = builtin.boolean; var isRegExp = builtin.RegExp; var shared = require(“../lib/shared”); var defaults = shared.defaults; var geq = shared.geq;

def(“Node”)

.field("type", isString)
.field("loc", or(
    def("SourceLocation"),
    null
), defaults["null"], true);

def(“SourceLocation”)

.build("start", "end", "source")
.field("start", def("Position"))
.field("end", def("Position"))
.field("source", or(isString, null), defaults["null"]);

def(“Position”)

.build("line", "column")
.field("line", geq(1))
.field("column", geq(0));

def(“Program”)

.bases("Node")
.build("body")
.field("body", [def("Statement")]);

def(“Function”)

.bases("Node")
.field("id", or(def("Identifier"), null), defaults["null"])
.field("params", [def("Pattern")])
.field("body", or(def("BlockStatement"), def("Expression")));

def(“Statement”).bases(“Node”);

// The empty .build() here means that an EmptyStatement can be constructed // (i.e. it's not abstract) but that it needs no arguments. def(“EmptyStatement”).bases(“Statement”).build();

def(“BlockStatement”)

.bases("Statement")
.build("body")
.field("body", [def("Statement")]);

// TODO Figure out how to silently coerce Expressions to // ExpressionStatements where a Statement was expected. def(“ExpressionStatement”)

.bases("Statement")
.build("expression")
.field("expression", def("Expression"));

def(“IfStatement”)

.bases("Statement")
.build("test", "consequent", "alternate")
.field("test", def("Expression"))
.field("consequent", def("Statement"))
.field("alternate", or(def("Statement"), null), defaults["null"]);

def(“LabeledStatement”)

.bases("Statement")
.build("label", "body")
.field("label", def("Identifier"))
.field("body", def("Statement"));

def(“BreakStatement”)

.bases("Statement")
.build("label")
.field("label", or(def("Identifier"), null), defaults["null"]);

def(“ContinueStatement”)

.bases("Statement")
.build("label")
.field("label", or(def("Identifier"), null), defaults["null"]);

def(“WithStatement”)

.bases("Statement")
.build("object", "body")
.field("object", def("Expression"))
.field("body", def("Statement"));

def(“SwitchStatement”)

.bases("Statement")
.build("discriminant", "cases", "lexical")
.field("discriminant", def("Expression"))
.field("cases", [def("SwitchCase")])
.field("lexical", isBoolean, defaults["false"]);

def(“ReturnStatement”)

.bases("Statement")
.build("argument")
.field("argument", or(def("Expression"), null));

def(“ThrowStatement”)

.bases("Statement")
.build("argument")
.field("argument", def("Expression"));

def(“TryStatement”)

.bases("Statement")
.build("block", "handler", "finalizer")
.field("block", def("BlockStatement"))
.field("handler", or(def("CatchClause"), null), function() {
    return this.handlers && this.handlers[0] || null;
})
.field("handlers", [def("CatchClause")], function() {
    return this.handler ? [this.handler] : [];
}, true) // Indicates this field is hidden from eachField iteration.
.field("guardedHandlers", [def("CatchClause")], defaults.emptyArray)
.field("finalizer", or(def("BlockStatement"), null), defaults["null"]);

def(“CatchClause”)

.bases("Node")
.build("param", "guard", "body")
.field("param", def("Pattern"))
.field("guard", or(def("Expression"), null), defaults["null"])
.field("body", def("BlockStatement"));

def(“WhileStatement”)

.bases("Statement")
.build("test", "body")
.field("test", def("Expression"))
.field("body", def("Statement"));

def(“DoWhileStatement”)

.bases("Statement")
.build("body", "test")
.field("body", def("Statement"))
.field("test", def("Expression"));

def(“ForStatement”)

.bases("Statement")
.build("init", "test", "update", "body")
.field("init", or(
    def("VariableDeclaration"),
    def("Expression"),
    null))
.field("test", or(def("Expression"), null))
.field("update", or(def("Expression"), null))
.field("body", def("Statement"));

def(“ForInStatement”)

.bases("Statement")
.build("left", "right", "body", "each")
.field("left", or(
    def("VariableDeclaration"),
    def("Expression")))
.field("right", def("Expression"))
.field("body", def("Statement"))
.field("each", isBoolean);

def(“DebuggerStatement”).bases(“Statement”).build();

def(“Declaration”).bases(“Statement”);

def(“FunctionDeclaration”)

.bases("Function", "Declaration")
.build("id", "params", "body")
.field("id", def("Identifier"));

def(“FunctionExpression”)

.bases("Function", "Expression")
.build("id", "params", "body");

def(“VariableDeclaration”)

.bases("Declaration")
.build("kind", "declarations")
.field("kind", or("var", "let", "const"))
.field("declarations", [or(
    def("VariableDeclarator"),
    def("Identifier") // TODO Esprima deviation.
)]);

def(“VariableDeclarator”)

.bases("Node")
.build("id", "init")
.field("id", def("Pattern"))
.field("init", or(def("Expression"), null));

// TODO Are all Expressions really Patterns? def(“Expression”).bases(“Node”, “Pattern”);

def(“ThisExpression”).bases(“Expression”).build();

def(“ArrayExpression”)

.bases("Expression")
.build("elements")
.field("elements", [or(def("Expression"), null)]);

def(“ObjectExpression”)

.bases("Expression")
.build("properties")
.field("properties", [def("Property")]);

// TODO Not in the Mozilla Parser API, but used by Esprima. def(“Property”)

.bases("Node") // Want to be able to visit Property Nodes.
.build("kind", "key", "value")
.field("kind", or("init", "get", "set"))
.field("key", or(def("Literal"), def("Identifier")))
.field("value", def("Expression"));

def(“SequenceExpression”)

.bases("Expression")
.build("expressions")
.field("expressions", [def("Expression")]);

var UnaryOperator = or(

"-", "+", "!", "~",
"typeof", "void", "delete");

def(“UnaryExpression”)

.bases("Expression")
.build("operator", "argument", "prefix")
.field("operator", UnaryOperator)
.field("argument", def("Expression"))
// TODO Esprima doesn't bother with this field, presumably because
// it's always true for unary operators.
.field("prefix", isBoolean, defaults["true"]);

var BinaryOperator = or(

"==", "!=", "===", "!==",
"<", "<=", ">", ">=",
"<<", ">>", ">>>",
"+", "-", "*", "/", "%",
"&", // TODO Missing from the Parser API.
"|", "^", "in",
"instanceof", "..");

def(“BinaryExpression”)

.bases("Expression")
.build("operator", "left", "right")
.field("operator", BinaryOperator)
.field("left", def("Expression"))
.field("right", def("Expression"));

var AssignmentOperator = or(

"=", "+=", "-=", "*=", "/=", "%=",
"<<=", ">>=", ">>>=",
"|=", "^=", "&=");

def(“AssignmentExpression”)

.bases("Expression")
.build("operator", "left", "right")
.field("operator", AssignmentOperator)
.field("left", def("Pattern"))
.field("right", def("Expression"));

var UpdateOperator = or(“++”, “–”);

def(“UpdateExpression”)

.bases("Expression")
.build("operator", "argument", "prefix")
.field("operator", UpdateOperator)
.field("argument", def("Expression"))
.field("prefix", isBoolean);

var LogicalOperator = or(“||”, “&&”);

def(“LogicalExpression”)

.bases("Expression")
.build("operator", "left", "right")
.field("operator", LogicalOperator)
.field("left", def("Expression"))
.field("right", def("Expression"));

def(“ConditionalExpression”)

.bases("Expression")
.build("test", "consequent", "alternate")
.field("test", def("Expression"))
.field("consequent", def("Expression"))
.field("alternate", def("Expression"));

def(“NewExpression”)

.bases("Expression")
.build("callee", "arguments")
.field("callee", def("Expression"))
// The Mozilla Parser API gives this type as [or(def("Expression"),
// null)], but null values don't really make sense at the call site.
// TODO Report this nonsense.
.field("arguments", [def("Expression")]);

def(“CallExpression”)

.bases("Expression")
.build("callee", "arguments")
.field("callee", def("Expression"))
// See comment for NewExpression above.
.field("arguments", [def("Expression")]);

def(“MemberExpression”)

.bases("Expression")
.build("object", "property", "computed")
.field("object", def("Expression"))
.field("property", or(def("Identifier"), def("Expression")))
.field("computed", isBoolean);

def(“Pattern”).bases(“Node”);

def(“ObjectPattern”)

.bases("Pattern")
.build("properties")
// TODO File a bug to get PropertyPattern added to the interfaces API.
.field("properties", [def("PropertyPattern")]);

def(“PropertyPattern”)

.bases("Pattern")
.build("key", "pattern")
.field("key", or(def("Literal"), def("Identifier")))
.field("pattern", def("Pattern"));

def(“ArrayPattern”)

.bases("Pattern")
.build("elements")
.field("elements", [or(def("Pattern"), null)]);

def(“SwitchCase”)

.bases("Node")
.build("test", "consequent")
.field("test", or(def("Expression"), null))
.field("consequent", [def("Statement")]);

def(“Identifier”)

// But aren't Expressions and Patterns already Nodes? TODO Report this.
.bases("Node", "Expression", "Pattern")
.build("name")
.field("name", isString);

def(“Literal”)

// But aren't Expressions already Nodes? TODO Report this.
.bases("Node", "Expression")
.build("value")
.field("value", or(
    isString,
    isBoolean,
    null, // isNull would also work here.
    isNumber,
    isRegExp
));