Tie everything together in the new build system

This commit is contained in:
Marijn Haverbeke 2015-03-20 16:36:24 +01:00
parent cf613ce287
commit 6dd254d999
13 changed files with 385 additions and 4502 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
/node_modules /node_modules
/.tern-port /.tern-port
/acorn_csp.js
/local /local
/dist

2888
acorn.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,10 +31,18 @@ function acornShim(file) {
return tr return tr
} }
browserify() browserify({standalone: "acorn.loose"})
.transform(acornShim) .transform(acornShim)
.transform(babelify) .transform(babelify)
.require("./src/loose/index.js", {entry: true}) .require("./src/loose/index.js", {entry: true})
.bundle() .bundle()
.on("error", function (err) { console.log("Error: " + err.message) }) .on("error", function (err) { console.log("Error: " + err.message) })
.pipe(fs.createWriteStream("dist/acorn_loose.js")) .pipe(fs.createWriteStream("dist/acorn_loose.js"))
browserify({standalone: "acorn.walk"})
.transform(acornShim)
.transform(babelify)
.require("./src/walk/index.js", {entry: true})
.bundle()
.on("error", function (err) { console.log("Error: " + err.message) })
.pipe(fs.createWriteStream("dist/walk.js"))

2
bin/prepublish.sh Executable file
View File

@ -0,0 +1,2 @@
node bin/build-acorn.js
node bin/without_eval > dist/acorn_csp.js

View File

@ -1,45 +1,48 @@
#!/usr/bin/env node #!/usr/bin/env node
var fs = require("fs"); var fs = require("fs")
var acornSrc = fs.readFileSync(require.resolve("../acorn"), "utf8"); var acornSrc = fs.readFileSync(require.resolve("../dist/acorn"), "utf8")
var acorn = require("../acorn"), walk = require("../util/walk"); var acorn = require("../dist/acorn"), walk = require("../dist/walk")
var ast = acorn.parse(acornSrc); var ast = acorn.parse(acornSrc)
var touchups = [], uses = []; var touchups = [], uses = []
var makePred
walk.simple(ast, { walk.simple(ast, {
FunctionDeclaration: function(node) { FunctionDeclaration: function(node) {
if (node.id.name == "makePredicate") if (node.id.name == "makePredicate") {
touchups.push({text: "// Removed to create an eval-free library", from: node.start, to: node.end}); makePred = node
touchups.push({text: "// Removed to create an eval-free library", from: node.start, to: node.end})
}
}, },
VariableDeclaration: function(node) { ObjectExpression: function(node) {
node.declarations.forEach(function(decl) { node.properties.forEach(function(prop) {
if (decl.init && decl.init.type == "CallExpression" && if (prop.value.type == "CallExpression" &&
decl.init.callee.name == "makePredicate") prop.value.callee.name == "makePredicate")
uses.push(decl); uses.push(prop.value)
}); })
} }
}); })
var results = Object.create(null); var results = []
var functions = new Function("predicates", acornSrc.replace( var dryRun = acornSrc.slice(0, makePred.end) + "; makePredicate = (function(mp) {" +
/\}\);\s*$/, uses.map(function(decl) { "return function(words) { var r = mp(words); predicates.push(r); return r }})(makePredicate);" +
return "predicates[" + JSON.stringify(decl.id.name) + "] = " + decl.id.name + ";"; acornSrc.slice(makePred.end)
}).join("") + "});"))(results); ;(new Function("predicates", dryRun))(results)
uses.forEach(function(decl) { uses.forEach(function (node, i) {
touchups.push({text: results[decl.id.name].toString(), touchups.push({text: results[i].toString(), from: node.start, to: node.end})
from: decl.init.start, to: decl.init.end}); })
});
var result = "", pos = 0; var result = "", pos = 0
touchups.sort(function(a, b) { return a.from - b.from; }); touchups.sort(function(a, b) { return a.from - b.from })
touchups.forEach(function(touchup) { touchups.forEach(function(touchup) {
result += acornSrc.slice(pos, touchup.from); result += acornSrc.slice(pos, touchup.from)
result += touchup.text; result += touchup.text
pos = touchup.to; pos = touchup.to
}); })
result += acornSrc.slice(pos); result += acornSrc.slice(pos)
process.stdout.write(result); process.stdout.write(result)

View File

@ -1,22 +1,40 @@
{ {
"name": "acorn", "name": "acorn",
"description": "ECMAScript parser", "description": "ECMAScript parser",
"homepage": "http://marijnhaverbeke.nl/acorn/", "homepage": "http://marijnhaverbeke.nl/acorn/",
"main": "acorn.js", "main": "dist/acorn.js",
"version": "0.12.1", "version": "0.12.1",
"engines": {"node": ">=0.4.0"}, "engines": {
"maintainers": [{"name": "Marijn Haverbeke", "node": ">=0.4.0"
"email": "marijnh@gmail.com", },
"web": "http://marijnhaverbeke.nl"}], "maintainers": [
"repository": {"type": "git", {
"url": "http://marijnhaverbeke.nl/git/acorn"}, "name": "Marijn Haverbeke",
"licenses": [{"type": "MIT", "email": "marijnh@gmail.com",
"url": "http://marijnhaverbeke.nl/acorn/LICENSE"}], "web": "http://marijnhaverbeke.nl"
"scripts": { }
"test": "node test/run.js", ],
"prepublish": "node bin/without_eval > acorn_csp.js" "repository": {
}, "type": "git",
"bin": {"acorn": "./bin/acorn"}, "url": "http://marijnhaverbeke.nl/git/acorn"
"devDependencies": {"regenerate": "~0.6.2", },
"unicode-7.0.0": "~0.1.5"} "licenses": [
{
"type": "MIT",
"url": "http://marijnhaverbeke.nl/acorn/LICENSE"
}
],
"scripts": {
"test": "node test/run.js",
"prepublish": "node bin/prepublish.sh"
},
"bin": {
"acorn": "./bin/acorn"
},
"devDependencies": {
"babelify": "^5.0.4",
"browserify": "^9.0.3",
"regenerate": "~0.6.2",
"unicode-7.0.0": "~0.1.5"
}
} }

View File

@ -5,7 +5,7 @@ import {getLineInfo, tokTypes as tt} from ".."
const lp = LooseParser.prototype const lp = LooseParser.prototype
lp.parseTopLevel = function() { lp.parseTopLevel = function() {
var node = this.startNodeAt(this.options.locations ? [0, acorn.getLineInfo(this.input, 0)] : 0); var node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0);
node.body = []; node.body = [];
while (this.tok.type !== tt.eof) node.body.push(this.parseStatement()); while (this.tok.type !== tt.eof) node.body.push(this.parseStatement());
this.last = this.tok; this.last = this.tok;

291
src/walk/index.js Normal file
View File

@ -0,0 +1,291 @@
// AST walker module for Mozilla Parser API compatible trees
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
export function simple(node, visitors, base, state) {
if (!base) base = exports.base
;(function c(node, st, override) {
let type = override || node.type, found = visitors[type]
base[type](node, st, c)
if (found) found(node, st)
})(node, state)
}
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
export function ancestor(node, visitors, base, state) {
if (!base) base = exports.base
if (!state) state = []
;(function c(node, st, override) {
let type = override || node.type, found = visitors[type]
if (node != st[st.length - 1]) {
st = st.slice()
st.push(node)
}
base[type](node, st, c)
if (found) found(node, st)
})(node, state)
}
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
export function recursive(node, state, funcs, base) {
let visitor = funcs ? exports.make(funcs, base) : base
;(function c(node, st, override) {
visitor[override || node.type](node, st, c)
})(node, state)
}
function makeTest(test) {
if (typeof test == "string")
return type => type == test
else if (!test)
return () => true
else
return test
}
class Found {
constructor(node, state) { this.node = node; this.state = state }
}
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
export function findNodeAt(node, start, end, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
let type = override || node.type
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
base[type](node, st, c)
if (test(type, node) &&
(start == null || node.start == start) &&
(end == null || node.end == end))
throw new Found(node, st)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
export function findNodeAround(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
let type = override || node.type
if (node.start > pos || node.end < pos) return
base[type](node, st, c)
if (test(type, node)) throw new Found(node, st)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the outermost matching node after a given position.
export function findNodeAfter(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
if (node.end < pos) return
let type = override || node.type
if (node.start >= pos && test(type, node)) throw new Found(node, st)
base[type](node, st, c)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the outermost matching node before a given position.
export function findNodeBefore(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
let max
;(function c(node, st, override) {
if (node.start > pos) return
let type = override || node.type
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
max = new Found(node, st)
base[type](node, st, c)
})(node, state)
return max
}
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
export function make(funcs, base) {
if (!base) base = exports.base
let visitor = {}
for (var type in base) visitor[type] = base[type]
for (var type in funcs) visitor[type] = funcs[type]
return visitor
}
function skipThrough(node, st, c) { c(node, st) }
function ignore(_node, _st, _c) {}
// Node walkers.
export const base = {}
base.Program = base.BlockStatement = (node, st, c) => {
for (let i = 0; i < node.body.length; ++i)
c(node.body[i], st, "Statement")
}
base.Statement = skipThrough
base.EmptyStatement = ignore
base.ExpressionStatement = base.ParenthesizedExpression =
(node, st, c) => c(node.expression, st, "Expression")
base.IfStatement = (node, st, c) => {
c(node.test, st, "Expression")
c(node.consequent, st, "Statement")
if (node.alternate) c(node.alternate, st, "Statement")
}
base.LabeledStatement = (node, st, c) => c(node.body, st, "Statement")
base.BreakStatement = base.ContinueStatement = ignore
base.WithStatement = (node, st, c) => {
c(node.object, st, "Expression")
c(node.body, st, "Statement")
}
base.SwitchStatement = (node, st, c) => {
c(node.discriminant, st, "Expression")
for (let i = 0; i < node.cases.length; ++i) {
let cs = node.cases[i]
if (cs.test) c(cs.test, st, "Expression")
for (let j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement")
}
}
base.ReturnStatement = base.YieldExpression = (node, st, c) => {
if (node.argument) c(node.argument, st, "Expression")
}
base.ThrowStatement = base.SpreadElement = base.RestElement =
(node, st, c) => c(node.argument, st, "Expression")
base.TryStatement = (node, st, c) => {
c(node.block, st, "Statement")
if (node.handler) c(node.handler.body, st, "ScopeBody")
if (node.finalizer) c(node.finalizer, st, "Statement")
}
base.WhileStatement = base.DoWhileStatement = (node, st, c) => {
c(node.test, st, "Expression")
c(node.body, st, "Statement")
}
base.ForStatement = (node, st, c) => {
if (node.init) c(node.init, st, "ForInit")
if (node.test) c(node.test, st, "Expression")
if (node.update) c(node.update, st, "Expression")
c(node.body, st, "Statement")
}
base.ForInStatement = base.ForOfStatement = (node, st, c) => {
c(node.left, st, "ForInit")
c(node.right, st, "Expression")
c(node.body, st, "Statement")
}
base.ForInit = (node, st, c) => {
if (node.type == "VariableDeclaration") c(node, st)
else c(node, st, "Expression")
}
base.DebuggerStatement = ignore
base.FunctionDeclaration = (node, st, c) => c(node, st, "Function")
base.VariableDeclaration = (node, st, c) => {
for (let i = 0; i < node.declarations.length; ++i) {
let decl = node.declarations[i]
if (decl.init) c(decl.init, st, "Expression")
}
}
base.Function = (node, st, c) => c(node.body, st, "ScopeBody")
base.ScopeBody = (node, st, c) => c(node, st, "Statement")
base.Expression = skipThrough
base.ThisExpression = ignore
base.ArrayExpression = base.ArrayPattern = (node, st, c) => {
for (let i = 0; i < node.elements.length; ++i) {
let elt = node.elements[i]
if (elt) c(elt, st, "Expression")
}
}
base.ObjectExpression = base.ObjectPattern = (node, st, c) => {
for (let i = 0; i < node.properties.length; ++i)
c(node.properties[i], st)
}
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration
base.SequenceExpression = base.TemplateLiteral = (node, st, c) => {
for (let i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression")
}
base.UnaryExpression = base.UpdateExpression = (node, st, c) => {
c(node.argument, st, "Expression")
}
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = (node, st, c) => {
c(node.left, st, "Expression")
c(node.right, st, "Expression")
}
base.ConditionalExpression = (node, st, c) => {
c(node.test, st, "Expression")
c(node.consequent, st, "Expression")
c(node.alternate, st, "Expression")
}
base.NewExpression = base.CallExpression = (node, st, c) => {
c(node.callee, st, "Expression")
if (node.arguments) for (let i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression")
}
base.MemberExpression = (node, st, c) => {
c(node.object, st, "Expression")
if (node.computed) c(node.property, st, "Expression")
}
base.ExportDeclaration = (node, st, c) => c(node.declaration, st)
base.ImportDeclaration = (node, st, c) => {
for (let i = 0; i < node.specifiers.length; i++)
c(node.specifiers[i], st)
}
base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore
base.TaggedTemplateExpression = (node, st, c) => {
c(node.tag, st, "Expression")
c(node.quasi, st)
}
base.ClassDeclaration = base.ClassExpression = (node, st, c) => {
if (node.superClass) c(node.superClass, st, "Expression")
for (let i = 0; i < node.body.body.length; i++)
c(node.body.body[i], st)
}
base.MethodDefinition = base.Property = (node, st, c) => {
if (node.computed) c(node.key, st, "Expression")
c(node.value, st, "Expression")
}
base.ComprehensionExpression = (node, st, c) => {
for (let i = 0; i < node.blocks.length; i++)
c(node.blocks[i].right, st, "Expression")
c(node.body, st, "Expression")
}

View File

@ -2,8 +2,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Acorn test suite</title> <title>Acorn test suite</title>
<script src="../acorn.js"></script> <script src="../dist/acorn.js"></script>
<script src="../acorn_loose.js"></script> <script src="../dist/acorn_loose.js"></script>
<script src="driver.js"></script> <script src="driver.js"></script>
<script src="tests.js" charset="utf-8"></script> <script src="tests.js" charset="utf-8"></script>
<script src="tests-harmony.js" charset="utf-8"></script> <script src="tests-harmony.js" charset="utf-8"></script>

View File

@ -1,12 +1,16 @@
(function() { (function() {
var driver; var driver, acorn;
if (typeof require !== "undefined") { if (typeof require !== "undefined") {
driver = require("./driver.js"); driver = require("./driver.js");
require("./tests.js"); require("./tests.js");
require("./tests-harmony.js"); require("./tests-harmony.js");
require("babelify/node_modules/babel-core/register")
acorn = require("../src")
require("../src/loose")
} else { } else {
driver = window; driver = window;
acorn = window.acorn;
} }
var htmlLog = typeof document === "object" && document.getElementById('log'); var htmlLog = typeof document === "object" && document.getElementById('log');
@ -47,12 +51,12 @@
var stats, modes = { var stats, modes = {
Normal: { Normal: {
config: { config: {
parse: (typeof require === "undefined" ? window.acorn : require("../acorn.js")).parse parse: (typeof require === "undefined" ? window.acorn : require("../dist/acorn")).parse
} }
}, },
Loose: { Loose: {
config: { config: {
parse: (typeof require === "undefined" ? window.acorn : require("../acorn_loose")).parse_dammit, parse: (typeof require === "undefined" ? window.acorn : require("../dist/acorn_loose")).parse_dammit,
loose: true, loose: true,
filter: function (test) { filter: function (test) {
var opts = test.options || {}; var opts = test.options || {};

View File

@ -1,359 +0,0 @@
// AST walker module for Mozilla Parser API compatible trees
(function(mod) {
if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
})(function(exports) {
"use strict";
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
exports.simple = function(node, visitors, base, state) {
if (!base) base = exports.base;
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
exports.ancestor = function(node, visitors, base, state) {
if (!base) base = exports.base;
if (!state) state = [];
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
if (node != st[st.length - 1]) {
st = st.slice();
st.push(node);
}
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
exports.recursive = function(node, state, funcs, base) {
var visitor = funcs ? exports.make(funcs, base) : base;
function c(node, st, override) {
visitor[override || node.type](node, st, c);
}
c(node, state);
};
function makeTest(test) {
if (typeof test == "string")
return function(type) { return type == test; };
else if (!test)
return function() { return true; };
else
return test;
}
function Found(node, state) { this.node = node; this.state = state; }
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
exports.findNodeAt = function(node, start, end, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
base[type](node, st, c);
if (test(type, node) &&
(start == null || node.start == start) &&
(end == null || node.end == end))
throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
exports.findNodeAround = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) return;
base[type](node, st, c);
if (test(type, node)) throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node after a given position.
exports.findNodeAfter = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
if (node.end < pos) return;
var type = override || node.type;
if (node.start >= pos && test(type, node)) throw new Found(node, st);
base[type](node, st, c);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node before a given position.
exports.findNodeBefore = function(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
var max;
var c = function(node, st, override) {
if (node.start > pos) return;
var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
max = new Found(node, st);
base[type](node, st, c);
};
c(node, state);
return max;
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
exports.make = function(funcs, base) {
if (!base) base = exports.base;
var visitor = {};
for (var type in base) visitor[type] = base[type];
for (var type in funcs) visitor[type] = funcs[type];
return visitor;
};
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
// Node walkers.
var base = exports.base = {};
base.Program = base.BlockStatement = function(node, st, c) {
for (var i = 0; i < node.body.length; ++i)
c(node.body[i], st, "Statement");
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = function(node, st, c) {
c(node.expression, st, "Expression");
};
base.IfStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) c(node.alternate, st, "Statement");
};
base.LabeledStatement = function(node, st, c) {
c(node.body, st, "Statement");
};
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function(node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function(node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0; i < node.cases.length; ++i) {
var cs = node.cases[i];
if (cs.test) c(cs.test, st, "Expression");
for (var j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement");
}
};
base.ReturnStatement = base.YieldExpression = function(node, st, c) {
if (node.argument) c(node.argument, st, "Expression");
};
base.ThrowStatement = base.SpreadElement = base.RestElement = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.TryStatement = function(node, st, c) {
c(node.block, st, "Statement");
if (node.handler) c(node.handler.body, st, "ScopeBody");
if (node.finalizer) c(node.finalizer, st, "Statement");
};
base.WhileStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.DoWhileStatement = base.WhileStatement;
base.ForStatement = function(node, st, c) {
if (node.init) c(node.init, st, "ForInit");
if (node.test) c(node.test, st, "Expression");
if (node.update) c(node.update, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function(node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function(node, st, c) {
if (node.type == "VariableDeclaration") c(node, st);
else c(node, st, "Expression");
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function(node, st, c) {
c(node, st, "Function");
};
base.VariableDeclaration = function(node, st, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
if (decl.init) c(decl.init, st, "Expression");
}
};
base.Function = function(node, st, c) {
c(node.body, st, "ScopeBody");
};
base.ScopeBody = function(node, st, c) {
c(node, st, "Statement");
};
base.Expression = skipThrough;
base.ThisExpression = ignore;
base.ArrayExpression = base.ArrayPattern = function(node, st, c) {
for (var i = 0; i < node.elements.length; ++i) {
var elt = node.elements[i];
if (elt) c(elt, st, "Expression");
}
};
base.ObjectExpression = base.ObjectPattern = function(node, st, c) {
for (var i = 0; i < node.properties.length; ++i)
c(node.properties[i], st);
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function(node, st, c) {
for (var i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression");
};
base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = function(node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function(node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression");
};
base.MemberExpression = function(node, st, c) {
c(node.object, st, "Expression");
if (node.computed) c(node.property, st, "Expression");
};
base.ExportDeclaration = function (node, st, c) {
c(node.declaration, st);
};
base.ImportDeclaration = function (node, st, c) {
node.specifiers.forEach(function (specifier) {
c(specifier, st);
});
};
base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore;
base.TaggedTemplateExpression = function(node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st);
};
base.ClassDeclaration = base.ClassExpression = function(node, st, c) {
if (node.superClass) c(node.superClass, st, "Expression");
for (var i = 0; i < node.body.body.length; i++)
c(node.body.body[i], st);
};
base.MethodDefinition = base.Property = function(node, st, c) {
if (node.computed) c(node.key, st, "Expression");
c(node.value, st, "Expression");
};
base.ComprehensionExpression = function(node, st, c) {
for (var i = 0; i < node.blocks.length; i++)
c(node.blocks[i].right, st, "Expression");
c(node.body, st, "Expression");
};
// NOTE: the stuff below is deprecated, and will be removed when 1.0 is released
// A custom walker that keeps track of the scope chain and the
// variables defined in it.
function makeScope(prev, isCatch) {
return {vars: Object.create(null), prev: prev, isCatch: isCatch};
}
function normalScope(scope) {
while (scope.isCatch) scope = scope.prev;
return scope;
}
exports.scopeVisitor = exports.make({
Function: function(node, scope, c) {
var inner = makeScope(scope);
for (var i = 0; i < node.params.length; ++i)
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
if (node.id) {
var decl = node.type == "FunctionDeclaration";
(decl ? normalScope(scope) : inner).vars[node.id.name] =
{type: decl ? "function" : "function name", node: node.id};
}
c(node.body, inner, "ScopeBody");
},
TryStatement: function(node, scope, c) {
c(node.block, scope, "Statement");
if (node.handler) {
var inner = makeScope(scope, true);
inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
c(node.handler.body, inner, "ScopeBody");
}
if (node.finalizer) c(node.finalizer, scope, "Statement");
},
VariableDeclaration: function(node, scope, c) {
var target = normalScope(scope);
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
target.vars[decl.id.name] = {type: "var", node: decl.id};
if (decl.init) c(decl.init, scope, "Expression");
}
}
});
});