Make loose parser work with minor interface changes introduced by modularization

This commit is contained in:
Marijn Haverbeke 2015-03-20 12:04:33 +01:00
parent a1fe3a1859
commit dd89c6e112
5 changed files with 133 additions and 130 deletions

View File

@ -41,13 +41,13 @@
acorn.defaultOptions.tabSize = 4;
exports.parse_dammit = function(input, options) {
var p = new LooseParser(options, input);
var p = new LooseParser(input, options);
p.next();
return p.parseTopLevel();
};
var LooseParser = exports.LooseParser = function(input, options) {
this.toks = new acorn.Parser(input, options);
this.toks = acorn.tokenizer(input, options)
this.options = this.toks.options;
this.input = this.toks.input;
this.tok = this.last = {type: tt.eof, start: 0, end: 0};
@ -149,9 +149,9 @@
if (this.options.locations) {
this.toks.curLine = 1;
this.toks.lineStart = acorn.lineBreak.lastIndex = 0;
this.toks.lineStart = acorn.lineBreakG.lastIndex = 0;
var match;
while ((match = acorn.lineBreak.exec(this.input)) && match.index < pos) {
while ((match = acorn.lineBreakG.exec(this.input)) && match.index < pos) {
++this.toks.curLine;
this.toks.lineStart = match.index + match[0].length;
}
@ -273,7 +273,7 @@
lp.canInsertSemicolon = function() {
return this.tok.type === tt.eof || this.tok.type === tt.braceR ||
acorn.newline.test(this.input.slice(this.last.end, this.tok.start));
acorn.lineBreak.test(this.input.slice(this.last.end, this.tok.start));
};
lp.semicolon = function() {

View File

@ -19,15 +19,15 @@
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
import {Parser} from "./state"
import {has, isArray} from "./util"
import {SourceLocation} from "./location"
import {Parser, plugins} from "./state"
import {getOptions} from "./options"
import "./parseutil"
import "./statement"
import "./lval"
import "./expression"
export {Parser} from "./state"
export {defaultOptions} from "./options"
export {SourceLocation} from "./location"
export {getLineInfo} from "./location"
export {Node} from "./node"
@ -35,7 +35,7 @@ export {TokenType, types as tokTypes} from "./tokentype"
export {TokContext, types as tokContexts} from "./tokencontext"
export {isIdentifierChar, isIdentifierStart} from "./identifier"
export {Token} from "./tokenize"
export {isNewLine, lineBreak} from "./whitespace"
export {isNewLine, lineBreak, lineBreakG} from "./whitespace"
export const version = "0.12.1"
@ -53,126 +53,6 @@ export function parse(input, options) {
return p.parseTopLevel(p.options.program || p.startNodeAt(startPos))
}
// 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 not enforced. Disable
// `allowReserved` to enforce them. When this option has the
// value "never", reserved words and keywords can also not be
// used as property names.
allowReserved: true,
// 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 tokenize() returns. 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: {}
}
// Registered plugins
export const plugins = {}
// Interpret and default an options object
function getOptions(opts) {
let options = {}
for (let opt in defaultOptions)
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]
if (isArray(options.onToken)) {
var 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)
}
}
// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.

119
src/options.js Normal file
View File

@ -0,0 +1,119 @@
import {has, isArray} from "./util"
import {SourceLocation} from "./location"
// 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 not enforced. Disable
// `allowReserved` to enforce them. When this option has the
// value "never", reserved words and keywords can also not be
// used as property names.
allowReserved: true,
// 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 tokenize() returns. 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 (isArray(options.onToken)) {
var 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)
}
}

View File

@ -59,6 +59,10 @@ Parser.prototype.extend = function(name, f) {
this[name] = f(this[name])
}
// Registered plugins
export const plugins = {}
Parser.prototype.loadPlugins = function(plugins) {
for (let name in plugins) {
let plugin = exports.plugins[name]

View File

@ -27,7 +27,7 @@
try {
var ast = parse(test.code, testOpts);
} catch(e) {
if (!(e instanceof SyntaxError)) throw e;
if (!(e instanceof SyntaxError)) { console.log(e.stack); throw e; }
if (test.error) {
if (e.message == test.error) callback("ok", test.code);
else callback("fail", test.code,