knipknap/Gelatin

grammar execution oddity/bug

Opened this issue · 2 comments

There appears to be a bug or quirk about how functions are executed in a grammar match. Given the following Gelatin snippet:

grammar doset:
    match bareword:
        out.open('set?attr="$0"')
        addelement()
        do.return()

When "bareword" is matched, everything goes as expected, until the addelement() grammar returns. At the time it returns, the next thing that happens is NOT the call to do.return(), but that the doset() grammar loops and starts again.

Context: I'm trying to parse a text snippet the following:

config system interface
    edit "BYOD VLAN"
        set dhcp-relay-service "enable"
        set dhcp-relay-ip "10.90.2.101" "10.90.2.109" "10.90.2.110"
        set allowaccess "ping"
    next
end

Into the following XML:

<xml>
  <system-interface>
    <edit dqname="BYOD VLAN">
      <dhcp-relay-service>"enable"</dhcp-relay-service>
      <set attr="dhcp-relay-ip">
        <val>"10.90.2.101"</val>
        <val>"10.90.2.109"</val>
        <val>"10.90.2.110"</val>
      </set>
      <allowaccess>"ping"</allowaccess>
    </edit>
  </system-interface>
</xml>

The number of parameters after "set dhcp-relay-ip ..." can be arbitrary.

I have a gelatin file that is close to what I want:

#------------------------------------------------------------------------------------
define eol /[ \t]*[\r\n]/
define ws /[ \t]+/

define bareword       /[^"\s]+/
define dqstring        /"(?:(\\[^\r\n]|[^"\r\n])*)"/

define config   /\s*config/
define edit     /\s*edit/
define end      /\s*end/
define next     /\s*next/
define set      /\s*set/

grammar addelement:
    match ws dqstring:
        out.create('val', '$1')
    match ws bareword:
        out.add('$1', 't')
    match eol:
        do.return()

## The "do.return() in 'match bareword:' is not honored
## The "match ws:" statement below it is the hack for working around this problem.
grammar doset:
    match bareword ws dqstring eol:
        out.add('$0', '$2')
        do.return()
    match bareword:
        out.open('set?attr="$0"')
        addelement()
        do.return()
    match ws:
        do.return()

grammar parse2next:
    match next:
        do.return()
    match set ws:
        doset()

grammar parse2end:
    match end eol:
        do.return()
    match edit ws dqstring eol:
        out.open('edit?dqname=$2')
        parse2next()

grammar input:
    match config ws 'system' ws bareword eol:
        out.open('system-$4')
        parse2end()

#------------------------------------------------------------------------------------

In looking through the source, it looks like "do.return()" can take a numeric parameter to indicate how many levels back to go. However, using "do.return(2)" in the addelement() grammar doesn't seem to work either.

Thoughts?

- Daniel

The formatting in your message seems broken, and without meaningful indent I can't tell what the problem is. This is the intended behavior:

“match” statements in each grammar are executed sequentially. If a match is found, the indented statements in the match block are executed. After reaching the end of a match block, the grammar restarts at the top of the grammar block.

I see the broken formatting now. I've edited that comment so that it renders properly.

What you're describing is what I was expecting, but not what I was seeing. Let me try to give just a couple of grammar sections, and describe what I was seeing:

grammar inside:
    match pattern1:
        out.add('$0', '$2')
    match pattern2:
        do.return()

grammar outside:
    match pattern3:
        out.add('element', 'value')
        do.return()
    match pattern4:
        do.say('i am before')
        inside()
        do.say('i am after')
        do.return()

When the 'outside' grammar is invoked, and pattern4 is matched, Gelatin will output "i am before" and begin using the 'inside' grammar.

When the inside grammar reaches pattern2, it returns as expected to the outside() grammar, but not to continuing to execute the rest of the statements under 'match pattern4:'. What I observe it doing is jumping back to the top of the outside() grammar (as if there were a "do.next()" immediately after the call to inside()) and start parsing again. The do.say('i am after') and the do.return() at the bottom of 'match pattern4:' are never executed.

Does that help?