Module: NonTerminalNode

Defined in:
lib/rubypeg.rb

Overview

By default all non terminals that are returned by RubyPeg#parse are Arrays that have been extended with the NonTerminalNode module

If we consider this example:

class BasketPeg < RubyPeg
def root
  node :basket do
    one_or_more { items }
  end
end

def items
  node :item do
    number && optional_space && fruit && optional_space
  end
end

def number
  terminal(/\d+/)
end

def fruit
  node :fruit do
    (terminal("apple") || terminal("pear")) && ignore{ optional{ terminal("s") } }
  end
end

def optional_space
  ignore{ optional{ terminal(" ") }}
end
end

Then BasketPeg.parse("1 apple 2 apples 3 pears").should be_kind_of(NonTerminalNode)

This is an array of children of this non terminal. The children may be other non-terminals or terminals The array will be empty if there are no children.

basket = BasketPeg.parse("1 apple 2 apples 3 pears")
basket.class.should == Array
basket.size.should == 3
basket.first.should be_kind_of(NonTerminalNode)
basket.first.type.should == :item
basket.first.class.should == Array
basket.first.size.should == 2
basket.first.first.should be_kind_of(TerminalNode)
basket.first.first.should == "1"
basket.first.last.should be_kind_of(NonTerminalNode)
basket.first.last.type == :fruit
basket.first.last.class.should == Array
basket.first.last.size.should == 1
basket.first.last.first.should be_kind_of(TerminalNode)
basket.first.last.first.should == "apple"

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#type ⇒ Object

Contains the argument given to RubyPeg#node BasketPeg.parse("1 apple 2 apples 3 pears").type.should == :basket



67
68
69
# File 'lib/rubypeg.rb', line 67

def type
  @type
end

Instance Method Details

#inspect ⇒ Object

Lists the non-terminal node and its children. Same content as #to_ast but in string form. BasketPeg.parse("1 apple 2 apples 3 pears").inspect.should == '[:basket, [:item, "1", [:fruit, "apple"]], [:item, "2", [:fruit, "apple"]], [:item, "3", [:fruit, "pear"]]]'



119
# File 'lib/rubypeg.rb', line 119

def inspect; to_ast.inspect end

#to_ast ⇒ Object

Returns the node network as an abstract syntax tree

BasketPeg.parse("1 apple 2 apples 3 pears").to_ast.should == [:basket, [:item, "1", [:fruit, "apple"]], [:item, "2", [:fruit, "apple"]], [:item, "3", [:fruit, "pear"]]]

Note that the items wrapped in ignore {} in the parser, shuch as the spaces and the optional 's' in apples and pears do not appear.



113
114
115
# File 'lib/rubypeg.rb', line 113

def to_ast
  [type,*self.map(&:to_ast)]
end

#to_s ⇒ Object

Returns the result of calling to_s on each of its children. By default, TerminalNode#to_s returns its text value, so:

BasketPeg.parse("1 apple 2 apples 3 pears").to_s.should == "1apple2apple3pear"

Note that the items wrapped in ignore {} in the parser, shuch as the spaces and the optional 's' in apples and pears do not appear.



124
# File 'lib/rubypeg.rb', line 124

def to_s; self.map(&:to_s).join end

#visit(builder = nil) ⇒ Object

This is a quick way of carrying out the visitor pattern on the parsed structure.

If no visitor is supplied then a nested array of child nodes is returned, with terminals turned into strings:

BasketPeg.parse("1 apple 2 apples 3 pears").build.should == [["1", "apple"], ["2", "apple"], ["3", "pear"]]

If a visitor is supplied, then each non terminal node checks if there is a method on the visitor with a name the same as the non terminal's type. If there is, then the method is called with the children of the non terminal as arguments. If there isn't, then the build methods on the children of this node ar recursively called. E.g.,:

BasketPeg.parse("1 apple 2 apples 3 pears").build.should == [["1", "apple"], ["2", "apple"], ["3", "pear"]]
class BasketPegBuilderExample
attr_accessor :total

def initialize
  @total = 0
end

def item(number,kind)
  @total = @total + (number.to_f * kind.build(self).to_f)
end

def fruit(kind_of_fruit)
  case kind_of_fruit
  when "apple"; 3.0
  when "pear"; 1.0
  else  10.0
  end
end
end
counter = BasketPegBuilderExample.new
BasketPeg.parse("1 apple 2 apples 3 pears").build(counter)
counter.total.should == 12.0


103
104
105
106
107
# File 'lib/rubypeg.rb', line 103

def visit(builder = nil)
  return builder.send(type,*self) if builder.respond_to?(type)
  return self.first.visit(builder) if self.size == 1
  self.map { |c| c.visit(builder) }
end