Block-scope: how to get Match instance of a selected rule #29

Closed
zipizap opened this Issue Jul 20, 2011 · 3 comments

Projects

None yet

2 participants

@zipizap
zipizap commented Jul 20, 2011

I'm having a hard time with this issue... I need to make a rule, that groups severall other rules, so to choose one from the group. The selected rule of the group should then be called by it's #value method.

It seems pretty easy, but somehow I can't figure out how to call the #value method on the group-selected-rule... my difficulty is in getting a handle to the group-selected-rule...

require 'citrus'

Citrus.eval(<<CITRUS)
grammar G
  rule all_the_candidates
    (cand1|cand2) { 
        matched_candidate = ....  # I want to call .value on the selected rule ("cand1" or "cand2" Match instance)
        matched_candidate.value   # How do I get the selected rule (in this case, cand1 or cand2)??   
      }
  end

  rule cand1
    '111' { "I'm 111" }
  end

  rule cand2
    '222' { "I'm 222" }
  end
end
CITRUS

match = G.parse('222')
puts match.value
zipizap commented Jul 20, 2011

This keeps itching and itching under my skin... and I feel I did not reported clearly my doubt previously

I'll try to contrast the situation in 3 examples - the focus is in how to "grab" the matched_rule in order to call on it a method - #mm in first example, and #value in second and third example.

First example:

require 'citrus'

Citrus.eval(<<CITRUS)
grammar G
  rule select_candidate
    # We want to call #mm on the matched_rule (cand111 or cand222)
    # To get the matched_rule, we use captures[0]
    # This works well, as expected
    (cand111|cand222)  { 
      matched_rule = captures[0]     # get the rule that matched: cand111 or cand222 (more specifically, we get the Match instance of that rule)      
      matched_rule.mm                # call #mm on the matched_rule, all works well
    }     
  end

  rule cand111
    '111' { def mm; "I'm 111"; end }
  end

  rule cand222
    '222' { def mm; "I'm 222"; end }
  end
end
CITRUS

match = G.parse('222')
puts match.value                      #  => "I'm 222"

Second example:

require 'citrus'

Citrus.eval(<<CITRUS)
grammar G
  rule select_candidate
    # We want to call #value on the matched_rule (cand222 or cand222)
    # To get the matched_rule, we use captures[0]
    # This does not work: when matched_rule.value is called, the program enters in  
    #  a loop indefinitively/recursively and finally break with an error ('<main>:1: stack level too deep (SystemStackError)')
    # However, if we instead called matched_rule.mm, it would have worked... 
    #
    # ? Why does it work with a used-defined method such as #mm but
    #    does not work with the #value method 
    # ? Is it captures[0] a reliable way to get the matched_rule ?
    # 
    # Note: 
    #   If we delete the block from this rule, everything will work as expected - see the third example
    (cand111|cand222)  { 
      matched_rule = captures[0]     # get the rule that matched: cand111 or cand222 (more specifically, we get the Match instance of that rule)      
      matched_rule.value             # call #value on the matched-rule - all goes wrong, program loops indefinitively/recursively and breaks
    }     
  end

  rule cand111
    '111' { "I'm 111" }
  end

  rule cand222
    '222' { "I'm 222" }
  end
end
CITRUS

match = G.parse('222')
puts match.value                      # never gets here - program breaks before

Third example:

require 'citrus'

Citrus.eval(<<CITRUS)
grammar G
  rule select_candidate
    # We want to call #value on the matched_rule (cand222 or cand222)
    # To do so, we simply leave the expanding block undefined and it seems to delegate the execution to the matched-rule block
    # However, I don't understand the reason for it... I could find this "workaround" but could not understand it...
    (cand111|cand222)  # no expanding block defined 
  end

  rule cand111
    '111' { "I'm 111" }
  end

  rule cand222
    '222' { "I'm 222" }
  end
end
CITRUS

match = G.parse('222')
puts match.value                      #  => "I'm 222"

Please explain me where is the pitfall
I've already searched though the documentation (which is properly made and very clear, thanks :) ) and over other resolved Issues in github, but found no lead to understand this behaviour... and I was having a lot of fun with ruby/citrus untill I got stuck in this...

Thanks in advance, zipizap

Owner

Try using super.

require 'citrus'

Citrus.eval(<<CITRUS)
grammar G
  rule all_the_candidates
    (cand1 | cand2) {
      super
    }
  end

  rule cand1
    '111' { "I'm 111" }
  end

  rule cand2
    '222' { "I'm 222" }
  end
end
CITRUS

match = G.parse('222')
puts match.value

The key thing you need to remember is that the block for all_the_candidates is going to be evaluated on an instance of a Match object that was generated by that rule. When Match objects are generated, they are extended by the modules associated with all rules that were responsible for generating that match, in the order they matched. So, in this case, calling super on the Match object that results from all_the_candidates will simply call the value method of either a cand1 match or a cand2 match.

Also, in the future please ask questions like this on the citrus-users Google Group so that the issue tracker remains solely for bugs and such. Thanks!

@mjackson mjackson closed this Dec 29, 2011
zipizap commented Feb 12, 2012

Ok, I see now - thanks for your time Michael
Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment