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

#typeObject

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

#inspectObject

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_astObject

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_sObject

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