0

I’m trying to write an Erlang parser with Yecc, but I’m having some troubles with the precedence of the semantic rules. In my case I defined the grammar, the terminal and non-terminal symbols, the rules and the associated code.

This is what I wrote for testing.

%Grammar non terminals
Nonterminals    product require require1 mandatory mandatory1.

%Grammar  terminals
Terminals       'tick' 'feature' '(' ')' 'req' 'mand' ';' 'nil'. 

%Initial symbol
Rootsymbol product.

%Operands priority

Left 200 require.
Left 190 require1.
Left 180 mandatory.
Left 170 mandatory1.

Left    80  'req'.
Left    60  'mand'.
Left    50  ';'.        %Secuence
Left    40  'feature'.  %Optional feature



%--------------------------------------------------
%Grammar with operational rules

%[req1 & req2]
product -> require: '$1'.
require -> feature req feature '(' feature ';' product ')' :    if
                                                                    '$1' == '$5'    -> {'$5', {'$4', '$7', '$8', {mand,1}, '$3'}};
                                                                    true            -> {'$5', {'$1', '$2', '$3', '$4', '$7', '$8'}}
                                                                end.
%[req3]
product -> require1 : '$1'.
require1 -> feature req feature '(' tick ')' : {nil,1}.



%[mand2 & mand3]
product -> mandatory : '$1'.
mandatory -> '(' feature ';' product ')' mand feature : if
                                                        '$2' == '$7'    -> {'$2', {'$4'}};
                                                        true            -> {'$2',{'$1', '$4', '$5', '$6', '$7'}}
                                                    end.


%[mand1]
product -> mandatory1: '$1'.
mandatory1 -> '(' tick ')' mand feature : {$5, {tick,1}}.


%[tick]
product -> feature ';' tick : {'$1', {nil,1}}.
product -> nil.
product -> feature ';' product : {'$1', {'$3'}}.


Erlang code.    
%To remove brackets and return only the third parameter, right now is not used.
unwrap_feature({_,_,V}) -> V.


%%How to compile and use
%Save this as stack.yrl
%Run erl and then
%yecc:yecc("stack.yrl","stack.erl"). 
%c(stack).

Now lets execute a specific term to check how rules are applied.

stack:parse([{feature,1,'A'},{'req',1},{feature,1,'C'},{'(',1},{feature,1,'A'},{';',1},{feature,1,'B'},{';',1},{feature,1,'C'},{';',1},{tick,1},{')',1}]).

The parser output is:

{ok,{{feature,1,'A'},
     {{'(',1},
      {{feature,1,'B'},{{{feature,1,'C'},{nil,1}}}},
      {')',1},
      {mand,1},
      {feature,1,'C'}}}}

But I need this. I’m writing the output as long the parser process the term (like a debug output).

Initial term.

{feature,1,'A'},{'req',1},{feature,1,'C'},{'(',1},{feature,1,'A'},{';',1},{feature,1,'B'},{';',1},{feature,1,'C'},{';',1},{tick,1},{')',1}

Rule %[req1 & req2]. (This is applied correctly – Case '$1' == '$5')

{feature,1,'A'},{{'(',1},{feature,1,'B'},{';',1},{feature,1,'C'},{';',1},{tick,1},{')',1},{mand,1},{feature,1,'C'}}

Now, I don’t know what happens, but the output should be as this.

Rule %[mand2 & mand3]. (Case true)

{feature,1,'A'},{{feature,1,'B'},{{'(',1},{feature,1,'C'},{';',1},{tick,1},{')',1},{mand,1},{feature,1,'C'}}}

Rule %[mand2 & mand3]. (Case '$2' == '$7')

{feature,1,'A'},{{feature,1,'B'},{{feature,1,'C'},{{tick,1}}}}

Rule %[tick] – And final result.

{feature,1,'A'},{{feature,1,'B'},{{feature,1,'C'},{{{tick,1},{nil,1}}}}}

I already tried this:

As is explained in Yecc manual, I was able to do this:

  • Playing with the operator precedences.
  • Applying precedence to rules. From the documentation (It is also possible to declare precedence for non-terminals, "one level up". This is practical when an operator is overloaded (see also example 3 below)).

But it doesn’t seem to work for me. Any help???

Thanks!

librik
  • 3,738
  • 1
  • 19
  • 20
ccamacho
  • 707
  • 8
  • 22

2 Answers2

1

Just a few notes:

1. Left 200 require. Left 190 require1. Left 180 mandatory. Left 170 mandatory1. is not effective. Those are not operators as they are not present in righthand side of ->.

  1. Again about require, require1, mandatory and mandatory1: those are not conflicting rules. Just write product -> feature req feature '(' feature ';' product ')': …. product -> feature req feature '(' tick ')': …. …

  2. Why do you sometimes add {·,1}? That should be the lexer's job.

fenollp
  • 2,406
  • 1
  • 15
  • 14
  • Hi! Thanks for your answer. 1- I just putted some weight in the precedence part for the require , require1, mandatory and mandatory1 terms to see if change the behavior of the parser (Does not work :( ). 2- require, require1, mandatory and mandatory1 are auxiliary terms, also to check if by adding some weight precedence can change the behavior of the parser (As is detailed in the documentation). 3- You mean {mand,1}?? This is because y want to change the term to make it fit other rules.. At first it does not have {mand,1} instead it have {req,1}, so it matches some or other rules. – ccamacho Jan 09 '14 at 10:27
  • This is the reference for the documentation "This is practical when an operator is overloaded (see also example 3 below)", http://erldocs.com/R14B01couch/parsetools/yecc.html – ccamacho Jan 09 '14 at 10:28
1

I had the same problem in a parser for Lua on which I was working. The solution I found was that you need to use the operators within the same terminal for it to work. I could break it out into one terminal which handled all the binary operators which had a precedence:

bin_op -> exp '+' exp : ... .
bin_op -> exp '-' exp : ... .
...
bin_op -> exp 'and' exp -> ... .
bin_op -> exp 'or' exp -> ... .

So if you could something like

Left    80  'req'.
Left    60  'mand'.
Left    50  ';'.        %Secuence
Left    40  'feature'.  %Optional feature

product -> feature 'req' feature : ... .
product -> feature mand feature : ... .
product -> feature ';' feature : ... .

This is not of course not always possible. If you look at the examples which use precedence in the yecc documentation they are structured like this. Also if you look at the erlang grammar you will see that it does not use precedence but does each precedence level explicitly.

In your case with only four levels it should be relatively easy.

rvirding
  • 20,848
  • 2
  • 37
  • 56
  • I'll rewrite the rules as operand operator operand to see what happens. :) – ccamacho Jan 10 '14 at 09:17
  • Nope, i can not rewrite the rules as operand operator operand, as i.e. in rule [req1 & req2] i have to ask if '$1' == '$5'. I'll need that the whole rule, when it match a pattern have more priority than other rule who also match... I dont have more ideas about what to test.. :( – ccamacho Jan 10 '14 at 13:33
  • I don't think you can put operator precedence on non-terminals. I haven't tried doing it but the documentation only shows it for terminals. I have loaded down the .yrl and will try to understand it. Do you any better examples of how you would like it to handle various input, sort of [...] ==> {...}? Will try to get back later today or tomorrow. – rvirding Jan 10 '14 at 14:15
  • 1
    How would you write it **WITHOUT** precedences? – rvirding Jan 10 '14 at 14:25
  • I think this is the way to go, first write everything out explicitly **without** precedences. After that you can see if it is possible to use precedences. Do you have without precedences? Loaded it down and the grammar looks weird. – rvirding Jan 10 '14 at 14:35