5
votes

I'm having trouble with a simple grammar I have created to support function calls.
I am using the lemon-based PHP_ParserGenerator by Greg.

This is the relevant part of the grammar:

program ::= expr(A).                        { $this->result = A; }

value(A) ::= SIMPLE_STRING(B).              { A = B; }
value(A) ::= NUMBER(B).                     { A = B; }
value(A) ::= CONTEXT_REFERENCE(B).          { A = B; }

arg_list ::= arg_list SEPARATOR value(B).   { $this->args[] = B; }
arg_list ::= value(B).                      { $this->args[] = B; }
arg_list ::= . 

expr(A) ::= SIMPLE_STRING(B) PAREN_LEFT arg_list PAREN_RIGHT. { A = call_user_func_array(B, $this->args); }

expr(A) ::= CONTEXT_REFERENCE(B). {
    list($context, $key) = explode('.', B);
    A = $this->context[$context][$key]; 
}

When I initialize the parser with a context of array('user' => array('name' => 'Dennis')); and execute the following code:

$parser->doParse(PelParser::CONTEXT_REFERENCE, 'user.name');
$parser->doParse(0, 0);

The $result is as follows: 'Dennis'. Life is good.

But when I supply a CONTEXT_REFERENCE as argument to a function call, it doesn't work:

$parser->doParse(PelParser::SIMPLE_STRING, 'str_replace');
$parser->doParse(PelParser::PAREN_LEFT, '(');
$parser->doParse(PelParser::SIMPLE_STRING, 'e');
$parser->doParse(PelParser::SEPARATOR, ',');
$parser->doParse(PelParser::NUMBER, 3);
$parser->doParse(PelParser::SEPARATOR, ',');
$parser->doParse(PelParser::CONTEXT_REFERENCE, 'user.name');
$parser->doParse(PelParser::PAREN_RIGHT, ')');
$parser->doParse(0, 0);

The $result is 'us3r.nam3'. Not quite as expected. For the record, the expected output is of course 'D3nnis'. (user.name first gets replaced with the string 'Dennis' and after that passed to the str_replace() function).

I suspect it has something to do with precedence. But I can't figure what I should change to make this. The very sparse Lemon documentation is not of very great help.

Any help would be greatly appreciated! Thanks

1

1 Answers

1
votes

I seem to have found the answer to my question.

When I change my grammar to:

program ::= expr(A).                        { $this->result = A; }

value(A) ::= SIMPLE_STRING(B).              { A = B; }
value(A) ::= NUMBER(B).                     { A = B; }
value(A) ::= CONTEXT_REFERENCE(B). {
    // B=='{context}.{name}' 
    list($context, $key) = explode('.', B);
    A = $this->context[$context][$key]; 
}

arg_list ::= arg_list SEPARATOR value(B).   { $this->args[] = B; }
arg_list ::= value(B).                      { $this->args[] = B; }
arg_list ::= . 

expr(A) ::= SIMPLE_STRING(B) PAREN_LEFT arg_list PAREN_RIGHT. { A = call_user_func_array(B, $this->args); }

It seems to work as expected.
The problem was that I created an ambiguity in the first grammar:

value(A) ::= CONTEXT_REFERENCE(B).          { A = B; }

expr(A) ::= CONTEXT_REFERENCE(B). {
    list($context, $key) = explode('.', B);
    A = $this->context[$context][$key]; 
}

I'll leave the question and answer here so others might benefit from my mistakes :) If somebody has anything to share, please do.