0

In a functional language compiler written using the happy parser, which is a quite similar with yacc/bison, I implemented lists and with lists some core functions map, concat and filter, using the following rules:

Exp:
...
| concat '(' Exp ',' Exp ')'         { Concat $3 $5 }
| map '(' Exp ',' Exp ')'            { Map $3 $5 }
| filter '(' Exp ',' Exp ')'         { Filter $3 $5 }

This works just fine, but in most functional languages there is no paranthesis or commas, so instead of map(myfun, [1,2,3]) I would rather write map myfun [1,2,3]. The obvious modification in the grammar is the following:

Exp:
...
| concat Exp Exp         { Concat $2 $3 }
| map Exp Exp            { Map $2 $3 }
| filter Exp Exp         { Filter $2 $3 }

But this modification includes lots of reduce-reduce conflicts. How can I achieve the parsing of function calls without commas and paranthesis?

The smallest conflicting grammar I could extract was this:

Exp :
    -- Math
     Exp '+' Exp                         { Op $1 Add $3 }
    | Exp '-' Exp                        { Op $1 Sub $3 }

    -- Literals
    | num                                { Num $1 }
    | '-' num %prec NEGATIVE             { Num (-$2) }

    -- Lists
    | map Exp Exp                        { Map $2 $3 }

It generates 4 reduce/reduce conflicts. Removing any of the rules also ends up with the conflicts. Here is the full grammar if you are interested.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
fotanus
  • 19,618
  • 13
  • 77
  • 111
  • I converted the grammar on github to bison, and bison reported no conflicts. – rici Oct 06 '16 at 23:15
  • @ric that is because the full grammar linked has the rules as I stated first, not as I really want (second example on the copy of my post) – fotanus Oct 07 '16 at 02:51
  • :this is why we always ask for a [mcve]. – rici Oct 07 '16 at 05:09
  • @rici I have updated my question with the minimal grammar that generates reduce/reduce conflicts. I'm not sure how you manage to convert this to bison? If you need the full file to do that, [here it is](http://pastebin.com/raw/WnvTvpgc) – fotanus Oct 07 '16 at 16:10
  • 2
    The reduced example has to do with the ambiguity of `map f - 3 ...`, in which it is not clear whether `f` is the first argument to `map` (in which case `-3` is a negative number) or part of an infix operator. I doubt whether this can be solved using precedence declarations, but I could be wrong. It can certainly be solved (in a sense) grammatically, as Haskell itself does; in Haskell, if you wanted to write the negative number, you would have to use parentheses ( `map f (-3)` ) and perhaps that would be acceptable for your language as well. I'll try to write up an answer a bit later. – rici Oct 07 '16 at 23:28
  • @rici Thanks! using parenthesis for the -3 is definitively acceptable. I can't imagine right now how to structure the grammar taking this in consideration, but I'll try now that I understand better the problem. – fotanus Oct 08 '16 at 00:49

1 Answers1

1

The problem is that since there's no token in a function application, token-based precedence conflict resolution doesn't work very well -- when its trying to decide on a shift that might be a function application and a reduce of some other expression, the lookahead token is whatever the argument expression begins with; there no 'blank space' token that can be used.

To hack around that problem and make it work, you need to set the precedence of EVERY token that might being an expression (every token in FIRST(Exp)) to that of function application. If any of those tokens need some other precedence (eg, any token that might be either infix or prefix), this gets much trickier and might not work.

An alternative that might work better is to not use precedence rules at all -- instead, disambiguate the grammar with different rules for each level of precedence:

Exp: Term | Exp '+' Term
Term: Factor | Term '*' Factor
Factor: Primary | Factor Primary
Primary: num | id | '(' Exp ')'
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226