From fe1ca481abc7ee5a499a26eed226f06c9c2024d5 Mon Sep 17 00:00:00 2001 From: David Majda Date: Tue, 1 Jan 2013 15:15:37 +0100 Subject: [PATCH] Code generator rewrite This is a complete rewrite of the PEG.js code generator. Its goals are: 1. Allow optimizing the generated parser code for code size as well as for parsing speed. 2. Prepare ground for future optimizations and big features (like incremental parsing). 2. Replace the old template-based code-generation system with something more lightweight and flexible. 4. General code cleanup (structure, style, variable names, ...). New Architecture ---------------- The new code generator consists of two steps: * Bytecode generator -- produces bytecode for an abstract virtual machine * JavaScript generator -- produces JavaScript code based on the bytecode The abstract virtual machine is stack-based. Originally I wanted to make it register-based, but it turned out that all the code related to it would be more complex and the bytecode itself would be longer (because of explicit register specifications in instructions). The only downsides of the stack-based approach seem to be few small inefficiencies (see e.g. the |NIP| instruction), which seem to be insignificant. The new generator allows optimizing for parsing speed or code size (you can choose using the |optimize| option of the |PEG.buildParser| method or the --optimize/-o option on the command-line). When optimizing for size, the JavaScript generator emits the bytecode together with its constant table and a generic bytecode interpreter. Because the interpreter is small and the bytecode and constant table grow only slowly with size of the grammar, the resulting parser is also small. When optimizing for speed, the JavaScript generator just compiles the bytecode into JavaScript. The generated code is relatively efficient, so the resulting parser is fast. Internal Identifiers -------------------- As a small bonus, all internal identifiers visible to user code in the initializer, actions and predicates are prefixed by |peg$|. This lowers the chance that identifiers in user code will conflict with the ones from PEG.js. It also makes using any internals in user code ugly, which is a good thing. This solves GH-92. Performance ----------- The new code generator improved parsing speed and parser code size significantly. The generated parsers are now: * 39% faster when optimizing for speed * 69% smaller when optimizing for size (without minification) * 31% smaller when optimizing for size (with minification) (Parsing speed was measured using the |benchmark/run| script. Code size was measured by generating parsers for examples in the |examples| directory and adding up the file sizes. Minification was done by |uglify --ascii| in version 1.3.4.) Final Note ---------- This is just a beginning! The new code generator lays a foundation upon which many optimizations and improvements can (and will) be made. Stay tuned :-) --- Makefile | 5 +- README.md | 2 + benchmark/index.css | 8 +- benchmark/index.html | 5 + benchmark/index.js | 3 +- benchmark/run | 19 +- bin/pegjs | 19 +- lib/compiler.js | 4 +- lib/compiler/opcodes.js | 46 + lib/compiler/passes.js | 4 +- lib/compiler/passes/allocate-registers.js | 230 - lib/compiler/passes/generate-bytecode.js | 594 ++ lib/compiler/passes/generate-code.js | 867 --- lib/compiler/passes/generate-javascript.js | 950 +++ lib/parser.js | 5253 +++++++++-------- lib/utils.js | 14 + package.json | 5 +- .../passes/allocate-registers.spec.js | 309 - .../compiler/passes/generate-bytecode.spec.js | 674 +++ spec/generated-parser.spec.js | 7 +- spec/index.html | 2 +- 21 files changed, 4973 insertions(+), 4047 deletions(-) create mode 100644 lib/compiler/opcodes.js delete mode 100644 lib/compiler/passes/allocate-registers.js create mode 100644 lib/compiler/passes/generate-bytecode.js delete mode 100644 lib/compiler/passes/generate-code.js create mode 100644 lib/compiler/passes/generate-javascript.js delete mode 100644 spec/compiler/passes/allocate-registers.spec.js create mode 100644 spec/compiler/passes/generate-bytecode.spec.js diff --git a/Makefile b/Makefile index 5df7cd9..d99a681 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,9 @@ PEGJS_VERSION = `cat $(VERSION_FILE)` MODULES = utils \ grammar-error \ parser \ - compiler/passes/allocate-registers \ - compiler/passes/generate-code \ + compiler/opcodes \ + compiler/passes/generate-bytecode \ + compiler/passes/generate-javascript \ compiler/passes/remove-proxy-rules \ compiler/passes/report-left-recursion \ compiler/passes/report-missing-rules \ diff --git a/README.md b/README.md index 59d7ed9..4151146 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,8 @@ object to `PEG.buildParser`. The following options are supported: * `output` — if set to `"parser"`, the method will return generated parser object; if set to `"source"`, it will return parser source code as a string (default: `"parser"`) + * `optimize`— selects between optimizing the generated parser for parsing + speed (`"speed"`) or code size (`"size"`) (default: `"speed"`) Using the Parser ---------------- diff --git a/benchmark/index.css b/benchmark/index.css index 9156969..adb079b 100644 --- a/benchmark/index.css +++ b/benchmark/index.css @@ -24,11 +24,11 @@ table tr.total td.parse-speed .value { font-size: 175%; } a, a:visited { color: #3d586c; } #options { - width: 46em; - margin: 2em auto; border-radius: .5em; -moz-border-radius: .5em; padding: .5em 1em; - text-align: center; + width: 45em; + margin: 2em auto; border-radius: .5em; -moz-border-radius: .5em; padding: .5em 1.5em; background-color: #f0f0f0; } #options #run-count { width: 3em; } #options #cache { margin-left: 2em; } -#options #run { width: 5em; margin-left: 2em; } +#options label[for=optimize] { margin-left: 2em; } +#options #run { float:right; width: 5em; } diff --git a/benchmark/index.html b/benchmark/index.html index 5c81418..92cf58a 100644 --- a/benchmark/index.html +++ b/benchmark/index.html @@ -13,6 +13,11 @@ times + + diff --git a/benchmark/index.js b/benchmark/index.js index 874f196..f13aa41 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -63,7 +63,8 @@ $("#run").click(function() { } var options = { - cache: $("#cache").is(":checked"), + cache: $("#cache").is(":checked"), + optimize: $("#optimize").val() }; Runner.run(benchmarks, runCount, options, { diff --git a/benchmark/run b/benchmark/run index 1882fb1..1e9081b 100755 --- a/benchmark/run +++ b/benchmark/run @@ -81,6 +81,8 @@ function printHelp() { util.puts("Options:"); util.puts(" -n, --run-count number of runs (default: 10)"); util.puts(" --cache make tested parsers cache results"); + util.puts(" -o, --optimize select optimization for speed or size (default:"); + util.puts(" speed)"); } function exitSuccess() { @@ -111,7 +113,10 @@ function nextArg() { /* Main */ var runCount = 10; -var options = { }; +var options = { + cache: false, + optimize: "speed" +}; while (args.length > 0 && isOption(args[0])) { switch (args[0]) { @@ -131,6 +136,18 @@ while (args.length > 0 && isOption(args[0])) { options.cache = true; break; + case "-o": + case "--optimize": + nextArg(); + if (args.length === 0) { + abort("Missing parameter of the -o/--optimize option."); + } + if (args[0] !== "speed" && args[0] !== "size") { + abort("Optimization goal must be either \"speed\" or \"size\"."); + } + options.optimize = args[0]; + break; + case "-h": case "--help": printHelp(); diff --git a/bin/pegjs b/bin/pegjs index 3c1421e..fd3c586 100755 --- a/bin/pegjs +++ b/bin/pegjs @@ -29,6 +29,8 @@ function printHelp() { util.puts(" parser will be allowed to start parsing"); util.puts(" from (default: the first rule in the"); util.puts(" grammar)"); + util.puts(" -o, --optimize select optimization for speed or size (default:"); + util.puts(" speed)"); util.puts(" -v, --version print version information and exit"); util.puts(" -h, --help print help and exit"); } @@ -71,8 +73,9 @@ function readStream(inputStream, callback) { /* This makes the generated parser a CommonJS module by default. */ var exportVar = "module.exports"; var options = { - cache: false, - output: "source" + cache: false, + output: "source", + optimize: "speed" }; while (args.length > 0 && isOption(args[0])) { @@ -100,6 +103,18 @@ while (args.length > 0 && isOption(args[0])) { .map(function(s) { return s.trim() }); break; + case "-o": + case "--optimize": + nextArg(); + if (args.length === 0) { + abort("Missing parameter of the -o/--optimize option."); + } + if (args[0] !== "speed" && args[0] !== "size") { + abort("Optimization goal must be either \"speed\" or \"size\"."); + } + options.optimize = args[0]; + break; + case "-v": case "--version": printVersion(); diff --git a/lib/compiler.js b/lib/compiler.js index 0ebd2cd..3d107c0 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -11,8 +11,8 @@ module.exports = { "reportMissingRules", "reportLeftRecursion", "removeProxyRules", - "allocateRegisters", - "generateCode" + "generateBytecode", + "generateJavascript" ], /* diff --git a/lib/compiler/opcodes.js b/lib/compiler/opcodes.js new file mode 100644 index 0000000..fb3fa71 --- /dev/null +++ b/lib/compiler/opcodes.js @@ -0,0 +1,46 @@ +/* Bytecode instruction opcodes. */ +module.exports = { + /* 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 + NIP_CURR_POS: 6, // NIP_CURR_POS + APPEND: 7, // APPEND + WRAP: 8, // WRAP n + TEXT: 9, // TEXT + + /* Conditions and Loops */ + + IF: 10, // IF t, f + IF_ERROR: 11, // IF_ERROR t, f + IF_NOT_ERROR: 12, // IF_NOT_ERROR t, f + WHILE_NOT_ERROR: 13, // WHILE_NOT_ERROR b + + /* Matching */ + + MATCH_ANY: 14, // MATCH_ANY a, f, ... + MATCH_STRING: 15, // MATCH_STRING s, a, f, ... + MATCH_STRING_IC: 16, // MATCH_STRING_IC s, a, f, ... + MATCH_REGEXP: 17, // MATCH_REGEXP r, a, f, ... + ACCEPT_N: 18, // ACCEPT_N n + ACCEPT_STRING: 19, // ACCEPT_STRING s + FAIL: 20, // FAIL e + + /* Calls */ + + REPORT_SAVED_POS: 21, // REPORT_SAVED_POS p + REPORT_CURR_POS: 22, // REPORT_CURR_POS + CALL: 23, // CALL f, n, pc, p1, p2, ..., pN + + /* Rules */ + + RULE: 24, // RULE r + + /* Failure Reporting */ + + SILENT_FAILS_ON: 25, // SILENT_FAILS_ON + SILENT_FAILS_OFF: 26 // SILENT_FAILS_FF +}; diff --git a/lib/compiler/passes.js b/lib/compiler/passes.js index a4c3723..bf3a0bf 100644 --- a/lib/compiler/passes.js +++ b/lib/compiler/passes.js @@ -9,6 +9,6 @@ module.exports = { reportMissingRules: require("./passes/report-missing-rules"), reportLeftRecursion: require("./passes/report-left-recursion"), removeProxyRules: require("./passes/remove-proxy-rules"), - allocateRegisters: require("./passes/allocate-registers"), - generateCode: require("./passes/generate-code") + generateBytecode: require("./passes/generate-bytecode"), + generateJavascript: require("./passes/generate-javascript") }; diff --git a/lib/compiler/passes/allocate-registers.js b/lib/compiler/passes/allocate-registers.js deleted file mode 100644 index 7ec8af7..0000000 --- a/lib/compiler/passes/allocate-registers.js +++ /dev/null @@ -1,230 +0,0 @@ -var utils = require("../../utils"); - -/* - * Allocates registers that the generated code for each node will use to store - * match results and parse positions. For "action", "semantic_and" and - * "semantic_or" nodes it also computes visibility of labels at the point of - * action/predicate code execution and a mapping from label names to registers - * that will contain the labeled values. - * - * The following will hold after running this pass: - * - * * All nodes except "grammar" and "rule" nodes will have a |resultIndex| - * property. It will contain an index of a register that will store a match - * result of the expression represented by the node in generated code. - * - * * Some nodes will have a |posIndex| property. It will contain an index of a - * register that will store a saved parse position in generated code. - * - * * All "rule" nodes will contain a |registerCount| property. It will contain - * the number of registers that will be used by code generated for the - * rule's expression. - * - * * All "action", "semantic_and" and "semantic_or" nodes will have a |params| - * property. It will contain a mapping from names of labels visible at the - * point of action/predicate code execution to registers that will contain - * the labeled values. - */ -module.exports = function(ast) { - /* - * Register allocator that allocates registers from an unlimited - * integer-indexed pool. It allows allocating and releaseing registers in any - * order. It also supports reference counting (this simplifies tracking active - * registers when they store values passed to action/predicate code). - * Allocating a register allways uses the first free register (the one with - * the lowest index). - */ - var registers = (function() { - var refCounts = []; // reference count for each register that was - // allocated at least once - - return { - alloc: function() { - var i; - - for (i = 0; i < refCounts.length; i++) { - if (refCounts[i] === 0) { - refCounts[i] = 1; - return i; - } - } - - refCounts.push(1); - return refCounts.length - 1; - }, - - use: function(index) { - refCounts[index]++; - }, - - release: function(index) { - refCounts[index]--; - }, - - maxIndex: function() { - return refCounts.length - 1; - }, - - reset: function() { - refCounts = []; - } - }; - })(); - - /* - * Manages mapping of label names to indices of registers that will store the - * labeled values as long as they are in scope. - */ - var vars = (function(registers) { - var envs = []; // stack of nested environments - - return { - beginScope: function() { - envs.push({}); - }, - - endScope: function() { - var env = envs.pop(), name; - - for (name in env) { - registers.release(env[name]); - } - }, - - add: function(name, index) { - envs[envs.length - 1][name] = index; - registers.use(index); - }, - - buildParams: function() { - var env = envs[envs.length - 1], params = {}, name; - - for (name in env) { - params[name] = env[name]; - } - - return params; - } - }; - })(registers); - - function savePos(node, f) { - node.posIndex = registers.alloc(); - f(); - registers.release(node.posIndex); - } - - function reuseResult(node, subnode) { - subnode.resultIndex = node.resultIndex; - } - - function allocResult(node, f) { - node.resultIndex = registers.alloc(); - f(); - registers.release(node.resultIndex); - } - - function scoped(f) { - vars.beginScope(); - f(); - vars.endScope(); - } - - function nop() {} - - function computeExpressionScoped(node) { - scoped(function() { compute(node.expression); }); - } - - function computeExpressionScopedReuseResult(node) { - reuseResult(node, node.expression); - computeExpressionScoped(node); - } - - function computeExpressionScopedAllocResult(node) { - allocResult(node.expression, function() { computeExpressionScoped(node); }); - } - - function computeExpressionScopedReuseResultSavePos(node) { - savePos(node, function() { computeExpressionScopedReuseResult(node); }); - } - - function computeParams(node) { - node.params = vars.buildParams(); - } - - var compute = utils.buildNodeVisitor({ - grammar: - function(node) { - utils.each(node.rules, compute); - }, - - rule: - function(node) { - registers.reset(); - computeExpressionScopedAllocResult(node); - node.registerCount = registers.maxIndex() + 1; - }, - - named: - function(node) { - reuseResult(node, node.expression); - compute(node.expression); - }, - - choice: - function(node) { - utils.each(node.alternatives, function(alternative) { - reuseResult(node, alternative); - scoped(function() { - compute(alternative); - }); - }); - }, - - action: - function(node) { - savePos(node, function() { - reuseResult(node, node.expression); - scoped(function() { - compute(node.expression); - computeParams(node); - }); - }); - }, - - sequence: - function(node) { - savePos(node, function() { - utils.each(node.elements, function(element) { - element.resultIndex = registers.alloc(); - compute(element); - }); - utils.each(node.elements, function(element) { - registers.release(element.resultIndex); - }); - }); - }, - - labeled: - function(node) { - vars.add(node.label, node.resultIndex); - computeExpressionScopedReuseResult(node); - }, - - text: computeExpressionScopedReuseResultSavePos, - simple_and: computeExpressionScopedReuseResultSavePos, - simple_not: computeExpressionScopedReuseResultSavePos, - semantic_and: computeParams, - semantic_not: computeParams, - optional: computeExpressionScopedReuseResult, - zero_or_more: computeExpressionScopedAllocResult, - one_or_more: computeExpressionScopedAllocResult, - rule_ref: nop, - literal: nop, - "class": nop, - any: nop - }); - - compute(ast); -}; diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js new file mode 100644 index 0000000..3d4cf52 --- /dev/null +++ b/lib/compiler/passes/generate-bytecode.js @@ -0,0 +1,594 @@ +var utils = require("../../utils"), + op = require("../opcodes"); + +/* Generates bytecode. + * + * Instructions + * ============ + * + * Stack Manipulation + * ------------------ + * + * [0] PUSH c + * + * stack.push(consts[c]); + * + * [1] PUSH_CURR_POS + * + * stack.push(currPos); + * + * [2] POP + * + * stack.pop(); + * + * [3] POP_CURR_POS + * + * currPos = stack.pop(); + * + * [4] POP_N n + * + * stack.pop(n); + * + * [5] NIP + * + * value = stack.pop(); + * stack.pop(); + * stack.push(value); + * + * [6] NIP_CURR_POS + * + * value = stack.pop(); + * currPos = stack.pop(); + * stack.push(value); + * + * [8] APPEND + * + * value = stack.pop(); + * array = stack.pop(); + * array.push(value); + * stack.push(array); + * + * [9] WRAP n + * + * stack.push(stack.pop(n)); + * + * [10] TEXT + * + * stack.pop(); + * stack.push(input.substring(stack.top(), currPos)); + * + * Conditions and Loops + * -------------------- + * + * [11] IF t, f + * + * if (stack.top()) { + * interpret(ip + 3, ip + 3 + t); + * } else { + * interpret(ip + 3 + t, ip + 3 + t + f); + * } + * + * [12] IF_ERROR t, f + * + * if (stack.top() === null) { + * interpret(ip + 3, ip + 3 + t); + * } else { + * interpret(ip + 3 + t, ip + 3 + t + f); + * } + * + * [13] IF_NOT_ERROR t, f + * + * if (stack.top() !== null) { + * interpret(ip + 3, ip + 3 + t); + * } else { + * interpret(ip + 3 + t, ip + 3 + t + f); + * } + * + * [14] WHILE_NOT_ERROR b + * + * while(stack.top() !== null) { + * interpret(ip + 2, ip + 2 + b); + * } + * + * Matching + * -------- + * + * [15] MATCH_ANY a, f, ... + * + * if (input.length > currPos) { + * interpret(ip + 3, ip + 3 + a); + * } else { + * interpret(ip + 3 + a, ip + 3 + a + f); + * } + * + * [16] MATCH_STRING s, a, f, ... + * + * if (input.substr(currPos, consts[s].length) === consts[s]) { + * interpret(ip + 4, ip + 4 + a); + * } else { + * interpret(ip + 4 + a, ip + 4 + a + f); + * } + * + * [17] MATCH_STRING_IC s, a, f, ... + * + * if (input.substr(currPos, consts[s].length).toLowerCase() === consts[s]) { + * interpret(ip + 4, ip + 4 + a); + * } else { + * interpret(ip + 4 + a, ip + 4 + a + f); + * } + * + * [18] MATCH_REGEXP r, a, f, ... + * + * if (consts[r].test(input.charAt(currPos))) { + * interpret(ip + 4, ip + 4 + a); + * } else { + * interpret(ip + 4 + a, ip + 4 + a + f); + * } + * + * [19] ACCEPT_N n + * + * stack.push(input.substring(currPos, n)); + * currPos += n; + * + * [20] ACCEPT_STRING s + * + * stack.push(consts[s]); + * currPos += consts[s].length; + * + * [21] FAIL e + * + * stack.push(null); + * fail(consts[e]); + * + * Calls + * ----- + * + * [22] REPORT_SAVED_POS p + * + * reportedPos = stack[p]; + * + * [23] REPORT_CURR_POS + * + * reportedPos = currPos; + * + * [25] CALL f, n, pc, p1, p2, ..., pN + * + * value = consts[f](stack[p1], ..., stack[pN]); + * stack.pop(n); + * stack.push(value); + * + * Rules + * ----- + * + * [26] RULE r + * + * stack.push(parseRule(r)); + * + * Failure Reporting + * ----------------- + * + * [27] SILENT_FAILS_ON + * + * silentFails++; + * + * [28] SILENT_FAILS_OFF + * + * silentFails--; + */ +module.exports = function(ast, options) { + var consts = []; + + function addConst(value) { + var index = utils.indexOf(consts, function(c) { return c === value; }); + + return index === -1 ? consts.push(value) - 1 : index; + } + + function addFunctionConst(params, code) { + return addConst( + "function(" + params.join(", ") + ") {" + code + "}" + ); + } + + function buildSequence() { + return Array.prototype.concat.apply([], arguments); + } + + function buildCondition(condCode, thenCode, elseCode) { + return condCode.concat( + [thenCode.length, elseCode.length], + thenCode, + elseCode + ); + } + + function buildLoop(condCode, bodyCode) { + return condCode.concat([bodyCode.length], bodyCode); + } + + function buildCall(functionIndex, delta, env, sp) { + var params = utils.map( utils.values(env), function(p) { return sp - p; }); + + return [op.CALL, functionIndex, delta, params.length].concat(params); + } + + function buildSimplePredicate(expression, negative, context) { + var emptyStringIndex = addConst('""'), + nullIndex = addConst('null'); + + return buildSequence( + [op.PUSH_CURR_POS], + [op.SILENT_FAILS_ON], + generate(expression, { + sp: context.sp + 1, + env: { }, + action: null + }), + [op.SILENT_FAILS_OFF], + buildCondition( + [negative ? op.IF_ERROR : op.IF_NOT_ERROR], + buildSequence( + [op.POP], + [negative ? op.POP : op.POP_CURR_POS], + [op.PUSH, emptyStringIndex] + ), + buildSequence( + [op.POP], + [negative ? op.POP_CURR_POS : op.POP], + [op.PUSH, nullIndex] + ) + ) + ); + } + + function buildSemanticPredicate(code, negative, context) { + var functionIndex = addFunctionConst(utils.keys(context.env), code), + emptyStringIndex = addConst('""'), + nullIndex = addConst('null'); + + return buildSequence( + [op.REPORT_CURR_POS], + buildCall(functionIndex, 0, context.env, context.sp), + buildCondition( + [op.IF], + buildSequence( + [op.POP], + [op.PUSH, negative ? nullIndex : emptyStringIndex] + ), + buildSequence( + [op.POP], + [op.PUSH, negative ? emptyStringIndex : nullIndex] + ) + ) + ); + } + + function buildAppendLoop(expressionCode) { + return buildLoop( + [op.WHILE_NOT_ERROR], + buildSequence([op.APPEND], expressionCode) + ); + } + + var generate = utils.buildNodeVisitor({ + grammar: function(node) { + utils.each(node.rules, generate); + + node.consts = consts; + }, + + rule: function(node) { + 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 + }); + }, + + named: function(node, context) { + var nameIndex = addConst(utils.quote(node.name)); + + /* + * The code generated below is slightly suboptimal because |FAIL| pushes + * to the stack, so we need to stick a |POP| in front of it. We lack a + * dedicated instruction that would just report the failure and not touch + * the stack. + */ + return buildSequence( + [op.SILENT_FAILS_ON], + generate(node.expression, context), + [op.SILENT_FAILS_OFF], + buildCondition([op.IF_ERROR], [op.FAIL, nameIndex], []) + ); + }, + + choice: function(node, context) { + function buildAlternativesCode(alternatives, context) { + return buildSequence( + generate(alternatives[0], { + sp: context.sp, + env: { }, + action: null + }), + alternatives.length > 1 + ? buildCondition( + [op.IF_ERROR], + buildSequence( + [op.POP], + buildAlternativesCode(alternatives.slice(1), context) + ), + [] + ) + : [] + ); + } + + return buildAlternativesCode(node.alternatives, context); + }, + + action: function(node, context) { + var env = { }, + emitCall = node.expression.type !== "sequence" + || node.expression.elements.length === 0; + expressionCode = generate(node.expression, { + sp: context.sp + (emitCall ? 1 : 0), + env: env, + action: node + }), + functionIndex = addFunctionConst(utils.keys(env), node.code); + + return emitCall + ? buildSequence( + [op.PUSH_CURR_POS], + expressionCode, + buildCondition( + [op.IF_NOT_ERROR], + buildSequence( + [op.REPORT_SAVED_POS, 1], + buildCall(functionIndex, 1, env, context.sp + 2) + ), + [] + ), + buildCondition([op.IF_ERROR], [op.NIP_CURR_POS], [op.NIP]) + ) + : expressionCode; + }, + + sequence: function(node, context) { + var emptyArrayIndex, nullIndex; + + function buildElementsCode(elements, context) { + var processedCount, functionIndex; + + if (elements.length > 0) { + processedCount = node.elements.length - elements.slice(1).length; + + return buildSequence( + generate(elements[0], context), + buildCondition( + [op.IF_NOT_ERROR], + buildElementsCode(elements.slice(1), { + sp: context.sp + 1, + env: context.env, + action: context.action + }), + buildSequence( + processedCount > 1 ? [op.POP_N, processedCount] : [op.POP], + [op.POP_CURR_POS], + [op.PUSH, nullIndex] + ) + ) + ); + } else { + if (context.action) { + functionIndex = addFunctionConst( + utils.keys(context.env), + context.action.code + ); + + return buildSequence( + [op.REPORT_SAVED_POS, node.elements.length], + buildCall( + functionIndex, + node.elements.length, + context.env, + context.sp + ), + buildCondition([op.IF_ERROR], [op.NIP_CURR_POS], [op.NIP]) + ); + } else { + return buildSequence([op.WRAP, node.elements.length], [op.NIP]); + } + } + } + + if (node.elements.length > 0) { + nullIndex = addConst('null'); + + 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]; + } + }, + + labeled: function(node, context) { + context.env[node.label] = context.sp + 1; + + return generate(node.expression, { + sp: context.sp, + env: { }, + action: null + }); + }, + + text: function(node, context) { + return buildSequence( + [op.PUSH_CURR_POS], + generate(node.expression, { + sp: context.sp + 1, + env: { }, + action: null + }), + buildCondition([op.IF_NOT_ERROR], [op.TEXT], []), + [op.NIP] + ); + }, + + simple_and: function(node, context) { + return buildSimplePredicate(node.expression, false, context); + }, + + simple_not: function(node, context) { + return buildSimplePredicate(node.expression, true, context); + }, + + 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 emptyStringIndex = addConst('""'); + + return buildSequence( + generate(node.expression, { + sp: context.sp, + env: { }, + action: null + }), + buildCondition( + [op.IF_ERROR], + buildSequence([op.POP], [op.PUSH, emptyStringIndex]), + [] + ) + ); + }, + + zero_or_more: function(node, context) { + var emptyArrayIndex = addConst('[]'); + expressionCode = generate(node.expression, { + sp: context.sp + 1, + env: { }, + action: null + }); + + return buildSequence( + [op.PUSH, emptyArrayIndex], + expressionCode, + buildAppendLoop(expressionCode), + [op.POP] + ); + }, + + one_or_more: function(node, context) { + var emptyArrayIndex = addConst('[]'); + nullIndex = addConst('null'); + expressionCode = generate(node.expression, { + sp: context.sp + 1, + env: { }, + action: null + }); + + return buildSequence( + [op.PUSH, emptyArrayIndex], + expressionCode, + buildCondition( + [op.IF_NOT_ERROR], + buildSequence(buildAppendLoop(expressionCode), [op.POP]), + buildSequence([op.POP], [op.POP], [op.PUSH, nullIndex]) + ) + ); + }, + + rule_ref: function(node) { + return [op.RULE, utils.indexOfRuleByName(ast, node.name)]; + }, + + literal: function(node) { + var stringIndex, expectedIndex; + + if (node.value.length > 0) { + stringIndex = addConst(node.ignoreCase + ? utils.quote(node.value.toLowerCase()) + : utils.quote(node.value) + ); + expectedIndex = addConst(utils.quote(utils.quote(node.value))); + + /* + * For case-sensitive strings the value must match the beginning of the + * remaining input exactly. As a result, we can use |ACCEPT_STRING| and + * save one |substr| call that would be needed if we used |ACCEPT_N|. + */ + return buildCondition( + node.ignoreCase + ? [op.MATCH_STRING_IC, stringIndex] + : [op.MATCH_STRING, stringIndex], + node.ignoreCase + ? [op.ACCEPT_N, node.value.length] + : [op.ACCEPT_STRING, stringIndex], + [op.FAIL, expectedIndex] + ); + } else { + stringIndex = addConst('""'); + + return [op.PUSH, stringIndex]; + } + }, + + "class": function(node) { + var regexp, regexpIndex, expectedIndex; + + if (node.parts.length > 0) { + regexp = '/^[' + + (node.inverted ? '^' : '') + + utils.map(node.parts, function(part) { + return part instanceof Array + ? utils.quoteForRegexpClass(part[0]) + + '-' + + utils.quoteForRegexpClass(part[1]) + : utils.quoteForRegexpClass(part); + }).join('') + + ']/' + (node.ignoreCase ? 'i' : ''); + } else { + /* + * IE considers regexps /[]/ and /[^]/ as syntactically invalid, so we + * translate them into euqivalents it can handle. + */ + regexp = node.inverted ? '/^[\\S\\s]/' : '/^(?!)/'; + } + + regexpIndex = addConst(regexp); + expectedIndex = addConst(utils.quote(node.rawText)); + + return buildCondition( + [op.MATCH_REGEXP, regexpIndex], + [op.ACCEPT_N, 1], + [op.FAIL, expectedIndex] + ); + }, + + any: function(node) { + var expectedIndex = addConst(utils.quote("any character")); + + return buildCondition( + [op.MATCH_ANY], + [op.ACCEPT_N, 1], + [op.FAIL, expectedIndex] + ); + } + }); + + generate(ast); +}; diff --git a/lib/compiler/passes/generate-code.js b/lib/compiler/passes/generate-code.js deleted file mode 100644 index a858570..0000000 --- a/lib/compiler/passes/generate-code.js +++ /dev/null @@ -1,867 +0,0 @@ -var utils = require("../../utils"); - -/* Generates the parser code. */ -module.exports = function(ast, options) { - options = utils.clone(options); - utils.defaults(options, { - cache: false, - allowedStartRules: [ast.startRule] - }); - - /* - * Codie 1.1.0 - * - * https://github.com/dmajda/codie - * - * Copyright (c) 2011-2012 David Majda - * Licensend under the MIT license. - */ - var Codie = (function(undefined) { - - function stringEscape(s) { - function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } - - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a - * string literal except for the closing quote character, backslash, - * carriage return, line separator, paragraph separator, and line feed. - * Any character may appear in the form of an escape sequence. - * - * For portability, we also escape escape all control and non-ASCII - * characters. Note that "\0" and "\v" escape sequences are not used - * because JSHint does not like the first and IE the second. - */ - 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(/[\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); }); - } - - function push(s) { return '__p.push(' + s + ');'; } - - function pushRaw(template, length, state) { - function unindent(code, level, unindentFirst) { - return code.replace( - new RegExp('^.{' + level +'}', "gm"), - function(str, offset) { - if (offset === 0) { - return unindentFirst ? '' : str; - } else { - return ""; - } - } - ); - } - - var escaped = stringEscape(unindent( - template.substring(0, length), - state.indentLevel(), - state.atBOL - )); - - return escaped.length > 0 ? push('"' + escaped + '"') : ''; - } - - - var Codie = { - /* Codie version (uses semantic versioning). */ - VERSION: "1.1.0", - - /* - * Specifies by how many characters do #if/#else and #for unindent their - * content in the generated code. - */ - indentStep: 2, - - /* Description of #-commands. Extend to define your own commands. */ - commands: { - "if": { - params: /^(.*)$/, - compile: function(state, prefix, params) { - return ['if(' + params[0] + '){', []]; - }, - stackOp: "push" - }, - "else": { - params: /^$/, - compile: function(state) { - var stack = state.commandStack, - insideElse = stack[stack.length - 1] === "else", - insideIf = stack[stack.length - 1] === "if"; - - if (insideElse) { throw new Error("Multiple #elses."); } - if (!insideIf) { throw new Error("Using #else outside of #if."); } - - return ['}else{', []]; - }, - stackOp: "replace" - }, - "for": { - params: /^([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+in[ \t]+(.*)$/, - init: function(state) { - state.forCurrLevel = 0; // current level of #for loop nesting - state.forMaxLevel = 0; // maximum level of #for loop nesting - }, - compile: function(state, prefix, params) { - var c = '__c' + state.forCurrLevel, // __c for "collection" - l = '__l' + state.forCurrLevel, // __l for "length" - i = '__i' + state.forCurrLevel; // __i for "index" - - state.forCurrLevel++; - if (state.forMaxLevel < state.forCurrLevel) { - state.forMaxLevel = state.forCurrLevel; - } - - return [ - c + '=' + params[1] + ';' - + l + '=' + c + '.length;' - + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' - + params[0] + '=' + c + '[' + i + '];', - [params[0], c, l, i] - ]; - }, - exit: function(state) { state.forCurrLevel--; }, - stackOp: "push" - }, - "end": { - params: /^$/, - compile: function(state) { - var stack = state.commandStack, exit; - - if (stack.length === 0) { throw new Error("Too many #ends."); } - - exit = Codie.commands[stack[stack.length - 1]].exit; - if (exit) { exit(state); } - - return ['}', []]; - }, - stackOp: "pop" - }, - "block": { - params: /^(.*)$/, - compile: function(state, prefix, params) { - var x = '__x', // __x for "prefix", - n = '__n', // __n for "lines" - l = '__l', // __l for "length" - i = '__i'; // __i for "index" - - /* - * Originally, the generated code used |String.prototype.replace|, but - * it is buggy in certain versions of V8 so it was rewritten. See the - * tests for details. - */ - return [ - x + '="' + stringEscape(prefix.substring(state.indentLevel())) + '";' - + n + '=(' + params[0] + ').toString().split("\\n");' - + l + '=' + n + '.length;' - + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' - + n + '[' + i +']=' + x + '+' + n + '[' + i + ']+"\\n";' - + '}' - + push(n + '.join("")'), - [x, n, l, i] - ]; - }, - stackOp: "nop" - } - }, - - /* - * Compiles a template into a function. When called, this function will - * execute the template in the context of an object passed in a parameter and - * return the result. - */ - template: function(template) { - var stackOps = { - push: function(stack, name) { stack.push(name); }, - replace: function(stack, name) { stack[stack.length - 1] = name; }, - pop: function(stack) { stack.pop(); }, - nop: function() { } - }; - - function compileExpr(state, expr) { - state.atBOL = false; - return [push(expr), []]; - } - - function compileCommand(state, prefix, name, params) { - var command, match, result; - - command = Codie.commands[name]; - if (!command) { throw new Error("Unknown command: #" + name + "."); } - - match = command.params.exec(params); - if (match === null) { - throw new Error( - "Invalid params for command #" + name + ": " + params + "." - ); - } - - result = command.compile(state, prefix, match.slice(1)); - stackOps[command.stackOp](state.commandStack, name); - state.atBOL = true; - return result; - } - - var state = { // compilation state - commandStack: [], // stack of commands as they were nested - atBOL: true, // is the next character to process at BOL? - indentLevel: function() { - return Codie.indentStep * this.commandStack.length; - } - }, - code = '', // generated template function code - vars = ['__p=[]'], // variables used by generated code - name, match, result, i; - - /* Initialize state. */ - for (name in Codie.commands) { - if (Codie.commands[name].init) { Codie.commands[name].init(state); } - } - - /* Compile the template. */ - while ((match = /^([ \t]*)#([a-zA-Z_][a-zA-Z0-9_]*)(?:[ \t]+([^ \t\n][^\n]*))?[ \t]*(?:\n|$)|#\{([^}]*)\}/m.exec(template)) !== null) { - code += pushRaw(template, match.index, state); - result = match[2] !== undefined && match[2] !== "" - ? compileCommand(state, match[1], match[2], match[3] || "") // #-command - : compileExpr(state, match[4]); // #{...} - code += result[0]; - vars = vars.concat(result[1]); - template = template.substring(match.index + match[0].length); - } - code += pushRaw(template, template.length, state); - - /* Check the final state. */ - if (state.commandStack.length > 0) { throw new Error("Missing #end."); } - - /* Sanitize the list of variables used by commands. */ - vars.sort(); - for (i = 0; i < vars.length; i++) { - if (vars[i] === vars[i - 1]) { vars.splice(i--, 1); } - } - - /* Create the resulting function. */ - return new Function("__v", [ - '__v=__v||{};', - 'var ' + vars.join(',') + ';', - 'with(__v){', - code, - 'return __p.join("").replace(/^\\n+|\\n+$/g,"");};' - ].join('')); - } - }; - - return Codie; - - })(); - - var templates = (function() { - var name, - templates = {}, - sources = { - grammar: [ - '(function(){', - ' /*', - ' * Generated by PEG.js 0.7.0.', - ' *', - ' * http://pegjs.majda.cz/', - ' */', - ' ', - /* This needs to be in sync with |subclass| in utils.js. */ - ' function subclass(child, parent) {', - ' function ctor() { this.constructor = child; }', - ' ctor.prototype = parent.prototype;', - ' child.prototype = new ctor();', - ' }', - ' ', - /* This needs to be in sync with |quote| in utils.js. */ - ' function quote(s) {', - ' /*', - ' * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a', - ' * string literal except for the closing quote character, backslash,', - ' * carriage return, line separator, paragraph separator, and line feed.', - ' * Any character may appear in the form of an escape sequence.', - ' *', - ' * For portability, we also escape escape all control and non-ASCII', - ' * characters. Note that "\\0" and "\\v" escape sequences are not used', - ' * because JSHint does not like the first and IE the second.', - ' */', - ' return \'"\' + s', - ' .replace(/\\\\/g, \'\\\\\\\\\') // backslash', - ' .replace(/"/g, \'\\\\"\') // closing quote character', - ' .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-\\x1F\\x80-\\uFFFF]/g, escape)', - ' + \'"\';', - ' }', - ' ', - ' var result = {', - ' /*', - ' * Parses the input with a generated parser. If the parsing is successful,', - ' * returns a value explicitly or implicitly specified by the grammar from', - ' * which the parser was generated (see |PEG.buildParser|). If the parsing is', - ' * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.', - ' */', - ' parse: function(input) {', - ' var parseFunctions = {', - ' #for rule in options.allowedStartRules', - ' #{string(rule) + ": parse_" + rule + (rule !== options.allowedStartRules[options.allowedStartRules.length - 1] ? "," : "")}', - ' #end', - ' };', - ' ', - ' var options = arguments.length > 1 ? arguments[1] : {},', - ' startRule;', - ' ', - ' if (options.startRule !== undefined) {', - ' startRule = options.startRule;', - ' ', - ' if (parseFunctions[startRule] === undefined) {', - ' throw new Error("Can\'t start parsing from rule " + quote(startRule) + ".");', - ' }', - ' } else {', - ' startRule = #{string(options.allowedStartRules[0])};', - ' }', - ' ', - ' var pos = 0;', - ' var reportedPos = 0;', - ' var cachedReportedPos = 0;', - ' var cachedReportedPosDetails = { line: 1, column: 1, seenCR: false };', - ' var reportFailures = 0;', // 0 = report, anything > 0 = do not report - ' var rightmostFailuresPos = 0;', - ' var rightmostFailuresExpected = [];', - ' #if options.cache', - ' var cache = {};', - ' #end', - ' ', - /* This needs to be in sync with |padLeft| in utils.js. */ - ' function padLeft(input, padding, length) {', - ' var result = input;', - ' ', - ' var padLength = length - input.length;', - ' for (var i = 0; i < padLength; i++) {', - ' result = padding + result;', - ' }', - ' ', - ' return result;', - ' }', - ' ', - /* This needs to be in sync with |escape| in utils.js. */ - ' function escape(ch) {', - ' var charCode = ch.charCodeAt(0);', - ' var escapeChar;', - ' var length;', - ' ', - ' if (charCode <= 0xFF) {', - ' escapeChar = \'x\';', - ' length = 2;', - ' } else {', - ' escapeChar = \'u\';', - ' length = 4;', - ' }', - ' ', - ' return \'\\\\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), \'0\', length);', - ' }', - ' ', - ' function computeReportedPosDetails() {', - ' function advanceCachedReportedPos() {', - ' var ch;', - ' ', - ' for (; cachedReportedPos < reportedPos; cachedReportedPos++) {', - ' ch = input.charAt(cachedReportedPos);', - ' if (ch === "\\n") {', - ' if (!cachedReportedPosDetails.seenCR) { cachedReportedPosDetails.line++; }', - ' cachedReportedPosDetails.column = 1;', - ' cachedReportedPosDetails.seenCR = false;', - ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', - ' cachedReportedPosDetails.line++;', - ' cachedReportedPosDetails.column = 1;', - ' cachedReportedPosDetails.seenCR = true;', - ' } else {', - ' cachedReportedPosDetails.column++;', - ' cachedReportedPosDetails.seenCR = false;', - ' }', - ' }', - ' }', - ' ', - ' if (cachedReportedPos !== reportedPos) {', - ' if (cachedReportedPos > reportedPos) {', - ' cachedReportedPos = 0;', - ' cachedReportedPosDetails = { line: 1, column: 1, seenCR: false };', - ' }', - ' advanceCachedReportedPos();', - ' }', - ' ', - ' return cachedReportedPosDetails;', - ' }', - ' ', - ' function text() {', - ' return input.substring(reportedPos, pos);', - ' }', - ' ', - ' function offset() {', - ' return reportedPos;', - ' }', - ' ', - ' function line() {', - ' return computeReportedPosDetails().line;', - ' }', - ' ', - ' function column() {', - ' return computeReportedPosDetails().column;', - ' }', - ' ', - ' function matchFailed(failure) {', - ' if (pos < rightmostFailuresPos) {', - ' return;', - ' }', - ' ', - ' if (pos > rightmostFailuresPos) {', - ' rightmostFailuresPos = pos;', - ' rightmostFailuresExpected = [];', - ' }', - ' ', - ' rightmostFailuresExpected.push(failure);', - ' }', - ' ', - ' #for rule in node.rules', - ' #block emit(rule)', - ' ', - ' #end', - ' ', - ' function cleanupExpected(expected) {', - ' expected.sort();', - ' ', - ' var lastExpected = null;', - ' var cleanExpected = [];', - ' for (var i = 0; i < expected.length; i++) {', - ' if (expected[i] !== lastExpected) {', - ' cleanExpected.push(expected[i]);', - ' lastExpected = expected[i];', - ' }', - ' }', - ' return cleanExpected;', - ' }', - ' ', - ' #if node.initializer', - ' #block emit(node.initializer)', - ' #end', - ' ', - ' var result = parseFunctions[startRule]();', - ' ', - ' /*', - ' * The parser is now in one of the following three states:', - ' *', - ' * 1. The parser successfully parsed the whole input.', - ' *', - ' * - |result !== null|', - ' * - |pos === input.length|', - ' * - |rightmostFailuresExpected| may or may not contain something', - ' *', - ' * 2. The parser successfully parsed only a part of the input.', - ' *', - ' * - |result !== null|', - ' * - |pos < input.length|', - ' * - |rightmostFailuresExpected| may or may not contain something', - ' *', - ' * 3. The parser did not successfully parse any part of the input.', - ' *', - ' * - |result === null|', - ' * - |pos === 0|', - ' * - |rightmostFailuresExpected| contains at least one failure', - ' *', - ' * All code following this comment (including called functions) must', - ' * handle these states.', - ' */', - ' if (result === null || pos !== input.length) {', - ' reportedPos = Math.max(pos, rightmostFailuresPos);', - ' var found = reportedPos < input.length ? input.charAt(reportedPos) : null;', - ' var reportedPosDetails = computeReportedPosDetails();', - ' ', - ' throw new this.SyntaxError(', - ' cleanupExpected(rightmostFailuresExpected),', - ' found,', - ' reportedPos,', - ' reportedPosDetails.line,', - ' reportedPosDetails.column', - ' );', - ' }', - ' ', - ' return result;', - ' }', - ' };', - ' ', - ' /* Thrown when a parser encounters a syntax error. */', - ' ', - ' result.SyntaxError = function(expected, found, offset, line, column) {', - ' function buildMessage(expected, found) {', - ' var expectedHumanized, foundHumanized;', - ' ', - ' switch (expected.length) {', - ' case 0:', - ' expectedHumanized = "end of input";', - ' break;', - ' case 1:', - ' expectedHumanized = expected[0];', - ' break;', - ' default:', - ' expectedHumanized = expected.slice(0, expected.length - 1).join(", ")', - ' + " or "', - ' + expected[expected.length - 1];', - ' }', - ' ', - ' foundHumanized = found ? quote(found) : "end of input";', - ' ', - ' return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";', - ' }', - ' ', - ' this.name = "SyntaxError";', - ' this.expected = expected;', - ' this.found = found;', - ' this.message = buildMessage(expected, found);', - ' this.offset = offset;', - ' this.line = line;', - ' this.column = column;', - ' };', - ' ', - ' subclass(result.SyntaxError, Error);', - ' ', - ' return result;', - '})()' - ], - rule: [ - 'function parse_#{node.name}() {', - ' #if options.cache', - ' var cacheKey = "#{node.name}@" + pos;', - ' var cachedResult = cache[cacheKey];', - ' if (cachedResult) {', - ' pos = cachedResult.nextPos;', - ' return cachedResult.result;', - ' }', - ' ', - ' #end', - ' #if node.registerCount > 0', - ' var #{map(range(node.registerCount), r).join(", ")};', - ' #end', - ' ', - ' #block emit(node.expression)', - ' #if options.cache', - ' ', - ' cache[cacheKey] = {', - ' nextPos: pos,', - ' result: #{r(node.expression.resultIndex)}', - ' };', - ' #end', - ' return #{r(node.expression.resultIndex)};', - '}' - ], - named: [ - 'reportFailures++;', - '#block emit(node.expression)', - 'reportFailures--;', - 'if (reportFailures === 0 && #{r(node.resultIndex)} === null) {', - ' matchFailed(#{string(node.name)});', - '}' - ], - choice: [ - '#block emit(alternative)', - '#block nextAlternativesCode' - ], - "choice.next": [ - 'if (#{r(node.resultIndex)} === null) {', - ' #block code', - '}' - ], - action: [ - '#{r(node.posIndex)} = pos;', - '#block emit(node.expression)', - 'if (#{r(node.resultIndex)} !== null) {', - ' reportedPos = #{r(node.posIndex)};', - ' #{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")});', - '}', - 'if (#{r(node.resultIndex)} === null) {', - ' pos = #{r(node.posIndex)};', - '}' - ], - sequence: [ - '#{r(node.posIndex)} = pos;', - '#block code' - ], - "sequence.iteration": [ - '#block emit(element)', - 'if (#{r(element.resultIndex)} !== null) {', - ' #block code', - '} else {', - ' #{r(node.resultIndex)} = null;', - ' pos = #{r(node.posIndex)};', - '}' - ], - "sequence.inner": [ - '#{r(node.resultIndex)} = [#{map(pluck(node.elements, "resultIndex"), r).join(", ")}];' - ], - text: [ - '#{r(node.posIndex)} = pos;', - '#block emit(node.expression)', - 'if (#{r(node.resultIndex)} !== null) {', - ' #{r(node.resultIndex)} = input.substring(pos, #{r(node.posIndex)});', - '}' - ], - simple_and: [ - '#{r(node.posIndex)} = pos;', - 'reportFailures++;', - '#block emit(node.expression)', - 'reportFailures--;', - 'if (#{r(node.resultIndex)} !== null) {', - ' #{r(node.resultIndex)} = "";', - ' pos = #{r(node.posIndex)};', - '} else {', - ' #{r(node.resultIndex)} = null;', - '}' - ], - simple_not: [ - '#{r(node.posIndex)} = pos;', - 'reportFailures++;', - '#block emit(node.expression)', - 'reportFailures--;', - 'if (#{r(node.resultIndex)} === null) {', - ' #{r(node.resultIndex)} = "";', - '} else {', - ' #{r(node.resultIndex)} = null;', - ' pos = #{r(node.posIndex)};', - '}' - ], - semantic_and: [ - 'reportedPos = pos;', - '#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? "" : null;' - ], - semantic_not: [ - 'reportedPos = pos;', - '#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? null : "";' - ], - optional: [ - '#block emit(node.expression)', - '#{r(node.resultIndex)} = #{r(node.resultIndex)} !== null ? #{r(node.resultIndex)} : "";' - ], - zero_or_more: [ - '#{r(node.resultIndex)} = [];', - '#block emit(node.expression)', - 'while (#{r(node.expression.resultIndex)} !== null) {', - ' #{r(node.resultIndex)}.push(#{r(node.expression.resultIndex)});', - ' #block emit(node.expression)', - '}' - ], - one_or_more: [ - '#block emit(node.expression)', - 'if (#{r(node.expression.resultIndex)} !== null) {', - ' #{r(node.resultIndex)} = [];', - ' while (#{r(node.expression.resultIndex)} !== null) {', - ' #{r(node.resultIndex)}.push(#{r(node.expression.resultIndex)});', - ' #block emit(node.expression)', - ' }', - '} else {', - ' #{r(node.resultIndex)} = null;', - '}' - ], - rule_ref: [ - '#{r(node.resultIndex)} = parse_#{node.name}();' - ], - literal: [ - '#if node.value.length === 0', - ' #{r(node.resultIndex)} = "";', - '#else', - ' #if !node.ignoreCase', - ' #if node.value.length === 1', - ' if (input.charCodeAt(pos) === #{node.value.charCodeAt(0)}) {', - ' #else', - ' if (input.substr(pos, #{node.value.length}) === #{string(node.value)}) {', - ' #end', - ' #else', - /* - * One-char literals are not optimized when case-insensitive - * matching is enabled. This is because there is no simple way to - * lowercase a character code that works for character outside ASCII - * letters. Moreover, |toLowerCase| can change string length, - * meaning the result of lowercasing a character can be more - * characters. - */ - ' if (input.substr(pos, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {', - ' #end', - ' #if !node.ignoreCase', - ' #{r(node.resultIndex)} = #{string(node.value)};', - ' #else', - ' #{r(node.resultIndex)} = input.substr(pos, #{node.value.length});', - ' #end', - ' #{node.value.length > 1 ? "pos += " + node.value.length : "pos++"};', - ' } else {', - ' #{r(node.resultIndex)} = null;', - ' if (reportFailures === 0) {', - ' matchFailed(#{string(string(node.value))});', - ' }', - ' }', - '#end' - ], - "class": [ - 'if (#{regexp}.test(input.charAt(pos))) {', - ' #{r(node.resultIndex)} = input.charAt(pos);', - ' pos++;', - '} else {', - ' #{r(node.resultIndex)} = null;', - ' if (reportFailures === 0) {', - ' matchFailed(#{string(node.rawText)});', - ' }', - '}' - ], - any: [ - 'if (input.length > pos) {', - ' #{r(node.resultIndex)} = input.charAt(pos);', - ' pos++;', - '} else {', - ' #{r(node.resultIndex)} = null;', - ' if (reportFailures === 0) {', - ' matchFailed("any character");', - ' }', - '}' - ] - }; - - for (name in sources) { - templates[name] = Codie.template(sources[name].join('\n')); - } - - return templates; - })(); - - function fill(name, vars) { - vars.string = utils.quote; - vars.range = utils.range; - vars.map = utils.map; - vars.pluck = utils.pluck; - vars.keys = utils.keys; - vars.values = utils.values; - vars.emit = emit; - vars.options = options; - - vars.r = function(index) { return "r" + index; }; - - return templates[name](vars); - } - - function emitSimple(name) { - return function(node) { return fill(name, { node: node }); }; - } - - var emit = utils.buildNodeVisitor({ - grammar: emitSimple("grammar"), - - initializer: function(node) { return node.code; }, - - rule: emitSimple("rule"), - - /* - * The contract for all code fragments generated by the following functions - * is as follows. - * - * The code fragment tries to match a part of the input starting with the - * position indicated in |pos|. That position may point past the end of the - * input. - * - * * If the code fragment matches the input, it advances |pos| to point to - * the first chracter following the matched part of the input and sets a - * register with index specified by |node.resultIndex| to an appropriate - * value. This value is always non-|null|. - * - * * If the code fragment does not match the input, it returns with |pos| - * set to the original value and it sets a register with index specified - * by |node.posIndex| to |null|. - * - * The code uses only registers with indices specified by |node.resultIndex| - * and |node.posIndex| where |node| is the processed node or some of its - * subnodes. It does not use any other registers. - */ - - named: emitSimple("named"), - - choice: function(node) { - var code, nextAlternativesCode; - - for (var i = node.alternatives.length - 1; i >= 0; i--) { - nextAlternativesCode = i !== node.alternatives.length - 1 - ? fill("choice.next", { node: node, code: code }) - : ''; - code = fill("choice", { - alternative: node.alternatives[i], - nextAlternativesCode: nextAlternativesCode - }); - } - - return code; - }, - - action: emitSimple("action"), - - sequence: function(node) { - var code = fill("sequence.inner", { node: node }); - - for (var i = node.elements.length - 1; i >= 0; i--) { - code = fill("sequence.iteration", { - node: node, - element: node.elements[i], - code: code - }); - } - - return fill("sequence", { node: node, code: code }); - }, - - labeled: function(node) { return emit(node.expression); }, - - text: emitSimple("text"), - simple_and: emitSimple("simple_and"), - simple_not: emitSimple("simple_not"), - semantic_and: emitSimple("semantic_and"), - semantic_not: emitSimple("semantic_not"), - optional: emitSimple("optional"), - zero_or_more: emitSimple("zero_or_more"), - one_or_more: emitSimple("one_or_more"), - rule_ref: emitSimple("rule_ref"), - literal: emitSimple("literal"), - - "class": function(node) { - var regexp; - - if (node.parts.length > 0) { - regexp = '/^[' - + (node.inverted ? '^' : '') - + utils.map(node.parts, function(part) { - return part instanceof Array - ? utils.quoteForRegexpClass(part[0]) - + '-' - + utils.quoteForRegexpClass(part[1]) - : utils.quoteForRegexpClass(part); - }).join('') - + ']/' + (node.ignoreCase ? 'i' : ''); - } else { - /* - * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so - * we translate them into euqivalents it can handle. - */ - regexp = node.inverted ? '/^[\\S\\s]/' : '/^(?!)/'; - } - - return fill("class", { node: node, regexp: regexp }); - }, - - any: emitSimple("any") - }); - - ast.code = emit(ast); -}; diff --git a/lib/compiler/passes/generate-javascript.js b/lib/compiler/passes/generate-javascript.js new file mode 100644 index 0000000..719c93b --- /dev/null +++ b/lib/compiler/passes/generate-javascript.js @@ -0,0 +1,950 @@ +var utils = require("../../utils"), + op = require("../opcodes"); + +/* Generates parser JavaScript code. */ +module.exports = function(ast, options) { + options = utils.clone(options); + utils.defaults(options, { + cache: false, + allowedStartRules: [ast.startRule], + optimize: "speed" + }); + + /* These only indent non-empty lines to avoid trailing whitespace. */ + function indent2(code) { return code.replace(/^(.+)$/gm, ' $1'); } + function indent4(code) { return code.replace(/^(.+)$/gm, ' $1'); } + function indent8(code) { return code.replace(/^(.+)$/gm, ' $1'); } + function indent10(code) { return code.replace(/^(.+)$/gm, ' $1'); } + + function generateTables() { + if (options.optimize === "size") { + return [ + 'peg$consts = [', + indent2(ast.consts.join(',\n')), + '],', + '', + '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')), + '],' + ].join('\n'); + } else { + return utils.map( + ast.consts, + function(c, i) { return 'peg$c' + i + ' = ' + c + ','; } + ).join('\n'); + } + } + + 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 generateCacheFooter(resultCode) { + return [ + '', + 'peg$cache[key] = { nextPos: peg$currPos, result: ' + resultCode + ' };' + ].join('\n'); + } + + function generateInterpreter() { + var parts = []; + + function generateCondition(cond, argsLength) { + var baseLength = argsLength + 3, + thenLengthCode = 'bc[ip + ' + (baseLength - 2) + ']', + elseLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; + + return [ + 'ends.push(end);', + 'ips.push(ip + ' + baseLength + ' + ' + thenLengthCode + ' + ' + elseLengthCode + ');', + '', + 'if (' + cond + ') {', + ' end = ip + ' + baseLength + ' + ' + thenLengthCode + ';', + ' ip += ' + baseLength + ';', + '} else {', + ' end = ip + ' + baseLength + ' + ' + thenLengthCode + ' + ' + elseLengthCode + ';', + ' ip += ' + baseLength + ' + ' + thenLengthCode + ';', + '}', + '', + 'break;' + ].join('\n'); + } + + function generateLoop(cond) { + var baseLength = 2, + bodyLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; + + return [ + 'if (' + cond + ') {', + ' ends.push(end);', + ' ips.push(ip);', + '', + ' end = ip + ' + baseLength + ' + ' + bodyLengthCode + ';', + ' ip += ' + baseLength + ';', + '} else {', + ' ip += ' + baseLength + ' + ' + bodyLengthCode + ';', + '}', + '', + 'break;' + ].join('\n'); + } + + function generateCall() { + var baseLength = 4, + paramsLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; + + return [ + 'params = bc.slice(ip + ' + baseLength + ', ip + ' + baseLength + ' + ' + paramsLengthCode + ');', + 'for (i = 0; i < ' + paramsLengthCode + '; i++) {', + ' params[i] = stack[stack.length - 1 - params[i]];', + '}', + '', + 'stack.splice(', + ' stack.length - bc[ip + 2],', + ' bc[ip + 2],', + ' peg$consts[bc[ip + 1]].apply(null, params)', + ');', + '', + 'ip += ' + baseLength + ' + ' + paramsLengthCode + ';', + 'break;' + ].join('\n'); + } + + parts.push([ + 'function peg$decode(s) {', + ' var bc = new Array(s.length), i;', + '', + ' for (i = 0; i < s.length; i++) {', + ' bc[i] = s.charCodeAt(i) - 32;', + ' }', + '', + ' return bc;', + '}', + '', + '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'))); + } + + parts.push([ + ' function protect(object) {', + ' return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;', + ' }', + '', + /* + * The point of the outer loop and the |ips| & |ends| stacks is to avoid + * recursive calls for interpreting parts of bytecode. In other words, we + * implement the |interpret| operation of the abstract machine without + * function calls. Such calls would likely slow the parser down and more + * importantly cause stack overflows for complex grammars. + */ + ' while (true) {', + ' 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 cloned + * because it can be modified later on the stack by |APPEND|. + */ + ' stack.push(protect(peg$consts[bc[ip + 1]]));', + ' ip += 2;', + ' break;', + '', + ' case ' + op.PUSH_CURR_POS + ':', // PUSH_CURR_POS + ' stack.push(peg$currPos);', + ' ip++;', + ' break;', + '', + ' case ' + op.POP + ':', // POP + ' stack.pop();', + ' ip++;', + ' break;', + '', + ' case ' + op.POP_CURR_POS + ':', // POP_CURR_POS + ' peg$currPos = stack.pop();', + ' ip++;', + ' break;', + '', + ' case ' + op.POP_N + ':', // POP_N n + ' stack.length -= bc[ip + 1];', + ' ip += 2;', + ' break;', + '', + ' case ' + op.NIP + ':', // NIP + ' stack.splice(-2, 1);', + ' ip++;', + ' break;', + '', + ' case ' + op.NIP_CURR_POS + ':', // NIP_CURR_POS + ' peg$currPos = stack.splice(-2, 1)[0];', + ' ip++;', + ' break;', + '', + ' case ' + op.APPEND + ':', // APPEND + ' stack[stack.length - 2].push(stack.pop());', + ' ip++;', + ' break;', + '', + ' case ' + op.WRAP + ':', // WRAP n + ' stack.push(stack.splice(stack.length - bc[ip + 1]));', + ' ip += 2;', + ' break;', + '', + ' case ' + op.TEXT + ':', // TEXT + ' stack.pop();', + ' stack.push(input.substring(stack[stack.length - 1], peg$currPos));', + ' ip++;', + ' break;', + '', + ' case ' + op.IF + ':', // IF t, f + indent10(generateCondition('stack[stack.length - 1]', 0)), + '', + ' case ' + op.IF_ERROR + ':', // IF_ERROR t, f + indent10(generateCondition( + 'stack[stack.length - 1] === null', + 0 + )), + '', + ' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f + indent10( + generateCondition('stack[stack.length - 1] !== null', + 0 + )), + '', + ' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b + indent10(generateLoop('stack[stack.length - 1] !== null')), + '', + ' case ' + op.MATCH_ANY + ':', // MATCH_ANY a, f, ... + indent10(generateCondition('input.length > peg$currPos', 0)), + '', + ' case ' + op.MATCH_STRING + ':', // MATCH_STRING s, a, f, ... + indent10(generateCondition( + 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]', + 1 + )), + '', + ' case ' + op.MATCH_STRING_IC + ':', // MATCH_STRING_IC s, a, f, ... + indent10(generateCondition( + 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]', + 1 + )), + '', + ' case ' + op.MATCH_REGEXP + ':', // MATCH_REGEXP r, a, f, ... + indent10(generateCondition( + 'peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))', + 1 + )), + '', + ' case ' + op.ACCEPT_N + ':', // ACCEPT_N n + ' stack.push(input.substr(peg$currPos, bc[ip + 1]));', + ' peg$currPos += bc[ip + 1];', + ' ip += 2;', + ' break;', + '', + ' case ' + op.ACCEPT_STRING + ':', // ACCEPT_STRING s + ' stack.push(peg$consts[bc[ip + 1]]);', + ' peg$currPos += peg$consts[bc[ip + 1]].length;', + ' ip += 2;', + ' break;', + '', + ' case ' + op.FAIL + ':', // FAIL e + ' stack.push(null);', + ' if (peg$silentFails === 0) {', + ' peg$fail(peg$consts[bc[ip + 1]]);', + ' }', + ' ip += 2;', + ' break;', + '', + ' case ' + op.REPORT_SAVED_POS + ':', // REPORT_SAVED_POS p + ' peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];', + ' ip += 2;', + ' break;', + '', + ' case ' + op.REPORT_CURR_POS + ':', // REPORT_CURR_POS + ' peg$reportedPos = peg$currPos;', + ' ip++;', + ' break;', + '', + ' case ' + op.CALL + ':', // CALL f, n, pc, p1, p2, ..., pN + indent10(generateCall()), + '', + ' case ' + op.RULE + ':', // RULE r + ' stack.push(peg$parseRule(bc[ip + 1]));', + ' ip += 2;', + ' break;', + '', + ' case ' + op.SILENT_FAILS_ON + ':', // SILENT_FAILS_ON + ' peg$silentFails++;', + ' ip++;', + ' break;', + '', + ' case ' + op.SILENT_FAILS_OFF + ':', // SILENT_FAILS_OFF + ' peg$silentFails--;', + ' ip++;', + ' break;', + '', + ' default:', + ' throw new Error("Invalid opcode: " + bc[ip] + ".");', + ' }', + ' }', + '', + ' if (ends.length > 0) {', + ' end = ends.pop();', + ' ip = ips.pop();', + ' } else {', + ' break;', + ' }', + ' }' + ].join('\n')); + + if (options.cache) { + parts.push(indent2(generateCacheFooter('stack[0]'))); + } + + parts.push([ + '', + ' return stack[0];', + '}' + ].join('\n')); + + return parts.join('\n'); + } + + function generateRuleFunction(rule) { + var parts = [], code; + + function c(i) { return "peg$c" + i; } // |consts[i]| of the abstract machine + function s(i) { return "s" + i; } // |stack[i]| of the abstract machine + + var stack = { + sp: -1, + maxSp: -1, + + push: function(exprCode) { + var code = s(++this.sp) + ' = ' + exprCode + ';'; + + if (this.sp > this.maxSp) { this.maxSp = this.sp; } + + return code; + }, + + pop: function() { + var n, values; + + if (arguments.length === 0) { + return s(this.sp--); + } else { + n = arguments[0]; + values = utils.map(utils.range(this.sp - n + 1, this.sp + 1), s); + this.sp -= n; + + return values; + } + }, + + top: function() { + return s(this.sp); + }, + + index: function(i) { + return s(this.sp - i); + } + }; + + function compile(bc) { + var ip = 0, + end = bc.length, + parts = [], + value; + + function compileCondition(cond, argCount) { + var baseLength = argCount + 3, + thenLength = bc[ip + baseLength - 2], + elseLength = bc[ip + baseLength - 1], + baseSp = stack.sp, + thenCode, elseCode; + + ip += baseLength; + thenCode = compile(bc.slice(ip, ip + thenLength)); + ip += thenLength; + if (elseLength > 0) { + stack.sp = baseSp; + elseCode = compile(bc.slice(ip, ip + elseLength)); + ip += elseLength; + } + + parts.push('if (' + cond + ') {'); + parts.push(indent2(thenCode)); + if (elseLength > 0) { + parts.push('} else {'); + parts.push(indent2(elseCode)); + } + parts.push('}'); + } + + function compileLoop(cond) { + var baseLength = 2, + bodyLength = bc[ip + baseLength - 1], + bodyCode; + + ip += baseLength; + bodyCode = compile(bc.slice(ip, ip + bodyLength)); + ip += bodyLength; + + parts.push('while (' + cond + ') {'); + parts.push(indent2(bodyCode)); + parts.push('}'); + } + + function compileCall(cond) { + var baseLength = 4, + paramsLength = bc[ip + baseLength - 1]; + + var value = c(bc[ip + 1]) + '(' + + utils.map( + bc.slice(ip + baseLength, ip + baseLength + paramsLength), + stackIndex + ) + + ')'; + stack.pop(bc[ip + 2]); + parts.push(stack.push(value)); + ip += baseLength + paramsLength; + } + + /* + * 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])) + ); + ip += 2; + break; + + case op.PUSH_CURR_POS: // PUSH_CURR_POS + parts.push(stack.push('peg$currPos')); + ip++; + break; + + case op.POP: // POP + stack.pop(); + ip++; + break; + + case op.POP_CURR_POS: // POP_CURR_POS + parts.push('peg$currPos = ' + stack.pop() + ';'); + ip++; + break; + + case op.POP_N: // POP_N n + stack.pop(bc[ip + 1]); + ip += 2; + break; + + case op.NIP: // NIP + value = stack.pop(); + stack.pop(); + parts.push(stack.push(value)); + ip++; + break; + + case op.NIP_CURR_POS: // NIP_CURR_POS + value = stack.pop(); + parts.push('peg$currPos = ' + stack.pop() + ';'); + parts.push(stack.push(value)); + ip++; + break; + + case op.APPEND: // APPEND + value = stack.pop(); + parts.push(stack.top() + '.push(' + value + ');'); + ip++; + break; + + case op.WRAP: // WRAP n + parts.push( + stack.push('[' + stack.pop(bc[ip + 1]).join(', ') + ']') + ); + ip += 2; + break; + + case op.TEXT: // TEXT + stack.pop(); + parts.push( + stack.push('input.substring(' + stack.top() + ', peg$currPos)') + ); + ip++; + break; + + case op.IF: // IF t, f + compileCondition(stack.top(), 0); + break; + + case op.IF_ERROR: // IF_ERROR t, f + compileCondition(stack.top() + ' === null', 0); + break; + + case op.IF_NOT_ERROR: // IF_NOT_ERROR t, f + compileCondition(stack.top() + ' !== null', 0); + break; + + case op.WHILE_NOT_ERROR: // WHILE_NOT_ERROR b + compileLoop(stack.top() + ' !== null', 0); + break; + + case op.MATCH_ANY: // MATCH_ANY a, f, ... + compileCondition('input.length > peg$currPos', 0); + break; + + case op.MATCH_STRING: // MATCH_STRING s, a, f, ... + compileCondition( + eval(ast.consts[bc[ip + 1]]).length > 1 + ? 'input.substr(peg$currPos, ' + + eval(ast.consts[bc[ip + 1]]).length + + ') === ' + + c(bc[ip + 1]) + : 'input.charCodeAt(peg$currPos) === ' + + eval(ast.consts[bc[ip + 1]]).charCodeAt(0), + 1 + ); + break; + + case op.MATCH_STRING_IC: // MATCH_STRING_IC s, a, f, ... + compileCondition( + 'input.substr(peg$currPos, ' + + ast.consts[bc[ip + 1]].length + + ').toLowerCase() === ' + + c(bc[ip + 1]), + 1 + ); + break; + + case op.MATCH_REGEXP: // MATCH_REGEXP r, a, f, ... + compileCondition( + c(bc[ip + 1]) + '.test(input.charAt(peg$currPos))', + 1 + ); + break; + + case op.ACCEPT_N: // ACCEPT_N n + parts.push(stack.push( + bc[ip + 1] > 1 + ? 'input.substr(peg$currPos, ' + bc[ip + 1] + ')' + : 'input.charAt(peg$currPos)' + )); + parts.push( + bc[ip + 1] > 1 + ? 'peg$currPos += ' + bc[ip + 1] + ';' + : 'peg$currPos++;' + ); + ip += 2; + break; + + case op.ACCEPT_STRING: // ACCEPT_STRING s + parts.push(stack.push(c(bc[ip + 1]))); + parts.push( + eval(ast.consts[bc[ip + 1]]).length > 1 + ? 'peg$currPos += ' + eval(ast.consts[bc[ip + 1]]).length + ';' + : 'peg$currPos++;' + ); + ip += 2; + break; + + case op.FAIL: // FAIL e + parts.push(stack.push('null')); + parts.push('if (peg$silentFails === 0) { peg$fail(' + c(bc[ip + 1]) + '); }'); + ip += 2; + break; + + case op.REPORT_SAVED_POS: // REPORT_SAVED_POS p + parts.push('peg$reportedPos = ' + stack.index(bc[ip + 1]) + ';'); + ip += 2; + break; + + case op.REPORT_CURR_POS: // REPORT_CURR_POS + parts.push('peg$reportedPos = peg$currPos;'); + ip++; + break; + + case op.CALL: // CALL f, n, pc, p1, p2, ..., pN + compileCall(); + break; + + case op.RULE: // RULE r + parts.push(stack.push("peg$parse" + ast.rules[bc[ip + 1]].name + "()")); + ip += 2; + break; + + case op.SILENT_FAILS_ON: // SILENT_FAILS_ON + parts.push('peg$silentFails++;'); + ip++; + break; + + case op.SILENT_FAILS_OFF: // SILENT_FAILS_OFF + parts.push('peg$silentFails--;'); + ip++; + break; + + default: + throw new Error("Invalid opcode: " + bc[ip] + "."); + } + } + + return parts.join('\n'); + } + + code = compile(rule.bytecode); + + parts.push([ + 'function peg$parse' + rule.name + '() {', + ' var ' + utils.map(utils.range(0, stack.maxSp + 1), s).join(', ') + ';', + '' + ].join('\n')); + + if (options.cache) { + parts.push(indent2( + generateCacheHeader(utils.indexOfRuleByName(ast, rule.name)) + )); + } + + parts.push(indent2(code)); + + if (options.cache) { + parts.push(indent2(generateCacheFooter('s0'))); + } + + parts.push([ + '', + ' return s0;', + '}' + ].join('\n')); + + return parts.join('\n'); + } + + var parts = [], + startRuleIndices, startRuleIndex, + startRuleFunctions, startRuleFunction; + + parts.push([ + '(function() {', + ' /*', + ' * Generated by PEG.js 0.7.0.', + ' *', + ' * http://pegjs.majda.cz/', + ' */', + '', + ' function subclass(child, parent) {', + ' function ctor() { this.constructor = child; }', + ' ctor.prototype = parent.prototype;', + ' child.prototype = new ctor();', + ' }', + '', + ' function SyntaxError(expected, found, offset, line, column) {', + ' function buildMessage(expected, found) {', + ' function stringEscape(s) {', + ' function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }', + '', + /* + * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string + * literal except for the closing quote character, backslash, carriage + * return, line separator, paragraph separator, and line feed. Any character + * may appear in the form of an escape sequence. + * + * For portability, we also escape escape all control and non-ASCII + * characters. Note that "\0" and "\v" escape sequences are not used because + * JSHint does not like the first and IE the second. + */ + ' 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(/[\\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); });', + ' }', + '', + ' var expectedDesc, foundDesc;', + '', + ' switch (expected.length) {', + ' case 0:', + ' expectedDesc = "end of input";', + ' break;', + '', + ' case 1:', + ' expectedDesc = expected[0];', + ' break;', + '', + ' default:', + ' expectedDesc = expected.slice(0, -1).join(", ")', + ' + " or "', + ' + expected[expected.length - 1];', + ' }', + '', + ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', + '', + ' return "Expected " + expectedDesc + " but " + foundDesc + " found.";', + ' }', + '', + ' this.expected = expected;', + ' this.found = found;', + ' this.offset = offset;', + ' this.line = line;', + ' this.column = column;', + '', + ' this.name = "SyntaxError";', + ' this.message = buildMessage(expected, found);', + ' }', + '', + ' subclass(SyntaxError, Error);', + '', + ' function parse(input) {', + ' var options = arguments.length > 1 ? arguments[1] : {},', + '' + ].join('\n')); + + if (options.optimize === "size") { + startRuleIndices = '{ ' + + utils.map( + options.allowedStartRules, + function(r) { return r + ': ' + utils.indexOfRuleByName(ast, r); } + ).join(', ') + + ' }'; + startRuleIndex = utils.indexOfRuleByName(ast, options.allowedStartRules[0]); + + parts.push([ + ' peg$startRuleIndices = ' + startRuleIndices + ',', + ' peg$startRuleIndex = ' + startRuleIndex + ',' + ].join('\n')); + } else { + startRuleFunctions = '{ ' + + utils.map( + options.allowedStartRules, + function(r) { return r + ': peg$parse' + r; } + ).join(', ') + + ' }'; + startRuleFunction = 'peg$parse' + options.allowedStartRules[0]; + + parts.push([ + ' peg$startRuleFunctions = ' + startRuleFunctions + ',', + ' peg$startRuleFunction = ' + startRuleFunction + ',' + ].join('\n')); + } + + parts.push(''); + + parts.push(indent8(generateTables())); + + parts.push([ + '', + ' peg$currPos = 0,', + ' peg$reportedPos = 0,', + ' peg$cachedPos = 0,', + ' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },', + ' peg$maxFailPos = 0,', + ' peg$maxFailExpected = [],', + ' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures + '' + ].join('\n')); + + if (options.cache) { + parts.push(' peg$cache = {},'); + } + + parts.push([ + ' peg$result;', + '' + ].join('\n')); + + if (options.optimize === "size") { + parts.push([ + ' if ("startRule" in options) {', + ' if (!(options.startRule in peg$startRuleIndices)) {', + ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + ' }', + '', + ' peg$startRuleIndex = peg$startRuleIndices[options.startRule];', + ' }' + ].join('\n')); + } else { + parts.push([ + ' if ("startRule" in options) {', + ' if (!(options.startRule in peg$startRuleFunctions)) {', + ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + ' }', + '', + ' peg$startRuleFunction = peg$startRuleFunctions[options.startRule];', + ' }' + ].join('\n')); + } + + parts.push([ + '', + ' function text() {', + ' return input.substring(peg$reportedPos, peg$currPos);', + ' }', + '', + ' function offset() {', + ' return peg$reportedPos;', + ' }', + '', + ' function line() {', + ' return peg$computePosDetails(peg$reportedPos).line;', + ' }', + '', + ' function column() {', + ' return peg$computePosDetails(peg$reportedPos).column;', + ' }', + '', + ' function peg$computePosDetails(pos) {', + ' function advance(details, pos) {', + ' var p, ch;', + '', + ' for (p = 0; p < pos; p++) {', + ' ch = input.charAt(p);', + ' if (ch === "\\n") {', + ' if (!details.seenCR) { details.line++; }', + ' details.column = 1;', + ' details.seenCR = false;', + ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', + ' details.line++;', + ' details.column = 1;', + ' details.seenCR = true;', + ' } else {', + ' details.column++;', + ' details.seenCR = false;', + ' }', + ' }', + ' }', + '', + ' if (peg$cachedPos !== pos) {', + ' if (peg$cachedPos > pos) {', + ' peg$cachedPos = 0;', + ' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };', + ' }', + ' peg$cachedPos = pos;', + ' advance(peg$cachedPosDetails, peg$cachedPos);', + ' }', + '', + ' return peg$cachedPosDetails;', + ' }', + '', + ' function peg$fail(expected) {', + ' if (peg$currPos < peg$maxFailPos) { return; }', + '', + ' if (peg$currPos > peg$maxFailPos) {', + ' peg$maxFailPos = peg$currPos;', + ' peg$maxFailExpected = [];', + ' }', + '', + ' peg$maxFailExpected.push(expected);', + ' }', + '', + ' function peg$cleanupExpected(expected) {', + ' var i;', + '', + ' expected.sort();', + '', + ' for (i = 1; i < expected.length; i++) {', + ' if (expected[i - 1] === expected[i]) {', + ' expected.splice(i, 1);', + ' }', + ' }', + ' }', + '' + ].join('\n')); + + if (options.optimize === "size") { + parts.push(indent4(generateInterpreter())); + parts.push(''); + } else { + utils.each(ast.rules, function(rule) { + parts.push(indent4(generateRuleFunction(rule))); + parts.push(''); + }); + } + + if (ast.initializer) { + parts.push(indent4(ast.initializer.code)); + parts.push(''); + } + + if (options.optimize === "size") { + parts.push(' peg$result = peg$parseRule(peg$startRuleIndex);'); + } else { + parts.push(' peg$result = peg$startRuleFunction();'); + } + + parts.push([ + '', + ' if (peg$result !== null && peg$currPos === input.length) {', + ' return peg$result;', + ' } else {', + ' peg$cleanupExpected(peg$maxFailExpected);', + ' peg$reportedPos = Math.max(peg$currPos, peg$maxFailPos);', + '', + ' throw new SyntaxError(', + ' peg$maxFailExpected,', + ' peg$reportedPos < input.length ? input.charAt(peg$reportedPos) : null,', + ' peg$reportedPos,', + ' peg$computePosDetails(peg$reportedPos).line,', + ' peg$computePosDetails(peg$reportedPos).column', + ' );', + ' }', + ' }', + '', + ' return {', + ' SyntaxError: SyntaxError,', + ' parse : parse', + ' };', + '})()' + ].join('\n')); + + ast.code = parts.join('\n'); +}; diff --git a/lib/parser.js b/lib/parser.js index 26a0dce..8427ad3 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,282 +1,93 @@ -module.exports = (function(){ +module.exports = (function() { /* * Generated by PEG.js 0.7.0. * * http://pegjs.majda.cz/ */ - + function subclass(child, parent) { function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); } - - function quote(s) { - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a - * string literal except for the closing quote character, backslash, - * carriage return, line separator, paragraph separator, and line feed. - * Any character may appear in the form of an escape sequence. - * - * For portability, we also escape escape all control and non-ASCII - * characters. Note that "\0" and "\v" escape sequences are not used - * because JSHint does not like the first and IE the second. - */ - return '"' + s - .replace(/\\/g, '\\\\') // backslash - .replace(/"/g, '\\"') // closing quote character - .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-\x1F\x80-\uFFFF]/g, escape) - + '"'; + + function SyntaxError(expected, found, offset, line, column) { + function buildMessage(expected, found) { + function stringEscape(s) { + function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } + + return s + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\x08/g, '\\b') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\f/g, '\\f') + .replace(/\r/g, '\\r') + .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); }); + } + + var expectedDesc, foundDesc; + + switch (expected.length) { + case 0: + expectedDesc = "end of input"; + break; + + case 1: + expectedDesc = expected[0]; + break; + + default: + expectedDesc = expected.slice(0, -1).join(", ") + + " or " + + expected[expected.length - 1]; + } + + foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; + + return "Expected " + expectedDesc + " but " + foundDesc + " found."; + } + + this.expected = expected; + this.found = found; + this.offset = offset; + this.line = line; + this.column = column; + + this.name = "SyntaxError"; + this.message = buildMessage(expected, found); } - - var result = { - /* - * Parses the input with a generated parser. If the parsing is successful, - * returns a value explicitly or implicitly specified by the grammar from - * which the parser was generated (see |PEG.buildParser|). If the parsing is - * unsuccessful, throws |PEG.parser.SyntaxError| describing the error. - */ - parse: function(input) { - var parseFunctions = { - "grammar": parse_grammar - }; - - var options = arguments.length > 1 ? arguments[1] : {}, - startRule; - - if (options.startRule !== undefined) { - startRule = options.startRule; - - if (parseFunctions[startRule] === undefined) { - throw new Error("Can't start parsing from rule " + quote(startRule) + "."); - } - } else { - startRule = "grammar"; - } - - var pos = 0; - var reportedPos = 0; - var cachedReportedPos = 0; - var cachedReportedPosDetails = { line: 1, column: 1, seenCR: false }; - var reportFailures = 0; - var rightmostFailuresPos = 0; - var rightmostFailuresExpected = []; - - function padLeft(input, padding, length) { - var result = input; - - var padLength = length - input.length; - for (var i = 0; i < padLength; i++) { - result = padding + result; - } - - return result; - } - - function escape(ch) { - var charCode = ch.charCodeAt(0); - var escapeChar; - var length; - - if (charCode <= 0xFF) { - escapeChar = 'x'; - length = 2; - } else { - escapeChar = 'u'; - length = 4; - } - - return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); - } - - function computeReportedPosDetails() { - function advanceCachedReportedPos() { - var ch; - - for (; cachedReportedPos < reportedPos; cachedReportedPos++) { - ch = input.charAt(cachedReportedPos); - if (ch === "\n") { - if (!cachedReportedPosDetails.seenCR) { cachedReportedPosDetails.line++; } - cachedReportedPosDetails.column = 1; - cachedReportedPosDetails.seenCR = false; - } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { - cachedReportedPosDetails.line++; - cachedReportedPosDetails.column = 1; - cachedReportedPosDetails.seenCR = true; - } else { - cachedReportedPosDetails.column++; - cachedReportedPosDetails.seenCR = false; - } - } - } - - if (cachedReportedPos !== reportedPos) { - if (cachedReportedPos > reportedPos) { - cachedReportedPos = 0; - cachedReportedPosDetails = { line: 1, column: 1, seenCR: false }; - } - advanceCachedReportedPos(); - } - - return cachedReportedPosDetails; - } - - function offset() { - return reportedPos; - } - - function line() { - return computeReportedPosDetails().line; - } - - function column() { - return computeReportedPosDetails().column; - } - - function matchFailed(failure) { - if (pos < rightmostFailuresPos) { - return; - } - - if (pos > rightmostFailuresPos) { - rightmostFailuresPos = pos; - rightmostFailuresExpected = []; - } - - rightmostFailuresExpected.push(failure); - } - - function parse_grammar() { - var r0, r1, r2, r3, r4, r5, r6; - - r1 = pos; - r2 = pos; - r3 = parse___(); - if (r3 !== null) { - r4 = parse_initializer(); - r4 = r4 !== null ? r4 : ""; - if (r4 !== null) { - r6 = parse_rule(); - if (r6 !== null) { - r5 = []; - while (r6 !== null) { - r5.push(r6); - r6 = parse_rule(); - } - } else { - r5 = null; - } - if (r5 !== null) { - r0 = [r3, r4, r5]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(initializer, rules) { + + subclass(SyntaxError, Error); + + function parse(input) { + var options = arguments.length > 1 ? arguments[1] : {}, + + peg$startRuleFunctions = { grammar: peg$parsegrammar }, + peg$startRuleFunction = peg$parsegrammar, + + peg$c0 = null, + peg$c1 = "", + peg$c2 = [], + peg$c3 = function(initializer, rules) { return { type: "grammar", initializer: initializer !== "" ? initializer : null, rules: rules, startRule: rules[0].name }; - })(r4, r5); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_initializer() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r3 = parse_action(); - if (r3 !== null) { - r4 = parse_semicolon(); - r4 = r4 !== null ? r4 : ""; - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(code) { + }, + peg$c4 = function(code) { return { type: "initializer", code: code }; - })(r3); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_rule() { - var r0, r1, r2, r3, r4, r5, r6, r7; - - r1 = pos; - r2 = pos; - r3 = parse_identifier(); - if (r3 !== null) { - r4 = parse_string(); - r4 = r4 !== null ? r4 : ""; - if (r4 !== null) { - r5 = parse_equals(); - if (r5 !== null) { - r6 = parse_choice(); - if (r6 !== null) { - r7 = parse_semicolon(); - r7 = r7 !== null ? r7 : ""; - if (r7 !== null) { - r0 = [r3, r4, r5, r6, r7]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(name, displayName, expression) { + }, + peg$c5 = function(name, displayName, expression) { return { type: "rule", name: name, @@ -288,66 +99,8 @@ module.exports = (function(){ } : expression }; - })(r3, r4, r6); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_choice() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8; - - r1 = pos; - r2 = pos; - r3 = parse_sequence(); - if (r3 !== null) { - r4 = []; - r6 = pos; - r7 = parse_slash(); - if (r7 !== null) { - r8 = parse_sequence(); - if (r8 !== null) { - r5 = [r7, r8]; - } else { - r5 = null; - pos = r6; - } - } else { - r5 = null; - pos = r6; - } - while (r5 !== null) { - r4.push(r5); - r6 = pos; - r7 = parse_slash(); - if (r7 !== null) { - r8 = parse_sequence(); - if (r8 !== null) { - r5 = [r7, r8]; - } else { - r5 = null; - pos = r6; - } - } else { - r5 = null; - pos = r6; - } - } - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(head, tail) { + }, + peg$c6 = function(head, tail) { if (tail.length > 0) { var alternatives = [head].concat(utils.map( tail, @@ -360,40 +113,8 @@ module.exports = (function(){ } else { return head; } - })(r3, r4); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_sequence() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r3 = []; - r4 = parse_labeled(); - while (r4 !== null) { - r3.push(r4); - r4 = parse_labeled(); - } - if (r3 !== null) { - r4 = parse_action(); - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(elements, code) { + }, + peg$c7 = function(elements, code) { var expression = elements.length !== 1 ? { type: "sequence", @@ -405,2591 +126,2879 @@ module.exports = (function(){ expression: expression, code: code }; - })(r3, r4); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r0 = []; - r2 = parse_labeled(); - while (r2 !== null) { - r0.push(r2); - r2 = parse_labeled(); - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(elements) { - return elements.length !== 1 - ? { - type: "sequence", - elements: elements - } - : elements[0]; - })(r0); - } - if (r0 === null) { - pos = r1; - } - } - return r0; - } - - function parse_labeled() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - r3 = parse_identifier(); - if (r3 !== null) { - r4 = parse_colon(); - if (r4 !== null) { - r5 = parse_prefixed(); - if (r5 !== null) { - r0 = [r3, r4, r5]; + }, + peg$c8 = function(elements) { + return elements.length !== 1 + ? { + type: "sequence", + elements: elements + } + : elements[0]; + }, + peg$c9 = function(label, expression) { + return { + type: "labeled", + label: label, + expression: expression + }; + }, + peg$c10 = function(expression) { + return { + type: "text", + expression: expression + }; + }, + peg$c11 = function(code) { + return { + type: "semantic_and", + code: code + }; + }, + peg$c12 = function(expression) { + return { + type: "simple_and", + expression: expression + }; + }, + peg$c13 = function(code) { + return { + type: "semantic_not", + code: code + }; + }, + peg$c14 = function(expression) { + return { + type: "simple_not", + expression: expression + }; + }, + peg$c15 = function(expression) { + return { + type: "optional", + expression: expression + }; + }, + peg$c16 = function(expression) { + return { + type: "zero_or_more", + expression: expression + }; + }, + peg$c17 = function(expression) { + return { + type: "one_or_more", + expression: expression + }; + }, + peg$c18 = function(name) { + return { + type: "rule_ref", + name: name + }; + }, + peg$c19 = function() { return { type: "any" }; }, + peg$c20 = function(expression) { return expression; }, + peg$c21 = "action", + peg$c22 = function(braced) { return braced.substr(1, braced.length - 2); }, + peg$c23 = "{", + peg$c24 = "\"{\"", + peg$c25 = "}", + peg$c26 = "\"}\"", + peg$c27 = /^[^{}]/, + peg$c28 = "[^{}]", + peg$c29 = "=", + peg$c30 = "\"=\"", + peg$c31 = function() { return "="; }, + peg$c32 = ":", + peg$c33 = "\":\"", + peg$c34 = function() { return ":"; }, + peg$c35 = ";", + peg$c36 = "\";\"", + peg$c37 = function() { return ";"; }, + peg$c38 = "/", + peg$c39 = "\"/\"", + peg$c40 = function() { return "/"; }, + peg$c41 = "&", + peg$c42 = "\"&\"", + peg$c43 = function() { return "&"; }, + peg$c44 = "!", + peg$c45 = "\"!\"", + peg$c46 = function() { return "!"; }, + peg$c47 = "$", + peg$c48 = "\"$\"", + peg$c49 = function() { return "$"; }, + peg$c50 = "?", + peg$c51 = "\"?\"", + peg$c52 = function() { return "?"; }, + peg$c53 = "*", + peg$c54 = "\"*\"", + peg$c55 = function() { return "*"; }, + peg$c56 = "+", + peg$c57 = "\"+\"", + peg$c58 = function() { return "+"; }, + peg$c59 = "(", + peg$c60 = "\"(\"", + peg$c61 = function() { return "("; }, + peg$c62 = ")", + peg$c63 = "\")\"", + peg$c64 = function() { return ")"; }, + peg$c65 = ".", + peg$c66 = "\".\"", + peg$c67 = function() { return "."; }, + peg$c68 = "identifier", + peg$c69 = "_", + peg$c70 = "\"_\"", + peg$c71 = function(chars) { return chars; }, + peg$c72 = "literal", + peg$c73 = "i", + peg$c74 = "\"i\"", + peg$c75 = function(value, flags) { + return { + type: "literal", + value: value, + ignoreCase: flags === "i" + }; + }, + peg$c76 = "string", + peg$c77 = function(string) { return string; }, + peg$c78 = "\"", + peg$c79 = "\"\\\"\"", + peg$c80 = function(chars) { return chars.join(""); }, + peg$c81 = "\\", + peg$c82 = "\"\\\\\"", + peg$c83 = "any character", + peg$c84 = function(char_) { return char_; }, + peg$c85 = "'", + peg$c86 = "\"'\"", + peg$c87 = "character class", + peg$c88 = "[", + peg$c89 = "\"[\"", + peg$c90 = "^", + peg$c91 = "\"^\"", + peg$c92 = "]", + peg$c93 = "\"]\"", + peg$c94 = function(inverted, parts, flags) { + var partsConverted = utils.map(parts, function(part) { return part.data; }); + var rawText = "[" + + inverted + + utils.map(parts, function(part) { return part.rawText; }).join("") + + "]" + + flags; + + return { + type: "class", + parts: partsConverted, + // FIXME: Get the raw text from the input directly. + rawText: rawText, + inverted: inverted === "^", + ignoreCase: flags === "i" + }; + }, + peg$c95 = "-", + peg$c96 = "\"-\"", + peg$c97 = function(begin, end) { + if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) { + throw new this.SyntaxError( + "Invalid character range: " + begin.rawText + "-" + end.rawText + "." + ); + } + + return { + data: [begin.data, end.data], + // FIXME: Get the raw text from the input directly. + rawText: begin.rawText + "-" + end.rawText + }; + }, + peg$c98 = function(char_) { + return { + data: char_, + // FIXME: Get the raw text from the input directly. + rawText: utils.quoteForRegexpClass(char_) + }; + }, + peg$c99 = "x", + peg$c100 = "\"x\"", + peg$c101 = "u", + peg$c102 = "\"u\"", + peg$c103 = function(char_) { + return char_ + .replace("b", "\b") + .replace("f", "\f") + .replace("n", "\n") + .replace("r", "\r") + .replace("t", "\t") + .replace("v", "\x0B"); // IE does not recognize "\v". + }, + peg$c104 = "\\0", + peg$c105 = "\"\\\\0\"", + peg$c106 = function() { return "\x00"; }, + peg$c107 = "\\x", + peg$c108 = "\"\\\\x\"", + peg$c109 = function(digits) { + return String.fromCharCode(parseInt(digits, 16)); + }, + peg$c110 = "\\u", + peg$c111 = "\"\\\\u\"", + peg$c112 = function(eol) { return eol; }, + peg$c113 = /^[0-9]/, + peg$c114 = "[0-9]", + peg$c115 = /^[0-9a-fA-F]/, + peg$c116 = "[0-9a-fA-F]", + peg$c117 = /^[a-z]/, + peg$c118 = "[a-z]", + peg$c119 = /^[A-Z]/, + peg$c120 = "[A-Z]", + peg$c121 = "comment", + peg$c122 = "//", + peg$c123 = "\"//\"", + peg$c124 = "/*", + peg$c125 = "\"/*\"", + peg$c126 = "*/", + peg$c127 = "\"*/\"", + peg$c128 = "end of line", + peg$c129 = "\n", + peg$c130 = "\"\\n\"", + peg$c131 = "\r\n", + peg$c132 = "\"\\r\\n\"", + peg$c133 = "\r", + peg$c134 = "\"\\r\"", + peg$c135 = "\u2028", + peg$c136 = "\"\\u2028\"", + peg$c137 = "\u2029", + peg$c138 = "\"\\u2029\"", + peg$c139 = /^[\n\r\u2028\u2029]/, + peg$c140 = "[\\n\\r\\u2028\\u2029]", + peg$c141 = "whitespace", + peg$c142 = /^[ \t\x0B\f\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/, + peg$c143 = "[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]", + + peg$currPos = 0, + peg$reportedPos = 0, + peg$cachedPos = 0, + peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }, + peg$maxFailPos = 0, + peg$maxFailExpected = [], + peg$silentFails = 0, + + peg$result; + + if ("startRule" in options) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$reportedPos, peg$currPos); + } + + function offset() { + return peg$reportedPos; + } + + function line() { + return peg$computePosDetails(peg$reportedPos).line; + } + + function column() { + return peg$computePosDetails(peg$reportedPos).column; + } + + function peg$computePosDetails(pos) { + function advance(details, pos) { + var p, ch; + + for (p = 0; p < pos; p++) { + ch = input.charAt(p); + if (ch === "\n") { + if (!details.seenCR) { details.line++; } + details.column = 1; + details.seenCR = false; + } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { + details.line++; + details.column = 1; + details.seenCR = true; + } else { + details.column++; + details.seenCR = false; + } + } + } + + if (peg$cachedPos !== pos) { + if (peg$cachedPos > pos) { + peg$cachedPos = 0; + peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }; + } + peg$cachedPos = pos; + advance(peg$cachedPosDetails, peg$cachedPos); + } + + return peg$cachedPosDetails; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$cleanupExpected(expected) { + var i; + + expected.sort(); + + for (i = 1; i < expected.length; i++) { + if (expected[i - 1] === expected[i]) { + expected.splice(i, 1); + } + } + } + + function peg$parsegrammar() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = peg$parse__(); + if (s1 !== null) { + s2 = peg$parseinitializer(); + if (s2 === null) { + s2 = peg$c1; + } + if (s2 !== null) { + s3 = []; + s4 = peg$parserule(); + if (s4 !== null) { + while (s4 !== null) { + s3.push(s4); + s4 = peg$parserule(); + } + } else { + s3 = peg$c0; + } + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c3(s2,s3); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(label, expression) { - return { - type: "labeled", - label: label, - expression: expression - }; - })(r3, r5); + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parseinitializer() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseaction(); + if (s1 !== null) { + s2 = peg$parsesemicolon(); + if (s2 === null) { + s2 = peg$c1; + } + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c4(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parserule() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseidentifier(); + if (s1 !== null) { + s2 = peg$parsestring(); + if (s2 === null) { + s2 = peg$c1; + } + if (s2 !== null) { + s3 = peg$parseequals(); + if (s3 !== null) { + s4 = peg$parsechoice(); + if (s4 !== null) { + s5 = peg$parsesemicolon(); + if (s5 === null) { + s5 = peg$c1; + } + if (s5 !== null) { + peg$reportedPos = s0; + s1 = peg$c5(s1,s2,s4); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parsechoice() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parsesequence(); + if (s1 !== null) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parseslash(); + if (s4 !== null) { + s5 = peg$parsesequence(); + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + } else { + peg$currPos = s3; + s3 = peg$c0; + } + while (s3 !== null) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseslash(); + if (s4 !== null) { + s5 = peg$parsesequence(); + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + } else { + peg$currPos = s3; + s3 = peg$c0; + } + } + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c6(s1,s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parsesequence() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parselabeled(); + while (s2 !== null) { + s1.push(s2); + s2 = peg$parselabeled(); + } + if (s1 !== null) { + s2 = peg$parseaction(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c7(s1,s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = []; + s2 = peg$parselabeled(); + while (s2 !== null) { + s1.push(s2); + s2 = peg$parselabeled(); } - if (r0 === null) { - pos = r1; + if (s1 !== null) { + peg$reportedPos = s0; + s1 = peg$c8(s1); } - if (r0 === null) { - r0 = parse_prefixed(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; } - return r0; } - - function parse_prefixed() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r3 = parse_dollar(); - if (r3 !== null) { - r4 = parse_suffixed(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parselabeled() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parseidentifier(); + if (s1 !== null) { + s2 = peg$parsecolon(); + if (s2 !== null) { + s3 = peg$parseprefixed(); + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c9(s1,s3); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "text", - expression: expression - }; - })(r4); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_and(); - if (r3 !== null) { - r4 = parse_action(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$parseprefixed(); + } + + return s0; + } + + function peg$parseprefixed() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parsedollar(); + if (s1 !== null) { + s2 = peg$parsesuffixed(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c10(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parseand(); + if (s1 !== null) { + s2 = peg$parseaction(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c11(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(code) { - return { - type: "semantic_and", - code: code - }; - })(r4); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_and(); - if (r3 !== null) { - r4 = parse_suffixed(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parseand(); + if (s1 !== null) { + s2 = peg$parsesuffixed(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c12(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "simple_and", - expression: expression - }; - })(r4); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_not(); - if (r3 !== null) { - r4 = parse_action(); - if (r4 !== null) { - r0 = [r3, r4]; + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parsenot(); + if (s1 !== null) { + s2 = peg$parseaction(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c13(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(code) { - return { - type: "semantic_not", - code: code - }; - })(r4); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_not(); - if (r3 !== null) { - r4 = parse_suffixed(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parsenot(); + if (s1 !== null) { + s2 = peg$parsesuffixed(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c14(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "simple_not", - expression: expression - }; - })(r4); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r0 = parse_suffixed(); + peg$currPos = s0; + s0 = peg$c0; } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$parsesuffixed(); } } } } - return r0; } - - function parse_suffixed() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r3 = parse_primary(); - if (r3 !== null) { - r4 = parse_question(); - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; + + return s0; + } + + function peg$parsesuffixed() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseprimary(); + if (s1 !== null) { + s2 = peg$parsequestion(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c15(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "optional", - expression: expression - }; - })(r3); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_primary(); - if (r3 !== null) { - r4 = parse_star(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parseprimary(); + if (s1 !== null) { + s2 = peg$parsestar(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c16(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "zero_or_more", - expression: expression - }; - })(r3); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_primary(); - if (r3 !== null) { - r4 = parse_plus(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parseprimary(); + if (s1 !== null) { + s2 = peg$parseplus(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c17(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { - return { - type: "one_or_more", - expression: expression - }; - })(r3); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r0 = parse_primary(); - } - } - } - return r0; - } - - function parse_primary() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8; - - r1 = pos; - r2 = pos; - r3 = parse_identifier(); - if (r3 !== null) { - r5 = pos; - reportFailures++; - r6 = pos; - r7 = parse_string(); - r7 = r7 !== null ? r7 : ""; - if (r7 !== null) { - r8 = parse_equals(); - if (r8 !== null) { - r4 = [r7, r8]; - } else { - r4 = null; - pos = r6; + peg$currPos = s0; + s0 = peg$c0; } } else { - r4 = null; - pos = r6; + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$parseprimary(); } - reportFailures--; - if (r4 === null) { - r4 = ""; + } + } + + return s0; + } + + function peg$parseprimary() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseidentifier(); + if (s1 !== null) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$currPos; + s4 = peg$parsestring(); + if (s4 === null) { + s4 = peg$c1; + } + if (s4 !== null) { + s5 = peg$parseequals(); + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; } else { - r4 = null; - pos = r5; + peg$currPos = s3; + s3 = peg$c0; } - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + peg$silentFails--; + if (s3 === null) { + s2 = peg$c1; + } else { + peg$currPos = s2; + s2 = peg$c0; + } + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c18(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(name) { - return { - type: "rule_ref", - name: name - }; - })(r3); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r0 = parse_literal(); - if (r0 === null) { - r0 = parse_class(); - if (r0 === null) { - r1 = pos; - r0 = parse_dot(); - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return { type: "any" }; })(); - } - if (r0 === null) { - pos = r1; - } - if (r0 === null) { - r1 = pos; - r2 = pos; - r3 = parse_lparen(); - if (r3 !== null) { - r4 = parse_choice(); - if (r4 !== null) { - r5 = parse_rparen(); - if (r5 !== null) { - r0 = [r3, r4, r5]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + if (s0 === null) { + s0 = peg$parseliteral(); + if (s0 === null) { + s0 = peg$parseclass(); + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parsedot(); + if (s1 !== null) { + peg$reportedPos = s0; + s1 = peg$c19(); + } + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + if (s0 === null) { + s0 = peg$currPos; + s1 = peg$parselparen(); + if (s1 !== null) { + s2 = peg$parsechoice(); + if (s2 !== null) { + s3 = peg$parserparen(); + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c20(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(expression) { return expression; })(r4); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } + } else { + peg$currPos = s0; + s0 = peg$c0; } } } } - return r0; } - - function parse_action() { - var r0, r1, r2, r3, r4; - - reportFailures++; - r1 = pos; - r2 = pos; - r3 = parse_braced(); - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(braced) { return braced.substr(1, braced.length - 2); })(r3); - } - if (r0 === null) { - pos = r1; - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("action"); + + return s0; + } + + function peg$parseaction() { + var s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsebraced(); + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c22(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_braced() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 123) { - r3 = "{"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"{\""); - } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c21); } + } + + return s0; + } + + function peg$parsebraced() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 123) { + s2 = peg$c23; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c24); } + } + if (s2 !== null) { + s3 = []; + s4 = peg$parsebraced(); + if (s4 === null) { + s4 = peg$parsenonBraceCharacters(); } - if (r3 !== null) { - r4 = []; - r5 = parse_braced(); - if (r5 === null) { - r5 = parse_nonBraceCharacters(); + while (s4 !== null) { + s3.push(s4); + s4 = peg$parsebraced(); + if (s4 === null) { + s4 = peg$parsenonBraceCharacters(); } - while (r5 !== null) { - r4.push(r5); - r5 = parse_braced(); - if (r5 === null) { - r5 = parse_nonBraceCharacters(); - } + } + if (s3 !== null) { + if (input.charCodeAt(peg$currPos) === 125) { + s4 = peg$c25; + peg$currPos++; + } else { + s4 = null; + if (peg$silentFails === 0) { peg$fail(peg$c26); } } - if (r4 !== null) { - if (input.charCodeAt(pos) === 125) { - r5 = "}"; - pos++; - } else { - r5 = null; - if (reportFailures === 0) { - matchFailed("\"}\""); - } - } - if (r5 !== null) { - r0 = [r3, r4, r5]; - } else { - r0 = null; - pos = r2; - } + if (s4 !== null) { + s2 = [s2, s3, s4]; + s1 = s2; } else { - r0 = null; - pos = r2; + peg$currPos = s1; + s1 = peg$c0; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - r0 = input.substring(pos, r1); + peg$currPos = s1; + s1 = peg$c0; } - return r0; + } else { + peg$currPos = s1; + s1 = peg$c0; } - - function parse_nonBraceCharacters() { - var r0, r1; - - r1 = parse_nonBraceCharacter(); - if (r1 !== null) { - r0 = []; - while (r1 !== null) { - r0.push(r1); - r1 = parse_nonBraceCharacter(); - } - } else { - r0 = null; - } - return r0; + if (s1 !== null) { + s1 = input.substring(s0, peg$currPos); } - - function parse_nonBraceCharacter() { - var r0; - - if (/^[^{}]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[^{}]"); - } + s0 = s1; + + return s0; + } + + function peg$parsenonBraceCharacters() { + var s0, s1; + + s0 = []; + s1 = peg$parsenonBraceCharacter(); + if (s1 !== null) { + while (s1 !== null) { + s0.push(s1); + s1 = peg$parsenonBraceCharacter(); } - return r0; + } else { + s0 = peg$c0; } - - function parse_equals() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 61) { - r3 = "="; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"=\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsenonBraceCharacter() { + var s0; + + if (peg$c27.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c28); } + } + + return s0; + } + + function peg$parseequals() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 61) { + s1 = peg$c29; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c30); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c31(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "="; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_colon() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 58) { - r3 = ":"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\":\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsecolon() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s1 = peg$c32; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c33); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c34(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return ":"; })(); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_semicolon() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 59) { - r3 = ";"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\";\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsesemicolon() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 59) { + s1 = peg$c35; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c36); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c37(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return ";"; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_slash() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 47) { - r3 = "/"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"/\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parseslash() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 47) { + s1 = peg$c38; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c39); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c40(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "/"; })(); - } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_and() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 38) { - r3 = "&"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"&\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parseand() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 38) { + s1 = peg$c41; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c42); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c43(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "&"; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_not() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 33) { - r3 = "!"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"!\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsenot() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c44; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c45); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c46(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "!"; })(); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_dollar() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 36) { - r3 = "$"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"$\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsedollar() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 36) { + s1 = peg$c47; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c48); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c49(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "$"; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_question() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 63) { - r3 = "?"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"?\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsequestion() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 63) { + s1 = peg$c50; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c51); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c52(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "?"; })(); - } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_star() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 42) { - r3 = "*"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"*\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsestar() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c53; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c55(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "*"; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_plus() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 43) { - r3 = "+"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"+\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parseplus() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 43) { + s1 = peg$c56; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c57); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c58(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "+"; })(); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_lparen() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 40) { - r3 = "("; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"(\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parselparen() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c59; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c60); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c61(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "("; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_rparen() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 41) { - r3 = ")"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\")\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parserparen() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 41) { + s1 = peg$c62; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c63); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c64(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return ")"; })(); - } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_dot() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 46) { - r3 = "."; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\".\""); - } - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + + return s0; + } + + function peg$parsedot() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s1 = peg$c65; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c66); } + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c67(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "."; })(); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_identifier() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8; - - reportFailures++; - r1 = pos; - r2 = pos; - r4 = pos; - r5 = pos; - r6 = parse_letter(); - if (r6 === null) { - if (input.charCodeAt(pos) === 95) { - r6 = "_"; - pos++; - } else { - r6 = null; - if (reportFailures === 0) { - matchFailed("\"_\""); + + return s0; + } + + function peg$parseidentifier() { + var s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = peg$currPos; + s3 = peg$parseletter(); + if (s3 === null) { + if (input.charCodeAt(peg$currPos) === 95) { + s3 = peg$c69; + peg$currPos++; + } else { + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c70); } + } + } + if (s3 !== null) { + s4 = []; + s5 = peg$parseletter(); + if (s5 === null) { + s5 = peg$parsedigit(); + if (s5 === null) { + if (input.charCodeAt(peg$currPos) === 95) { + s5 = peg$c69; + peg$currPos++; + } else { + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c70); } } } } - if (r6 !== null) { - r7 = []; - r8 = parse_letter(); - if (r8 === null) { - r8 = parse_digit(); - if (r8 === null) { - if (input.charCodeAt(pos) === 95) { - r8 = "_"; - pos++; + while (s5 !== null) { + s4.push(s5); + s5 = peg$parseletter(); + if (s5 === null) { + s5 = peg$parsedigit(); + if (s5 === null) { + if (input.charCodeAt(peg$currPos) === 95) { + s5 = peg$c69; + peg$currPos++; } else { - r8 = null; - if (reportFailures === 0) { - matchFailed("\"_\""); - } + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c70); } } } } - while (r8 !== null) { - r7.push(r8); - r8 = parse_letter(); - if (r8 === null) { - r8 = parse_digit(); - if (r8 === null) { - if (input.charCodeAt(pos) === 95) { - r8 = "_"; - pos++; - } else { - r8 = null; - if (reportFailures === 0) { - matchFailed("\"_\""); - } - } - } - } - } - if (r7 !== null) { - r3 = [r6, r7]; - } else { - r3 = null; - pos = r5; - } - } else { - r3 = null; - pos = r5; } - if (r3 !== null) { - r3 = input.substring(pos, r4); + if (s4 !== null) { + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$c0; } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s2; + s2 = peg$c0; + } + if (s2 !== null) { + s2 = input.substring(s1, peg$currPos); + } + s1 = s2; + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c71(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(chars) { return chars; })(r3); - } - if (r0 === null) { - pos = r1; - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("identifier"); + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_literal() { - var r0, r1, r2, r3, r4, r5; - - reportFailures++; - r1 = pos; - r2 = pos; - r3 = parse_doubleQuotedString(); - if (r3 === null) { - r3 = parse_singleQuotedString(); - } - if (r3 !== null) { - if (input.charCodeAt(pos) === 105) { - r4 = "i"; - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"i\""); - } - } - r4 = r4 !== null ? r4 : ""; - if (r4 !== null) { - r5 = parse___(); - if (r5 !== null) { - r0 = [r3, r4, r5]; + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c68); } + } + + return s0; + } + + function peg$parseliteral() { + var s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsedoubleQuotedString(); + if (s1 === null) { + s1 = peg$parsesingleQuotedString(); + } + if (s1 !== null) { + if (input.charCodeAt(peg$currPos) === 105) { + s2 = peg$c73; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c74); } + } + if (s2 === null) { + s2 = peg$c1; + } + if (s2 !== null) { + s3 = peg$parse__(); + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c75(s1,s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(value, flags) { - return { - type: "literal", - value: value, - ignoreCase: flags === "i" - }; - })(r3, r4); - } - if (r0 === null) { - pos = r1; - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("literal"); - } - return r0; - } - - function parse_string() { - var r0, r1, r2, r3, r4; - - reportFailures++; - r1 = pos; - r2 = pos; - r3 = parse_doubleQuotedString(); - if (r3 === null) { - r3 = parse_singleQuotedString(); - } - if (r3 !== null) { - r4 = parse___(); - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(string) { return string; })(r3); - } - if (r0 === null) { - pos = r1; - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("string"); - } - return r0; - } - - function parse_doubleQuotedString() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 34) { - r3 = "\""; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\"\""); - } - } - if (r3 !== null) { - r4 = []; - r5 = parse_doubleQuotedCharacter(); - while (r5 !== null) { - r4.push(r5); - r5 = parse_doubleQuotedCharacter(); - } - if (r4 !== null) { - if (input.charCodeAt(pos) === 34) { - r5 = "\""; - pos++; - } else { - r5 = null; - if (reportFailures === 0) { - matchFailed("\"\\\"\""); - } - } - if (r5 !== null) { - r0 = [r3, r4, r5]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c72); } + } + + return s0; + } + + function peg$parsestring() { + var s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsedoubleQuotedString(); + if (s1 === null) { + s1 = peg$parsesingleQuotedString(); + } + if (s1 !== null) { + s2 = peg$parse__(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c77(s1); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c76); } + } + + return s0; + } + + function peg$parsedoubleQuotedString() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c78; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c79); } + } + if (s1 !== null) { + s2 = []; + s3 = peg$parsedoubleQuotedCharacter(); + while (s3 !== null) { + s2.push(s3); + s3 = peg$parsedoubleQuotedCharacter(); + } + if (s2 !== null) { + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c78; + peg$currPos++; + } else { + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c79); } + } + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c80(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(chars) { return chars.join(""); })(r4); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_doubleQuotedCharacter() { - var r0; - - r0 = parse_simpleDoubleQuotedCharacter(); - if (r0 === null) { - r0 = parse_simpleEscapeSequence(); - if (r0 === null) { - r0 = parse_zeroEscapeSequence(); - if (r0 === null) { - r0 = parse_hexEscapeSequence(); - if (r0 === null) { - r0 = parse_unicodeEscapeSequence(); - if (r0 === null) { - r0 = parse_eolEscapeSequence(); - } + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parsedoubleQuotedCharacter() { + var s0; + + s0 = peg$parsesimpleDoubleQuotedCharacter(); + if (s0 === null) { + s0 = peg$parsesimpleEscapeSequence(); + if (s0 === null) { + s0 = peg$parsezeroEscapeSequence(); + if (s0 === null) { + s0 = peg$parsehexEscapeSequence(); + if (s0 === null) { + s0 = peg$parseunicodeEscapeSequence(); + if (s0 === null) { + s0 = peg$parseeolEscapeSequence(); } } } } - return r0; } - - function parse_simpleDoubleQuotedCharacter() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r4 = pos; - reportFailures++; - if (input.charCodeAt(pos) === 34) { - r3 = "\""; - pos++; + + return s0; + } + + function peg$parsesimpleDoubleQuotedCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 34) { + s2 = peg$c78; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c79); } + } + if (s2 === null) { + if (input.charCodeAt(peg$currPos) === 92) { + s2 = peg$c81; + peg$currPos++; } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\"\""); - } + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c82); } } - if (r3 === null) { - if (input.charCodeAt(pos) === 92) { - r3 = "\\"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\\""); - } - } - if (r3 === null) { - r3 = parse_eolChar(); - } + if (s2 === null) { + s2 = peg$parseeolChar(); } - reportFailures--; - if (r3 === null) { - r3 = ""; + } + peg$silentFails--; + if (s2 === null) { + s1 = peg$c1; + } else { + peg$currPos = s1; + s1 = peg$c0; + } + if (s1 !== null) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; } else { - r3 = null; - pos = r4; + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } } - if (r3 !== null) { - if (input.length > pos) { - r4 = input.charAt(pos); - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r4 !== null) { - r0 = [r3, r4]; + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(char_) { return char_; })(r4); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_singleQuotedString() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 39) { - r3 = "'"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"'\""); - } - } - if (r3 !== null) { - r4 = []; - r5 = parse_singleQuotedCharacter(); - while (r5 !== null) { - r4.push(r5); - r5 = parse_singleQuotedCharacter(); - } - if (r4 !== null) { - if (input.charCodeAt(pos) === 39) { - r5 = "'"; - pos++; - } else { - r5 = null; - if (reportFailures === 0) { - matchFailed("\"'\""); - } - } - if (r5 !== null) { - r0 = [r3, r4, r5]; + + return s0; + } + + function peg$parsesingleQuotedString() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c85; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c86); } + } + if (s1 !== null) { + s2 = []; + s3 = peg$parsesingleQuotedCharacter(); + while (s3 !== null) { + s2.push(s3); + s3 = peg$parsesingleQuotedCharacter(); + } + if (s2 !== null) { + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c85; + peg$currPos++; + } else { + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c86); } + } + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c80(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(chars) { return chars.join(""); })(r4); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_singleQuotedCharacter() { - var r0; - - r0 = parse_simpleSingleQuotedCharacter(); - if (r0 === null) { - r0 = parse_simpleEscapeSequence(); - if (r0 === null) { - r0 = parse_zeroEscapeSequence(); - if (r0 === null) { - r0 = parse_hexEscapeSequence(); - if (r0 === null) { - r0 = parse_unicodeEscapeSequence(); - if (r0 === null) { - r0 = parse_eolEscapeSequence(); - } + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parsesingleQuotedCharacter() { + var s0; + + s0 = peg$parsesimpleSingleQuotedCharacter(); + if (s0 === null) { + s0 = peg$parsesimpleEscapeSequence(); + if (s0 === null) { + s0 = peg$parsezeroEscapeSequence(); + if (s0 === null) { + s0 = peg$parsehexEscapeSequence(); + if (s0 === null) { + s0 = peg$parseunicodeEscapeSequence(); + if (s0 === null) { + s0 = peg$parseeolEscapeSequence(); } } } } - return r0; } - - function parse_simpleSingleQuotedCharacter() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r4 = pos; - reportFailures++; - if (input.charCodeAt(pos) === 39) { - r3 = "'"; - pos++; + + return s0; + } + + function peg$parsesimpleSingleQuotedCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 39) { + s2 = peg$c85; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c86); } + } + if (s2 === null) { + if (input.charCodeAt(peg$currPos) === 92) { + s2 = peg$c81; + peg$currPos++; } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"'\""); - } + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c82); } } - if (r3 === null) { - if (input.charCodeAt(pos) === 92) { - r3 = "\\"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\\""); - } - } - if (r3 === null) { - r3 = parse_eolChar(); - } + if (s2 === null) { + s2 = peg$parseeolChar(); } - reportFailures--; - if (r3 === null) { - r3 = ""; + } + peg$silentFails--; + if (s2 === null) { + s1 = peg$c1; + } else { + peg$currPos = s1; + s1 = peg$c0; + } + if (s1 !== null) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; } else { - r3 = null; - pos = r4; - } - if (r3 !== null) { - if (input.length > pos) { - r4 = input.charAt(pos); - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r4 !== null) { - r0 = [r3, r4]; + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } + } + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(char_) { return char_; })(r4); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_class() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8; - - reportFailures++; - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 91) { - r3 = "["; - pos++; + + return s0; + } + + function peg$parseclass() { + var s0, s1, s2, s3, s4, s5, s6; + + peg$silentFails++; + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 91) { + s1 = peg$c88; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c89); } + } + if (s1 !== null) { + if (input.charCodeAt(peg$currPos) === 94) { + s2 = peg$c90; + peg$currPos++; } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"[\""); - } + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c91); } } - if (r3 !== null) { - if (input.charCodeAt(pos) === 94) { - r4 = "^"; - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"^\""); - } + if (s2 === null) { + s2 = peg$c1; + } + if (s2 !== null) { + s3 = []; + s4 = peg$parseclassCharacterRange(); + if (s4 === null) { + s4 = peg$parseclassCharacter(); } - r4 = r4 !== null ? r4 : ""; - if (r4 !== null) { - r5 = []; - r6 = parse_classCharacterRange(); - if (r6 === null) { - r6 = parse_classCharacter(); + while (s4 !== null) { + s3.push(s4); + s4 = peg$parseclassCharacterRange(); + if (s4 === null) { + s4 = peg$parseclassCharacter(); } - while (r6 !== null) { - r5.push(r6); - r6 = parse_classCharacterRange(); - if (r6 === null) { - r6 = parse_classCharacter(); - } + } + if (s3 !== null) { + if (input.charCodeAt(peg$currPos) === 93) { + s4 = peg$c92; + peg$currPos++; + } else { + s4 = null; + if (peg$silentFails === 0) { peg$fail(peg$c93); } } - if (r5 !== null) { - if (input.charCodeAt(pos) === 93) { - r6 = "]"; - pos++; + if (s4 !== null) { + if (input.charCodeAt(peg$currPos) === 105) { + s5 = peg$c73; + peg$currPos++; } else { - r6 = null; - if (reportFailures === 0) { - matchFailed("\"]\""); - } + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c74); } } - if (r6 !== null) { - if (input.charCodeAt(pos) === 105) { - r7 = "i"; - pos++; - } else { - r7 = null; - if (reportFailures === 0) { - matchFailed("\"i\""); - } - } - r7 = r7 !== null ? r7 : ""; - if (r7 !== null) { - r8 = parse___(); - if (r8 !== null) { - r0 = [r3, r4, r5, r6, r7, r8]; + if (s5 === null) { + s5 = peg$c1; + } + if (s5 !== null) { + s6 = peg$parse__(); + if (s6 !== null) { + peg$reportedPos = s0; + s1 = peg$c94(s2,s3,s5); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(inverted, parts, flags) { - var partsConverted = utils.map(parts, function(part) { return part.data; }); - var rawText = "[" - + inverted - + utils.map(parts, function(part) { return part.rawText; }).join("") - + "]" - + flags; - - return { - type: "class", - parts: partsConverted, - // FIXME: Get the raw text from the input directly. - rawText: rawText, - inverted: inverted === "^", - ignoreCase: flags === "i" - }; - })(r4, r5, r7); - } - if (r0 === null) { - pos = r1; - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("character class"); - } - return r0; - } - - function parse_classCharacterRange() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - r3 = parse_classCharacter(); - if (r3 !== null) { - if (input.charCodeAt(pos) === 45) { - r4 = "-"; - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"-\""); - } - } - if (r4 !== null) { - r5 = parse_classCharacter(); - if (r5 !== null) { - r0 = [r3, r4, r5]; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c87); } + } + + return s0; + } + + function peg$parseclassCharacterRange() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parseclassCharacter(); + if (s1 !== null) { + if (input.charCodeAt(peg$currPos) === 45) { + s2 = peg$c95; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c96); } + } + if (s2 !== null) { + s3 = peg$parseclassCharacter(); + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c97(s1,s3); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(begin, end) { - if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) { - throw new this.SyntaxError( - "Invalid character range: " + begin.rawText + "-" + end.rawText + "." - ); - } - - return { - data: [begin.data, end.data], - // FIXME: Get the raw text from the input directly. - rawText: begin.rawText + "-" + end.rawText - }; - })(r3, r5); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_classCharacter() { - var r0, r1; - - r1 = pos; - r0 = parse_bracketDelimitedCharacter(); - if (r0 !== null) { - reportedPos = r1; - r0 = (function(char_) { - return { - data: char_, - // FIXME: Get the raw text from the input directly. - rawText: utils.quoteForRegexpClass(char_) - }; - })(r0); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_bracketDelimitedCharacter() { - var r0; - - r0 = parse_simpleBracketDelimitedCharacter(); - if (r0 === null) { - r0 = parse_simpleEscapeSequence(); - if (r0 === null) { - r0 = parse_zeroEscapeSequence(); - if (r0 === null) { - r0 = parse_hexEscapeSequence(); - if (r0 === null) { - r0 = parse_unicodeEscapeSequence(); - if (r0 === null) { - r0 = parse_eolEscapeSequence(); - } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parseclassCharacter() { + var s0, s1; + + s0 = peg$currPos; + s1 = peg$parsebracketDelimitedCharacter(); + if (s1 !== null) { + peg$reportedPos = s0; + s1 = peg$c98(s1); + } + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; + } + + return s0; + } + + function peg$parsebracketDelimitedCharacter() { + var s0; + + s0 = peg$parsesimpleBracketDelimitedCharacter(); + if (s0 === null) { + s0 = peg$parsesimpleEscapeSequence(); + if (s0 === null) { + s0 = peg$parsezeroEscapeSequence(); + if (s0 === null) { + s0 = peg$parsehexEscapeSequence(); + if (s0 === null) { + s0 = peg$parseunicodeEscapeSequence(); + if (s0 === null) { + s0 = peg$parseeolEscapeSequence(); } } } } - return r0; } - - function parse_simpleBracketDelimitedCharacter() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - r4 = pos; - reportFailures++; - if (input.charCodeAt(pos) === 93) { - r3 = "]"; - pos++; + + return s0; + } + + function peg$parsesimpleBracketDelimitedCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 93) { + s2 = peg$c92; + peg$currPos++; + } else { + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c93); } + } + if (s2 === null) { + if (input.charCodeAt(peg$currPos) === 92) { + s2 = peg$c81; + peg$currPos++; } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"]\""); - } + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c82); } } - if (r3 === null) { - if (input.charCodeAt(pos) === 92) { - r3 = "\\"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\\""); - } - } - if (r3 === null) { - r3 = parse_eolChar(); - } + if (s2 === null) { + s2 = peg$parseeolChar(); } - reportFailures--; - if (r3 === null) { - r3 = ""; + } + peg$silentFails--; + if (s2 === null) { + s1 = peg$c1; + } else { + peg$currPos = s1; + s1 = peg$c0; + } + if (s1 !== null) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; } else { - r3 = null; - pos = r4; + s2 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } } - if (r3 !== null) { - if (input.length > pos) { - r4 = input.charAt(pos); - pos++; + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(char_) { return char_; })(r4); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_simpleEscapeSequence() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 92) { - r3 = "\\"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\\""); - } - } - if (r3 !== null) { - r5 = pos; - reportFailures++; - r4 = parse_digit(); - if (r4 === null) { - if (input.charCodeAt(pos) === 120) { - r4 = "x"; - pos++; + + return s0; + } + + function peg$parsesimpleEscapeSequence() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c81; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c82); } + } + if (s1 !== null) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsedigit(); + if (s3 === null) { + if (input.charCodeAt(peg$currPos) === 120) { + s3 = peg$c99; + peg$currPos++; + } else { + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c100); } + } + if (s3 === null) { + if (input.charCodeAt(peg$currPos) === 117) { + s3 = peg$c101; + peg$currPos++; } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"x\""); - } + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c102); } } - if (r4 === null) { - if (input.charCodeAt(pos) === 117) { - r4 = "u"; - pos++; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"u\""); - } - } - if (r4 === null) { - r4 = parse_eolChar(); - } + if (s3 === null) { + s3 = peg$parseeolChar(); } } - reportFailures--; - if (r4 === null) { - r4 = ""; + } + peg$silentFails--; + if (s3 === null) { + s2 = peg$c1; + } else { + peg$currPos = s2; + s2 = peg$c0; + } + if (s2 !== null) { + if (input.length > peg$currPos) { + s3 = input.charAt(peg$currPos); + peg$currPos++; } else { - r4 = null; - pos = r5; + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } } - if (r4 !== null) { - if (input.length > pos) { - r5 = input.charAt(pos); - pos++; - } else { - r5 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r5 !== null) { - r0 = [r3, r4, r5]; + if (s3 !== null) { + peg$reportedPos = s0; + s1 = peg$c103(s3); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(char_) { - return char_ - .replace("b", "\b") - .replace("f", "\f") - .replace("n", "\n") - .replace("r", "\r") - .replace("t", "\t") - .replace("v", "\x0B"); // IE does not recognize "\v". - })(r5); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_zeroEscapeSequence() { - var r0, r1, r2, r3, r4, r5; - - r1 = pos; - r2 = pos; - if (input.substr(pos, 2) === "\\0") { - r3 = "\\0"; - pos += 2; + + return s0; + } + + function peg$parsezeroEscapeSequence() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c104) { + s1 = peg$c104; + peg$currPos += 2; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c105); } + } + if (s1 !== null) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsedigit(); + peg$silentFails--; + if (s3 === null) { + s2 = peg$c1; } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\0\""); - } + peg$currPos = s2; + s2 = peg$c0; } - if (r3 !== null) { - r5 = pos; - reportFailures++; - r4 = parse_digit(); - reportFailures--; - if (r4 === null) { - r4 = ""; + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c106(); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r4 = null; - pos = r5; - } - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function() { return "\x00"; })(); - } - if (r0 === null) { - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_hexEscapeSequence() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8; - - r1 = pos; - r2 = pos; - if (input.substr(pos, 2) === "\\x") { - r3 = "\\x"; - pos += 2; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\x\""); - } - } - if (r3 !== null) { - r5 = pos; - r6 = pos; - r7 = parse_hexDigit(); - if (r7 !== null) { - r8 = parse_hexDigit(); - if (r8 !== null) { - r4 = [r7, r8]; - } else { - r4 = null; - pos = r6; - } + + return s0; + } + + function peg$parsehexEscapeSequence() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c107) { + s1 = peg$c107; + peg$currPos += 2; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c108); } + } + if (s1 !== null) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = peg$parsehexDigit(); + if (s4 !== null) { + s5 = peg$parsehexDigit(); + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; } else { - r4 = null; - pos = r6; - } - if (r4 !== null) { - r4 = input.substring(pos, r5); + peg$currPos = s3; + s3 = peg$c0; } - if (r4 !== null) { - r0 = [r3, r4]; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + if (s3 !== null) { + s3 = input.substring(s2, peg$currPos); + } + s2 = s3; + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c109(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; + peg$currPos = s0; + s0 = peg$c0; } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(digits) { - return String.fromCharCode(parseInt(digits, 16)); - })(r4); - } - if (r0 === null) { - pos = r1; - } - return r0; - } - - function parse_unicodeEscapeSequence() { - var r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10; - - r1 = pos; - r2 = pos; - if (input.substr(pos, 2) === "\\u") { - r3 = "\\u"; - pos += 2; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\u\""); - } - } - if (r3 !== null) { - r5 = pos; - r6 = pos; - r7 = parse_hexDigit(); - if (r7 !== null) { - r8 = parse_hexDigit(); - if (r8 !== null) { - r9 = parse_hexDigit(); - if (r9 !== null) { - r10 = parse_hexDigit(); - if (r10 !== null) { - r4 = [r7, r8, r9, r10]; - } else { - r4 = null; - pos = r6; - } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parseunicodeEscapeSequence() { + var s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c110) { + s1 = peg$c110; + peg$currPos += 2; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c111); } + } + if (s1 !== null) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = peg$parsehexDigit(); + if (s4 !== null) { + s5 = peg$parsehexDigit(); + if (s5 !== null) { + s6 = peg$parsehexDigit(); + if (s6 !== null) { + s7 = peg$parsehexDigit(); + if (s7 !== null) { + s4 = [s4, s5, s6, s7]; + s3 = s4; } else { - r4 = null; - pos = r6; + peg$currPos = s3; + s3 = peg$c0; } } else { - r4 = null; - pos = r6; + peg$currPos = s3; + s3 = peg$c0; } } else { - r4 = null; - pos = r6; - } - if (r4 !== null) { - r4 = input.substring(pos, r5); - } - if (r4 !== null) { - r0 = [r3, r4]; - } else { - r0 = null; - pos = r2; + peg$currPos = s3; + s3 = peg$c0; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(digits) { - return String.fromCharCode(parseInt(digits, 16)); - })(r4); - } - if (r0 === null) { - pos = r1; + peg$currPos = s3; + s3 = peg$c0; } - return r0; - } - - function parse_eolEscapeSequence() { - var r0, r1, r2, r3, r4; - - r1 = pos; - r2 = pos; - if (input.charCodeAt(pos) === 92) { - r3 = "\\"; - pos++; - } else { - r3 = null; - if (reportFailures === 0) { - matchFailed("\"\\\\\""); - } + if (s3 !== null) { + s3 = input.substring(s2, peg$currPos); } - if (r3 !== null) { - r4 = parse_eol(); - if (r4 !== null) { - r0 = [r3, r4]; + s2 = s3; + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c109(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; } else { - r0 = null; - pos = r2; + s0 = s1; } } else { - r0 = null; - pos = r2; - } - if (r0 !== null) { - reportedPos = r1; - r0 = (function(eol) { return eol; })(r4); + peg$currPos = s0; + s0 = peg$c0; } - if (r0 === null) { - pos = r1; - } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_digit() { - var r0; - - if (/^[0-9]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - return r0; + + return s0; + } + + function peg$parseeolEscapeSequence() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c81; + peg$currPos++; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c82); } } - - function parse_hexDigit() { - var r0; - - if (/^[0-9a-fA-F]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[0-9a-fA-F]"); + if (s1 !== null) { + s2 = peg$parseeol(); + if (s2 !== null) { + peg$reportedPos = s0; + s1 = peg$c112(s2); + if (s1 === null) { + peg$currPos = s0; + s0 = s1; + } else { + s0 = s1; } + } else { + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; } - - function parse_letter() { - var r0; - - r0 = parse_lowerCaseLetter(); - if (r0 === null) { - r0 = parse_upperCaseLetter(); - } - return r0; + + return s0; + } + + function peg$parsedigit() { + var s0; + + if (peg$c113.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c114); } } - - function parse_lowerCaseLetter() { - var r0; - - if (/^[a-z]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[a-z]"); - } - } - return r0; + + return s0; + } + + function peg$parsehexDigit() { + var s0; + + if (peg$c115.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c116); } } - - function parse_upperCaseLetter() { - var r0; - - if (/^[A-Z]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[A-Z]"); - } - } - return r0; + + return s0; + } + + function peg$parseletter() { + var s0; + + s0 = peg$parselowerCaseLetter(); + if (s0 === null) { + s0 = peg$parseupperCaseLetter(); + } + + return s0; + } + + function peg$parselowerCaseLetter() { + var s0; + + if (peg$c117.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c118); } } - - function parse___() { - var r0, r1; - - r0 = []; - r1 = parse_whitespace(); - if (r1 === null) { - r1 = parse_eol(); - if (r1 === null) { - r1 = parse_comment(); - } - } - while (r1 !== null) { - r0.push(r1); - r1 = parse_whitespace(); - if (r1 === null) { - r1 = parse_eol(); - if (r1 === null) { - r1 = parse_comment(); - } - } - } - return r0; - } - - function parse_comment() { - var r0; - - reportFailures++; - r0 = parse_singleLineComment(); - if (r0 === null) { - r0 = parse_multiLineComment(); - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("comment"); - } - return r0; - } - - function parse_singleLineComment() { - var r0, r1, r2, r3, r4, r5, r6, r7; - - r1 = pos; - if (input.substr(pos, 2) === "//") { - r2 = "//"; - pos += 2; - } else { - r2 = null; - if (reportFailures === 0) { - matchFailed("\"//\""); - } - } - if (r2 !== null) { - r3 = []; - r5 = pos; - r7 = pos; - reportFailures++; - r6 = parse_eolChar(); - reportFailures--; - if (r6 === null) { - r6 = ""; - } else { - r6 = null; - pos = r7; - } - if (r6 !== null) { - if (input.length > pos) { - r7 = input.charAt(pos); - pos++; - } else { - r7 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r7 !== null) { - r4 = [r6, r7]; - } else { - r4 = null; - pos = r5; - } - } else { - r4 = null; - pos = r5; - } - while (r4 !== null) { - r3.push(r4); - r5 = pos; - r7 = pos; - reportFailures++; - r6 = parse_eolChar(); - reportFailures--; - if (r6 === null) { - r6 = ""; + + return s0; + } + + function peg$parseupperCaseLetter() { + var s0; + + if (peg$c119.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c120); } + } + + return s0; + } + + function peg$parse__() { + var s0, s1; + + s0 = []; + s1 = peg$parsewhitespace(); + if (s1 === null) { + s1 = peg$parseeol(); + if (s1 === null) { + s1 = peg$parsecomment(); + } + } + while (s1 !== null) { + s0.push(s1); + s1 = peg$parsewhitespace(); + if (s1 === null) { + s1 = peg$parseeol(); + if (s1 === null) { + s1 = peg$parsecomment(); + } + } + } + + return s0; + } + + function peg$parsecomment() { + var s0, s1; + + peg$silentFails++; + s0 = peg$parsesingleLineComment(); + if (s0 === null) { + s0 = peg$parsemultiLineComment(); + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c121); } + } + + return s0; + } + + function peg$parsesingleLineComment() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c122) { + s1 = peg$c122; + peg$currPos += 2; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c123); } + } + if (s1 !== null) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = peg$parseeolChar(); + peg$silentFails--; + if (s5 === null) { + s4 = peg$c1; + } else { + peg$currPos = s4; + s4 = peg$c0; + } + if (s4 !== null) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } + } + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + } else { + peg$currPos = s3; + s3 = peg$c0; + } + while (s3 !== null) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = peg$parseeolChar(); + peg$silentFails--; + if (s5 === null) { + s4 = peg$c1; + } else { + peg$currPos = s4; + s4 = peg$c0; + } + if (s4 !== null) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; } else { - r6 = null; - pos = r7; + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } } - if (r6 !== null) { - if (input.length > pos) { - r7 = input.charAt(pos); - pos++; - } else { - r7 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r7 !== null) { - r4 = [r6, r7]; - } else { - r4 = null; - pos = r5; - } + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; } else { - r4 = null; - pos = r5; + peg$currPos = s3; + s3 = peg$c0; } - } - if (r3 !== null) { - r0 = [r2, r3]; } else { - r0 = null; - pos = r1; + peg$currPos = s3; + s3 = peg$c0; } - } else { - r0 = null; - pos = r1; } - return r0; - } - - function parse_multiLineComment() { - var r0, r1, r2, r3, r4, r5, r6, r7; - - r1 = pos; - if (input.substr(pos, 2) === "/*") { - r2 = "/*"; - pos += 2; + if (s2 !== null) { + s1 = [s1, s2]; + s0 = s1; } else { - r2 = null; - if (reportFailures === 0) { - matchFailed("\"/*\""); - } + peg$currPos = s0; + s0 = peg$c0; } - if (r2 !== null) { - r3 = []; - r5 = pos; - r7 = pos; - reportFailures++; - if (input.substr(pos, 2) === "*/") { - r6 = "*/"; - pos += 2; - } else { - r6 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - reportFailures--; - if (r6 === null) { - r6 = ""; - } else { - r6 = null; - pos = r7; - } - if (r6 !== null) { - if (input.length > pos) { - r7 = input.charAt(pos); - pos++; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parsemultiLineComment() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c124) { + s1 = peg$c124; + peg$currPos += 2; + } else { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c125); } + } + if (s1 !== null) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c126) { + s5 = peg$c126; + peg$currPos += 2; + } else { + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c127); } + } + peg$silentFails--; + if (s5 === null) { + s4 = peg$c1; + } else { + peg$currPos = s4; + s4 = peg$c0; + } + if (s4 !== null) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } + } + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$c0; + } + } else { + peg$currPos = s3; + s3 = peg$c0; + } + while (s3 !== null) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c126) { + s5 = peg$c126; + peg$currPos += 2; + } else { + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c127); } + } + peg$silentFails--; + if (s5 === null) { + s4 = peg$c1; + } else { + peg$currPos = s4; + s4 = peg$c0; + } + if (s4 !== null) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; } else { - r7 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } + s5 = null; + if (peg$silentFails === 0) { peg$fail(peg$c83); } } - if (r7 !== null) { - r4 = [r6, r7]; + if (s5 !== null) { + s4 = [s4, s5]; + s3 = s4; } else { - r4 = null; - pos = r5; + peg$currPos = s3; + s3 = peg$c0; } } else { - r4 = null; - pos = r5; - } - while (r4 !== null) { - r3.push(r4); - r5 = pos; - r7 = pos; - reportFailures++; - if (input.substr(pos, 2) === "*/") { - r6 = "*/"; - pos += 2; - } else { - r6 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - reportFailures--; - if (r6 === null) { - r6 = ""; - } else { - r6 = null; - pos = r7; - } - if (r6 !== null) { - if (input.length > pos) { - r7 = input.charAt(pos); - pos++; - } else { - r7 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (r7 !== null) { - r4 = [r6, r7]; - } else { - r4 = null; - pos = r5; - } - } else { - r4 = null; - pos = r5; - } + peg$currPos = s3; + s3 = peg$c0; } - if (r3 !== null) { - if (input.substr(pos, 2) === "*/") { - r4 = "*/"; - pos += 2; - } else { - r4 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - if (r4 !== null) { - r0 = [r2, r3, r4]; - } else { - r0 = null; - pos = r1; - } + } + if (s2 !== null) { + if (input.substr(peg$currPos, 2) === peg$c126) { + s3 = peg$c126; + peg$currPos += 2; + } else { + s3 = null; + if (peg$silentFails === 0) { peg$fail(peg$c127); } + } + if (s3 !== null) { + s1 = [s1, s2, s3]; + s0 = s1; } else { - r0 = null; - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } } else { - r0 = null; - pos = r1; + peg$currPos = s0; + s0 = peg$c0; } - return r0; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + + function peg$parseeol() { + var s0, s1; + + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 10) { + s0 = peg$c129; + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c130); } } - - function parse_eol() { - var r0; - - reportFailures++; - if (input.charCodeAt(pos) === 10) { - r0 = "\n"; - pos++; + if (s0 === null) { + if (input.substr(peg$currPos, 2) === peg$c131) { + s0 = peg$c131; + peg$currPos += 2; } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("\"\\n\""); - } + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c132); } } - if (r0 === null) { - if (input.substr(pos, 2) === "\r\n") { - r0 = "\r\n"; - pos += 2; + if (s0 === null) { + if (input.charCodeAt(peg$currPos) === 13) { + s0 = peg$c133; + peg$currPos++; } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("\"\\r\\n\""); - } + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c134); } } - if (r0 === null) { - if (input.charCodeAt(pos) === 13) { - r0 = "\r"; - pos++; + if (s0 === null) { + if (input.charCodeAt(peg$currPos) === 8232) { + s0 = peg$c135; + peg$currPos++; } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("\"\\r\""); - } + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c136); } } - if (r0 === null) { - if (input.charCodeAt(pos) === 8232) { - r0 = "\u2028"; - pos++; + if (s0 === null) { + if (input.charCodeAt(peg$currPos) === 8233) { + s0 = peg$c137; + peg$currPos++; } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("\"\\u2028\""); - } - } - if (r0 === null) { - if (input.charCodeAt(pos) === 8233) { - r0 = "\u2029"; - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("\"\\u2029\""); - } - } + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c138); } } } } } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("end of line"); - } - return r0; - } - - function parse_eolChar() { - var r0; - - if (/^[\n\r\u2028\u2029]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[\\n\\r\\u2028\\u2029]"); - } - } - return r0; - } - - function parse_whitespace() { - var r0; - - reportFailures++; - if (/^[ \t\x0B\f\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/.test(input.charAt(pos))) { - r0 = input.charAt(pos); - pos++; - } else { - r0 = null; - if (reportFailures === 0) { - matchFailed("[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]"); - } - } - reportFailures--; - if (reportFailures === 0 && r0 === null) { - matchFailed("whitespace"); - } - return r0; - } - - - function cleanupExpected(expected) { - expected.sort(); - - var lastExpected = null; - var cleanExpected = []; - for (var i = 0; i < expected.length; i++) { - if (expected[i] !== lastExpected) { - cleanExpected.push(expected[i]); - lastExpected = expected[i]; - } - } - return cleanExpected; - } - - - var utils = require("./utils"); - - - var result = parseFunctions[startRule](); - - /* - * The parser is now in one of the following three states: - * - * 1. The parser successfully parsed the whole input. - * - * - |result !== null| - * - |pos === input.length| - * - |rightmostFailuresExpected| may or may not contain something - * - * 2. The parser successfully parsed only a part of the input. - * - * - |result !== null| - * - |pos < input.length| - * - |rightmostFailuresExpected| may or may not contain something - * - * 3. The parser did not successfully parse any part of the input. - * - * - |result === null| - * - |pos === 0| - * - |rightmostFailuresExpected| contains at least one failure - * - * All code following this comment (including called functions) must - * handle these states. - */ - if (result === null || pos !== input.length) { - reportedPos = Math.max(pos, rightmostFailuresPos); - var found = reportedPos < input.length ? input.charAt(reportedPos) : null; - var reportedPosDetails = computeReportedPosDetails(); - - throw new this.SyntaxError( - cleanupExpected(rightmostFailuresExpected), - found, - reportedPos, - reportedPosDetails.line, - reportedPosDetails.column - ); - } - - return result; + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c128); } + } + + return s0; } - }; - - /* Thrown when a parser encounters a syntax error. */ - - result.SyntaxError = function(expected, found, offset, line, column) { - function buildMessage(expected, found) { - var expectedHumanized, foundHumanized; - - switch (expected.length) { - case 0: - expectedHumanized = "end of input"; - break; - case 1: - expectedHumanized = expected[0]; - break; - default: - expectedHumanized = expected.slice(0, expected.length - 1).join(", ") - + " or " - + expected[expected.length - 1]; + + function peg$parseeolChar() { + var s0; + + if (peg$c139.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c140); } } - - foundHumanized = found ? quote(found) : "end of input"; - - return "Expected " + expectedHumanized + " but " + foundHumanized + " found."; + + return s0; } - - this.name = "SyntaxError"; - this.expected = expected; - this.found = found; - this.message = buildMessage(expected, found); - this.offset = offset; - this.line = line; - this.column = column; + + function peg$parsewhitespace() { + var s0, s1; + + peg$silentFails++; + if (peg$c142.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = null; + if (peg$silentFails === 0) { peg$fail(peg$c143); } + } + peg$silentFails--; + if (s0 === null) { + s1 = null; + if (peg$silentFails === 0) { peg$fail(peg$c141); } + } + + return s0; + } + + + var utils = require("./utils"); + + + peg$result = peg$startRuleFunction(); + + if (peg$result !== null && peg$currPos === input.length) { + return peg$result; + } else { + peg$cleanupExpected(peg$maxFailExpected); + peg$reportedPos = Math.max(peg$currPos, peg$maxFailPos); + + throw new SyntaxError( + peg$maxFailExpected, + peg$reportedPos < input.length ? input.charAt(peg$reportedPos) : null, + peg$reportedPos, + peg$computePosDetails(peg$reportedPos).line, + peg$computePosDetails(peg$reportedPos).column + ); + } + } + + return { + SyntaxError: SyntaxError, + parse : parse }; - - subclass(result.SyntaxError, Error); - - return result; })(); diff --git a/lib/utils.js b/lib/utils.js index 18018f4..cf6aacc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -22,6 +22,16 @@ var utils = { } }, + indexOf: function(array, callback) { + var length = array.length; + for (var i = 0; i < length; i++) { + if (callback(array[i])) { + return i; + } + } + return -1; + }, + contains: function(array, value) { /* * Stupid IE does not have Array.prototype.indexOf, otherwise this function @@ -209,6 +219,10 @@ var utils = { findRuleByName: function(ast, name) { return utils.find(ast.rules, function(r) { return r.name === name; }); + }, + + indexOfRuleByName: function(ast, name) { + return utils.indexOf(ast.rules, function(r) { return r.name === name; }); } }; diff --git a/package.json b/package.json index c3e5523..bdd7a42 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,10 @@ "examples/javascript.pegjs", "examples/json.pegjs", "lib/compiler.js", + "lib/compiler/opcodes.js", "lib/compiler/passes.js", - "lib/compiler/passes/allocate-registers.js", - "lib/compiler/passes/generate-code.js", + "lib/compiler/passes/generate-bytecode.js", + "lib/compiler/passes/generate-javascript.js", "lib/compiler/passes/remove-proxy-rules.js", "lib/compiler/passes/report-left-recursion.js", "lib/compiler/passes/report-missing-rules.js", diff --git a/spec/compiler/passes/allocate-registers.spec.js b/spec/compiler/passes/allocate-registers.spec.js deleted file mode 100644 index 3e31621..0000000 --- a/spec/compiler/passes/allocate-registers.spec.js +++ /dev/null @@ -1,309 +0,0 @@ -describe("compiler pass |allocateRegisters|", function() { - var pass = PEG.compiler.passes.allocateRegisters; - - function ruleDetails(details) { return { rules: [details] }; } - - function expressionDetails(details) { - return ruleDetails({ expression: details }); - } - - function innerExpressionDetails(details) { - return expressionDetails({ expression: details }); - } - - var reuseResultDetails = innerExpressionDetails({ resultIndex: 0 }), - allocResultDetails = innerExpressionDetails({ resultIndex: 1 }), - savePosDetails = expressionDetails({ posIndex: 1 }), - scopedDetails = expressionDetails({ params: {} }), - blockedDetails = expressionDetails({ - elements: [ - {}, - { - resultIndex: 3, - posIndex: 5, - elements: [{ resultIndex: 6 }, { resultIndex: 7 }] - } - ] - }), - unblockedDetails = expressionDetails({ - elements: [ - {}, - { - resultIndex: 3, - posIndex: 4, - elements: [{ resultIndex: 5 }, { resultIndex: 6 }] - } - ] - }); - - describe("for rule", function() { - it("allocates a new result register for the expression", function() { - expect(pass).toChangeAST('start = "a"', expressionDetails({ - resultIndex: 0 - })); - }); - - it("counts used registers", function() { - expect(pass).toChangeAST('start = "a"', ruleDetails({ - registerCount: 1 - })); - expect(pass).toChangeAST('start = "a"*', ruleDetails({ - registerCount: 2 - })); - expect(pass).toChangeAST('start = ("a"*)*', ruleDetails({ - registerCount: 3 - })); - }); - - it("resets used registers counter", function() { - expect(pass).toChangeAST('a = "a"*; b = "b"', { - rules: [ { registerCount: 2 }, { registerCount: 1 }] - }); - }); - }); - - describe("for named", function() { - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start "start" = "a"', reuseResultDetails); - }); - }); - - describe("for choice", function() { - it("reuses its own result register for the alternatives", function() { - expect(pass).toChangeAST('start = "a" / "b" / "c"', expressionDetails({ - alternatives: [ - { resultIndex: 0 }, - { resultIndex: 0 }, - { resultIndex: 0 } - ] - })); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = (a:"a" / "b" / "c") { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ((a:"a" / "b" / "c") "d") ("e" "f")', - unblockedDetails - ); - }); - }); - - describe("for action", function() { - it("allocates a position register", function() { - expect(pass).toChangeAST('start = "a" { }', savePosDetails); - }); - - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = "a" { }', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = (a:"a" { }) { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ((a:"a" { }) "b") ("c" "d")', - unblockedDetails - ); - }); - - it("computes params", function() { - expect(pass).toChangeAST('start = a:"a" b:"b" c:"c" { }', expressionDetails({ - params: { a: 3, b: 4, c: 5 } - })); - }); - }); - - describe("for sequence", function() { - it("allocates a position register", function() { - expect(pass).toChangeAST('start = ', savePosDetails); - expect(pass).toChangeAST('start = "a" "b" "c"', savePosDetails); - }); - - it("allocates new result registers for the elements", function() { - expect(pass).toChangeAST('start = "a" "b" "c"', expressionDetails({ - elements: [{ resultIndex: 2 }, { resultIndex: 3 }, { resultIndex: 4 }] - })); - }); - - it("does not create a new scope", function() { - expect(pass).toChangeAST( - 'start = a:"a" b:"b" c:"c" { }', - expressionDetails({ params: { a: 3, b: 4, c: 5 } }) - ); - }); - - it("does not unblock blocked result registers from children", function() { - expect(pass).toChangeAST( - 'start = (a:"a" "b") ("c" "d")', - blockedDetails - ); - }); - }); - - describe("for labeled", function() { - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = a:"a"', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = a:(b:"b") { }', expressionDetails({ - params: { a: 0 } - })); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = (a:(b:"b") "c") ("d" "e")', - blockedDetails - ); - }); - - it("adds label to the environment", function() { - expect(pass).toChangeAST('start = a:"a" { }', expressionDetails({ - params: { a: 0 } - })); - }); - - it("blocks its own result register", function() { - expect(pass).toChangeAST( - 'start = (a:"a" "b") ("c" "d")', - blockedDetails - ); - }); - }); - - describe("for text", function() { - it("allocates a position register", function() { - expect(pass).toChangeAST('start = $"a"', savePosDetails); - }); - - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = $"a"', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = $(a:"a") { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ($(a:"a") "b") ("c" "d")', - unblockedDetails - ); - }); - }); - - describe("for simple and", function() { - it("allocates a position register", function() { - expect(pass).toChangeAST('start = &"a"', savePosDetails); - }); - - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = &"a"', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = &(a:"a") { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = (&(a:"a") "b") ("c" "d")', - unblockedDetails - ); - }); - }); - - describe("for simple not", function() { - it("allocates a position register", function() { - expect(pass).toChangeAST('start = !"a"', savePosDetails); - }); - - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = !"a"', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = !(a:"a") { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = (!(a:"a") "b") ("c" "d")', - unblockedDetails - ); - }); - }); - - describe("for semantic and", function() { - it("computes params", function() { - expect(pass).toChangeAST('start = a:"a" b:"b" c:"c" &{ }', expressionDetails({ - elements: [{}, {}, {}, { params: { a: 2, b: 3, c: 4 } }] - })); - }); - }); - - describe("for semantic not", function() { - it("computes params", function() { - expect(pass).toChangeAST('start = a:"a" b:"b" c:"c" !{ }', expressionDetails({ - elements: [{}, {}, {}, { params: { a: 2, b: 3, c: 4 } }] - })); - }); - }); - - describe("for optional", function() { - it("reuses its own result register for the expression", function() { - expect(pass).toChangeAST('start = "a"?', reuseResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = (a:"a")? { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ((a:"a")? "b") ("c" "d")', - unblockedDetails - ); - }); - }); - - describe("for zero or more", function() { - it("allocates a new result register for the expression", function() { - expect(pass).toChangeAST('start = "a"*', allocResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = (a:"a")* { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ((a:"a")* "b") ("c" "d")', - unblockedDetails - ); - }); - }); - - describe("for one or more", function() { - it("allocates a new result register for the expression", function() { - expect(pass).toChangeAST('start = "a"+', allocResultDetails); - }); - - it("creates a new scope", function() { - expect(pass).toChangeAST('start = (a:"a")+ { }', scopedDetails); - }); - - it("unblocks registers blocked by its children", function() { - expect(pass).toChangeAST( - 'start = ((a:"a")+ "b") ("c" "d")', - unblockedDetails - ); - }); - }); -}); diff --git a/spec/compiler/passes/generate-bytecode.spec.js b/spec/compiler/passes/generate-bytecode.spec.js new file mode 100644 index 0000000..8b58dec --- /dev/null +++ b/spec/compiler/passes/generate-bytecode.spec.js @@ -0,0 +1,674 @@ +describe("compiler pass |generateBytecode|", function() { + var pass = PEG.compiler.passes.generateBytecode; + + function bytecodeDetails(bytecode) { + return { + rules: [{ bytecode: bytecode }] + }; + } + + function constsDetails(consts) { return { consts: consts }; } + + describe("for grammar", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST([ + 'a = "a"', + 'b = "b"', + 'c = "c"' + ].join("\n"), { + rules: [ + { bytecode: [15, 0, 2, 2, 19, 0, 20, 1] }, + { bytecode: [15, 2, 2, 2, 19, 2, 20, 3] }, + { bytecode: [15, 4, 2, 2, 19, 4, 20, 5] } + ] + }); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST([ + 'a = "a"', + 'b = "b"', + 'c = "c"' + ].join("\n"), constsDetails([ + '"a"', + '"\\"a\\""', + '"b"', + '"\\"b\\""', + '"c"', + '"\\"c\\""' + ])); + }); + }); + + describe("for rule", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST('start = "a"', bytecodeDetails([ + 15, 0, 2, 2, 19, 0, 20, 1 // + ])); + }); + }); + + describe("for named", function() { + var grammar = 'start "start" = "a"'; + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 25, // SILENT_FAILS_ON + 15, 1, 2, 2, 19, 1, 20, 2, // + 26, // SILENT_FAILS_OFF + 11, 2, 0, // IF_ERROR + 20, 0 // FAIL + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['"start"', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for choice", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST('start = "a" / "b" / "c"', bytecodeDetails([ + 15, 0, 2, 2, 19, 0, 20, 1, // + 11, 21, 0, // IF_ERROR + 2, // * POP + 15, 2, 2, 2, 19, 2, 20, 3, // + 11, 9, 0, // IF_ERROR + 2, // * POP + 15, 4, 2, 2, 19, 4, 20, 5 // + ])); + }); + }); + + describe("for action", function() { + describe("without labels", function() { + var grammar = 'start = { code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 0, 0, // PUSH + 12, 6, 0, // IF_NOT_ERROR + 21, 1, // * REPORT_SAVED_POS + 23, 1, 1, 0, // CALL + 11, 1, 1, // IF_ERROR + 6, // * NIP_CURR_POS + 5 // * NIP + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['[]', 'function() { code }']) + ); + }); + }); + + describe("with one label", function() { + var grammar = 'start = a:"a" { code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 0, 2, 2, 19, 0, 20, 1, // + 12, 7, 0, // IF_NOT_ERROR + 21, 1, // * REPORT_SAVED_POS + 23, 2, 1, 1, 0, // CALL + 11, 1, 1, // IF_ERROR + 6, // * NIP_CURR_POS + 5 // * NIP + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['"a"', '"\\"a\\""', 'function(a) { code }']) + ); + }); + }); + + describe("with multiple labels", function() { + var grammar = 'start = a:"a" b:"b" c:"c" { code }'; + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 1, 2, 2, 19, 1, 20, 2, // + 12, 46, 4, // IF_NOT_ERROR + 15, 3, 2, 2, 19, 3, 20, 4, // * + 12, 30, 5, // IF_NOT_ERROR + 15, 5, 2, 2, 19, 5, 20, 6, // * + 12, 14, 5, // IF_NOT_ERROR + 21, 3, // * REPORT_SAVED_POS + 23, 7, 3, 3, 2, 1, 0, // CALL + 11, 1, 1, // IF_ERROR + 6, // * NIP_CURR_POS + 5, // * NIP + 4, 3, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 2, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 2, // * POP + 3, // POP_CURR_POS + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails([ + 'null', + '"a"', + '"\\"a\\""', + '"b"', + '"\\"b\\""', + '"c"', + '"\\"c\\""', + 'function(a, b, c) { code }' + ])); + }); + }); + }); + + describe("for sequence", function() { + describe("empty", function() { + var grammar = 'start = '; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails(['[]'])); + }); + }); + + describe("non-empty", function() { + var grammar = 'start = "a" "b" "c"'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 1, 2, 2, 19, 1, 20, 2, // + 12, 35, 4, // IF_NOT_ERROR + 15, 3, 2, 2, 19, 3, 20, 4, // * + 12, 19, 5, // IF_NOT_ERROR + 15, 5, 2, 2, 19, 5, 20, 6, // * + 12, 3, 5, // IF_NOT_ERROR + 8, 3, // * WRAP + 5, // NIP + 4, 3, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 2, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 2, // * POP + 3, // POP_CURR_POS + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails([ + 'null', + '"a"', + '"\\"a\\""', + '"b"', + '"\\"b\\""', + '"c"', + '"\\"c\\""' + ]) + ); + }); + }); + }); + + describe("for labeled", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST('start = a:"a"', bytecodeDetails([ + 15, 0, 2, 2, 19, 0, 20, 1 // + ])); + }); + }); + + describe("for text", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST('start = $"a"', bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 0, 2, 2, 19, 0, 20, 1, // + 12, 1, 0, // IF_NOT_ERROR + 9, // * TEXT + 5 // NIP + ])); + }); + }); + + describe("for simple and", function() { + var grammar = 'start = &"a"'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 25, // SILENT_FAILS_ON + 15, 2, 2, 2, 19, 2, 20, 3, // + 26, // SILENT_FAILS_OFF + 12, 4, 4, // IF_NOT_ERROR + 2, // * POP + 3, // POP_CURR_POS + 0, 0, // PUSH + 2, // * POP + 2, // POP + 0, 1 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['""', 'null', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for simple not", function() { + var grammar = 'start = !"a"'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 25, // SILENT_FAILS_ON + 15, 2, 2, 2, 19, 2, 20, 3, // + 26, // SILENT_FAILS_OFF + 11, 4, 4, // IF_ERROR + 2, // * POP + 2, // POP + 0, 0, // PUSH + 2, // * POP + 3, // POP_CURR_POS + 0, 1 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['""', 'null', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for semantic and", function() { + describe("without labels", function() { + var grammar = 'start = &{ code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 22, // REPORT_CURR_POS + 23, 0, 0, 0, // CALL + 10, 3, 3, // IF + 2, // * POP + 0, 1, // PUSH + 2, // * POP + 0, 2 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['function() { code }', '""', 'null']) + ); + }); + }); + + describe("with labels", function() { + var grammar = 'start = a:"a" b:"b" c:"c" &{ code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 1, 2, 2, 19, 1, 20, 2, // + 12, 60, 4, // IF_NOT_ERROR + 15, 3, 2, 2, 19, 3, 20, 4, // * + 12, 44, 5, // IF_NOT_ERROR + 15, 5, 2, 2, 19, 5, 20, 6, // * + 12, 28, 5, // IF_NOT_ERROR + 22, // * REPORT_CURR_POS + 23, 7, 0, 3, 2, 1, 0, // CALL + 10, 3, 3, // IF + 2, // * POP + 0, 8, // PUSH + 2, // * POP + 0, 0, // PUSH + 12, 3, 5, // IF_NOT_ERROR + 8, 4, // * WRAP + 5, // NIP + 4, 4, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 3, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 2, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 2, // * POP + 3, // POP_CURR_POS + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails([ + 'null', + '"a"', + '"\\"a\\""', + '"b"', + '"\\"b\\""', + '"c"', + '"\\"c\\""', + 'function(a, b, c) { code }', + '""' + ])); + }); + }); + }); + + describe("for semantic not", function() { + describe("without labels", function() { + var grammar = 'start = !{ code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 22, // REPORT_CURR_POS + 23, 0, 0, 0, // CALL_PREDICATE + 10, 3, 3, // IF + 2, // * POP + 0, 2, // PUSH + 2, // * POP + 0, 1 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['function() { code }', '""', 'null']) + ); + }); + }); + + describe("with labels", function() { + var grammar = 'start = a:"a" b:"b" c:"c" !{ code }'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 1, // PUSH_CURR_POS + 15, 1, 2, 2, 19, 1, 20, 2, // + 12, 60, 4, // IF_NOT_ERROR + 15, 3, 2, 2, 19, 3, 20, 4, // * + 12, 44, 5, // IF_NOT_ERROR + 15, 5, 2, 2, 19, 5, 20, 6, // * + 12, 28, 5, // IF_NOT_ERROR + 22, // * REPORT_CURR_POS + 23, 7, 0, 3, 2, 1, 0, // CALL + 10, 3, 3, // IF + 2, // * POP + 0, 0, // PUSH + 2, // * POP + 0, 8, // PUSH + 12, 3, 5, // IF_NOT_ERROR + 8, 4, // * WRAP + 5, // NIP + 4, 4, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 3, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 4, 2, // * POP_N + 3, // POP_CURR_POS + 0, 0, // PUSH + 2, // * POP + 3, // POP_CURR_POS + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails([ + 'null', + '"a"', + '"\\"a\\""', + '"b"', + '"\\"b\\""', + '"c"', + '"\\"c\\""', + 'function(a, b, c) { code }', + '""' + ])); + }); + }); + }); + + describe("for optional", function() { + var grammar = 'start = "a"?'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 15, 1, 2, 2, 19, 1, 20, 2, // + 11, 3, 0, // IF_ERROR + 2, // * POP + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['""', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for zero or more", function() { + var grammar = 'start = "a"*'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 0, 0, // PUSH + 15, 1, 2, 2, 19, 1, 20, 2, // + 13, 9, // WHILE_NOT_ERROR + 7, // * APPEND + 15, 1, 2, 2, 19, 1, 20, 2, // + 2 // POP + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['[]', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for one or more", function() { + var grammar = 'start = "a"+'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 0, 0, // PUSH + 15, 2, 2, 2, 19, 2, 20, 3, // + 12, 12, 4, // IF_NOT_ERROR + 13, 9, // * WHILE_NOT_ERROR + 7, // * APPEND + 15, 2, 2, 2, 19, 2, 20, 3, // + 2, // POP + 2, // * POP + 2, // POP + 0, 1 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST( + grammar, + constsDetails(['[]', 'null', '"a"', '"\\"a\\""']) + ); + }); + }); + + describe("for rule reference", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST([ + 'start = other', + 'other = "other"' + ].join("\n"), { + rules: [ + { + bytecode: [24, 1] // RULE + }, + { } + ] + }); + }); + }); + + describe("for literal", function() { + describe("empty", function() { + var grammar = 'start = ""'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 0, 0 // PUSH + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails(['""'])); + }); + }); + + describe("non-empty case-sensitive", function() { + var grammar = 'start = "a"'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 15, 0, 2, 2, // MATCH_STRING + 19, 0, // * ACCEPT_STRING + 20, 1 // * FAIL + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails(['"a"', '"\\"a\\""'])); + }); + }); + + describe("non-empty case-insensitive", function() { + var grammar = 'start = "A"i'; + + it("generates correct bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 16, 0, 2, 2, // MATCH_STRING_IC + 18, 1, // * ACCEPT_N + 20, 1 // * FAIL + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails(['"a"', '"\\"A\\""'])); + }); + }); + }); + + describe("for class", function() { + it("generates correct bytecode", function() { + expect(pass).toChangeAST('start = [a]', bytecodeDetails([ + 17, 0, 2, 2, // MATCH_REGEXP + 18, 1, // * ACCEPT_N + 20, 1 // * FAIL + ])); + }); + + describe("non-empty non-inverted case-sensitive", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = [a]', + constsDetails(['/^[a]/', '"[a]"']) + ); + }); + }); + + describe("non-empty inverted case-sensitive", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = [^a]', + constsDetails(['/^[^a]/', '"[^a]"']) + ); + }); + }); + + describe("non-empty non-inverted case-insensitive", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = [a]i', + constsDetails(['/^[a]/i', '"[a]i"']) + ); + }); + }); + + describe("non-empty complex", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = [ab-def-hij-l]', + constsDetails(['/^[ab-def-hij-l]/', '"[ab-def-hij-l]"']) + ); + }); + }); + + describe("empty non-inverted", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = []', + constsDetails(['/^(?!)/', '"[]"']) + ); + }); + }); + + describe("empty inverted", function() { + it("defines correct constants", function() { + expect(pass).toChangeAST( + 'start = [^]', + constsDetails(['/^[\\S\\s]/', '"[^]"']) + ); + }); + }); + }); + + describe("for any", function() { + var grammar = 'start = .'; + + it("generates bytecode", function() { + expect(pass).toChangeAST(grammar, bytecodeDetails([ + 14, 2, 2, // MATCH_ANY + 18, 1, // * ACCEPT_N + 20, 0 // * FAIL + ])); + }); + + it("defines correct constants", function() { + expect(pass).toChangeAST(grammar, constsDetails(['"any character"'])); + }); + }); +}); diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index 7c38a47..a904549 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -1,6 +1,9 @@ describe("generated parser", function() { function vary(names, block) { - var values = { cache: [false, true] }; + var values = { + cache: [false, true], + optimize: ["speed", "size"] + }; function varyStep(names, options) { var clonedOptions = {}, key, name, i; @@ -32,7 +35,7 @@ describe("generated parser", function() { } function varyAll(block) { - vary(["cache"], block); + vary(["cache", "optimize"], block); } beforeEach(function() { diff --git a/spec/index.html b/spec/index.html index ac58f7b..c1b2b25 100644 --- a/spec/index.html +++ b/spec/index.html @@ -12,7 +12,7 @@ - +