http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/lval.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/lval.js b/node_modules/acorn/src/lval.js new file mode 100644 index 0000000..1e2508e --- /dev/null +++ b/node_modules/acorn/src/lval.js @@ -0,0 +1,215 @@ +import {types as tt} from "./tokentype" +import {Parser} from "./state" +import {has} from "./util" + +const pp = Parser.prototype + +// Convert existing expression atom to assignable pattern +// if possible. + +pp.toAssignable = function(node, isBinding) { + if (this.options.ecmaVersion >= 6 && node) { + switch (node.type) { + case "Identifier": + case "ObjectPattern": + case "ArrayPattern": + break + + case "ObjectExpression": + node.type = "ObjectPattern" + for (let i = 0; i < node.properties.length; i++) { + let prop = node.properties[i] + if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter") + this.toAssignable(prop.value, isBinding) + } + break + + case "ArrayExpression": + node.type = "ArrayPattern" + this.toAssignableList(node.elements, isBinding) + break + + case "AssignmentExpression": + if (node.operator === "=") { + node.type = "AssignmentPattern" + delete node.operator + // falls through to AssignmentPattern + } else { + this.raise(node.left.end, "Only '=' operator can be used for specifying default value.") + break; + } + + case "AssignmentPattern": + if (node.right.type === "YieldExpression") + this.raise(node.right.start, "Yield expression cannot be a default value") + break; + + case "ParenthesizedExpression": + node.expression = this.toAssignable(node.expression, isBinding) + break + + case "MemberExpression": + if (!isBinding) break + + default: + this.raise(node.start, "Assigning to rvalue") + } + } + return node +} + +// Convert list of expression atoms to binding list. + +pp.toAssignableList = function(exprList, isBinding) { + let end = exprList.length + if (end) { + let last = exprList[end - 1] + if (last && last.type == "RestElement") { + --end + } else if (last && last.type == "SpreadElement") { + last.type = "RestElement" + let arg = last.argument + this.toAssignable(arg, isBinding) + if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") + this.unexpected(arg.start) + --end + } + + if (isBinding && last.type === "RestElement" && last.argument.type !== "Identifier") + this.unexpected(last.argument.start); + } + for (let i = 0; i < end; i++) { + let elt = exprList[i] + if (elt) this.toAssignable(elt, isBinding) + } + return exprList +} + +// Parses spread element. + +pp.parseSpread = function(refDestructuringErrors) { + let node = this.startNode() + this.next() + node.argument = this.parseMaybeAssign(refDestructuringErrors) + return this.finishNode(node, "SpreadElement") +} + +pp.parseRest = function(allowNonIdent) { + let node = this.startNode() + this.next() + + // RestElement inside of a function parameter must be an identifier + if (allowNonIdent) node.argument = this.type === tt.name ? this.parseIdent() : this.unexpected() + else node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected() + + return this.finishNode(node, "RestElement") +} + +// Parses lvalue (assignable) atom. + +pp.parseBindingAtom = function() { + if (this.options.ecmaVersion < 6) return this.parseIdent() + switch (this.type) { + case tt.name: + return this.parseIdent() + + case tt.bracketL: + let node = this.startNode() + this.next() + node.elements = this.parseBindingList(tt.bracketR, true, true) + return this.finishNode(node, "ArrayPattern") + + case tt.braceL: + return this.parseObj(true) + + default: + this.unexpected() + } +} + +pp.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowNonIdent) { + let elts = [], first = true + while (!this.eat(close)) { + if (first) first = false + else this.expect(tt.comma) + if (allowEmpty && this.type === tt.comma) { + elts.push(null) + } else if (allowTrailingComma && this.afterTrailingComma(close)) { + break + } else if (this.type === tt.ellipsis) { + let rest = this.parseRest(allowNonIdent) + this.parseBindingListItem(rest) + elts.push(rest) + this.expect(close) + break + } else { + let elem = this.parseMaybeDefault(this.start, this.startLoc) + this.parseBindingListItem(elem) + elts.push(elem) + } + } + return elts +} + +pp.parseBindingListItem = function(param) { + return param +} + +// Parses assignment pattern around given atom if possible. + +pp.parseMaybeDefault = function(startPos, startLoc, left) { + left = left || this.parseBindingAtom() + if (this.options.ecmaVersion < 6 || !this.eat(tt.eq)) return left + let node = this.startNodeAt(startPos, startLoc) + node.left = left + node.right = this.parseMaybeAssign() + return this.finishNode(node, "AssignmentPattern") +} + +// Verify that a node is an lval â something that can be assigned +// to. + +pp.checkLVal = function(expr, isBinding, checkClashes) { + switch (expr.type) { + case "Identifier": + if (this.strict && this.reservedWordsStrictBind.test(expr.name)) + this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode") + if (checkClashes) { + if (has(checkClashes, expr.name)) + this.raise(expr.start, "Argument name clash") + checkClashes[expr.name] = true + } + break + + case "MemberExpression": + if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression") + break + + case "ObjectPattern": + for (let i = 0; i < expr.properties.length; i++) + this.checkLVal(expr.properties[i].value, isBinding, checkClashes) + break + + case "ArrayPattern": + for (let i = 0; i < expr.elements.length; i++) { + let elem = expr.elements[i] + if (elem) this.checkLVal(elem, isBinding, checkClashes) + } + break + + case "AssignmentPattern": + this.checkLVal(expr.left, isBinding, checkClashes) + break + + case "RestElement": + this.checkLVal(expr.argument, isBinding, checkClashes) + break + + case "ParenthesizedExpression": + this.checkLVal(expr.expression, isBinding, checkClashes) + break + + default: + this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue") + } +}
http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/node.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/node.js b/node_modules/acorn/src/node.js new file mode 100644 index 0000000..76b5b09 --- /dev/null +++ b/node_modules/acorn/src/node.js @@ -0,0 +1,50 @@ +import {Parser} from "./state" +import {SourceLocation} from "./locutil" + +export class Node { + constructor(parser, pos, loc) { + this.type = "" + this.start = pos + this.end = 0 + if (parser.options.locations) + this.loc = new SourceLocation(parser, loc) + if (parser.options.directSourceFile) + this.sourceFile = parser.options.directSourceFile + if (parser.options.ranges) + this.range = [pos, 0] + } +} + +// Start an AST node, attaching a start offset. + +const pp = Parser.prototype + +pp.startNode = function() { + return new Node(this, this.start, this.startLoc) +} + +pp.startNodeAt = function(pos, loc) { + return new Node(this, pos, loc) +} + +// Finish an AST node, adding `type` and `end` properties. + +function finishNodeAt(node, type, pos, loc) { + node.type = type + node.end = pos + if (this.options.locations) + node.loc.end = loc + if (this.options.ranges) + node.range[1] = pos + return node +} + +pp.finishNode = function(node, type) { + return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc) +} + +// Finish node at given position + +pp.finishNodeAt = function(node, type, pos, loc) { + return finishNodeAt.call(this, node, type, pos, loc) +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/options.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/options.js b/node_modules/acorn/src/options.js new file mode 100644 index 0000000..f2d2981 --- /dev/null +++ b/node_modules/acorn/src/options.js @@ -0,0 +1,121 @@ +import {has, isArray} from "./util" +import {SourceLocation} from "./locutil" + +// A second optional argument can be given to further configure +// the parser process. These options are recognized: + +export const defaultOptions = { + // `ecmaVersion` indicates the ECMAScript version to parse. Must + // be either 3, or 5, or 6. This influences support for strict + // mode, the set of reserved words, support for getters and + // setters and other features. + ecmaVersion: 5, + // Source type ("script" or "module") for different semantics + sourceType: "script", + // `onInsertedSemicolon` can be a callback that will be called + // when a semicolon is automatically inserted. It will be passed + // th position of the comma as an offset, and if `locations` is + // enabled, it is given the location as a `{line, column}` object + // as second argument. + onInsertedSemicolon: null, + // `onTrailingComma` is similar to `onInsertedSemicolon`, but for + // trailing commas. + onTrailingComma: null, + // By default, reserved words are only enforced if ecmaVersion >= 5. + // Set `allowReserved` to a boolean value to explicitly turn this on + // an off. When this option has the value "never", reserved words + // and keywords can also not be used as property names. + allowReserved: null, + // When enabled, a return at the top level is not considered an + // error. + allowReturnOutsideFunction: false, + // When enabled, import/export statements are not constrained to + // appearing at the top of the program. + allowImportExportEverywhere: false, + // When enabled, hashbang directive in the beginning of file + // is allowed and treated as a line comment. + allowHashBang: false, + // When `locations` is on, `loc` properties holding objects with + // `start` and `end` properties in `{line, column}` form (with + // line being 1-based and column 0-based) will be attached to the + // nodes. + locations: false, + // A function can be passed as `onToken` option, which will + // cause Acorn to call that function with object in the same + // format as tokens returned from `tokenizer().getToken()`. Note + // that you are not allowed to call the parser from the + // callbackâthat will corrupt its internal state. + onToken: null, + // A function can be passed as `onComment` option, which will + // cause Acorn to call that function with `(block, text, start, + // end)` parameters whenever a comment is skipped. `block` is a + // boolean indicating whether this is a block (`/* */`) comment, + // `text` is the content of the comment, and `start` and `end` are + // character offsets that denote the start and end of the comment. + // When the `locations` option is on, two more parameters are + // passed, the full `{line, column}` locations of the start and + // end of the comments. Note that you are not allowed to call the + // parser from the callbackâthat will corrupt its internal state. + onComment: null, + // Nodes have their start and end characters offsets recorded in + // `start` and `end` properties (directly on the node, rather than + // the `loc` object, which holds line/column data. To also add a + // [semi-standardized][range] `range` property holding a `[start, + // end]` array with the same numbers, set the `ranges` option to + // `true`. + // + // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 + ranges: false, + // It is possible to parse multiple files into a single AST by + // passing the tree produced by parsing the first file as + // `program` option in subsequent parses. This will add the + // toplevel forms of the parsed file to the `Program` (top) node + // of an existing parse tree. + program: null, + // When `locations` is on, you can pass this to record the source + // file in every node's `loc` object. + sourceFile: null, + // This value, if given, is stored in every node, whether + // `locations` is on or off. + directSourceFile: null, + // When enabled, parenthesized expressions are represented by + // (non-standard) ParenthesizedExpression nodes + preserveParens: false, + plugins: {} +} + +// Interpret and default an options object + +export function getOptions(opts) { + let options = {} + for (let opt in defaultOptions) + options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt] + if (options.allowReserved == null) + options.allowReserved = options.ecmaVersion < 5 + + if (isArray(options.onToken)) { + let tokens = options.onToken + options.onToken = (token) => tokens.push(token) + } + if (isArray(options.onComment)) + options.onComment = pushComment(options, options.onComment) + + return options +} + +function pushComment(options, array) { + return function (block, text, start, end, startLoc, endLoc) { + let comment = { + type: block ? 'Block' : 'Line', + value: text, + start: start, + end: end + } + if (options.locations) + comment.loc = new SourceLocation(this, startLoc, endLoc) + if (options.ranges) + comment.range = [start, end] + array.push(comment) + } +} + http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/parseutil.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/parseutil.js b/node_modules/acorn/src/parseutil.js new file mode 100644 index 0000000..9c71c48 --- /dev/null +++ b/node_modules/acorn/src/parseutil.js @@ -0,0 +1,102 @@ +import {types as tt} from "./tokentype" +import {Parser} from "./state" +import {lineBreak} from "./whitespace" + +const pp = Parser.prototype + +// ## Parser utilities + +// Test whether a statement node is the string literal `"use strict"`. + +pp.isUseStrict = function(stmt) { + return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && + stmt.expression.type === "Literal" && + stmt.expression.raw.slice(1, -1) === "use strict" +} + +// Predicate that tests whether the next token is of the given +// type, and if yes, consumes it as a side effect. + +pp.eat = function(type) { + if (this.type === type) { + this.next() + return true + } else { + return false + } +} + +// Tests whether parsed token is a contextual keyword. + +pp.isContextual = function(name) { + return this.type === tt.name && this.value === name +} + +// Consumes contextual keyword if possible. + +pp.eatContextual = function(name) { + return this.value === name && this.eat(tt.name) +} + +// Asserts that following token is given contextual keyword. + +pp.expectContextual = function(name) { + if (!this.eatContextual(name)) this.unexpected() +} + +// Test whether a semicolon can be inserted at the current position. + +pp.canInsertSemicolon = function() { + return this.type === tt.eof || + this.type === tt.braceR || + lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) +} + +pp.insertSemicolon = function() { + if (this.canInsertSemicolon()) { + if (this.options.onInsertedSemicolon) + this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc) + return true + } +} + +// Consume a semicolon, or, failing that, see if we are allowed to +// pretend that there is a semicolon at this position. + +pp.semicolon = function() { + if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected() +} + +pp.afterTrailingComma = function(tokType) { + if (this.type == tokType) { + if (this.options.onTrailingComma) + this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc) + this.next() + return true + } +} + +// Expect a token of a given type. If found, consume it, otherwise, +// raise an unexpected token error. + +pp.expect = function(type) { + this.eat(type) || this.unexpected() +} + +// Raise an unexpected token error. + +pp.unexpected = function(pos) { + this.raise(pos != null ? pos : this.start, "Unexpected token") +} + +pp.checkPatternErrors = function(refDestructuringErrors, andThrow) { + let pos = refDestructuringErrors && refDestructuringErrors.trailingComma + if (!andThrow) return !!pos + if (pos) this.raise(pos, "Trailing comma is not permitted in destructuring patterns") +} + +pp.checkExpressionErrors = function(refDestructuringErrors, andThrow) { + let pos = refDestructuringErrors && refDestructuringErrors.shorthandAssign + if (!andThrow) return !!pos + if (pos) this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns") +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/state.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/state.js b/node_modules/acorn/src/state.js new file mode 100644 index 0000000..3a0d5ff --- /dev/null +++ b/node_modules/acorn/src/state.js @@ -0,0 +1,104 @@ +import {reservedWords, keywords} from "./identifier" +import {types as tt} from "./tokentype" +import {lineBreak} from "./whitespace" +import {getOptions} from "./options" + +// Registered plugins +export const plugins = {} + +function keywordRegexp(words) { + return new RegExp("^(" + words.replace(/ /g, "|") + ")$") +} + +export class Parser { + constructor(options, input, startPos) { + this.options = options = getOptions(options) + this.sourceFile = options.sourceFile + this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5]) + let reserved = options.allowReserved ? "" : + reservedWords[options.ecmaVersion] + (options.sourceType == "module" ? " await" : "") + this.reservedWords = keywordRegexp(reserved) + let reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict + this.reservedWordsStrict = keywordRegexp(reservedStrict) + this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + reservedWords.strictBind) + this.input = String(input) + + // Used to signal to callers of `readWord1` whether the word + // contained any escape sequences. This is needed because words with + // escape sequences must not be interpreted as keywords. + this.containsEsc = false; + + // Load plugins + this.loadPlugins(options.plugins) + + // Set up token state + + // The current position of the tokenizer in the input. + if (startPos) { + this.pos = startPos + this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)) + this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length + } else { + this.pos = this.lineStart = 0 + this.curLine = 1 + } + + // Properties of the current token: + // Its type + this.type = tt.eof + // For tokens that include more information than their type, the value + this.value = null + // Its start and end offset + this.start = this.end = this.pos + // And, if locations are used, the {line, column} object + // corresponding to those offsets + this.startLoc = this.endLoc = this.curPosition() + + // Position information for the previous token + this.lastTokEndLoc = this.lastTokStartLoc = null + this.lastTokStart = this.lastTokEnd = this.pos + + // The context stack is used to superficially track syntactic + // context to predict whether a regular expression is allowed in a + // given position. + this.context = this.initialContext() + this.exprAllowed = true + + // Figure out if it's a module code. + this.strict = this.inModule = options.sourceType === "module" + + // Used to signify the start of a potential arrow function + this.potentialArrowAt = -1 + + // Flags to track whether we are in a function, a generator. + this.inFunction = this.inGenerator = false + // Labels in scope. + this.labels = [] + + // If enabled, skip leading hashbang line. + if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === '#!') + this.skipLineComment(2) + } + + // DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them + isKeyword(word) { return this.keywords.test(word) } + isReservedWord(word) { return this.reservedWords.test(word) } + + extend(name, f) { + this[name] = f(this[name]) + } + + loadPlugins(pluginConfigs) { + for (let name in pluginConfigs) { + let plugin = plugins[name] + if (!plugin) throw new Error("Plugin '" + name + "' not found") + plugin(this, pluginConfigs[name]) + } + } + + parse() { + let node = this.options.program || this.startNode() + this.nextToken() + return this.parseTopLevel(node) + } +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/statement.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/statement.js b/node_modules/acorn/src/statement.js new file mode 100644 index 0000000..7ade79f --- /dev/null +++ b/node_modules/acorn/src/statement.js @@ -0,0 +1,626 @@ +import {types as tt} from "./tokentype" +import {Parser} from "./state" +import {lineBreak} from "./whitespace" + +const pp = Parser.prototype + +// ### Statement parsing + +// Parse a program. Initializes the parser, reads any number of +// statements, and wraps them in a Program node. Optionally takes a +// `program` argument. If present, the statements will be appended +// to its body instead of creating a new node. + +pp.parseTopLevel = function(node) { + let first = true + if (!node.body) node.body = [] + while (this.type !== tt.eof) { + let stmt = this.parseStatement(true, true) + node.body.push(stmt) + if (first) { + if (this.isUseStrict(stmt)) this.setStrict(true) + first = false + } + } + this.next() + if (this.options.ecmaVersion >= 6) { + node.sourceType = this.options.sourceType + } + return this.finishNode(node, "Program") +} + +const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"} + +// Parse a single statement. +// +// If expecting a statement and finding a slash operator, parse a +// regular expression literal. This is to handle cases like +// `if (foo) /blah/.exec(foo)`, where looking at the previous token +// does not help. + +pp.parseStatement = function(declaration, topLevel) { + let starttype = this.type, node = this.startNode() + + // Most types of statements are recognized by the keyword they + // start with. Many are trivial to parse, some require a bit of + // complexity. + + switch (starttype) { + case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword) + case tt._debugger: return this.parseDebuggerStatement(node) + case tt._do: return this.parseDoStatement(node) + case tt._for: return this.parseForStatement(node) + case tt._function: + if (!declaration && this.options.ecmaVersion >= 6) this.unexpected() + return this.parseFunctionStatement(node) + case tt._class: + if (!declaration) this.unexpected() + return this.parseClass(node, true) + case tt._if: return this.parseIfStatement(node) + case tt._return: return this.parseReturnStatement(node) + case tt._switch: return this.parseSwitchStatement(node) + case tt._throw: return this.parseThrowStatement(node) + case tt._try: return this.parseTryStatement(node) + case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var + case tt._var: return this.parseVarStatement(node, starttype) + case tt._while: return this.parseWhileStatement(node) + case tt._with: return this.parseWithStatement(node) + case tt.braceL: return this.parseBlock() + case tt.semi: return this.parseEmptyStatement(node) + case tt._export: + case tt._import: + if (!this.options.allowImportExportEverywhere) { + if (!topLevel) + this.raise(this.start, "'import' and 'export' may only appear at the top level") + if (!this.inModule) + this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'") + } + return starttype === tt._import ? this.parseImport(node) : this.parseExport(node) + + // If the statement does not start with a statement keyword or a + // brace, it's an ExpressionStatement or LabeledStatement. We + // simply start parsing an expression, and afterwards, if the + // next token is a colon and the expression was a simple + // Identifier node, we switch to interpreting it as a label. + default: + let maybeName = this.value, expr = this.parseExpression() + if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) + return this.parseLabeledStatement(node, maybeName, expr) + else return this.parseExpressionStatement(node, expr) + } +} + +pp.parseBreakContinueStatement = function(node, keyword) { + let isBreak = keyword == "break" + this.next() + if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null + else if (this.type !== tt.name) this.unexpected() + else { + node.label = this.parseIdent() + this.semicolon() + } + + // Verify that there is an actual destination to break or + // continue to. + for (var i = 0; i < this.labels.length; ++i) { + let lab = this.labels[i] + if (node.label == null || lab.name === node.label.name) { + if (lab.kind != null && (isBreak || lab.kind === "loop")) break + if (node.label && isBreak) break + } + } + if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword) + return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement") +} + +pp.parseDebuggerStatement = function(node) { + this.next() + this.semicolon() + return this.finishNode(node, "DebuggerStatement") +} + +pp.parseDoStatement = function(node) { + this.next() + this.labels.push(loopLabel) + node.body = this.parseStatement(false) + this.labels.pop() + this.expect(tt._while) + node.test = this.parseParenExpression() + if (this.options.ecmaVersion >= 6) + this.eat(tt.semi) + else + this.semicolon() + return this.finishNode(node, "DoWhileStatement") +} + +// Disambiguating between a `for` and a `for`/`in` or `for`/`of` +// loop is non-trivial. Basically, we have to parse the init `var` +// statement or expression, disallowing the `in` operator (see +// the second parameter to `parseExpression`), and then check +// whether the next token is `in` or `of`. When there is no init +// part (semicolon immediately after the opening parenthesis), it +// is a regular `for` loop. + +pp.parseForStatement = function(node) { + this.next() + this.labels.push(loopLabel) + this.expect(tt.parenL) + if (this.type === tt.semi) return this.parseFor(node, null) + if (this.type === tt._var || this.type === tt._let || this.type === tt._const) { + let init = this.startNode(), varKind = this.type + this.next() + this.parseVar(init, true, varKind) + this.finishNode(init, "VariableDeclaration") + if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 && + !(varKind !== tt._var && init.declarations[0].init)) + return this.parseForIn(node, init) + return this.parseFor(node, init) + } + let refDestructuringErrors = {shorthandAssign: 0, trailingComma: 0} + let init = this.parseExpression(true, refDestructuringErrors) + if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) { + this.checkPatternErrors(refDestructuringErrors, true) + this.toAssignable(init) + this.checkLVal(init) + return this.parseForIn(node, init) + } else { + this.checkExpressionErrors(refDestructuringErrors, true) + } + return this.parseFor(node, init) +} + +pp.parseFunctionStatement = function(node) { + this.next() + return this.parseFunction(node, true) +} + +pp.parseIfStatement = function(node) { + this.next() + node.test = this.parseParenExpression() + node.consequent = this.parseStatement(false) + node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null + return this.finishNode(node, "IfStatement") +} + +pp.parseReturnStatement = function(node) { + if (!this.inFunction && !this.options.allowReturnOutsideFunction) + this.raise(this.start, "'return' outside of function") + this.next() + + // In `return` (and `break`/`continue`), the keywords with + // optional arguments, we eagerly look for a semicolon or the + // possibility to insert one. + + if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null + else { node.argument = this.parseExpression(); this.semicolon() } + return this.finishNode(node, "ReturnStatement") +} + +pp.parseSwitchStatement = function(node) { + this.next() + node.discriminant = this.parseParenExpression() + node.cases = [] + this.expect(tt.braceL) + this.labels.push(switchLabel) + + // Statements under must be grouped (by label) in SwitchCase + // nodes. `cur` is used to keep the node that we are currently + // adding statements to. + + for (var cur, sawDefault = false; this.type != tt.braceR;) { + if (this.type === tt._case || this.type === tt._default) { + let isCase = this.type === tt._case + if (cur) this.finishNode(cur, "SwitchCase") + node.cases.push(cur = this.startNode()) + cur.consequent = [] + this.next() + if (isCase) { + cur.test = this.parseExpression() + } else { + if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses") + sawDefault = true + cur.test = null + } + this.expect(tt.colon) + } else { + if (!cur) this.unexpected() + cur.consequent.push(this.parseStatement(true)) + } + } + if (cur) this.finishNode(cur, "SwitchCase") + this.next() // Closing brace + this.labels.pop() + return this.finishNode(node, "SwitchStatement") +} + +pp.parseThrowStatement = function(node) { + this.next() + if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) + this.raise(this.lastTokEnd, "Illegal newline after throw") + node.argument = this.parseExpression() + this.semicolon() + return this.finishNode(node, "ThrowStatement") +} + +// Reused empty array added for node fields that are always empty. + +const empty = [] + +pp.parseTryStatement = function(node) { + this.next() + node.block = this.parseBlock() + node.handler = null + if (this.type === tt._catch) { + let clause = this.startNode() + this.next() + this.expect(tt.parenL) + clause.param = this.parseBindingAtom() + this.checkLVal(clause.param, true) + this.expect(tt.parenR) + clause.body = this.parseBlock() + node.handler = this.finishNode(clause, "CatchClause") + } + node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null + if (!node.handler && !node.finalizer) + this.raise(node.start, "Missing catch or finally clause") + return this.finishNode(node, "TryStatement") +} + +pp.parseVarStatement = function(node, kind) { + this.next() + this.parseVar(node, false, kind) + this.semicolon() + return this.finishNode(node, "VariableDeclaration") +} + +pp.parseWhileStatement = function(node) { + this.next() + node.test = this.parseParenExpression() + this.labels.push(loopLabel) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, "WhileStatement") +} + +pp.parseWithStatement = function(node) { + if (this.strict) this.raise(this.start, "'with' in strict mode") + this.next() + node.object = this.parseParenExpression() + node.body = this.parseStatement(false) + return this.finishNode(node, "WithStatement") +} + +pp.parseEmptyStatement = function(node) { + this.next() + return this.finishNode(node, "EmptyStatement") +} + +pp.parseLabeledStatement = function(node, maybeName, expr) { + for (let i = 0; i < this.labels.length; ++i) + if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared") + let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null + for (let i = this.labels.length - 1; i >= 0; i--) { + let label = this.labels[i] + if (label.statementStart == node.start) { + label.statementStart = this.start; + label.kind = kind; + } else break; + } + this.labels.push({name: maybeName, kind: kind, statementStart: this.start}) + node.body = this.parseStatement(true) + this.labels.pop() + node.label = expr + return this.finishNode(node, "LabeledStatement") +} + +pp.parseExpressionStatement = function(node, expr) { + node.expression = expr + this.semicolon() + return this.finishNode(node, "ExpressionStatement") +} + +// Parse a semicolon-enclosed block of statements, handling `"use +// strict"` declarations when `allowStrict` is true (used for +// function bodies). + +pp.parseBlock = function(allowStrict) { + let node = this.startNode(), first = true, oldStrict + node.body = [] + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + let stmt = this.parseStatement(true) + node.body.push(stmt) + if (first && allowStrict && this.isUseStrict(stmt)) { + oldStrict = this.strict + this.setStrict(this.strict = true) + } + first = false + } + if (oldStrict === false) this.setStrict(false) + return this.finishNode(node, "BlockStatement") +} + +// Parse a regular `for` loop. The disambiguation code in +// `parseStatement` will already have parsed the init statement or +// expression. + +pp.parseFor = function(node, init) { + node.init = init + this.expect(tt.semi) + node.test = this.type === tt.semi ? null : this.parseExpression() + this.expect(tt.semi) + node.update = this.type === tt.parenR ? null : this.parseExpression() + this.expect(tt.parenR) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, "ForStatement") +} + +// Parse a `for`/`in` and `for`/`of` loop, which are almost +// same from parser's perspective. + +pp.parseForIn = function(node, init) { + let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement" + this.next() + node.left = init + node.right = this.parseExpression() + this.expect(tt.parenR) + node.body = this.parseStatement(false) + this.labels.pop() + return this.finishNode(node, type) +} + +// Parse a list of variable declarations. + +pp.parseVar = function(node, isFor, kind) { + node.declarations = [] + node.kind = kind.keyword + for (;;) { + let decl = this.startNode() + this.parseVarId(decl) + if (this.eat(tt.eq)) { + decl.init = this.parseMaybeAssign(isFor) + } else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) { + this.unexpected() + } else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) { + this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value") + } else { + decl.init = null + } + node.declarations.push(this.finishNode(decl, "VariableDeclarator")) + if (!this.eat(tt.comma)) break + } + return node +} + +pp.parseVarId = function(decl) { + decl.id = this.parseBindingAtom() + this.checkLVal(decl.id, true) +} + +// Parse a function declaration or literal (depending on the +// `isStatement` parameter). + +pp.parseFunction = function(node, isStatement, allowExpressionBody) { + this.initFunction(node) + if (this.options.ecmaVersion >= 6) + node.generator = this.eat(tt.star) + if (isStatement || this.type === tt.name) + node.id = this.parseIdent() + this.parseFunctionParams(node) + this.parseFunctionBody(node, allowExpressionBody) + return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression") +} + +pp.parseFunctionParams = function(node) { + this.expect(tt.parenL) + node.params = this.parseBindingList(tt.parenR, false, false, true) +} + +// Parse a class declaration or literal (depending on the +// `isStatement` parameter). + +pp.parseClass = function(node, isStatement) { + this.next() + this.parseClassId(node, isStatement) + this.parseClassSuper(node) + let classBody = this.startNode() + let hadConstructor = false + classBody.body = [] + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + if (this.eat(tt.semi)) continue + let method = this.startNode() + let isGenerator = this.eat(tt.star) + let isMaybeStatic = this.type === tt.name && this.value === "static" + this.parsePropertyName(method) + method.static = isMaybeStatic && this.type !== tt.parenL + if (method.static) { + if (isGenerator) this.unexpected() + isGenerator = this.eat(tt.star) + this.parsePropertyName(method) + } + method.kind = "method" + let isGetSet = false + if (!method.computed) { + let {key} = method + if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) { + isGetSet = true + method.kind = key.name + key = this.parsePropertyName(method) + } + if (!method.static && (key.type === "Identifier" && key.name === "constructor" || + key.type === "Literal" && key.value === "constructor")) { + if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class") + if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier") + if (isGenerator) this.raise(key.start, "Constructor can't be a generator") + method.kind = "constructor" + hadConstructor = true + } + } + this.parseClassMethod(classBody, method, isGenerator) + if (isGetSet) { + let paramCount = method.kind === "get" ? 0 : 1 + if (method.value.params.length !== paramCount) { + let start = method.value.start + if (method.kind === "get") + this.raise(start, "getter should have no params"); + else + this.raise(start, "setter should have exactly one param") + } + if (method.kind === "set" && method.value.params[0].type === "RestElement") + this.raise(method.value.params[0].start, "Setter cannot use rest params") + } + } + node.body = this.finishNode(classBody, "ClassBody") + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") +} + +pp.parseClassMethod = function(classBody, method, isGenerator) { + method.value = this.parseMethod(isGenerator) + classBody.body.push(this.finishNode(method, "MethodDefinition")) +} + +pp.parseClassId = function(node, isStatement) { + node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null +} + +pp.parseClassSuper = function(node) { + node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null +} + +// Parses module export declaration. + +pp.parseExport = function(node) { + this.next() + // export * from '...' + if (this.eat(tt.star)) { + this.expectContextual("from") + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + this.semicolon() + return this.finishNode(node, "ExportAllDeclaration") + } + if (this.eat(tt._default)) { // export default ... + let expr = this.parseMaybeAssign() + let needsSemi = true + if (expr.type == "FunctionExpression" || + expr.type == "ClassExpression") { + needsSemi = false + if (expr.id) { + expr.type = expr.type == "FunctionExpression" + ? "FunctionDeclaration" + : "ClassDeclaration" + } + } + node.declaration = expr + if (needsSemi) this.semicolon() + return this.finishNode(node, "ExportDefaultDeclaration") + } + // export var|const|let|function|class ... + if (this.shouldParseExportStatement()) { + node.declaration = this.parseStatement(true) + node.specifiers = [] + node.source = null + } else { // export { x, y as z } [from '...'] + node.declaration = null + node.specifiers = this.parseExportSpecifiers() + if (this.eatContextual("from")) { + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + } else { + // check for keywords used as local names + for (let i = 0; i < node.specifiers.length; i++) { + if (this.keywords.test(node.specifiers[i].local.name) || this.reservedWords.test(node.specifiers[i].local.name)) { + this.unexpected(node.specifiers[i].local.start) + } + } + + node.source = null + } + this.semicolon() + } + return this.finishNode(node, "ExportNamedDeclaration") +} + +pp.shouldParseExportStatement = function() { + return this.type.keyword +} + +// Parses a comma-separated list of module exports. + +pp.parseExportSpecifiers = function() { + let nodes = [], first = true + // export { x, y as z } [from '...'] + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma) + if (this.afterTrailingComma(tt.braceR)) break + } else first = false + + let node = this.startNode() + node.local = this.parseIdent(this.type === tt._default) + node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local + nodes.push(this.finishNode(node, "ExportSpecifier")) + } + return nodes +} + +// Parses import declaration. + +pp.parseImport = function(node) { + this.next() + // import '...' + if (this.type === tt.string) { + node.specifiers = empty + node.source = this.parseExprAtom() + } else { + node.specifiers = this.parseImportSpecifiers() + this.expectContextual("from") + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + } + this.semicolon() + return this.finishNode(node, "ImportDeclaration") +} + +// Parses a comma-separated list of module imports. + +pp.parseImportSpecifiers = function() { + let nodes = [], first = true + if (this.type === tt.name) { + // import defaultObj, { x, y as z } from '...' + let node = this.startNode() + node.local = this.parseIdent() + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportDefaultSpecifier")) + if (!this.eat(tt.comma)) return nodes + } + if (this.type === tt.star) { + let node = this.startNode() + this.next() + this.expectContextual("as") + node.local = this.parseIdent() + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportNamespaceSpecifier")) + return nodes + } + this.expect(tt.braceL) + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma) + if (this.afterTrailingComma(tt.braceR)) break + } else first = false + + let node = this.startNode() + node.imported = this.parseIdent(true) + if (this.eatContextual("as")) { + node.local = this.parseIdent() + } else { + node.local = node.imported + if (this.isKeyword(node.local.name)) this.unexpected(node.local.start) + if (this.reservedWordsStrict.test(node.local.name)) this.raise(node.local.start, "The keyword '" + node.local.name + "' is reserved") + } + this.checkLVal(node.local, true) + nodes.push(this.finishNode(node, "ImportSpecifier")) + } + return nodes +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/tokencontext.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/tokencontext.js b/node_modules/acorn/src/tokencontext.js new file mode 100644 index 0000000..c76fc9d --- /dev/null +++ b/node_modules/acorn/src/tokencontext.js @@ -0,0 +1,109 @@ +// The algorithm used to determine whether a regexp can appear at a +// given point in the program is loosely based on sweet.js' approach. +// See https://github.com/mozilla/sweet.js/wiki/design + +import {Parser} from "./state" +import {types as tt} from "./tokentype" +import {lineBreak} from "./whitespace" + +export class TokContext { + constructor(token, isExpr, preserveSpace, override) { + this.token = token + this.isExpr = !!isExpr + this.preserveSpace = !!preserveSpace + this.override = override + } +} + +export const types = { + b_stat: new TokContext("{", false), + b_expr: new TokContext("{", true), + b_tmpl: new TokContext("${", true), + p_stat: new TokContext("(", false), + p_expr: new TokContext("(", true), + q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()), + f_expr: new TokContext("function", true) +} + +const pp = Parser.prototype + +pp.initialContext = function() { + return [types.b_stat] +} + +pp.braceIsBlock = function(prevType) { + if (prevType === tt.colon) { + let parent = this.curContext() + if (parent === types.b_stat || parent === types.b_expr) + return !parent.isExpr + } + if (prevType === tt._return) + return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) + if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR) + return true + if (prevType == tt.braceL) + return this.curContext() === types.b_stat + return !this.exprAllowed +} + +pp.updateContext = function(prevType) { + let update, type = this.type + if (type.keyword && prevType == tt.dot) + this.exprAllowed = false + else if (update = type.updateContext) + update.call(this, prevType) + else + this.exprAllowed = type.beforeExpr +} + +// Token-specific context update code + +tt.parenR.updateContext = tt.braceR.updateContext = function() { + if (this.context.length == 1) { + this.exprAllowed = true + return + } + let out = this.context.pop() + if (out === types.b_stat && this.curContext() === types.f_expr) { + this.context.pop() + this.exprAllowed = false + } else if (out === types.b_tmpl) { + this.exprAllowed = true + } else { + this.exprAllowed = !out.isExpr + } +} + +tt.braceL.updateContext = function(prevType) { + this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr) + this.exprAllowed = true +} + +tt.dollarBraceL.updateContext = function() { + this.context.push(types.b_tmpl) + this.exprAllowed = true +} + +tt.parenL.updateContext = function(prevType) { + let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while + this.context.push(statementParens ? types.p_stat : types.p_expr) + this.exprAllowed = true +} + +tt.incDec.updateContext = function() { + // tokExprAllowed stays unchanged +} + +tt._function.updateContext = function() { + if (this.curContext() !== types.b_stat) + this.context.push(types.f_expr) + this.exprAllowed = false +} + +tt.backQuote.updateContext = function() { + if (this.curContext() === types.q_tmpl) + this.context.pop() + else + this.context.push(types.q_tmpl) + this.exprAllowed = false +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/tokenize.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/tokenize.js b/node_modules/acorn/src/tokenize.js new file mode 100644 index 0000000..1b2bd95 --- /dev/null +++ b/node_modules/acorn/src/tokenize.js @@ -0,0 +1,682 @@ +import {isIdentifierStart, isIdentifierChar} from "./identifier" +import {types as tt, keywords as keywordTypes} from "./tokentype" +import {Parser} from "./state" +import {SourceLocation} from "./locutil" +import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace" + +// Object type used to represent tokens. Note that normally, tokens +// simply exist as properties on the parser object. This is only +// used for the onToken callback and the external tokenizer. + +export class Token { + constructor(p) { + this.type = p.type + this.value = p.value + this.start = p.start + this.end = p.end + if (p.options.locations) + this.loc = new SourceLocation(p, p.startLoc, p.endLoc) + if (p.options.ranges) + this.range = [p.start, p.end] + } +} + +// ## Tokenizer + +const pp = Parser.prototype + +// Are we running under Rhino? +const isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]" + +// Move to the next token + +pp.next = function() { + if (this.options.onToken) + this.options.onToken(new Token(this)) + + this.lastTokEnd = this.end + this.lastTokStart = this.start + this.lastTokEndLoc = this.endLoc + this.lastTokStartLoc = this.startLoc + this.nextToken() +} + +pp.getToken = function() { + this.next() + return new Token(this) +} + +// If we're in an ES6 environment, make parsers iterable +if (typeof Symbol !== "undefined") + pp[Symbol.iterator] = function () { + let self = this + return {next: function () { + let token = self.getToken() + return { + done: token.type === tt.eof, + value: token + } + }} + } + +// Toggle strict mode. Re-reads the next number or string to please +// pedantic tests (`"use strict"; 010;` should fail). + +pp.setStrict = function(strict) { + this.strict = strict + if (this.type !== tt.num && this.type !== tt.string) return + this.pos = this.start + if (this.options.locations) { + while (this.pos < this.lineStart) { + this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1 + --this.curLine + } + } + this.nextToken() +} + +pp.curContext = function() { + return this.context[this.context.length - 1] +} + +// Read a single token, updating the parser object's token-related +// properties. + +pp.nextToken = function() { + let curContext = this.curContext() + if (!curContext || !curContext.preserveSpace) this.skipSpace() + + this.start = this.pos + if (this.options.locations) this.startLoc = this.curPosition() + if (this.pos >= this.input.length) return this.finishToken(tt.eof) + + if (curContext.override) return curContext.override(this) + else this.readToken(this.fullCharCodeAtPos()) +} + +pp.readToken = function(code) { + // Identifier or keyword. '\uXXXX' sequences are allowed in + // identifiers, so '\' also dispatches to that. + if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) + return this.readWord() + + return this.getTokenFromCode(code) +} + +pp.fullCharCodeAtPos = function() { + let code = this.input.charCodeAt(this.pos) + if (code <= 0xd7ff || code >= 0xe000) return code + let next = this.input.charCodeAt(this.pos + 1) + return (code << 10) + next - 0x35fdc00 +} + +pp.skipBlockComment = function() { + let startLoc = this.options.onComment && this.curPosition() + let start = this.pos, end = this.input.indexOf("*/", this.pos += 2) + if (end === -1) this.raise(this.pos - 2, "Unterminated comment") + this.pos = end + 2 + if (this.options.locations) { + lineBreakG.lastIndex = start + let match + while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { + ++this.curLine + this.lineStart = match.index + match[0].length + } + } + if (this.options.onComment) + this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, + startLoc, this.curPosition()) +} + +pp.skipLineComment = function(startSkip) { + let start = this.pos + let startLoc = this.options.onComment && this.curPosition() + let ch = this.input.charCodeAt(this.pos+=startSkip) + while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { + ++this.pos + ch = this.input.charCodeAt(this.pos) + } + if (this.options.onComment) + this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, + startLoc, this.curPosition()) +} + +// Called at the start of the parse and after every token. Skips +// whitespace and comments, and. + +pp.skipSpace = function() { + loop: while (this.pos < this.input.length) { + let ch = this.input.charCodeAt(this.pos) + switch (ch) { + case 32: case 160: // ' ' + ++this.pos + break + case 13: + if (this.input.charCodeAt(this.pos + 1) === 10) { + ++this.pos + } + case 10: case 8232: case 8233: + ++this.pos + if (this.options.locations) { + ++this.curLine + this.lineStart = this.pos + } + break + case 47: // '/' + switch (this.input.charCodeAt(this.pos + 1)) { + case 42: // '*' + this.skipBlockComment() + break + case 47: + this.skipLineComment(2) + break + default: + break loop + } + break + default: + if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { + ++this.pos + } else { + break loop + } + } + } +} + +// Called at the end of every token. Sets `end`, `val`, and +// maintains `context` and `exprAllowed`, and skips the space after +// the token, so that the next one's `start` will point at the +// right position. + +pp.finishToken = function(type, val) { + this.end = this.pos + if (this.options.locations) this.endLoc = this.curPosition() + let prevType = this.type + this.type = type + this.value = val + + this.updateContext(prevType) +} + +// ### Token reading + +// This is the function that is called to fetch the next token. It +// is somewhat obscure, because it works in character codes rather +// than characters, and because operator parsing has been inlined +// into it. +// +// All in the name of speed. +// +pp.readToken_dot = function() { + let next = this.input.charCodeAt(this.pos + 1) + if (next >= 48 && next <= 57) return this.readNumber(true) + let next2 = this.input.charCodeAt(this.pos + 2) + if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.' + this.pos += 3 + return this.finishToken(tt.ellipsis) + } else { + ++this.pos + return this.finishToken(tt.dot) + } +} + +pp.readToken_slash = function() { // '/' + let next = this.input.charCodeAt(this.pos + 1) + if (this.exprAllowed) {++this.pos; return this.readRegexp();} + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(tt.slash, 1) +} + +pp.readToken_mult_modulo = function(code) { // '%*' + let next = this.input.charCodeAt(this.pos + 1) + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(code === 42 ? tt.star : tt.modulo, 1) +} + +pp.readToken_pipe_amp = function(code) { // '|&' + let next = this.input.charCodeAt(this.pos + 1) + if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2) + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1) +} + +pp.readToken_caret = function() { // '^' + let next = this.input.charCodeAt(this.pos + 1) + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(tt.bitwiseXOR, 1) +} + +pp.readToken_plus_min = function(code) { // '+-' + let next = this.input.charCodeAt(this.pos + 1) + if (next === code) { + if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && + lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) { + // A `-->` line comment + this.skipLineComment(3) + this.skipSpace() + return this.nextToken() + } + return this.finishOp(tt.incDec, 2) + } + if (next === 61) return this.finishOp(tt.assign, 2) + return this.finishOp(tt.plusMin, 1) +} + +pp.readToken_lt_gt = function(code) { // '<>' + let next = this.input.charCodeAt(this.pos + 1) + let size = 1 + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2 + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1) + return this.finishOp(tt.bitShift, size) + } + if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && + this.input.charCodeAt(this.pos + 3) == 45) { + if (this.inModule) this.unexpected() + // `<!--`, an XML-style comment that should be interpreted as a line comment + this.skipLineComment(4) + this.skipSpace() + return this.nextToken() + } + if (next === 61) + size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2 + return this.finishOp(tt.relational, size) +} + +pp.readToken_eq_excl = function(code) { // '=!' + let next = this.input.charCodeAt(this.pos + 1) + if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) + if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>' + this.pos += 2 + return this.finishToken(tt.arrow) + } + return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1) +} + +pp.getTokenFromCode = function(code) { + switch (code) { + // The interpretation of a dot depends on whether it is followed + // by a digit or another two dots. + case 46: // '.' + return this.readToken_dot() + + // Punctuation tokens. + case 40: ++this.pos; return this.finishToken(tt.parenL) + case 41: ++this.pos; return this.finishToken(tt.parenR) + case 59: ++this.pos; return this.finishToken(tt.semi) + case 44: ++this.pos; return this.finishToken(tt.comma) + case 91: ++this.pos; return this.finishToken(tt.bracketL) + case 93: ++this.pos; return this.finishToken(tt.bracketR) + case 123: ++this.pos; return this.finishToken(tt.braceL) + case 125: ++this.pos; return this.finishToken(tt.braceR) + case 58: ++this.pos; return this.finishToken(tt.colon) + case 63: ++this.pos; return this.finishToken(tt.question) + + case 96: // '`' + if (this.options.ecmaVersion < 6) break + ++this.pos + return this.finishToken(tt.backQuote) + + case 48: // '0' + let next = this.input.charCodeAt(this.pos + 1) + if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number + if (this.options.ecmaVersion >= 6) { + if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number + if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number + } + // Anything else beginning with a digit is an integer, octal + // number, or float. + case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9 + return this.readNumber(false) + + // Quotes produce strings. + case 34: case 39: // '"', "'" + return this.readString(code) + + // Operators are parsed inline in tiny state machines. '=' (61) is + // often referred to. `finishOp` simply skips the amount of + // characters it is given as second argument, and returns a token + // of the type given by its first argument. + + case 47: // '/' + return this.readToken_slash() + + case 37: case 42: // '%*' + return this.readToken_mult_modulo(code) + + case 124: case 38: // '|&' + return this.readToken_pipe_amp(code) + + case 94: // '^' + return this.readToken_caret() + + case 43: case 45: // '+-' + return this.readToken_plus_min(code) + + case 60: case 62: // '<>' + return this.readToken_lt_gt(code) + + case 61: case 33: // '=!' + return this.readToken_eq_excl(code) + + case 126: // '~' + return this.finishOp(tt.prefix, 1) + } + + this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'") +} + +pp.finishOp = function(type, size) { + let str = this.input.slice(this.pos, this.pos + size) + this.pos += size + return this.finishToken(type, str) +} + +// Parse a regular expression. Some context-awareness is necessary, +// since a '/' inside a '[]' set does not end the expression. + +function tryCreateRegexp(src, flags, throwErrorAt, parser) { + try { + return new RegExp(src, flags); + } catch (e) { + if (throwErrorAt !== undefined) { + if (e instanceof SyntaxError) parser.raise(throwErrorAt, "Error parsing regular expression: " + e.message) + throw e + } + } +} + +var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u"); + +pp.readRegexp = function() { + let escaped, inClass, start = this.pos + for (;;) { + if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression") + let ch = this.input.charAt(this.pos) + if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression") + if (!escaped) { + if (ch === "[") inClass = true + else if (ch === "]" && inClass) inClass = false + else if (ch === "/" && !inClass) break + escaped = ch === "\\" + } else escaped = false + ++this.pos + } + let content = this.input.slice(start, this.pos) + ++this.pos + // Need to use `readWord1` because '\uXXXX' sequences are allowed + // here (don't ask). + let mods = this.readWord1() + let tmp = content + if (mods) { + let validFlags = /^[gim]*$/ + if (this.options.ecmaVersion >= 6) validFlags = /^[gimuy]*$/ + if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag") + if (mods.indexOf('u') >= 0 && !regexpUnicodeSupport) { + // Replace each astral symbol and every Unicode escape sequence that + // possibly represents an astral symbol or a paired surrogate with a + // single ASCII symbol to avoid throwing on regular expressions that + // are only valid in combination with the `/u` flag. + // Note: replacing with the ASCII symbol `x` might cause false + // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a + // perfectly valid pattern that is equivalent to `[a-b]`, but it would + // be replaced by `[x-b]` which throws an error. + tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, (_match, code, offset) => { + code = Number("0x" + code) + if (code > 0x10FFFF) this.raise(start + offset + 3, "Code point out of bounds") + return "x" + }); + tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x") + } + } + // Detect invalid regular expressions. + let value = null + // Rhino's regular expression parser is flaky and throws uncatchable exceptions, + // so don't do detection if we are running under Rhino + if (!isRhino) { + tryCreateRegexp(tmp, undefined, start, this); + // Get a regular expression object for this pattern-flag pair, or `null` in + // case the current environment doesn't support the flags it uses. + value = tryCreateRegexp(content, mods) + } + return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value}) +} + +// Read an integer in the given radix. Return null if zero digits +// were read, the integer value otherwise. When `len` is given, this +// will return `null` unless the integer has exactly `len` digits. + +pp.readInt = function(radix, len) { + let start = this.pos, total = 0 + for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) { + let code = this.input.charCodeAt(this.pos), val + if (code >= 97) val = code - 97 + 10; // a + else if (code >= 65) val = code - 65 + 10; // A + else if (code >= 48 && code <= 57) val = code - 48; // 0-9 + else val = Infinity + if (val >= radix) break + ++this.pos + total = total * radix + val + } + if (this.pos === start || len != null && this.pos - start !== len) return null + + return total +} + +pp.readRadixNumber = function(radix) { + this.pos += 2; // 0x + let val = this.readInt(radix) + if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix) + if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number") + return this.finishToken(tt.num, val) +} + +// Read an integer, octal integer, or floating-point number. + +pp.readNumber = function(startsWithDot) { + let start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48 + if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number") + let next = this.input.charCodeAt(this.pos) + if (next === 46) { // '.' + ++this.pos + this.readInt(10) + isFloat = true + next = this.input.charCodeAt(this.pos) + } + if (next === 69 || next === 101) { // 'eE' + next = this.input.charCodeAt(++this.pos) + if (next === 43 || next === 45) ++this.pos; // '+-' + if (this.readInt(10) === null) this.raise(start, "Invalid number") + isFloat = true + } + if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number") + + let str = this.input.slice(start, this.pos), val + if (isFloat) val = parseFloat(str) + else if (!octal || str.length === 1) val = parseInt(str, 10) + else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number") + else val = parseInt(str, 8) + return this.finishToken(tt.num, val) +} + +// Read a string value, interpreting backslash-escapes. + +pp.readCodePoint = function() { + let ch = this.input.charCodeAt(this.pos), code + + if (ch === 123) { + if (this.options.ecmaVersion < 6) this.unexpected() + let codePos = ++this.pos + code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos) + ++this.pos + if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds") + } else { + code = this.readHexChar(4) + } + return code +} + +function codePointToString(code) { + // UTF-16 Decoding + if (code <= 0xFFFF) return String.fromCharCode(code) + code -= 0x10000 + return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00) +} + +pp.readString = function(quote) { + let out = "", chunkStart = ++this.pos + for (;;) { + if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant") + let ch = this.input.charCodeAt(this.pos) + if (ch === quote) break + if (ch === 92) { // '\' + out += this.input.slice(chunkStart, this.pos) + out += this.readEscapedChar(false) + chunkStart = this.pos + } else { + if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant") + ++this.pos + } + } + out += this.input.slice(chunkStart, this.pos++) + return this.finishToken(tt.string, out) +} + +// Reads template string tokens. + +pp.readTmplToken = function() { + let out = "", chunkStart = this.pos + for (;;) { + if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template") + let ch = this.input.charCodeAt(this.pos) + if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${' + if (this.pos === this.start && this.type === tt.template) { + if (ch === 36) { + this.pos += 2 + return this.finishToken(tt.dollarBraceL) + } else { + ++this.pos + return this.finishToken(tt.backQuote) + } + } + out += this.input.slice(chunkStart, this.pos) + return this.finishToken(tt.template, out) + } + if (ch === 92) { // '\' + out += this.input.slice(chunkStart, this.pos) + out += this.readEscapedChar(true) + chunkStart = this.pos + } else if (isNewLine(ch)) { + out += this.input.slice(chunkStart, this.pos) + ++this.pos + switch (ch) { + case 13: + if (this.input.charCodeAt(this.pos) === 10) ++this.pos; + case 10: + out += "\n"; + break; + default: + out += String.fromCharCode(ch); + break; + } + if (this.options.locations) { + ++this.curLine + this.lineStart = this.pos + } + chunkStart = this.pos + } else { + ++this.pos + } + } +} + +// Used to read escaped characters + +pp.readEscapedChar = function(inTemplate) { + let ch = this.input.charCodeAt(++this.pos) + ++this.pos + switch (ch) { + case 110: return "\n"; // 'n' -> '\n' + case 114: return "\r"; // 'r' -> '\r' + case 120: return String.fromCharCode(this.readHexChar(2)); // 'x' + case 117: return codePointToString(this.readCodePoint()); // 'u' + case 116: return "\t"; // 't' -> '\t' + case 98: return "\b"; // 'b' -> '\b' + case 118: return "\u000b"; // 'v' -> '\u000b' + case 102: return "\f"; // 'f' -> '\f' + case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n' + case 10: // ' \n' + if (this.options.locations) { this.lineStart = this.pos; ++this.curLine } + return "" + default: + if (ch >= 48 && ch <= 55) { + let octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0] + let octal = parseInt(octalStr, 8) + if (octal > 255) { + octalStr = octalStr.slice(0, -1) + octal = parseInt(octalStr, 8) + } + if (octalStr !== "0" && (this.strict || inTemplate)) { + this.raise(this.pos - 2, "Octal literal in strict mode") + } + this.pos += octalStr.length - 1 + return String.fromCharCode(octal) + } + return String.fromCharCode(ch) + } +} + +// Used to read character escape sequences ('\x', '\u', '\U'). + +pp.readHexChar = function(len) { + let codePos = this.pos + let n = this.readInt(16, len) + if (n === null) this.raise(codePos, "Bad character escape sequence") + return n +} + +// Read an identifier, and return it as a string. Sets `this.containsEsc` +// to whether the word contained a '\u' escape. +// +// Incrementally adds only escaped chars, adding other chunks as-is +// as a micro-optimization. + +pp.readWord1 = function() { + this.containsEsc = false + let word = "", first = true, chunkStart = this.pos + let astral = this.options.ecmaVersion >= 6 + while (this.pos < this.input.length) { + let ch = this.fullCharCodeAtPos() + if (isIdentifierChar(ch, astral)) { + this.pos += ch <= 0xffff ? 1 : 2 + } else if (ch === 92) { // "\" + this.containsEsc = true + word += this.input.slice(chunkStart, this.pos) + let escStart = this.pos + if (this.input.charCodeAt(++this.pos) != 117) // "u" + this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX") + ++this.pos + let esc = this.readCodePoint() + if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral)) + this.raise(escStart, "Invalid Unicode escape") + word += codePointToString(esc) + chunkStart = this.pos + } else { + break + } + first = false + } + return word + this.input.slice(chunkStart, this.pos) +} + +// Read an identifier or keyword token. Will check for reserved +// words when necessary. + +pp.readWord = function() { + let word = this.readWord1() + let type = tt.name + if ((this.options.ecmaVersion >= 6 || !this.containsEsc) && this.keywords.test(word)) + type = keywordTypes[word] + return this.finishToken(type, word) +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/tokentype.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/tokentype.js b/node_modules/acorn/src/tokentype.js new file mode 100644 index 0000000..e4efd2b --- /dev/null +++ b/node_modules/acorn/src/tokentype.js @@ -0,0 +1,147 @@ +// ## Token types + +// The assignment of fine-grained, information-carrying type objects +// allows the tokenizer to store the information it has about a +// token in a way that is very cheap for the parser to look up. + +// All token type variables start with an underscore, to make them +// easy to recognize. + +// The `beforeExpr` property is used to disambiguate between regular +// expressions and divisions. It is set on all token types that can +// be followed by an expression (thus, a slash after them would be a +// regular expression). +// +// The `startsExpr` property is used to check if the token ends a +// `yield` expression. It is set on all token types that either can +// directly start an expression (like a quotation mark) or can +// continue an expression (like the body of a string). +// +// `isLoop` marks a keyword as starting a loop, which is important +// to know when parsing a label, in order to allow or disallow +// continue jumps to that label. + +export class TokenType { + constructor(label, conf = {}) { + this.label = label + this.keyword = conf.keyword + this.beforeExpr = !!conf.beforeExpr + this.startsExpr = !!conf.startsExpr + this.isLoop = !!conf.isLoop + this.isAssign = !!conf.isAssign + this.prefix = !!conf.prefix + this.postfix = !!conf.postfix + this.binop = conf.binop || null + this.updateContext = null + } +} + +function binop(name, prec) { + return new TokenType(name, {beforeExpr: true, binop: prec}) +} +const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true} + +export const types = { + num: new TokenType("num", startsExpr), + regexp: new TokenType("regexp", startsExpr), + string: new TokenType("string", startsExpr), + name: new TokenType("name", startsExpr), + eof: new TokenType("eof"), + + // Punctuation token types. + bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}), + bracketR: new TokenType("]"), + braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}), + braceR: new TokenType("}"), + parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}), + parenR: new TokenType(")"), + comma: new TokenType(",", beforeExpr), + semi: new TokenType(";", beforeExpr), + colon: new TokenType(":", beforeExpr), + dot: new TokenType("."), + question: new TokenType("?", beforeExpr), + arrow: new TokenType("=>", beforeExpr), + template: new TokenType("template"), + ellipsis: new TokenType("...", beforeExpr), + backQuote: new TokenType("`", startsExpr), + dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}), + + // Operators. These carry several kinds of properties to help the + // parser use them properly (the presence of these properties is + // what categorizes them as operators). + // + // `binop`, when present, specifies that this operator is a binary + // operator, and will refer to its precedence. + // + // `prefix` and `postfix` mark the operator as a prefix or postfix + // unary operator. + // + // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as + // binary operators with a very low precedence, that should result + // in AssignmentExpression nodes. + + eq: new TokenType("=", {beforeExpr: true, isAssign: true}), + assign: new TokenType("_=", {beforeExpr: true, isAssign: true}), + incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}), + prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}), + logicalOR: binop("||", 1), + logicalAND: binop("&&", 2), + bitwiseOR: binop("|", 3), + bitwiseXOR: binop("^", 4), + bitwiseAND: binop("&", 5), + equality: binop("==/!=", 6), + relational: binop("</>", 7), + bitShift: binop("<</>>", 8), + plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}), + modulo: binop("%", 10), + star: binop("*", 10), + slash: binop("/", 10) +} + +// Map keyword names to token types. + +export const keywords = {} + +// Succinct definitions of keyword token types +function kw(name, options = {}) { + options.keyword = name + keywords[name] = types["_" + name] = new TokenType(name, options) +} + +kw("break") +kw("case", beforeExpr) +kw("catch") +kw("continue") +kw("debugger") +kw("default", beforeExpr) +kw("do", {isLoop: true, beforeExpr: true}) +kw("else", beforeExpr) +kw("finally") +kw("for", {isLoop: true}) +kw("function", startsExpr) +kw("if") +kw("return", beforeExpr) +kw("switch") +kw("throw", beforeExpr) +kw("try") +kw("var") +kw("let") +kw("const") +kw("while", {isLoop: true}) +kw("with") +kw("new", {beforeExpr: true, startsExpr: true}) +kw("this", startsExpr) +kw("super", startsExpr) +kw("class") +kw("extends", beforeExpr) +kw("export") +kw("import") +kw("yield", {beforeExpr: true, startsExpr: true}) +kw("null", startsExpr) +kw("true", startsExpr) +kw("false", startsExpr) +kw("in", {beforeExpr: true, binop: 7}) +kw("instanceof", {beforeExpr: true, binop: 7}) +kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true}) +kw("void", {beforeExpr: true, prefix: true, startsExpr: true}) +kw("delete", {beforeExpr: true, prefix: true, startsExpr: true}) http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/util.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/util.js b/node_modules/acorn/src/util.js new file mode 100644 index 0000000..3517f8d --- /dev/null +++ b/node_modules/acorn/src/util.js @@ -0,0 +1,9 @@ +export function isArray(obj) { + return Object.prototype.toString.call(obj) === "[object Array]" +} + +// Checks if an object has a property. + +export function has(obj, propName) { + return Object.prototype.hasOwnProperty.call(obj, propName) +} http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/acorn/src/walk/index.js ---------------------------------------------------------------------- diff --git a/node_modules/acorn/src/walk/index.js b/node_modules/acorn/src/walk/index.js new file mode 100644 index 0000000..9f45a0c --- /dev/null +++ b/node_modules/acorn/src/walk/index.js @@ -0,0 +1,340 @@ +// 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, override) { + 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, override) +} + +// 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, override) { + let visitor = funcs ? exports.make(funcs, base) : base + ;(function c(node, st, override) { + visitor[override || node.type](node, st, c) + })(node, state, override) +} + +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 ((start == null || node.start == start) && + (end == null || node.end == end) && + test(type, node)) + 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 = + (node, st, c) => c(node.argument, st, "Expression") +base.TryStatement = (node, st, c) => { + c(node.block, st, "Statement") + if (node.handler) { + c(node.handler.param, st, "Pattern") + 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) + c(node.declarations[i], st) +} +base.VariableDeclarator = (node, st, c) => { + c(node.id, st, "Pattern") + if (node.init) c(node.init, st, "Expression") +} + +base.Function = (node, st, c) => { + if (node.id) c(node.id, st, "Pattern") + for (let i = 0; i < node.params.length; i++) + c(node.params[i], st, "Pattern") + c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody") +} +// FIXME drop these node types in next major version +// (They are awkward, and in ES6 every block can be a scope.) +base.ScopeBody = (node, st, c) => c(node, st, "Statement") +base.ScopeExpression = (node, st, c) => c(node, st, "Expression") + +base.Pattern = (node, st, c) => { + if (node.type == "Identifier") + c(node, st, "VariablePattern") + else if (node.type == "MemberExpression") + c(node, st, "MemberPattern") + else + c(node, st) +} +base.VariablePattern = ignore +base.MemberPattern = skipThrough +base.RestElement = (node, st, c) => c(node.argument, st, "Pattern") +base.ArrayPattern = (node, st, c) => { + for (let i = 0; i < node.elements.length; ++i) { + let elt = node.elements[i] + if (elt) c(elt, st, "Pattern") + } +} +base.ObjectPattern = (node, st, c) => { + for (let i = 0; i < node.properties.length; ++i) + c(node.properties[i].value, st, "Pattern") +} + +base.Expression = skipThrough +base.ThisExpression = base.Super = base.MetaProperty = ignore +base.ArrayExpression = (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 = (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.LogicalExpression = (node, st, c) => { + c(node.left, st, "Expression") + c(node.right, st, "Expression") +} +base.AssignmentExpression = base.AssignmentPattern = (node, st, c) => { + c(node.left, st, "Pattern") + 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.ExportNamedDeclaration = base.ExportDefaultDeclaration = (node, st, c) => { + if (node.declaration) + c(node.declaration, st, node.type == "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression") + if (node.source) c(node.source, st, "Expression") +} +base.ExportAllDeclaration = (node, st, c) => { + c(node.source, st, "Expression") +} +base.ImportDeclaration = (node, st, c) => { + for (let i = 0; i < node.specifiers.length; i++) + c(node.specifiers[i], st) + c(node.source, st, "Expression") +} +base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = 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) => c(node, st, "Class") +base.Class = (node, st, c) => { + if (node.id) c(node.id, st, "Pattern") + 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") +}
