Extending example grammar for Fsyacc with unary minus

907 Views Asked by At

I tried to extend the example grammar that comes as part of the "F# Parsed Language Starter" to support unary minus (for expressions like 2 * -5).

I hit a block like Samsdram here

Basically, I extended the header of the .fsy file to include precedence like so:

......
%nonassoc UMINUS
....

and then the rules of the grammar like so:

...
Expr: 
| MINUS Expr %prec UMINUS   { Negative ($2) }
...

also, the definition of the AST:

...
and Expr =
    | Negative of Expr
.....

but still get a parser error when trying to parse the expression mentioned above.

Any ideas what's missing? I read the source code of the F# compiler and it is not clear how they solve this, seems quite similar

EDIT

The precedences are ordered this way:

%left ASSIGN
%left AND OR
%left EQ NOTEQ LT LTE GTE GT
%left PLUS MINUS
%left ASTER SLASH
%nonassoc UMINUS
2

There are 2 best solutions below

1
On BEST ANSWER

Had a play around and managed to get the precedence working without the need for %prec. Modified the starter a little though (more meaningful names)

Prog:
    | Expression EOF { $1 }

Expression:
    | Additive { $1 }

Additive:
    | Multiplicative { $1 }
    | Additive PLUS  Multiplicative { Plus($1, $3)  }
    | Additive MINUS Multiplicative { Minus($1, $3) }

Multiplicative:
    | Unary { $1 }
    | Multiplicative ASTER Unary { Times($1, $3)  }
    | Multiplicative SLASH Unary { Divide($1, $3) }

Unary:
    | Value { $1 }
    | MINUS Value { Negative($2) }

Value:
    | FLOAT { Value(Float($1)) }
    | INT32 { Value(Integer($1)) }
    | LPAREN Expression RPAREN { $2 }

I also grouped the expressions into a single variant, as I didn't like the way the starter done it. (was awkward to walk through it).

type Value =
    | Float   of Double
    | Integer of Int32
    | Expression of Expression

and Expression =
    | Value of Value
    | Negative of Expression
    | Times  of Expression * Expression
    | Divide of Expression * Expression
    | Plus  of Expression * Expression
    | Minus of Expression * Expression

and Equation =
    | Equation of Expression
2
On

Taking code from my article Parsing text with Lex and Yacc (October 2007).

My precedences look like:

%left PLUS MINUS
%left TIMES DIVIDE
%nonassoc prec_uminus
%right POWER
%nonassoc FACTORIAL

and the yacc parsing code is:

expr:
| NUM                          { Num(float_of_string $1) }
| MINUS expr %prec prec_uminus { Neg $2 }
| expr FACTORIAL               { Factorial $1 }
| expr PLUS expr               { Add($1, $3) }
| expr MINUS expr              { Sub($1, $3) }
| expr TIMES expr              { Mul($1, $3) }
| expr DIVIDE expr             { Div($1, $3) }
| expr POWER expr              { Pow($1, $3) }
| OPEN expr CLOSE              { $2 }
;

Looks equivalent. I don't suppose the problem is your use of UMINUS in capitals instead of prec_uminus in my case?

Another option is to split expr into several mutually-recursive parts, one for each precedence level.