[Haskell-cafe] Operator precedence and associativity on happy grammars

j.romildo at gmail.com j.romildo at gmail.com
Thu May 7 08:59:47 EDT 2009


Hello.

I am learning how to use Happy (a LALR(1) parser generator) and I have a
question on a grammar based on an example from the manual. The input
file to Happy is attached to this message.

The grammar is:

   Exp -> let var = Exp in Exp
   Exp -> Exp + Exp
   Exp -> Exp - Exp
   Exp -> Exp * Exp
   Exp -> Exp / Exp
   Exp -> ( Exp )

This grammar is ambiguous, but the conflicts that will appear in the
parser table can be resolved with precedence and associativity
declarations for the operators that surround expressions:

   %right in
   %left + -
   %left * /

giving right associativity to the in token, and left associativity to
the arithmetic operators, and giving lower precedence to in, followed by
+ and -, and then by * and /.

I see that if the token in is not given a precedence declaration, the
grammar still is ambiguous, as can be seen in the example of parsing the
expression:

   let v = e1 in e2 + e3

which could be parsed as

   (let v = e1 in e2) + e3

or

   let v = e1 in (e2 + e3)

Giving the token in a lower precedence makes the parser choose the last
option.

My question is about the associativity of the token in? Does it make any
difference giving it left associativity, right associativity, or making
it non associative?

Regards.

José Romildo
-------------- next part --------------
-- calc3.y    -*- mode: haskell -*-
{
module Main where

import Data.Char (isSpace, isDigit, isAlpha, isAlphaNum)
import Data.Maybe (fromMaybe)
}

%name calc
%tokentype { Token }
%error { parseError }

%token
    let { TokenLet }
    in  { TokenIn }
    int { TokenInt $$ }
    var { TokenVar $$ }
    '=' { TokenEq }
    '+' { TokenPlus }
    '-' { TokenMinus }
    '*' { TokenTimes }
    '/' { TokenDiv }
    '(' { TokenLP }
    ')' { TokenRP }


%nonassoc in
%left '+' '-'
%left '*' '/'
%left NEG

%%

Exp : let var '=' Exp in Exp { \env -> $6 (($2,$4 env) : env)      }
    | Exp '+' Exp            { \env -> $1 env + $3 env             }
    | Exp '-' Exp            { \env -> $1 env - $3 env             }
    | Exp '*' Exp            { \env -> $1 env * $3 env             }
    | Exp '/' Exp            { \env -> $1 env `div` $3 env         }
    | '(' Exp ')'            { $2                                  }
    | '-' Exp %prec NEG      { \env -> - ($2 env)                  }
    | int                    { \env -> $1                          }
    | var                    { \env -> fromMaybe 0 (lookup $1 env) }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

data Token
    = TokenLet
    | TokenIn
    | TokenInt Int
    | TokenVar String
    | TokenEq
    | TokenPlus
    | TokenMinus
    | TokenTimes
    | TokenDiv
    | TokenLP
    | TokenRP
    deriving (Show)

lexer :: String -> [Token]
lexer [] = []
lexer (c:cs) | isSpace c = lexer cs
             | isAlpha c = let (name,rest) = span isAlphaNum cs
                               tok = case c:name of
                                       "let" -> TokenLet
                                       "in"  -> TokenIn
                                       var   -> TokenVar var
                           in tok : lexer rest
             | isDigit c = let (num,rest) = span isDigit cs
                           in TokenInt (read (c:num)) : lexer rest
lexer ('=':cs) = TokenEq    : lexer cs
lexer ('+':cs) = TokenPlus  : lexer cs
lexer ('-':cs) = TokenMinus : lexer cs
lexer ('*':cs) = TokenTimes : lexer cs
lexer ('/':cs) = TokenDiv   : lexer cs
lexer ('(':cs) = TokenLP    : lexer cs
lexer (')':cs) = TokenRP    : lexer cs

main = do input <- getContents
          mapM_ print (map (($ []) . calc . lexer) (lines input))
}


More information about the Haskell-Cafe mailing list