Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsec: get buildExpressionParser example to typecheck in 2014

Tags:

haskell

parsec

Here's an example from http://hackage.haskell.org/package/parsec-3.1.7/docs/Text-Parsec-Expr.html :

expr    = buildExpressionParser table term
        <?> "expression"

term    =  parens expr 
        <|> natural
        <?> "simple expression"

table   = [ [prefix "-" negate, prefix "+" id ]
        , [postfix "++" (+1)]
        , [binary "*" (*) AssocLeft, binary "/" (div) AssocLeft ]
        , [binary "+" (+) AssocLeft, binary "-" (-)   AssocLeft ]
        ]

binary  name fun assoc = Infix (do{ reservedOp name; return fun }) assoc
prefix  name fun       = Prefix (do{ reservedOp name; return fun })
postfix name fun       = Postfix (do{ reservedOp name; return fun })

Seems you need a few imports to get started:

import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.Token

And now it fails to typecheck on almost every line.

Does anyone know how to fix it?

Update

Errors looking like this:

Couldn't match expected type ‘ParsecT s u m a0’
            with actual type ‘String -> ParsecT s9 u9 m9 ()’
Relevant bindings include
  name :: GenTokenParser s9 u9 m9 (bound at Eval2.hs:28:9)
  postfix :: GenTokenParser s9 u9 m9 -> (a -> a) -> Operator s u m a
    (bound at Eval2.hs:28:1)
Probable cause: ‘reservedOp’ is applied to too few arguments
In a stmt of a 'do' block: reservedOp name
In the first argument of ‘Postfix’, namely
  ‘(do { reservedOp name;
         return fun })’

Makes me curious about the type of reservedOp

λ :t reservedOp
reservedOp
  :: GenTokenParser s u m
     -> String -> Text.Parsec.Prim.ParsecT s u m ()

So looking at the documentation here http://hackage.haskell.org/package/parsec-3.1.7/docs/Text-Parsec-Token.html :

reservedOp :: String -> ParsecT s u m ()

Orly? So what's a GenTokenParser s u m and how do I get one?

Just for fun

Check out this type. What could it possibly mean?

λ :t TokenParser
TokenParser
:: Text.Parsec.Prim.ParsecT s u m String
 -> (String -> Text.Parsec.Prim.ParsecT s u m ())
 -> Text.Parsec.Prim.ParsecT s u m String
 -> (String -> Text.Parsec.Prim.ParsecT s u m ())
 -> Text.Parsec.Prim.ParsecT s u m Char
 -> Text.Parsec.Prim.ParsecT s u m String
 -> Text.Parsec.Prim.ParsecT s u m Integer
 -> Text.Parsec.Prim.ParsecT s u m Integer
 -> Text.Parsec.Prim.ParsecT s u m Double
 -> Text.Parsec.Prim.ParsecT s u m (Either Integer Double)
 -> Text.Parsec.Prim.ParsecT s u m Integer
 -> Text.Parsec.Prim.ParsecT s u m Integer
 -> Text.Parsec.Prim.ParsecT s u m Integer
 -> (String -> Text.Parsec.Prim.ParsecT s u m String)
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> Text.Parsec.Prim.ParsecT s u m ()
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m a)
 -> Text.Parsec.Prim.ParsecT s u m String
 -> Text.Parsec.Prim.ParsecT s u m String
 -> Text.Parsec.Prim.ParsecT s u m String
 -> Text.Parsec.Prim.ParsecT s u m String
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m [a])
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m [a])
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m [a])
 -> (forall a.
     Text.Parsec.Prim.ParsecT s u m a
     -> Text.Parsec.Prim.ParsecT s u m [a])
 -> GenTokenParser s u m
like image 711
Michael Fox Avatar asked Dec 15 '22 19:12

Michael Fox


2 Answers

Thanks @Christian Conkle and @bheklilr and this here http://hackage.haskell.org/package/parsec-3.1.7/docs/Text-Parsec-Token.html#v:makeTokenParser

Here's the 2014 version of the buildExpressionParser example which should be in the Parsec docs instead of what's there.

import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.Token
import Text.Parsec.Language (javaStyle)

lexer = makeTokenParser javaStyle

expr    = buildExpressionParser table term
        <?> "expression"

term    =  parens lexer expr 
        <|> natural lexer
        <?> "simple expression"

table   = [ [prefix "-" negate, prefix "+" id ]
        , [postfix "++" (+1)]
        , [binary "*" (*) AssocLeft, binary "/" (div) AssocLeft ]
        , [binary "+" (+) AssocLeft, binary "-" (-)   AssocLeft ]
        ]

binary  name fun assoc = Infix (do{ reservedOp lexer name; return fun }) assoc
prefix  name fun       = Prefix (do{ reservedOp lexer name; return fun })
postfix name fun       = Postfix (do{ reservedOp lexer name; return fun })
like image 145
Michael Fox Avatar answered Mar 02 '23 17:03

Michael Fox


The data constructor GenTokenParser has all those parameters because it's the constructor for a type with a lot of fields.

The way to make a TokenParser is indeed with makeTokenParser. Its type says that it returns a GenTokenParser; TokenParser is just a restricted type synonym. TokenParser st is exactly the same thing as GenTokenParser String st Identity.

You can, as the docs suggest, pass it a premade language definition like haskellDef. You could also manually construct a LanguageDef, which isn't too complicated; ask if you want elaboration. The middle ground is to use record modification syntax to modify one of the premade definitions: makeTokenParser (haskellDef { commentStart = "<<", commentEnd = ">>" }).

like image 42
Christian Conkle Avatar answered Mar 02 '23 17:03

Christian Conkle