diff --git a/README.md b/README.md index 73750cff1d..26d724de52 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,11 @@ object referring to that same position. - **onToken**: If a function is passed for this option, each found token will be passed in same format as `tokenize()` returns. + If array is passed, each found token is pushed to it. + + Note that you are not allowed to call the parser from the + callback—that will corrupt its internal state. + - **onComment**: If a function is passed for this option, whenever a comment is encountered the function will be called with the following parameters: @@ -90,6 +95,18 @@ object referring to that same position. of the comment’s start and end are passed as two additional parameters. + If array is passed for this option, each found comment is pushed + to it as object in Esprima format: + + ```javascript + { + "type": "Line" | "Block", + "value": "comment text", + "range": ..., + "loc": ... + } + ``` + Note that you are not allowed to call the parser from the callback—that will corrupt its internal state. @@ -125,9 +142,8 @@ Acorn's tokenizer. The function takes an input string and options similar to `parse` (though only some options are meaningful here), and returns a function that can be called repeatedly to read a single token, and returns a `{start, end, type, value}` object (with added -`startLoc` and `endLoc` properties when the `locations` option is -enabled). This object will be reused (updated) for each token, so you -can't count on it staying stable. +`loc` property when the `locations` option is enabled and `range` +property when the `ranges` option is enabled). **tokTypes** holds an object mapping names to the token type objects that end up in the `type` properties of tokens. @@ -136,8 +152,7 @@ that end up in the `type` properties of tokens. Escodegen supports generating comments from AST, attached in Esprima-specific format. In order to simulate same format in -Acorn, consider following example (this may be simplified -in future): +Acorn, consider following example: ```javascript var comments = [], tokens = []; @@ -146,19 +161,9 @@ var ast = acorn.parse('var x = 42; // answer', { // collect ranges for each node ranges: true, // collect comments in Esprima's format - onComment: function (block, text, start, end) { - comments.push({ - type: block ? 'Block' : 'Line', - value: text, - range: [start, end] - }); - }, + onComment: comments, // collect token ranges - onToken: function (token) { - tokens.push({ - range: [token.start, token.end] - }); - } + onToken: tokens }); // attach comments using collected information diff --git a/acorn.js b/acorn.js index f01d4a4ef5..05124c7edb 100644 --- a/acorn.js +++ b/acorn.js @@ -114,12 +114,40 @@ directSourceFile: null }; + var isArray = function (obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + function setOptions(opts) { options = opts || {}; for (var opt in defaultOptions) if (!has(options, opt)) options[opt] = defaultOptions[opt]; sourceFile = options.sourceFile || null; - + if (isArray(options.onToken)) { + var tokens = options.onToken; + options.onToken = function (token) { + tokens.push(token); + }; + } + if (isArray(options.onComment)) { + var comments = options.onComment; + options.onComment = function (block, text, start, end, startLoc, endLoc) { + var comment = { + type: block ? 'Block' : 'Line', + value: text, + start: start, + end: end + }; + if (options.locations) { + comment.loc = new SourceLocation(); + comment.loc.start = startLoc; + comment.loc.end = endLoc; + } + if (options.ranges) + comment.range = [start, end]; + comments.push(comment); + }; + } isKeyword = options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword; } @@ -141,19 +169,23 @@ return {line: line, column: offset - cur}; }; - var getCurrentToken = function () { - var token = { - type: tokType, - value: tokVal, - start: tokStart, - end: tokEnd - }; + function Token() { + this.type = tokType; + this.value = tokVal; + this.start = tokStart; + this.end = tokEnd; if (options.locations) { - token.startLoc = tokStartLoc; - token.endLoc = tokEndLoc; + this.loc = new SourceLocation(); + this.loc.end = tokEndLoc; + // TODO: remove in next major release + this.startLoc = tokStartLoc; + this.endLoc = tokEndLoc; } - return token; - }; + if (options.ranges) + this.range = [tokStart, tokEnd]; + } + + exports.Token = Token; // Acorn is organized as a tokenizer and a recursive-descent parser. // The `tokenize` export provides an interface to the tokenizer. @@ -170,7 +202,7 @@ function getToken(forceRegexp) { lastEnd = tokEnd; readToken(forceRegexp); - return getCurrentToken(); + return new Token(); } getToken.jumpTo = function(pos, reAllowed) { tokPos = pos; @@ -543,7 +575,7 @@ tokVal = val; tokRegexpAllowed = type.beforeExpr; if (options.onToken) { - options.onToken(getCurrentToken()); + options.onToken(new Token()); } } diff --git a/test/tests.js b/test/tests.js index b51695ad24..936e73a5fb 100644 --- a/test/tests.js +++ b/test/tests.js @@ -28699,72 +28699,90 @@ test("