Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

More lazy evaluation, including a bug fix

  • Loading branch information...
commit 838578176866913f51ed861c1395a93a89ba403a 1 parent 172ac01
@cjheath authored
View
10 lib/treetop/compiler/node_classes/anything_symbol.rb
@@ -4,8 +4,12 @@ class AnythingSymbol < AtomicExpression
def compile(address, builder, parent_expression = nil)
super
builder.if__ "index < input_length" do
- assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
- extend_result_with_inline_module
+ if address == 0 || decorated?
+ assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
+ extend_result_with_inline_module
+ else
+ assign_lazily_instantiated_node
+ end
builder << "@index += 1"
end
builder.else_ do
@@ -15,4 +19,4 @@ def compile(address, builder, parent_expression = nil)
end
end
end
-end
+end
View
4 lib/treetop/compiler/node_classes/choice.rb
@@ -13,6 +13,8 @@ def compile_alternatives(alternatives)
obtain_new_subexpression_address
alternatives.first.compile(subexpression_address, builder)
builder.if__ subexpression_success? do
+ # Undo lazy instantiation:
+ builder << "#{subexpression_result_var} = SyntaxNode.new(input, (index-1)...index) if #{subexpression_result_var} == true"
assign_result subexpression_result_var
extend_result_with_declared_module
extend_result_with_inline_module
@@ -28,4 +30,4 @@ def compile_alternatives(alternatives)
end
end
end
-end
+end
View
6 lib/treetop/compiler/node_classes/parsing_rule.rb
@@ -33,9 +33,11 @@ def generate_method_definition(builder)
def generate_cache_lookup(builder)
builder.if_ "node_cache[:#{name}].has_key?(index)" do
- builder.assign 'cached', "node_cache[:#{name}][index]"
+ cache_address = "node_cache[:#{name}][index]"
+ builder.assign 'cached', cache_address
builder.if_ "cached" do
- builder << 'cached = SyntaxNode.new(input, index...(index + 1)) if cached == true'
+ # Handle lazily instantiated nodes:
+ builder << "#{cache_address} = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true"
builder << '@index = cached.interval.end'
end
builder << 'return cached'
View
8 lib/treetop/compiler/node_classes/terminal.rb
@@ -6,8 +6,12 @@ def compile(address, builder, parent_expression = nil)
string_length = eval(text_value).length
builder.if__ "has_terminal?(#{text_value}, false, index)" do
- assign_result "instantiate_node(#{node_class_name},input, index...(index + #{string_length}))"
- extend_result_with_inline_module
+ if address == 0 || decorated? || string_length > 1
+ assign_result "instantiate_node(#{node_class_name},input, index...(index + #{string_length}))"
+ extend_result_with_inline_module
+ else
+ assign_lazily_instantiated_node
+ end
builder << "@index += #{string_length}"
end
builder.else_ do
View
34 spec/compiler/character_class_spec.rb
@@ -228,19 +228,19 @@ module ModFoo
end
describe "a character class mixed with other expressions" do
- testing_expression '[A-Z] "a"'
+ testing_expression '[A-Z] "a" "bc"'
it "lazily instantiates a node for the character" do
- result = parse('Aa')
+ result = parse('Aabc')
result.instance_variable_get("@elements").should include(true)
result.elements.should_not include(true)
- result.elements.size.should == 2
+ result.elements.size.should == 3
end
end
describe "a character class with a node class declaration mixed with other expressions" do
- testing_expression '([A-Z] <CharacterClassSpec::Foo>) "a"'
+ testing_expression '([A-Z] <CharacterClassSpec::Foo>) "ab"'
it "actively generates a node for the character because it has a node class declared" do
- result = parse('Aa')
+ result = parse('Aab')
result.instance_variable_get("@elements").should_not include(true)
result.elements.should_not include(true)
result.elements.size.should == 2
@@ -248,9 +248,9 @@ module ModFoo
end
describe "a character class with a node module declaration mixed with other expressions" do
- testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "a"'
+ testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "ab"'
it "actively generates a node for the character because it has a node module declared" do
- result = parse('Aa')
+ result = parse('Aab')
result.instance_variable_get("@elements").should_not include(true)
result.elements.should_not include(true)
result.elements.size.should == 2
@@ -258,9 +258,9 @@ module ModFoo
end
describe "a character class with an inline block mixed with other expressions" do
- testing_expression '([A-Z] { def a_method; end }) "a"'
+ testing_expression '([A-Z] { def a_method; end }) "ab"'
it "actively generates a node for the character because it has an inline block" do
- result = parse('Aa')
+ result = parse('Aab')
result.instance_variable_get("@elements").should_not include(true)
result.elements.should_not include(true)
result.elements.size.should == 2
@@ -268,28 +268,28 @@ module ModFoo
end
describe "a character class with a label mixed with other expressions" do
- testing_expression 'upper:([A-Z]) "b"'
+ testing_expression 'upper:([A-Z]) "b" "cd"'
it "returns the correct element for the labeled expression" do
- result = parse('Ab')
+ result = parse('Abcd')
result.upper.text_value.should == "A"
- result.elements.size.should == 2
+ result.elements.size.should == 3
end
end
describe "a character class repetition mixed with other expressions" do
- testing_expression '[A-Z]+ "a"'
+ testing_expression '[A-Z]+ "a" "bc"'
it "lazily instantiates a node for the character" do
- result = parse('ABCa')
+ result = parse('ABCabc')
result.elements[0].instance_variable_get("@elements").should include(true)
result.elements[0].elements.should_not include(true)
result.elements[0].elements.size.should == 3
- result.elements.size.should == 2
- result.elements.inspect.should == %Q{[SyntaxNode offset=0, "ABC":\n SyntaxNode offset=0, "A"\n SyntaxNode offset=1, "B"\n SyntaxNode offset=2, "C", SyntaxNode offset=3, "a"]}
+ result.elements.size.should == 3
+ result.elements.inspect.should == %Q{[SyntaxNode offset=0, "ABC":\n SyntaxNode offset=0, "A"\n SyntaxNode offset=1, "B"\n SyntaxNode offset=2, "C", SyntaxNode offset=3, "a", SyntaxNode offset=4, "bc"]}
end
end
describe "a character class that gets cached because of a choice" do
- testing_expression "[A-Z] 'a' / [A-Z]"
+ testing_expression "[A-Z] 'a' 'bc' / [A-Z]"
it "generates a node for the lazily-instantiated character when it is the primary node" do
result = parse('A')
View
8 spec/compiler/choice_spec.rb
@@ -54,10 +54,10 @@ module ChoiceSpec
end
describe "A choice between terminals followed by a block" do
- testing_expression "('a'/ 'b' / 'c') { def a_method; end }"
+ testing_expression "('a'/ 'bb' / [c]) { def a_method; end }"
it "extends a match of any of its subexpressions with a module created from the block" do
- ['a', 'b', 'c'].each do |letter|
+ ['a', 'bb', 'c'].each do |letter|
parse(letter).should respond_to(:a_method)
end
end
@@ -69,10 +69,10 @@ def a_method
end
describe "a choice followed by a declared module" do
- testing_expression "('a'/ 'b' / 'c') <ChoiceSpec::TestModule>"
+ testing_expression "('a'/ 'bb' / [c]) <ChoiceSpec::TestModule>"
it "extends a match of any of its subexpressions with a module created from the block" do
- ['a', 'b', 'c'].each do |letter|
+ ['a', 'bb', 'c'].each do |letter|
parse(letter).should respond_to(:a_method)
end
end
View
4 spec/compiler/not_predicate_spec.rb
@@ -29,10 +29,10 @@ module NotPredicateSpec
end
describe "A !-predicated sequence" do
- testing_expression '!("a" "b" "c")'
+ testing_expression '!("a" "b" "cc")'
it "fails to parse matching input" do
- parse('abc').should be_nil
+ parse('abcc').should be_nil
end
end
end
View
4 spec/compiler/repeated_subrule_spec.rb
@@ -5,7 +5,7 @@ module RepeatedSubruleSpec
testing_grammar %{
grammar Foo
rule foo
- a:'a' space b:'b' space 'c'
+ a:'a' space b:'b' space 'cc'
end
rule space
@@ -15,7 +15,7 @@ module RepeatedSubruleSpec
}
it "should produce a parser having sequence-numbered node accessor methods" do
- parse("a b c") do |result|
+ parse("a b cc") do |result|
result.should_not be_nil
result.should respond_to(:space1)
result.should respond_to(:space2)
View
2  spec/compiler/zero_or_more_spec.rb
@@ -47,7 +47,7 @@ class Foo < Treetop::Runtime::SyntaxNode
end
describe "Zero or more of a choice" do
- testing_expression '("a" / "b")*'
+ testing_expression '("a" / "bb")*'
it "successfully parses matching input" do
parse('abba').should_not be_nil
View
4 spec/composition/b.treetop
@@ -1,11 +1,11 @@
module Test
grammar B
rule b
- 'b'
+ 'bb'
end
rule inherit
super 'keyword'
end
end
-end
+end
View
4 spec/composition/grammar_composition_spec.rb
@@ -16,7 +16,7 @@ module GrammarCompositionSpec
end
specify "rules in C have access to rules defined in A and B" do
- @c.parse('abc').should_not be_nil
+ @c.parse('abbc').should_not be_nil
end
specify "rules in C can override rules in A and B with super semantics" do
@@ -33,7 +33,7 @@ module GrammarCompositionSpec
end
specify "rules in F have access to rule defined in E" do
- @f.parse('abcef').should_not be_nil
+ @f.parse('abbcef').should_not be_nil
end
end
View
6 spec/runtime/compiled_parser_spec.rb
@@ -11,7 +11,7 @@ module CompiledParserSpec
end
rule b
- 'b'
+ 'bb'
end
end
}
@@ -25,7 +25,7 @@ module CompiledParserSpec
parser.parse('b').should be_nil
# Check that the temporary-override works:
- parser.parse('b', :root => :b).should_not be_nil
+ parser.parse('bb', :root => :b).should_not be_nil
parser.parse('a', :root => :b).should be_nil
# Check that the temporary-override isn't sticky:
@@ -33,7 +33,7 @@ module CompiledParserSpec
# Try a permanent override:
parser.root = :b
- parser.parse('b').should_not be_nil
+ parser.parse('bb').should_not be_nil
parser.parse('a').should be_nil
end
Please sign in to comment.
Something went wrong with that request. Please try again.