Search code examples
haskellparsec

Adding infix operator to expression parser


I'm trying to add a parser for infix operators to a simple expressions parser. I have already looked at the documentation and at this question, but it seems like I am missing something.

import qualified Text.Parsec.Expr as Expr
import qualified Text.Parsec.Token as Tokens
import Text.ParserCombinators.Parsec
import Text.Parsec

data Expr = Number Integer
          | Op Expr Expr
          | Boolean Bool
      
instance Show Expr where
  show (Op l r) = "(+ " ++ (show l) ++ " " ++ (show r) ++ ")"
  show (Number r) = show r
  show (Boolean b) = show b

parens = Tokens.parens haskell
reserved = Tokens.reservedOp haskell

infix_ operator func =
  Expr.Infix (spaces >> reserved operator >> spaces >> return func) Expr.AssocLeft

infixOp =
  Expr.buildExpressionParser table parser
  where
    table = [[infix_ "+" Op]]

number :: Parser Expr
number = 
  do num <- many1 digit
     return $ Number $ read num

bool :: Parser Expr
bool = (string "true" >> return (Boolean True)) <|> (string "false" >> return (Boolean False))

parser = parens infixOp <|> number <|> bool

run = Text.Parsec.runParser parser () ""

This parser is able to parse expressions like 1, false, (1 + 2), (1 + false), but not 1 + 2 (it's parsed as 1). If I try to change the parser to parens infixOp <|> infixOp <|> number <|> bool, it get stuck.

What should i change in order to parse expressions like 1 + 2 without parenthesis?


Solution

  • You have to run the infixOp parser at the top level like this:

    run = Text.Parsec.runParser infixOp () ""

    Otherwise the your infix expressions can only be parsed when occuring in parentheses.

    The attempt to use parens infixOp <|> infixOp <|> number <|> bool most likely gets stuck because it loops: parser tries to parse using infixOp, which tries to parse using parse and so on...

    These tutorial might help you getting started with parsec (they did for me):

    https://wiki.haskell.org/Parsing_a_simple_imperative_language

    http://dev.stephendiehl.com/fun/002_parsers.html