From 5b3321d302315fa47c3147492a8bd619b2174089 Mon Sep 17 00:00:00 2001 From: David Majda Date: Mon, 16 Apr 2012 18:27:31 +0200 Subject: [PATCH] Implement |cache| option for |PEG.buildParser| This option enables/disables the results cache in generated parsers. Until now, it was always enabled, but after this commit it needs to be enabled explicitly (i.e. the |cache| option default value is |false|). The reason is that parsing without it is *much* faster according to the benchmark. Note that disabling the cache breaks the linear parsing time guarantee, meaning that with some grammars you can get exponential parsing time with respect to the input length. This, together with the possibility of improving the cache performance in the future, is the reason to keep it as an option. Speed impact ------------ Before: 214.08 kB/s After: 827.52 kB/s Difference: 286.54% Size impact ----------- Before: 1045396 b After: 949783 b Difference: -9.15% (Measured by /tools/impact with Node.js v0.6.6 on x86_64 GNU/Linux.) --- src/emitter.js | 35 ++++++++++++++++++++++------------- test/compiler-test.js | 27 +++++++++++++++++---------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/emitter.js b/src/emitter.js index fc8d884..6752bb5 100644 --- a/src/emitter.js +++ b/src/emitter.js @@ -1,6 +1,9 @@ /* Emits the generated code for the AST. */ PEG.compiler.emitter = function(ast, options) { options = options || {}; + if (options.cache === undefined) { + options.cache = false; + } if (options.trackLineAndColumn === undefined) { options.trackLineAndColumn = false; } @@ -321,7 +324,9 @@ PEG.compiler.emitter = function(ast, options) { ' var reportFailures = 0;', // 0 = report, anything > 0 = do not report ' #{posInit("rightmostFailuresPos")};', ' var rightmostFailuresExpected = [];', - ' var cache = {};', + ' #if options.cache', + ' var cache = {};', + ' #end', ' ', /* This needs to be in sync with |padLeft| in utils.js. */ ' function padLeft(input, padding, length) {', @@ -544,13 +549,15 @@ PEG.compiler.emitter = function(ast, options) { ], rule: [ 'function parse_#{node.name}() {', - ' var cacheKey = "#{node.name}@" + #{posOffset("pos")};', - ' var cachedResult = cache[cacheKey];', - ' if (cachedResult) {', - ' pos = #{posClone("cachedResult.nextPos")};', - ' return cachedResult.result;', - ' }', - ' ', + ' #if options.cache', + ' var cacheKey = "#{node.name}@" + #{posOffset("pos")};', + ' var cachedResult = cache[cacheKey];', + ' if (cachedResult) {', + ' pos = #{posClone("cachedResult.nextPos")};', + ' return cachedResult.result;', + ' }', + ' ', + ' #end', ' #if node.resultVars.length > 0', ' var #{node.resultVars.join(", ")};', ' #end', @@ -568,11 +575,13 @@ PEG.compiler.emitter = function(ast, options) { ' matchFailed(#{string(node.displayName)});', ' }', ' #end', - ' ', - ' cache[cacheKey] = {', - ' nextPos: #{posClone("pos")},', - ' result: #{node.resultVar}', - ' };', + ' #if options.cache', + ' ', + ' cache[cacheKey] = {', + ' nextPos: #{posClone("pos")},', + ' result: #{node.resultVar}', + ' };', + ' #end', ' return #{node.resultVar};', '}' ], diff --git a/test/compiler-test.js b/test/compiler-test.js index b6e9dd6..fbecc8f 100644 --- a/test/compiler-test.js +++ b/test/compiler-test.js @@ -785,16 +785,23 @@ testWithVaryingTrackLineAndColumn("classes", function(options) { }); testWithVaryingTrackLineAndColumn("cache", function(options) { - /* - * Should trigger a codepath where the cache is used (for the "a" rule). - */ - var parser = PEG.buildParser([ - 'start = (a b) / (a c)', - 'a = "a"', - 'b = "b"', - 'c = "c"' - ].join("\n"), options); - parses(parser, "ac", ["a", "c"]); + var grammar = [ + '{ var n = 0; }', + 'start = (a "b") / (a "c") { return n; }', + 'a = "a" { n++; }', + ].join("\n"); + + /* Without cache */ + + parses(PEG.buildParser(grammar, options), "ac", 2); + + options.cache = false; + parses(PEG.buildParser(grammar, options), "ac", 2); + + /* With cache */ + + options.cache = true; + parses(PEG.buildParser(grammar, options), "ac", 1); }); testWithVaryingTrackLineAndColumn("indempotence", function(options) {