0

I am having problems with a grammer I am using to make a CUP Parser. The grammer is as follows: In CUP format.

terminal           PLUS, MINUS, TIMES, DIV, EQUALS, LESS, IF, THEN, ELSE, LET, EQ, IN, FUN
                    , ARROW, LPAREN, RPAREN, INVALID_TOKEN, APPL;
terminal String   NUMBER, ID;
   
// Non terminals used in the grammar section.
//non terminal exprList;
non terminal Expr expr, cExpr;

precedence left LPAREN, RPAREN;
precedence left  PLUS, MINUS;
precedence left TIMES, DIV;
precedence left LESS;
precedence left EQUALS;
precedence right EQ;
precedence right ARROW;

start with cExpr;
/* ----------------------------Grammar Section-------------------- */
// to do: implement function application

cExpr ::=
    IF cExpr:ifExp THEN cExpr:thenExp ELSE cExpr:elseExp
        {: RESULT = new ExprIfThenElse(ifExp, thenExp, elseExp); :}
    | LET ID:id EQ cExpr:value IN cExpr:expression
        {: RESULT = new ExprLetIn(id, value, expression); :}
    | FUN ID:id ARROW cExpr:e
        {: RESULT = new ExprLambda(id, e); :}
    | rExpr:e
        {:RESULT = e;:}
;

expr ::=
    expr:l PLUS expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Plus); :}
    | expr:l TIMES expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Times); :}
    | expr:l DIV expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Div); :}
    | expr:l MINUS expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Minus); :}
    | expr:l LESS expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Less); :}
    | expr:l EQUALS expr:r
        {: RESULT = new ExprBinary(l, r, Operator.Equals); :}
    |expr:exprFunc expr:arg
        {: RESULT = new ExprFuncApp(exprFunc, arg); :}
    | NUMBER:n
        {: RESULT = new ExprNumber(n); :}
    | ID:i
        {: RESULT = new ExprId(i); :}
    | LPAREN cExpr:e RPAREN
         {: RESULT = e; :}
;

Or written out:


CExp -> if CExp then CExp else CExp 
      | let if = CExp in CExp 
      | fun id -> CExp 
      | Exp 
Exp  -> Exp op Exp 
      | Exp Exp 
      | {identifier} 
      | {integer literal} 
      | ( CExp ) 
op   -> + | - | * | / | < | ==

My main problem resides with the function operator Exp Exp. "f 4" would mean something like function f with value 4 as argument. But as this grammer is obviously ambiguous (for example for the input "5 * f 1") i tried to make it unambiguous.

I tried to make it unambiguous was making a step inbetween with the Exp Exp operator:

terminal           PLUS, MINUS, TIMES, DIV, EQUALS, LESS, IF, THEN, ELSE, LET, EQ, IN, FUN
                    , ARROW, LPAREN, RPAREN, INVALID_TOKEN, APPL;
terminal String   NUMBER, ID;
   
// Non terminals used in the grammar section.
//non terminal exprList;
non terminal Expr expr, cExpr,rExpr;


precedence left LPAREN, RPAREN;
precedence left  PLUS, MINUS;
precedence left TIMES, DIV;
precedence left LESS;
precedence left EQUALS;
precedence right EQ;
precedence right ARROW;
precedence left APPL;

start with cExpr;
/* ----------------------------Grammar Section-------------------- */
// to do: implement function application

cExpr ::=
    IF cExpr:ifExp THEN cExpr:thenExp ELSE cExpr:elseExp
        {: RESULT = new ExprIfThenElse(ifExp, thenExp, elseExp); :}
    | LET ID:id EQ cExpr:value IN cExpr:expression
        {: RESULT = new ExprLetIn(id, value, expression); :}
    | FUN ID:id ARROW cExpr:e
        {: RESULT = new ExprLambda(id, e); :}
    | rExpr:e
        {:RESULT = e;:}
;

expr ::=
    expr:l PLUS rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Plus); :}
    | expr:l TIMES rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Times); :}
    | expr:l DIV rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Div); :}
    | expr:l MINUS rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Minus); :}
    | expr:l LESS rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Less); :}
    | expr:l EQUALS rExpr:r
        {: RESULT = new ExprBinary(l, r, Operator.Equals); :}
    | NUMBER:n
        {: RESULT = new ExprNumber(n); :}
    | ID:i
        {: RESULT = new ExprId(i); :}
    | LPAREN cExpr:e RPAREN
         {: RESULT = e; :}
;
rExpr ::=
    expr:e
        {:RESULT = e;:}
    |rExpr:exprFunc expr:arg
        {: RESULT = new ExprFuncApp(exprFunc, arg); :} %prec APPL
;

This way i hoped it would make it unambigous, which is not the case...
I tried my hand on trying with precedence rules of CUP but I also did not get anywhere with that.\ It is a LR(0) Parser, so i also couldn't use look aheads. If someone could give me a tip on how to make this grammer unambiguous i would appreciate it.

  • CUP produces LALR(1) parsers, not LR(0). Operator-less constructs, like implicit multiplication or implicit function application, do not usually yield to operator precedence declarations, for the simple reason that there is no operator. – rici Nov 11 '22 at 17:26
  • You need to say what you believe the precedence of function application should be. Presumably `5 * f 1` is intended to be `5 * (f 1)`. But what about `f x * 5`? Is it `f (x * 5)`? Or `(f x) * 5`? Also, how does it associate? Is `f x y` `(f (x y))`? Or `((f x) y)`? – rici Nov 11 '22 at 17:35
  • Last question (at least until you answer some of them). Now that I had a chance to look more closely at your grammar, I notice that you don't allow `4 * if x > 0 then 1 else -1`, which is not (at least to my eye) ambiguous. That's a pretty common decision, which I believe is the result of either not understanding how to do asymmetric precedence, or not wanting to do it (for whatever aesthetic reason, including not wanting to document it). But it's not the only decision, and it's actually pretty easy to allow it (along with the equivalent with `let`); the question is, which would you prefer? – rici Nov 11 '22 at 20:27
  • I'm sorry i could not respond sooner, thank you for your help. I came up with a solution by myself over the weekend. Im going to post the solution below, it was very simple, just change the grammar to take into account associativity. – Gabriel Sánchez Nov 16 '22 at 10:15

1 Answers1

0

This is the solution i came up with, I hope someone benefits from it. It is pretty simple and i only needed to account for associativity and change the grammer to make it unambiguous, instead of relying on precedence rules.

    /* ------------Declaration of Terminals and Non Terminals Section----------- */
   
//Terminals (tokens returned by the scanner).  
terminal           PLUS, MINUS, TIMES, DIV, EQUALS, LESS, IF, THEN, ELSE, LET, EQ, IN, FUN
                    , ARROW, LPAREN, RPAREN, INVALID_TOKEN, APPL;
terminal String   NUMBER, ID;
   
// Non terminals used in the grammar section.
//non terminal exprList;
non terminal Expr expr, cExpr, ter, plusExpr, multExpr, lessExpr, eqExpr , funcExpr;

precedence right EQ;
precedence right ARROW;

start with cExpr;
/* ----------------------------Grammar Section-------------------- */

cExpr ::=
    IF cExpr:ifExp THEN cExpr:thenExp ELSE cExpr:elseExp
        {: RESULT = new ExprIfThenElse(ifExp, thenExp, elseExp); :}
    | LET ID:id EQ cExpr:value IN cExpr:expression
        {: RESULT = new ExprLetIn(new ExprId(id), value, expression); :}
    | FUN ID:id ARROW cExpr:e
        {: RESULT = new ExprLambda(new ExprId(id), e); :}
    | plusExpr:e
        {:RESULT = e;:}
;
plusExpr ::=
    plusExpr:l PLUS multExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Plus); :}
    | plusExpr:l MINUS multExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Minus); :}
    | multExpr:t
            {: RESULT = t;:}
;

multExpr ::=
    multExpr:l TIMES lessExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Times); :}
    | multExpr:l DIV lessExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Div); :}
    | lessExpr:s
            {: RESULT = s;:}
;

lessExpr ::=
    lessExpr:l LESS eqExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Less); :}
    | eqExpr:u
            {: RESULT = u;:}
;

eqExpr ::=
    | eqExpr:l EQUALS funcExpr:r
            {: RESULT = new ExprBinary(l, r, Operator.Equals); :}
    | funcExpr:w
        {: RESULT = w;:}
;

funcExpr ::=
    funcExpr:exprFunc expr:arg
            {: RESULT = new ExprFuncApp(exprFunc, arg); :}
    | expr:e
        {:RESULT = e;:}
;

expr ::=
    ter:t
        {: RESULT = t ;:}
    | LPAREN cExpr:e RPAREN
         {: RESULT = e; :}
;

ter ::=
    NUMBER:n
        {: RESULT = new ExprNumber(n); :}
    | ID:i
        {: RESULT = new ExprId(i); :}
;