How to code an action trigger with treetop?

77 views Asked by At

I'm trying to have some bit of code running each time the parser recognises a token.

Let's say

grammar FooBar

  rule start
    (foo "\n")+
  end

  rule foo
    stuff_i_want:([a-z]+) {
       puts "Hi there I found: #{stuff_i_want.text_value}"
     }
  end

end

The idea here would be to have this puts action execute each time the foo token is found. Coded as is, it does not work as it is triggered only once (at the class loading time) and of course stuff_i_want.text_value does not exist then.

Any idea? Is it even possible? The lack of documentation on the library does not make it easy to tell.

2

There are 2 answers

0
The Mighty Rubber Duck On BEST ANSWER

Well, I'm not sure what I did to deserve the downvote.

Anyway, here is the solution I used:

node_extension.rb

module Crawlable

  def crawl *args
    continue = true
    continue = action(*args) if respond_to? :action

    return if !continue || elements.nil?

    elements.each do |elt|
      elt.crawl(*args)
    end
  end

end

# reopen the SyntaxNode class and include the module to add the functionality
class Treetop::Runtime::SyntaxNode

  include Crawlable

end

then all is left is to define a action(*args) method on each node you want to trigger an effect on and have to start the crawling on the top parser node (the one returned by the parse call

parse_tree = FooBarParser.new.parse "mycontent"
parse_tree.crawl # add optional parameters for context/state

The optional parameters are passed to each action method. You can also return a falsey value (false or nil) in action in order to stop the subtree crawling.

grammar FooBar

  rule start
    (foo "\n")+
  end

  rule foo
    stuff_i_want:([a-z]+) {
       def action
         puts "Hi there I found: #{stuff_i_want.text_value}"

         false
       end
     }
  end

end
2
Josh Voigts On

This might be a simpler solution than the one you can up with. I can't see why you would need open up the SyntaxNode class in order to get the functionality that you want. All you need to do is traverse the nodes a little bit more (unless I'm not understanding what you're trying to accomplish).

Here's an example:

require 'treetop'

Treetop.load_from_string DATA.read

parser = FooBarParser.new

parser.parse("hello\nok\nmorestuff\n").action

__END__
grammar FooBar
  rule start
     (foo "\n")+
     {
        def action
           elements.each {|e| e.elements[0].action }
        end
     }
  end

  rule foo
    stuff_i_want:([a-z]+)
    {
       def action
          puts "Hi there I found: #{stuff_i_want.text_value}"
       end
    }
  end
end

# => Hi there I found: hello
#    Hi there I found: ok
#    Hi there I found: morestuff