From 89799819f57b88db645d951583f9aa022ede3e79 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 10 Mar 2008 23:23:31 -0700 Subject: [PATCH] Fixed bug relocating nodes in the node cache after change in buffer length. As I relocated each memoized result, I deleted its former location. But if another memoized result for the same rule had already been moved into that location, then I deleted the wrong result. I was moving the result I deleted to the new location, so if it wasn't the result that belonged to the memoization this screwed things up even further. Now I check if the result belonging to the memoization is stored at the previous location (that it hasn't been overwritten by another result) and only delete it if it still exists. Then I move the result pointed to by the Memoization to the correct location. Also fixed Range#transpose for end-excluding ranges. --- lib/treetop/ruby_extensions/range.rb | 6 +- lib/treetop/runtime/memoization.rb | 5 +- spec/iterative_parsing/arithmetic.rb | 777 ++++++++++++++++++ spec/iterative_parsing/arithmetic.treetop | 97 +++ .../iterative_parsing_spec.rb | 28 +- spec/ruby_extensions/range_spec.rb | 4 +- spec/runtime/node_cache_spec.rb | 21 +- textra/app/assets/arithmetic.rb | 777 ++++++++++++++++++ textra/config/environment.rb | 4 +- 9 files changed, 1711 insertions(+), 8 deletions(-) create mode 100644 spec/iterative_parsing/arithmetic.rb create mode 100644 spec/iterative_parsing/arithmetic.treetop create mode 100644 textra/app/assets/arithmetic.rb diff --git a/lib/treetop/ruby_extensions/range.rb b/lib/treetop/ruby_extensions/range.rb index 12e80a1..504e9ba 100644 --- a/lib/treetop/ruby_extensions/range.rb +++ b/lib/treetop/ruby_extensions/range.rb @@ -9,6 +9,10 @@ def intersects?(other_range) end def transpose(delta) - (first + delta)..(last + delta) + if exclude_end? + (first + delta)...(last + delta) + else + (first + delta)..(last + delta) + end end end \ No newline at end of file diff --git a/lib/treetop/runtime/memoization.rb b/lib/treetop/runtime/memoization.rb index 50f7840..69cb7b1 100644 --- a/lib/treetop/runtime/memoization.rb +++ b/lib/treetop/runtime/memoization.rb @@ -20,7 +20,10 @@ def rule_index end def relocate(length_change) - rule_index[interval.first + length_change] = rule_index.delete(interval.first) + if rule_index[interval.first] == result + rule_index.delete(interval.first) + end + rule_index[interval.first + length_change] = result end def expire diff --git a/spec/iterative_parsing/arithmetic.rb b/spec/iterative_parsing/arithmetic.rb new file mode 100644 index 0000000..4289702 --- /dev/null +++ b/spec/iterative_parsing/arithmetic.rb @@ -0,0 +1,777 @@ +module Arithmetic + include Treetop::Runtime + + def root + @root || :expression + end + + def _nt_expression + start_index = index + if expirable_node_cache.has_result?(:expression, index) + cached = expirable_node_cache.get(:expression, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + r1 = _nt_comparative + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r2 = _nt_additive + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:expression, r0) + + return r0 + end + + module Comparative0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_comparative + start_index = index + if expirable_node_cache.has_result?(:comparative, index) + cached = expirable_node_cache.get(:comparative, index) + @index = cached.resume_index if cached + return cached + end + + i0, s0 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r1 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r1 + if r1 && !r1.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_equality_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r5 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s0.last.is_a?(ParseFailure) + r0 = (BinaryOperation).new(input, i0...index, s0) + r0.extend(Comparative0) + else + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + r0.dependencies.concat(s0) + end + + expirable_node_cache.store(:comparative, r0) + + return r0 + end + + module EqualityOp0 + def apply(a, b) + a == b + end + end + + def _nt_equality_op + start_index = index + if expirable_node_cache.has_result?(:equality_op, index) + cached = expirable_node_cache.get(:equality_op, index) + @index = cached.resume_index if cached + return cached + end + + if input.index('==', index) == index + r0 = (SyntaxNode).new(input, index...(index + 2)) + r0.extend(EqualityOp0) + @index += 2 + else + r0 = terminal_parse_failure('==', 2) + end + + expirable_node_cache.store(:equality_op, r0) + + return r0 + end + + module Additive0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_additive + start_index = index + if expirable_node_cache.has_result?(:additive, index) + cached = expirable_node_cache.get(:additive, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_multitive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_additive_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r6 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (BinaryOperation).new(input, i1...index, s1) + r1.extend(Additive0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r7 = _nt_multitive + if r7 && !r7.is_a?(ParseFailure) + r0 = r7 + r0 = Propagation.new(r0) + else + failed_alternatives << r7 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:additive, r0) + + return r0 + end + + module AdditiveOp0 + def apply(a, b) + a + b + end + end + + module AdditiveOp1 + def apply(a, b) + a - b + end + end + + def _nt_additive_op + start_index = index + if expirable_node_cache.has_result?(:additive_op, index) + cached = expirable_node_cache.get(:additive_op, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + if input.index('+', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + r1.extend(AdditiveOp0) + @index += 1 + else + r1 = terminal_parse_failure('+', 1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('-', index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + r2.extend(AdditiveOp1) + @index += 1 + else + r2 = terminal_parse_failure('-', 1) + end + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:additive_op, r0) + + return r0 + end + + module Multitive0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_multitive + start_index = index + if expirable_node_cache.has_result?(:multitive, index) + cached = expirable_node_cache.get(:multitive, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_primary + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_multitive_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_multitive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r6 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (BinaryOperation).new(input, i1...index, s1) + r1.extend(Multitive0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r7 = _nt_primary + if r7 && !r7.is_a?(ParseFailure) + r0 = r7 + r0 = Propagation.new(r0) + else + failed_alternatives << r7 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:multitive, r0) + + return r0 + end + + module MultitiveOp0 + def apply(a, b) + a * b + end + end + + module MultitiveOp1 + def apply(a, b) + a / b + end + end + + def _nt_multitive_op + start_index = index + if expirable_node_cache.has_result?(:multitive_op, index) + cached = expirable_node_cache.get(:multitive_op, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + if input.index('*', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + r1.extend(MultitiveOp0) + @index += 1 + else + r1 = terminal_parse_failure('*', 1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('/', index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + r2.extend(MultitiveOp1) + @index += 1 + else + r2 = terminal_parse_failure('/', 1) + end + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:multitive_op, r0) + + return r0 + end + + module Primary0 + def space + elements[1] + end + + def expression + elements[2] + end + + def space + elements[3] + end + + end + + module Primary1 + def eval(env={}) + expression.eval(env) + end + end + + def _nt_primary + start_index = index + if expirable_node_cache.has_result?(:primary, index) + cached = expirable_node_cache.get(:primary, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + r1 = _nt_variable + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r2 = _nt_number + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + i3, s3 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index('(', index) == index + r4 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r4 = terminal_parse_failure('(', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_expression + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r6 + if r6 && !r6.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r7 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r7 + if r7 && !r7.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index(')', index) == index + r8 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r8 = terminal_parse_failure(')', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r8 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s3.last.is_a?(ParseFailure) + r3 = (SyntaxNode).new(input, i3...index, s3) + r3.extend(Primary0) + r3.extend(Primary1) + else + self.index = i3 + failure_range = (max_terminal_failure_last_index > input_length) ? (i3..max_terminal_failure_last_index) : (i3...max_terminal_failure_last_index) + r3 = ParseFailure.new(failure_range) + r3.dependencies.concat(s3) + end + if r3 && !r3.is_a?(ParseFailure) + r0 = r3 + r0 = Propagation.new(r0) + else + failed_alternatives << r3 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:primary, r0) + + return r0 + end + + module Variable0 + def eval(env={}) + env[name] + end + + def name + text_value + end + end + + def _nt_variable + start_index = index + if expirable_node_cache.has_result?(:variable, index) + cached = expirable_node_cache.get(:variable, index) + @index = cached.resume_index if cached + return cached + end + + s0, i0 = [], index + r1 = nil + loop do + if input.index(Regexp.new('[a-z]'), index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r1 = terminal_parse_failure('a-z', 1) + end + if r1 && !r1.is_a?(ParseFailure) + s0 << r1 + else + break + end + end + if s0.empty? + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + else + r0 = SyntaxNode.new(input, i0...index, s0) + r0.extend(Variable0) + end + r0.dependencies << r1 + + expirable_node_cache.store(:variable, r0) + + return r0 + end + + module Number0 + end + + module Number1 + def eval(env={}) + text_value.to_i + end + end + + def _nt_number + start_index = index + if expirable_node_cache.has_result?(:number, index) + cached = expirable_node_cache.get(:number, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index(Regexp.new('[1-9]'), index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r2 = terminal_parse_failure('1-9', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + s3, i3 = [], index + r4 = nil + loop do + if input.index(Regexp.new('[0-9]'), index) == index + r4 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r4 = terminal_parse_failure('0-9', 1) + end + if r4 && !r4.is_a?(ParseFailure) + s3 << r4 + else + break + end + end + r3 = SyntaxNode.new(input, i3...index, s3) + r3.dependencies << r4 + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (SyntaxNode).new(input, i1...index, s1) + r1.extend(Number0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0.extend(Number1) + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('0', index) == index + r5 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r5 = terminal_parse_failure('0', 1) + end + if r5 && !r5.is_a?(ParseFailure) + r0 = r5 + r0.extend(Number1) + r0 = Propagation.new(r0) + else + failed_alternatives << r5 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:number, r0) + + return r0 + end + + def _nt_space + start_index = index + if expirable_node_cache.has_result?(:space, index) + cached = expirable_node_cache.get(:space, index) + @index = cached.resume_index if cached + return cached + end + + s0, i0 = [], index + r1 = nil + loop do + if input.index(' ', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r1 = terminal_parse_failure(' ', 1) + end + if r1 && !r1.is_a?(ParseFailure) + s0 << r1 + else + break + end + end + r0 = SyntaxNode.new(input, i0...index, s0) + r0.dependencies << r1 + + expirable_node_cache.store(:space, r0) + + return r0 + end + +end + +class ArithmeticParser < Treetop::Runtime::CompiledParser + include Arithmetic +end diff --git a/spec/iterative_parsing/arithmetic.treetop b/spec/iterative_parsing/arithmetic.treetop new file mode 100644 index 0000000..a54f6e2 --- /dev/null +++ b/spec/iterative_parsing/arithmetic.treetop @@ -0,0 +1,97 @@ +grammar Arithmetic + rule expression + comparative / additive + end + + rule comparative + operand_1:additive space operator:equality_op space operand_2:additive + end + + rule equality_op + '==' { + def apply(a, b) + a == b + end + } + end + + rule additive + operand_1:multitive + space operator:additive_op space + operand_2:additive + / + multitive + end + + rule additive_op + '+' { + def apply(a, b) + a + b + end + } + / + '-' { + def apply(a, b) + a - b + end + } + end + + rule multitive + operand_1:primary + space operator:multitive_op space + operand_2:multitive + / + primary + end + + rule multitive_op + '*' { + def apply(a, b) + a * b + end + } + / + '/' { + def apply(a, b) + a / b + end + } + end + + rule primary + variable + / + number + / + '(' space expression space ')' { + def eval(env={}) + expression.eval(env) + end + } + end + + rule variable + [a-z]+ { + def eval(env={}) + env[name] + end + + def name + text_value + end + } + end + + rule number + ([1-9] [0-9]* / '0') { + def eval(env={}) + text_value.to_i + end + } + end + + rule space + ' '* + end +end \ No newline at end of file diff --git a/spec/iterative_parsing/iterative_parsing_spec.rb b/spec/iterative_parsing/iterative_parsing_spec.rb index 2039f16..742c745 100644 --- a/spec/iterative_parsing/iterative_parsing_spec.rb +++ b/spec/iterative_parsing/iterative_parsing_spec.rb @@ -158,9 +158,11 @@ module IterativeParsingSpec node_cache.should have_result(:addition, 2) node_cache.should have_result(:number, 2) end + + end - describe "A parser for a simplified addition grammar" do + describe "A parser for a simplified addition grammar with parenthesized expressions" do testing_grammar %{ grammar Addition rule addition @@ -237,5 +239,29 @@ module IterativeParsingSpec input.replace('(1+2)') reparse.should_not be_nil end + + + end + + describe "The full Arithmetic grammar" do + attr_reader :parser_class_under_test + before do + dir = File.dirname(__FILE__) + require "#{dir}/arithmetic" + @parser_class_under_test = ArithmeticParser + end + + it "successfully parses a problematic series of inputs without stack overflow" do + input = '(1)' + parse(input).should_not be_nil + + input.replace('((1)') + expire(0..0, 1) + parser.reparse.should be_nil + + input.replace('((1))') + expire(4..4, 1) + parser.reparse.should_not be_nil + end end end \ No newline at end of file diff --git a/spec/ruby_extensions/range_spec.rb b/spec/ruby_extensions/range_spec.rb index 4248613..a3a66cd 100644 --- a/spec/ruby_extensions/range_spec.rb +++ b/spec/ruby_extensions/range_spec.rb @@ -40,8 +40,8 @@ describe "#transpose" do it "returns a range whose start and end values are adjusted by the given amount" do - (1...5).transpose(5).should == (6..10) - (5...6).transpose(-2).should == (3..4) + (1...5).transpose(5).should == (6...10) + (5..6).transpose(-2).should == (3..4) end end end \ No newline at end of file diff --git a/spec/runtime/node_cache_spec.rb b/spec/runtime/node_cache_spec.rb index 3819b07..2d58302 100644 --- a/spec/runtime/node_cache_spec.rb +++ b/spec/runtime/node_cache_spec.rb @@ -130,7 +130,7 @@ module NodeCacheSpec cache.should_not have_result(:foo, 3) cache.should_not have_result(:foo, 7) cache.get(:foo, 12).should == c - c.interval.should == (12..18) + c.interval.should == (12...18) cache.expire(13..15, 0) cache.should_not have_result(:foo, 12) @@ -187,5 +187,24 @@ module NodeCacheSpec additional_dependency.interval.should == additional_dependency_interval_before_expire.transpose(5) end end + + describe "with a results for the same rule with intervals 1..2 and 2..3" do + attr_reader :result_1, :result_2 + + before do + @result_1 = ParseFailure.new(1..2) + @result_2 = ParseFailure.new(2..3) + cache.store(:foo, result_1) + cache.store(:foo, result_2) + end + + it "correctly relocates both results without one clobbering the other when it is relocated" do + cache.get(:foo, 1).should == result_1 + cache.get(:foo, 2).should == result_2 + cache.expire(0..0, 1) + cache.get(:foo, 2).should == result_1 + cache.get(:foo, 3).should == result_2 + end + end end end \ No newline at end of file diff --git a/textra/app/assets/arithmetic.rb b/textra/app/assets/arithmetic.rb new file mode 100644 index 0000000..4289702 --- /dev/null +++ b/textra/app/assets/arithmetic.rb @@ -0,0 +1,777 @@ +module Arithmetic + include Treetop::Runtime + + def root + @root || :expression + end + + def _nt_expression + start_index = index + if expirable_node_cache.has_result?(:expression, index) + cached = expirable_node_cache.get(:expression, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + r1 = _nt_comparative + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r2 = _nt_additive + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:expression, r0) + + return r0 + end + + module Comparative0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_comparative + start_index = index + if expirable_node_cache.has_result?(:comparative, index) + cached = expirable_node_cache.get(:comparative, index) + @index = cached.resume_index if cached + return cached + end + + i0, s0 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r1 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r1 + if r1 && !r1.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_equality_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s0 << r5 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s0.last.is_a?(ParseFailure) + r0 = (BinaryOperation).new(input, i0...index, s0) + r0.extend(Comparative0) + else + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + r0.dependencies.concat(s0) + end + + expirable_node_cache.store(:comparative, r0) + + return r0 + end + + module EqualityOp0 + def apply(a, b) + a == b + end + end + + def _nt_equality_op + start_index = index + if expirable_node_cache.has_result?(:equality_op, index) + cached = expirable_node_cache.get(:equality_op, index) + @index = cached.resume_index if cached + return cached + end + + if input.index('==', index) == index + r0 = (SyntaxNode).new(input, index...(index + 2)) + r0.extend(EqualityOp0) + @index += 2 + else + r0 = terminal_parse_failure('==', 2) + end + + expirable_node_cache.store(:equality_op, r0) + + return r0 + end + + module Additive0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_additive + start_index = index + if expirable_node_cache.has_result?(:additive, index) + cached = expirable_node_cache.get(:additive, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_multitive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_additive_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_additive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r6 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (BinaryOperation).new(input, i1...index, s1) + r1.extend(Additive0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r7 = _nt_multitive + if r7 && !r7.is_a?(ParseFailure) + r0 = r7 + r0 = Propagation.new(r0) + else + failed_alternatives << r7 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:additive, r0) + + return r0 + end + + module AdditiveOp0 + def apply(a, b) + a + b + end + end + + module AdditiveOp1 + def apply(a, b) + a - b + end + end + + def _nt_additive_op + start_index = index + if expirable_node_cache.has_result?(:additive_op, index) + cached = expirable_node_cache.get(:additive_op, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + if input.index('+', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + r1.extend(AdditiveOp0) + @index += 1 + else + r1 = terminal_parse_failure('+', 1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('-', index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + r2.extend(AdditiveOp1) + @index += 1 + else + r2 = terminal_parse_failure('-', 1) + end + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:additive_op, r0) + + return r0 + end + + module Multitive0 + def operand_1 + elements[0] + end + + def space + elements[1] + end + + def operator + elements[2] + end + + def space + elements[3] + end + + def operand_2 + elements[4] + end + end + + def _nt_multitive + start_index = index + if expirable_node_cache.has_result?(:multitive, index) + cached = expirable_node_cache.get(:multitive, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r2 = _nt_primary + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r3 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + if r3 && !r3.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r4 = _nt_multitive_op + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_multitive + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r6 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (BinaryOperation).new(input, i1...index, s1) + r1.extend(Multitive0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r7 = _nt_primary + if r7 && !r7.is_a?(ParseFailure) + r0 = r7 + r0 = Propagation.new(r0) + else + failed_alternatives << r7 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:multitive, r0) + + return r0 + end + + module MultitiveOp0 + def apply(a, b) + a * b + end + end + + module MultitiveOp1 + def apply(a, b) + a / b + end + end + + def _nt_multitive_op + start_index = index + if expirable_node_cache.has_result?(:multitive_op, index) + cached = expirable_node_cache.get(:multitive_op, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + if input.index('*', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + r1.extend(MultitiveOp0) + @index += 1 + else + r1 = terminal_parse_failure('*', 1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('/', index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + r2.extend(MultitiveOp1) + @index += 1 + else + r2 = terminal_parse_failure('/', 1) + end + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:multitive_op, r0) + + return r0 + end + + module Primary0 + def space + elements[1] + end + + def expression + elements[2] + end + + def space + elements[3] + end + + end + + module Primary1 + def eval(env={}) + expression.eval(env) + end + end + + def _nt_primary + start_index = index + if expirable_node_cache.has_result?(:primary, index) + cached = expirable_node_cache.get(:primary, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + r1 = _nt_variable + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + r2 = _nt_number + if r2 && !r2.is_a?(ParseFailure) + r0 = r2 + r0 = Propagation.new(r0) + else + failed_alternatives << r2 + i3, s3 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index('(', index) == index + r4 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r4 = terminal_parse_failure('(', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r4 + if r4 && !r4.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r5 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r5 + if r5 && !r5.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r6 = _nt_expression + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r6 + if r6 && !r6.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + r7 = _nt_space + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r7 + if r7 && !r7.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index(')', index) == index + r8 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r8 = terminal_parse_failure(')', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s3 << r8 + end + end + end + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s3.last.is_a?(ParseFailure) + r3 = (SyntaxNode).new(input, i3...index, s3) + r3.extend(Primary0) + r3.extend(Primary1) + else + self.index = i3 + failure_range = (max_terminal_failure_last_index > input_length) ? (i3..max_terminal_failure_last_index) : (i3...max_terminal_failure_last_index) + r3 = ParseFailure.new(failure_range) + r3.dependencies.concat(s3) + end + if r3 && !r3.is_a?(ParseFailure) + r0 = r3 + r0 = Propagation.new(r0) + else + failed_alternatives << r3 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:primary, r0) + + return r0 + end + + module Variable0 + def eval(env={}) + env[name] + end + + def name + text_value + end + end + + def _nt_variable + start_index = index + if expirable_node_cache.has_result?(:variable, index) + cached = expirable_node_cache.get(:variable, index) + @index = cached.resume_index if cached + return cached + end + + s0, i0 = [], index + r1 = nil + loop do + if input.index(Regexp.new('[a-z]'), index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r1 = terminal_parse_failure('a-z', 1) + end + if r1 && !r1.is_a?(ParseFailure) + s0 << r1 + else + break + end + end + if s0.empty? + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + else + r0 = SyntaxNode.new(input, i0...index, s0) + r0.extend(Variable0) + end + r0.dependencies << r1 + + expirable_node_cache.store(:variable, r0) + + return r0 + end + + module Number0 + end + + module Number1 + def eval(env={}) + text_value.to_i + end + end + + def _nt_number + start_index = index + if expirable_node_cache.has_result?(:number, index) + cached = expirable_node_cache.get(:number, index) + @index = cached.resume_index if cached + return cached + end + + i0 = index + failed_alternatives = [] + i1, s1 = index, [] + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + if input.index(Regexp.new('[1-9]'), index) == index + r2 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r2 = terminal_parse_failure('1-9', 1) + end + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r2 + if r2 && !r2.is_a?(ParseFailure) + start_max_terminal_failure_last_index = max_terminal_failure_last_index + local_max_terminal_failure_last_index = max_terminal_failure_last_index + s3, i3 = [], index + r4 = nil + loop do + if input.index(Regexp.new('[0-9]'), index) == index + r4 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r4 = terminal_parse_failure('0-9', 1) + end + if r4 && !r4.is_a?(ParseFailure) + s3 << r4 + else + break + end + end + r3 = SyntaxNode.new(input, i3...index, s3) + r3.dependencies << r4 + local_max_terminal_failure_last_index = max_terminal_failure_last_index + @max_terminal_failure_last_index = start_max_terminal_failure_last_index + s1 << r3 + end + @max_terminal_failure_last_index = local_max_terminal_failure_last_index + if !s1.last.is_a?(ParseFailure) + r1 = (SyntaxNode).new(input, i1...index, s1) + r1.extend(Number0) + else + self.index = i1 + failure_range = (max_terminal_failure_last_index > input_length) ? (i1..max_terminal_failure_last_index) : (i1...max_terminal_failure_last_index) + r1 = ParseFailure.new(failure_range) + r1.dependencies.concat(s1) + end + if r1 && !r1.is_a?(ParseFailure) + r0 = r1 + r0.extend(Number1) + r0 = Propagation.new(r0) + else + failed_alternatives << r1 + if input.index('0', index) == index + r5 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r5 = terminal_parse_failure('0', 1) + end + if r5 && !r5.is_a?(ParseFailure) + r0 = r5 + r0.extend(Number1) + r0 = Propagation.new(r0) + else + failed_alternatives << r5 + self.index = i0 + failure_range = (max_terminal_failure_last_index > input_length) ? (i0..max_terminal_failure_last_index) : (i0...max_terminal_failure_last_index) + r0 = ParseFailure.new(failure_range) + end + end + r0.dependencies.concat(failed_alternatives) + + expirable_node_cache.store(:number, r0) + + return r0 + end + + def _nt_space + start_index = index + if expirable_node_cache.has_result?(:space, index) + cached = expirable_node_cache.get(:space, index) + @index = cached.resume_index if cached + return cached + end + + s0, i0 = [], index + r1 = nil + loop do + if input.index(' ', index) == index + r1 = (SyntaxNode).new(input, index...(index + 1)) + @index += 1 + else + r1 = terminal_parse_failure(' ', 1) + end + if r1 && !r1.is_a?(ParseFailure) + s0 << r1 + else + break + end + end + r0 = SyntaxNode.new(input, i0...index, s0) + r0.dependencies << r1 + + expirable_node_cache.store(:space, r0) + + return r0 + end + +end + +class ArithmeticParser < Treetop::Runtime::CompiledParser + include Arithmetic +end diff --git a/textra/config/environment.rb b/textra/config/environment.rb index b70c65f..3e2ca9b 100644 --- a/textra/config/environment.rb +++ b/textra/config/environment.rb @@ -1,7 +1,7 @@ $LOAD_PATH.push(File.expand_path("#{File.dirname(__FILE__)}/../../lib")) require 'treetop' -require 'ruby-debug' -Treetop.load(Rucola::RCApp.path_for_asset("arithmetic")) +#Treetop.load(Rucola::RCApp.path_for_asset("arithmetic")) +require Rucola::RCApp.path_for_asset("arithmetic") require Rucola::RCApp.path_for_asset("arithmetic_node_classes") Rucola::Initializer.run do |config|