Skip to content

Commit

Permalink
Lazy instantiation of character class nodes.
Browse files Browse the repository at this point in the history
Speedup:
  a = 0.0237299
  b = 0.02062
  • Loading branch information
jgarber committed Aug 2, 2009
1 parent 6d3f810 commit 70dfe22
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 4 deletions.
8 changes: 6 additions & 2 deletions lib/treetop/compiler/node_classes/character_class.rb
Expand Up @@ -5,8 +5,12 @@ def compile(address, builder, parent_expression = nil)
super

builder.if__ "has_terminal?(#{grounded_regexp(text_value)}, true, index)" 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
Expand Down
8 changes: 8 additions & 0 deletions lib/treetop/compiler/node_classes/parsing_expression.rb
Expand Up @@ -21,6 +21,10 @@ def inline_module_name
parent_expression && parent_expression.inline_module_name
end

def decorated?
parent_expression && (parent_expression.node_class_name || parent_expression.node_class_name || parent_expression.inline_module_name)
end

def optional_arg(arg)
if arg
", #{arg}"
Expand Down Expand Up @@ -89,6 +93,10 @@ def epsilon_node
def assign_failure(start_index_var)
assign_result("nil")
end

def assign_lazily_instantiated_node
assign_result("true")
end

def var_initialization
left, right = [], []
Expand Down
18 changes: 16 additions & 2 deletions lib/treetop/runtime/syntax_node.rb
@@ -1,19 +1,33 @@
module Treetop
module Runtime
class SyntaxNode
attr_reader :input, :interval, :elements
attr_reader :input, :interval
attr_accessor :parent

def initialize(input, interval, elements = nil)
@input = input
@interval = interval
if @elements = elements
elements.each do |element|
@elements.delete(true)
@elements.each do |element|
element.parent = self
end
end
end

def elements
return @elements if terminal?
# fill in any gaps in the sequence (lazy instantiation)
interval.each do |index|
unless @elements.any? {|element| element.interval.include?(index) }
node = SyntaxNode.new(input, index...(index + 1))
node.parent = self
@elements << node
end
end
@elements
end

def terminal?
@elements.nil?
end
Expand Down
57 changes: 57 additions & 0 deletions spec/compiler/character_class_spec.rb
Expand Up @@ -69,6 +69,18 @@ module ModFoo
parse(' 1', :index => 1).should be_nil
end
end

describe "a character class followed by a node class declaration and a block" do

testing_expression "[A-Z] <CharacterClassSpec::Foo>"

it "actively generates nodes for the character when it is the primary node" do
result = parse('A')
result.should be_a(Treetop::Runtime::SyntaxNode)
result.elements.should be_nil
end

end

describe "A character class containing quotes" do
testing_expression "[\"']"
Expand Down Expand Up @@ -178,5 +190,50 @@ module ModFoo
parse("0").should be_nil
end
end

describe "a character class" do
testing_expression "[A-Z]"
it "actively generates a node for the character because it is the primary node" do
result = parse('A')
result.should be_a(Treetop::Runtime::SyntaxNode)
result.elements.should be_nil
end
end

describe "a character class mixed with other expressions" do
testing_expression '[A-Z] "a"'
it "lazily instantiates a node for the character" do
result = parse('Aa')
result.instance_variable_get("@elements").size.should == 1
result.elements.size.should == 2
end
end

describe "a character class with a node class declaration mixed with other expressions" do
testing_expression '([A-Z] <CharacterClassSpec::Foo>) "a"'
it "actively generates a node for the character because it has a node class declared" do
result = parse('Aa')
result.instance_variable_get("@elements").size.should == 2
result.elements.size.should == 2
end
end

describe "a character class with a node module declaration mixed with other expressions" do
testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "a"'
it "actively generates a node for the character because it has a node module declared" do
result = parse('Aa')
result.instance_variable_get("@elements").size.should == 2
result.elements.size.should == 2
end
end

describe "a character class with an inline block mixed with other expressions" do
testing_expression '([A-Z] { def a_method; end }) "a"'
it "actively generates a node for the character because it has an inline block" do
result = parse('Aa')
result.instance_variable_get("@elements").size.should == 2
result.elements.size.should == 2
end
end

end
15 changes: 15 additions & 0 deletions spec/runtime/syntax_node_spec.rb
Expand Up @@ -50,4 +50,19 @@ module SyntaxNodeSpec
end
end
end

describe "A new nonterminal syntax node with all children lazily instantiated" do
attr_reader :node

before do
@input = 'test input'
@node = Runtime::SyntaxNode.new('input', 0...3, [true, true, true])
end

it "should lazily instantiate its child nodes" do
node.elements.size.should == 3
node.elements.first.interval.should == (0...1)
node.elements.first.parent.should == @node
end
end
end

0 comments on commit 70dfe22

Please sign in to comment.