rubypegtreetop

Custom Methods for Treetop Syntax Nodes


I have a Treetop PEG grammar that matches some keys. I want to look up the values associated with those keys in a hash I give the parser. How can I make it so that the syntax nodes have access to methods or variables from the parser?

For example, here's a simple grammar that finds a single word and tries to look up its value:

# var.treetop
grammar VarResolver
  include VarLookup

  rule variable
    [a-zA-Z] [a-zA-Z0-9_]*
    {
      def value
        p found:text_value
        find_variable(text_value)
      end
    }
  end
end

Here's a test file using it:

# test.rb
require 'treetop'

module VarLookup
  def set_variables(variable_hash)
    @vars = variable_hash
  end
  def find_variable(str)
    @vars[str.to_sym]
  end
end

Treetop.load('var.treetop')

@p = VarResolverParser.new
@p.set_variables name:'Phrogz'
p @p.parse('name').value

Running this test, I get the output:

{:found=>"name"}
(eval):16:in `value': undefined method `find_variable'
     for #<Treetop::Runtime::SyntaxNode:0x00007f88e091b340> (NoMethodError)

How can I make find_variable accessible inside the value method? (In the real parser, these rules are deeply nested, and need to resolve the value without returning the actual name to the top of the parse tree. I cannot just return the text_value and look it up outside.)


Solution

  • This is a significant weakness in the design of Treetop.

    I (as maintainer) didn't want to slow it down further by passing yet another argument to every SyntaxNode, and break any custom SyntaxNode classes folk have written. These constructors get the "input" object, a Range that selects part of that input, and optionally an array of child SyntaxNodes. They should have received the Parser itself instead of the input as a member.

    So instead, for my own use (some years back), I made a custom proxy for the "input" and attached my Context to it. You might get away with doing something similar:

    https://github.com/cjheath/activefacts-cql/blob/master/lib/activefacts/cql/parser.rb#L203-L249