ast-types
Advanced tools
Comparing version 0.8.18 to 0.9.0
170
def/babel.js
@@ -1,105 +0,107 @@ | ||
require("./es7"); | ||
module.exports = function (fork) { | ||
fork.use(require("./es7")); | ||
var types = require("../lib/types"); | ||
var defaults = require("../lib/shared").defaults; | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var types = fork.use(require("../lib/types")); | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
def("Noop") | ||
.bases("Node") | ||
.build(); | ||
def("Noop") | ||
.bases("Node") | ||
.build(); | ||
def("DoExpression") | ||
.bases("Expression") | ||
.build("body") | ||
.field("body", [def("Statement")]); | ||
def("DoExpression") | ||
.bases("Expression") | ||
.build("body") | ||
.field("body", [def("Statement")]); | ||
def("Super") | ||
.bases("Expression") | ||
.build(); | ||
def("Super") | ||
.bases("Expression") | ||
.build(); | ||
def("BindExpression") | ||
.bases("Expression") | ||
.build("object", "callee") | ||
.field("object", or(def("Expression"), null)) | ||
.field("callee", def("Expression")); | ||
def("BindExpression") | ||
.bases("Expression") | ||
.build("object", "callee") | ||
.field("object", or(def("Expression"), null)) | ||
.field("callee", def("Expression")); | ||
def("Decorator") | ||
.bases("Node") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
def("Decorator") | ||
.bases("Node") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
def("Property") | ||
.field("decorators", | ||
or([def("Decorator")], null), | ||
defaults["null"]); | ||
def("Property") | ||
.field("decorators", | ||
or([def("Decorator")], null), | ||
defaults["null"]); | ||
def("MethodDefinition") | ||
.field("decorators", | ||
or([def("Decorator")], null), | ||
defaults["null"]); | ||
def("MethodDefinition") | ||
.field("decorators", | ||
or([def("Decorator")], null), | ||
defaults["null"]); | ||
def("MetaProperty") | ||
.bases("Expression") | ||
.build("meta", "property") | ||
.field("meta", def("Identifier")) | ||
.field("property", def("Identifier")); | ||
def("MetaProperty") | ||
.bases("Expression") | ||
.build("meta", "property") | ||
.field("meta", def("Identifier")) | ||
.field("property", def("Identifier")); | ||
def("ParenthesizedExpression") | ||
.bases("Expression") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
def("ParenthesizedExpression") | ||
.bases("Expression") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
def("ImportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("imported", "local") | ||
.field("imported", def("Identifier")); | ||
def("ImportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("imported", "local") | ||
.field("imported", def("Identifier")); | ||
def("ImportDefaultSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local"); | ||
def("ImportDefaultSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local"); | ||
def("ImportNamespaceSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local"); | ||
def("ImportNamespaceSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local"); | ||
def("ExportDefaultDeclaration") | ||
.bases("Declaration") | ||
.build("declaration") | ||
.field("declaration", or(def("Declaration"), def("Expression"))); | ||
def("ExportDefaultDeclaration") | ||
.bases("Declaration") | ||
.build("declaration") | ||
.field("declaration", or(def("Declaration"), def("Expression"))); | ||
def("ExportNamedDeclaration") | ||
.bases("Declaration") | ||
.build("declaration", "specifiers", "source") | ||
.field("declaration", or(def("Declaration"), null)) | ||
.field("specifiers", [def("ExportSpecifier")], defaults.emptyArray) | ||
.field("source", or(def("Literal"), null), defaults["null"]); | ||
def("ExportNamedDeclaration") | ||
.bases("Declaration") | ||
.build("declaration", "specifiers", "source") | ||
.field("declaration", or(def("Declaration"), null)) | ||
.field("specifiers", [def("ExportSpecifier")], defaults.emptyArray) | ||
.field("source", or(def("Literal"), null), defaults["null"]); | ||
def("ExportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local", "exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("local", "exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportNamespaceSpecifier") | ||
.bases("Specifier") | ||
.build("exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportNamespaceSpecifier") | ||
.bases("Specifier") | ||
.build("exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportDefaultSpecifier") | ||
.bases("Specifier") | ||
.build("exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportDefaultSpecifier") | ||
.bases("Specifier") | ||
.build("exported") | ||
.field("exported", def("Identifier")); | ||
def("ExportAllDeclaration") | ||
.bases("Declaration") | ||
.build("exported", "source") | ||
.field("exported", or(def("Identifier"), null)) | ||
.field("source", def("Literal")); | ||
def("ExportAllDeclaration") | ||
.bases("Declaration") | ||
.build("exported", "source") | ||
.field("exported", or(def("Identifier"), null)) | ||
.field("source", def("Literal")); | ||
def("CommentBlock") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("CommentBlock") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("CommentLine") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("CommentLine") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
}; |
614
def/core.js
@@ -1,367 +0,369 @@ | ||
var types = require("../lib/types"); | ||
var Type = types.Type; | ||
var def = Type.def; | ||
var or = Type.or; | ||
var shared = require("../lib/shared"); | ||
var defaults = shared.defaults; | ||
var geq = shared.geq; | ||
module.exports = function (fork) { | ||
var types = fork.use(require("../lib/types")); | ||
var Type = types.Type; | ||
var def = Type.def; | ||
var or = Type.or; | ||
var shared = fork.use(require("../lib/shared")); | ||
var defaults = shared.defaults; | ||
var geq = shared.geq; | ||
// Abstract supertype of all syntactic entities that are allowed to have a | ||
// .loc field. | ||
def("Printable") | ||
.field("loc", or( | ||
def("SourceLocation"), | ||
null | ||
), defaults["null"], true); | ||
// Abstract supertype of all syntactic entities that are allowed to have a | ||
// .loc field. | ||
def("Printable") | ||
.field("loc", or( | ||
def("SourceLocation"), | ||
null | ||
), defaults["null"], true); | ||
def("Node") | ||
.bases("Printable") | ||
.field("type", String) | ||
.field("comments", or( | ||
[def("Comment")], | ||
null | ||
), defaults["null"], true); | ||
def("Node") | ||
.bases("Printable") | ||
.field("type", String) | ||
.field("comments", or( | ||
[def("Comment")], | ||
null | ||
), defaults["null"], true); | ||
def("SourceLocation") | ||
.build("start", "end", "source") | ||
.field("start", def("Position")) | ||
.field("end", def("Position")) | ||
.field("source", or(String, null), defaults["null"]); | ||
def("SourceLocation") | ||
.build("start", "end", "source") | ||
.field("start", def("Position")) | ||
.field("end", def("Position")) | ||
.field("source", or(String, null), defaults["null"]); | ||
def("Position") | ||
.build("line", "column") | ||
.field("line", geq(1)) | ||
.field("column", geq(0)); | ||
def("Position") | ||
.build("line", "column") | ||
.field("line", geq(1)) | ||
.field("column", geq(0)); | ||
def("File") | ||
.bases("Node") | ||
.build("program") | ||
.field("program", def("Program")); | ||
def("File") | ||
.bases("Node") | ||
.build("program") | ||
.field("program", def("Program")); | ||
def("Program") | ||
.bases("Node") | ||
.build("body") | ||
.field("body", [def("Statement")]); | ||
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", def("BlockStatement")); | ||
def("Function") | ||
.bases("Node") | ||
.field("id", or(def("Identifier"), null), defaults["null"]) | ||
.field("params", [def("Pattern")]) | ||
.field("body", def("BlockStatement")); | ||
def("Statement").bases("Node"); | ||
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("EmptyStatement").bases("Statement").build(); | ||
def("BlockStatement") | ||
.bases("Statement") | ||
.build("body") | ||
.field("body", [def("Statement")]); | ||
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")); | ||
// 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("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("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("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("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("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", Boolean, defaults["false"]); | ||
def("SwitchStatement") | ||
.bases("Statement") | ||
.build("discriminant", "cases", "lexical") | ||
.field("discriminant", def("Expression")) | ||
.field("cases", [def("SwitchCase")]) | ||
.field("lexical", Boolean, defaults["false"]); | ||
def("ReturnStatement") | ||
.bases("Statement") | ||
.build("argument") | ||
.field("argument", or(def("Expression"), null)); | ||
def("ReturnStatement") | ||
.bases("Statement") | ||
.build("argument") | ||
.field("argument", or(def("Expression"), null)); | ||
def("ThrowStatement") | ||
.bases("Statement") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
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("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("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("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("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("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") | ||
.field("left", or( | ||
def("VariableDeclaration"), | ||
def("Expression"))) | ||
.field("right", def("Expression")) | ||
.field("body", def("Statement")); | ||
def("ForInStatement") | ||
.bases("Statement") | ||
.build("left", "right", "body") | ||
.field("left", or( | ||
def("VariableDeclaration"), | ||
def("Expression"))) | ||
.field("right", def("Expression")) | ||
.field("body", def("Statement")); | ||
def("DebuggerStatement").bases("Statement").build(); | ||
def("DebuggerStatement").bases("Statement").build(); | ||
def("Declaration").bases("Statement"); | ||
def("Declaration").bases("Statement"); | ||
def("FunctionDeclaration") | ||
.bases("Function", "Declaration") | ||
.build("id", "params", "body") | ||
.field("id", def("Identifier")); | ||
def("FunctionDeclaration") | ||
.bases("Function", "Declaration") | ||
.build("id", "params", "body") | ||
.field("id", def("Identifier")); | ||
def("FunctionExpression") | ||
.bases("Function", "Expression") | ||
.build("id", "params", "body"); | ||
def("FunctionExpression") | ||
.bases("Function", "Expression") | ||
.build("id", "params", "body"); | ||
def("VariableDeclaration") | ||
.bases("Declaration") | ||
.build("kind", "declarations") | ||
.field("kind", or("var", "let", "const")) | ||
.field("declarations", [def("VariableDeclarator")]); | ||
def("VariableDeclaration") | ||
.bases("Declaration") | ||
.build("kind", "declarations") | ||
.field("kind", or("var", "let", "const")) | ||
.field("declarations", [def("VariableDeclarator")]); | ||
def("VariableDeclarator") | ||
.bases("Node") | ||
.build("id", "init") | ||
.field("id", def("Pattern")) | ||
.field("init", or(def("Expression"), null)); | ||
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"); | ||
// TODO Are all Expressions really Patterns? | ||
def("Expression").bases("Node", "Pattern"); | ||
def("ThisExpression").bases("Expression").build(); | ||
def("ThisExpression").bases("Expression").build(); | ||
def("ArrayExpression") | ||
.bases("Expression") | ||
.build("elements") | ||
.field("elements", [or(def("Expression"), null)]); | ||
def("ArrayExpression") | ||
.bases("Expression") | ||
.build("elements") | ||
.field("elements", [or(def("Expression"), null)]); | ||
def("ObjectExpression") | ||
.bases("Expression") | ||
.build("properties") | ||
.field("properties", [def("Property")]); | ||
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")); | ||
// 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")]); | ||
def("SequenceExpression") | ||
.bases("Expression") | ||
.build("expressions") | ||
.field("expressions", [def("Expression")]); | ||
var UnaryOperator = or( | ||
"-", "+", "!", "~", | ||
"typeof", "void", "delete"); | ||
var UnaryOperator = or( | ||
"-", "+", "!", "~", | ||
"typeof", "void", "delete"); | ||
def("UnaryExpression") | ||
.bases("Expression") | ||
.build("operator", "argument", "prefix") | ||
.field("operator", UnaryOperator) | ||
.field("argument", def("Expression")) | ||
// Esprima doesn't bother with this field, presumably because it's | ||
// always true for unary operators. | ||
.field("prefix", Boolean, defaults["true"]); | ||
def("UnaryExpression") | ||
.bases("Expression") | ||
.build("operator", "argument", "prefix") | ||
.field("operator", UnaryOperator) | ||
.field("argument", def("Expression")) | ||
// Esprima doesn't bother with this field, presumably because it's | ||
// always true for unary operators. | ||
.field("prefix", Boolean, defaults["true"]); | ||
var BinaryOperator = or( | ||
"==", "!=", "===", "!==", | ||
"<", "<=", ">", ">=", | ||
"<<", ">>", ">>>", | ||
"+", "-", "*", "/", "%", | ||
"&", // TODO Missing from the Parser API. | ||
"|", "^", "in", | ||
"instanceof", ".."); | ||
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")); | ||
def("BinaryExpression") | ||
.bases("Expression") | ||
.build("operator", "left", "right") | ||
.field("operator", BinaryOperator) | ||
.field("left", def("Expression")) | ||
.field("right", def("Expression")); | ||
var AssignmentOperator = or( | ||
"=", "+=", "-=", "*=", "/=", "%=", | ||
"<<=", ">>=", ">>>=", | ||
"|=", "^=", "&="); | ||
var AssignmentOperator = or( | ||
"=", "+=", "-=", "*=", "/=", "%=", | ||
"<<=", ">>=", ">>>=", | ||
"|=", "^=", "&="); | ||
def("AssignmentExpression") | ||
.bases("Expression") | ||
.build("operator", "left", "right") | ||
.field("operator", AssignmentOperator) | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")); | ||
def("AssignmentExpression") | ||
.bases("Expression") | ||
.build("operator", "left", "right") | ||
.field("operator", AssignmentOperator) | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")); | ||
var UpdateOperator = or("++", "--"); | ||
var UpdateOperator = or("++", "--"); | ||
def("UpdateExpression") | ||
.bases("Expression") | ||
.build("operator", "argument", "prefix") | ||
.field("operator", UpdateOperator) | ||
.field("argument", def("Expression")) | ||
.field("prefix", Boolean); | ||
def("UpdateExpression") | ||
.bases("Expression") | ||
.build("operator", "argument", "prefix") | ||
.field("operator", UpdateOperator) | ||
.field("argument", def("Expression")) | ||
.field("prefix", Boolean); | ||
var LogicalOperator = or("||", "&&"); | ||
var LogicalOperator = or("||", "&&"); | ||
def("LogicalExpression") | ||
.bases("Expression") | ||
.build("operator", "left", "right") | ||
.field("operator", LogicalOperator) | ||
.field("left", def("Expression")) | ||
.field("right", def("Expression")); | ||
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("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("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("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", Boolean, function(){ | ||
var type = this.property.type; | ||
if (type === 'Literal' || | ||
type === 'MemberExpression' || | ||
type === 'BinaryExpression') { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
def("MemberExpression") | ||
.bases("Expression") | ||
.build("object", "property", "computed") | ||
.field("object", def("Expression")) | ||
.field("property", or(def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean, function () { | ||
var type = this.property.type; | ||
if (type === 'Literal' || | ||
type === 'MemberExpression' || | ||
type === 'BinaryExpression') { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
def("Pattern").bases("Node"); | ||
def("Pattern").bases("Node"); | ||
def("SwitchCase") | ||
.bases("Node") | ||
.build("test", "consequent") | ||
.field("test", or(def("Expression"), null)) | ||
.field("consequent", [def("Statement")]); | ||
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", String); | ||
def("Identifier") | ||
// But aren't Expressions and Patterns already Nodes? TODO Report this. | ||
.bases("Node", "Expression", "Pattern") | ||
.build("name") | ||
.field("name", String); | ||
def("Literal") | ||
// But aren't Expressions already Nodes? TODO Report this. | ||
.bases("Node", "Expression") | ||
.build("value") | ||
.field("value", or(String, Boolean, null, Number, RegExp)) | ||
.field("regex", or({ | ||
pattern: String, | ||
flags: String | ||
}, null), function() { | ||
if (this.value instanceof RegExp) { | ||
var flags = ""; | ||
def("Literal") | ||
// But aren't Expressions already Nodes? TODO Report this. | ||
.bases("Node", "Expression") | ||
.build("value") | ||
.field("value", or(String, Boolean, null, Number, RegExp)) | ||
.field("regex", or({ | ||
pattern: String, | ||
flags: String | ||
}, null), function () { | ||
if (this.value instanceof RegExp) { | ||
var flags = ""; | ||
if (this.value.ignoreCase) flags += "i"; | ||
if (this.value.multiline) flags += "m"; | ||
if (this.value.global) flags += "g"; | ||
if (this.value.ignoreCase) flags += "i"; | ||
if (this.value.multiline) flags += "m"; | ||
if (this.value.global) flags += "g"; | ||
return { | ||
pattern: this.value.source, | ||
flags: flags | ||
}; | ||
} | ||
return { | ||
pattern: this.value.source, | ||
flags: flags | ||
}; | ||
} | ||
return null; | ||
}); | ||
return null; | ||
}); | ||
// Abstract (non-buildable) comment supertype. Not a Node. | ||
def("Comment") | ||
.bases("Printable") | ||
.field("value", String) | ||
// A .leading comment comes before the node, whereas a .trailing | ||
// comment comes after it. These two fields should not both be true, | ||
// but they might both be false when the comment falls inside a node | ||
// and the node has no children for the comment to lead or trail, | ||
// e.g. { /*dangling*/ }. | ||
.field("leading", Boolean, defaults["true"]) | ||
.field("trailing", Boolean, defaults["false"]); | ||
// Abstract (non-buildable) comment supertype. Not a Node. | ||
def("Comment") | ||
.bases("Printable") | ||
.field("value", String) | ||
// A .leading comment comes before the node, whereas a .trailing | ||
// comment comes after it. These two fields should not both be true, | ||
// but they might both be false when the comment falls inside a node | ||
// and the node has no children for the comment to lead or trail, | ||
// e.g. { /*dangling*/ }. | ||
.field("leading", Boolean, defaults["true"]) | ||
.field("trailing", Boolean, defaults["false"]); | ||
}; |
130
def/e4x.js
@@ -1,84 +0,86 @@ | ||
require("./core"); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
module.exports = function (fork) { | ||
fork.use(require("./core")); | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
// Note that none of these types are buildable because the Mozilla Parser | ||
// API doesn't specify any builder functions, and nobody uses E4X anymore. | ||
// Note that none of these types are buildable because the Mozilla Parser | ||
// API doesn't specify any builder functions, and nobody uses E4X anymore. | ||
def("XMLDefaultDeclaration") | ||
.bases("Declaration") | ||
.field("namespace", def("Expression")); | ||
def("XMLDefaultDeclaration") | ||
.bases("Declaration") | ||
.field("namespace", def("Expression")); | ||
def("XMLAnyName").bases("Expression"); | ||
def("XMLAnyName").bases("Expression"); | ||
def("XMLQualifiedIdentifier") | ||
.bases("Expression") | ||
.field("left", or(def("Identifier"), def("XMLAnyName"))) | ||
.field("right", or(def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean); | ||
def("XMLQualifiedIdentifier") | ||
.bases("Expression") | ||
.field("left", or(def("Identifier"), def("XMLAnyName"))) | ||
.field("right", or(def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean); | ||
def("XMLFunctionQualifiedIdentifier") | ||
.bases("Expression") | ||
.field("right", or(def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean); | ||
def("XMLFunctionQualifiedIdentifier") | ||
.bases("Expression") | ||
.field("right", or(def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean); | ||
def("XMLAttributeSelector") | ||
.bases("Expression") | ||
.field("attribute", def("Expression")); | ||
def("XMLAttributeSelector") | ||
.bases("Expression") | ||
.field("attribute", def("Expression")); | ||
def("XMLFilterExpression") | ||
.bases("Expression") | ||
.field("left", def("Expression")) | ||
.field("right", def("Expression")); | ||
def("XMLFilterExpression") | ||
.bases("Expression") | ||
.field("left", def("Expression")) | ||
.field("right", def("Expression")); | ||
def("XMLElement") | ||
.bases("XML", "Expression") | ||
.field("contents", [def("XML")]); | ||
def("XMLElement") | ||
.bases("XML", "Expression") | ||
.field("contents", [def("XML")]); | ||
def("XMLList") | ||
.bases("XML", "Expression") | ||
.field("contents", [def("XML")]); | ||
def("XMLList") | ||
.bases("XML", "Expression") | ||
.field("contents", [def("XML")]); | ||
def("XML").bases("Node"); | ||
def("XML").bases("Node"); | ||
def("XMLEscape") | ||
.bases("XML") | ||
.field("expression", def("Expression")); | ||
def("XMLEscape") | ||
.bases("XML") | ||
.field("expression", def("Expression")); | ||
def("XMLText") | ||
.bases("XML") | ||
.field("text", String); | ||
def("XMLText") | ||
.bases("XML") | ||
.field("text", String); | ||
def("XMLStartTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLStartTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLEndTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLEndTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLPointTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLPointTag") | ||
.bases("XML") | ||
.field("contents", [def("XML")]); | ||
def("XMLName") | ||
.bases("XML") | ||
.field("contents", or(String, [def("XML")])); | ||
def("XMLName") | ||
.bases("XML") | ||
.field("contents", or(String, [def("XML")])); | ||
def("XMLAttribute") | ||
.bases("XML") | ||
.field("value", String); | ||
def("XMLAttribute") | ||
.bases("XML") | ||
.field("value", String); | ||
def("XMLCdata") | ||
.bases("XML") | ||
.field("contents", String); | ||
def("XMLCdata") | ||
.bases("XML") | ||
.field("contents", String); | ||
def("XMLComment") | ||
.bases("XML") | ||
.field("contents", String); | ||
def("XMLComment") | ||
.bases("XML") | ||
.field("contents", String); | ||
def("XMLProcessingInstruction") | ||
.bases("XML") | ||
.field("target", String) | ||
.field("contents", or(String, null)); | ||
def("XMLProcessingInstruction") | ||
.bases("XML") | ||
.field("target", String) | ||
.field("contents", or(String, null)); | ||
}; |
364
def/es6.js
@@ -1,112 +0,113 @@ | ||
require("./core"); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = require("../lib/shared").defaults; | ||
module.exports = function (fork) { | ||
fork.use(require("./core")); | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
def("Function") | ||
.field("generator", Boolean, defaults["false"]) | ||
.field("expression", Boolean, defaults["false"]) | ||
.field("defaults", [or(def("Expression"), null)], defaults.emptyArray) | ||
// TODO This could be represented as a RestElement in .params. | ||
.field("rest", or(def("Identifier"), null), defaults["null"]); | ||
def("Function") | ||
.field("generator", Boolean, defaults["false"]) | ||
.field("expression", Boolean, defaults["false"]) | ||
.field("defaults", [or(def("Expression"), null)], defaults.emptyArray) | ||
// TODO This could be represented as a RestElement in .params. | ||
.field("rest", or(def("Identifier"), null), defaults["null"]); | ||
// The ESTree way of representing a ...rest parameter. | ||
def("RestElement") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
// The ESTree way of representing a ...rest parameter. | ||
def("RestElement") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
def("SpreadElementPattern") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
def("SpreadElementPattern") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
def("FunctionDeclaration") | ||
.build("id", "params", "body", "generator", "expression"); | ||
def("FunctionDeclaration") | ||
.build("id", "params", "body", "generator", "expression"); | ||
def("FunctionExpression") | ||
.build("id", "params", "body", "generator", "expression"); | ||
def("FunctionExpression") | ||
.build("id", "params", "body", "generator", "expression"); | ||
// The Parser API calls this ArrowExpression, but Esprima and all other | ||
// actual parsers use ArrowFunctionExpression. | ||
def("ArrowFunctionExpression") | ||
.bases("Function", "Expression") | ||
.build("params", "body", "expression") | ||
// The forced null value here is compatible with the overridden | ||
// definition of the "id" field in the Function interface. | ||
.field("id", null, defaults["null"]) | ||
// Arrow function bodies are allowed to be expressions. | ||
.field("body", or(def("BlockStatement"), def("Expression"))) | ||
// The current spec forbids arrow generators, so I have taken the | ||
// liberty of enforcing that. TODO Report this. | ||
.field("generator", false, defaults["false"]); | ||
// The Parser API calls this ArrowExpression, but Esprima and all other | ||
// actual parsers use ArrowFunctionExpression. | ||
def("ArrowFunctionExpression") | ||
.bases("Function", "Expression") | ||
.build("params", "body", "expression") | ||
// The forced null value here is compatible with the overridden | ||
// definition of the "id" field in the Function interface. | ||
.field("id", null, defaults["null"]) | ||
// Arrow function bodies are allowed to be expressions. | ||
.field("body", or(def("BlockStatement"), def("Expression"))) | ||
// The current spec forbids arrow generators, so I have taken the | ||
// liberty of enforcing that. TODO Report this. | ||
.field("generator", false, defaults["false"]); | ||
def("YieldExpression") | ||
.bases("Expression") | ||
.build("argument", "delegate") | ||
.field("argument", or(def("Expression"), null)) | ||
.field("delegate", Boolean, defaults["false"]); | ||
def("YieldExpression") | ||
.bases("Expression") | ||
.build("argument", "delegate") | ||
.field("argument", or(def("Expression"), null)) | ||
.field("delegate", Boolean, defaults["false"]); | ||
def("GeneratorExpression") | ||
.bases("Expression") | ||
.build("body", "blocks", "filter") | ||
.field("body", def("Expression")) | ||
.field("blocks", [def("ComprehensionBlock")]) | ||
.field("filter", or(def("Expression"), null)); | ||
def("GeneratorExpression") | ||
.bases("Expression") | ||
.build("body", "blocks", "filter") | ||
.field("body", def("Expression")) | ||
.field("blocks", [def("ComprehensionBlock")]) | ||
.field("filter", or(def("Expression"), null)); | ||
def("ComprehensionExpression") | ||
.bases("Expression") | ||
.build("body", "blocks", "filter") | ||
.field("body", def("Expression")) | ||
.field("blocks", [def("ComprehensionBlock")]) | ||
.field("filter", or(def("Expression"), null)); | ||
def("ComprehensionExpression") | ||
.bases("Expression") | ||
.build("body", "blocks", "filter") | ||
.field("body", def("Expression")) | ||
.field("blocks", [def("ComprehensionBlock")]) | ||
.field("filter", or(def("Expression"), null)); | ||
def("ComprehensionBlock") | ||
.bases("Node") | ||
.build("left", "right", "each") | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")) | ||
.field("each", Boolean); | ||
def("ComprehensionBlock") | ||
.bases("Node") | ||
.build("left", "right", "each") | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")) | ||
.field("each", Boolean); | ||
def("Property") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("value", or(def("Expression"), def("Pattern"))) | ||
.field("method", Boolean, defaults["false"]) | ||
.field("shorthand", Boolean, defaults["false"]) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("Property") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("value", or(def("Expression"), def("Pattern"))) | ||
.field("method", Boolean, defaults["false"]) | ||
.field("shorthand", Boolean, defaults["false"]) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("PropertyPattern") | ||
.bases("Pattern") | ||
.build("key", "pattern") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("pattern", def("Pattern")) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("PropertyPattern") | ||
.bases("Pattern") | ||
.build("key", "pattern") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("pattern", def("Pattern")) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("ObjectPattern") | ||
.bases("Pattern") | ||
.build("properties") | ||
.field("properties", [or(def("PropertyPattern"), def("Property"))]); | ||
def("ObjectPattern") | ||
.bases("Pattern") | ||
.build("properties") | ||
.field("properties", [or(def("PropertyPattern"), def("Property"))]); | ||
def("ArrayPattern") | ||
.bases("Pattern") | ||
.build("elements") | ||
.field("elements", [or(def("Pattern"), null)]); | ||
def("ArrayPattern") | ||
.bases("Pattern") | ||
.build("elements") | ||
.field("elements", [or(def("Pattern"), null)]); | ||
def("MethodDefinition") | ||
.bases("Declaration") | ||
.build("kind", "key", "value", "static") | ||
.field("kind", or("constructor", "method", "get", "set")) | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("value", def("Function")) | ||
.field("computed", Boolean, defaults["false"]) | ||
.field("static", Boolean, defaults["false"]); | ||
def("MethodDefinition") | ||
.bases("Declaration") | ||
.build("kind", "key", "value", "static") | ||
.field("kind", or("constructor", "method", "get", "set")) | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("value", def("Function")) | ||
.field("computed", Boolean, defaults["false"]) | ||
.field("static", Boolean, defaults["false"]); | ||
def("SpreadElement") | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
def("SpreadElement") | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
def("ArrayExpression") | ||
.field("elements", [or( | ||
def("ArrayExpression") | ||
.field("elements", [or( | ||
def("Expression"), | ||
@@ -116,103 +117,104 @@ def("SpreadElement"), | ||
null | ||
)]); | ||
)]); | ||
def("NewExpression") | ||
.field("arguments", [or(def("Expression"), def("SpreadElement"))]); | ||
def("NewExpression") | ||
.field("arguments", [or(def("Expression"), def("SpreadElement"))]); | ||
def("CallExpression") | ||
.field("arguments", [or(def("Expression"), def("SpreadElement"))]); | ||
def("CallExpression") | ||
.field("arguments", [or(def("Expression"), def("SpreadElement"))]); | ||
// Note: this node type is *not* an AssignmentExpression with a Pattern on | ||
// the left-hand side! The existing AssignmentExpression type already | ||
// supports destructuring assignments. AssignmentPattern nodes may appear | ||
// wherever a Pattern is allowed, and the right-hand side represents a | ||
// default value to be destructured against the left-hand side, if no | ||
// value is otherwise provided. For example: default parameter values. | ||
def("AssignmentPattern") | ||
.bases("Pattern") | ||
.build("left", "right") | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")); | ||
// Note: this node type is *not* an AssignmentExpression with a Pattern on | ||
// the left-hand side! The existing AssignmentExpression type already | ||
// supports destructuring assignments. AssignmentPattern nodes may appear | ||
// wherever a Pattern is allowed, and the right-hand side represents a | ||
// default value to be destructured against the left-hand side, if no | ||
// value is otherwise provided. For example: default parameter values. | ||
def("AssignmentPattern") | ||
.bases("Pattern") | ||
.build("left", "right") | ||
.field("left", def("Pattern")) | ||
.field("right", def("Expression")); | ||
var ClassBodyElement = or( | ||
def("MethodDefinition"), | ||
def("VariableDeclarator"), | ||
def("ClassPropertyDefinition"), | ||
var ClassBodyElement = or( | ||
def("MethodDefinition"), | ||
def("VariableDeclarator"), | ||
def("ClassPropertyDefinition"), | ||
def("ClassProperty") | ||
); | ||
def("ClassProperty") | ||
); | ||
.bases("Declaration") | ||
.build("key") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("ClassProperty") | ||
.bases("Declaration") | ||
.build("key") | ||
.field("key", or(def("Literal"), def("Identifier"), def("Expression"))) | ||
.field("computed", Boolean, defaults["false"]); | ||
def("ClassPropertyDefinition") // static property | ||
.bases("Declaration") | ||
.build("definition") | ||
// Yes, Virginia, circular definitions are permitted. | ||
.field("definition", ClassBodyElement); | ||
def("ClassPropertyDefinition") // static property | ||
.bases("Declaration") | ||
.build("definition") | ||
// Yes, Virginia, circular definitions are permitted. | ||
.field("definition", ClassBodyElement); | ||
def("ClassBody") | ||
.bases("Declaration") | ||
.build("body") | ||
.field("body", [ClassBodyElement]); | ||
def("ClassBody") | ||
.bases("Declaration") | ||
.build("body") | ||
.field("body", [ClassBodyElement]); | ||
def("ClassDeclaration") | ||
.bases("Declaration") | ||
.build("id", "body", "superClass") | ||
.field("id", or(def("Identifier"), null)) | ||
.field("body", def("ClassBody")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]); | ||
def("ClassDeclaration") | ||
.bases("Declaration") | ||
.build("id", "body", "superClass") | ||
.field("id", or(def("Identifier"), null)) | ||
.field("body", def("ClassBody")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]); | ||
def("ClassExpression") | ||
.bases("Expression") | ||
.build("id", "body", "superClass") | ||
.field("id", or(def("Identifier"), null), defaults["null"]) | ||
.field("body", def("ClassBody")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]) | ||
.field("implements", [def("ClassImplements")], defaults.emptyArray); | ||
def("ClassExpression") | ||
.bases("Expression") | ||
.build("id", "body", "superClass") | ||
.field("id", or(def("Identifier"), null), defaults["null"]) | ||
.field("body", def("ClassBody")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]) | ||
.field("implements", [def("ClassImplements")], defaults.emptyArray); | ||
def("ClassImplements") | ||
.bases("Node") | ||
.build("id") | ||
.field("id", def("Identifier")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]); | ||
def("ClassImplements") | ||
.bases("Node") | ||
.build("id") | ||
.field("id", def("Identifier")) | ||
.field("superClass", or(def("Expression"), null), defaults["null"]); | ||
// Specifier and ModuleSpecifier are abstract non-standard types | ||
// introduced for definitional convenience. | ||
def("Specifier").bases("Node"); | ||
// Specifier and ModuleSpecifier are abstract non-standard types | ||
// introduced for definitional convenience. | ||
def("Specifier").bases("Node"); | ||
// This supertype is shared/abused by both def/babel.js and | ||
// def/esprima.js. In the future, it will be possible to load only one set | ||
// of definitions appropriate for a given parser, but until then we must | ||
// rely on default functions to reconcile the conflicting AST formats. | ||
def("ModuleSpecifier") | ||
.bases("Specifier") | ||
// This local field is used by Babel/Acorn. It should not technically | ||
// be optional in the Babel/Acorn AST format, but it must be optional | ||
// in the Esprima AST format. | ||
.field("local", or(def("Identifier"), null), defaults["null"]) | ||
// The id and name fields are used by Esprima. The id field should not | ||
// technically be optional in the Esprima AST format, but it must be | ||
// optional in the Babel/Acorn AST format. | ||
.field("id", or(def("Identifier"), null), defaults["null"]) | ||
.field("name", or(def("Identifier"), null), defaults["null"]); | ||
// This supertype is shared/abused by both def/babel.js and | ||
// def/esprima.js. In the future, it will be possible to load only one set | ||
// of definitions appropriate for a given parser, but until then we must | ||
// rely on default functions to reconcile the conflicting AST formats. | ||
def("ModuleSpecifier") | ||
.bases("Specifier") | ||
// This local field is used by Babel/Acorn. It should not technically | ||
// be optional in the Babel/Acorn AST format, but it must be optional | ||
// in the Esprima AST format. | ||
.field("local", or(def("Identifier"), null), defaults["null"]) | ||
// The id and name fields are used by Esprima. The id field should not | ||
// technically be optional in the Esprima AST format, but it must be | ||
// optional in the Babel/Acorn AST format. | ||
.field("id", or(def("Identifier"), null), defaults["null"]) | ||
.field("name", or(def("Identifier"), null), defaults["null"]); | ||
def("TaggedTemplateExpression") | ||
.bases("Expression") | ||
.build("tag", "quasi") | ||
.field("tag", def("Expression")) | ||
.field("quasi", def("TemplateLiteral")); | ||
def("TaggedTemplateExpression") | ||
.bases("Expression") | ||
.build("tag", "quasi") | ||
.field("tag", def("Expression")) | ||
.field("quasi", def("TemplateLiteral")); | ||
def("TemplateLiteral") | ||
.bases("Expression") | ||
.build("quasis", "expressions") | ||
.field("quasis", [def("TemplateElement")]) | ||
.field("expressions", [def("Expression")]); | ||
def("TemplateLiteral") | ||
.bases("Expression") | ||
.build("quasis", "expressions") | ||
.field("quasis", [def("TemplateElement")]) | ||
.field("expressions", [def("Expression")]); | ||
def("TemplateElement") | ||
.bases("Node") | ||
.build("value", "tail") | ||
.field("value", {"cooked": String, "raw": String}) | ||
.field("tail", Boolean); | ||
def("TemplateElement") | ||
.bases("Node") | ||
.build("value", "tail") | ||
.field("value", {"cooked": String, "raw": String}) | ||
.field("tail", Boolean); | ||
}; |
@@ -1,36 +0,38 @@ | ||
require("./es6"); | ||
module.exports = function (fork) { | ||
fork.use(require('./es6')); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var builtin = types.builtInTypes; | ||
var defaults = require("../lib/shared").defaults; | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var builtin = types.builtInTypes; | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
def("Function") | ||
.field("async", Boolean, defaults["false"]); | ||
def("Function") | ||
.field("async", Boolean, defaults["false"]); | ||
def("SpreadProperty") | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
def("SpreadProperty") | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
def("ObjectExpression") | ||
.field("properties", [or(def("Property"), def("SpreadProperty"))]); | ||
def("ObjectExpression") | ||
.field("properties", [or(def("Property"), def("SpreadProperty"))]); | ||
def("SpreadPropertyPattern") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
def("SpreadPropertyPattern") | ||
.bases("Pattern") | ||
.build("argument") | ||
.field("argument", def("Pattern")); | ||
def("ObjectPattern") | ||
.field("properties", [or( | ||
def("ObjectPattern") | ||
.field("properties", [or( | ||
def("Property"), | ||
def("PropertyPattern"), | ||
def("SpreadPropertyPattern") | ||
)]); | ||
)]); | ||
def("AwaitExpression") | ||
.bases("Expression") | ||
.build("argument", "all") | ||
.field("argument", or(def("Expression"), null)) | ||
.field("all", Boolean, defaults["false"]); | ||
def("AwaitExpression") | ||
.bases("Expression") | ||
.build("argument", "all") | ||
.field("argument", or(def("Expression"), null)) | ||
.field("all", Boolean, defaults["false"]); | ||
}; |
@@ -1,29 +0,30 @@ | ||
require("./es7"); | ||
module.exports = function (fork) { | ||
fork.use(require("./es7")); | ||
var types = require("../lib/types"); | ||
var defaults = require("../lib/shared").defaults; | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var types = fork.use(require("../lib/types")); | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
def("VariableDeclaration") | ||
.field("declarations", [or( | ||
def("VariableDeclaration") | ||
.field("declarations", [or( | ||
def("VariableDeclarator"), | ||
def("Identifier") // Esprima deviation. | ||
)]); | ||
)]); | ||
def("Property") | ||
.field("value", or( | ||
def("Property") | ||
.field("value", or( | ||
def("Expression"), | ||
def("Pattern") // Esprima deviation. | ||
)); | ||
)); | ||
def("ArrayPattern") | ||
.field("elements", [or( | ||
def("ArrayPattern") | ||
.field("elements", [or( | ||
def("Pattern"), | ||
def("SpreadElement"), | ||
null | ||
)]); | ||
)]); | ||
def("ObjectPattern") | ||
.field("properties", [or( | ||
def("ObjectPattern") | ||
.field("properties", [or( | ||
def("Property"), | ||
@@ -33,65 +34,66 @@ def("PropertyPattern"), | ||
def("SpreadProperty") // Used by Esprima. | ||
)]); | ||
)]); | ||
// Like ModuleSpecifier, except type:"ExportSpecifier" and buildable. | ||
// export {<id [as name]>} [from ...]; | ||
def("ExportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id", "name"); | ||
def("ExportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id", "name"); | ||
// export <*> from ...; | ||
def("ExportBatchSpecifier") | ||
.bases("Specifier") | ||
.build(); | ||
def("ExportBatchSpecifier") | ||
.bases("Specifier") | ||
.build(); | ||
// Like ModuleSpecifier, except type:"ImportSpecifier" and buildable. | ||
// import {<id [as name]>} from ...; | ||
def("ImportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id", "name"); | ||
def("ImportSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id", "name"); | ||
// import <* as id> from ...; | ||
def("ImportNamespaceSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id"); | ||
def("ImportNamespaceSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id"); | ||
// import <id> from ...; | ||
def("ImportDefaultSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id"); | ||
def("ImportDefaultSpecifier") | ||
.bases("ModuleSpecifier") | ||
.build("id"); | ||
def("ExportDeclaration") | ||
.bases("Declaration") | ||
.build("default", "declaration", "specifiers", "source") | ||
.field("default", Boolean) | ||
.field("declaration", or( | ||
def("ExportDeclaration") | ||
.bases("Declaration") | ||
.build("default", "declaration", "specifiers", "source") | ||
.field("default", Boolean) | ||
.field("declaration", or( | ||
def("Declaration"), | ||
def("Expression"), // Implies default. | ||
null | ||
)) | ||
.field("specifiers", [or( | ||
)) | ||
.field("specifiers", [or( | ||
def("ExportSpecifier"), | ||
def("ExportBatchSpecifier") | ||
)], defaults.emptyArray) | ||
.field("source", or( | ||
)], defaults.emptyArray) | ||
.field("source", or( | ||
def("Literal"), | ||
null | ||
), defaults["null"]); | ||
), defaults["null"]); | ||
def("ImportDeclaration") | ||
.bases("Declaration") | ||
.build("specifiers", "source") | ||
.field("specifiers", [or( | ||
def("ImportDeclaration") | ||
.bases("Declaration") | ||
.build("specifiers", "source") | ||
.field("specifiers", [or( | ||
def("ImportSpecifier"), | ||
def("ImportNamespaceSpecifier"), | ||
def("ImportDefaultSpecifier") | ||
)], defaults.emptyArray) | ||
.field("source", def("Literal")); | ||
)], defaults.emptyArray) | ||
.field("source", def("Literal")); | ||
def("Block") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("Block") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("Line") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
def("Line") | ||
.bases("Comment") | ||
.build("value", /*optional:*/ "leading", "trailing"); | ||
}; |
468
def/flow.js
@@ -1,275 +0,276 @@ | ||
require("./es7"); | ||
module.exports = function (fork) { | ||
fork.use(require("./es7")); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = require("../lib/shared").defaults; | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
// Type Annotations | ||
def("Type").bases("Node"); | ||
// Type Annotations | ||
def("Type").bases("Node"); | ||
def("AnyTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("AnyTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("MixedTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("MixedTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("VoidTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("VoidTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NumberTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NumberTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NumberLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", Number) | ||
.field("raw", String); | ||
def("NumberLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", Number) | ||
.field("raw", String); | ||
def("StringTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("StringTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("StringLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", String) | ||
.field("raw", String); | ||
def("StringLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", String) | ||
.field("raw", String); | ||
def("BooleanTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("BooleanTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("BooleanLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", Boolean) | ||
.field("raw", String); | ||
def("BooleanLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build("value", "raw") | ||
.field("value", Boolean) | ||
.field("raw", String); | ||
def("TypeAnnotation") | ||
.bases("Node") | ||
.build("typeAnnotation") | ||
.field("typeAnnotation", def("Type")); | ||
def("TypeAnnotation") | ||
.bases("Node") | ||
.build("typeAnnotation") | ||
.field("typeAnnotation", def("Type")); | ||
def("NullableTypeAnnotation") | ||
.bases("Type") | ||
.build("typeAnnotation") | ||
.field("typeAnnotation", def("Type")); | ||
def("NullableTypeAnnotation") | ||
.bases("Type") | ||
.build("typeAnnotation") | ||
.field("typeAnnotation", def("Type")); | ||
def("NullLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NullLiteralTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NullTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("NullTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("ThisTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("ThisTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("ExistsTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("FunctionTypeAnnotation") | ||
.bases("Type") | ||
.build("params", "returnType", "rest", "typeParameters") | ||
.field("params", [def("FunctionTypeParam")]) | ||
.field("returnType", def("Type")) | ||
.field("rest", or(def("FunctionTypeParam"), null)) | ||
.field("typeParameters", or(def("TypeParameterDeclaration"), null)); | ||
def("ExistsTypeAnnotation") | ||
.bases("Type") | ||
.build(); | ||
def("FunctionTypeParam") | ||
.bases("Node") | ||
.build("name", "typeAnnotation", "optional") | ||
.field("name", def("Identifier")) | ||
.field("typeAnnotation", def("Type")) | ||
.field("optional", Boolean); | ||
def("FunctionTypeAnnotation") | ||
.bases("Type") | ||
.build("params", "returnType", "rest", "typeParameters") | ||
.field("params", [def("FunctionTypeParam")]) | ||
.field("returnType", def("Type")) | ||
.field("rest", or(def("FunctionTypeParam"), null)) | ||
.field("typeParameters", or(def("TypeParameterDeclaration"), null)); | ||
def("ArrayTypeAnnotation") | ||
.bases("Type") | ||
.build("elementType") | ||
.field("elementType", def("Type")); | ||
def("FunctionTypeParam") | ||
.bases("Node") | ||
.build("name", "typeAnnotation", "optional") | ||
.field("name", def("Identifier")) | ||
.field("typeAnnotation", def("Type")) | ||
.field("optional", Boolean); | ||
def("ObjectTypeAnnotation") | ||
.bases("Type") | ||
.build("properties", "indexers", "callProperties") | ||
.field("properties", [def("ObjectTypeProperty")]) | ||
.field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray) | ||
.field("callProperties", | ||
[def("ObjectTypeCallProperty")], | ||
defaults.emptyArray); | ||
def("ArrayTypeAnnotation") | ||
.bases("Type") | ||
.build("elementType") | ||
.field("elementType", def("Type")); | ||
def("ObjectTypeProperty") | ||
.bases("Node") | ||
.build("key", "value", "optional") | ||
.field("key", or(def("Literal"), def("Identifier"))) | ||
.field("value", def("Type")) | ||
.field("optional", Boolean); | ||
def("ObjectTypeAnnotation") | ||
.bases("Type") | ||
.build("properties", "indexers", "callProperties") | ||
.field("properties", [def("ObjectTypeProperty")]) | ||
.field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray) | ||
.field("callProperties", | ||
[def("ObjectTypeCallProperty")], | ||
defaults.emptyArray); | ||
def("ObjectTypeIndexer") | ||
.bases("Node") | ||
.build("id", "key", "value") | ||
.field("id", def("Identifier")) | ||
.field("key", def("Type")) | ||
.field("value", def("Type")); | ||
def("ObjectTypeProperty") | ||
.bases("Node") | ||
.build("key", "value", "optional") | ||
.field("key", or(def("Literal"), def("Identifier"))) | ||
.field("value", def("Type")) | ||
.field("optional", Boolean); | ||
def("ObjectTypeCallProperty") | ||
.bases("Node") | ||
.build("value") | ||
.field("value", def("FunctionTypeAnnotation")) | ||
.field("static", Boolean, defaults["false"]); | ||
def("ObjectTypeIndexer") | ||
.bases("Node") | ||
.build("id", "key", "value") | ||
.field("id", def("Identifier")) | ||
.field("key", def("Type")) | ||
.field("value", def("Type")); | ||
def("QualifiedTypeIdentifier") | ||
.bases("Node") | ||
.build("qualification", "id") | ||
.field("qualification", | ||
or(def("Identifier"), | ||
def("QualifiedTypeIdentifier"))) | ||
.field("id", def("Identifier")); | ||
def("ObjectTypeCallProperty") | ||
.bases("Node") | ||
.build("value") | ||
.field("value", def("FunctionTypeAnnotation")) | ||
.field("static", Boolean, defaults["false"]); | ||
def("GenericTypeAnnotation") | ||
.bases("Type") | ||
.build("id", "typeParameters") | ||
.field("id", or(def("Identifier"), def("QualifiedTypeIdentifier"))) | ||
.field("typeParameters", or(def("TypeParameterInstantiation"), null)); | ||
def("QualifiedTypeIdentifier") | ||
.bases("Node") | ||
.build("qualification", "id") | ||
.field("qualification", | ||
or(def("Identifier"), | ||
def("QualifiedTypeIdentifier"))) | ||
.field("id", def("Identifier")); | ||
def("MemberTypeAnnotation") | ||
.bases("Type") | ||
.build("object", "property") | ||
.field("object", def("Identifier")) | ||
.field("property", | ||
or(def("MemberTypeAnnotation"), | ||
def("GenericTypeAnnotation"))); | ||
def("GenericTypeAnnotation") | ||
.bases("Type") | ||
.build("id", "typeParameters") | ||
.field("id", or(def("Identifier"), def("QualifiedTypeIdentifier"))) | ||
.field("typeParameters", or(def("TypeParameterInstantiation"), null)); | ||
def("UnionTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("MemberTypeAnnotation") | ||
.bases("Type") | ||
.build("object", "property") | ||
.field("object", def("Identifier")) | ||
.field("property", | ||
or(def("MemberTypeAnnotation"), | ||
def("GenericTypeAnnotation"))); | ||
def("IntersectionTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("UnionTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("TypeofTypeAnnotation") | ||
.bases("Type") | ||
.build("argument") | ||
.field("argument", def("Type")); | ||
def("IntersectionTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("Identifier") | ||
.field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]); | ||
def("TypeofTypeAnnotation") | ||
.bases("Type") | ||
.build("argument") | ||
.field("argument", def("Type")); | ||
def("TypeParameterDeclaration") | ||
.bases("Node") | ||
.build("params") | ||
.field("params", [def("TypeParameter")]); | ||
def("Identifier") | ||
.field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]); | ||
def("TypeParameterInstantiation") | ||
.bases("Node") | ||
.build("params") | ||
.field("params", [def("Type")]); | ||
def("TypeParameterDeclaration") | ||
.bases("Node") | ||
.build("params") | ||
.field("params", [def("TypeParameter")]); | ||
def("TypeParameter") | ||
.bases("Type") | ||
.build("name", "variance", "bound") | ||
.field("name", String) | ||
.field("variance", | ||
or("plus", "minus", null), | ||
defaults["null"]) | ||
.field("bound", | ||
or(def("TypeAnnotation"), null), | ||
defaults["null"]); | ||
def("Function") | ||
.field("returnType", | ||
or(def("TypeAnnotation"), null), | ||
defaults["null"]) | ||
.field("typeParameters", | ||
or(def("TypeParameterDeclaration"), null), | ||
defaults["null"]); | ||
def("TypeParameterInstantiation") | ||
.bases("Node") | ||
.build("params") | ||
.field("params", [def("Type")]); | ||
def("ClassProperty") | ||
.build("key", "value", "typeAnnotation", "static") | ||
.field("value", or(def("Expression"), null)) | ||
.field("typeAnnotation", or(def("TypeAnnotation"), null)) | ||
.field("static", Boolean, defaults["false"]); | ||
def("TypeParameter") | ||
.bases("Type") | ||
.build("name", "variance", "bound") | ||
.field("name", String) | ||
.field("variance", | ||
or("plus", "minus", null), | ||
defaults["null"]) | ||
.field("bound", | ||
or(def("TypeAnnotation"), null), | ||
defaults["null"]); | ||
def("ClassImplements") | ||
.field("typeParameters", | ||
or(def("TypeParameterInstantiation"), null), | ||
defaults["null"]); | ||
def("Function") | ||
.field("returnType", | ||
or(def("TypeAnnotation"), null), | ||
defaults["null"]) | ||
.field("typeParameters", | ||
or(def("TypeParameterDeclaration"), null), | ||
defaults["null"]); | ||
def("InterfaceDeclaration") | ||
.bases("Declaration") | ||
.build("id", "body", "extends") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", | ||
or(def("TypeParameterDeclaration"), null), | ||
defaults["null"]) | ||
.field("body", def("ObjectTypeAnnotation")) | ||
.field("extends", [def("InterfaceExtends")]); | ||
def("ClassProperty") | ||
.build("key", "value", "typeAnnotation", "static") | ||
.field("value", or(def("Expression"), null)) | ||
.field("typeAnnotation", or(def("TypeAnnotation"), null)) | ||
.field("static", Boolean, defaults["false"]); | ||
def("DeclareInterface") | ||
.bases("InterfaceDeclaration") | ||
.build("id", "body", "extends"); | ||
def("ClassImplements") | ||
.field("typeParameters", | ||
or(def("TypeParameterInstantiation"), null), | ||
defaults["null"]); | ||
def("InterfaceExtends") | ||
.bases("Node") | ||
.build("id") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", or(def("TypeParameterInstantiation"), null)); | ||
def("InterfaceDeclaration") | ||
.bases("Declaration") | ||
.build("id", "body", "extends") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", | ||
or(def("TypeParameterDeclaration"), null), | ||
defaults["null"]) | ||
.field("body", def("ObjectTypeAnnotation")) | ||
.field("extends", [def("InterfaceExtends")]); | ||
def("TypeAlias") | ||
.bases("Declaration") | ||
.build("id", "typeParameters", "right") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", or(def("TypeParameterDeclaration"), null)) | ||
.field("right", def("Type")); | ||
def("DeclareInterface") | ||
.bases("InterfaceDeclaration") | ||
.build("id", "body", "extends"); | ||
def("DeclareTypeAlias") | ||
.bases("TypeAlias") | ||
.build("id", "typeParameters", "right"); | ||
def("InterfaceExtends") | ||
.bases("Node") | ||
.build("id") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", or(def("TypeParameterInstantiation"), null)); | ||
def("TypeCastExpression") | ||
.bases("Expression") | ||
.build("expression", "typeAnnotation") | ||
.field("expression", def("Expression")) | ||
.field("typeAnnotation", def("TypeAnnotation")); | ||
def("TypeAlias") | ||
.bases("Declaration") | ||
.build("id", "typeParameters", "right") | ||
.field("id", def("Identifier")) | ||
.field("typeParameters", or(def("TypeParameterDeclaration"), null)) | ||
.field("right", def("Type")); | ||
def("TupleTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("DeclareTypeAlias") | ||
.bases("TypeAlias") | ||
.build("id", "typeParameters", "right"); | ||
def("DeclareVariable") | ||
.bases("Statement") | ||
.build("id") | ||
.field("id", def("Identifier")); | ||
def("TypeCastExpression") | ||
.bases("Expression") | ||
.build("expression", "typeAnnotation") | ||
.field("expression", def("Expression")) | ||
.field("typeAnnotation", def("TypeAnnotation")); | ||
def("DeclareFunction") | ||
.bases("Statement") | ||
.build("id") | ||
.field("id", def("Identifier")); | ||
def("TupleTypeAnnotation") | ||
.bases("Type") | ||
.build("types") | ||
.field("types", [def("Type")]); | ||
def("DeclareClass") | ||
.bases("InterfaceDeclaration") | ||
.build("id"); | ||
def("DeclareVariable") | ||
.bases("Statement") | ||
.build("id") | ||
.field("id", def("Identifier")); | ||
def("DeclareModule") | ||
.bases("Statement") | ||
.build("id", "body") | ||
.field("id", or(def("Identifier"), def("Literal"))) | ||
.field("body", def("BlockStatement")); | ||
def("DeclareFunction") | ||
.bases("Statement") | ||
.build("id") | ||
.field("id", def("Identifier")); | ||
def("DeclareClass") | ||
.bases("InterfaceDeclaration") | ||
.build("id"); | ||
def("DeclareModule") | ||
.bases("Statement") | ||
.build("id", "body") | ||
.field("id", or(def("Identifier"), def("Literal"))) | ||
.field("body", def("BlockStatement")); | ||
def("DeclareExportDeclaration") | ||
.bases("Declaration") | ||
.build("default", "declaration", "specifiers", "source") | ||
.field("default", Boolean) | ||
.field("declaration", or( | ||
def("DeclareExportDeclaration") | ||
.bases("Declaration") | ||
.build("default", "declaration", "specifiers", "source") | ||
.field("default", Boolean) | ||
.field("declaration", or( | ||
def("DeclareVariable"), | ||
@@ -280,10 +281,11 @@ def("DeclareFunction"), | ||
null | ||
)) | ||
.field("specifiers", [or( | ||
)) | ||
.field("specifiers", [or( | ||
def("ExportSpecifier"), | ||
def("ExportBatchSpecifier") | ||
)], defaults.emptyArray) | ||
.field("source", or( | ||
)], defaults.emptyArray) | ||
.field("source", or( | ||
def("Literal"), | ||
null | ||
), defaults["null"]); | ||
), defaults["null"]); | ||
}; |
159
def/jsx.js
@@ -1,63 +0,64 @@ | ||
require("./es7"); | ||
module.exports = function (fork) { | ||
fork.use(require("./es7")); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = require("../lib/shared").defaults; | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var defaults = fork.use(require("../lib/shared")).defaults; | ||
def("JSXAttribute") | ||
.bases("Node") | ||
.build("name", "value") | ||
.field("name", or(def("JSXIdentifier"), def("JSXNamespacedName"))) | ||
.field("value", or( | ||
def("JSXAttribute") | ||
.bases("Node") | ||
.build("name", "value") | ||
.field("name", or(def("JSXIdentifier"), def("JSXNamespacedName"))) | ||
.field("value", or( | ||
def("Literal"), // attr="value" | ||
def("JSXExpressionContainer"), // attr={value} | ||
null // attr= or just attr | ||
), defaults["null"]); | ||
), defaults["null"]); | ||
def("JSXIdentifier") | ||
.bases("Identifier") | ||
.build("name") | ||
.field("name", String); | ||
def("JSXIdentifier") | ||
.bases("Identifier") | ||
.build("name") | ||
.field("name", String); | ||
def("JSXNamespacedName") | ||
.bases("Node") | ||
.build("namespace", "name") | ||
.field("namespace", def("JSXIdentifier")) | ||
.field("name", def("JSXIdentifier")); | ||
def("JSXNamespacedName") | ||
.bases("Node") | ||
.build("namespace", "name") | ||
.field("namespace", def("JSXIdentifier")) | ||
.field("name", def("JSXIdentifier")); | ||
def("JSXMemberExpression") | ||
.bases("MemberExpression") | ||
.build("object", "property") | ||
.field("object", or(def("JSXIdentifier"), def("JSXMemberExpression"))) | ||
.field("property", def("JSXIdentifier")) | ||
.field("computed", Boolean, defaults.false); | ||
var JSXElementName = or( | ||
def("JSXIdentifier"), | ||
def("JSXNamespacedName"), | ||
def("JSXMemberExpression") | ||
); | ||
.bases("MemberExpression") | ||
.build("object", "property") | ||
.field("object", or(def("JSXIdentifier"), def("JSXMemberExpression"))) | ||
.field("property", def("JSXIdentifier")) | ||
.field("computed", Boolean, defaults.false); | ||
def("JSXSpreadAttribute") | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
var JSXElementName = or( | ||
def("JSXIdentifier"), | ||
def("JSXNamespacedName"), | ||
def("JSXMemberExpression") | ||
); | ||
var JSXAttributes = [or( | ||
def("JSXAttribute"), | ||
def("JSXSpreadAttribute") | ||
)]; | ||
.bases("Node") | ||
.build("argument") | ||
.field("argument", def("Expression")); | ||
def("JSXExpressionContainer") | ||
.bases("Expression") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
var JSXAttributes = [or( | ||
def("JSXAttribute"), | ||
def("JSXSpreadAttribute") | ||
)]; | ||
def("JSXElement") | ||
.bases("Expression") | ||
.build("openingElement", "closingElement", "children") | ||
.field("openingElement", def("JSXOpeningElement")) | ||
.field("closingElement", or(def("JSXClosingElement"), null), defaults["null"]) | ||
.field("children", [or( | ||
def("JSXExpressionContainer") | ||
.bases("Expression") | ||
.build("expression") | ||
.field("expression", def("Expression")); | ||
def("JSXElement") | ||
.bases("Expression") | ||
.build("openingElement", "closingElement", "children") | ||
.field("openingElement", def("JSXOpeningElement")) | ||
.field("closingElement", or(def("JSXClosingElement"), null), defaults["null"]) | ||
.field("children", [or( | ||
def("JSXElement"), | ||
@@ -67,35 +68,37 @@ def("JSXExpressionContainer"), | ||
def("Literal") // TODO Esprima should return JSXText instead. | ||
)], defaults.emptyArray) | ||
.field("name", JSXElementName, function() { | ||
// Little-known fact: the `this` object inside a default function | ||
// is none other than the partially-built object itself, and any | ||
// fields initialized directly from builder function arguments | ||
// (like openingElement, closingElement, and children) are | ||
// guaranteed to be available. | ||
return this.openingElement.name; | ||
}, true) // hidden from traversal | ||
.field("selfClosing", Boolean, function() { | ||
return this.openingElement.selfClosing; | ||
}, true) // hidden from traversal | ||
.field("attributes", JSXAttributes, function() { | ||
return this.openingElement.attributes; | ||
}, true); // hidden from traversal | ||
)], defaults.emptyArray) | ||
.field("name", JSXElementName, function () { | ||
// Little-known fact: the `this` object inside a default function | ||
// is none other than the partially-built object itself, and any | ||
// fields initialized directly from builder function arguments | ||
// (like openingElement, closingElement, and children) are | ||
// guaranteed to be available. | ||
return this.openingElement.name; | ||
}, true) // hidden from traversal | ||
.field("selfClosing", Boolean, function () { | ||
return this.openingElement.selfClosing; | ||
}, true) // hidden from traversal | ||
.field("attributes", JSXAttributes, function () { | ||
return this.openingElement.attributes; | ||
}, true); // hidden from traversal | ||
def("JSXOpeningElement") | ||
.bases("Node") // TODO Does this make sense? Can't really be an JSXElement. | ||
.build("name", "attributes", "selfClosing") | ||
.field("name", JSXElementName) | ||
.field("attributes", JSXAttributes, defaults.emptyArray) | ||
.field("selfClosing", Boolean, defaults["false"]); | ||
def("JSXOpeningElement") | ||
.bases("Node") // TODO Does this make sense? Can't really be an JSXElement. | ||
.build("name", "attributes", "selfClosing") | ||
.field("name", JSXElementName) | ||
.field("attributes", JSXAttributes, defaults.emptyArray) | ||
.field("selfClosing", Boolean, defaults["false"]); | ||
def("JSXClosingElement") | ||
.bases("Node") // TODO Same concern. | ||
.build("name") | ||
.field("name", JSXElementName); | ||
def("JSXClosingElement") | ||
.bases("Node") // TODO Same concern. | ||
.build("name") | ||
.field("name", JSXElementName); | ||
def("JSXText") | ||
.bases("Literal") | ||
.build("value") | ||
.field("value", String); | ||
def("JSXText") | ||
.bases("Literal") | ||
.build("value") | ||
.field("value", String); | ||
def("JSXEmptyExpression").bases("Expression").build(); | ||
def("JSXEmptyExpression").bases("Expression").build(); | ||
}; |
@@ -1,49 +0,51 @@ | ||
require("./core"); | ||
var types = require("../lib/types"); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var shared = require("../lib/shared"); | ||
var geq = shared.geq; | ||
var defaults = shared.defaults; | ||
module.exports = function (fork) { | ||
fork.use(require("./core")); | ||
var types = fork.use(require("../lib/types")); | ||
var def = types.Type.def; | ||
var or = types.Type.or; | ||
var shared = fork.use(require("../lib/shared")); | ||
var geq = shared.geq; | ||
var defaults = shared.defaults; | ||
def("Function") | ||
// SpiderMonkey allows expression closures: function(x) x+1 | ||
.field("body", or(def("BlockStatement"), def("Expression"))); | ||
def("Function") | ||
// SpiderMonkey allows expression closures: function(x) x+1 | ||
.field("body", or(def("BlockStatement"), def("Expression"))); | ||
def("ForInStatement") | ||
.build("left", "right", "body", "each") | ||
.field("each", Boolean, defaults["false"]); | ||
def("ForInStatement") | ||
.build("left", "right", "body", "each") | ||
.field("each", Boolean, defaults["false"]); | ||
def("ForOfStatement") | ||
.bases("Statement") | ||
.build("left", "right", "body") | ||
.field("left", or( | ||
def("VariableDeclaration"), | ||
def("Expression"))) | ||
.field("right", def("Expression")) | ||
.field("body", def("Statement")); | ||
def("ForOfStatement") | ||
.bases("Statement") | ||
.build("left", "right", "body") | ||
.field("left", or( | ||
def("VariableDeclaration"), | ||
def("Expression"))) | ||
.field("right", def("Expression")) | ||
.field("body", def("Statement")); | ||
def("LetStatement") | ||
.bases("Statement") | ||
.build("head", "body") | ||
// TODO Deviating from the spec by reusing VariableDeclarator here. | ||
.field("head", [def("VariableDeclarator")]) | ||
.field("body", def("Statement")); | ||
def("LetStatement") | ||
.bases("Statement") | ||
.build("head", "body") | ||
// TODO Deviating from the spec by reusing VariableDeclarator here. | ||
.field("head", [def("VariableDeclarator")]) | ||
.field("body", def("Statement")); | ||
def("LetExpression") | ||
.bases("Expression") | ||
.build("head", "body") | ||
// TODO Deviating from the spec by reusing VariableDeclarator here. | ||
.field("head", [def("VariableDeclarator")]) | ||
.field("body", def("Expression")); | ||
def("LetExpression") | ||
.bases("Expression") | ||
.build("head", "body") | ||
// TODO Deviating from the spec by reusing VariableDeclarator here. | ||
.field("head", [def("VariableDeclarator")]) | ||
.field("body", def("Expression")); | ||
def("GraphExpression") | ||
.bases("Expression") | ||
.build("index", "expression") | ||
.field("index", geq(0)) | ||
.field("expression", def("Literal")); | ||
def("GraphExpression") | ||
.bases("Expression") | ||
.build("index", "expression") | ||
.field("index", geq(0)) | ||
.field("expression", def("Literal")); | ||
def("GraphIndexExpression") | ||
.bases("Expression") | ||
.build("index") | ||
.field("index", geq(0)); | ||
def("GraphIndexExpression") | ||
.bases("Expression") | ||
.build("index") | ||
.field("index", geq(0)); | ||
}; |
270
lib/equiv.js
@@ -1,144 +0,100 @@ | ||
var types = require("../main"); | ||
var getFieldNames = types.getFieldNames; | ||
var getFieldValue = types.getFieldValue; | ||
var isArray = types.builtInTypes.array; | ||
var isObject = types.builtInTypes.object; | ||
var isDate = types.builtInTypes.Date; | ||
var isRegExp = types.builtInTypes.RegExp; | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
module.exports = function (fork) { | ||
var types = fork.use(require('../lib/types')); | ||
var getFieldNames = types.getFieldNames; | ||
var getFieldValue = types.getFieldValue; | ||
var isArray = types.builtInTypes.array; | ||
var isObject = types.builtInTypes.object; | ||
var isDate = types.builtInTypes.Date; | ||
var isRegExp = types.builtInTypes.RegExp; | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
function astNodesAreEquivalent(a, b, problemPath) { | ||
if (isArray.check(problemPath)) { | ||
problemPath.length = 0; | ||
} else { | ||
problemPath = null; | ||
} | ||
return areEquivalent(a, b, problemPath); | ||
} | ||
astNodesAreEquivalent.assert = function(a, b) { | ||
var problemPath = []; | ||
if (!astNodesAreEquivalent(a, b, problemPath)) { | ||
if (problemPath.length === 0) { | ||
if (a !== b) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
function astNodesAreEquivalent(a, b, problemPath) { | ||
if (isArray.check(problemPath)) { | ||
problemPath.length = 0; | ||
} else { | ||
throw new Error( | ||
"Nodes differ in the following path: " + | ||
problemPath.map(subscriptForProperty).join("") | ||
); | ||
problemPath = null; | ||
} | ||
} | ||
}; | ||
function subscriptForProperty(property) { | ||
if (/[_$a-z][_$a-z0-9]*/i.test(property)) { | ||
return "." + property; | ||
return areEquivalent(a, b, problemPath); | ||
} | ||
return "[" + JSON.stringify(property) + "]"; | ||
} | ||
function areEquivalent(a, b, problemPath) { | ||
if (a === b) { | ||
return true; | ||
} | ||
astNodesAreEquivalent.assert = function (a, b) { | ||
var problemPath = []; | ||
if (!astNodesAreEquivalent(a, b, problemPath)) { | ||
if (problemPath.length === 0) { | ||
if (a !== b) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
} else { | ||
throw new Error( | ||
"Nodes differ in the following path: " + | ||
problemPath.map(subscriptForProperty).join("") | ||
); | ||
} | ||
} | ||
}; | ||
if (isArray.check(a)) { | ||
return arraysAreEquivalent(a, b, problemPath); | ||
function subscriptForProperty(property) { | ||
if (/[_$a-z][_$a-z0-9]*/i.test(property)) { | ||
return "." + property; | ||
} | ||
return "[" + JSON.stringify(property) + "]"; | ||
} | ||
if (isObject.check(a)) { | ||
return objectsAreEquivalent(a, b, problemPath); | ||
} | ||
function areEquivalent(a, b, problemPath) { | ||
if (a === b) { | ||
return true; | ||
} | ||
if (isDate.check(a)) { | ||
return isDate.check(b) && (+a === +b); | ||
} | ||
if (isRegExp.check(a)) { | ||
return isRegExp.check(b) && ( | ||
a.source === b.source && | ||
a.global === b.global && | ||
a.multiline === b.multiline && | ||
a.ignoreCase === b.ignoreCase | ||
); | ||
} | ||
return a == b; | ||
} | ||
function arraysAreEquivalent(a, b, problemPath) { | ||
isArray.assert(a); | ||
var aLength = a.length; | ||
if (!isArray.check(b) || b.length !== aLength) { | ||
if (problemPath) { | ||
problemPath.push("length"); | ||
if (isArray.check(a)) { | ||
return arraysAreEquivalent(a, b, problemPath); | ||
} | ||
return false; | ||
} | ||
for (var i = 0; i < aLength; ++i) { | ||
if (problemPath) { | ||
problemPath.push(i); | ||
if (isObject.check(a)) { | ||
return objectsAreEquivalent(a, b, problemPath); | ||
} | ||
if (i in a !== i in b) { | ||
return false; | ||
if (isDate.check(a)) { | ||
return isDate.check(b) && (+a === +b); | ||
} | ||
if (!areEquivalent(a[i], b[i], problemPath)) { | ||
return false; | ||
if (isRegExp.check(a)) { | ||
return isRegExp.check(b) && ( | ||
a.source === b.source && | ||
a.global === b.global && | ||
a.multiline === b.multiline && | ||
a.ignoreCase === b.ignoreCase | ||
); | ||
} | ||
if (problemPath) { | ||
var problemPathTail = problemPath.pop(); | ||
if (problemPathTail !== i) { | ||
throw new Error("" + problemPathTail); | ||
} | ||
} | ||
return a == b; | ||
} | ||
return true; | ||
} | ||
function arraysAreEquivalent(a, b, problemPath) { | ||
isArray.assert(a); | ||
var aLength = a.length; | ||
function objectsAreEquivalent(a, b, problemPath) { | ||
isObject.assert(a); | ||
if (!isObject.check(b)) { | ||
return false; | ||
} | ||
// Fast path for a common property of AST nodes. | ||
if (a.type !== b.type) { | ||
if (problemPath) { | ||
problemPath.push("type"); | ||
if (!isArray.check(b) || b.length !== aLength) { | ||
if (problemPath) { | ||
problemPath.push("length"); | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
var aNames = getFieldNames(a); | ||
var aNameCount = aNames.length; | ||
var bNames = getFieldNames(b); | ||
var bNameCount = bNames.length; | ||
if (aNameCount === bNameCount) { | ||
for (var i = 0; i < aNameCount; ++i) { | ||
var name = aNames[i]; | ||
var aChild = getFieldValue(a, name); | ||
var bChild = getFieldValue(b, name); | ||
for (var i = 0; i < aLength; ++i) { | ||
if (problemPath) { | ||
problemPath.push(name); | ||
problemPath.push(i); | ||
} | ||
if (!areEquivalent(aChild, bChild, problemPath)) { | ||
if (i in a !== i in b) { | ||
return false; | ||
} | ||
if (!areEquivalent(a[i], b[i], problemPath)) { | ||
return false; | ||
} | ||
if (problemPath) { | ||
var problemPathTail = problemPath.pop(); | ||
if (problemPathTail !== name) { | ||
if (problemPathTail !== i) { | ||
throw new Error("" + problemPathTail); | ||
@@ -152,34 +108,80 @@ } | ||
if (!problemPath) { | ||
return false; | ||
} | ||
function objectsAreEquivalent(a, b, problemPath) { | ||
isObject.assert(a); | ||
if (!isObject.check(b)) { | ||
return false; | ||
} | ||
// Since aNameCount !== bNameCount, we need to find some name that's | ||
// missing in aNames but present in bNames, or vice-versa. | ||
// Fast path for a common property of AST nodes. | ||
if (a.type !== b.type) { | ||
if (problemPath) { | ||
problemPath.push("type"); | ||
} | ||
return false; | ||
} | ||
var seenNames = Object.create(null); | ||
var aNames = getFieldNames(a); | ||
var aNameCount = aNames.length; | ||
for (i = 0; i < aNameCount; ++i) { | ||
seenNames[aNames[i]] = true; | ||
} | ||
var bNames = getFieldNames(b); | ||
var bNameCount = bNames.length; | ||
for (i = 0; i < bNameCount; ++i) { | ||
name = bNames[i]; | ||
if (aNameCount === bNameCount) { | ||
for (var i = 0; i < aNameCount; ++i) { | ||
var name = aNames[i]; | ||
var aChild = getFieldValue(a, name); | ||
var bChild = getFieldValue(b, name); | ||
if (!hasOwn.call(seenNames, name)) { | ||
problemPath.push(name); | ||
if (problemPath) { | ||
problemPath.push(name); | ||
} | ||
if (!areEquivalent(aChild, bChild, problemPath)) { | ||
return false; | ||
} | ||
if (problemPath) { | ||
var problemPathTail = problemPath.pop(); | ||
if (problemPathTail !== name) { | ||
throw new Error("" + problemPathTail); | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
if (!problemPath) { | ||
return false; | ||
} | ||
delete seenNames[name]; | ||
} | ||
// Since aNameCount !== bNameCount, we need to find some name that's | ||
// missing in aNames but present in bNames, or vice-versa. | ||
for (name in seenNames) { | ||
problemPath.push(name); | ||
break; | ||
} | ||
var seenNames = Object.create(null); | ||
return false; | ||
} | ||
for (i = 0; i < aNameCount; ++i) { | ||
seenNames[aNames[i]] = true; | ||
} | ||
module.exports = astNodesAreEquivalent; | ||
for (i = 0; i < bNameCount; ++i) { | ||
name = bNames[i]; | ||
if (!hasOwn.call(seenNames, name)) { | ||
problemPath.push(name); | ||
return false; | ||
} | ||
delete seenNames[name]; | ||
} | ||
for (name in seenNames) { | ||
problemPath.push(name); | ||
break; | ||
} | ||
return false; | ||
} | ||
return astNodesAreEquivalent; | ||
}; |
@@ -1,474 +0,476 @@ | ||
var types = require("./types"); | ||
var n = types.namedTypes; | ||
var b = types.builders; | ||
var isNumber = types.builtInTypes.number; | ||
var isArray = types.builtInTypes.array; | ||
var Path = require("./path"); | ||
var Scope = require("./scope"); | ||
module.exports = function (fork) { | ||
var types = fork.use(require("./types")); | ||
var n = types.namedTypes; | ||
var b = types.builders; | ||
var isNumber = types.builtInTypes.number; | ||
var isArray = types.builtInTypes.array; | ||
var Path = fork.use(require("./path")); | ||
var Scope = fork.use(require("./scope")); | ||
function NodePath(value, parentPath, name) { | ||
if (!(this instanceof NodePath)) { | ||
throw new Error("NodePath constructor cannot be invoked without 'new'"); | ||
function NodePath(value, parentPath, name) { | ||
if (!(this instanceof NodePath)) { | ||
throw new Error("NodePath constructor cannot be invoked without 'new'"); | ||
} | ||
Path.call(this, value, parentPath, name); | ||
} | ||
Path.call(this, value, parentPath, name); | ||
} | ||
var NPp = NodePath.prototype = Object.create(Path.prototype, { | ||
constructor: { | ||
value: NodePath, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
var NPp = NodePath.prototype = Object.create(Path.prototype, { | ||
constructor: { | ||
value: NodePath, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
Object.defineProperties(NPp, { | ||
node: { | ||
get: function() { | ||
Object.defineProperty(this, "node", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeNode() | ||
}); | ||
Object.defineProperties(NPp, { | ||
node: { | ||
get: function () { | ||
Object.defineProperty(this, "node", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeNode() | ||
}); | ||
return this.node; | ||
} | ||
}, | ||
return this.node; | ||
} | ||
}, | ||
parent: { | ||
get: function() { | ||
Object.defineProperty(this, "parent", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeParent() | ||
}); | ||
parent: { | ||
get: function () { | ||
Object.defineProperty(this, "parent", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeParent() | ||
}); | ||
return this.parent; | ||
} | ||
}, | ||
return this.parent; | ||
} | ||
}, | ||
scope: { | ||
get: function() { | ||
Object.defineProperty(this, "scope", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeScope() | ||
}); | ||
scope: { | ||
get: function () { | ||
Object.defineProperty(this, "scope", { | ||
configurable: true, // Enable deletion. | ||
value: this._computeScope() | ||
}); | ||
return this.scope; | ||
return this.scope; | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
NPp.replace = function() { | ||
delete this.node; | ||
delete this.parent; | ||
delete this.scope; | ||
return Path.prototype.replace.apply(this, arguments); | ||
}; | ||
NPp.replace = function () { | ||
delete this.node; | ||
delete this.parent; | ||
delete this.scope; | ||
return Path.prototype.replace.apply(this, arguments); | ||
}; | ||
NPp.prune = function() { | ||
var remainingNodePath = this.parent; | ||
NPp.prune = function () { | ||
var remainingNodePath = this.parent; | ||
this.replace(); | ||
this.replace(); | ||
return cleanUpNodesAfterPrune(remainingNodePath); | ||
}; | ||
return cleanUpNodesAfterPrune(remainingNodePath); | ||
}; | ||
// The value of the first ancestor Path whose value is a Node. | ||
NPp._computeNode = function() { | ||
var value = this.value; | ||
if (n.Node.check(value)) { | ||
return value; | ||
} | ||
// The value of the first ancestor Path whose value is a Node. | ||
NPp._computeNode = function () { | ||
var value = this.value; | ||
if (n.Node.check(value)) { | ||
return value; | ||
} | ||
var pp = this.parentPath; | ||
return pp && pp.node || null; | ||
}; | ||
var pp = this.parentPath; | ||
return pp && pp.node || null; | ||
}; | ||
// The first ancestor Path whose value is a Node distinct from this.node. | ||
NPp._computeParent = function() { | ||
var value = this.value; | ||
var pp = this.parentPath; | ||
// The first ancestor Path whose value is a Node distinct from this.node. | ||
NPp._computeParent = function () { | ||
var value = this.value; | ||
var pp = this.parentPath; | ||
if (!n.Node.check(value)) { | ||
while (pp && !n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
if (!n.Node.check(value)) { | ||
while (pp && !n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
} | ||
if (pp) { | ||
pp = pp.parentPath; | ||
} | ||
} | ||
if (pp) { | ||
while (pp && !n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
} | ||
} | ||
while (pp && !n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
} | ||
return pp || null; | ||
}; | ||
return pp || null; | ||
}; | ||
// The closest enclosing scope that governs this node. | ||
NPp._computeScope = function () { | ||
var value = this.value; | ||
var pp = this.parentPath; | ||
var scope = pp && pp.scope; | ||
// The closest enclosing scope that governs this node. | ||
NPp._computeScope = function() { | ||
var value = this.value; | ||
var pp = this.parentPath; | ||
var scope = pp && pp.scope; | ||
if (n.Node.check(value) && | ||
Scope.isEstablishedBy(value)) { | ||
scope = new Scope(this, scope); | ||
} | ||
if (n.Node.check(value) && | ||
Scope.isEstablishedBy(value)) { | ||
scope = new Scope(this, scope); | ||
} | ||
return scope || null; | ||
}; | ||
return scope || null; | ||
}; | ||
NPp.getValueProperty = function (name) { | ||
return types.getFieldValue(this.value, name); | ||
}; | ||
NPp.getValueProperty = function(name) { | ||
return types.getFieldValue(this.value, name); | ||
}; | ||
/** | ||
* Determine whether this.node needs to be wrapped in parentheses in order | ||
* for a parser to reproduce the same local AST structure. | ||
* | ||
* For instance, in the expression `(1 + 2) * 3`, the BinaryExpression | ||
* whose operator is "+" needs parentheses, because `1 + 2 * 3` would | ||
* parse differently. | ||
* | ||
* If assumeExpressionContext === true, we don't worry about edge cases | ||
* like an anonymous FunctionExpression appearing lexically first in its | ||
* enclosing statement and thus needing parentheses to avoid being parsed | ||
* as a FunctionDeclaration with a missing name. | ||
*/ | ||
NPp.needsParens = function (assumeExpressionContext) { | ||
var pp = this.parentPath; | ||
if (!pp) { | ||
return false; | ||
} | ||
/** | ||
* Determine whether this.node needs to be wrapped in parentheses in order | ||
* for a parser to reproduce the same local AST structure. | ||
* | ||
* For instance, in the expression `(1 + 2) * 3`, the BinaryExpression | ||
* whose operator is "+" needs parentheses, because `1 + 2 * 3` would | ||
* parse differently. | ||
* | ||
* If assumeExpressionContext === true, we don't worry about edge cases | ||
* like an anonymous FunctionExpression appearing lexically first in its | ||
* enclosing statement and thus needing parentheses to avoid being parsed | ||
* as a FunctionDeclaration with a missing name. | ||
*/ | ||
NPp.needsParens = function(assumeExpressionContext) { | ||
var pp = this.parentPath; | ||
if (!pp) { | ||
return false; | ||
} | ||
var node = this.value; | ||
var node = this.value; | ||
// Only expressions need parentheses. | ||
if (!n.Expression.check(node)) { | ||
return false; | ||
} | ||
// Only expressions need parentheses. | ||
if (!n.Expression.check(node)) { | ||
return false; | ||
} | ||
// Identifiers never need parentheses. | ||
if (node.type === "Identifier") { | ||
return false; | ||
} | ||
while (!n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
if (!pp) { | ||
// Identifiers never need parentheses. | ||
if (node.type === "Identifier") { | ||
return false; | ||
} | ||
} | ||
var parent = pp.value; | ||
while (!n.Node.check(pp.value)) { | ||
pp = pp.parentPath; | ||
if (!pp) { | ||
return false; | ||
} | ||
} | ||
switch (node.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
return parent.type === "MemberExpression" | ||
&& this.name === "object" | ||
&& parent.object === node; | ||
var parent = pp.value; | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
switch (parent.type) { | ||
case "CallExpression": | ||
return this.name === "callee" | ||
&& parent.callee === node; | ||
switch (node.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
return parent.type === "MemberExpression" | ||
&& this.name === "object" | ||
&& parent.object === node; | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
return true; | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
switch (parent.type) { | ||
case "CallExpression": | ||
return this.name === "callee" | ||
&& parent.callee === node; | ||
case "MemberExpression": | ||
return this.name === "object" | ||
&& parent.object === node; | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
return true; | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
var po = parent.operator; | ||
var pp = PRECEDENCE[po]; | ||
var no = node.operator; | ||
var np = PRECEDENCE[no]; | ||
case "MemberExpression": | ||
return this.name === "object" | ||
&& parent.object === node; | ||
if (pp > np) { | ||
return true; | ||
} | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
var po = parent.operator; | ||
var pp = PRECEDENCE[po]; | ||
var no = node.operator; | ||
var np = PRECEDENCE[no]; | ||
if (pp === np && this.name === "right") { | ||
if (parent.right !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (pp > np) { | ||
return true; | ||
} | ||
if (pp === np && this.name === "right") { | ||
if (parent.right !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
return true; | ||
} | ||
default: | ||
return false; | ||
} | ||
return true; | ||
} | ||
default: | ||
return false; | ||
} | ||
case "SequenceExpression": | ||
switch (parent.type) { | ||
case "ForStatement": | ||
// Although parentheses wouldn't hurt around sequence | ||
// expressions in the head of for loops, traditional style | ||
// dictates that e.g. i++, j++ should not be wrapped with | ||
// parentheses. | ||
return false; | ||
case "SequenceExpression": | ||
switch (parent.type) { | ||
case "ForStatement": | ||
// Although parentheses wouldn't hurt around sequence | ||
// expressions in the head of for loops, traditional style | ||
// dictates that e.g. i++, j++ should not be wrapped with | ||
// parentheses. | ||
return false; | ||
case "ExpressionStatement": | ||
return this.name !== "expression"; | ||
case "ExpressionStatement": | ||
return this.name !== "expression"; | ||
default: | ||
// Otherwise err on the side of overparenthesization, adding | ||
// explicit exceptions above if this proves overzealous. | ||
return true; | ||
} | ||
default: | ||
// Otherwise err on the side of overparenthesization, adding | ||
// explicit exceptions above if this proves overzealous. | ||
return true; | ||
} | ||
case "YieldExpression": | ||
switch (parent.type) { | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "CallExpression": | ||
case "MemberExpression": | ||
case "NewExpression": | ||
case "ConditionalExpression": | ||
case "YieldExpression": | ||
return true; | ||
case "YieldExpression": | ||
switch (parent.type) { | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "CallExpression": | ||
case "MemberExpression": | ||
case "NewExpression": | ||
case "ConditionalExpression": | ||
case "YieldExpression": | ||
return true; | ||
default: | ||
return false; | ||
} | ||
default: | ||
return false; | ||
} | ||
case "Literal": | ||
return parent.type === "MemberExpression" | ||
&& isNumber.check(node.value) | ||
&& this.name === "object" | ||
&& parent.object === node; | ||
case "Literal": | ||
return parent.type === "MemberExpression" | ||
&& isNumber.check(node.value) | ||
&& this.name === "object" | ||
&& parent.object === node; | ||
case "AssignmentExpression": | ||
case "ConditionalExpression": | ||
switch (parent.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return true; | ||
case "AssignmentExpression": | ||
case "ConditionalExpression": | ||
switch (parent.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return true; | ||
case "CallExpression": | ||
return this.name === "callee" | ||
&& parent.callee === node; | ||
case "CallExpression": | ||
return this.name === "callee" | ||
&& parent.callee === node; | ||
case "ConditionalExpression": | ||
return this.name === "test" | ||
&& parent.test === node; | ||
case "ConditionalExpression": | ||
return this.name === "test" | ||
&& parent.test === node; | ||
case "MemberExpression": | ||
return this.name === "object" | ||
&& parent.object === node; | ||
case "MemberExpression": | ||
return this.name === "object" | ||
&& parent.object === node; | ||
default: | ||
return false; | ||
} | ||
default: | ||
return false; | ||
default: | ||
if (parent.type === "NewExpression" && | ||
this.name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
} | ||
default: | ||
if (parent.type === "NewExpression" && | ||
this.name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
} | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) | ||
return true; | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) | ||
return true; | ||
return false; | ||
}; | ||
return false; | ||
}; | ||
function isBinary(node) { | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
} | ||
function isBinary(node) { | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
} | ||
function isUnaryLike(node) { | ||
return n.UnaryExpression.check(node) | ||
// I considered making SpreadElement and SpreadProperty subtypes | ||
// of UnaryExpression, but they're not really Expression nodes. | ||
|| (n.SpreadElement && n.SpreadElement.check(node)) | ||
|| (n.SpreadProperty && n.SpreadProperty.check(node)); | ||
} | ||
function isUnaryLike(node) { | ||
return n.UnaryExpression.check(node) | ||
// I considered making SpreadElement and SpreadProperty subtypes | ||
// of UnaryExpression, but they're not really Expression nodes. | ||
|| (n.SpreadElement && n.SpreadElement.check(node)) | ||
|| (n.SpreadProperty && n.SpreadProperty.check(node)); | ||
} | ||
var PRECEDENCE = {}; | ||
[["||"], | ||
["&&"], | ||
["|"], | ||
["^"], | ||
["&"], | ||
["==", "===", "!=", "!=="], | ||
["<", ">", "<=", ">=", "in", "instanceof"], | ||
[">>", "<<", ">>>"], | ||
["+", "-"], | ||
["*", "/", "%"] | ||
].forEach(function(tier, i) { | ||
tier.forEach(function(op) { | ||
PRECEDENCE[op] = i; | ||
var PRECEDENCE = {}; | ||
[["||"], | ||
["&&"], | ||
["|"], | ||
["^"], | ||
["&"], | ||
["==", "===", "!=", "!=="], | ||
["<", ">", "<=", ">=", "in", "instanceof"], | ||
[">>", "<<", ">>>"], | ||
["+", "-"], | ||
["*", "/", "%"] | ||
].forEach(function (tier, i) { | ||
tier.forEach(function (op) { | ||
PRECEDENCE[op] = i; | ||
}); | ||
}); | ||
}); | ||
function containsCallExpression(node) { | ||
if (n.CallExpression.check(node)) { | ||
return true; | ||
} | ||
function containsCallExpression(node) { | ||
if (n.CallExpression.check(node)) { | ||
return true; | ||
} | ||
if (isArray.check(node)) { | ||
return node.some(containsCallExpression); | ||
} | ||
if (isArray.check(node)) { | ||
return node.some(containsCallExpression); | ||
} | ||
if (n.Node.check(node)) { | ||
return types.someField(node, function(name, child) { | ||
return containsCallExpression(child); | ||
}); | ||
if (n.Node.check(node)) { | ||
return types.someField(node, function (name, child) { | ||
return containsCallExpression(child); | ||
}); | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
NPp.canBeFirstInStatement = function () { | ||
var node = this.node; | ||
return !n.FunctionExpression.check(node) | ||
&& !n.ObjectExpression.check(node); | ||
}; | ||
NPp.canBeFirstInStatement = function() { | ||
var node = this.node; | ||
return !n.FunctionExpression.check(node) | ||
&& !n.ObjectExpression.check(node); | ||
}; | ||
NPp.firstInStatement = function () { | ||
return firstInStatement(this); | ||
}; | ||
NPp.firstInStatement = function() { | ||
return firstInStatement(this); | ||
}; | ||
function firstInStatement(path) { | ||
for (var node, parent; path.parent; path = path.parent) { | ||
node = path.node; | ||
parent = path.parent.node; | ||
function firstInStatement(path) { | ||
for (var node, parent; path.parent; path = path.parent) { | ||
node = path.node; | ||
parent = path.parent.node; | ||
if (n.BlockStatement.check(parent) && | ||
path.parent.name === "body" && | ||
path.name === 0) { | ||
if (parent.body[0] !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
return true; | ||
} | ||
if (n.BlockStatement.check(parent) && | ||
path.parent.name === "body" && | ||
path.name === 0) { | ||
if (parent.body[0] !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.ExpressionStatement.check(parent) && | ||
path.name === "expression") { | ||
if (parent.expression !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
return true; | ||
} | ||
return true; | ||
} | ||
if (n.ExpressionStatement.check(parent) && | ||
path.name === "expression") { | ||
if (parent.expression !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.SequenceExpression.check(parent) && | ||
path.parent.name === "expressions" && | ||
path.name === 0) { | ||
if (parent.expressions[0] !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
return true; | ||
} | ||
if (n.SequenceExpression.check(parent) && | ||
path.parent.name === "expressions" && | ||
path.name === 0) { | ||
if (parent.expressions[0] !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.CallExpression.check(parent) && | ||
path.name === "callee") { | ||
if (parent.callee !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
continue; | ||
} | ||
if (n.CallExpression.check(parent) && | ||
path.name === "callee") { | ||
if (parent.callee !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.MemberExpression.check(parent) && | ||
path.name === "object") { | ||
if (parent.object !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
continue; | ||
} | ||
if (n.MemberExpression.check(parent) && | ||
path.name === "object") { | ||
if (parent.object !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.ConditionalExpression.check(parent) && | ||
path.name === "test") { | ||
if (parent.test !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
continue; | ||
} | ||
if (n.ConditionalExpression.check(parent) && | ||
path.name === "test") { | ||
if (parent.test !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (isBinary(parent) && | ||
path.name === "left") { | ||
if (parent.left !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
continue; | ||
} | ||
if (isBinary(parent) && | ||
path.name === "left") { | ||
if (parent.left !== node) { | ||
throw new Error("Nodes must be equal"); | ||
if (n.UnaryExpression.check(parent) && | ||
!parent.prefix && | ||
path.name === "argument") { | ||
if (parent.argument !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
} | ||
continue; | ||
} | ||
if (n.UnaryExpression.check(parent) && | ||
!parent.prefix && | ||
path.name === "argument") { | ||
if (parent.argument !== node) { | ||
throw new Error("Nodes must be equal"); | ||
} | ||
continue; | ||
return false; | ||
} | ||
return false; | ||
return true; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Pruning certain nodes will result in empty or incomplete nodes, here we clean those nodes up. | ||
*/ | ||
function cleanUpNodesAfterPrune(remainingNodePath) { | ||
if (n.VariableDeclaration.check(remainingNodePath.node)) { | ||
var declarations = remainingNodePath.get('declarations').value; | ||
if (!declarations || declarations.length === 0) { | ||
return remainingNodePath.prune(); | ||
} | ||
} else if (n.ExpressionStatement.check(remainingNodePath.node)) { | ||
if (!remainingNodePath.get('expression').value) { | ||
return remainingNodePath.prune(); | ||
} | ||
} else if (n.IfStatement.check(remainingNodePath.node)) { | ||
cleanUpIfStatementAfterPrune(remainingNodePath); | ||
} | ||
/** | ||
* Pruning certain nodes will result in empty or incomplete nodes, here we clean those nodes up. | ||
*/ | ||
function cleanUpNodesAfterPrune(remainingNodePath) { | ||
if (n.VariableDeclaration.check(remainingNodePath.node)) { | ||
var declarations = remainingNodePath.get('declarations').value; | ||
if (!declarations || declarations.length === 0) { | ||
return remainingNodePath.prune(); | ||
} | ||
} else if (n.ExpressionStatement.check(remainingNodePath.node)) { | ||
if (!remainingNodePath.get('expression').value) { | ||
return remainingNodePath.prune(); | ||
} | ||
} else if (n.IfStatement.check(remainingNodePath.node)) { | ||
cleanUpIfStatementAfterPrune(remainingNodePath); | ||
return remainingNodePath; | ||
} | ||
return remainingNodePath; | ||
} | ||
function cleanUpIfStatementAfterPrune(ifStatement) { | ||
var testExpression = ifStatement.get('test').value; | ||
var alternate = ifStatement.get('alternate').value; | ||
var consequent = ifStatement.get('consequent').value; | ||
function cleanUpIfStatementAfterPrune(ifStatement) { | ||
var testExpression = ifStatement.get('test').value; | ||
var alternate = ifStatement.get('alternate').value; | ||
var consequent = ifStatement.get('consequent').value; | ||
if (!consequent && !alternate) { | ||
var testExpressionStatement = b.expressionStatement(testExpression); | ||
if (!consequent && !alternate) { | ||
var testExpressionStatement = b.expressionStatement(testExpression); | ||
ifStatement.replace(testExpressionStatement); | ||
} else if (!consequent && alternate) { | ||
var negatedTestExpression = b.unaryExpression('!', testExpression, true); | ||
ifStatement.replace(testExpressionStatement); | ||
} else if (!consequent && alternate) { | ||
var negatedTestExpression = b.unaryExpression('!', testExpression, true); | ||
if (n.UnaryExpression.check(testExpression) && testExpression.operator === '!') { | ||
negatedTestExpression = testExpression.argument; | ||
} | ||
if (n.UnaryExpression.check(testExpression) && testExpression.operator === '!') { | ||
negatedTestExpression = testExpression.argument; | ||
ifStatement.get("test").replace(negatedTestExpression); | ||
ifStatement.get("consequent").replace(alternate); | ||
ifStatement.get("alternate").replace(); | ||
} | ||
ifStatement.get("test").replace(negatedTestExpression); | ||
ifStatement.get("consequent").replace(alternate); | ||
ifStatement.get("alternate").replace(); | ||
} | ||
} | ||
module.exports = NodePath; | ||
return NodePath; | ||
}; |
@@ -1,419 +0,422 @@ | ||
var types = require("./types"); | ||
var NodePath = require("./node-path"); | ||
var Printable = types.namedTypes.Printable; | ||
var isArray = types.builtInTypes.array; | ||
var isObject = types.builtInTypes.object; | ||
var isFunction = types.builtInTypes.function; | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
var undefined; | ||
function PathVisitor() { | ||
if (!(this instanceof PathVisitor)) { | ||
throw new Error( | ||
"PathVisitor constructor cannot be invoked without 'new'" | ||
); | ||
} | ||
module.exports = function (fork) { | ||
var types = fork.use(require("./types")); | ||
var NodePath = fork.use(require("./node-path")); | ||
var Printable = types.namedTypes.Printable; | ||
var isArray = types.builtInTypes.array; | ||
var isObject = types.builtInTypes.object; | ||
var isFunction = types.builtInTypes.function; | ||
var undefined; | ||
// Permanent state. | ||
this._reusableContextStack = []; | ||
function PathVisitor() { | ||
if (!(this instanceof PathVisitor)) { | ||
throw new Error( | ||
"PathVisitor constructor cannot be invoked without 'new'" | ||
); | ||
} | ||
this._methodNameTable = computeMethodNameTable(this); | ||
this._shouldVisitComments = | ||
hasOwn.call(this._methodNameTable, "Block") || | ||
hasOwn.call(this._methodNameTable, "Line"); | ||
// Permanent state. | ||
this._reusableContextStack = []; | ||
this.Context = makeContextConstructor(this); | ||
this._methodNameTable = computeMethodNameTable(this); | ||
this._shouldVisitComments = | ||
hasOwn.call(this._methodNameTable, "Block") || | ||
hasOwn.call(this._methodNameTable, "Line"); | ||
// State reset every time PathVisitor.prototype.visit is called. | ||
this._visiting = false; | ||
this._changeReported = false; | ||
} | ||
this.Context = makeContextConstructor(this); | ||
function computeMethodNameTable(visitor) { | ||
var typeNames = Object.create(null); | ||
for (var methodName in visitor) { | ||
if (/^visit[A-Z]/.test(methodName)) { | ||
typeNames[methodName.slice("visit".length)] = true; | ||
} | ||
// State reset every time PathVisitor.prototype.visit is called. | ||
this._visiting = false; | ||
this._changeReported = false; | ||
} | ||
var supertypeTable = types.computeSupertypeLookupTable(typeNames); | ||
var methodNameTable = Object.create(null); | ||
function computeMethodNameTable(visitor) { | ||
var typeNames = Object.create(null); | ||
var typeNames = Object.keys(supertypeTable); | ||
var typeNameCount = typeNames.length; | ||
for (var i = 0; i < typeNameCount; ++i) { | ||
var typeName = typeNames[i]; | ||
methodName = "visit" + supertypeTable[typeName]; | ||
if (isFunction.check(visitor[methodName])) { | ||
methodNameTable[typeName] = methodName; | ||
for (var methodName in visitor) { | ||
if (/^visit[A-Z]/.test(methodName)) { | ||
typeNames[methodName.slice("visit".length)] = true; | ||
} | ||
} | ||
} | ||
return methodNameTable; | ||
} | ||
var supertypeTable = types.computeSupertypeLookupTable(typeNames); | ||
var methodNameTable = Object.create(null); | ||
PathVisitor.fromMethodsObject = function fromMethodsObject(methods) { | ||
if (methods instanceof PathVisitor) { | ||
return methods; | ||
} | ||
var typeNames = Object.keys(supertypeTable); | ||
var typeNameCount = typeNames.length; | ||
for (var i = 0; i < typeNameCount; ++i) { | ||
var typeName = typeNames[i]; | ||
methodName = "visit" + supertypeTable[typeName]; | ||
if (isFunction.check(visitor[methodName])) { | ||
methodNameTable[typeName] = methodName; | ||
} | ||
} | ||
if (!isObject.check(methods)) { | ||
// An empty visitor? | ||
return new PathVisitor; | ||
return methodNameTable; | ||
} | ||
function Visitor() { | ||
if (!(this instanceof Visitor)) { | ||
throw new Error( | ||
"Visitor constructor cannot be invoked without 'new'" | ||
); | ||
PathVisitor.fromMethodsObject = function fromMethodsObject(methods) { | ||
if (methods instanceof PathVisitor) { | ||
return methods; | ||
} | ||
PathVisitor.call(this); | ||
} | ||
var Vp = Visitor.prototype = Object.create(PVp); | ||
Vp.constructor = Visitor; | ||
if (!isObject.check(methods)) { | ||
// An empty visitor? | ||
return new PathVisitor; | ||
} | ||
extend(Vp, methods); | ||
extend(Visitor, PathVisitor); | ||
function Visitor() { | ||
if (!(this instanceof Visitor)) { | ||
throw new Error( | ||
"Visitor constructor cannot be invoked without 'new'" | ||
); | ||
} | ||
PathVisitor.call(this); | ||
} | ||
isFunction.assert(Visitor.fromMethodsObject); | ||
isFunction.assert(Visitor.visit); | ||
var Vp = Visitor.prototype = Object.create(PVp); | ||
Vp.constructor = Visitor; | ||
return new Visitor; | ||
}; | ||
extend(Vp, methods); | ||
extend(Visitor, PathVisitor); | ||
function extend(target, source) { | ||
for (var property in source) { | ||
if (hasOwn.call(source, property)) { | ||
target[property] = source[property]; | ||
isFunction.assert(Visitor.fromMethodsObject); | ||
isFunction.assert(Visitor.visit); | ||
return new Visitor; | ||
}; | ||
function extend(target, source) { | ||
for (var property in source) { | ||
if (hasOwn.call(source, property)) { | ||
target[property] = source[property]; | ||
} | ||
} | ||
return target; | ||
} | ||
return target; | ||
} | ||
PathVisitor.visit = function visit(node, methods) { | ||
return PathVisitor.fromMethodsObject(methods).visit(node); | ||
}; | ||
PathVisitor.visit = function visit(node, methods) { | ||
return PathVisitor.fromMethodsObject(methods).visit(node); | ||
}; | ||
var PVp = PathVisitor.prototype; | ||
var PVp = PathVisitor.prototype; | ||
PVp.visit = function () { | ||
if (this._visiting) { | ||
throw new Error( | ||
"Recursively calling visitor.visit(path) resets visitor state. " + | ||
"Try this.visit(path) or this.traverse(path) instead." | ||
); | ||
} | ||
PVp.visit = function() { | ||
if (this._visiting) { | ||
throw new Error( | ||
"Recursively calling visitor.visit(path) resets visitor state. " + | ||
"Try this.visit(path) or this.traverse(path) instead." | ||
); | ||
} | ||
// Private state that needs to be reset before every traversal. | ||
this._visiting = true; | ||
this._changeReported = false; | ||
this._abortRequested = false; | ||
// Private state that needs to be reset before every traversal. | ||
this._visiting = true; | ||
this._changeReported = false; | ||
this._abortRequested = false; | ||
var argc = arguments.length; | ||
var args = new Array(argc) | ||
for (var i = 0; i < argc; ++i) { | ||
args[i] = arguments[i]; | ||
} | ||
var argc = arguments.length; | ||
var args = new Array(argc) | ||
for (var i = 0; i < argc; ++i) { | ||
args[i] = arguments[i]; | ||
} | ||
if (!(args[0] instanceof NodePath)) { | ||
args[0] = new NodePath({root: args[0]}).get("root"); | ||
} | ||
if (!(args[0] instanceof NodePath)) { | ||
args[0] = new NodePath({ root: args[0] }).get("root"); | ||
} | ||
// Called with the same arguments as .visit. | ||
this.reset.apply(this, args); | ||
// Called with the same arguments as .visit. | ||
this.reset.apply(this, args); | ||
try { | ||
var root = this.visitWithoutReset(args[0]); | ||
var didNotThrow = true; | ||
} finally { | ||
this._visiting = false; | ||
try { | ||
var root = this.visitWithoutReset(args[0]); | ||
var didNotThrow = true; | ||
} finally { | ||
this._visiting = false; | ||
if (!didNotThrow && this._abortRequested) { | ||
// If this.visitWithoutReset threw an exception and | ||
// this._abortRequested was set to true, return the root of | ||
// the AST instead of letting the exception propagate, so that | ||
// client code does not have to provide a try-catch block to | ||
// intercept the AbortRequest exception. Other kinds of | ||
// exceptions will propagate without being intercepted and | ||
// rethrown by a catch block, so their stacks will accurately | ||
// reflect the original throwing context. | ||
return args[0].value; | ||
if (!didNotThrow && this._abortRequested) { | ||
// If this.visitWithoutReset threw an exception and | ||
// this._abortRequested was set to true, return the root of | ||
// the AST instead of letting the exception propagate, so that | ||
// client code does not have to provide a try-catch block to | ||
// intercept the AbortRequest exception. Other kinds of | ||
// exceptions will propagate without being intercepted and | ||
// rethrown by a catch block, so their stacks will accurately | ||
// reflect the original throwing context. | ||
return args[0].value; | ||
} | ||
} | ||
} | ||
return root; | ||
}; | ||
return root; | ||
}; | ||
PVp.AbortRequest = function AbortRequest() {}; | ||
PVp.abort = function() { | ||
var visitor = this; | ||
visitor._abortRequested = true; | ||
var request = new visitor.AbortRequest(); | ||
PVp.AbortRequest = function AbortRequest() {}; | ||
PVp.abort = function () { | ||
var visitor = this; | ||
visitor._abortRequested = true; | ||
var request = new visitor.AbortRequest(); | ||
// If you decide to catch this exception and stop it from propagating, | ||
// make sure to call its cancel method to avoid silencing other | ||
// exceptions that might be thrown later in the traversal. | ||
request.cancel = function() { | ||
visitor._abortRequested = false; | ||
// If you decide to catch this exception and stop it from propagating, | ||
// make sure to call its cancel method to avoid silencing other | ||
// exceptions that might be thrown later in the traversal. | ||
request.cancel = function () { | ||
visitor._abortRequested = false; | ||
}; | ||
throw request; | ||
}; | ||
throw request; | ||
}; | ||
PVp.reset = function (path/*, additional arguments */) { | ||
// Empty stub; may be reassigned or overridden by subclasses. | ||
}; | ||
PVp.reset = function(path/*, additional arguments */) { | ||
// Empty stub; may be reassigned or overridden by subclasses. | ||
}; | ||
PVp.visitWithoutReset = function (path) { | ||
if (this instanceof this.Context) { | ||
// Since this.Context.prototype === this, there's a chance we | ||
// might accidentally call context.visitWithoutReset. If that | ||
// happens, re-invoke the method against context.visitor. | ||
return this.visitor.visitWithoutReset(path); | ||
} | ||
PVp.visitWithoutReset = function(path) { | ||
if (this instanceof this.Context) { | ||
// Since this.Context.prototype === this, there's a chance we | ||
// might accidentally call context.visitWithoutReset. If that | ||
// happens, re-invoke the method against context.visitor. | ||
return this.visitor.visitWithoutReset(path); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
var value = path.value; | ||
var value = path.value; | ||
var methodName = value && | ||
typeof value === "object" && | ||
typeof value.type === "string" && | ||
this._methodNameTable[value.type]; | ||
var methodName = value && | ||
typeof value === "object" && | ||
typeof value.type === "string" && | ||
this._methodNameTable[value.type]; | ||
if (methodName) { | ||
var context = this.acquireContext(path); | ||
try { | ||
return context.invokeVisitorMethod(methodName); | ||
} finally { | ||
this.releaseContext(context); | ||
} | ||
if (methodName) { | ||
var context = this.acquireContext(path); | ||
try { | ||
return context.invokeVisitorMethod(methodName); | ||
} finally { | ||
this.releaseContext(context); | ||
} else { | ||
// If there was no visitor method to call, visit the children of | ||
// this node generically. | ||
return visitChildren(path, this); | ||
} | ||
}; | ||
} else { | ||
// If there was no visitor method to call, visit the children of | ||
// this node generically. | ||
return visitChildren(path, this); | ||
} | ||
}; | ||
function visitChildren(path, visitor) { | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(visitor instanceof PathVisitor)) { | ||
throw new Error(""); | ||
} | ||
function visitChildren(path, visitor) { | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(visitor instanceof PathVisitor)) { | ||
throw new Error(""); | ||
} | ||
var value = path.value; | ||
var value = path.value; | ||
if (isArray.check(value)) { | ||
path.each(visitor.visitWithoutReset, visitor); | ||
} else if (!isObject.check(value)) { | ||
// No children to visit. | ||
} else { | ||
var childNames = types.getFieldNames(value); | ||
if (isArray.check(value)) { | ||
path.each(visitor.visitWithoutReset, visitor); | ||
} else if (!isObject.check(value)) { | ||
// No children to visit. | ||
} else { | ||
var childNames = types.getFieldNames(value); | ||
// The .comments field of the Node type is hidden, so we only | ||
// visit it if the visitor defines visitBlock or visitLine, and | ||
// value.comments is defined. | ||
if (visitor._shouldVisitComments && | ||
value.comments && | ||
childNames.indexOf("comments") < 0) { | ||
childNames.push("comments"); | ||
} | ||
// The .comments field of the Node type is hidden, so we only | ||
// visit it if the visitor defines visitBlock or visitLine, and | ||
// value.comments is defined. | ||
if (visitor._shouldVisitComments && | ||
value.comments && | ||
childNames.indexOf("comments") < 0) { | ||
childNames.push("comments"); | ||
} | ||
var childCount = childNames.length; | ||
var childPaths = []; | ||
var childCount = childNames.length; | ||
var childPaths = []; | ||
for (var i = 0; i < childCount; ++i) { | ||
var childName = childNames[i]; | ||
if (!hasOwn.call(value, childName)) { | ||
value[childName] = types.getFieldValue(value, childName); | ||
} | ||
childPaths.push(path.get(childName)); | ||
} | ||
for (var i = 0; i < childCount; ++i) { | ||
var childName = childNames[i]; | ||
if (!hasOwn.call(value, childName)) { | ||
value[childName] = types.getFieldValue(value, childName); | ||
for (var i = 0; i < childCount; ++i) { | ||
visitor.visitWithoutReset(childPaths[i]); | ||
} | ||
childPaths.push(path.get(childName)); | ||
} | ||
for (var i = 0; i < childCount; ++i) { | ||
visitor.visitWithoutReset(childPaths[i]); | ||
} | ||
return path.value; | ||
} | ||
return path.value; | ||
} | ||
PVp.acquireContext = function (path) { | ||
if (this._reusableContextStack.length === 0) { | ||
return new this.Context(path); | ||
} | ||
return this._reusableContextStack.pop().reset(path); | ||
}; | ||
PVp.acquireContext = function(path) { | ||
if (this._reusableContextStack.length === 0) { | ||
return new this.Context(path); | ||
} | ||
return this._reusableContextStack.pop().reset(path); | ||
}; | ||
PVp.releaseContext = function (context) { | ||
if (!(context instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
this._reusableContextStack.push(context); | ||
context.currentPath = null; | ||
}; | ||
PVp.releaseContext = function(context) { | ||
if (!(context instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
this._reusableContextStack.push(context); | ||
context.currentPath = null; | ||
}; | ||
PVp.reportChanged = function () { | ||
this._changeReported = true; | ||
}; | ||
PVp.reportChanged = function() { | ||
this._changeReported = true; | ||
}; | ||
PVp.wasChangeReported = function () { | ||
return this._changeReported; | ||
}; | ||
PVp.wasChangeReported = function() { | ||
return this._changeReported; | ||
}; | ||
function makeContextConstructor(visitor) { | ||
function Context(path) { | ||
if (!(this instanceof Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(this instanceof PathVisitor)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
function makeContextConstructor(visitor) { | ||
function Context(path) { | ||
if (!(this instanceof Context)) { | ||
throw new Error(""); | ||
Object.defineProperty(this, "visitor", { | ||
value: visitor, | ||
writable: false, | ||
enumerable: true, | ||
configurable: false | ||
}); | ||
this.currentPath = path; | ||
this.needToCallTraverse = true; | ||
Object.seal(this); | ||
} | ||
if (!(this instanceof PathVisitor)) { | ||
if (!(visitor instanceof PathVisitor)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
Object.defineProperty(this, "visitor", { | ||
value: visitor, | ||
writable: false, | ||
enumerable: true, | ||
configurable: false | ||
}); | ||
// Note that the visitor object is the prototype of Context.prototype, | ||
// so all visitor methods are inherited by context objects. | ||
var Cp = Context.prototype = Object.create(visitor); | ||
this.currentPath = path; | ||
this.needToCallTraverse = true; | ||
Cp.constructor = Context; | ||
extend(Cp, sharedContextProtoMethods); | ||
Object.seal(this); | ||
return Context; | ||
} | ||
if (!(visitor instanceof PathVisitor)) { | ||
throw new Error(""); | ||
} | ||
// Note that the visitor object is the prototype of Context.prototype, | ||
// so all visitor methods are inherited by context objects. | ||
var Cp = Context.prototype = Object.create(visitor); | ||
Cp.constructor = Context; | ||
extend(Cp, sharedContextProtoMethods); | ||
return Context; | ||
} | ||
// Every PathVisitor has a different this.Context constructor and | ||
// this.Context.prototype object, but those prototypes can all use the | ||
// same reset, invokeVisitorMethod, and traverse function objects. | ||
var sharedContextProtoMethods = Object.create(null); | ||
var sharedContextProtoMethods = Object.create(null); | ||
sharedContextProtoMethods.reset = | ||
function reset(path) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
sharedContextProtoMethods.reset = | ||
function reset(path) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
this.currentPath = path; | ||
this.needToCallTraverse = true; | ||
this.currentPath = path; | ||
this.needToCallTraverse = true; | ||
return this; | ||
}; | ||
return this; | ||
}; | ||
sharedContextProtoMethods.invokeVisitorMethod = | ||
function invokeVisitorMethod(methodName) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
sharedContextProtoMethods.invokeVisitorMethod = | ||
function invokeVisitorMethod(methodName) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
var result = this.visitor[methodName].call(this, this.currentPath); | ||
var result = this.visitor[methodName].call(this, this.currentPath); | ||
if (result === false) { | ||
// Visitor methods return false to indicate that they have handled | ||
// their own traversal needs, and we should not complain if | ||
// this.needToCallTraverse is still true. | ||
this.needToCallTraverse = false; | ||
if (result === false) { | ||
// Visitor methods return false to indicate that they have handled | ||
// their own traversal needs, and we should not complain if | ||
// this.needToCallTraverse is still true. | ||
this.needToCallTraverse = false; | ||
} else if (result !== undefined) { | ||
// Any other non-undefined value returned from the visitor method | ||
// is interpreted as a replacement value. | ||
this.currentPath = this.currentPath.replace(result)[0]; | ||
} else if (result !== undefined) { | ||
// Any other non-undefined value returned from the visitor method | ||
// is interpreted as a replacement value. | ||
this.currentPath = this.currentPath.replace(result)[0]; | ||
if (this.needToCallTraverse) { | ||
// If this.traverse still hasn't been called, visit the | ||
// children of the replacement node. | ||
this.traverse(this.currentPath); | ||
} | ||
} | ||
if (this.needToCallTraverse) { | ||
// If this.traverse still hasn't been called, visit the | ||
// children of the replacement node. | ||
this.traverse(this.currentPath); | ||
} | ||
} | ||
if (this.needToCallTraverse !== false) { | ||
throw new Error( | ||
"Must either call this.traverse or return false in " + methodName | ||
); | ||
} | ||
if (this.needToCallTraverse !== false) { | ||
throw new Error( | ||
"Must either call this.traverse or return false in " + methodName | ||
); | ||
} | ||
var path = this.currentPath; | ||
return path && path.value; | ||
}; | ||
var path = this.currentPath; | ||
return path && path.value; | ||
}; | ||
sharedContextProtoMethods.traverse = | ||
function traverse(path, newVisitor) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
sharedContextProtoMethods.traverse = | ||
function traverse(path, newVisitor) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
this.needToCallTraverse = false; | ||
this.needToCallTraverse = false; | ||
return visitChildren(path, PathVisitor.fromMethodsObject( | ||
newVisitor || this.visitor | ||
)); | ||
}; | ||
return visitChildren(path, PathVisitor.fromMethodsObject( | ||
newVisitor || this.visitor | ||
)); | ||
}; | ||
sharedContextProtoMethods.visit = | ||
function visit(path, newVisitor) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
sharedContextProtoMethods.visit = | ||
function visit(path, newVisitor) { | ||
if (!(this instanceof this.Context)) { | ||
throw new Error(""); | ||
} | ||
if (!(path instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
if (!(this.currentPath instanceof NodePath)) { | ||
throw new Error(""); | ||
} | ||
this.needToCallTraverse = false; | ||
this.needToCallTraverse = false; | ||
return PathVisitor.fromMethodsObject( | ||
newVisitor || this.visitor | ||
).visitWithoutReset(path); | ||
}; | ||
return PathVisitor.fromMethodsObject( | ||
newVisitor || this.visitor | ||
).visitWithoutReset(path); | ||
}; | ||
sharedContextProtoMethods.reportChanged = function reportChanged() { | ||
this.visitor.reportChanged(); | ||
}; | ||
sharedContextProtoMethods.reportChanged = function reportChanged() { | ||
this.visitor.reportChanged(); | ||
}; | ||
sharedContextProtoMethods.abort = function abort() { | ||
this.needToCallTraverse = false; | ||
this.visitor.abort(); | ||
sharedContextProtoMethods.abort = function abort() { | ||
this.needToCallTraverse = false; | ||
this.visitor.abort(); | ||
}; | ||
return PathVisitor; | ||
}; | ||
module.exports = PathVisitor; |
567
lib/path.js
@@ -1,366 +0,369 @@ | ||
var Op = Object.prototype; | ||
var hasOwn = Op.hasOwnProperty; | ||
var types = require("./types"); | ||
var isArray = types.builtInTypes.array; | ||
var isNumber = types.builtInTypes.number; | ||
var Ap = Array.prototype; | ||
var slice = Ap.slice; | ||
var map = Ap.map; | ||
var Op = Object.prototype; | ||
var hasOwn = Op.hasOwnProperty; | ||
function Path(value, parentPath, name) { | ||
if (!(this instanceof Path)) { | ||
throw new Error("Path constructor cannot be invoked without 'new'"); | ||
} | ||
module.exports = function (fork) { | ||
var types = fork.use(require("./types")); | ||
var isArray = types.builtInTypes.array; | ||
var isNumber = types.builtInTypes.number; | ||
if (parentPath) { | ||
if (!(parentPath instanceof Path)) { | ||
throw new Error(""); | ||
function Path(value, parentPath, name) { | ||
if (!(this instanceof Path)) { | ||
throw new Error("Path constructor cannot be invoked without 'new'"); | ||
} | ||
} else { | ||
parentPath = null; | ||
name = null; | ||
} | ||
// The value encapsulated by this Path, generally equal to | ||
// parentPath.value[name] if we have a parentPath. | ||
this.value = value; | ||
if (parentPath) { | ||
if (!(parentPath instanceof Path)) { | ||
throw new Error(""); | ||
} | ||
} else { | ||
parentPath = null; | ||
name = null; | ||
} | ||
// The immediate parent Path of this Path. | ||
this.parentPath = parentPath; | ||
// The value encapsulated by this Path, generally equal to | ||
// parentPath.value[name] if we have a parentPath. | ||
this.value = value; | ||
// The name of the property of parentPath.value through which this | ||
// Path's value was reached. | ||
this.name = name; | ||
// The immediate parent Path of this Path. | ||
this.parentPath = parentPath; | ||
// Calling path.get("child") multiple times always returns the same | ||
// child Path object, for both performance and consistency reasons. | ||
this.__childCache = null; | ||
} | ||
// The name of the property of parentPath.value through which this | ||
// Path's value was reached. | ||
this.name = name; | ||
var Pp = Path.prototype; | ||
// Calling path.get("child") multiple times always returns the same | ||
// child Path object, for both performance and consistency reasons. | ||
this.__childCache = null; | ||
} | ||
function getChildCache(path) { | ||
// Lazily create the child cache. This also cheapens cache | ||
// invalidation, since you can just reset path.__childCache to null. | ||
return path.__childCache || (path.__childCache = Object.create(null)); | ||
} | ||
var Pp = Path.prototype; | ||
function getChildPath(path, name) { | ||
var cache = getChildCache(path); | ||
var actualChildValue = path.getValueProperty(name); | ||
var childPath = cache[name]; | ||
if (!hasOwn.call(cache, name) || | ||
// Ensure consistency between cache and reality. | ||
childPath.value !== actualChildValue) { | ||
childPath = cache[name] = new path.constructor( | ||
actualChildValue, path, name | ||
); | ||
function getChildCache(path) { | ||
// Lazily create the child cache. This also cheapens cache | ||
// invalidation, since you can just reset path.__childCache to null. | ||
return path.__childCache || (path.__childCache = Object.create(null)); | ||
} | ||
return childPath; | ||
} | ||
function getChildPath(path, name) { | ||
var cache = getChildCache(path); | ||
var actualChildValue = path.getValueProperty(name); | ||
var childPath = cache[name]; | ||
if (!hasOwn.call(cache, name) || | ||
// Ensure consistency between cache and reality. | ||
childPath.value !== actualChildValue) { | ||
childPath = cache[name] = new path.constructor( | ||
actualChildValue, path, name | ||
); | ||
} | ||
return childPath; | ||
} | ||
// This method is designed to be overridden by subclasses that need to | ||
// handle missing properties, etc. | ||
Pp.getValueProperty = function getValueProperty(name) { | ||
return this.value[name]; | ||
}; | ||
Pp.getValueProperty = function getValueProperty(name) { | ||
return this.value[name]; | ||
}; | ||
Pp.get = function get(name) { | ||
var path = this; | ||
var names = arguments; | ||
var count = names.length; | ||
Pp.get = function get(name) { | ||
var path = this; | ||
var names = arguments; | ||
var count = names.length; | ||
for (var i = 0; i < count; ++i) { | ||
path = getChildPath(path, names[i]); | ||
} | ||
for (var i = 0; i < count; ++i) { | ||
path = getChildPath(path, names[i]); | ||
} | ||
return path; | ||
}; | ||
return path; | ||
}; | ||
Pp.each = function each(callback, context) { | ||
var childPaths = []; | ||
var len = this.value.length; | ||
var i = 0; | ||
Pp.each = function each(callback, context) { | ||
var childPaths = []; | ||
var len = this.value.length; | ||
var i = 0; | ||
// Collect all the original child paths before invoking the callback. | ||
for (var i = 0; i < len; ++i) { | ||
if (hasOwn.call(this.value, i)) { | ||
childPaths[i] = this.get(i); | ||
// Collect all the original child paths before invoking the callback. | ||
for (var i = 0; i < len; ++i) { | ||
if (hasOwn.call(this.value, i)) { | ||
childPaths[i] = this.get(i); | ||
} | ||
} | ||
} | ||
// Invoke the callback on just the original child paths, regardless of | ||
// any modifications made to the array by the callback. I chose these | ||
// semantics over cleverly invoking the callback on new elements because | ||
// this way is much easier to reason about. | ||
context = context || this; | ||
for (i = 0; i < len; ++i) { | ||
if (hasOwn.call(childPaths, i)) { | ||
callback.call(context, childPaths[i]); | ||
// Invoke the callback on just the original child paths, regardless of | ||
// any modifications made to the array by the callback. I chose these | ||
// semantics over cleverly invoking the callback on new elements because | ||
// this way is much easier to reason about. | ||
context = context || this; | ||
for (i = 0; i < len; ++i) { | ||
if (hasOwn.call(childPaths, i)) { | ||
callback.call(context, childPaths[i]); | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
Pp.map = function map(callback, context) { | ||
var result = []; | ||
Pp.map = function map(callback, context) { | ||
var result = []; | ||
this.each(function(childPath) { | ||
result.push(callback.call(this, childPath)); | ||
}, context); | ||
this.each(function (childPath) { | ||
result.push(callback.call(this, childPath)); | ||
}, context); | ||
return result; | ||
}; | ||
return result; | ||
}; | ||
Pp.filter = function filter(callback, context) { | ||
var result = []; | ||
Pp.filter = function filter(callback, context) { | ||
var result = []; | ||
this.each(function(childPath) { | ||
if (callback.call(this, childPath)) { | ||
result.push(childPath); | ||
} | ||
}, context); | ||
this.each(function (childPath) { | ||
if (callback.call(this, childPath)) { | ||
result.push(childPath); | ||
} | ||
}, context); | ||
return result; | ||
}; | ||
return result; | ||
}; | ||
function emptyMoves() {} | ||
function getMoves(path, offset, start, end) { | ||
isArray.assert(path.value); | ||
function emptyMoves() {} | ||
function getMoves(path, offset, start, end) { | ||
isArray.assert(path.value); | ||
if (offset === 0) { | ||
return emptyMoves; | ||
} | ||
if (offset === 0) { | ||
return emptyMoves; | ||
} | ||
var length = path.value.length; | ||
if (length < 1) { | ||
return emptyMoves; | ||
} | ||
var length = path.value.length; | ||
if (length < 1) { | ||
return emptyMoves; | ||
} | ||
var argc = arguments.length; | ||
if (argc === 2) { | ||
start = 0; | ||
end = length; | ||
} else if (argc === 3) { | ||
start = Math.max(start, 0); | ||
end = length; | ||
} else { | ||
start = Math.max(start, 0); | ||
end = Math.min(end, length); | ||
} | ||
var argc = arguments.length; | ||
if (argc === 2) { | ||
start = 0; | ||
end = length; | ||
} else if (argc === 3) { | ||
start = Math.max(start, 0); | ||
end = length; | ||
} else { | ||
start = Math.max(start, 0); | ||
end = Math.min(end, length); | ||
} | ||
isNumber.assert(start); | ||
isNumber.assert(end); | ||
isNumber.assert(start); | ||
isNumber.assert(end); | ||
var moves = Object.create(null); | ||
var cache = getChildCache(path); | ||
var moves = Object.create(null); | ||
var cache = getChildCache(path); | ||
for (var i = start; i < end; ++i) { | ||
if (hasOwn.call(path.value, i)) { | ||
var childPath = path.get(i); | ||
if (childPath.name !== i) { | ||
throw new Error(""); | ||
for (var i = start; i < end; ++i) { | ||
if (hasOwn.call(path.value, i)) { | ||
var childPath = path.get(i); | ||
if (childPath.name !== i) { | ||
throw new Error(""); | ||
} | ||
var newIndex = i + offset; | ||
childPath.name = newIndex; | ||
moves[newIndex] = childPath; | ||
delete cache[i]; | ||
} | ||
var newIndex = i + offset; | ||
childPath.name = newIndex; | ||
moves[newIndex] = childPath; | ||
delete cache[i]; | ||
} | ||
} | ||
delete cache.length; | ||
delete cache.length; | ||
return function() { | ||
for (var newIndex in moves) { | ||
var childPath = moves[newIndex]; | ||
if (childPath.name !== +newIndex) { | ||
throw new Error(""); | ||
return function () { | ||
for (var newIndex in moves) { | ||
var childPath = moves[newIndex]; | ||
if (childPath.name !== +newIndex) { | ||
throw new Error(""); | ||
} | ||
cache[newIndex] = childPath; | ||
path.value[newIndex] = childPath.value; | ||
} | ||
cache[newIndex] = childPath; | ||
path.value[newIndex] = childPath.value; | ||
} | ||
}; | ||
} | ||
Pp.shift = function shift() { | ||
var move = getMoves(this, -1); | ||
var result = this.value.shift(); | ||
move(); | ||
return result; | ||
}; | ||
} | ||
Pp.shift = function shift() { | ||
var move = getMoves(this, -1); | ||
var result = this.value.shift(); | ||
move(); | ||
return result; | ||
}; | ||
Pp.unshift = function unshift(node) { | ||
var move = getMoves(this, arguments.length); | ||
var result = this.value.unshift.apply(this.value, arguments); | ||
move(); | ||
return result; | ||
}; | ||
Pp.unshift = function unshift(node) { | ||
var move = getMoves(this, arguments.length); | ||
var result = this.value.unshift.apply(this.value, arguments); | ||
move(); | ||
return result; | ||
}; | ||
Pp.push = function push(node) { | ||
isArray.assert(this.value); | ||
delete getChildCache(this).length | ||
return this.value.push.apply(this.value, arguments); | ||
}; | ||
Pp.push = function push(node) { | ||
isArray.assert(this.value); | ||
delete getChildCache(this).length | ||
return this.value.push.apply(this.value, arguments); | ||
}; | ||
Pp.pop = function pop() { | ||
isArray.assert(this.value); | ||
var cache = getChildCache(this); | ||
delete cache[this.value.length - 1]; | ||
delete cache.length; | ||
return this.value.pop(); | ||
}; | ||
Pp.pop = function pop() { | ||
isArray.assert(this.value); | ||
var cache = getChildCache(this); | ||
delete cache[this.value.length - 1]; | ||
delete cache.length; | ||
return this.value.pop(); | ||
}; | ||
Pp.insertAt = function insertAt(index, node) { | ||
var argc = arguments.length; | ||
var move = getMoves(this, argc - 1, index); | ||
if (move === emptyMoves) { | ||
return this; | ||
} | ||
Pp.insertAt = function insertAt(index, node) { | ||
var argc = arguments.length; | ||
var move = getMoves(this, argc - 1, index); | ||
if (move === emptyMoves) { | ||
index = Math.max(index, 0); | ||
for (var i = 1; i < argc; ++i) { | ||
this.value[index + i - 1] = arguments[i]; | ||
} | ||
move(); | ||
return this; | ||
} | ||
}; | ||
index = Math.max(index, 0); | ||
Pp.insertBefore = function insertBefore(node) { | ||
var pp = this.parentPath; | ||
var argc = arguments.length; | ||
var insertAtArgs = [this.name]; | ||
for (var i = 0; i < argc; ++i) { | ||
insertAtArgs.push(arguments[i]); | ||
} | ||
return pp.insertAt.apply(pp, insertAtArgs); | ||
}; | ||
for (var i = 1; i < argc; ++i) { | ||
this.value[index + i - 1] = arguments[i]; | ||
} | ||
Pp.insertAfter = function insertAfter(node) { | ||
var pp = this.parentPath; | ||
var argc = arguments.length; | ||
var insertAtArgs = [this.name + 1]; | ||
for (var i = 0; i < argc; ++i) { | ||
insertAtArgs.push(arguments[i]); | ||
} | ||
return pp.insertAt.apply(pp, insertAtArgs); | ||
}; | ||
move(); | ||
function repairRelationshipWithParent(path) { | ||
if (!(path instanceof Path)) { | ||
throw new Error(""); | ||
} | ||
return this; | ||
}; | ||
var pp = path.parentPath; | ||
if (!pp) { | ||
// Orphan paths have no relationship to repair. | ||
return path; | ||
} | ||
Pp.insertBefore = function insertBefore(node) { | ||
var pp = this.parentPath; | ||
var argc = arguments.length; | ||
var insertAtArgs = [this.name]; | ||
for (var i = 0; i < argc; ++i) { | ||
insertAtArgs.push(arguments[i]); | ||
} | ||
return pp.insertAt.apply(pp, insertAtArgs); | ||
}; | ||
var parentValue = pp.value; | ||
var parentCache = getChildCache(pp); | ||
Pp.insertAfter = function insertAfter(node) { | ||
var pp = this.parentPath; | ||
var argc = arguments.length; | ||
var insertAtArgs = [this.name + 1]; | ||
for (var i = 0; i < argc; ++i) { | ||
insertAtArgs.push(arguments[i]); | ||
} | ||
return pp.insertAt.apply(pp, insertAtArgs); | ||
}; | ||
// Make sure parentCache[path.name] is populated. | ||
if (parentValue[path.name] === path.value) { | ||
parentCache[path.name] = path; | ||
} else if (isArray.check(parentValue)) { | ||
// Something caused path.name to become out of date, so attempt to | ||
// recover by searching for path.value in parentValue. | ||
var i = parentValue.indexOf(path.value); | ||
if (i >= 0) { | ||
parentCache[path.name = i] = path; | ||
} | ||
} else { | ||
// If path.value disagrees with parentValue[path.name], and | ||
// path.name is not an array index, let path.value become the new | ||
// parentValue[path.name] and update parentCache accordingly. | ||
parentValue[path.name] = path.value; | ||
parentCache[path.name] = path; | ||
} | ||
function repairRelationshipWithParent(path) { | ||
if (!(path instanceof Path)) { | ||
throw new Error(""); | ||
} | ||
if (parentValue[path.name] !== path.value) { | ||
throw new Error(""); | ||
} | ||
if (path.parentPath.get(path.name) !== path) { | ||
throw new Error(""); | ||
} | ||
var pp = path.parentPath; | ||
if (!pp) { | ||
// Orphan paths have no relationship to repair. | ||
return path; | ||
} | ||
var parentValue = pp.value; | ||
var parentCache = getChildCache(pp); | ||
Pp.replace = function replace(replacement) { | ||
var results = []; | ||
var parentValue = this.parentPath.value; | ||
var parentCache = getChildCache(this.parentPath); | ||
var count = arguments.length; | ||
// Make sure parentCache[path.name] is populated. | ||
if (parentValue[path.name] === path.value) { | ||
parentCache[path.name] = path; | ||
} else if (isArray.check(parentValue)) { | ||
// Something caused path.name to become out of date, so attempt to | ||
// recover by searching for path.value in parentValue. | ||
var i = parentValue.indexOf(path.value); | ||
if (i >= 0) { | ||
parentCache[path.name = i] = path; | ||
} | ||
} else { | ||
// If path.value disagrees with parentValue[path.name], and | ||
// path.name is not an array index, let path.value become the new | ||
// parentValue[path.name] and update parentCache accordingly. | ||
parentValue[path.name] = path.value; | ||
parentCache[path.name] = path; | ||
} | ||
repairRelationshipWithParent(this); | ||
if (parentValue[path.name] !== path.value) { | ||
throw new Error(""); | ||
} | ||
if (path.parentPath.get(path.name) !== path) { | ||
throw new Error(""); | ||
} | ||
if (isArray.check(parentValue)) { | ||
var originalLength = parentValue.length; | ||
var move = getMoves(this.parentPath, count - 1, this.name + 1); | ||
return path; | ||
} | ||
var spliceArgs = [this.name, 1]; | ||
for (var i = 0; i < count; ++i) { | ||
spliceArgs.push(arguments[i]); | ||
} | ||
Pp.replace = function replace(replacement) { | ||
var results = []; | ||
var parentValue = this.parentPath.value; | ||
var parentCache = getChildCache(this.parentPath); | ||
var count = arguments.length; | ||
var splicedOut = parentValue.splice.apply(parentValue, spliceArgs); | ||
repairRelationshipWithParent(this); | ||
if (splicedOut[0] !== this.value) { | ||
throw new Error(""); | ||
} | ||
if (parentValue.length !== (originalLength - 1 + count)) { | ||
throw new Error(""); | ||
} | ||
if (isArray.check(parentValue)) { | ||
var originalLength = parentValue.length; | ||
var move = getMoves(this.parentPath, count - 1, this.name + 1); | ||
move(); | ||
var spliceArgs = [this.name, 1]; | ||
for (var i = 0; i < count; ++i) { | ||
spliceArgs.push(arguments[i]); | ||
} | ||
if (count === 0) { | ||
delete this.value; | ||
delete parentCache[this.name]; | ||
this.__childCache = null; | ||
var splicedOut = parentValue.splice.apply(parentValue, spliceArgs); | ||
} else { | ||
if (parentValue[this.name] !== replacement) { | ||
throw new Error(""); | ||
} | ||
if (splicedOut[0] !== this.value) { | ||
throw new Error(""); | ||
} | ||
if (parentValue.length !== (originalLength - 1 + count)) { | ||
throw new Error(""); | ||
} | ||
if (this.value !== replacement) { | ||
this.value = replacement; | ||
this.__childCache = null; | ||
} | ||
move(); | ||
for (i = 0; i < count; ++i) { | ||
results.push(this.parentPath.get(this.name + i)); | ||
} | ||
if (count === 0) { | ||
delete this.value; | ||
delete parentCache[this.name]; | ||
this.__childCache = null; | ||
} else { | ||
if (parentValue[this.name] !== replacement) { | ||
throw new Error(""); | ||
if (results[0] !== this) { | ||
throw new Error(""); | ||
} | ||
} | ||
} else if (count === 1) { | ||
if (this.value !== replacement) { | ||
this.value = replacement; | ||
this.__childCache = null; | ||
} | ||
this.value = parentValue[this.name] = replacement; | ||
results.push(this); | ||
for (i = 0; i < count; ++i) { | ||
results.push(this.parentPath.get(this.name + i)); | ||
} | ||
} else if (count === 0) { | ||
delete parentValue[this.name]; | ||
delete this.value; | ||
this.__childCache = null; | ||
if (results[0] !== this) { | ||
throw new Error(""); | ||
} | ||
} | ||
// Leave this path cached as parentCache[this.name], even though | ||
// it no longer has a value defined. | ||
} else if (count === 1) { | ||
if (this.value !== replacement) { | ||
this.__childCache = null; | ||
} else { | ||
throw new Error("Could not replace path"); | ||
} | ||
this.value = parentValue[this.name] = replacement; | ||
results.push(this); | ||
} else if (count === 0) { | ||
delete parentValue[this.name]; | ||
delete this.value; | ||
this.__childCache = null; | ||
return results; | ||
}; | ||
// Leave this path cached as parentCache[this.name], even though | ||
// it no longer has a value defined. | ||
} else { | ||
throw new Error("Could not replace path"); | ||
} | ||
return results; | ||
return Path; | ||
}; | ||
module.exports = Path; |
547
lib/scope.js
@@ -1,347 +0,350 @@ | ||
var types = require("./types"); | ||
var Type = types.Type; | ||
var namedTypes = types.namedTypes; | ||
var Node = namedTypes.Node; | ||
var Expression = namedTypes.Expression; | ||
var isArray = types.builtInTypes.array; | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
var b = types.builders; | ||
function Scope(path, parentScope) { | ||
if (!(this instanceof Scope)) { | ||
throw new Error("Scope constructor cannot be invoked without 'new'"); | ||
} | ||
if (!(path instanceof require("./node-path"))) { | ||
throw new Error(""); | ||
} | ||
ScopeType.assert(path.value); | ||
module.exports = function (fork) { | ||
var types = fork.use(require("./types")); | ||
var Type = types.Type; | ||
var namedTypes = types.namedTypes; | ||
var Node = namedTypes.Node; | ||
var Expression = namedTypes.Expression; | ||
var isArray = types.builtInTypes.array; | ||
var b = types.builders; | ||
var depth; | ||
if (parentScope) { | ||
if (!(parentScope instanceof Scope)) { | ||
function Scope(path, parentScope) { | ||
if (!(this instanceof Scope)) { | ||
throw new Error("Scope constructor cannot be invoked without 'new'"); | ||
} | ||
if (!(path instanceof fork.use(require("./node-path")))) { | ||
throw new Error(""); | ||
} | ||
depth = parentScope.depth + 1; | ||
} else { | ||
parentScope = null; | ||
depth = 0; | ||
ScopeType.assert(path.value); | ||
var depth; | ||
if (parentScope) { | ||
if (!(parentScope instanceof Scope)) { | ||
throw new Error(""); | ||
} | ||
depth = parentScope.depth + 1; | ||
} else { | ||
parentScope = null; | ||
depth = 0; | ||
} | ||
Object.defineProperties(this, { | ||
path: { value: path }, | ||
node: { value: path.value }, | ||
isGlobal: { value: !parentScope, enumerable: true }, | ||
depth: { value: depth }, | ||
parent: { value: parentScope }, | ||
bindings: { value: {} }, | ||
types: { value: {} }, | ||
}); | ||
} | ||
Object.defineProperties(this, { | ||
path: { value: path }, | ||
node: { value: path.value }, | ||
isGlobal: { value: !parentScope, enumerable: true }, | ||
depth: { value: depth }, | ||
parent: { value: parentScope }, | ||
bindings: { value: {} }, | ||
types: { value: {} }, | ||
}); | ||
} | ||
var scopeTypes = [ | ||
// Program nodes introduce global scopes. | ||
namedTypes.Program, | ||
var scopeTypes = [ | ||
// Program nodes introduce global scopes. | ||
namedTypes.Program, | ||
// Function is the supertype of FunctionExpression, | ||
// FunctionDeclaration, ArrowExpression, etc. | ||
namedTypes.Function, | ||
// Function is the supertype of FunctionExpression, | ||
// FunctionDeclaration, ArrowExpression, etc. | ||
namedTypes.Function, | ||
// In case you didn't know, the caught parameter shadows any variable | ||
// of the same name in an outer scope. | ||
namedTypes.CatchClause | ||
]; | ||
// In case you didn't know, the caught parameter shadows any variable | ||
// of the same name in an outer scope. | ||
namedTypes.CatchClause | ||
]; | ||
var ScopeType = Type.or.apply(Type, scopeTypes); | ||
var ScopeType = Type.or.apply(Type, scopeTypes); | ||
Scope.isEstablishedBy = function(node) { | ||
return ScopeType.check(node); | ||
}; | ||
Scope.isEstablishedBy = function(node) { | ||
return ScopeType.check(node); | ||
}; | ||
var Sp = Scope.prototype; | ||
var Sp = Scope.prototype; | ||
// Will be overridden after an instance lazily calls scanScope. | ||
Sp.didScan = false; | ||
Sp.didScan = false; | ||
Sp.declares = function(name) { | ||
this.scan(); | ||
return hasOwn.call(this.bindings, name); | ||
}; | ||
Sp.declares = function(name) { | ||
this.scan(); | ||
return hasOwn.call(this.bindings, name); | ||
}; | ||
Sp.declaresType = function(name) { | ||
this.scan(); | ||
return hasOwn.call(this.types, name); | ||
}; | ||
Sp.declaresType = function(name) { | ||
this.scan(); | ||
return hasOwn.call(this.types, name); | ||
}; | ||
Sp.declareTemporary = function(prefix) { | ||
if (prefix) { | ||
if (!/^[a-z$_]/i.test(prefix)) { | ||
throw new Error(""); | ||
Sp.declareTemporary = function(prefix) { | ||
if (prefix) { | ||
if (!/^[a-z$_]/i.test(prefix)) { | ||
throw new Error(""); | ||
} | ||
} else { | ||
prefix = "t$"; | ||
} | ||
} else { | ||
prefix = "t$"; | ||
} | ||
// Include this.depth in the name to make sure the name does not | ||
// collide with any variables in nested/enclosing scopes. | ||
prefix += this.depth.toString(36) + "$"; | ||
// Include this.depth in the name to make sure the name does not | ||
// collide with any variables in nested/enclosing scopes. | ||
prefix += this.depth.toString(36) + "$"; | ||
this.scan(); | ||
this.scan(); | ||
var index = 0; | ||
while (this.declares(prefix + index)) { | ||
++index; | ||
} | ||
var index = 0; | ||
while (this.declares(prefix + index)) { | ||
++index; | ||
} | ||
var name = prefix + index; | ||
return this.bindings[name] = types.builders.identifier(name); | ||
}; | ||
var name = prefix + index; | ||
return this.bindings[name] = types.builders.identifier(name); | ||
}; | ||
Sp.injectTemporary = function(identifier, init) { | ||
identifier || (identifier = this.declareTemporary()); | ||
Sp.injectTemporary = function(identifier, init) { | ||
identifier || (identifier = this.declareTemporary()); | ||
var bodyPath = this.path.get("body"); | ||
if (namedTypes.BlockStatement.check(bodyPath.value)) { | ||
bodyPath = bodyPath.get("body"); | ||
} | ||
var bodyPath = this.path.get("body"); | ||
if (namedTypes.BlockStatement.check(bodyPath.value)) { | ||
bodyPath = bodyPath.get("body"); | ||
} | ||
bodyPath.unshift( | ||
b.variableDeclaration( | ||
bodyPath.unshift( | ||
b.variableDeclaration( | ||
"var", | ||
[b.variableDeclarator(identifier, init || null)] | ||
) | ||
); | ||
) | ||
); | ||
return identifier; | ||
}; | ||
return identifier; | ||
}; | ||
Sp.scan = function(force) { | ||
if (force || !this.didScan) { | ||
for (var name in this.bindings) { | ||
// Empty out this.bindings, just in cases. | ||
delete this.bindings[name]; | ||
Sp.scan = function(force) { | ||
if (force || !this.didScan) { | ||
for (var name in this.bindings) { | ||
// Empty out this.bindings, just in cases. | ||
delete this.bindings[name]; | ||
} | ||
scanScope(this.path, this.bindings, this.types); | ||
this.didScan = true; | ||
} | ||
scanScope(this.path, this.bindings, this.types); | ||
this.didScan = true; | ||
} | ||
}; | ||
}; | ||
Sp.getBindings = function () { | ||
this.scan(); | ||
return this.bindings; | ||
}; | ||
Sp.getBindings = function () { | ||
this.scan(); | ||
return this.bindings; | ||
}; | ||
Sp.getTypes = function () { | ||
this.scan(); | ||
return this.types; | ||
}; | ||
Sp.getTypes = function () { | ||
this.scan(); | ||
return this.types; | ||
}; | ||
function scanScope(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
ScopeType.assert(node); | ||
function scanScope(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
ScopeType.assert(node); | ||
if (namedTypes.CatchClause.check(node)) { | ||
// A catch clause establishes a new scope but the only variable | ||
// bound in that scope is the catch parameter. Any other | ||
// declarations create bindings in the outer scope. | ||
addPattern(path.get("param"), bindings); | ||
if (namedTypes.CatchClause.check(node)) { | ||
// A catch clause establishes a new scope but the only variable | ||
// bound in that scope is the catch parameter. Any other | ||
// declarations create bindings in the outer scope. | ||
addPattern(path.get("param"), bindings); | ||
} else { | ||
recursiveScanScope(path, bindings, scopeTypes); | ||
} else { | ||
recursiveScanScope(path, bindings, scopeTypes); | ||
} | ||
} | ||
} | ||
function recursiveScanScope(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
function recursiveScanScope(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
if (path.parent && | ||
namedTypes.FunctionExpression.check(path.parent.node) && | ||
path.parent.node.id) { | ||
addPattern(path.parent.get("id"), bindings); | ||
} | ||
if (path.parent && | ||
namedTypes.FunctionExpression.check(path.parent.node) && | ||
path.parent.node.id) { | ||
addPattern(path.parent.get("id"), bindings); | ||
} | ||
if (!node) { | ||
// None of the remaining cases matter if node is falsy. | ||
if (!node) { | ||
// None of the remaining cases matter if node is falsy. | ||
} else if (isArray.check(node)) { | ||
path.each(function(childPath) { | ||
recursiveScanChild(childPath, bindings, scopeTypes); | ||
}); | ||
} else if (isArray.check(node)) { | ||
path.each(function(childPath) { | ||
recursiveScanChild(childPath, bindings, scopeTypes); | ||
}); | ||
} else if (namedTypes.Function.check(node)) { | ||
path.get("params").each(function(paramPath) { | ||
addPattern(paramPath, bindings); | ||
}); | ||
} else if (namedTypes.Function.check(node)) { | ||
path.get("params").each(function(paramPath) { | ||
addPattern(paramPath, bindings); | ||
}); | ||
recursiveScanChild(path.get("body"), bindings, scopeTypes); | ||
recursiveScanChild(path.get("body"), bindings, scopeTypes); | ||
} else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) { | ||
addTypePattern(path.get("id"), scopeTypes); | ||
} else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) { | ||
addTypePattern(path.get("id"), scopeTypes); | ||
} else if (namedTypes.VariableDeclarator.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
recursiveScanChild(path.get("init"), bindings, scopeTypes); | ||
} else if (namedTypes.VariableDeclarator.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
recursiveScanChild(path.get("init"), bindings, scopeTypes); | ||
} else if (node.type === "ImportSpecifier" || | ||
node.type === "ImportNamespaceSpecifier" || | ||
node.type === "ImportDefaultSpecifier") { | ||
addPattern( | ||
// Esprima used to use the .name field to refer to the local | ||
// binding identifier for ImportSpecifier nodes, but .id for | ||
// ImportNamespaceSpecifier and ImportDefaultSpecifier nodes. | ||
// ESTree/Acorn/ESpree use .local for all three node types. | ||
path.get(node.local ? "local" : | ||
node.name ? "name" : "id"), | ||
bindings | ||
); | ||
} else if (node.type === "ImportSpecifier" || | ||
node.type === "ImportNamespaceSpecifier" || | ||
node.type === "ImportDefaultSpecifier") { | ||
addPattern( | ||
// Esprima used to use the .name field to refer to the local | ||
// binding identifier for ImportSpecifier nodes, but .id for | ||
// ImportNamespaceSpecifier and ImportDefaultSpecifier nodes. | ||
// ESTree/Acorn/ESpree use .local for all three node types. | ||
path.get(node.local ? "local" : | ||
node.name ? "name" : "id"), | ||
bindings | ||
); | ||
} else if (Node.check(node) && !Expression.check(node)) { | ||
types.eachField(node, function(name, child) { | ||
var childPath = path.get(name); | ||
if (! pathHasValue(childPath, child)) { | ||
throw new Error(""); | ||
} | ||
recursiveScanChild(childPath, bindings, scopeTypes); | ||
}); | ||
} else if (Node.check(node) && !Expression.check(node)) { | ||
types.eachField(node, function(name, child) { | ||
var childPath = path.get(name); | ||
if (!pathHasValue(childPath, child)) { | ||
throw new Error(""); | ||
} | ||
recursiveScanChild(childPath, bindings, scopeTypes); | ||
}); | ||
} | ||
} | ||
} | ||
function pathHasValue(path, value) { | ||
if (path.value === value) { | ||
return true; | ||
} | ||
function pathHasValue(path, value) { | ||
if (path.value === value) { | ||
return true; | ||
} | ||
// Empty arrays are probably produced by defaults.emptyArray, in which | ||
// case is makes sense to regard them as equivalent, if not ===. | ||
if (Array.isArray(path.value) && | ||
path.value.length === 0 && | ||
Array.isArray(value) && | ||
value.length === 0) { | ||
return true; | ||
} | ||
// Empty arrays are probably produced by defaults.emptyArray, in which | ||
// case is makes sense to regard them as equivalent, if not ===. | ||
if (Array.isArray(path.value) && | ||
path.value.length === 0 && | ||
Array.isArray(value) && | ||
value.length === 0) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
function recursiveScanChild(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
function recursiveScanChild(path, bindings, scopeTypes) { | ||
var node = path.value; | ||
if (!node || Expression.check(node)) { | ||
// Ignore falsy values and Expressions. | ||
if (!node || Expression.check(node)) { | ||
// Ignore falsy values and Expressions. | ||
} else if (namedTypes.FunctionDeclaration.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
} else if (namedTypes.FunctionDeclaration.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
} else if (namedTypes.ClassDeclaration && | ||
namedTypes.ClassDeclaration.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
} else if (namedTypes.ClassDeclaration && | ||
namedTypes.ClassDeclaration.check(node)) { | ||
addPattern(path.get("id"), bindings); | ||
} else if (ScopeType.check(node)) { | ||
if (namedTypes.CatchClause.check(node)) { | ||
var catchParamName = node.param.name; | ||
var hadBinding = hasOwn.call(bindings, catchParamName); | ||
} else if (ScopeType.check(node)) { | ||
if (namedTypes.CatchClause.check(node)) { | ||
var catchParamName = node.param.name; | ||
var hadBinding = hasOwn.call(bindings, catchParamName); | ||
// Any declarations that occur inside the catch body that do | ||
// not have the same name as the catch parameter should count | ||
// as bindings in the outer scope. | ||
recursiveScanScope(path.get("body"), bindings, scopeTypes); | ||
// Any declarations that occur inside the catch body that do | ||
// not have the same name as the catch parameter should count | ||
// as bindings in the outer scope. | ||
recursiveScanScope(path.get("body"), bindings, scopeTypes); | ||
// If a new binding matching the catch parameter name was | ||
// created while scanning the catch body, ignore it because it | ||
// actually refers to the catch parameter and not the outer | ||
// scope that we're currently scanning. | ||
if (!hadBinding) { | ||
delete bindings[catchParamName]; | ||
// If a new binding matching the catch parameter name was | ||
// created while scanning the catch body, ignore it because it | ||
// actually refers to the catch parameter and not the outer | ||
// scope that we're currently scanning. | ||
if (!hadBinding) { | ||
delete bindings[catchParamName]; | ||
} | ||
} | ||
} | ||
} else { | ||
recursiveScanScope(path, bindings, scopeTypes); | ||
} | ||
} | ||
function addPattern(patternPath, bindings) { | ||
var pattern = patternPath.value; | ||
namedTypes.Pattern.assert(pattern); | ||
if (namedTypes.Identifier.check(pattern)) { | ||
if (hasOwn.call(bindings, pattern.name)) { | ||
bindings[pattern.name].push(patternPath); | ||
} else { | ||
bindings[pattern.name] = [patternPath]; | ||
recursiveScanScope(path, bindings, scopeTypes); | ||
} | ||
} | ||
} else if (namedTypes.ObjectPattern && | ||
namedTypes.ObjectPattern.check(pattern)) { | ||
patternPath.get('properties').each(function(propertyPath) { | ||
var property = propertyPath.value; | ||
if (namedTypes.Pattern.check(property)) { | ||
addPattern(propertyPath, bindings); | ||
} else if (namedTypes.Property.check(property)) { | ||
addPattern(propertyPath.get('value'), bindings); | ||
} else if (namedTypes.SpreadProperty && | ||
namedTypes.SpreadProperty.check(property)) { | ||
addPattern(propertyPath.get('argument'), bindings); | ||
} | ||
}); | ||
function addPattern(patternPath, bindings) { | ||
var pattern = patternPath.value; | ||
namedTypes.Pattern.assert(pattern); | ||
} else if (namedTypes.ArrayPattern && | ||
namedTypes.ArrayPattern.check(pattern)) { | ||
patternPath.get('elements').each(function(elementPath) { | ||
var element = elementPath.value; | ||
if (namedTypes.Pattern.check(element)) { | ||
addPattern(elementPath, bindings); | ||
} else if (namedTypes.SpreadElement && | ||
namedTypes.SpreadElement.check(element)) { | ||
addPattern(elementPath.get("argument"), bindings); | ||
if (namedTypes.Identifier.check(pattern)) { | ||
if (hasOwn.call(bindings, pattern.name)) { | ||
bindings[pattern.name].push(patternPath); | ||
} else { | ||
bindings[pattern.name] = [patternPath]; | ||
} | ||
}); | ||
} else if (namedTypes.PropertyPattern && | ||
namedTypes.PropertyPattern.check(pattern)) { | ||
addPattern(patternPath.get('pattern'), bindings); | ||
} else if (namedTypes.ObjectPattern && | ||
namedTypes.ObjectPattern.check(pattern)) { | ||
patternPath.get('properties').each(function(propertyPath) { | ||
var property = propertyPath.value; | ||
if (namedTypes.Pattern.check(property)) { | ||
addPattern(propertyPath, bindings); | ||
} else if (namedTypes.Property.check(property)) { | ||
addPattern(propertyPath.get('value'), bindings); | ||
} else if (namedTypes.SpreadProperty && | ||
namedTypes.SpreadProperty.check(property)) { | ||
addPattern(propertyPath.get('argument'), bindings); | ||
} | ||
}); | ||
} else if ((namedTypes.SpreadElementPattern && | ||
namedTypes.SpreadElementPattern.check(pattern)) || | ||
(namedTypes.SpreadPropertyPattern && | ||
namedTypes.SpreadPropertyPattern.check(pattern))) { | ||
addPattern(patternPath.get('argument'), bindings); | ||
} else if (namedTypes.ArrayPattern && | ||
namedTypes.ArrayPattern.check(pattern)) { | ||
patternPath.get('elements').each(function(elementPath) { | ||
var element = elementPath.value; | ||
if (namedTypes.Pattern.check(element)) { | ||
addPattern(elementPath, bindings); | ||
} else if (namedTypes.SpreadElement && | ||
namedTypes.SpreadElement.check(element)) { | ||
addPattern(elementPath.get("argument"), bindings); | ||
} | ||
}); | ||
} else if (namedTypes.PropertyPattern && | ||
namedTypes.PropertyPattern.check(pattern)) { | ||
addPattern(patternPath.get('pattern'), bindings); | ||
} else if ((namedTypes.SpreadElementPattern && | ||
namedTypes.SpreadElementPattern.check(pattern)) || | ||
(namedTypes.SpreadPropertyPattern && | ||
namedTypes.SpreadPropertyPattern.check(pattern))) { | ||
addPattern(patternPath.get('argument'), bindings); | ||
} | ||
} | ||
} | ||
function addTypePattern(patternPath, types) { | ||
var pattern = patternPath.value; | ||
namedTypes.Pattern.assert(pattern); | ||
function addTypePattern(patternPath, types) { | ||
var pattern = patternPath.value; | ||
namedTypes.Pattern.assert(pattern); | ||
if (namedTypes.Identifier.check(pattern)) { | ||
if (hasOwn.call(types, pattern.name)) { | ||
types[pattern.name].push(patternPath); | ||
} else { | ||
types[pattern.name] = [patternPath]; | ||
if (namedTypes.Identifier.check(pattern)) { | ||
if (hasOwn.call(types, pattern.name)) { | ||
types[pattern.name].push(patternPath); | ||
} else { | ||
types[pattern.name] = [patternPath]; | ||
} | ||
} | ||
} | ||
} | ||
Sp.lookup = function(name) { | ||
for (var scope = this; scope; scope = scope.parent) | ||
if (scope.declares(name)) | ||
break; | ||
return scope; | ||
}; | ||
Sp.lookup = function(name) { | ||
for (var scope = this; scope; scope = scope.parent) | ||
if (scope.declares(name)) | ||
break; | ||
return scope; | ||
}; | ||
Sp.lookupType = function(name) { | ||
for (var scope = this; scope; scope = scope.parent) | ||
if (scope.declaresType(name)) | ||
break; | ||
return scope; | ||
}; | ||
Sp.lookupType = function(name) { | ||
for (var scope = this; scope; scope = scope.parent) | ||
if (scope.declaresType(name)) | ||
break; | ||
return scope; | ||
}; | ||
Sp.getGlobalScope = function() { | ||
var scope = this; | ||
while (!scope.isGlobal) | ||
scope = scope.parent; | ||
return scope; | ||
Sp.getGlobalScope = function() { | ||
var scope = this; | ||
while (!scope.isGlobal) | ||
scope = scope.parent; | ||
return scope; | ||
}; | ||
return Scope; | ||
}; | ||
module.exports = Scope; |
@@ -1,41 +0,46 @@ | ||
var types = require("../lib/types"); | ||
var Type = types.Type; | ||
var builtin = types.builtInTypes; | ||
var isNumber = builtin.number; | ||
module.exports = function (fork) { | ||
var exports = {}; | ||
var types = fork.use(require("../lib/types")); | ||
var Type = types.Type; | ||
var builtin = types.builtInTypes; | ||
var isNumber = builtin.number; | ||
// An example of constructing a new type with arbitrary constraints from | ||
// an existing type. | ||
exports.geq = function(than) { | ||
return new Type(function(value) { | ||
return isNumber.check(value) && value >= than; | ||
}, isNumber + " >= " + than); | ||
}; | ||
// An example of constructing a new type with arbitrary constraints from | ||
// an existing type. | ||
exports.geq = function (than) { | ||
return new Type(function (value) { | ||
return isNumber.check(value) && value >= than; | ||
}, isNumber + " >= " + than); | ||
}; | ||
// Default value-returning functions that may optionally be passed as a | ||
// third argument to Def.prototype.field. | ||
exports.defaults = { | ||
// Functions were used because (among other reasons) that's the most | ||
// elegant way to allow for the emptyArray one always to give a new | ||
// array instance. | ||
"null": function() { return null }, | ||
"emptyArray": function() { return [] }, | ||
"false": function() { return false }, | ||
"true": function() { return true }, | ||
"undefined": function() {} | ||
}; | ||
// Default value-returning functions that may optionally be passed as a | ||
// third argument to Def.prototype.field. | ||
exports.defaults = { | ||
// Functions were used because (among other reasons) that's the most | ||
// elegant way to allow for the emptyArray one always to give a new | ||
// array instance. | ||
"null": function () { return null }, | ||
"emptyArray": function () { return [] }, | ||
"false": function () { return false }, | ||
"true": function () { return true }, | ||
"undefined": function () {} | ||
}; | ||
var naiveIsPrimitive = Type.or( | ||
builtin.string, | ||
builtin.number, | ||
builtin.boolean, | ||
builtin.null, | ||
builtin.undefined | ||
); | ||
var naiveIsPrimitive = Type.or( | ||
builtin.string, | ||
builtin.number, | ||
builtin.boolean, | ||
builtin.null, | ||
builtin.undefined | ||
); | ||
exports.isPrimitive = new Type(function(value) { | ||
if (value === null) | ||
return true; | ||
var type = typeof value; | ||
return !(type === "object" || | ||
type === "function"); | ||
}, naiveIsPrimitive.toString()); | ||
exports.isPrimitive = new Type(function (value) { | ||
if (value === null) | ||
return true; | ||
var type = typeof value; | ||
return !(type === "object" || | ||
type === "function"); | ||
}, naiveIsPrimitive.toString()); | ||
return exports; | ||
}; |
1307
lib/types.js
@@ -11,819 +11,826 @@ var Ap = Array.prototype; | ||
// A type is an object with a .check method that takes a value and returns | ||
// true or false according to whether the value matches the type. | ||
module.exports = function () { | ||
function Type(check, name) { | ||
var self = this; | ||
if (!(self instanceof Type)) { | ||
throw new Error("Type constructor cannot be invoked without 'new'"); | ||
} | ||
var exports = {}; | ||
// Unfortunately we can't elegantly reuse isFunction and isString, | ||
// here, because this code is executed while defining those types. | ||
if (objToStr.call(check) !== funObjStr) { | ||
throw new Error(check + " is not a function"); | ||
} | ||
// A type is an object with a .check method that takes a value and returns | ||
// true or false according to whether the value matches the type. | ||
// The `name` parameter can be either a function or a string. | ||
var nameObjStr = objToStr.call(name); | ||
if (!(nameObjStr === funObjStr || | ||
function Type(check, name) { | ||
var self = this; | ||
if (!(self instanceof Type)) { | ||
throw new Error("Type constructor cannot be invoked without 'new'"); | ||
} | ||
// Unfortunately we can't elegantly reuse isFunction and isString, | ||
// here, because this code is executed while defining those types. | ||
if (objToStr.call(check) !== funObjStr) { | ||
throw new Error(check + " is not a function"); | ||
} | ||
// The `name` parameter can be either a function or a string. | ||
var nameObjStr = objToStr.call(name); | ||
if (!(nameObjStr === funObjStr || | ||
nameObjStr === strObjStr)) { | ||
throw new Error(name + " is neither a function nor a string"); | ||
throw new Error(name + " is neither a function nor a string"); | ||
} | ||
Object.defineProperties(self, { | ||
name: {value: name}, | ||
check: { | ||
value: function (value, deep) { | ||
var result = check.call(self, value, deep); | ||
if (!result && deep && objToStr.call(deep) === funObjStr) | ||
deep(self, value); | ||
return result; | ||
} | ||
} | ||
}); | ||
} | ||
Object.defineProperties(self, { | ||
name: { value: name }, | ||
check: { | ||
value: function(value, deep) { | ||
var result = check.call(self, value, deep); | ||
if (!result && deep && objToStr.call(deep) === funObjStr) | ||
deep(self, value); | ||
return result; | ||
} | ||
var Tp = Type.prototype; | ||
// Throughout this file we use Object.defineProperty to prevent | ||
// redefinition of exported properties. | ||
exports.Type = Type; | ||
// Like .check, except that failure triggers an AssertionError. | ||
Tp.assert = function (value, deep) { | ||
if (!this.check(value, deep)) { | ||
var str = shallowStringify(value); | ||
throw new Error(str + " does not match type " + this); | ||
} | ||
}); | ||
} | ||
return true; | ||
}; | ||
var Tp = Type.prototype; | ||
function shallowStringify(value) { | ||
if (isObject.check(value)) | ||
return "{" + Object.keys(value).map(function (key) { | ||
return key + ": " + value[key]; | ||
}).join(", ") + "}"; | ||
// Throughout this file we use Object.defineProperty to prevent | ||
// redefinition of exported properties. | ||
exports.Type = Type; | ||
if (isArray.check(value)) | ||
return "[" + value.map(shallowStringify).join(", ") + "]"; | ||
// Like .check, except that failure triggers an AssertionError. | ||
Tp.assert = function(value, deep) { | ||
if (!this.check(value, deep)) { | ||
var str = shallowStringify(value); | ||
throw new Error(str + " does not match type " + this); | ||
return JSON.stringify(value); | ||
} | ||
return true; | ||
}; | ||
function shallowStringify(value) { | ||
if (isObject.check(value)) | ||
return "{" + Object.keys(value).map(function(key) { | ||
return key + ": " + value[key]; | ||
}).join(", ") + "}"; | ||
Tp.toString = function () { | ||
var name = this.name; | ||
if (isArray.check(value)) | ||
return "[" + value.map(shallowStringify).join(", ") + "]"; | ||
if (isString.check(name)) | ||
return name; | ||
return JSON.stringify(value); | ||
} | ||
if (isFunction.check(name)) | ||
return name.call(this) + ""; | ||
Tp.toString = function() { | ||
var name = this.name; | ||
return name + " type"; | ||
}; | ||
if (isString.check(name)) | ||
return name; | ||
var builtInCtorFns = []; | ||
var builtInCtorTypes = []; | ||
var builtInTypes = {}; | ||
exports.builtInTypes = builtInTypes; | ||
if (isFunction.check(name)) | ||
return name.call(this) + ""; | ||
function defBuiltInType(example, name) { | ||
var objStr = objToStr.call(example); | ||
return name + " type"; | ||
}; | ||
var type = new Type(function (value) { | ||
return objToStr.call(value) === objStr; | ||
}, name); | ||
var builtInCtorFns = []; | ||
var builtInCtorTypes = []; | ||
var builtInTypes = {}; | ||
exports.builtInTypes = builtInTypes; | ||
builtInTypes[name] = type; | ||
function defBuiltInType(example, name) { | ||
var objStr = objToStr.call(example); | ||
if (example && typeof example.constructor === "function") { | ||
builtInCtorFns.push(example.constructor); | ||
builtInCtorTypes.push(type); | ||
} | ||
var type = new Type(function(value) { | ||
return objToStr.call(value) === objStr; | ||
}, name); | ||
builtInTypes[name] = type; | ||
if (example && typeof example.constructor === "function") { | ||
builtInCtorFns.push(example.constructor); | ||
builtInCtorTypes.push(type); | ||
return type; | ||
} | ||
return type; | ||
} | ||
// These types check the underlying [[Class]] attribute of the given | ||
// value, rather than using the problematic typeof operator. Note however | ||
// that no subtyping is considered; so, for instance, isObject.check | ||
// returns false for [], /./, new Date, and null. | ||
var isString = defBuiltInType("truthy", "string"); | ||
var isFunction = defBuiltInType(function () {}, "function"); | ||
var isArray = defBuiltInType([], "array"); | ||
var isObject = defBuiltInType({}, "object"); | ||
var isRegExp = defBuiltInType(/./, "RegExp"); | ||
var isDate = defBuiltInType(new Date, "Date"); | ||
var isNumber = defBuiltInType(3, "number"); | ||
var isBoolean = defBuiltInType(true, "boolean"); | ||
var isNull = defBuiltInType(null, "null"); | ||
var isUndefined = defBuiltInType(void 0, "undefined"); | ||
// These types check the underlying [[Class]] attribute of the given | ||
// value, rather than using the problematic typeof operator. Note however | ||
// that no subtyping is considered; so, for instance, isObject.check | ||
// returns false for [], /./, new Date, and null. | ||
var isString = defBuiltInType("truthy", "string"); | ||
var isFunction = defBuiltInType(function(){}, "function"); | ||
var isArray = defBuiltInType([], "array"); | ||
var isObject = defBuiltInType({}, "object"); | ||
var isRegExp = defBuiltInType(/./, "RegExp"); | ||
var isDate = defBuiltInType(new Date, "Date"); | ||
var isNumber = defBuiltInType(3, "number"); | ||
var isBoolean = defBuiltInType(true, "boolean"); | ||
var isNull = defBuiltInType(null, "null"); | ||
var isUndefined = defBuiltInType(void 0, "undefined"); | ||
// There are a number of idiomatic ways of expressing types, so this | ||
// function serves to coerce them all to actual Type objects. Note that | ||
// providing the name argument is not necessary in most cases. | ||
function toType(from, name) { | ||
// The toType function should of course be idempotent. | ||
if (from instanceof Type) | ||
return from; | ||
// There are a number of idiomatic ways of expressing types, so this | ||
// function serves to coerce them all to actual Type objects. Note that | ||
// providing the name argument is not necessary in most cases. | ||
function toType(from, name) { | ||
// The toType function should of course be idempotent. | ||
if (from instanceof Type) | ||
return from; | ||
// The Def type is used as a helper for constructing compound | ||
// interface types for AST nodes. | ||
if (from instanceof Def) | ||
return from.type; | ||
// The Def type is used as a helper for constructing compound | ||
// interface types for AST nodes. | ||
if (from instanceof Def) | ||
return from.type; | ||
// Support [ElemType] syntax. | ||
if (isArray.check(from)) | ||
return Type.fromArray(from); | ||
// Support [ElemType] syntax. | ||
if (isArray.check(from)) | ||
return Type.fromArray(from); | ||
// Support { someField: FieldType, ... } syntax. | ||
if (isObject.check(from)) | ||
return Type.fromObject(from); | ||
// Support { someField: FieldType, ... } syntax. | ||
if (isObject.check(from)) | ||
return Type.fromObject(from); | ||
if (isFunction.check(from)) { | ||
var bicfIndex = builtInCtorFns.indexOf(from); | ||
if (bicfIndex >= 0) { | ||
return builtInCtorTypes[bicfIndex]; | ||
} | ||
if (isFunction.check(from)) { | ||
var bicfIndex = builtInCtorFns.indexOf(from); | ||
if (bicfIndex >= 0) { | ||
return builtInCtorTypes[bicfIndex]; | ||
// If isFunction.check(from), and from is not a built-in | ||
// constructor, assume from is a binary predicate function we can | ||
// use to define the type. | ||
return new Type(from, name); | ||
} | ||
// If isFunction.check(from), and from is not a built-in | ||
// constructor, assume from is a binary predicate function we can | ||
// use to define the type. | ||
return new Type(from, name); | ||
// As a last resort, toType returns a type that matches any value that | ||
// is === from. This is primarily useful for literal values like | ||
// toType(null), but it has the additional advantage of allowing | ||
// toType to be a total function. | ||
return new Type(function (value) { | ||
return value === from; | ||
}, isUndefined.check(name) ? function () { | ||
return from + ""; | ||
} : name); | ||
} | ||
// As a last resort, toType returns a type that matches any value that | ||
// is === from. This is primarily useful for literal values like | ||
// toType(null), but it has the additional advantage of allowing | ||
// toType to be a total function. | ||
return new Type(function(value) { | ||
return value === from; | ||
}, isUndefined.check(name) ? function() { | ||
return from + ""; | ||
} : name); | ||
} | ||
// Returns a type that matches the given value iff any of type1, type2, | ||
// etc. match the value. | ||
Type.or = function (/* type1, type2, ... */) { | ||
var types = []; | ||
var len = arguments.length; | ||
for (var i = 0; i < len; ++i) | ||
types.push(toType(arguments[i])); | ||
// Returns a type that matches the given value iff any of type1, type2, | ||
// etc. match the value. | ||
Type.or = function(/* type1, type2, ... */) { | ||
var types = []; | ||
var len = arguments.length; | ||
for (var i = 0; i < len; ++i) | ||
types.push(toType(arguments[i])); | ||
return new Type(function (value, deep) { | ||
for (var i = 0; i < len; ++i) | ||
if (types[i].check(value, deep)) | ||
return true; | ||
return false; | ||
}, function () { | ||
return types.join(" | "); | ||
}); | ||
}; | ||
return new Type(function(value, deep) { | ||
for (var i = 0; i < len; ++i) | ||
if (types[i].check(value, deep)) | ||
return true; | ||
return false; | ||
}, function() { | ||
return types.join(" | "); | ||
}); | ||
}; | ||
Type.fromArray = function (arr) { | ||
if (!isArray.check(arr)) { | ||
throw new Error(""); | ||
} | ||
if (arr.length !== 1) { | ||
throw new Error("only one element type is permitted for typed arrays"); | ||
} | ||
return toType(arr[0]).arrayOf(); | ||
}; | ||
Type.fromArray = function(arr) { | ||
if (!isArray.check(arr)) { | ||
throw new Error(""); | ||
} | ||
if (arr.length !== 1) { | ||
throw new Error("only one element type is permitted for typed arrays"); | ||
} | ||
return toType(arr[0]).arrayOf(); | ||
}; | ||
Tp.arrayOf = function () { | ||
var elemType = this; | ||
return new Type(function (value, deep) { | ||
return isArray.check(value) && value.every(function (elem) { | ||
return elemType.check(elem, deep); | ||
}); | ||
}, function () { | ||
return "[" + elemType + "]"; | ||
}); | ||
}; | ||
Tp.arrayOf = function() { | ||
var elemType = this; | ||
return new Type(function(value, deep) { | ||
return isArray.check(value) && value.every(function(elem) { | ||
return elemType.check(elem, deep); | ||
Type.fromObject = function (obj) { | ||
var fields = Object.keys(obj).map(function (name) { | ||
return new Field(name, obj[name]); | ||
}); | ||
}, function() { | ||
return "[" + elemType + "]"; | ||
}); | ||
}; | ||
Type.fromObject = function(obj) { | ||
var fields = Object.keys(obj).map(function(name) { | ||
return new Field(name, obj[name]); | ||
}); | ||
return new Type(function(value, deep) { | ||
return isObject.check(value) && fields.every(function(field) { | ||
return field.type.check(value[field.name], deep); | ||
return new Type(function (value, deep) { | ||
return isObject.check(value) && fields.every(function (field) { | ||
return field.type.check(value[field.name], deep); | ||
}); | ||
}, function () { | ||
return "{ " + fields.join(", ") + " }"; | ||
}); | ||
}, function() { | ||
return "{ " + fields.join(", ") + " }"; | ||
}); | ||
}; | ||
}; | ||
function Field(name, type, defaultFn, hidden) { | ||
var self = this; | ||
function Field(name, type, defaultFn, hidden) { | ||
var self = this; | ||
if (!(self instanceof Field)) { | ||
throw new Error("Field constructor cannot be invoked without 'new'"); | ||
} | ||
isString.assert(name); | ||
if (!(self instanceof Field)) { | ||
throw new Error("Field constructor cannot be invoked without 'new'"); | ||
} | ||
isString.assert(name); | ||
type = toType(type); | ||
type = toType(type); | ||
var properties = { | ||
name: { value: name }, | ||
type: { value: type }, | ||
hidden: { value: !!hidden } | ||
}; | ||
var properties = { | ||
name: {value: name}, | ||
type: {value: type}, | ||
hidden: {value: !!hidden} | ||
}; | ||
if (isFunction.check(defaultFn)) { | ||
properties.defaultFn = { value: defaultFn }; | ||
if (isFunction.check(defaultFn)) { | ||
properties.defaultFn = {value: defaultFn}; | ||
} | ||
Object.defineProperties(self, properties); | ||
} | ||
Object.defineProperties(self, properties); | ||
} | ||
var Fp = Field.prototype; | ||
var Fp = Field.prototype; | ||
Fp.toString = function () { | ||
return JSON.stringify(this.name) + ": " + this.type; | ||
}; | ||
Fp.toString = function() { | ||
return JSON.stringify(this.name) + ": " + this.type; | ||
}; | ||
Fp.getValue = function (obj) { | ||
var value = obj[this.name]; | ||
Fp.getValue = function(obj) { | ||
var value = obj[this.name]; | ||
if (!isUndefined.check(value)) | ||
return value; | ||
if (!isUndefined.check(value)) | ||
if (this.defaultFn) | ||
value = this.defaultFn.call(obj); | ||
return value; | ||
}; | ||
if (this.defaultFn) | ||
value = this.defaultFn.call(obj); | ||
// Define a type whose name is registered in a namespace (the defCache) so | ||
// that future definitions will return the same type given the same name. | ||
// In particular, this system allows for circular and forward definitions. | ||
// The Def object d returned from Type.def may be used to configure the | ||
// type d.type by calling methods such as d.bases, d.build, and d.field. | ||
Type.def = function (typeName) { | ||
isString.assert(typeName); | ||
return hasOwn.call(defCache, typeName) | ||
? defCache[typeName] | ||
: defCache[typeName] = new Def(typeName); | ||
}; | ||
return value; | ||
}; | ||
// In order to return the same Def instance every time Type.def is called | ||
// with a particular name, those instances need to be stored in a cache. | ||
var defCache = Object.create(null); | ||
// Define a type whose name is registered in a namespace (the defCache) so | ||
// that future definitions will return the same type given the same name. | ||
// In particular, this system allows for circular and forward definitions. | ||
// The Def object d returned from Type.def may be used to configure the | ||
// type d.type by calling methods such as d.bases, d.build, and d.field. | ||
Type.def = function(typeName) { | ||
isString.assert(typeName); | ||
return hasOwn.call(defCache, typeName) | ||
? defCache[typeName] | ||
: defCache[typeName] = new Def(typeName); | ||
}; | ||
function Def(typeName) { | ||
var self = this; | ||
if (!(self instanceof Def)) { | ||
throw new Error("Def constructor cannot be invoked without 'new'"); | ||
} | ||
// In order to return the same Def instance every time Type.def is called | ||
// with a particular name, those instances need to be stored in a cache. | ||
var defCache = Object.create(null); | ||
Object.defineProperties(self, { | ||
typeName: {value: typeName}, | ||
baseNames: {value: []}, | ||
ownFields: {value: Object.create(null)}, | ||
function Def(typeName) { | ||
var self = this; | ||
if (!(self instanceof Def)) { | ||
throw new Error("Def constructor cannot be invoked without 'new'"); | ||
// These two are populated during finalization. | ||
allSupertypes: {value: Object.create(null)}, // Includes own typeName. | ||
supertypeList: {value: []}, // Linear inheritance hierarchy. | ||
allFields: {value: Object.create(null)}, // Includes inherited fields. | ||
fieldNames: {value: []}, // Non-hidden keys of allFields. | ||
type: { | ||
value: new Type(function (value, deep) { | ||
return self.check(value, deep); | ||
}, typeName) | ||
} | ||
}); | ||
} | ||
Object.defineProperties(self, { | ||
typeName: { value: typeName }, | ||
baseNames: { value: [] }, | ||
ownFields: { value: Object.create(null) }, | ||
Def.fromValue = function (value) { | ||
if (value && typeof value === "object") { | ||
var type = value.type; | ||
if (typeof type === "string" && | ||
hasOwn.call(defCache, type)) { | ||
var d = defCache[type]; | ||
if (d.finalized) { | ||
return d; | ||
} | ||
} | ||
} | ||
// These two are populated during finalization. | ||
allSupertypes: { value: Object.create(null) }, // Includes own typeName. | ||
supertypeList: { value: [] }, // Linear inheritance hierarchy. | ||
allFields: { value: Object.create(null) }, // Includes inherited fields. | ||
fieldNames: { value: [] }, // Non-hidden keys of allFields. | ||
return null; | ||
}; | ||
type: { | ||
value: new Type(function(value, deep) { | ||
return self.check(value, deep); | ||
}, typeName) | ||
} | ||
}); | ||
} | ||
var Dp = Def.prototype; | ||
Def.fromValue = function(value) { | ||
if (value && typeof value === "object") { | ||
var type = value.type; | ||
if (typeof type === "string" && | ||
hasOwn.call(defCache, type)) { | ||
var d = defCache[type]; | ||
if (d.finalized) { | ||
return d; | ||
Dp.isSupertypeOf = function (that) { | ||
if (that instanceof Def) { | ||
if (this.finalized !== true || | ||
that.finalized !== true) { | ||
throw new Error(""); | ||
} | ||
return hasOwn.call(that.allSupertypes, this.typeName); | ||
} else { | ||
throw new Error(that + " is not a Def"); | ||
} | ||
} | ||
}; | ||
return null; | ||
}; | ||
var Dp = Def.prototype; | ||
Dp.isSupertypeOf = function(that) { | ||
if (that instanceof Def) { | ||
if (this.finalized !== true || | ||
that.finalized !== true) { | ||
// Note that the list returned by this function is a copy of the internal | ||
// supertypeList, *without* the typeName itself as the first element. | ||
exports.getSupertypeNames = function (typeName) { | ||
if (!hasOwn.call(defCache, typeName)) { | ||
throw new Error(""); | ||
} | ||
return hasOwn.call(that.allSupertypes, this.typeName); | ||
} else { | ||
throw new Error(that + " is not a Def"); | ||
} | ||
}; | ||
// Note that the list returned by this function is a copy of the internal | ||
// supertypeList, *without* the typeName itself as the first element. | ||
exports.getSupertypeNames = function(typeName) { | ||
if (!hasOwn.call(defCache, typeName)) { | ||
throw new Error(""); | ||
} | ||
var d = defCache[typeName]; | ||
if (d.finalized !== true) { | ||
throw new Error(""); | ||
} | ||
return d.supertypeList.slice(1); | ||
}; | ||
// Returns an object mapping from every known type in the defCache to the | ||
// most specific supertype whose name is an own property of the candidates | ||
// object. | ||
exports.computeSupertypeLookupTable = function(candidates) { | ||
var table = {}; | ||
var typeNames = Object.keys(defCache); | ||
var typeNameCount = typeNames.length; | ||
for (var i = 0; i < typeNameCount; ++i) { | ||
var typeName = typeNames[i]; | ||
var d = defCache[typeName]; | ||
if (d.finalized !== true) { | ||
throw new Error("" + typeName); | ||
throw new Error(""); | ||
} | ||
for (var j = 0; j < d.supertypeList.length; ++j) { | ||
var superTypeName = d.supertypeList[j]; | ||
if (hasOwn.call(candidates, superTypeName)) { | ||
table[typeName] = superTypeName; | ||
break; | ||
return d.supertypeList.slice(1); | ||
}; | ||
// Returns an object mapping from every known type in the defCache to the | ||
// most specific supertype whose name is an own property of the candidates | ||
// object. | ||
exports.computeSupertypeLookupTable = function (candidates) { | ||
var table = {}; | ||
var typeNames = Object.keys(defCache); | ||
var typeNameCount = typeNames.length; | ||
for (var i = 0; i < typeNameCount; ++i) { | ||
var typeName = typeNames[i]; | ||
var d = defCache[typeName]; | ||
if (d.finalized !== true) { | ||
throw new Error("" + typeName); | ||
} | ||
for (var j = 0; j < d.supertypeList.length; ++j) { | ||
var superTypeName = d.supertypeList[j]; | ||
if (hasOwn.call(candidates, superTypeName)) { | ||
table[typeName] = superTypeName; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return table; | ||
}; | ||
return table; | ||
}; | ||
Dp.checkAllFields = function(value, deep) { | ||
var allFields = this.allFields; | ||
if (this.finalized !== true) { | ||
throw new Error("" + this.typeName); | ||
} | ||
Dp.checkAllFields = function (value, deep) { | ||
var allFields = this.allFields; | ||
if (this.finalized !== true) { | ||
throw new Error("" + this.typeName); | ||
} | ||
function checkFieldByName(name) { | ||
var field = allFields[name]; | ||
var type = field.type; | ||
var child = field.getValue(value); | ||
return type.check(child, deep); | ||
} | ||
function checkFieldByName(name) { | ||
var field = allFields[name]; | ||
var type = field.type; | ||
var child = field.getValue(value); | ||
return type.check(child, deep); | ||
} | ||
return isObject.check(value) | ||
&& Object.keys(allFields).every(checkFieldByName); | ||
}; | ||
return isObject.check(value) | ||
&& Object.keys(allFields).every(checkFieldByName); | ||
}; | ||
Dp.check = function(value, deep) { | ||
if (this.finalized !== true) { | ||
throw new Error( | ||
"prematurely checking unfinalized type " + this.typeName | ||
); | ||
} | ||
Dp.check = function (value, deep) { | ||
if (this.finalized !== true) { | ||
throw new Error( | ||
"prematurely checking unfinalized type " + this.typeName | ||
); | ||
} | ||
// A Def type can only match an object value. | ||
if (!isObject.check(value)) | ||
return false; | ||
// A Def type can only match an object value. | ||
if (!isObject.check(value)) | ||
return false; | ||
var vDef = Def.fromValue(value); | ||
if (!vDef) { | ||
// If we couldn't infer the Def associated with the given value, | ||
// and we expected it to be a SourceLocation or a Position, it was | ||
// probably just missing a "type" field (because Esprima does not | ||
// assign a type property to such nodes). Be optimistic and let | ||
// this.checkAllFields make the final decision. | ||
if (this.typeName === "SourceLocation" || | ||
this.typeName === "Position") { | ||
return this.checkAllFields(value, deep); | ||
var vDef = Def.fromValue(value); | ||
if (!vDef) { | ||
// If we couldn't infer the Def associated with the given value, | ||
// and we expected it to be a SourceLocation or a Position, it was | ||
// probably just missing a "type" field (because Esprima does not | ||
// assign a type property to such nodes). Be optimistic and let | ||
// this.checkAllFields make the final decision. | ||
if (this.typeName === "SourceLocation" || | ||
this.typeName === "Position") { | ||
return this.checkAllFields(value, deep); | ||
} | ||
// Calling this.checkAllFields for any other type of node is both | ||
// bad for performance and way too forgiving. | ||
return false; | ||
} | ||
// Calling this.checkAllFields for any other type of node is both | ||
// bad for performance and way too forgiving. | ||
return false; | ||
} | ||
// If checking deeply and vDef === this, then we only need to call | ||
// checkAllFields once. Calling checkAllFields is too strict when deep | ||
// is false, because then we only care about this.isSupertypeOf(vDef). | ||
if (deep && vDef === this) | ||
return this.checkAllFields(value, deep); | ||
// If checking deeply and vDef === this, then we only need to call | ||
// checkAllFields once. Calling checkAllFields is too strict when deep | ||
// is false, because then we only care about this.isSupertypeOf(vDef). | ||
if (deep && vDef === this) | ||
return this.checkAllFields(value, deep); | ||
// In most cases we rely exclusively on isSupertypeOf to make O(1) | ||
// subtyping determinations. This suffices in most situations outside | ||
// of unit tests, since interface conformance is checked whenever new | ||
// instances are created using builder functions. | ||
if (!this.isSupertypeOf(vDef)) | ||
return false; | ||
// In most cases we rely exclusively on isSupertypeOf to make O(1) | ||
// subtyping determinations. This suffices in most situations outside | ||
// of unit tests, since interface conformance is checked whenever new | ||
// instances are created using builder functions. | ||
if (!this.isSupertypeOf(vDef)) | ||
return false; | ||
// The exception is when deep is true; then, we recursively check all | ||
// fields. | ||
if (!deep) | ||
return true; | ||
// The exception is when deep is true; then, we recursively check all | ||
// fields. | ||
if (!deep) | ||
return true; | ||
// Use the more specific Def (vDef) to perform the deep check, but | ||
// shallow-check fields defined by the less specific Def (this). | ||
return vDef.checkAllFields(value, deep) | ||
&& this.checkAllFields(value, false); | ||
}; | ||
// Use the more specific Def (vDef) to perform the deep check, but | ||
// shallow-check fields defined by the less specific Def (this). | ||
return vDef.checkAllFields(value, deep) | ||
&& this.checkAllFields(value, false); | ||
}; | ||
Dp.bases = function () { | ||
var args = slice.call(arguments); | ||
var bases = this.baseNames; | ||
Dp.bases = function() { | ||
var args = slice.call(arguments); | ||
var bases = this.baseNames; | ||
if (this.finalized) { | ||
if (args.length !== bases.length) { | ||
throw new Error(""); | ||
} | ||
for (var i = 0; i < args.length; i++) { | ||
if (args[i] !== bases[i]) { | ||
if (this.finalized) { | ||
if (args.length !== bases.length) { | ||
throw new Error(""); | ||
} | ||
for (var i = 0; i < args.length; i++) { | ||
if (args[i] !== bases[i]) { | ||
throw new Error(""); | ||
} | ||
} | ||
return this; | ||
} | ||
return this; | ||
} | ||
args.forEach(function(baseName) { | ||
isString.assert(baseName); | ||
args.forEach(function (baseName) { | ||
isString.assert(baseName); | ||
// This indexOf lookup may be O(n), but the typical number of base | ||
// names is very small, and indexOf is a native Array method. | ||
if (bases.indexOf(baseName) < 0) | ||
bases.push(baseName); | ||
}); | ||
// This indexOf lookup may be O(n), but the typical number of base | ||
// names is very small, and indexOf is a native Array method. | ||
if (bases.indexOf(baseName) < 0) | ||
bases.push(baseName); | ||
}); | ||
return this; // For chaining. | ||
}; | ||
return this; // For chaining. | ||
}; | ||
// False by default until .build(...) is called on an instance. | ||
Object.defineProperty(Dp, "buildable", { value: false }); | ||
// False by default until .build(...) is called on an instance. | ||
Object.defineProperty(Dp, "buildable", {value: false}); | ||
var builders = {}; | ||
exports.builders = builders; | ||
var builders = {}; | ||
exports.builders = builders; | ||
// This object is used as prototype for any node created by a builder. | ||
var nodePrototype = {}; | ||
// This object is used as prototype for any node created by a builder. | ||
var nodePrototype = {}; | ||
// Call this function to define a new method to be shared by all AST | ||
// nodes. The replaced method (if any) is returned for easy wrapping. | ||
exports.defineMethod = function(name, func) { | ||
var old = nodePrototype[name]; | ||
// Call this function to define a new method to be shared by all AST | ||
// nodes. The replaced method (if any) is returned for easy wrapping. | ||
exports.defineMethod = function (name, func) { | ||
var old = nodePrototype[name]; | ||
// Pass undefined as func to delete nodePrototype[name]. | ||
if (isUndefined.check(func)) { | ||
delete nodePrototype[name]; | ||
// Pass undefined as func to delete nodePrototype[name]. | ||
if (isUndefined.check(func)) { | ||
delete nodePrototype[name]; | ||
} else { | ||
isFunction.assert(func); | ||
} else { | ||
isFunction.assert(func); | ||
Object.defineProperty(nodePrototype, name, { | ||
enumerable: true, // For discoverability. | ||
configurable: true, // For delete proto[name]. | ||
value: func | ||
}); | ||
} | ||
Object.defineProperty(nodePrototype, name, { | ||
enumerable: true, // For discoverability. | ||
configurable: true, // For delete proto[name]. | ||
value: func | ||
}); | ||
} | ||
return old; | ||
}; | ||
return old; | ||
}; | ||
var isArrayOfString = isString.arrayOf(); | ||
var isArrayOfString = isString.arrayOf(); | ||
// Calling the .build method of a Def simultaneously marks the type as | ||
// buildable (by defining builders[getBuilderName(typeName)]) and | ||
// specifies the order of arguments that should be passed to the builder | ||
// function to create an instance of the type. | ||
Dp.build = function(/* param1, param2, ... */) { | ||
var self = this; | ||
// Calling the .build method of a Def simultaneously marks the type as | ||
// buildable (by defining builders[getBuilderName(typeName)]) and | ||
// specifies the order of arguments that should be passed to the builder | ||
// function to create an instance of the type. | ||
Dp.build = function (/* param1, param2, ... */) { | ||
var self = this; | ||
var newBuildParams = slice.call(arguments); | ||
isArrayOfString.assert(newBuildParams); | ||
var newBuildParams = slice.call(arguments); | ||
isArrayOfString.assert(newBuildParams); | ||
// Calling Def.prototype.build multiple times has the effect of merely | ||
// redefining this property. | ||
Object.defineProperty(self, "buildParams", { | ||
value: newBuildParams, | ||
writable: false, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
// Calling Def.prototype.build multiple times has the effect of merely | ||
// redefining this property. | ||
Object.defineProperty(self, "buildParams", { | ||
value: newBuildParams, | ||
writable: false, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
if (self.buildable) { | ||
// If this Def is already buildable, update self.buildParams and | ||
// continue using the old builder function. | ||
return self; | ||
} | ||
if (self.buildable) { | ||
// If this Def is already buildable, update self.buildParams and | ||
// continue using the old builder function. | ||
return self; | ||
} | ||
// Every buildable type will have its "type" field filled in | ||
// automatically. This includes types that are not subtypes of Node, | ||
// like SourceLocation, but that seems harmless (TODO?). | ||
self.field("type", String, function() { return self.typeName }); | ||
// Every buildable type will have its "type" field filled in | ||
// automatically. This includes types that are not subtypes of Node, | ||
// like SourceLocation, but that seems harmless (TODO?). | ||
self.field("type", String, function () { return self.typeName }); | ||
// Override Dp.buildable for this Def instance. | ||
Object.defineProperty(self, "buildable", { value: true }); | ||
// Override Dp.buildable for this Def instance. | ||
Object.defineProperty(self, "buildable", {value: true}); | ||
Object.defineProperty(builders, getBuilderName(self.typeName), { | ||
enumerable: true, | ||
Object.defineProperty(builders, getBuilderName(self.typeName), { | ||
enumerable: true, | ||
value: function() { | ||
var args = arguments; | ||
var argc = args.length; | ||
var built = Object.create(nodePrototype); | ||
value: function () { | ||
var args = arguments; | ||
var argc = args.length; | ||
var built = Object.create(nodePrototype); | ||
if (!self.finalized) { | ||
throw new Error( | ||
"attempting to instantiate unfinalized type " + | ||
self.typeName | ||
); | ||
} | ||
if (!self.finalized) { | ||
throw new Error( | ||
"attempting to instantiate unfinalized type " + | ||
self.typeName | ||
); | ||
} | ||
function add(param, i) { | ||
if (hasOwn.call(built, param)) | ||
return; | ||
function add(param, i) { | ||
if (hasOwn.call(built, param)) | ||
return; | ||
var all = self.allFields; | ||
if (!hasOwn.call(all, param)) { | ||
throw new Error("" + param); | ||
} | ||
var all = self.allFields; | ||
if (!hasOwn.call(all, param)) { | ||
throw new Error("" + param); | ||
} | ||
var field = all[param]; | ||
var type = field.type; | ||
var value; | ||
var field = all[param]; | ||
var type = field.type; | ||
var value; | ||
if (isNumber.check(i) && i < argc) { | ||
value = args[i]; | ||
} else if (field.defaultFn) { | ||
// Expose the partially-built object to the default | ||
// function as its `this` object. | ||
value = field.defaultFn.call(built); | ||
} else { | ||
var message = "no value or default function given for field " + | ||
JSON.stringify(param) + " of " + self.typeName + "(" + | ||
self.buildParams.map(function(name) { | ||
return all[name]; | ||
}).join(", ") + ")"; | ||
throw new Error(message); | ||
if (isNumber.check(i) && i < argc) { | ||
value = args[i]; | ||
} else if (field.defaultFn) { | ||
// Expose the partially-built object to the default | ||
// function as its `this` object. | ||
value = field.defaultFn.call(built); | ||
} else { | ||
var message = "no value or default function given for field " + | ||
JSON.stringify(param) + " of " + self.typeName + "(" + | ||
self.buildParams.map(function (name) { | ||
return all[name]; | ||
}).join(", ") + ")"; | ||
throw new Error(message); | ||
} | ||
if (!type.check(value)) { | ||
throw new Error( | ||
shallowStringify(value) + | ||
" does not match field " + field + | ||
" of type " + self.typeName | ||
); | ||
} | ||
// TODO Could attach getters and setters here to enforce | ||
// dynamic type safety. | ||
built[param] = value; | ||
} | ||
if (!type.check(value)) { | ||
throw new Error( | ||
shallowStringify(value) + | ||
" does not match field " + field + | ||
" of type " + self.typeName | ||
); | ||
self.buildParams.forEach(function (param, i) { | ||
add(param, i); | ||
}); | ||
Object.keys(self.allFields).forEach(function (param) { | ||
add(param); // Use the default value. | ||
}); | ||
// Make sure that the "type" field was filled automatically. | ||
if (built.type !== self.typeName) { | ||
throw new Error(""); | ||
} | ||
// TODO Could attach getters and setters here to enforce | ||
// dynamic type safety. | ||
built[param] = value; | ||
return built; | ||
} | ||
}); | ||
self.buildParams.forEach(function(param, i) { | ||
add(param, i); | ||
}); | ||
return self; // For chaining. | ||
}; | ||
Object.keys(self.allFields).forEach(function(param) { | ||
add(param); // Use the default value. | ||
}); | ||
// Make sure that the "type" field was filled automatically. | ||
if (built.type !== self.typeName) { | ||
throw new Error(""); | ||
function getBuilderName(typeName) { | ||
return typeName.replace(/^[A-Z]+/, function (upperCasePrefix) { | ||
var len = upperCasePrefix.length; | ||
switch (len) { | ||
case 0: return ""; | ||
// If there's only one initial capital letter, just lower-case it. | ||
case 1: return upperCasePrefix.toLowerCase(); | ||
default: | ||
// If there's more than one initial capital letter, lower-case | ||
// all but the last one, so that XMLDefaultDeclaration (for | ||
// example) becomes xmlDefaultDeclaration. | ||
return upperCasePrefix.slice( | ||
0, len - 1).toLowerCase() + | ||
upperCasePrefix.charAt(len - 1); | ||
} | ||
}); | ||
} | ||
exports.getBuilderName = getBuilderName; | ||
return built; | ||
function getStatementBuilderName(typeName) { | ||
typeName = getBuilderName(typeName); | ||
return typeName.replace(/(Expression)?$/, "Statement"); | ||
} | ||
exports.getStatementBuilderName = getStatementBuilderName; | ||
// The reason fields are specified using .field(...) instead of an object | ||
// literal syntax is somewhat subtle: the object literal syntax would | ||
// support only one key and one value, but with .field(...) we can pass | ||
// any number of arguments to specify the field. | ||
Dp.field = function (name, type, defaultFn, hidden) { | ||
if (this.finalized) { | ||
console.error("Ignoring attempt to redefine field " + | ||
JSON.stringify(name) + " of finalized type " + | ||
JSON.stringify(this.typeName)); | ||
return this; | ||
} | ||
}); | ||
this.ownFields[name] = new Field(name, type, defaultFn, hidden); | ||
return this; // For chaining. | ||
}; | ||
return self; // For chaining. | ||
}; | ||
var namedTypes = {}; | ||
exports.namedTypes = namedTypes; | ||
function getBuilderName(typeName) { | ||
return typeName.replace(/^[A-Z]+/, function(upperCasePrefix) { | ||
var len = upperCasePrefix.length; | ||
switch (len) { | ||
case 0: return ""; | ||
// If there's only one initial capital letter, just lower-case it. | ||
case 1: return upperCasePrefix.toLowerCase(); | ||
default: | ||
// If there's more than one initial capital letter, lower-case | ||
// all but the last one, so that XMLDefaultDeclaration (for | ||
// example) becomes xmlDefaultDeclaration. | ||
return upperCasePrefix.slice( | ||
0, len - 1).toLowerCase() + | ||
upperCasePrefix.charAt(len - 1); | ||
// Like Object.keys, but aware of what fields each AST type should have. | ||
function getFieldNames(object) { | ||
var d = Def.fromValue(object); | ||
if (d) { | ||
return d.fieldNames.slice(0); | ||
} | ||
}); | ||
} | ||
exports.getBuilderName = getBuilderName; | ||
function getStatementBuilderName(typeName) { | ||
typeName = getBuilderName(typeName); | ||
return typeName.replace(/(Expression)?$/, "Statement"); | ||
} | ||
exports.getStatementBuilderName = getStatementBuilderName; | ||
if ("type" in object) { | ||
throw new Error( | ||
"did not recognize object of type " + | ||
JSON.stringify(object.type) | ||
); | ||
} | ||
// The reason fields are specified using .field(...) instead of an object | ||
// literal syntax is somewhat subtle: the object literal syntax would | ||
// support only one key and one value, but with .field(...) we can pass | ||
// any number of arguments to specify the field. | ||
Dp.field = function(name, type, defaultFn, hidden) { | ||
if (this.finalized) { | ||
console.error("Ignoring attempt to redefine field " + | ||
JSON.stringify(name) + " of finalized type " + | ||
JSON.stringify(this.typeName)); | ||
return this; | ||
return Object.keys(object); | ||
} | ||
this.ownFields[name] = new Field(name, type, defaultFn, hidden); | ||
return this; // For chaining. | ||
}; | ||
exports.getFieldNames = getFieldNames; | ||
var namedTypes = {}; | ||
exports.namedTypes = namedTypes; | ||
// Get the value of an object property, taking object.type and default | ||
// functions into account. | ||
function getFieldValue(object, fieldName) { | ||
var d = Def.fromValue(object); | ||
if (d) { | ||
var field = d.allFields[fieldName]; | ||
if (field) { | ||
return field.getValue(object); | ||
} | ||
} | ||
// Like Object.keys, but aware of what fields each AST type should have. | ||
function getFieldNames(object) { | ||
var d = Def.fromValue(object); | ||
if (d) { | ||
return d.fieldNames.slice(0); | ||
return object[fieldName]; | ||
} | ||
exports.getFieldValue = getFieldValue; | ||
if ("type" in object) { | ||
throw new Error( | ||
"did not recognize object of type " + | ||
JSON.stringify(object.type) | ||
); | ||
} | ||
// Iterate over all defined fields of an object, including those missing | ||
// or undefined, passing each field name and effective value (as returned | ||
// by getFieldValue) to the callback. If the object has no corresponding | ||
// Def, the callback will never be called. | ||
exports.eachField = function (object, callback, context) { | ||
getFieldNames(object).forEach(function (name) { | ||
callback.call(this, name, getFieldValue(object, name)); | ||
}, context); | ||
}; | ||
return Object.keys(object); | ||
} | ||
exports.getFieldNames = getFieldNames; | ||
// Similar to eachField, except that iteration stops as soon as the | ||
// callback returns a truthy value. Like Array.prototype.some, the final | ||
// result is either true or false to indicates whether the callback | ||
// returned true for any element or not. | ||
exports.someField = function (object, callback, context) { | ||
return getFieldNames(object).some(function (name) { | ||
return callback.call(this, name, getFieldValue(object, name)); | ||
}, context); | ||
}; | ||
// Get the value of an object property, taking object.type and default | ||
// functions into account. | ||
function getFieldValue(object, fieldName) { | ||
var d = Def.fromValue(object); | ||
if (d) { | ||
var field = d.allFields[fieldName]; | ||
if (field) { | ||
return field.getValue(object); | ||
} | ||
} | ||
// This property will be overridden as true by individual Def instances | ||
// when they are finalized. | ||
Object.defineProperty(Dp, "finalized", {value: false}); | ||
return object[fieldName]; | ||
} | ||
exports.getFieldValue = getFieldValue; | ||
Dp.finalize = function () { | ||
var self = this; | ||
// Iterate over all defined fields of an object, including those missing | ||
// or undefined, passing each field name and effective value (as returned | ||
// by getFieldValue) to the callback. If the object has no corresponding | ||
// Def, the callback will never be called. | ||
exports.eachField = function(object, callback, context) { | ||
getFieldNames(object).forEach(function(name) { | ||
callback.call(this, name, getFieldValue(object, name)); | ||
}, context); | ||
}; | ||
// It's not an error to finalize a type more than once, but only the | ||
// first call to .finalize does anything. | ||
if (!self.finalized) { | ||
var allFields = self.allFields; | ||
var allSupertypes = self.allSupertypes; | ||
// Similar to eachField, except that iteration stops as soon as the | ||
// callback returns a truthy value. Like Array.prototype.some, the final | ||
// result is either true or false to indicates whether the callback | ||
// returned true for any element or not. | ||
exports.someField = function(object, callback, context) { | ||
return getFieldNames(object).some(function(name) { | ||
return callback.call(this, name, getFieldValue(object, name)); | ||
}, context); | ||
}; | ||
self.baseNames.forEach(function (name) { | ||
var def = defCache[name]; | ||
if (def instanceof Def) { | ||
def.finalize(); | ||
extend(allFields, def.allFields); | ||
extend(allSupertypes, def.allSupertypes); | ||
} else { | ||
var message = "unknown supertype name " + | ||
JSON.stringify(name) + | ||
" for subtype " + | ||
JSON.stringify(self.typeName); | ||
throw new Error(message); | ||
} | ||
}); | ||
// This property will be overridden as true by individual Def instances | ||
// when they are finalized. | ||
Object.defineProperty(Dp, "finalized", { value: false }); | ||
// TODO Warn if fields are overridden with incompatible types. | ||
extend(allFields, self.ownFields); | ||
allSupertypes[self.typeName] = self; | ||
Dp.finalize = function() { | ||
var self = this; | ||
self.fieldNames.length = 0; | ||
for (var fieldName in allFields) { | ||
if (hasOwn.call(allFields, fieldName) && | ||
!allFields[fieldName].hidden) { | ||
self.fieldNames.push(fieldName); | ||
} | ||
} | ||
// It's not an error to finalize a type more than once, but only the | ||
// first call to .finalize does anything. | ||
if (!self.finalized) { | ||
var allFields = self.allFields; | ||
var allSupertypes = self.allSupertypes; | ||
// Types are exported only once they have been finalized. | ||
Object.defineProperty(namedTypes, self.typeName, { | ||
enumerable: true, | ||
value: self.type | ||
}); | ||
self.baseNames.forEach(function(name) { | ||
var def = defCache[name]; | ||
if (def instanceof Def) { | ||
def.finalize(); | ||
extend(allFields, def.allFields); | ||
extend(allSupertypes, def.allSupertypes); | ||
} else { | ||
var message = "unknown supertype name " + | ||
JSON.stringify(name) + | ||
" for subtype " + | ||
JSON.stringify(self.typeName); | ||
throw new Error(message); | ||
} | ||
}); | ||
Object.defineProperty(self, "finalized", {value: true}); | ||
// TODO Warn if fields are overridden with incompatible types. | ||
extend(allFields, self.ownFields); | ||
allSupertypes[self.typeName] = self; | ||
// A linearization of the inheritance hierarchy. | ||
populateSupertypeList(self.typeName, self.supertypeList); | ||
self.fieldNames.length = 0; | ||
for (var fieldName in allFields) { | ||
if (hasOwn.call(allFields, fieldName) && | ||
!allFields[fieldName].hidden) { | ||
self.fieldNames.push(fieldName); | ||
if (self.buildable && self.supertypeList.lastIndexOf("Expression") >= 0) { | ||
wrapExpressionBuilderWithStatement(self.typeName); | ||
} | ||
} | ||
}; | ||
// Types are exported only once they have been finalized. | ||
Object.defineProperty(namedTypes, self.typeName, { | ||
enumerable: true, | ||
value: self.type | ||
}); | ||
// Adds an additional builder for Expression subtypes | ||
// that wraps the built Expression in an ExpressionStatements. | ||
function wrapExpressionBuilderWithStatement(typeName) { | ||
var wrapperName = getStatementBuilderName(typeName); | ||
Object.defineProperty(self, "finalized", { value: true }); | ||
// skip if the builder already exists | ||
if (builders[wrapperName]) return; | ||
// A linearization of the inheritance hierarchy. | ||
populateSupertypeList(self.typeName, self.supertypeList); | ||
// the builder function to wrap with builders.ExpressionStatement | ||
var wrapped = builders[getBuilderName(typeName)]; | ||
if (self.buildable && self.supertypeList.lastIndexOf("Expression") >= 0) { | ||
wrapExpressionBuilderWithStatement(self.typeName); | ||
} | ||
// skip if there is nothing to wrap | ||
if (!wrapped) return; | ||
builders[wrapperName] = function () { | ||
return builders.expressionStatement(wrapped.apply(builders, arguments)); | ||
}; | ||
} | ||
}; | ||
// Adds an additional builder for Expression subtypes | ||
// that wraps the built Expression in an ExpressionStatements. | ||
function wrapExpressionBuilderWithStatement(typeName) { | ||
var wrapperName = getStatementBuilderName(typeName); | ||
function populateSupertypeList(typeName, list) { | ||
list.length = 0; | ||
list.push(typeName); | ||
// skip if the builder already exists | ||
if (builders[wrapperName]) return; | ||
var lastSeen = Object.create(null); | ||
// the builder function to wrap with builders.ExpressionStatement | ||
var wrapped = builders[getBuilderName(typeName)]; | ||
for (var pos = 0; pos < list.length; ++pos) { | ||
typeName = list[pos]; | ||
var d = defCache[typeName]; | ||
if (d.finalized !== true) { | ||
throw new Error(""); | ||
} | ||
// skip if there is nothing to wrap | ||
if (!wrapped) return; | ||
// If we saw typeName earlier in the breadth-first traversal, | ||
// delete the last-seen occurrence. | ||
if (hasOwn.call(lastSeen, typeName)) { | ||
delete list[lastSeen[typeName]]; | ||
} | ||
builders[wrapperName] = function() { | ||
return builders.expressionStatement(wrapped.apply(builders, arguments)); | ||
}; | ||
} | ||
// Record the new index of the last-seen occurrence of typeName. | ||
lastSeen[typeName] = pos; | ||
function populateSupertypeList(typeName, list) { | ||
list.length = 0; | ||
list.push(typeName); | ||
var lastSeen = Object.create(null); | ||
for (var pos = 0; pos < list.length; ++pos) { | ||
typeName = list[pos]; | ||
var d = defCache[typeName]; | ||
if (d.finalized !== true) { | ||
throw new Error(""); | ||
// Enqueue the base names of this type. | ||
list.push.apply(list, d.baseNames); | ||
} | ||
// If we saw typeName earlier in the breadth-first traversal, | ||
// delete the last-seen occurrence. | ||
if (hasOwn.call(lastSeen, typeName)) { | ||
delete list[lastSeen[typeName]]; | ||
// Compaction loop to remove array holes. | ||
for (var to = 0, from = to, len = list.length; from < len; ++from) { | ||
if (hasOwn.call(list, from)) { | ||
list[to++] = list[from]; | ||
} | ||
} | ||
// Record the new index of the last-seen occurrence of typeName. | ||
lastSeen[typeName] = pos; | ||
// Enqueue the base names of this type. | ||
list.push.apply(list, d.baseNames); | ||
list.length = to; | ||
} | ||
// Compaction loop to remove array holes. | ||
for (var to = 0, from = to, len = list.length; from < len; ++from) { | ||
if (hasOwn.call(list, from)) { | ||
list[to++] = list[from]; | ||
} | ||
} | ||
function extend(into, from) { | ||
Object.keys(from).forEach(function (name) { | ||
into[name] = from[name]; | ||
}); | ||
list.length = to; | ||
} | ||
return into; | ||
}; | ||
function extend(into, from) { | ||
Object.keys(from).forEach(function(name) { | ||
into[name] = from[name]; | ||
}); | ||
exports.finalize = function () { | ||
Object.keys(defCache).forEach(function (name) { | ||
defCache[name].finalize(); | ||
}); | ||
}; | ||
return into; | ||
return exports; | ||
}; | ||
exports.finalize = function() { | ||
Object.keys(defCache).forEach(function(name) { | ||
defCache[name].finalize(); | ||
}); | ||
}; |
48
main.js
@@ -1,34 +0,16 @@ | ||
var types = require("./lib/types"); | ||
module.exports = require('./fork')([ | ||
// This core module of AST types captures ES5 as it is parsed today by | ||
// git://github.com/ariya/esprima.git#master. | ||
require("./def/core"), | ||
// This core module of AST types captures ES5 as it is parsed today by | ||
// git://github.com/ariya/esprima.git#master. | ||
require("./def/core"); | ||
// Feel free to add to or remove from this list of extension modules to | ||
// configure the precise type hierarchy that you need. | ||
require("./def/es6"); | ||
require("./def/es7"); | ||
require("./def/mozilla"); | ||
require("./def/e4x"); | ||
require("./def/jsx"); | ||
require("./def/flow"); | ||
require("./def/esprima"); | ||
require("./def/babel"); | ||
types.finalize(); | ||
exports.Type = types.Type; | ||
exports.builtInTypes = types.builtInTypes; | ||
exports.namedTypes = types.namedTypes; | ||
exports.builders = types.builders; | ||
exports.defineMethod = types.defineMethod; | ||
exports.getFieldNames = types.getFieldNames; | ||
exports.getFieldValue = types.getFieldValue; | ||
exports.eachField = types.eachField; | ||
exports.someField = types.someField; | ||
exports.getSupertypeNames = types.getSupertypeNames; | ||
exports.astNodesAreEquivalent = require("./lib/equiv"); | ||
exports.finalize = types.finalize; | ||
exports.NodePath = require("./lib/node-path"); | ||
exports.PathVisitor = require("./lib/path-visitor"); | ||
exports.visit = exports.PathVisitor.visit; | ||
// Feel free to add to or remove from this list of extension modules to | ||
// configure the precise type hierarchy that you need. | ||
require("./def/es6"), | ||
require("./def/es7"), | ||
require("./def/mozilla"), | ||
require("./def/e4x"), | ||
require("./def/jsx"), | ||
require("./def/flow"), | ||
require("./def/esprima"), | ||
require("./def/babel") | ||
]); |
@@ -21,3 +21,3 @@ { | ||
], | ||
"version": "0.8.18", | ||
"version": "0.9.0", | ||
"homepage": "http://github.com/benjamn/ast-types", | ||
@@ -24,0 +24,0 @@ "repository": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
150118
23
3387