CodeTree - Production

synopsis

Production basically means visiting a CodeTree and producing/collecting values on each node. There is two major ways for making productions in CodeTree: using the visit method or using a Producer.

the visit method

CodeTree nodes provide the following method

# Makes a depth-first-search visit of the code tree, and calls _visitor_ 
# on each node, passing two arguments:
#
#   node: the current node being visited
#   collected: values previously collected on its children
#
# Returns what _visitor_ returned.
def visit(&visitor)
  ...
end

Let instantiate the pattern to find all variable names referenced in an expression:

CodeTree::parse{ z * (x + y) }.visit do |node, collected|
  if node.function == :'?'
    node.literal
  else
    collected.flatten
  end
end
# => [:z, :x, :y]

Let evaluate the expression through the visit method (see Evaluation the shortcut way)

values = {:x => 3, :y => 6, :z => 2}
CodeTree::parse{ z * (x + y) }.visit do |node, collected|
  case node.function
    when :'?'
      values[node.literal]
    when :'+'
      collected[0] + collected[1]
    when :'*'
      collected[0] * collected[1]
  end
end
# => 18

using a producer

The Producer method allows you to have the control over the tree search and visit algorithm, that is, no depth-first-search is hard-coded. A producer works similarly to a basic XSLT engine:

The following example demonstrates the approach with some generic rules:

producer = CodeTree::producer(false){|p|
  
  # This rule matches leaf nodes (literals)
  p.rule(:'_') do |engine, node|
    puts "On: #{node.literal.inspect}"
  end
  
  # This rule matches everything else
  p.rule("*") do |engine, node| 
    puts "Before: #{node.inspect}"
    engine.apply(node.children)
    puts "After #{node.inspect}"
  end
  
}
producer.apply CodeTree::parse{ (concat "hello ", who) }

# Before: (concat (_ "hello "), (? (_ :who)))
#   On: "hello" 
#   Before: (? (_ :who))
#     On: :who
#   After: (? (_ :who))
# After: (concat (_ "hello "), (? (_ :who)))