From 3333cdd18d702e257ddefb90e9230397beef91fc Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 1 Dec 2012 13:10:50 +0100 Subject: [PATCH] Position tracking: Kill the |trackLineAndColumn| option Getting rid of the |trackLineAndColumn| simplifies the code generator (by unifying two paths in the code). The |line| and |column| functions currently always compute all the position info from scratch, which is horribly ineffective. This will be improved in later commit(s). --- README.md | 25 +--- benchmark/index.css | 2 +- benchmark/index.html | 2 - benchmark/index.js | 7 +- benchmark/run | 7 +- bin/pegjs | 6 - lib/compiler/passes/generate-code.js | 215 +++++++++------------------ lib/parser.js | 14 +- spec/generated-parser.spec.js | 127 ++++++++-------- 9 files changed, 156 insertions(+), 249 deletions(-) diff --git a/README.md b/README.md index a860d22..ca4b6e7 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,6 @@ You can tweak the generated parser with several options: * `--cache` — makes the parser cache results, avoiding exponential parsing time in pathological cases but making the parser slower - * `--track-line-and-column` — makes the parser track line and column - (available as `line` and `column` variables in the actions and predicates) * `--allowed-start-rules` — comma-separated list of rules the parser will be allowed to start parsing from (default: the first rule in the grammar) @@ -105,9 +103,6 @@ object to `PEG.buildParser`. The following options are supported: * `cache` — if `true`, makes the parser cache results, avoiding exponential parsing time in pathological cases but making the parser slower (default: `false`) - * `trackLineAndColumn` — if `true`, makes the parser track line and column - (available as `line` and `column` variables in the actions and predicates) - (default: `false`) * `allowedStartRules` — rules the parser will be allowed to start parsing from (default: the first rule in the grammar) * `output` — if set to `"parser"`, the method will return generated parser @@ -289,10 +284,8 @@ the initializer at the beginning of the grammar. The code inside the predicate can also access the current parse position using the `offset` function. It returns a zero-based character index into the input -string. If the `trackLineAndColumn` option was set to `true` when the parser was -generated (or `--track-line-and-column` was used on the command line), the code -can also access the current line and column using the `line` and `column` -functions. Both return one-based indexes. +string. The code can also access the current line and column using the `line` +and `column` functions. Both return one-based indexes. The code inside the predicate can also access options passed to the parser using the `options` variable. @@ -313,10 +306,8 @@ the initializer at the beginning of the grammar. The code inside the predicate can also access the current parse position using the `offset` function. It returns a zero-based character index into the input -string. If the `trackLineAndColumn` option was set to `true` when the parser was -generated (or `--track-line-and-column` was used on the command line), the code -can also access the current line and column using the `line` and `column` -functions. Both return one-based indexes. +string. The code can also access the current line and column using the `line` +and `column` functions. Both return one-based indexes. The code inside the predicate can also access options passed to the parser using the `options` variable. @@ -352,11 +343,9 @@ must be balanced. The code inside the action can also access the parse position at the beginning of the action's expression using the `offset` function. It returns a zero-based -character index into the input string. If the `trackLineAndColumn` option was -set to `true` when the parser was generated (or `--track-line-and-column` was -used on the command line), the code can also access the line and column at the -beginning of the action's expression using the `line` and `column` functions. -Both return one-based indexes. +character index into the input string. The code can also access the line and +column at the beginning of the action's expression using the `line` and `column` +functions. Both return one-based indexes. The code inside the action can also access options passed to the parser using the `options` variable. diff --git a/benchmark/index.css b/benchmark/index.css index 9a4b378..9156969 100644 --- a/benchmark/index.css +++ b/benchmark/index.css @@ -30,5 +30,5 @@ a, a:visited { color: #3d586c; } background-color: #f0f0f0; } #options #run-count { width: 3em; } -#options #cache, #options #track-line-and-column { margin-left: 2em; } +#options #cache { margin-left: 2em; } #options #run { width: 5em; margin-left: 2em; } diff --git a/benchmark/index.html b/benchmark/index.html index a8633cc..5c81418 100644 --- a/benchmark/index.html +++ b/benchmark/index.html @@ -13,8 +13,6 @@ times - - diff --git a/benchmark/index.js b/benchmark/index.js index a31db56..874f196 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -63,8 +63,7 @@ $("#run").click(function() { } var options = { - cache: $("#cache").is(":checked"), - trackLineAndColumn: $("#track-line-and-column").is(":checked") + cache: $("#cache").is(":checked"), }; Runner.run(benchmarks, runCount, options, { @@ -106,7 +105,7 @@ $("#run").click(function() { }, start: function() { - $("#run-count, #cache, #track-line-and-column, #run").attr("disabled", "disabled"); + $("#run-count, #cache, #run").attr("disabled", "disabled"); resultsTable.show(); $("#results-table tr").slice(1).remove(); @@ -123,7 +122,7 @@ $("#run").click(function() { $.scrollTo("max", { axis: "y", duration: 500 }); - $("#run-count, #cache, #track-line-and-column, #run").removeAttr("disabled"); + $("#run-count, #cache, #run").removeAttr("disabled"); } }); diff --git a/benchmark/run b/benchmark/run index 48e9c93..1882fb1 100755 --- a/benchmark/run +++ b/benchmark/run @@ -81,7 +81,6 @@ 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(" --track-line-and-column make tested parsers track line and column"); } function exitSuccess() { @@ -112,7 +111,7 @@ function nextArg() { /* Main */ var runCount = 10; -var options = { trackLineAndColumn: false }; +var options = { }; while (args.length > 0 && isOption(args[0])) { switch (args[0]) { @@ -132,10 +131,6 @@ while (args.length > 0 && isOption(args[0])) { options.cache = true; break; - case "--track-line-and-column": - options.trackLineAndColumn = true; - break; - case "-h": case "--help": printHelp(); diff --git a/bin/pegjs b/bin/pegjs index 4fe30ab..3c1421e 100755 --- a/bin/pegjs +++ b/bin/pegjs @@ -25,7 +25,6 @@ function printHelp() { util.puts(" object will be stored (default:"); util.puts(" \"module.exports\")"); util.puts(" --cache make generated parser cache results"); - util.puts(" --track-line-and-column make generated parser track line and column"); util.puts(" --allowed-start-rules comma-separated list of rules the generated"); util.puts(" parser will be allowed to start parsing"); util.puts(" from (default: the first rule in the"); @@ -73,7 +72,6 @@ function readStream(inputStream, callback) { var exportVar = "module.exports"; var options = { cache: false, - trackLineAndColumn: false, output: "source" }; @@ -92,10 +90,6 @@ while (args.length > 0 && isOption(args[0])) { options.cache = true; break; - case "--track-line-and-column": - options.trackLineAndColumn = true; - break; - case "--allowed-start-rules": nextArg(); if (args.length === 0) { diff --git a/lib/compiler/passes/generate-code.js b/lib/compiler/passes/generate-code.js index 8bf93de..3faf8f2 100644 --- a/lib/compiler/passes/generate-code.js +++ b/lib/compiler/passes/generate-code.js @@ -5,7 +5,6 @@ module.exports = function(ast, options) { options = utils.clone(options); utils.defaults(options, { cache: false, - trackLineAndColumn: false, allowedStartRules: [ast.startRule] }); @@ -333,10 +332,10 @@ module.exports = function(ast, options) { ' startRule = #{string(options.allowedStartRules[0])};', ' }', ' ', - ' #{posInit("pos")};', - ' #{posInit("reportedPos")};', + ' var pos = 0;', + ' var reportedPos = 0;', ' var reportFailures = 0;', // 0 = report, anything > 0 = do not report - ' #{posInit("rightmostFailuresPos")};', + ' var rightmostFailuresPos = 0;', ' var rightmostFailuresExpected = [];', ' #if options.cache', ' var cache = {};', @@ -372,56 +371,24 @@ module.exports = function(ast, options) { ' }', ' ', ' function offset() {', - ' return #{posOffset("reportedPos")};', + ' return reportedPos;', + ' }', + ' ', + ' function line() {', + ' return computePosDetails(reportedPos).line;', + ' }', + ' ', + ' function column() {', + ' return computePosDetails(reportedPos).column;', ' }', ' ', - ' #if options.trackLineAndColumn', - ' function line() {', - ' return reportedPos.line;', - ' }', - ' ', - ' function column() {', - ' return reportedPos.column;', - ' }', - ' ', - ' function clone(object) {', - ' var result = {};', - ' for (var key in object) {', - ' result[key] = object[key];', - ' }', - ' return result;', - ' }', - ' ', - ' function advance(pos, n) {', - ' var endOffset = pos.offset + n;', - ' ', - ' for (var offset = pos.offset; offset < endOffset; offset++) {', - ' var ch = input.charAt(offset);', - ' if (ch === "\\n") {', - ' if (!pos.seenCR) { pos.line++; }', - ' pos.column = 1;', - ' pos.seenCR = false;', - ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', - ' pos.line++;', - ' pos.column = 1;', - ' pos.seenCR = true;', - ' } else {', - ' pos.column++;', - ' pos.seenCR = false;', - ' }', - ' }', - ' ', - ' pos.offset += n;', - ' }', - ' ', - ' #end', ' function matchFailed(failure) {', - ' if (#{posOffset("pos")} < #{posOffset("rightmostFailuresPos")}) {', + ' if (pos < rightmostFailuresPos) {', ' return;', ' }', ' ', - ' if (#{posOffset("pos")} > #{posOffset("rightmostFailuresPos")}) {', - ' rightmostFailuresPos = #{posClone("pos")};', + ' if (pos > rightmostFailuresPos) {', + ' rightmostFailuresPos = pos;', ' rightmostFailuresExpected = [];', ' }', ' ', @@ -447,38 +414,36 @@ module.exports = function(ast, options) { ' return cleanExpected;', ' }', ' ', - ' #if !options.trackLineAndColumn', - ' function computeErrorPosition() {', - ' /*', - ' * The first idea was to use |String.split| to break the input up to the', - ' * error position along newlines and derive the line and column from', - ' * there. However IE\'s |split| implementation is so broken that it was', - ' * enough to prevent it.', - ' */', - ' ', - ' var line = 1;', - ' var column = 1;', - ' var seenCR = false;', - ' ', - ' for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {', - ' var ch = input.charAt(i);', - ' if (ch === "\\n") {', - ' if (!seenCR) { line++; }', - ' column = 1;', - ' seenCR = false;', - ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', - ' line++;', - ' column = 1;', - ' seenCR = true;', - ' } else {', - ' column++;', - ' seenCR = false;', - ' }', + ' function computePosDetails(pos) {', + ' /*', + ' * The first idea was to use |String.split| to break the input up to the', + ' * error position along newlines and derive the line and column from', + ' * there. However IE\'s |split| implementation is so broken that it was', + ' * enough to prevent it.', + ' */', + ' ', + ' var line = 1;', + ' var column = 1;', + ' var seenCR = false;', + ' ', + ' for (var i = 0; i < pos; i++) {', + ' var ch = input.charAt(i);', + ' if (ch === "\\n") {', + ' if (!seenCR) { line++; }', + ' column = 1;', + ' seenCR = false;', + ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', + ' line++;', + ' column = 1;', + ' seenCR = true;', + ' } else {', + ' column++;', + ' seenCR = false;', ' }', - ' ', - ' return { line: line, column: column };', ' }', - ' #end', + ' ', + ' return { line: line, column: column };', + ' }', ' ', ' #if node.initializer', ' #block emit(node.initializer)', @@ -492,32 +457,28 @@ module.exports = function(ast, options) { ' * 1. The parser successfully parsed the whole input.', ' *', ' * - |result !== null|', - ' * - |#{posOffset("pos")} === input.length|', + ' * - |pos === input.length|', ' * - |rightmostFailuresExpected| may or may not contain something', ' *', ' * 2. The parser successfully parsed only a part of the input.', ' *', ' * - |result !== null|', - ' * - |#{posOffset("pos")} < input.length|', + ' * - |pos < input.length|', ' * - |rightmostFailuresExpected| may or may not contain something', ' *', ' * 3. The parser did not successfully parse any part of the input.', ' *', ' * - |result === null|', - ' * - |#{posOffset("pos")} === 0|', + ' * - |pos === 0|', ' * - |rightmostFailuresExpected| contains at least one failure', ' *', ' * All code following this comment (including called functions) must', ' * handle these states.', ' */', - ' if (result === null || #{posOffset("pos")} !== input.length) {', - ' var offset = Math.max(#{posOffset("pos")}, #{posOffset("rightmostFailuresPos")});', + ' if (result === null || pos !== input.length) {', + ' var offset = Math.max(pos, rightmostFailuresPos);', ' var found = offset < input.length ? input.charAt(offset) : null;', - ' #if options.trackLineAndColumn', - ' var errorPosition = #{posOffset("pos")} > #{posOffset("rightmostFailuresPos")} ? pos : rightmostFailuresPos;', - ' #else', - ' var errorPosition = computeErrorPosition();', - ' #end', + ' var errorPosition = computePosDetails(Math.max(pos, rightmostFailuresPos));', ' ', ' throw new this.SyntaxError(', ' cleanupExpected(rightmostFailuresExpected),', @@ -573,10 +534,10 @@ module.exports = function(ast, options) { rule: [ 'function parse_#{node.name}() {', ' #if options.cache', - ' var cacheKey = "#{node.name}@" + #{posOffset("pos")};', + ' var cacheKey = "#{node.name}@" + pos;', ' var cachedResult = cache[cacheKey];', ' if (cachedResult) {', - ' pos = #{posClone("cachedResult.nextPos")};', + ' pos = cachedResult.nextPos;', ' return cachedResult.result;', ' }', ' ', @@ -589,7 +550,7 @@ module.exports = function(ast, options) { ' #if options.cache', ' ', ' cache[cacheKey] = {', - ' nextPos: #{posClone("pos")},', + ' nextPos: pos,', ' result: #{r(node.expression.resultIndex)}', ' };', ' #end', @@ -614,18 +575,18 @@ module.exports = function(ast, options) { '}' ], action: [ - '#{posSave(node)};', + '#{r(node.posIndex)} = pos;', '#block emit(node.expression)', 'if (#{r(node.resultIndex)} !== null) {', - ' reportedPos = #{posClone(r(node.posIndex))};', + ' 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) {', - ' #{posRestore(node)};', + ' pos = #{r(node.posIndex)};', '}' ], sequence: [ - '#{posSave(node)};', + '#{r(node.posIndex)} = pos;', '#block code' ], "sequence.iteration": [ @@ -634,26 +595,26 @@ module.exports = function(ast, options) { ' #block code', '} else {', ' #{r(node.resultIndex)} = null;', - ' #{posRestore(node)};', + ' pos = #{r(node.posIndex)};', '}' ], "sequence.inner": [ '#{r(node.resultIndex)} = [#{map(pluck(node.elements, "resultIndex"), r).join(", ")}];' ], simple_and: [ - '#{posSave(node)};', + '#{r(node.posIndex)} = pos;', 'reportFailures++;', '#block emit(node.expression)', 'reportFailures--;', 'if (#{r(node.resultIndex)} !== null) {', ' #{r(node.resultIndex)} = "";', - ' #{posRestore(node)};', + ' pos = #{r(node.posIndex)};', '} else {', ' #{r(node.resultIndex)} = null;', '}' ], simple_not: [ - '#{posSave(node)};', + '#{r(node.posIndex)} = pos;', 'reportFailures++;', '#block emit(node.expression)', 'reportFailures--;', @@ -661,15 +622,15 @@ module.exports = function(ast, options) { ' #{r(node.resultIndex)} = "";', '} else {', ' #{r(node.resultIndex)} = null;', - ' #{posRestore(node)};', + ' pos = #{r(node.posIndex)};', '}' ], semantic_and: [ - 'reportedPos = #{posClone("pos")};', + 'reportedPos = pos;', '#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? "" : null;' ], semantic_not: [ - 'reportedPos = #{posClone("pos")};', + 'reportedPos = pos;', '#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? null : "";' ], optional: [ @@ -705,9 +666,9 @@ module.exports = function(ast, options) { '#else', ' #if !node.ignoreCase', ' #if node.value.length === 1', - ' if (input.charCodeAt(#{posOffset("pos")}) === #{node.value.charCodeAt(0)}) {', + ' if (input.charCodeAt(pos) === #{node.value.charCodeAt(0)}) {', ' #else', - ' if (input.substr(#{posOffset("pos")}, #{node.value.length}) === #{string(node.value)}) {', + ' if (input.substr(pos, #{node.value.length}) === #{string(node.value)}) {', ' #end', ' #else', /* @@ -718,14 +679,14 @@ module.exports = function(ast, options) { * meaning the result of lowercasing a character can be more * characters. */ - ' if (input.substr(#{posOffset("pos")}, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {', + ' 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(#{posOffset("pos")}, #{node.value.length});', + ' #{r(node.resultIndex)} = input.substr(pos, #{node.value.length});', ' #end', - ' #{posAdvance(node.value.length)};', + ' #{node.value.length > 1 ? "pos += " + node.value.length : "pos++"};', ' } else {', ' #{r(node.resultIndex)} = null;', ' if (reportFailures === 0) {', @@ -735,9 +696,9 @@ module.exports = function(ast, options) { '#end' ], "class": [ - 'if (#{regexp}.test(input.charAt(#{posOffset("pos")}))) {', - ' #{r(node.resultIndex)} = input.charAt(#{posOffset("pos")});', - ' #{posAdvance(1)};', + 'if (#{regexp}.test(input.charAt(pos))) {', + ' #{r(node.resultIndex)} = input.charAt(pos);', + ' pos++;', '} else {', ' #{r(node.resultIndex)} = null;', ' if (reportFailures === 0) {', @@ -746,9 +707,9 @@ module.exports = function(ast, options) { '}' ], any: [ - 'if (input.length > #{posOffset("pos")}) {', - ' #{r(node.resultIndex)} = input.charAt(#{posOffset("pos")});', - ' #{posAdvance(1)};', + 'if (input.length > pos) {', + ' #{r(node.resultIndex)} = input.charAt(pos);', + ' pos++;', '} else {', ' #{r(node.resultIndex)} = null;', ' if (reportFailures === 0) {', @@ -777,34 +738,6 @@ module.exports = function(ast, options) { vars.r = function(index) { return "r" + index; }; - /* Position-handling macros */ - if (options.trackLineAndColumn) { - vars.posInit = function(name) { - return "var " - + name - + " = " - + "{ offset: 0, line: 1, column: 1, seenCR: false }"; - }; - vars.posClone = function(name) { return "clone(" + name + ")"; }; - vars.posOffset = function(name) { return name + ".offset"; }; - - vars.posAdvance = function(n) { return "advance(pos, " + n + ")"; }; - } else { - vars.posInit = function(name) { return "var " + name + " = 0"; }; - vars.posClone = function(name) { return name; }; - vars.posOffset = function(name) { return name; }; - - vars.posAdvance = function(n) { - return n === 1 ? "pos++" : "pos += " + n; - }; - } - vars.posSave = function(node) { - return vars.r(node.posIndex) + " = " + vars.posClone("pos"); - }; - vars.posRestore = function(node) { - return "pos" + " = " + vars.posClone(vars.r(node.posIndex)); - }; - return templates[name](vars); } diff --git a/lib/parser.js b/lib/parser.js index 5cab64a..1c382fa 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -96,6 +96,14 @@ module.exports = (function(){ return reportedPos; } + function line() { + return computePosDetails(reportedPos).line; + } + + function column() { + return computePosDetails(reportedPos).column; + } + function matchFailed(failure) { if (pos < rightmostFailuresPos) { return; @@ -2817,7 +2825,7 @@ module.exports = (function(){ return cleanExpected; } - function computeErrorPosition() { + function computePosDetails(pos) { /* * The first idea was to use |String.split| to break the input up to the * error position along newlines and derive the line and column from @@ -2829,7 +2837,7 @@ module.exports = (function(){ var column = 1; var seenCR = false; - for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) { + for (var i = 0; i < pos; i++) { var ch = input.charAt(i); if (ch === "\n") { if (!seenCR) { line++; } @@ -2881,7 +2889,7 @@ module.exports = (function(){ if (result === null || pos !== input.length) { var offset = Math.max(pos, rightmostFailuresPos); var found = offset < input.length ? input.charAt(offset) : null; - var errorPosition = computeErrorPosition(); + var errorPosition = computePosDetails(Math.max(pos, rightmostFailuresPos)); throw new this.SyntaxError( cleanupExpected(rightmostFailuresExpected), diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index 1c8092c..09be92e 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -1,9 +1,6 @@ describe("generated parser", function() { function vary(names, block) { - var values = { - trackLineAndColumn: [false, true], - cache: [false, true] - }; + var values = { cache: [false, true] }; function varyStep(names, options) { var clonedOptions = {}, key, name, i; @@ -35,7 +32,7 @@ describe("generated parser", function() { } function varyAll(block) { - vary(["cache", "trackLineAndColumn"], block); + vary(["cache"], block); } beforeEach(function() { @@ -267,30 +264,28 @@ describe("generated parser", function() { expect(parser).toParse("ab", ["a", 1]); }); - if (options.trackLineAndColumn) { - it("can use the |line| and |column| functions to get the current line and column", function() { - var parser = PEG.buildParser([ - '{ var result; }', - 'start = line (nl+ line)* { return result; }', - 'line = thing (" "+ thing)*', - 'thing = digit / mark', - 'digit = [0-9]', - 'mark = "x" { result = [line(), column()]; }', - 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' - ].join("\n"), options); + it("can use the |line| and |column| functions to get the current line and column", function() { + var parser = PEG.buildParser([ + '{ var result; }', + 'start = line (nl+ line)* { return result; }', + 'line = thing (" "+ thing)*', + 'thing = digit / mark', + 'digit = [0-9]', + 'mark = "x" { result = [line(), column()]; }', + 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' + ].join("\n"), options); - expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); + expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); - /* Non-Unix newlines */ - expect(parser).toParse("1\rx", [2, 1]); // Old Mac - expect(parser).toParse("1\r\nx", [2, 1]); // Windows - expect(parser).toParse("1\n\rx", [3, 1]); // mismatched + /* Non-Unix newlines */ + expect(parser).toParse("1\rx", [2, 1]); // Old Mac + expect(parser).toParse("1\r\nx", [2, 1]); // Windows + expect(parser).toParse("1\n\rx", [3, 1]); // mismatched - /* Strange newlines */ - expect(parser).toParse("1\u2028x", [2, 1]); // line separator - expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator - }); - } + /* Strange newlines */ + expect(parser).toParse("1\u2028x", [2, 1]); // line separator + expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator + }); it("can use variables defined in the initializer", function() { var parser = PEG.buildParser([ @@ -431,30 +426,28 @@ describe("generated parser", function() { expect(parser).toParse("a", ["a", ""]); }); - if (options.trackLineAndColumn) { - it("can use the |line| and |column| functions to get the current line and column", function() { - var parser = PEG.buildParser([ - '{ var result; }', - 'start = line (nl+ line)* { return result; }', - 'line = thing (" "+ thing)*', - 'thing = digit / mark', - 'digit = [0-9]', - 'mark = &{ result = [line(), column()]; return true; } "x"', - 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' - ].join("\n"), options); + it("can use the |line| and |column| functions to get the current line and column", function() { + var parser = PEG.buildParser([ + '{ var result; }', + 'start = line (nl+ line)* { return result; }', + 'line = thing (" "+ thing)*', + 'thing = digit / mark', + 'digit = [0-9]', + 'mark = &{ result = [line(), column()]; return true; } "x"', + 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' + ].join("\n"), options); - expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); + expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); - /* Non-Unix newlines */ - expect(parser).toParse("1\rx", [2, 1]); // Old Mac - expect(parser).toParse("1\r\nx", [2, 1]); // Windows - expect(parser).toParse("1\n\rx", [3, 1]); // mismatched + /* Non-Unix newlines */ + expect(parser).toParse("1\rx", [2, 1]); // Old Mac + expect(parser).toParse("1\r\nx", [2, 1]); // Windows + expect(parser).toParse("1\n\rx", [3, 1]); // mismatched - /* Strange newlines */ - expect(parser).toParse("1\u2028x", [2, 1]); // line separator - expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator - }); - } + /* Strange newlines */ + expect(parser).toParse("1\u2028x", [2, 1]); // line separator + expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator + }); it("can use variables defined in the initializer", function() { var parser = PEG.buildParser([ @@ -515,30 +508,28 @@ describe("generated parser", function() { expect(parser).toParse("a", ["a", ""]); }); - if (options.trackLineAndColumn) { - it("can use the |line| and |column| functions to get the current line and column", function() { - var parser = PEG.buildParser([ - '{ var result; }', - 'start = line (nl+ line)* { return result; }', - 'line = thing (" "+ thing)*', - 'thing = digit / mark', - 'digit = [0-9]', - 'mark = !{ result = [line(), column()]; return false; } "x"', - 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' - ].join("\n"), options); + it("can use the |line| and |column| functions to get the current line and column", function() { + var parser = PEG.buildParser([ + '{ var result; }', + 'start = line (nl+ line)* { return result; }', + 'line = thing (" "+ thing)*', + 'thing = digit / mark', + 'digit = [0-9]', + 'mark = !{ result = [line(), column()]; return false; } "x"', + 'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")' + ].join("\n"), options); - expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); + expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]); - /* Non-Unix newlines */ - expect(parser).toParse("1\rx", [2, 1]); // Old Mac - expect(parser).toParse("1\r\nx", [2, 1]); // Windows - expect(parser).toParse("1\n\rx", [3, 1]); // mismatched + /* Non-Unix newlines */ + expect(parser).toParse("1\rx", [2, 1]); // Old Mac + expect(parser).toParse("1\r\nx", [2, 1]); // Windows + expect(parser).toParse("1\n\rx", [3, 1]); // mismatched - /* Strange newlines */ - expect(parser).toParse("1\u2028x", [2, 1]); // line separator - expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator - }); - } + /* Strange newlines */ + expect(parser).toParse("1\u2028x", [2, 1]); // line separator + expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator + }); it("can use variables defined in the initializer", function() { var parser = PEG.buildParser([