The examples in the Parsec users guide

Parsec is a free, industrial strength, monadic parser combinator library for Haskell, by Daan Leijen. You can find the users guide in the Parsec documentation. In this blog post, I am going to list the examples that Daan Leijen includes in the Parsec users guide. All programs in this blog post are his copyright, not mine. All I do is change the indenting and take care of small details, in order to make the programs more familiar to the readers of my blog who have become accustomed to the way I present Haskell programs.

The purpose of this blog post is to introduce the readers of my blog to parsing in Haskell and to a parser library for Haskell. I chose Parsec, because it is a great parser library and very well documented. I also recommend the following material:

• The Parsec page in Haskell.org
• The Parsec documentation, titled “Parsec, a fast combinator parser“, by Daan Leijen
• The Wikipedia article titled “Parser combinator
• The paper titled “Monadic Parsing in Haskell“, by Graham Hutton and Erik Meijer
• The paper titled “Monadic Parser Combinators“, by Graham Hutton and Erik Meijer
• The paper titled “Parsec: Direct Style Monadic Parser Combinators For The Real World“, by Daan Leijen and Erik Meijer”

Again, everything in this blog post is Daan Leijen’s copyright. But I am responsible for anything that you may find wrong, incorrect, inappropriate, and for any mistakes.

I compiled the programs with Haskell Platform 2012.4.0.0, which includes Parsec. Specifically, I used the following tool: In the following, I list each program along with its output.

So, here are the examples that are included in the Parsec documentation:

Example 01, code

module Main where

import Text.ParserCombinators.Parsec

simple :: Parser Char
simple  = letter

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run simple "a"

putStrLn ""

run simple ""

putStrLn ""

run simple "123"

putStrLn ""
putStrLn "Program ends."

Example 01, output

Program begins.

'a'

parse error at (line 1, column 1):
unexpected end of input
expecting letter

parse error at (line 1, column 1):
unexpected "1"
expecting letter

Program ends.

Example 02, code

module Main where

import Text.ParserCombinators.Parsec

parens :: Parser ()
parens  =
do
char '('
parens
char ')'
parens
<|>
return ()

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run parens "(())()"

putStrLn ""

run parens "(()()"

putStrLn ""
putStrLn "Program ends."

Example 02, output

Program begins.

()

parse error at (line 1, column 6):
unexpected end of input
expecting "(" or ")"

Program ends.

Example 03, code

module Main where

import Text.ParserCombinators.Parsec

testOr :: Parser String
testOr  =
string "(a)"
<|>
string "(b)"

testOr1 :: Parser Char
testOr1 =
do
char '('
char 'a' <|> char 'b'
char ')'

testOr2 :: Parser String
testOr2  =
try (string "(a)")
<|>
string "(b)"

testOr3 :: Parser String
testOr3  =
do
try (string "(a")
char ')'
return "(a)"
<|>
string "(b)"

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run testOr "(b)"

putStrLn ""

run testOr1 "(b)"

putStrLn ""

run testOr2 "(b)"

putStrLn ""

run testOr3 "(b)"

putStrLn ""
putStrLn "Program ends."

Example 03, output

Program begins.

parse error at (line 1, column 1):
unexpected "b"
expecting "(a)"

')'

"(b)"

"(b)"

Program ends.

Example 04, code

module Main where

import Text.ParserCombinators.Parsec

nesting :: Parser Int
nesting  =
do
char '('
n <- nesting
char ')'
m <- nesting
return (max (n+1) m)
<|>
return 0

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run nesting "(())()"

putStrLn ""

run nesting "(()(()))"

putStrLn ""

run nesting "(()(())"

putStrLn ""
putStrLn "Program ends."

Example 04, output

Program begins.

2

3

parse error at (line 1, column 8):
unexpected end of input
expecting "(" or ")"

Program ends.

Example 05, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word =
do
c <- letter
do
cs <- word
return (c:cs)
<|>
return [c]

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!"
return words

separator :: Parser ()
separator = skipMany1 (space <|> char ',')

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi,di,hi."

putStrLn ""

run sentence "hi,di hi!"

putStrLn ""

run sentence "hi,123"

putStrLn ""
putStrLn "Program ends."

Example 05, output

Program begins.

["hi","di","hi"]

["hi","di","hi"]

parse error at (line 1, column 4):
unexpected "1"
expecting space, "," or letter

Program ends.

Example 06, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word  = many1 letter

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!"
return words

separator :: Parser ()
separator  = skipMany1 (space <|> char ',')

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi,di,hi."

putStrLn ""

run sentence "hi,di hi!"

putStrLn ""

run sentence "hi,123"

putStrLn ""
putStrLn "Program ends."

Example 06, output

Program begins.

["hi","di","hi"]

["hi","di","hi"]

parse error at (line 1, column 4):
unexpected "1"
expecting space, "," or letter

Program ends.

Example 07, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word  = many1 letter <?> "word"

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!"
return words

separator :: Parser ()
separator = skipMany1 (space <|> char ',')

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi,123"

putStrLn ""
putStrLn "Program ends."

Example 07, output

Program begins.

parse error at (line 1, column 4):
unexpected "1"
expecting space, "," or word

Program ends.

Example 08, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word  = many1 letter <?> "word"

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!"
return words

separator :: Parser ()
separator  = skipMany1 (space <|> char ',' <?> "")

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi,123"

putStrLn ""
putStrLn "Program ends."

Example 08, output

Program begins.

parse error at (line 1, column 4):
unexpected "1"
expecting word

Program ends.

Example 09, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word  = many1 letter <?> "word"

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!" <?> "end of sentence"
return words

separator :: Parser ()
separator  = skipMany1 (space <|> char ',' <?> "")

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi,di"

putStrLn ""
putStrLn "Program ends."

Example 09, output

Program begins.

parse error at (line 1, column 6):
unexpected end of input
expecting end of sentence

Program ends.

Example 10, code

module Main where

import Text.ParserCombinators.Parsec

word :: Parser String
word  = many1 (letter <?> "") <?> "word"

sentence :: Parser [String]
sentence  =
do
words <- sepBy1 word separator
oneOf ".?!" <?> "end of sentence"
return words

separator :: Parser ()
separator  = skipMany1 (space <|> char ',' <?> "")

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run sentence "hi di"

putStrLn ""

run sentence "hi di,"

putStrLn ""
putStrLn "Program ends."

Example 10, output

Program begins.

parse error at (line 1, column 6):
unexpected end of input
expecting end of sentence

parse error at (line 1, column 7):
unexpected end of input
expecting word

Program ends.

Example 11, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr

expr :: Parser Integer
expr  =
buildExpressionParser table factor
<?>
"expression"

table :: OperatorTable Char () Integer
table  = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
]
where
op s f assoc = Infix (do {string s; return f}) assoc

factor :: Parser Integer
factor  =
do
char '('
x <- expr
char ')'
return x
<|>
number
<?>
"simple expression"

number :: Parser Integer
number  =
do
ds <- many1 digit
<?>
"number"

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run expr "1+2*3" -- '*' has higher priority

putStrLn ""

run expr "(1+2)*3"

putStrLn ""

run expr "8/4/2" -- '/' is left associative

putStrLn ""

run expr "8/(4/2)"

putStrLn ""

run expr "1 + 2" -- wrong!

putStrLn ""

run expr "1+ 2" -- wrong!

putStrLn ""
putStrLn "Program ends."

Example 11, output

Program begins.

7

9

1

4

1

parse error at (line 1, column 3):
unexpected " "
expecting simple expression

Program ends.

Example 12, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language

whiteSpace = P.whiteSpace lexer
lexeme     = P.lexeme lexer
symbol     = P.symbol lexer
natural    = P.natural lexer
parens     = P.parens lexer
semi       = P.semi lexer
identifier = P.identifier lexer
reserved   = P.reserved lexer
reservedOp = P.reservedOp lexer

lexer :: P.TokenParser ()
lexer  = P.makeTokenParser (haskellDef {reservedOpNames = ["*","/","+","-"]})

expr :: Parser Integer
expr  =
buildExpressionParser table factor
<?>
"expression"

table :: OperatorTable Char () Integer
table  = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
]
where
op s f assoc = Infix (do{ reservedOp s; return f} <?> "operator") assoc

factor :: Parser Integer
factor  =
parens expr
<|> natural
<?> "simple expression"

runLex        :: Show a => Parser a -> String -> IO ()
runLex p input = run (do { whiteSpace
; x <- p
; eof
; return x
}) input

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

runLex expr "1 + 2"

putStrLn ""

runLex expr "1 + {- comment -} 2 * 3 --multiply has higher priority"

putStrLn ""

runLex expr " 0xAA / 0o37 / 2"

putStrLn ""

runLex expr "0xAA / 0o37 2 "

putStrLn ""
putStrLn "Program ends."

Example 12, output

Program begins.

3

7

2

parse error at (line 1, column 13):
unexpected '2'
expecting operator or end of input

Program ends.

Example 13, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language
import Data.Char

whiteSpace = P.whiteSpace lexer
lexeme     = P.lexeme lexer
symbol     = P.symbol lexer
natural    = P.natural lexer
parens     = P.parens lexer
semi       = P.semi lexer
identifier = P.identifier lexer
reserved   = P.reserved lexer
reservedOp = P.reservedOp lexer

lexer :: P.TokenParser ()
lexer  = P.makeTokenParser (haskellDef { reservedOpNames = ["*","/","+","-"]})

expr :: Parser Integer
expr  =
buildExpressionParser table factor
<?>
"expression"

table :: OperatorTable Char () Integer
table  = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
]
where
op s f assoc = Infix (do {reservedOp s; return f} <?> "operator") assoc

factor :: Parser Integer
factor  =
parens expr
<|> natural
<?> "simple expression"

receipt :: Parser Bool
receipt  =
do
ps <- many produkt
p <- total
return (sum ps == p)

produkt :: Parser Int
produkt  =
do
symbol "return"
p <- price
semi
return (-p)
<|>
do
identifier
p <- price
semi
return p
<?>
"produkt"

total :: Parser Int
total  =
do
p <- price
symbol "total"
return p

price :: Parser Int -- price in cents
price  = lexeme (do { ds1 <- many1 digit
; char '.'
; ds2 <- count 2 digit
; return (convert 0 (ds1 ++ ds2))
})
<?> "price"
where
convert n []     = n
convert n (d:ds) = convert (10*n + digitToInt d) ds

runLex        :: Show a => Parser a -> String -> IO ()
runLex p input = run (do { whiteSpace
; x <- p
; eof
; return x
}) input

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

runLex receipt "book 12.00; plant 2.55; 14.55 total"

putStrLn ""

runLex receipt "book 12.00; plant 2.55; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; plant 2; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; return 2.00; plant 2.55; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; reader 2.00; plant 1.00; 15.00 total"

putStrLn ""
putStrLn "Program ends."

Example 13, output

Program begins.

True

False

parse error at (line 1, column 20):
unexpected ";"
expecting digit or "."

True

parse error at (line 1, column 13):
unexpected "a"
expecting "return"

Program ends.

Example 14, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language
import Data.Char

whiteSpace = P.whiteSpace lexer
lexeme     = P.lexeme lexer
symbol     = P.symbol lexer
natural    = P.natural lexer
parens     = P.parens lexer
semi       = P.semi lexer
identifier = P.identifier lexer
reserved   = P.reserved lexer
reservedOp = P.reservedOp lexer

lexer :: P.TokenParser ()
lexer  = P.makeTokenParser (haskellDef { reservedOpNames = ["*","/","+","-"]})

expr :: Parser Integer
expr  =
buildExpressionParser table factor
<?>
"expression"

table :: OperatorTable Char () Integer
table  = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
]
where
op s f assoc = Infix (do {reservedOp s; return f} <?> "operator") assoc

factor :: Parser Integer
factor  =
parens expr
<|> natural
<?> "simple expression"

receipt :: Parser Bool
receipt  =
do
ps <- many produkt
p <- total
return (sum ps == p)

produkt :: Parser Int
produkt  =
do
try (symbol "return")
p <- price
semi
return (-p)
<|>
do
identifier
p <- price
semi
return p
<?>
"produkt"

total :: Parser Int
total  =
do
p <- price
symbol "total"
return p

price :: Parser Int -- price in cents
price  = lexeme (do { ds1 <- many1 digit
; char '.'
; ds2 <- count 2 digit
; return (convert 0 (ds1 ++ ds2))
})
<?> "price"
where
convert n []     = n
convert n (d:ds) = convert (10*n + digitToInt d) ds

runLex        :: Show a => Parser a -> String -> IO ()
runLex p input = run (do { whiteSpace
; x <- p
; eof
; return x
}) input

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

runLex receipt "book 12.00; plant 2.55; 14.55 total"

putStrLn ""

runLex receipt "book 12.00; plant 2.55; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; plant 2; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; return 2.00; plant 2.55; 12.55 total"

putStrLn ""

runLex receipt "book 12.00; reader 2.00; plant 1.00; 15.00 total"

putStrLn ""

runLex receipt "book 12.00; returns 2.00; plant 1.00; 15.00 total"

putStrLn ""
putStrLn "Program ends."

Example 14, output

Program begins.

True

False

parse error at (line 1, column 20):
unexpected ";"
expecting digit or "."

True

True

parse error at (line 1, column 19):
unexpected "s"
expecting price

Program ends.

Example 15, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language
import Data.Char

whiteSpace = P.whiteSpace lexer
lexeme     = P.lexeme lexer
symbol     = P.symbol lexer
natural    = P.natural lexer
parens     = P.parens lexer
semi       = P.semi lexer
identifier = P.identifier lexer
reserved   = P.reserved lexer
reservedOp = P.reservedOp lexer

lexer :: P.TokenParser ()
lexer  = P.makeTokenParser (haskellDef
{ reservedNames = ["return","total"]
, reservedOpNames = ["*","/","+","-"]
})

expr :: Parser Integer
expr  =
buildExpressionParser table factor
<?>
"expression"

table :: OperatorTable Char () Integer
table  = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
]
where
op s f assoc = Infix (do {reservedOp s; return f} <?> "operator") assoc

factor :: Parser Integer
factor  =
parens expr
<|> natural
<?> "simple expression"

receipt :: Parser Bool
receipt  =
do
ps <- many produkt
p <- total
return (sum ps == p)

produkt :: Parser Int
produkt  =
do
reserved "return"
p <- price
semi
return (-p)
<|>
do
identifier
p <- price
semi
return p
<?>
"produkt"

total :: Parser Int
total  =
do
p <- price
reserved "total"
return p

price :: Parser Int -- price in cents
price  = lexeme (do { ds1 <- many1 digit
; char '.'
; ds2 <- count 2 digit
; return (convert 0 (ds1 ++ ds2))
})
<?> "price"
where
convert n [] = n
convert n (d:ds) = convert (10*n + digitToInt d) ds

runLex        :: Show a => Parser a -> String -> IO ()
runLex p input = run (do { whiteSpace
; x <- p
; eof
; return x
}) input

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

runLex receipt "book 12.00; returns 2.00; plant 1.00; 15.00 total"

putStrLn ""

runLex receipt "book 12.00; total 2.00; plant 1.00; 15.00 total"

putStrLn ""

runLex receipt "book 12.00; totals 2.00; return 1.00; 13.00 total"

putStrLn ""
putStrLn "Program ends."

Example 15, output

Program begins.

True

parse error at (line 1, column 18):
unexpected reserved word "total"
expecting produkt

True

Program ends.

Example 16, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Perm

perm0 :: Parser [Char]
perm0  = permute (f <\$\$> char 'a'
<||> char 'b'
<||> char 'c')
where
f a b c = [a,b,c]

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run perm0 "abc"

putStrLn ""

run perm0 "cba"

putStrLn ""

run perm0 "b"

putStrLn ""
putStrLn "Program ends."

Example 16, output

Program begins.

"abc"

"abc"

parse error at (line 1, column 2):
unexpected end of input
expecting "c" or "a"

Program ends.

Example 17, code

module Main where

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Perm

perm1 :: Parser (String,Char,Char)
perm1  = permute (tuple <\$?> ("",many1 (char 'a'))
<||> char 'b'
<|?> (' ',char 'c'))
where
tuple a b c = (a,b,c)

run        :: Show a => Parser a -> String -> IO ()
run p input =
case (parse p "" input) of
Left err ->
do
putStr "parse error at "
print err
Right x ->
print x

main :: IO ()
main  =
do
putStrLn "Program begins."
putStrLn ""

run perm1 "caaaaab"

putStrLn ""

run perm1 "cb"

putStrLn ""

run perm1 "b"

putStrLn ""

run perm1 ""

putStrLn ""

run perm1 "c"

putStrLn ""

run perm1 "ca"

putStrLn ""
putStrLn "Program ends."

Example 17, output

Program begins.

("aaaaa",'b','c')

("",'b','c')

("",'b',' ')

parse error at (line 1, column 1):
unexpected end of input

expecting "c", "b" or "a"

parse error at (line 1, column 2):
unexpected end of input
expecting "b" or "a"

parse error at (line 1, column 3):
unexpected end of input
expecting "a" or "b"

Program ends. 