Comparing version 0.8.0 to 0.9.0
@@ -0,1 +1,76 @@ | ||
Change Log | ||
========== | ||
This file documents all notable changes to PEG.js. The releases follow [semantic | ||
versioning](http://semver.org/). | ||
0.9.0 | ||
----- | ||
Released: August 30, 2015 | ||
### Major Changes | ||
* **Tracing support.** Parsers can be compiled with support for tracing their | ||
progress, which can help debugging complex grammars. This feature is | ||
experimental and is expected to evolve over time as experience is gained. | ||
[More details](https://github.com/pegjs/pegjs/commit/da57118a43a904f753d44d407994cf0b36358adc) | ||
* **Infinite loop detection.** Grammar constructs that could cause infinite | ||
loops in generated parsers are detected during compilation and cause errors. | ||
* **Improved location information API.** The `line`, `column`, and `offset` | ||
functions available in parser code were replaced by a single `location` | ||
function which returns an object describing the current location. Similarly, | ||
the `line`, `column`, and `offset` properties of exceptions were replaced by | ||
a single `location` property. The location is no longer a single point but a | ||
character range, which is meaningful mainly in actions where the range | ||
covers action’s expression. | ||
[More details](https://github.com/pegjs/pegjs/compare/e75f21dc8f0e66b3d87c4c19b3fcb8f89d9c3acd...eaca5f0acf97b66ef141fed84aa95d4e72e33757) | ||
* **Improved error reporting.** All exceptions thrown when generating a parser | ||
have associated location information. And all exceptions thrown by generated | ||
parser and PEG.js itself have a stack trace (the `stack` property) in | ||
environments that support `Error.captureStackTrace`. | ||
* **Strict mode code**. All PEG.js and generated parser code is written using | ||
[JavaScript strict mode](https://developer.mozilla.org/cs/docs/Web/JavaScript/Reference/Strict_mode). | ||
### Minor Changes | ||
* Labels behave like block-scoped variables. This means parser code can see | ||
labels defined outside expressions containing code. | ||
* Empty sequences are no longer allowed. | ||
* Label names can’t be JavaScript reserved words. | ||
* Rule and label names can contain Unicode characters like in JavaScript. | ||
* Rules have to be separated either by `;` or a newline (until now, any | ||
whitespace was enough). | ||
* The PEG.js grammar and all the example grammars were completely rewritten. | ||
This rewrite included a number of cleanups, formatting changes, naming | ||
changes, and bug fixes. | ||
* The parser object can now be accessed as `parser` in parser code. | ||
* Location information computation is faster. | ||
* Added support for Node.js >= 0.10.x, io.js, and Edge. Removed support for | ||
Node.js < 0.10.x. | ||
### Bug Fixes | ||
* Fixed left recursion detector which missed many cases. | ||
* Fixed escaping of U+0100—U+107F and U+1000—U+107F in generated code and | ||
error messages. | ||
* Renamed `parse` and `SyntaxError` to `peg$parse` and `peg$SyntaxError` to | ||
mark them as internal identifiers. | ||
[Complete set of changes](https://github.com/pegjs/pegjs/compare/v0.8.0...v0.9.0) | ||
0.8.0 | ||
@@ -2,0 +77,0 @@ ----- |
@@ -1,4 +0,7 @@ | ||
var utils = require("./utils"); | ||
"use strict"; | ||
module.exports = { | ||
var arrays = require("./utils/arrays"), | ||
objects = require("./utils/objects"); | ||
var compiler = { | ||
/* | ||
@@ -14,3 +17,4 @@ * Compiler passes. | ||
reportMissingRules: require("./compiler/passes/report-missing-rules"), | ||
reportLeftRecursion: require("./compiler/passes/report-left-recursion") | ||
reportLeftRecursion: require("./compiler/passes/report-left-recursion"), | ||
reportInfiniteLoops: require("./compiler/passes/report-infinite-loops") | ||
}, | ||
@@ -33,16 +37,9 @@ transform: { | ||
compile: function(ast, passes) { | ||
var options = arguments.length > 2 ? utils.clone(arguments[2]) : {}, | ||
var options = arguments.length > 2 ? objects.clone(arguments[2]) : {}, | ||
stage; | ||
/* | ||
* Extracted into a function just to silence JSHint complaining about | ||
* creating functions in a loop. | ||
*/ | ||
function runPass(pass) { | ||
pass(ast, options); | ||
} | ||
utils.defaults(options, { | ||
objects.defaults(options, { | ||
allowedStartRules: [ast.rules[0].name], | ||
cache: false, | ||
trace: false, | ||
optimize: "speed", | ||
@@ -54,3 +51,3 @@ output: "parser" | ||
if (passes.hasOwnProperty(stage)) { | ||
utils.each(passes[stage], runPass); | ||
arrays.each(passes[stage], function(p) { p(ast, options); }); | ||
} | ||
@@ -65,1 +62,3 @@ } | ||
}; | ||
module.exports = compiler; |
@@ -0,46 +1,54 @@ | ||
"use strict"; | ||
/* Bytecode instruction opcodes. */ | ||
module.exports = { | ||
var opcodes = { | ||
/* Stack Manipulation */ | ||
PUSH: 0, // PUSH c | ||
PUSH_CURR_POS: 1, // PUSH_CURR_POS | ||
POP: 2, // POP | ||
POP_CURR_POS: 3, // POP_CURR_POS | ||
POP_N: 4, // POP_N n | ||
NIP: 5, // NIP | ||
APPEND: 6, // APPEND | ||
WRAP: 7, // WRAP n | ||
TEXT: 8, // TEXT | ||
PUSH_UNDEFINED: 1, // PUSH_UNDEFINED | ||
PUSH_NULL: 2, // PUSH_NULL | ||
PUSH_FAILED: 3, // PUSH_FAILED | ||
PUSH_EMPTY_ARRAY: 4, // PUSH_EMPTY_ARRAY | ||
PUSH_CURR_POS: 5, // PUSH_CURR_POS | ||
POP: 6, // POP | ||
POP_CURR_POS: 7, // POP_CURR_POS | ||
POP_N: 8, // POP_N n | ||
NIP: 9, // NIP | ||
APPEND: 10, // APPEND | ||
WRAP: 11, // WRAP n | ||
TEXT: 12, // TEXT | ||
/* Conditions and Loops */ | ||
IF: 9, // IF t, f | ||
IF_ERROR: 10, // IF_ERROR t, f | ||
IF_NOT_ERROR: 11, // IF_NOT_ERROR t, f | ||
WHILE_NOT_ERROR: 12, // WHILE_NOT_ERROR b | ||
IF: 13, // IF t, f | ||
IF_ERROR: 14, // IF_ERROR t, f | ||
IF_NOT_ERROR: 15, // IF_NOT_ERROR t, f | ||
WHILE_NOT_ERROR: 16, // WHILE_NOT_ERROR b | ||
/* Matching */ | ||
MATCH_ANY: 13, // MATCH_ANY a, f, ... | ||
MATCH_STRING: 14, // MATCH_STRING s, a, f, ... | ||
MATCH_STRING_IC: 15, // MATCH_STRING_IC s, a, f, ... | ||
MATCH_REGEXP: 16, // MATCH_REGEXP r, a, f, ... | ||
ACCEPT_N: 17, // ACCEPT_N n | ||
ACCEPT_STRING: 18, // ACCEPT_STRING s | ||
FAIL: 19, // FAIL e | ||
MATCH_ANY: 17, // MATCH_ANY a, f, ... | ||
MATCH_STRING: 18, // MATCH_STRING s, a, f, ... | ||
MATCH_STRING_IC: 19, // MATCH_STRING_IC s, a, f, ... | ||
MATCH_REGEXP: 20, // MATCH_REGEXP r, a, f, ... | ||
ACCEPT_N: 21, // ACCEPT_N n | ||
ACCEPT_STRING: 22, // ACCEPT_STRING s | ||
FAIL: 23, // FAIL e | ||
/* Calls */ | ||
REPORT_SAVED_POS: 20, // REPORT_SAVED_POS p | ||
REPORT_CURR_POS: 21, // REPORT_CURR_POS | ||
CALL: 22, // CALL f, n, pc, p1, p2, ..., pN | ||
LOAD_SAVED_POS: 24, // LOAD_SAVED_POS p | ||
UPDATE_SAVED_POS: 25, // UPDATE_SAVED_POS | ||
CALL: 26, // CALL f, n, pc, p1, p2, ..., pN | ||
/* Rules */ | ||
RULE: 23, // RULE r | ||
RULE: 27, // RULE r | ||
/* Failure Reporting */ | ||
SILENT_FAILS_ON: 24, // SILENT_FAILS_ON | ||
SILENT_FAILS_OFF: 25 // SILENT_FAILS_FF | ||
SILENT_FAILS_ON: 28, // SILENT_FAILS_ON | ||
SILENT_FAILS_OFF: 29 // SILENT_FAILS_OFF | ||
}; | ||
module.exports = opcodes; |
@@ -1,4 +0,10 @@ | ||
var utils = require("../../utils"), | ||
op = require("../opcodes"); | ||
"use strict"; | ||
var arrays = require("../../utils/arrays"), | ||
objects = require("../../utils/objects"), | ||
asts = require("../asts"), | ||
visitor = require("../visitor"), | ||
op = require("../opcodes"), | ||
js = require("../javascript"); | ||
/* Generates bytecode. | ||
@@ -16,19 +22,35 @@ * | ||
* | ||
* [1] PUSH_CURR_POS | ||
* [1] PUSH_UNDEFINED | ||
* | ||
* stack.push(undefined); | ||
* | ||
* [2] PUSH_NULL | ||
* | ||
* stack.push(null); | ||
* | ||
* [3] PUSH_FAILED | ||
* | ||
* stack.push(FAILED); | ||
* | ||
* [4] PUSH_EMPTY_ARRAY | ||
* | ||
* stack.push([]); | ||
* | ||
* [5] PUSH_CURR_POS | ||
* | ||
* stack.push(currPos); | ||
* | ||
* [2] POP | ||
* [6] POP | ||
* | ||
* stack.pop(); | ||
* | ||
* [3] POP_CURR_POS | ||
* [7] POP_CURR_POS | ||
* | ||
* currPos = stack.pop(); | ||
* | ||
* [4] POP_N n | ||
* [8] POP_N n | ||
* | ||
* stack.pop(n); | ||
* | ||
* [5] NIP | ||
* [9] NIP | ||
* | ||
@@ -39,3 +61,3 @@ * value = stack.pop(); | ||
* | ||
* [6] APPEND | ||
* [10] APPEND | ||
* | ||
@@ -47,10 +69,9 @@ * value = stack.pop(); | ||
* | ||
* [7] WRAP n | ||
* [11] WRAP n | ||
* | ||
* stack.push(stack.pop(n)); | ||
* | ||
* [8] TEXT | ||
* [12] TEXT | ||
* | ||
* stack.pop(); | ||
* stack.push(input.substring(stack.top(), currPos)); | ||
* stack.push(input.substring(stack.pop(), currPos)); | ||
* | ||
@@ -60,3 +81,3 @@ * Conditions and Loops | ||
* | ||
* [9] IF t, f | ||
* [13] IF t, f | ||
* | ||
@@ -69,3 +90,3 @@ * if (stack.top()) { | ||
* | ||
* [10] IF_ERROR t, f | ||
* [14] IF_ERROR t, f | ||
* | ||
@@ -78,3 +99,3 @@ * if (stack.top() === FAILED) { | ||
* | ||
* [11] IF_NOT_ERROR t, f | ||
* [15] IF_NOT_ERROR t, f | ||
* | ||
@@ -87,3 +108,3 @@ * if (stack.top() !== FAILED) { | ||
* | ||
* [12] WHILE_NOT_ERROR b | ||
* [16] WHILE_NOT_ERROR b | ||
* | ||
@@ -97,3 +118,3 @@ * while(stack.top() !== FAILED) { | ||
* | ||
* [13] MATCH_ANY a, f, ... | ||
* [17] MATCH_ANY a, f, ... | ||
* | ||
@@ -106,3 +127,3 @@ * if (input.length > currPos) { | ||
* | ||
* [14] MATCH_STRING s, a, f, ... | ||
* [18] MATCH_STRING s, a, f, ... | ||
* | ||
@@ -115,3 +136,3 @@ * if (input.substr(currPos, consts[s].length) === consts[s]) { | ||
* | ||
* [15] MATCH_STRING_IC s, a, f, ... | ||
* [19] MATCH_STRING_IC s, a, f, ... | ||
* | ||
@@ -124,3 +145,3 @@ * if (input.substr(currPos, consts[s].length).toLowerCase() === consts[s]) { | ||
* | ||
* [16] MATCH_REGEXP r, a, f, ... | ||
* [20] MATCH_REGEXP r, a, f, ... | ||
* | ||
@@ -133,3 +154,3 @@ * if (consts[r].test(input.charAt(currPos))) { | ||
* | ||
* [17] ACCEPT_N n | ||
* [21] ACCEPT_N n | ||
* | ||
@@ -139,3 +160,3 @@ * stack.push(input.substring(currPos, n)); | ||
* | ||
* [18] ACCEPT_STRING s | ||
* [22] ACCEPT_STRING s | ||
* | ||
@@ -145,3 +166,3 @@ * stack.push(consts[s]); | ||
* | ||
* [19] FAIL e | ||
* [23] FAIL e | ||
* | ||
@@ -154,11 +175,11 @@ * stack.push(FAILED); | ||
* | ||
* [20] REPORT_SAVED_POS p | ||
* [24] LOAD_SAVED_POS p | ||
* | ||
* reportedPos = stack[p]; | ||
* savedPos = stack[p]; | ||
* | ||
* [21] REPORT_CURR_POS | ||
* [25] UPDATE_SAVED_POS | ||
* | ||
* reportedPos = currPos; | ||
* savedPos = currPos; | ||
* | ||
* [22] CALL f, n, pc, p1, p2, ..., pN | ||
* [26] CALL f, n, pc, p1, p2, ..., pN | ||
* | ||
@@ -172,3 +193,3 @@ * value = consts[f](stack[p1], ..., stack[pN]); | ||
* | ||
* [23] RULE r | ||
* [27] RULE r | ||
* | ||
@@ -180,15 +201,15 @@ * stack.push(parseRule(r)); | ||
* | ||
* [24] SILENT_FAILS_ON | ||
* [28] SILENT_FAILS_ON | ||
* | ||
* silentFails++; | ||
* | ||
* [25] SILENT_FAILS_OFF | ||
* [29] SILENT_FAILS_OFF | ||
* | ||
* silentFails--; | ||
*/ | ||
module.exports = function(ast) { | ||
function generateBytecode(ast) { | ||
var consts = []; | ||
function addConst(value) { | ||
var index = utils.indexOf(consts, function(c) { return c === value; }); | ||
var index = arrays.indexOf(consts, value); | ||
@@ -221,3 +242,3 @@ return index === -1 ? consts.push(value) - 1 : index; | ||
function buildCall(functionIndex, delta, env, sp) { | ||
var params = utils.map( utils.values(env), function(p) { return sp - p; }); | ||
var params = arrays.map(objects.values(env), function(p) { return sp - p; }); | ||
@@ -228,5 +249,2 @@ return [op.CALL, functionIndex, delta, params.length].concat(params); | ||
function buildSimplePredicate(expression, negative, context) { | ||
var undefinedIndex = addConst('void 0'), | ||
failedIndex = addConst('peg$FAILED'); | ||
return buildSequence( | ||
@@ -237,3 +255,3 @@ [op.PUSH_CURR_POS], | ||
sp: context.sp + 1, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
@@ -247,3 +265,3 @@ }), | ||
[negative ? op.POP : op.POP_CURR_POS], | ||
[op.PUSH, undefinedIndex] | ||
[op.PUSH_UNDEFINED] | ||
), | ||
@@ -253,3 +271,3 @@ buildSequence( | ||
[negative ? op.POP_CURR_POS : op.POP], | ||
[op.PUSH, failedIndex] | ||
[op.PUSH_FAILED] | ||
) | ||
@@ -261,8 +279,6 @@ ) | ||
function buildSemanticPredicate(code, negative, context) { | ||
var functionIndex = addFunctionConst(utils.keys(context.env), code), | ||
undefinedIndex = addConst('void 0'), | ||
failedIndex = addConst('peg$FAILED'); | ||
var functionIndex = addFunctionConst(objects.keys(context.env), code); | ||
return buildSequence( | ||
[op.REPORT_CURR_POS], | ||
[op.UPDATE_SAVED_POS], | ||
buildCall(functionIndex, 0, context.env, context.sp), | ||
@@ -273,7 +289,7 @@ buildCondition( | ||
[op.POP], | ||
[op.PUSH, negative ? failedIndex : undefinedIndex] | ||
negative ? [op.PUSH_FAILED] : [op.PUSH_UNDEFINED] | ||
), | ||
buildSequence( | ||
[op.POP], | ||
[op.PUSH, negative ? undefinedIndex : failedIndex] | ||
negative ? [op.PUSH_UNDEFINED] : [op.PUSH_FAILED] | ||
) | ||
@@ -291,5 +307,5 @@ ) | ||
var generate = utils.buildNodeVisitor({ | ||
var generate = visitor.build({ | ||
grammar: function(node) { | ||
utils.each(node.rules, generate); | ||
arrays.each(node.rules, generate); | ||
@@ -301,5 +317,5 @@ node.consts = consts; | ||
node.bytecode = generate(node.expression, { | ||
sp: -1, // stack pointer | ||
env: { }, // mapping of label names to stack positions | ||
action: null // action nodes pass themselves to children here | ||
sp: -1, // stack pointer | ||
env: { }, // mapping of label names to stack positions | ||
action: null // action nodes pass themselves to children here | ||
}); | ||
@@ -310,3 +326,3 @@ }, | ||
var nameIndex = addConst( | ||
'{ type: "other", description: ' + utils.quote(node.name) + ' }' | ||
'{ type: "other", description: "' + js.stringEscape(node.name) + '" }' | ||
); | ||
@@ -333,3 +349,3 @@ | ||
sp: context.sp, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
@@ -354,3 +370,3 @@ }), | ||
action: function(node, context) { | ||
var env = { }, | ||
var env = objects.clone(context.env), | ||
emitCall = node.expression.type !== "sequence" | ||
@@ -363,3 +379,3 @@ || node.expression.elements.length === 0, | ||
}), | ||
functionIndex = addFunctionConst(utils.keys(env), node.code); | ||
functionIndex = addFunctionConst(objects.keys(env), node.code); | ||
@@ -373,3 +389,3 @@ return emitCall | ||
buildSequence( | ||
[op.REPORT_SAVED_POS, 1], | ||
[op.LOAD_SAVED_POS, 1], | ||
buildCall(functionIndex, 1, env, context.sp + 2) | ||
@@ -385,4 +401,2 @@ ), | ||
sequence: function(node, context) { | ||
var emptyArrayIndex; | ||
function buildElementsCode(elements, context) { | ||
@@ -410,3 +424,3 @@ var processedCount, functionIndex; | ||
[op.POP_CURR_POS], | ||
[op.PUSH, failedIndex] | ||
[op.PUSH_FAILED] | ||
) | ||
@@ -418,3 +432,3 @@ ) | ||
functionIndex = addFunctionConst( | ||
utils.keys(context.env), | ||
objects.keys(context.env), | ||
context.action.code | ||
@@ -424,3 +438,3 @@ ); | ||
return buildSequence( | ||
[op.REPORT_SAVED_POS, node.elements.length], | ||
[op.LOAD_SAVED_POS, node.elements.length], | ||
buildCall( | ||
@@ -440,21 +454,15 @@ functionIndex, | ||
if (node.elements.length > 0) { | ||
failedIndex = addConst('peg$FAILED'); | ||
return buildSequence( | ||
[op.PUSH_CURR_POS], | ||
buildElementsCode(node.elements, { | ||
sp: context.sp + 1, | ||
env: context.env, | ||
action: context.action | ||
}) | ||
); | ||
} else { | ||
emptyArrayIndex = addConst('[]'); | ||
return [op.PUSH, emptyArrayIndex]; | ||
} | ||
return buildSequence( | ||
[op.PUSH_CURR_POS], | ||
buildElementsCode(node.elements, { | ||
sp: context.sp + 1, | ||
env: context.env, | ||
action: context.action | ||
}) | ||
); | ||
}, | ||
labeled: function(node, context) { | ||
var env = objects.clone(context.env); | ||
context.env[node.label] = context.sp + 1; | ||
@@ -464,3 +472,3 @@ | ||
sp: context.sp, | ||
env: { }, | ||
env: env, | ||
action: null | ||
@@ -475,7 +483,10 @@ }); | ||
sp: context.sp + 1, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
}), | ||
buildCondition([op.IF_NOT_ERROR], [op.TEXT], []), | ||
[op.NIP] | ||
buildCondition( | ||
[op.IF_NOT_ERROR], | ||
buildSequence([op.POP], [op.TEXT]), | ||
[op.NIP] | ||
) | ||
); | ||
@@ -492,17 +503,7 @@ }, | ||
semantic_and: function(node, context) { | ||
return buildSemanticPredicate(node.code, false, context); | ||
}, | ||
semantic_not: function(node, context) { | ||
return buildSemanticPredicate(node.code, true, context); | ||
}, | ||
optional: function(node, context) { | ||
var nullIndex = addConst('null'); | ||
return buildSequence( | ||
generate(node.expression, { | ||
sp: context.sp, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
@@ -512,3 +513,3 @@ }), | ||
[op.IF_ERROR], | ||
buildSequence([op.POP], [op.PUSH, nullIndex]), | ||
buildSequence([op.POP], [op.PUSH_NULL]), | ||
[] | ||
@@ -520,6 +521,5 @@ ) | ||
zero_or_more: function(node, context) { | ||
var emptyArrayIndex = addConst('[]'); | ||
expressionCode = generate(node.expression, { | ||
var expressionCode = generate(node.expression, { | ||
sp: context.sp + 1, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
@@ -529,3 +529,3 @@ }); | ||
return buildSequence( | ||
[op.PUSH, emptyArrayIndex], | ||
[op.PUSH_EMPTY_ARRAY], | ||
expressionCode, | ||
@@ -538,7 +538,5 @@ buildAppendLoop(expressionCode), | ||
one_or_more: function(node, context) { | ||
var emptyArrayIndex = addConst('[]'); | ||
failedIndex = addConst('peg$FAILED'); | ||
expressionCode = generate(node.expression, { | ||
var expressionCode = generate(node.expression, { | ||
sp: context.sp + 1, | ||
env: { }, | ||
env: objects.clone(context.env), | ||
action: null | ||
@@ -548,3 +546,3 @@ }); | ||
return buildSequence( | ||
[op.PUSH, emptyArrayIndex], | ||
[op.PUSH_EMPTY_ARRAY], | ||
expressionCode, | ||
@@ -554,3 +552,3 @@ buildCondition( | ||
buildSequence(buildAppendLoop(expressionCode), [op.POP]), | ||
buildSequence([op.POP], [op.POP], [op.PUSH, failedIndex]) | ||
buildSequence([op.POP], [op.POP], [op.PUSH_FAILED]) | ||
) | ||
@@ -560,4 +558,12 @@ ); | ||
semantic_and: function(node, context) { | ||
return buildSemanticPredicate(node.code, false, context); | ||
}, | ||
semantic_not: function(node, context) { | ||
return buildSemanticPredicate(node.code, true, context); | ||
}, | ||
rule_ref: function(node) { | ||
return [op.RULE, utils.indexOfRuleByName(ast, node.name)]; | ||
return [op.RULE, asts.indexOfRule(ast, node.name)]; | ||
}, | ||
@@ -569,5 +575,7 @@ | ||
if (node.value.length > 0) { | ||
stringIndex = addConst(node.ignoreCase | ||
? utils.quote(node.value.toLowerCase()) | ||
: utils.quote(node.value) | ||
stringIndex = addConst('"' | ||
+ js.stringEscape( | ||
node.ignoreCase ? node.value.toLowerCase() : node.value | ||
) | ||
+ '"' | ||
); | ||
@@ -577,4 +585,6 @@ expectedIndex = addConst([ | ||
'type: "literal",', | ||
'value: ' + utils.quote(node.value) + ',', | ||
'description: ' + utils.quote(utils.quote(node.value)), | ||
'value: "' + js.stringEscape(node.value) + '",', | ||
'description: "' | ||
+ js.stringEscape('"' + js.stringEscape(node.value) + '"') | ||
+ '"', | ||
'}' | ||
@@ -610,8 +620,8 @@ ].join(' ')); | ||
+ (node.inverted ? '^' : '') | ||
+ utils.map(node.parts, function(part) { | ||
+ arrays.map(node.parts, function(part) { | ||
return part instanceof Array | ||
? utils.quoteForRegexpClass(part[0]) | ||
? js.regexpClassEscape(part[0]) | ||
+ '-' | ||
+ utils.quoteForRegexpClass(part[1]) | ||
: utils.quoteForRegexpClass(part); | ||
+ js.regexpClassEscape(part[1]) | ||
: js.regexpClassEscape(part); | ||
}).join('') | ||
@@ -631,4 +641,4 @@ + ']/' + (node.ignoreCase ? 'i' : ''); | ||
'type: "class",', | ||
'value: ' + utils.quote(node.rawText) + ',', | ||
'description: ' + utils.quote(node.rawText), | ||
'value: "' + js.stringEscape(node.rawText) + '",', | ||
'description: "' + js.stringEscape(node.rawText) + '"', | ||
'}' | ||
@@ -656,2 +666,4 @@ ].join(' ')); | ||
generate(ast); | ||
}; | ||
} | ||
module.exports = generateBytecode; |
@@ -1,6 +0,10 @@ | ||
var utils = require("../../utils"), | ||
op = require("../opcodes"); | ||
"use strict"; | ||
var arrays = require("../../utils/arrays"), | ||
asts = require("../asts"), | ||
op = require("../opcodes"), | ||
js = require("../javascript"); | ||
/* Generates parser JavaScript code. */ | ||
module.exports = function(ast, options) { | ||
function generateJavascript(ast, options) { | ||
/* These only indent non-empty lines to avoid trailing whitespace. */ | ||
@@ -20,17 +24,14 @@ function indent2(code) { return code.replace(/^(.+)$/gm, ' $1'); } | ||
'peg$bytecode = [', | ||
indent2(utils.map( | ||
ast.rules, | ||
function(rule) { | ||
return 'peg$decode(' | ||
+ utils.quote(utils.map( | ||
rule.bytecode, | ||
function(b) { return String.fromCharCode(b + 32); } | ||
).join('')) | ||
+ ')'; | ||
} | ||
).join(',\n')), | ||
indent2(arrays.map(ast.rules, function(rule) { | ||
return 'peg$decode("' | ||
+ js.stringEscape(arrays.map( | ||
rule.bytecode, | ||
function(b) { return String.fromCharCode(b + 32); } | ||
).join('')) | ||
+ '")'; | ||
}).join(',\n')), | ||
'],' | ||
].join('\n'); | ||
} else { | ||
return utils.map( | ||
return arrays.map( | ||
ast.consts, | ||
@@ -42,20 +43,94 @@ function(c, i) { return 'peg$c' + i + ' = ' + c + ','; } | ||
function generateCacheHeader(ruleIndexCode) { | ||
return [ | ||
'var key = peg$currPos * ' + ast.rules.length + ' + ' + ruleIndexCode + ',', | ||
' cached = peg$cache[key];', | ||
'', | ||
'if (cached) {', | ||
' peg$currPos = cached.nextPos;', | ||
' return cached.result;', | ||
'}', | ||
'' | ||
].join('\n'); | ||
function generateRuleHeader(ruleNameCode, ruleIndexCode) { | ||
var parts = []; | ||
parts.push(''); | ||
if (options.trace) { | ||
parts.push([ | ||
'peg$tracer.trace({', | ||
' type: "rule.enter",', | ||
' rule: ' + ruleNameCode + ',', | ||
' location: peg$computeLocation(startPos, startPos)', | ||
'});', | ||
'' | ||
].join('\n')); | ||
} | ||
if (options.cache) { | ||
parts.push([ | ||
'var key = peg$currPos * ' + ast.rules.length + ' + ' + ruleIndexCode + ',', | ||
' cached = peg$resultsCache[key];', | ||
'', | ||
'if (cached) {', | ||
' peg$currPos = cached.nextPos;', | ||
'', | ||
].join('\n')); | ||
if (options.trace) { | ||
parts.push([ | ||
'if (cached.result !== peg$FAILED) {', | ||
' peg$tracer.trace({', | ||
' type: "rule.match",', | ||
' rule: ' + ruleNameCode + ',', | ||
' result: cached.result,', | ||
' location: peg$computeLocation(startPos, peg$currPos)', | ||
' });', | ||
'} else {', | ||
' peg$tracer.trace({', | ||
' type: "rule.fail",', | ||
' rule: ' + ruleNameCode + ',', | ||
' location: peg$computeLocation(startPos, startPos)', | ||
' });', | ||
'}', | ||
'' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
' return cached.result;', | ||
'}', | ||
'' | ||
].join('\n')); | ||
} | ||
return parts.join('\n'); | ||
} | ||
function generateCacheFooter(resultCode) { | ||
return [ | ||
function generateRuleFooter(ruleNameCode, resultCode) { | ||
var parts = []; | ||
if (options.cache) { | ||
parts.push([ | ||
'', | ||
'peg$resultsCache[key] = { nextPos: peg$currPos, result: ' + resultCode + ' };' | ||
].join('\n')); | ||
} | ||
if (options.trace) { | ||
parts.push([ | ||
'', | ||
'if (' + resultCode + ' !== peg$FAILED) {', | ||
' peg$tracer.trace({', | ||
' type: "rule.match",', | ||
' rule: ' + ruleNameCode + ',', | ||
' result: ' + resultCode + ',', | ||
' location: peg$computeLocation(startPos, peg$currPos)', | ||
' });', | ||
'} else {', | ||
' peg$tracer.trace({', | ||
' type: "rule.fail",', | ||
' rule: ' + ruleNameCode + ',', | ||
' location: peg$computeLocation(startPos, startPos)', | ||
' });', | ||
'}' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
'', | ||
'peg$cache[key] = { nextPos: peg$currPos, result: ' + resultCode + ' };' | ||
].join('\n'); | ||
'return ' + resultCode + ';' | ||
].join('\n')); | ||
return parts.join('\n'); | ||
} | ||
@@ -139,21 +214,30 @@ | ||
'function peg$parseRule(index) {', | ||
' var bc = peg$bytecode[index],', | ||
' ip = 0,', | ||
' ips = [],', | ||
' end = bc.length,', | ||
' ends = [],', | ||
' stack = [],', | ||
' params, i;', | ||
'' | ||
].join('\n')); | ||
if (options.cache) { | ||
parts.push(indent2(generateCacheHeader('index'))); | ||
if (options.trace) { | ||
parts.push([ | ||
' var bc = peg$bytecode[index],', | ||
' ip = 0,', | ||
' ips = [],', | ||
' end = bc.length,', | ||
' ends = [],', | ||
' stack = [],', | ||
' startPos = peg$currPos,', | ||
' params, i;', | ||
].join('\n')); | ||
} else { | ||
parts.push([ | ||
' var bc = peg$bytecode[index],', | ||
' ip = 0,', | ||
' ips = [],', | ||
' end = bc.length,', | ||
' ends = [],', | ||
' stack = [],', | ||
' params, i;', | ||
].join('\n')); | ||
} | ||
parts.push(indent2(generateRuleHeader('peg$ruleNames[index]', 'index'))); | ||
parts.push([ | ||
' function protect(object) {', | ||
' return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;', | ||
' }', | ||
'', | ||
/* | ||
@@ -169,12 +253,28 @@ * The point of the outer loop and the |ips| & |ends| stacks is to avoid | ||
' switch (bc[ip]) {', | ||
' case ' + op.PUSH + ':', // PUSH c | ||
/* | ||
* Hack: One of the constants can be an empty array. It needs to be cloned | ||
* because it can be modified later on the stack by |APPEND|. | ||
*/ | ||
' stack.push(protect(peg$consts[bc[ip + 1]]));', | ||
' case ' + op.PUSH + ':', // PUSH c | ||
' stack.push(peg$consts[bc[ip + 1]]);', | ||
' ip += 2;', | ||
' break;', | ||
'', | ||
' case ' + op.PUSH_CURR_POS + ':', // PUSH_CURR_POS | ||
' case ' + op.PUSH_UNDEFINED + ':', // PUSH_UNDEFINED | ||
' stack.push(void 0);', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.PUSH_NULL + ':', // PUSH_NULL | ||
' stack.push(null);', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.PUSH_FAILED + ':', // PUSH_FAILED | ||
' stack.push(peg$FAILED);', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.PUSH_EMPTY_ARRAY + ':', // PUSH_EMPTY_ARRAY | ||
' stack.push([]);', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.PUSH_CURR_POS + ':', // PUSH_CURR_POS | ||
' stack.push(peg$currPos);', | ||
@@ -184,3 +284,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.POP + ':', // POP | ||
' case ' + op.POP + ':', // POP | ||
' stack.pop();', | ||
@@ -190,3 +290,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.POP_CURR_POS + ':', // POP_CURR_POS | ||
' case ' + op.POP_CURR_POS + ':', // POP_CURR_POS | ||
' peg$currPos = stack.pop();', | ||
@@ -196,3 +296,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.POP_N + ':', // POP_N n | ||
' case ' + op.POP_N + ':', // POP_N n | ||
' stack.length -= bc[ip + 1];', | ||
@@ -202,3 +302,3 @@ ' ip += 2;', | ||
'', | ||
' case ' + op.NIP + ':', // NIP | ||
' case ' + op.NIP + ':', // NIP | ||
' stack.splice(-2, 1);', | ||
@@ -208,3 +308,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.APPEND + ':', // APPEND | ||
' case ' + op.APPEND + ':', // APPEND | ||
' stack[stack.length - 2].push(stack.pop());', | ||
@@ -214,3 +314,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.WRAP + ':', // WRAP n | ||
' case ' + op.WRAP + ':', // WRAP n | ||
' stack.push(stack.splice(stack.length - bc[ip + 1], bc[ip + 1]));', | ||
@@ -220,12 +320,11 @@ ' ip += 2;', | ||
'', | ||
' case ' + op.TEXT + ':', // TEXT | ||
' stack.pop();', | ||
' stack.push(input.substring(stack[stack.length - 1], peg$currPos));', | ||
' case ' + op.TEXT + ':', // TEXT | ||
' stack.push(input.substring(stack.pop(), peg$currPos));', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.IF + ':', // IF t, f | ||
' case ' + op.IF + ':', // IF t, f | ||
indent10(generateCondition('stack[stack.length - 1]', 0)), | ||
'', | ||
' case ' + op.IF_ERROR + ':', // IF_ERROR t, f | ||
' case ' + op.IF_ERROR + ':', // IF_ERROR t, f | ||
indent10(generateCondition( | ||
@@ -236,3 +335,3 @@ 'stack[stack.length - 1] === peg$FAILED', | ||
'', | ||
' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f | ||
' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f | ||
indent10( | ||
@@ -243,9 +342,9 @@ generateCondition('stack[stack.length - 1] !== peg$FAILED', | ||
'', | ||
' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b | ||
' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b | ||
indent10(generateLoop('stack[stack.length - 1] !== peg$FAILED')), | ||
'', | ||
' case ' + op.MATCH_ANY + ':', // MATCH_ANY a, f, ... | ||
' case ' + op.MATCH_ANY + ':', // MATCH_ANY a, f, ... | ||
indent10(generateCondition('input.length > peg$currPos', 0)), | ||
'', | ||
' case ' + op.MATCH_STRING + ':', // MATCH_STRING s, a, f, ... | ||
' case ' + op.MATCH_STRING + ':', // MATCH_STRING s, a, f, ... | ||
indent10(generateCondition( | ||
@@ -256,3 +355,3 @@ 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]', | ||
'', | ||
' case ' + op.MATCH_STRING_IC + ':', // MATCH_STRING_IC s, a, f, ... | ||
' case ' + op.MATCH_STRING_IC + ':', // MATCH_STRING_IC s, a, f, ... | ||
indent10(generateCondition( | ||
@@ -263,3 +362,3 @@ 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]', | ||
'', | ||
' case ' + op.MATCH_REGEXP + ':', // MATCH_REGEXP r, a, f, ... | ||
' case ' + op.MATCH_REGEXP + ':', // MATCH_REGEXP r, a, f, ... | ||
indent10(generateCondition( | ||
@@ -270,3 +369,3 @@ 'peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))', | ||
'', | ||
' case ' + op.ACCEPT_N + ':', // ACCEPT_N n | ||
' case ' + op.ACCEPT_N + ':', // ACCEPT_N n | ||
' stack.push(input.substr(peg$currPos, bc[ip + 1]));', | ||
@@ -277,3 +376,3 @@ ' peg$currPos += bc[ip + 1];', | ||
'', | ||
' case ' + op.ACCEPT_STRING + ':', // ACCEPT_STRING s | ||
' case ' + op.ACCEPT_STRING + ':', // ACCEPT_STRING s | ||
' stack.push(peg$consts[bc[ip + 1]]);', | ||
@@ -284,3 +383,3 @@ ' peg$currPos += peg$consts[bc[ip + 1]].length;', | ||
'', | ||
' case ' + op.FAIL + ':', // FAIL e | ||
' case ' + op.FAIL + ':', // FAIL e | ||
' stack.push(peg$FAILED);', | ||
@@ -293,16 +392,16 @@ ' if (peg$silentFails === 0) {', | ||
'', | ||
' case ' + op.REPORT_SAVED_POS + ':', // REPORT_SAVED_POS p | ||
' peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];', | ||
' case ' + op.LOAD_SAVED_POS + ':', // LOAD_SAVED_POS p | ||
' peg$savedPos = stack[stack.length - 1 - bc[ip + 1]];', | ||
' ip += 2;', | ||
' break;', | ||
'', | ||
' case ' + op.REPORT_CURR_POS + ':', // REPORT_CURR_POS | ||
' peg$reportedPos = peg$currPos;', | ||
' case ' + op.UPDATE_SAVED_POS + ':', // UPDATE_SAVED_POS | ||
' peg$savedPos = peg$currPos;', | ||
' ip++;', | ||
' break;', | ||
'', | ||
' case ' + op.CALL + ':', // CALL f, n, pc, p1, p2, ..., pN | ||
' case ' + op.CALL + ':', // CALL f, n, pc, p1, p2, ..., pN | ||
indent10(generateCall()), | ||
'', | ||
' case ' + op.RULE + ':', // RULE r | ||
' case ' + op.RULE + ':', // RULE r | ||
' stack.push(peg$parseRule(bc[ip + 1]));', | ||
@@ -312,3 +411,3 @@ ' ip += 2;', | ||
'', | ||
' case ' + op.SILENT_FAILS_ON + ':', // SILENT_FAILS_ON | ||
' case ' + op.SILENT_FAILS_ON + ':', // SILENT_FAILS_ON | ||
' peg$silentFails++;', | ||
@@ -318,3 +417,3 @@ ' ip++;', | ||
'', | ||
' case ' + op.SILENT_FAILS_OFF + ':', // SILENT_FAILS_OFF | ||
' case ' + op.SILENT_FAILS_OFF + ':', // SILENT_FAILS_OFF | ||
' peg$silentFails--;', | ||
@@ -338,12 +437,5 @@ ' ip++;', | ||
if (options.cache) { | ||
parts.push(indent2(generateCacheFooter('stack[0]'))); | ||
} | ||
parts.push(indent2(generateRuleFooter('peg$ruleNames[index]', 'stack[0]'))); | ||
parts.push('}'); | ||
parts.push([ | ||
'', | ||
' return stack[0];', | ||
'}' | ||
].join('\n')); | ||
return parts.join('\n'); | ||
@@ -377,3 +469,3 @@ } | ||
n = arguments[0]; | ||
values = utils.map(utils.range(this.sp - n + 1, this.sp + 1), s); | ||
values = arrays.map(arrays.range(this.sp - n + 1, this.sp + 1), s); | ||
this.sp -= n; | ||
@@ -459,5 +551,5 @@ | ||
var value = c(bc[ip + 1]) + '(' | ||
+ utils.map( | ||
+ arrays.map( | ||
bc.slice(ip + baseLength, ip + baseLength + paramsLength), | ||
stackIndex | ||
function(p) { return stack.index(p); } | ||
).join(', ') | ||
@@ -470,25 +562,10 @@ + ')'; | ||
/* | ||
* Extracted into a function just to silence JSHint complaining about | ||
* creating functions in a loop. | ||
*/ | ||
function stackIndex(p) { | ||
return stack.index(p); | ||
} | ||
while (ip < end) { | ||
switch (bc[ip]) { | ||
case op.PUSH: // PUSH c | ||
/* | ||
* Hack: One of the constants can be an empty array. It needs to be | ||
* handled specially because it can be modified later on the stack | ||
* by |APPEND|. | ||
*/ | ||
parts.push( | ||
stack.push(ast.consts[bc[ip + 1]] === "[]" ? "[]" : c(bc[ip + 1])) | ||
); | ||
case op.PUSH: // PUSH c | ||
parts.push(stack.push(c(bc[ip + 1]))); | ||
ip += 2; | ||
break; | ||
case op.PUSH_CURR_POS: // PUSH_CURR_POS | ||
case op.PUSH_CURR_POS: // PUSH_CURR_POS | ||
parts.push(stack.push('peg$currPos')); | ||
@@ -498,3 +575,23 @@ ip++; | ||
case op.POP: // POP | ||
case op.PUSH_UNDEFINED: // PUSH_UNDEFINED | ||
parts.push(stack.push('void 0')); | ||
ip++; | ||
break; | ||
case op.PUSH_NULL: // PUSH_NULL | ||
parts.push(stack.push('null')); | ||
ip++; | ||
break; | ||
case op.PUSH_FAILED: // PUSH_FAILED | ||
parts.push(stack.push('peg$FAILED')); | ||
ip++; | ||
break; | ||
case op.PUSH_EMPTY_ARRAY: // PUSH_EMPTY_ARRAY | ||
parts.push(stack.push('[]')); | ||
ip++; | ||
break; | ||
case op.POP: // POP | ||
stack.pop(); | ||
@@ -504,3 +601,3 @@ ip++; | ||
case op.POP_CURR_POS: // POP_CURR_POS | ||
case op.POP_CURR_POS: // POP_CURR_POS | ||
parts.push('peg$currPos = ' + stack.pop() + ';'); | ||
@@ -510,3 +607,3 @@ ip++; | ||
case op.POP_N: // POP_N n | ||
case op.POP_N: // POP_N n | ||
stack.pop(bc[ip + 1]); | ||
@@ -516,3 +613,3 @@ ip += 2; | ||
case op.NIP: // NIP | ||
case op.NIP: // NIP | ||
value = stack.pop(); | ||
@@ -524,3 +621,3 @@ stack.pop(); | ||
case op.APPEND: // APPEND | ||
case op.APPEND: // APPEND | ||
value = stack.pop(); | ||
@@ -531,3 +628,3 @@ parts.push(stack.top() + '.push(' + value + ');'); | ||
case op.WRAP: // WRAP n | ||
case op.WRAP: // WRAP n | ||
parts.push( | ||
@@ -539,6 +636,5 @@ stack.push('[' + stack.pop(bc[ip + 1]).join(', ') + ']') | ||
case op.TEXT: // TEXT | ||
stack.pop(); | ||
case op.TEXT: // TEXT | ||
parts.push( | ||
stack.push('input.substring(' + stack.top() + ', peg$currPos)') | ||
stack.push('input.substring(' + stack.pop() + ', peg$currPos)') | ||
); | ||
@@ -548,23 +644,23 @@ ip++; | ||
case op.IF: // IF t, f | ||
case op.IF: // IF t, f | ||
compileCondition(stack.top(), 0); | ||
break; | ||
case op.IF_ERROR: // IF_ERROR t, f | ||
case op.IF_ERROR: // IF_ERROR t, f | ||
compileCondition(stack.top() + ' === peg$FAILED', 0); | ||
break; | ||
case op.IF_NOT_ERROR: // IF_NOT_ERROR t, f | ||
case op.IF_NOT_ERROR: // IF_NOT_ERROR t, f | ||
compileCondition(stack.top() + ' !== peg$FAILED', 0); | ||
break; | ||
case op.WHILE_NOT_ERROR: // WHILE_NOT_ERROR b | ||
case op.WHILE_NOT_ERROR: // WHILE_NOT_ERROR b | ||
compileLoop(stack.top() + ' !== peg$FAILED', 0); | ||
break; | ||
case op.MATCH_ANY: // MATCH_ANY a, f, ... | ||
case op.MATCH_ANY: // MATCH_ANY a, f, ... | ||
compileCondition('input.length > peg$currPos', 0); | ||
break; | ||
case op.MATCH_STRING: // MATCH_STRING s, a, f, ... | ||
case op.MATCH_STRING: // MATCH_STRING s, a, f, ... | ||
compileCondition( | ||
@@ -582,3 +678,3 @@ eval(ast.consts[bc[ip + 1]]).length > 1 | ||
case op.MATCH_STRING_IC: // MATCH_STRING_IC s, a, f, ... | ||
case op.MATCH_STRING_IC: // MATCH_STRING_IC s, a, f, ... | ||
compileCondition( | ||
@@ -593,3 +689,3 @@ 'input.substr(peg$currPos, ' | ||
case op.MATCH_REGEXP: // MATCH_REGEXP r, a, f, ... | ||
case op.MATCH_REGEXP: // MATCH_REGEXP r, a, f, ... | ||
compileCondition( | ||
@@ -601,3 +697,3 @@ c(bc[ip + 1]) + '.test(input.charAt(peg$currPos))', | ||
case op.ACCEPT_N: // ACCEPT_N n | ||
case op.ACCEPT_N: // ACCEPT_N n | ||
parts.push(stack.push( | ||
@@ -616,3 +712,3 @@ bc[ip + 1] > 1 | ||
case op.ACCEPT_STRING: // ACCEPT_STRING s | ||
case op.ACCEPT_STRING: // ACCEPT_STRING s | ||
parts.push(stack.push(c(bc[ip + 1]))); | ||
@@ -627,3 +723,3 @@ parts.push( | ||
case op.FAIL: // FAIL e | ||
case op.FAIL: // FAIL e | ||
parts.push(stack.push('peg$FAILED')); | ||
@@ -634,17 +730,17 @@ parts.push('if (peg$silentFails === 0) { peg$fail(' + c(bc[ip + 1]) + '); }'); | ||
case op.REPORT_SAVED_POS: // REPORT_SAVED_POS p | ||
parts.push('peg$reportedPos = ' + stack.index(bc[ip + 1]) + ';'); | ||
case op.LOAD_SAVED_POS: // LOAD_SAVED_POS p | ||
parts.push('peg$savedPos = ' + stack.index(bc[ip + 1]) + ';'); | ||
ip += 2; | ||
break; | ||
case op.REPORT_CURR_POS: // REPORT_CURR_POS | ||
parts.push('peg$reportedPos = peg$currPos;'); | ||
case op.UPDATE_SAVED_POS: // UPDATE_SAVED_POS | ||
parts.push('peg$savedPos = peg$currPos;'); | ||
ip++; | ||
break; | ||
case op.CALL: // CALL f, n, pc, p1, p2, ..., pN | ||
case op.CALL: // CALL f, n, pc, p1, p2, ..., pN | ||
compileCall(); | ||
break; | ||
case op.RULE: // RULE r | ||
case op.RULE: // RULE r | ||
parts.push(stack.push("peg$parse" + ast.rules[bc[ip + 1]].name + "()")); | ||
@@ -654,3 +750,3 @@ ip += 2; | ||
case op.SILENT_FAILS_ON: // SILENT_FAILS_ON | ||
case op.SILENT_FAILS_ON: // SILENT_FAILS_ON | ||
parts.push('peg$silentFails++;'); | ||
@@ -660,3 +756,3 @@ ip++; | ||
case op.SILENT_FAILS_OFF: // SILENT_FAILS_OFF | ||
case op.SILENT_FAILS_OFF: // SILENT_FAILS_OFF | ||
parts.push('peg$silentFails--;'); | ||
@@ -676,26 +772,27 @@ ip++; | ||
parts.push([ | ||
'function peg$parse' + rule.name + '() {', | ||
' var ' + utils.map(utils.range(0, stack.maxSp + 1), s).join(', ') + ';', | ||
'' | ||
].join('\n')); | ||
parts.push('function peg$parse' + rule.name + '() {'); | ||
if (options.cache) { | ||
parts.push(indent2( | ||
generateCacheHeader(utils.indexOfRuleByName(ast, rule.name)) | ||
)); | ||
if (options.trace) { | ||
parts.push([ | ||
' var ' + arrays.map(arrays.range(0, stack.maxSp + 1), s).join(', ') + ',', | ||
' startPos = peg$currPos;' | ||
].join('\n')); | ||
} else { | ||
parts.push( | ||
' var ' + arrays.map(arrays.range(0, stack.maxSp + 1), s).join(', ') + ';' | ||
); | ||
} | ||
parts.push(indent2(generateRuleHeader( | ||
'"' + js.stringEscape(rule.name) + '"', | ||
asts.indexOfRule(ast, rule.name) | ||
))); | ||
parts.push(indent2(code)); | ||
parts.push(indent2(generateRuleFooter( | ||
'"' + js.stringEscape(rule.name) + '"', | ||
s(0) | ||
))); | ||
if (options.cache) { | ||
parts.push(indent2(generateCacheFooter(s(0)))); | ||
} | ||
parts.push('}'); | ||
parts.push([ | ||
'', | ||
' return ' + s(0) + ';', | ||
'}' | ||
].join('\n')); | ||
return parts.join('\n'); | ||
@@ -706,10 +803,13 @@ } | ||
startRuleIndices, startRuleIndex, | ||
startRuleFunctions, startRuleFunction; | ||
startRuleFunctions, startRuleFunction, | ||
ruleNames; | ||
parts.push([ | ||
'(function() {', | ||
' "use strict";', | ||
'', | ||
' /*', | ||
' * Generated by PEG.js 0.8.0.', | ||
' * Generated by PEG.js 0.9.0.', | ||
' *', | ||
' * http://pegjs.majda.cz/', | ||
' * http://pegjs.org/', | ||
' */', | ||
@@ -723,17 +823,80 @@ '', | ||
'', | ||
' function SyntaxError(message, expected, found, offset, line, column) {', | ||
' function peg$SyntaxError(message, expected, found, location) {', | ||
' this.message = message;', | ||
' this.expected = expected;', | ||
' this.found = found;', | ||
' this.offset = offset;', | ||
' this.line = line;', | ||
' this.column = column;', | ||
' this.location = location;', | ||
' this.name = "SyntaxError";', | ||
'', | ||
' this.name = "SyntaxError";', | ||
' if (typeof Error.captureStackTrace === "function") {', | ||
' Error.captureStackTrace(this, peg$SyntaxError);', | ||
' }', | ||
' }', | ||
'', | ||
' peg$subclass(SyntaxError, Error);', | ||
'', | ||
' function parse(input) {', | ||
' peg$subclass(peg$SyntaxError, Error);', | ||
'' | ||
].join('\n')); | ||
if (options.trace) { | ||
parts.push([ | ||
' function peg$DefaultTracer() {', | ||
' this.indentLevel = 0;', | ||
' }', | ||
'', | ||
' peg$DefaultTracer.prototype.trace = function(event) {', | ||
' var that = this;', | ||
'', | ||
' function log(event) {', | ||
' function repeat(string, n) {', | ||
' var result = "", i;', | ||
'', | ||
' for (i = 0; i < n; i++) {', | ||
' result += string;', | ||
' }', | ||
'', | ||
' return result;', | ||
' }', | ||
'', | ||
' function pad(string, length) {', | ||
' return string + repeat(" ", length - string.length);', | ||
' }', | ||
'', | ||
' if (typeof console === "object") {', // IE 8-10 | ||
' console.log(', | ||
' event.location.start.line + ":" + event.location.start.column + "-"', | ||
' + event.location.end.line + ":" + event.location.end.column + " "', | ||
' + pad(event.type, 10) + " "', | ||
' + repeat(" ", that.indentLevel) + event.rule', | ||
' );', | ||
' }', | ||
' }', | ||
'', | ||
' switch (event.type) {', | ||
' case "rule.enter":', | ||
' log(event);', | ||
' this.indentLevel++;', | ||
' break;', | ||
'', | ||
' case "rule.match":', | ||
' this.indentLevel--;', | ||
' log(event);', | ||
' break;', | ||
'', | ||
' case "rule.fail":', | ||
' this.indentLevel--;', | ||
' log(event);', | ||
' break;', | ||
'', | ||
' default:', | ||
' throw new Error("Invalid event type: " + event.type + ".");', | ||
' }', | ||
' };', | ||
'' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
' function peg$parse(input) {', | ||
' var options = arguments.length > 1 ? arguments[1] : {},', | ||
' parser = this,', | ||
'', | ||
@@ -746,8 +909,8 @@ ' peg$FAILED = {},', | ||
startRuleIndices = '{ ' | ||
+ utils.map( | ||
+ arrays.map( | ||
options.allowedStartRules, | ||
function(r) { return r + ': ' + utils.indexOfRuleByName(ast, r); } | ||
function(r) { return r + ': ' + asts.indexOfRule(ast, r); } | ||
).join(', ') | ||
+ ' }'; | ||
startRuleIndex = utils.indexOfRuleByName(ast, options.allowedStartRules[0]); | ||
startRuleIndex = asts.indexOfRule(ast, options.allowedStartRules[0]); | ||
@@ -760,3 +923,3 @@ parts.push([ | ||
startRuleFunctions = '{ ' | ||
+ utils.map( | ||
+ arrays.map( | ||
options.allowedStartRules, | ||
@@ -781,8 +944,7 @@ function(r) { return r + ': peg$parse' + r; } | ||
' peg$currPos = 0,', | ||
' peg$reportedPos = 0,', | ||
' peg$cachedPos = 0,', | ||
' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },', | ||
' peg$savedPos = 0,', | ||
' peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }],', | ||
' peg$maxFailPos = 0,', | ||
' peg$maxFailExpected = [],', | ||
' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures | ||
' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures | ||
'' | ||
@@ -792,5 +954,29 @@ ].join('\n')); | ||
if (options.cache) { | ||
parts.push(' peg$cache = {},'); | ||
parts.push([ | ||
' peg$resultsCache = {},', | ||
'' | ||
].join('\n')); | ||
} | ||
if (options.trace) { | ||
if (options.optimize === "size") { | ||
ruleNames = '[' | ||
+ arrays.map( | ||
ast.rules, | ||
function(r) { return '"' + js.stringEscape(r.name) + '"'; } | ||
).join(', ') | ||
+ ']'; | ||
parts.push([ | ||
' peg$ruleNames = ' + ruleNames + ',', | ||
'' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
' peg$tracer = "tracer" in options ? options.tracer : new peg$DefaultTracer(),', | ||
'' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
@@ -826,17 +1012,9 @@ ' peg$result;', | ||
' function text() {', | ||
' return input.substring(peg$reportedPos, peg$currPos);', | ||
' return input.substring(peg$savedPos, peg$currPos);', | ||
' }', | ||
'', | ||
' function offset() {', | ||
' return peg$reportedPos;', | ||
' function location() {', | ||
' return peg$computeLocation(peg$savedPos, peg$currPos);', | ||
' }', | ||
'', | ||
' function line() {', | ||
' return peg$computePosDetails(peg$reportedPos).line;', | ||
' }', | ||
'', | ||
' function column() {', | ||
' return peg$computePosDetails(peg$reportedPos).column;', | ||
' }', | ||
'', | ||
' function expected(description) {', | ||
@@ -846,3 +1024,4 @@ ' throw peg$buildException(', | ||
' [{ type: "other", description: description }],', | ||
' peg$reportedPos', | ||
' input.substring(peg$savedPos, peg$currPos),', | ||
' peg$computeLocation(peg$savedPos, peg$currPos)', | ||
' );', | ||
@@ -852,10 +1031,30 @@ ' }', | ||
' function error(message) {', | ||
' throw peg$buildException(message, null, peg$reportedPos);', | ||
' throw peg$buildException(', | ||
' message,', | ||
' null,', | ||
' input.substring(peg$savedPos, peg$currPos),', | ||
' peg$computeLocation(peg$savedPos, peg$currPos)', | ||
' );', | ||
' }', | ||
'', | ||
' function peg$computePosDetails(pos) {', | ||
' function advance(details, startPos, endPos) {', | ||
' var p, ch;', | ||
' var details = peg$posDetailsCache[pos],', | ||
' p, ch;', | ||
'', | ||
' for (p = startPos; p < endPos; p++) {', | ||
' if (details) {', | ||
' return details;', | ||
' } else {', | ||
' p = pos - 1;', | ||
' while (!peg$posDetailsCache[p]) {', | ||
' p--;', | ||
' }', | ||
'', | ||
' details = peg$posDetailsCache[p];', | ||
' details = {', | ||
' line: details.line,', | ||
' column: details.column,', | ||
' seenCR: details.seenCR', | ||
' };', | ||
'', | ||
' while (p < pos) {', | ||
' ch = input.charAt(p);', | ||
@@ -874,15 +1073,27 @@ ' if (ch === "\\n") {', | ||
' }', | ||
'', | ||
' p++;', | ||
' }', | ||
'', | ||
' peg$posDetailsCache[pos] = details;', | ||
' return details;', | ||
' }', | ||
' }', | ||
'', | ||
' if (peg$cachedPos !== pos) {', | ||
' if (peg$cachedPos > pos) {', | ||
' peg$cachedPos = 0;', | ||
' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };', | ||
' function peg$computeLocation(startPos, endPos) {', | ||
' var startPosDetails = peg$computePosDetails(startPos),', | ||
' endPosDetails = peg$computePosDetails(endPos);', | ||
'', | ||
' return {', | ||
' start: {', | ||
' offset: startPos,', | ||
' line: startPosDetails.line,', | ||
' column: startPosDetails.column', | ||
' },', | ||
' end: {', | ||
' offset: endPos,', | ||
' line: endPosDetails.line,', | ||
' column: endPosDetails.column', | ||
' }', | ||
' advance(peg$cachedPosDetails, peg$cachedPos, pos);', | ||
' peg$cachedPos = pos;', | ||
' }', | ||
'', | ||
' return peg$cachedPosDetails;', | ||
' };', | ||
' }', | ||
@@ -901,3 +1112,3 @@ '', | ||
'', | ||
' function peg$buildException(message, expected, pos) {', | ||
' function peg$buildException(message, expected, found, location) {', | ||
' function cleanupExpected(expected) {', | ||
@@ -945,13 +1156,13 @@ ' var i = 1;', | ||
' return s', | ||
' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash | ||
' .replace(/"/g, \'\\\\"\')', // closing double quote | ||
' .replace(/\\x08/g, \'\\\\b\')', // backspace | ||
' .replace(/\\t/g, \'\\\\t\')', // horizontal tab | ||
' .replace(/\\n/g, \'\\\\n\')', // line feed | ||
' .replace(/\\f/g, \'\\\\f\')', // form feed | ||
' .replace(/\\r/g, \'\\\\r\')', // carriage return | ||
' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash | ||
' .replace(/"/g, \'\\\\"\')', // closing double quote | ||
' .replace(/\\x08/g, \'\\\\b\')', // backspace | ||
' .replace(/\\t/g, \'\\\\t\')', // horizontal tab | ||
' .replace(/\\n/g, \'\\\\n\')', // line feed | ||
' .replace(/\\f/g, \'\\\\f\')', // form feed | ||
' .replace(/\\r/g, \'\\\\r\')', // carriage return | ||
' .replace(/[\\x00-\\x07\\x0B\\x0E\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', | ||
' .replace(/[\\x10-\\x1F\\x80-\\xFF]/g, function(ch) { return \'\\\\x\' + hex(ch); })', | ||
' .replace(/[\\u0180-\\u0FFF]/g, function(ch) { return \'\\\\u0\' + hex(ch); })', | ||
' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', | ||
' .replace(/[\\u0100-\\u0FFF]/g, function(ch) { return \'\\\\u0\' + hex(ch); })', | ||
' .replace(/[\\u1000-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', | ||
' }', | ||
@@ -977,5 +1188,2 @@ '', | ||
'', | ||
' var posDetails = peg$computePosDetails(pos),', | ||
' found = pos < input.length ? input.charAt(pos) : null;', | ||
'', | ||
' if (expected !== null) {', | ||
@@ -985,9 +1193,7 @@ ' cleanupExpected(expected);', | ||
'', | ||
' return new SyntaxError(', | ||
' return new peg$SyntaxError(', | ||
' message !== null ? message : buildMessage(expected, found),', | ||
' expected,', | ||
' found,', | ||
' pos,', | ||
' posDetails.line,', | ||
' posDetails.column', | ||
' location', | ||
' );', | ||
@@ -1002,3 +1208,3 @@ ' }', | ||
} else { | ||
utils.each(ast.rules, function(rule) { | ||
arrays.each(ast.rules, function(rule) { | ||
parts.push(indent4(generateRuleFunction(rule))); | ||
@@ -1029,9 +1235,30 @@ parts.push(''); | ||
'', | ||
' throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);', | ||
' throw peg$buildException(', | ||
' null,', | ||
' peg$maxFailExpected,', | ||
' peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,', | ||
' peg$maxFailPos < input.length', | ||
' ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)', | ||
' : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)', | ||
' );', | ||
' }', | ||
' }', | ||
'', | ||
' return {', | ||
' SyntaxError: SyntaxError,', | ||
' parse: parse', | ||
' return {' | ||
].join('\n')); | ||
if (options.trace) { | ||
parts.push([ | ||
' SyntaxError: peg$SyntaxError,', | ||
' DefaultTracer: peg$DefaultTracer,', | ||
' parse: peg$parse' | ||
].join('\n')); | ||
} else { | ||
parts.push([ | ||
' SyntaxError: peg$SyntaxError,', | ||
' parse: peg$parse' | ||
].join('\n')); | ||
} | ||
parts.push([ | ||
' };', | ||
@@ -1042,2 +1269,4 @@ '})()' | ||
ast.code = parts.join('\n'); | ||
}; | ||
} | ||
module.exports = generateJavascript; |
@@ -1,7 +0,10 @@ | ||
var utils = require("../../utils"); | ||
"use strict"; | ||
var arrays = require("../../utils/arrays"), | ||
visitor = require("../visitor"); | ||
/* | ||
* Removes proxy rules -- that is, rules that only delegate to other rule. | ||
*/ | ||
module.exports = function(ast, options) { | ||
function removeProxyRules(ast, options) { | ||
function isProxyRule(node) { | ||
@@ -12,46 +15,11 @@ return node.type === "rule" && node.expression.type === "rule_ref"; | ||
function replaceRuleRefs(ast, from, to) { | ||
function nop() {} | ||
function replaceInExpression(node, from, to) { | ||
replace(node.expression, from, to); | ||
} | ||
function replaceInSubnodes(propertyName) { | ||
return function(node, from, to) { | ||
utils.each(node[propertyName], function(subnode) { | ||
replace(subnode, from, to); | ||
}); | ||
}; | ||
} | ||
var replace = utils.buildNodeVisitor({ | ||
grammar: replaceInSubnodes("rules"), | ||
rule: replaceInExpression, | ||
named: replaceInExpression, | ||
choice: replaceInSubnodes("alternatives"), | ||
sequence: replaceInSubnodes("elements"), | ||
labeled: replaceInExpression, | ||
text: replaceInExpression, | ||
simple_and: replaceInExpression, | ||
simple_not: replaceInExpression, | ||
semantic_and: nop, | ||
semantic_not: nop, | ||
optional: replaceInExpression, | ||
zero_or_more: replaceInExpression, | ||
one_or_more: replaceInExpression, | ||
action: replaceInExpression, | ||
rule_ref: | ||
function(node, from, to) { | ||
if (node.name === from) { | ||
node.name = to; | ||
} | ||
}, | ||
literal: nop, | ||
"class": nop, | ||
any: nop | ||
var replace = visitor.build({ | ||
rule_ref: function(node) { | ||
if (node.name === from) { | ||
node.name = to; | ||
} | ||
} | ||
}); | ||
replace(ast, from, to); | ||
replace(ast); | ||
} | ||
@@ -61,6 +29,6 @@ | ||
utils.each(ast.rules, function(rule, i) { | ||
arrays.each(ast.rules, function(rule, i) { | ||
if (isProxyRule(rule)) { | ||
replaceRuleRefs(ast, rule.name, rule.expression.name); | ||
if (!utils.contains(options.allowedStartRules, rule.name)) { | ||
if (!arrays.contains(options.allowedStartRules, rule.name)) { | ||
indices.push(i); | ||
@@ -73,5 +41,5 @@ } | ||
utils.each(indices, function(index) { | ||
ast.rules.splice(index, 1); | ||
}); | ||
}; | ||
arrays.each(indices, function(i) { ast.rules.splice(i, 1); }); | ||
} | ||
module.exports = removeProxyRules; |
@@ -1,65 +0,53 @@ | ||
var utils = require("../../utils"), | ||
GrammarError = require("../../grammar-error"); | ||
"use strict"; | ||
/* Checks that no left recursion is present. */ | ||
module.exports = function(ast) { | ||
function nop() {} | ||
var arrays = require("../../utils/arrays"), | ||
GrammarError = require("../../grammar-error"), | ||
asts = require("../asts"), | ||
visitor = require("../visitor"); | ||
function checkExpression(node, appliedRules) { | ||
check(node.expression, appliedRules); | ||
} | ||
/* | ||
* Reports left recursion in the grammar, which prevents infinite recursion in | ||
* the generated parser. | ||
* | ||
* Both direct and indirect recursion is detected. The pass also correctly | ||
* reports cases like this: | ||
* | ||
* start = "a"? start | ||
* | ||
* In general, if a rule reference can be reached without consuming any input, | ||
* it can lead to left recursion. | ||
*/ | ||
function reportLeftRecursion(ast) { | ||
var visitedRules = []; | ||
function checkSubnodes(propertyName) { | ||
return function(node, appliedRules) { | ||
utils.each(node[propertyName], function(subnode) { | ||
check(subnode, appliedRules); | ||
}); | ||
}; | ||
} | ||
var check = visitor.build({ | ||
rule: function(node) { | ||
visitedRules.push(node.name); | ||
check(node.expression); | ||
visitedRules.pop(node.name); | ||
}, | ||
var check = utils.buildNodeVisitor({ | ||
grammar: checkSubnodes("rules"), | ||
sequence: function(node) { | ||
arrays.every(node.elements, function(element) { | ||
check(element); | ||
rule: | ||
function(node, appliedRules) { | ||
check(node.expression, appliedRules.concat(node.name)); | ||
}, | ||
return !asts.alwaysAdvancesOnSuccess(ast, element); | ||
}); | ||
}, | ||
named: checkExpression, | ||
choice: checkSubnodes("alternatives"), | ||
action: checkExpression, | ||
rule_ref: function(node) { | ||
if (arrays.contains(visitedRules, node.name)) { | ||
throw new GrammarError( | ||
"Left recursion detected for rule \"" + node.name + "\".", | ||
node.location | ||
); | ||
} | ||
sequence: | ||
function(node, appliedRules) { | ||
if (node.elements.length > 0) { | ||
check(node.elements[0], appliedRules); | ||
} | ||
}, | ||
check(asts.findRule(ast, node.name)); | ||
} | ||
}); | ||
labeled: checkExpression, | ||
text: checkExpression, | ||
simple_and: checkExpression, | ||
simple_not: checkExpression, | ||
semantic_and: nop, | ||
semantic_not: nop, | ||
optional: checkExpression, | ||
zero_or_more: checkExpression, | ||
one_or_more: checkExpression, | ||
check(ast); | ||
} | ||
rule_ref: | ||
function(node, appliedRules) { | ||
if (utils.contains(appliedRules, node.name)) { | ||
throw new GrammarError( | ||
"Left recursion detected for rule \"" + node.name + "\"." | ||
); | ||
} | ||
check(utils.findRuleByName(ast, node.name), appliedRules); | ||
}, | ||
literal: nop, | ||
"class": nop, | ||
any: nop | ||
}); | ||
check(ast, []); | ||
}; | ||
module.exports = reportLeftRecursion; |
@@ -1,46 +0,23 @@ | ||
var utils = require("../../utils"), | ||
GrammarError = require("../../grammar-error"); | ||
"use strict"; | ||
var GrammarError = require("../../grammar-error"), | ||
asts = require("../asts"), | ||
visitor = require("../visitor"); | ||
/* Checks that all referenced rules exist. */ | ||
module.exports = function(ast) { | ||
function nop() {} | ||
function checkExpression(node) { check(node.expression); } | ||
function checkSubnodes(propertyName) { | ||
return function(node) { utils.each(node[propertyName], check); }; | ||
} | ||
var check = utils.buildNodeVisitor({ | ||
grammar: checkSubnodes("rules"), | ||
rule: checkExpression, | ||
named: checkExpression, | ||
choice: checkSubnodes("alternatives"), | ||
action: checkExpression, | ||
sequence: checkSubnodes("elements"), | ||
labeled: checkExpression, | ||
text: checkExpression, | ||
simple_and: checkExpression, | ||
simple_not: checkExpression, | ||
semantic_and: nop, | ||
semantic_not: nop, | ||
optional: checkExpression, | ||
zero_or_more: checkExpression, | ||
one_or_more: checkExpression, | ||
rule_ref: | ||
function(node) { | ||
if (!utils.findRuleByName(ast, node.name)) { | ||
throw new GrammarError( | ||
"Referenced rule \"" + node.name + "\" does not exist." | ||
); | ||
} | ||
}, | ||
literal: nop, | ||
"class": nop, | ||
any: nop | ||
function reportMissingRules(ast) { | ||
var check = visitor.build({ | ||
rule_ref: function(node) { | ||
if (!asts.findRule(ast, node.name)) { | ||
throw new GrammarError( | ||
"Referenced rule \"" + node.name + "\" does not exist.", | ||
node.location | ||
); | ||
} | ||
} | ||
}); | ||
check(ast); | ||
}; | ||
} | ||
module.exports = reportMissingRules; |
@@ -1,9 +0,18 @@ | ||
var utils = require("./utils"); | ||
"use strict"; | ||
var classes = require("./utils/classes"); | ||
/* Thrown when the grammar contains an error. */ | ||
module.exports = function(message) { | ||
function GrammarError(message, location) { | ||
this.name = "GrammarError"; | ||
this.message = message; | ||
}; | ||
this.location = location; | ||
utils.subclass(module.exports, Error); | ||
if (typeof Error.captureStackTrace === "function") { | ||
Error.captureStackTrace(this, GrammarError); | ||
} | ||
} | ||
classes.subclass(GrammarError, Error); | ||
module.exports = GrammarError; |
@@ -1,6 +0,9 @@ | ||
var utils = require("./utils"); | ||
"use strict"; | ||
module.exports = { | ||
var arrays = require("./utils/arrays"), | ||
objects = require("./utils/objects"); | ||
var PEG = { | ||
/* PEG.js version (uses semantic versioning). */ | ||
VERSION: "0.8.0", | ||
VERSION: "0.9.0", | ||
@@ -28,3 +31,3 @@ GrammarError: require("./grammar-error"), | ||
if (passes.hasOwnProperty(stage)) { | ||
converted[stage] = utils.values(passes[stage]); | ||
converted[stage] = objects.values(passes[stage]); | ||
} | ||
@@ -36,3 +39,3 @@ } | ||
var options = arguments.length > 1 ? utils.clone(arguments[1]) : {}, | ||
var options = arguments.length > 1 ? objects.clone(arguments[1]) : {}, | ||
plugins = "plugins" in options ? options.plugins : [], | ||
@@ -44,3 +47,3 @@ config = { | ||
utils.each(plugins, function(p) { p.use(config, options); }); | ||
arrays.each(plugins, function(p) { p.use(config, options); }); | ||
@@ -54,1 +57,3 @@ return this.compiler.compile( | ||
}; | ||
module.exports = PEG; |
{ | ||
"name": "pegjs", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"description": "Parser generator for JavaScript", | ||
"homepage": "http://pegjs.majda.cz/", | ||
"homepage": "http://pegjs.org/", | ||
"license": "MIT", | ||
"author": { | ||
@@ -22,3 +23,6 @@ "name": "David Majda", | ||
"lib/compiler.js", | ||
"lib/compiler/asts.js", | ||
"lib/compiler/javascript.js", | ||
"lib/compiler/opcodes.js", | ||
"lib/compiler/visitor.js", | ||
"lib/compiler/passes/generate-bytecode.js", | ||
@@ -28,2 +32,3 @@ "lib/compiler/passes/generate-javascript.js", | ||
"lib/compiler/passes/report-left-recursion.js", | ||
"lib/compiler/passes/report-infinite-loops.js", | ||
"lib/compiler/passes/report-missing-rules.js", | ||
@@ -33,3 +38,5 @@ "lib/grammar-error.js", | ||
"lib/peg.js", | ||
"lib/utils.js", | ||
"lib/utils/arrays.js", | ||
"lib/utils/classes.js", | ||
"lib/utils/objects.js", | ||
"package.json" | ||
@@ -40,16 +47,16 @@ ], | ||
"scripts": { | ||
"test": "make spec" | ||
"test": "make hint && make spec" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/dmajda/pegjs.git" | ||
"url": "http://github.com/pegjs/pegjs.git" | ||
}, | ||
"devDependencies": { | ||
"jasmine-node": "= 1.11.0", | ||
"uglify-js": "= 2.4.7", | ||
"jshint": "= 2.3.0" | ||
"jasmine-node": "= 1.14.5", | ||
"uglify-js": "= 2.4.24", | ||
"jshint": "= 2.8.0" | ||
}, | ||
"engines": { | ||
"node": ">= 0.8" | ||
"node": ">= 0.10.0" | ||
} | ||
} |
132
README.md
@@ -18,4 +18,4 @@ PEG.js | ||
— more powerful than traditional LL(*k*) and LR(*k*) parsers | ||
* Usable [from your browser](http://pegjs.majda.cz/online), from the command | ||
line, or via JavaScript API | ||
* Usable [from your browser](http://pegjs.org/online), from the command line, | ||
or via JavaScript API | ||
@@ -25,3 +25,3 @@ Getting Started | ||
[Online version](http://pegjs.majda.cz/online) is the easiest way to generate a | ||
[Online version](http://pegjs.org/online) is the easiest way to generate a | ||
parser. Just enter your grammar, try parsing few inputs, and download generated | ||
@@ -48,5 +48,7 @@ parser code. | ||
[Download](http://pegjs.majda.cz/#download) the PEG.js library (regular or | ||
minified version). | ||
[Download](http://pegjs.org/#download) the PEG.js library (regular or minified | ||
version) or install it using Bower: | ||
$ bower install pegjs | ||
Generating a Parser | ||
@@ -70,3 +72,3 @@ ------------------- | ||
If you omit both input and ouptut file, standard input and output are used. | ||
If you omit both input and output file, standard input and output are used. | ||
@@ -90,2 +92,3 @@ By default, the parser object is assigned to `module.exports`, which makes the | ||
pass to `PEG.buildParser` | ||
* `--trace` — makes the parser trace its progress | ||
@@ -132,4 +135,4 @@ ### JavaScript API | ||
value depends on the grammar used to build the parser) or throw an exception if | ||
the input is invalid. The exception will contain `offset`, `line`, `column`, | ||
`expected`, `found` and `message` properties with more details about the error. | ||
the input is invalid. The exception will contain `location`, `expected`, `found` | ||
and `message` properties with more details about the error. | ||
@@ -141,5 +144,6 @@ parser.parse("abba"); // returns ["a", "b", "b", "a"] | ||
You can tweak parser behavior by passing a second parameter with an options | ||
object to the `parse` method. Only one option is currently supported: | ||
object to the `parse` method. The following options are supported: | ||
* `startRule` — name of the rule to start parsing from | ||
* `tracer` — tracer to use | ||
@@ -192,9 +196,10 @@ Parsers can also support their own custom options. | ||
Rules can be preceded by an *initializer* — a piece of JavaScript code in curly | ||
braces (“{” and “}”). This code is executed before the generated parser starts | ||
parsing. All variables and functions defined in the initializer are accessible | ||
in rule actions and semantic predicates. The code inside the initializer can | ||
access options passed to the parser using the `options` variable. Curly braces | ||
in the initializer code must be balanced. Let's look at the example grammar | ||
from above using a simple initializer. | ||
The first rule can be preceded by an *initializer* — a piece of JavaScript code | ||
in curly braces (“{” and “}”). This code is executed before the generated parser | ||
starts parsing. All variables and functions defined in the initializer are | ||
accessible in rule actions and semantic predicates. The code inside the | ||
initializer can access the parser object using the `parser` variable and options | ||
passed to the parser using the `options` variable. Curly braces in the | ||
initializer code must be balanced. Let's look at the example grammar from above | ||
using a simple initializer. | ||
@@ -291,3 +296,4 @@ { | ||
in an array. The matching is greedy, i.e. the parser tries to match the | ||
expression as many times as possible. | ||
expression as many times as possible. Unlike in regular expressions, there is no | ||
backtracking. | ||
@@ -298,3 +304,4 @@ #### *expression* + | ||
in an array. The matching is greedy, i.e. the parser tries to match the | ||
expression as many times as possible. | ||
expression as many times as possible. Unlike in regular expressions, there is no | ||
backtracking. | ||
@@ -304,3 +311,4 @@ #### *expression* ? | ||
Try to match the expression. If the match succeeds, return its match result, | ||
otherwise return `null`. | ||
otherwise return `null`. Unlike in regular expressions, there is no | ||
backtracking. | ||
@@ -330,10 +338,17 @@ #### & *expression* | ||
The code inside the predicate can also access the current parse position using | ||
the `offset` function. It returns a zero-based character index into the input | ||
string. The code can also access the current line and column using the `line` | ||
and `column` functions. Both return one-based indexes. | ||
The code inside the predicate can also access location information using the | ||
`location` function. It returns an object like this: | ||
The code inside the predicate can also access options passed to the parser using | ||
the `options` variable. | ||
{ | ||
start: { offset: 23, line: 5, column: 6 }, | ||
end: { offset: 23, line: 5, column: 6 } | ||
} | ||
The `start` and `end` properties both refer to the current parse position. The | ||
`offset` property contains an offset as a zero-based index and `line` and | ||
`column` properties contain a line and a column as one-based indices. | ||
The code inside the predicate can also access the parser object using the | ||
`parser` variable and options passed to the parser using the `options` variable. | ||
Note that curly braces in the predicate code must be balanced. | ||
@@ -353,10 +368,17 @@ | ||
The code inside the predicate can also access the current parse position using | ||
the `offset` function. It returns a zero-based character index into the input | ||
string. The code can also access the current line and column using the `line` | ||
and `column` functions. Both return one-based indexes. | ||
The code inside the predicate can also access location information using the | ||
`location` function. It returns an object like this: | ||
The code inside the predicate can also access options passed to the parser using | ||
the `options` variable. | ||
{ | ||
start: { offset: 23, line: 5, column: 6 }, | ||
end: { offset: 23, line: 5, column: 6 } | ||
} | ||
The `start` and `end` properties both refer to the current parse position. The | ||
`offset` property contains an offset as a zero-based index and `line` and | ||
`column` properties contain a line and a column as one-based indices. | ||
The code inside the predicate can also access the parser object using the | ||
`parser` variable and options passed to the parser using the `options` variable. | ||
Note that curly braces in the predicate code must be balanced. | ||
@@ -408,11 +430,19 @@ | ||
The code inside the action can also access the parse position at the beginning | ||
of the action's expression using the `offset` function. It returns a zero-based | ||
character index into the input string. The code can also access the line and | ||
column at the beginning of the action's expression using the `line` and `column` | ||
functions. Both return one-based indexes. | ||
The code inside the action can also access options passed to the parser using | ||
the `options` variable. | ||
The code inside the action can also access location information using the | ||
`location` function. It returns an object like this: | ||
{ | ||
start: { offset: 23, line: 5, column: 6 }, | ||
end: { offset: 25, line: 5, column: 8 } | ||
} | ||
The `start` property refers to the position at the beginning of the expression, | ||
the `end` property refers to position after the end of the expression. The | ||
`offset` property contains an offset as a zero-based index and `line` and | ||
`column` properties contain a line and a column as one-based indices. | ||
The code inside the action can also access the parser object using the `parser` | ||
variable and options passed to the parser using the `options` variable. | ||
Note that curly braces in the action code must be balanced. | ||
@@ -432,4 +462,6 @@ | ||
* Node.js 0.8.0+ | ||
* IE 8+ | ||
* Node.js 0.10.0+ | ||
* io.js | ||
* Internet Explorer 8+ | ||
* Edge | ||
* Firefox | ||
@@ -443,7 +475,7 @@ * Chrome | ||
* [Project website](http://pegjs.majda.cz/) | ||
* [Wiki](https://github.com/dmajda/pegjs/wiki) | ||
* [Source code](https://github.com/dmajda/pegjs) | ||
* [Project website](http://pegjs.org/) | ||
* [Wiki](https://github.com/pegjs/pegjs/wiki) | ||
* [Source code](https://github.com/pegjs/pegjs) | ||
* [Trello board](https://trello.com/board/peg-js/50a8eba48cf95d4957006b01) | ||
* [Issue tracker](https://github.com/dmajda/pegjs/issues) | ||
* [Issue tracker](https://github.com/pegjs/pegjs/issues) | ||
* [Google Group](http://groups.google.com/group/pegjs) | ||
@@ -453,8 +485,12 @@ * [Twitter](http://twitter.com/peg_js) | ||
PEG.js is developed by [David Majda](http://majda.cz/) | ||
([@dmajda](http://twitter.com/dmajda)). You are welcome to contribute code. | ||
Unless your contribution is really trivial you should get in touch with me first | ||
— this can prevent wasted effort on both sides. You can send code both as a | ||
patch or a GitHub pull request. | ||
([@dmajda](http://twitter.com/dmajda)). The [Bower | ||
package](https://github.com/pegjs/bower) is maintained by [Michel | ||
Krämer](http://www.michel-kraemer.com/) | ||
([@michelkraemer](https://twitter.com/michelkraemer)). | ||
You are welcome to contribute code. Unless your contribution is really trivial | ||
you should get in touch with me first — this can prevent wasted effort on both | ||
sides. You can send code both as a patch or a GitHub pull request. | ||
Note that PEG.js is still very much work in progress. There are no compatibility | ||
guarantees until version 1.0. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
365490
27
7032
1
480
7