Parsing with Haskell
Parsing with Haskell
Lennart Andersson Lennart Andersson Computer Science Computer Science Lund University Lund University October 28, 2001 October 28, 2001
Introduction
Introduction
This will be an extended example that will provide the basis for a small program project resulting This will be an extended example that will provide the basis for a small program project resulting in an
in an ininterterprepreter for ter for an an impeimperatirative languve languageage. . The examThe example will show how abstrple will show how abstractaction andion and higher order functions can be used to make the parsing program look like the grammar for the higher order functions can be used to make the parsing program look like the grammar for the imperative language.
imperative language.
The example will decompose a nontrivial problem into a large number of very small problems. The example will decompose a nontrivial problem into a large number of very small problems. Most of them can be solved with almost trivial functions. Actually, the longest function in the Most of them can be solved with almost trivial functions. Actually, the longest function in the example has just 7 lines of code. To do this decomposition requires a fair amount of experience; example has just 7 lines of code. To do this decomposition requires a fair amount of experience; it shoul
it should d be be a a lot easielot easier r to understo understand the functtand the functionions s and the prograand the program. m. WWe e belibelieveve e it is it is a a goodgood programming strategy to decompose a nontrivial problem into problems that are small and programming strategy to decompose a nontrivial problem into problems that are small and almost trivial.
almost trivial.
The programming language will have statements and integer expressions as its main building The programming language will have statements and integer expressions as its main building stones. Expressions will be represented in the interpreter with the following data type.
stones. Expressions will be represented in the interpreter with the following data type. da
data ta ExExpr pr = = NuNum m InInt t | | VaVar r StStriring ng | | AdAdd d ExExpr pr ExExprpr |
| SuSub b ExExpr Exppr Expr r | | MuMul l ExExpr Exppr Expr r | | DiDiv v ExExpr Exppr Exprr As an example the expression
As an example the expression 2*(x+3)2*(x+3) will be represented bywill be represented by Mu
Mul l (N(Num um 2) 2) (A(Add dd (V(Var ar "x"x") ") (N(Num um 3)3))) Statements will be represented using
Statements will be represented using dat
data a StaStatemtement ent == Ass
Assignignmenment t StrString ing ExpExpr r || ...
...
so that the assignment statement
so that the assignment statement cocoununt t := := cocoununt t + + 11 is represented byis represented by Ass
Assignignmenment t "co"countunt" " (Ad(Add d (Va(Var r "co"countunt") ") (Nu(Num m 1))1))
We are going to define parsing functions that takes such strings as arguments and returns the We are going to define parsing functions that takes such strings as arguments and returns the corres
corresponding represponding representaentation. tion. WWe are e are going to introduce a few going to introduce a few ververy simple parsers and somey simple parsers and some operators for combining parsers that may be used to construct complex parsers.
A parsing function will usua
A parsing function will usually not parse all of lly not parse all of the input string, but just a prefix. the input string, but just a prefix. The remaindThe remainderer of the string will b
of the string will be the input to e the input to anotheanother parser. r parser. Suppose that we havSuppose that we have a e a parser whicparser which accepts anh accepts an identifier consisting of letters, another one accepting the string “:=” and a third one accepting an identifier consisting of letters, another one accepting the string “:=” and a third one accepting an expre
expression and returning a ssion and returning a pair with some pair with some represrepresententation of ation of the accepted string and a the accepted string and a remainremainderder string:
string: ident
ident "coun"count:=cot:=count+1unt+1;" ;" -> -> ("cou("count", nt", ":=co":=count+unt+1;")1;") becom
becomes es ":=co":=count+1unt+1:" :" -> -> (":="(":=", , "cou"count+1;nt+1;")") exp
expr r "co"countunt+1;+1;" " -> -> (Ad(Add d (Va(Var r "co"countunt") ") (Nu(Num m 1), 1), ";"";")) The arrow
The arrow ->-> denotedenotes evaluas evaluation. tion. WWe can e can comcombine these parsers to bine these parsers to a parser a parser for an for an assignassignmenmentt statement:
statement: ass
assignignmenment t str str = = (As(Assigsignmenment nt id id e, e, resrest3) t3) whewherere (id
(id, , resrest1) = t1) = ideident nt strstr (_,
(_, resrest2) = t2) = becbecomeomes s resrest1t1 (e
(e, , rerestst3) 3) = = exexpr pr rerestst22 The type of such parsers would be The type of such parsers would be
ty
type pe PaParsrser er a a = = StStriring ng -> -> (a(a, , StStriringng)) A
A ParParser ser aa taktakes a es a stristring argumeng argument and nt and retureturns a rns a paipair. r. WWe e wilwill l calcall l the first componethe first component thent the result of the parser and second the remainder string. Thus we have
result of the parser and second the remainder string. Thus we have ide
ident nt :: :: ParParser ser StrStringing bec
becomeomes s :: :: ParParser ser StrStringing exp
expr r :: :: ParParser Exprser Expr assig
assignment nment :: :: ParseParser r StateStatementment
A parser should either accept or reject its input. The Prelude defines a data type for handling A parser should either accept or reject its input. The Prelude defines a data type for handling two such alternatives.
two such alternatives. da
data ta MaMaybybe e a a = = NoNoththining g | | JuJust st aa
This type can be used in any context where a computation either fails or returns some valid This type can be used in any context where a computation either fails or returns some valid result. We redefine our parser type
result. We redefine our parser type typ
type e ParParser a ser a = = StrString -> ing -> MayMaybe be (a,(a,StrStringing)) and the parsers so that e.g.
and the parsers so that e.g.
ident "count:=count+1;" ->
ident "count:=count+1;" -> Just("count",":=Just("count",":=count+1;")count+1;") ident
ident "123:"123:=coun=count+1;" t+1;" -> -> NothNothinging The
The typetype definitions just give synonyms to type expressions; there are no constructors as in thedefinitions just give synonyms to type expressions; there are no constructors as in the case of
case of datadata definitions.definitions.
Exer
Exercise cise 11 DefineDefine semsemicoicolon lon :: :: ParParser ser ChaCharr so that so that
sem
semicoicolon lon "; "; skiskip" p" -> -> JusJust(’t(’;’, ;’, " " skiskip")p") semic
semicolon olon "ski"skip" p" -> -> NothNothinging
Exer
Exercise cise 22 DefineDefine becomesbecomes.. Exer
Exercise cise 33 Define a parser Define a parser chachar r :: :: ParParser Charser Char whiwhich ch acacccepts one epts one chacharracteacter. r. The The solsolutioution n
appears on the next page, don’t look! appears on the next page, don’t look!
Basic parsers Basic parsers
The basic parsers are almost trivial. The first one will accept one character and is defined by The basic parsers are almost trivial. The first one will accept one character and is defined by
cha
char r :: :: ParParser Charser Char cha
char r (c:(c:cs) cs) = = JusJust(ct(c,cs,cs)) ch
char ar [] [] = = NoNoththiningg Pars
Parser er CharChar is a synonym foris a synonym for StriString ng -> -> MaybMaybe(Chae(Char,Strr,String)ing). . ThThe e fufuncnctition failon fails s if the inpif the inputut string is empty. Some examples
string is empty. Some examples cha
char r "" "" -> -> NotNothinhingg cha
char r "1" "1" -> -> JusJust(’t(’1’,1’,"")"") char
char "abc" -> "abc" -> Just(Just(’a’,"’a’,"bc")bc") The next parser is even simpler. The next parser is even simpler.
fa
fail il :: :: PaParsrser er aa fa
fail il cs cs = = NoNoththiningg This parser will neve
This parser will never accept any input, so r accept any input, so it seems to it seems to be useless. be useless. ActuallActually we will later y we will later definedefine a similar parser which will prin
a similar parser which will print an t an error message and abort the error message and abort the computcomputation. ation. The nameThe name failfail is define
is defined d in the in the PrePreludlude e so we so we hahave hide it ve hide it whewhen n impoimportinrting g the Prelthe Preludeude. . If you testIf you test failfail byby applying it to a string the system refuses to print
applying it to a string the system refuses to print NothingNothing:: >
> faifail l "ab"abc"c" ERR
ERROR: OR: CanCannot not finfind d "sh"show" ow" funfunctiction on forfor:: ***
*** expexpresressiosion n : : faifail l "ab"abc"c" *
*** ** of of tytype pe : M: Mayaybe be (a(a,[,[CChaharr])])
In order to print (show) the result the Haskell system must know how to print values of type In order to print (show) the result the Haskell system must know how to print values of type a
a which occurs inwhich occurs in Maybe Maybe (a,[C(a,[Char])har]) even if no such value is present ineven if no such value is present in NothingNothing. . SuSupppplylyining ag a printable type fixes the problem:
printable type fixes the problem: >
> fail "abc" fail "abc" :: :: MaybMaybe(Inte(Int,Stri,String)ng) Nothing
Nothing
The same error is reported if you enter an empty list to the system: The same error is reported if you enter an empty list to the system: >
> [][] ERR
ERROR: OR: CanCannot not finfind d "sh"show" ow" funfunctiction on forfor:: *** expre
*** expressission on : : [][] *
**** * oof f ttyyppe e : : [[aa]] While
While failfail is extreme in one way the next parser,is extreme in one way the next parser, returnreturn, is extreme in opposite sense; it will, is extreme in opposite sense; it will always succeed without inspecting the input string. It returns its first argument that may be of always succeed without inspecting the input string. It returns its first argument that may be of any type.
any type. re
retuturn :: rn :: a a -> Pars-> Parser aer a ret
return a urn a cs cs = = JusJust(at(a,cs,cs)) Examples
Examples ret
return urn 0 0 "ab"abc" c" -> -> JusJust(0t(0,"a,"abc"bc")) ret
return urn (Ad(Add d (Va(Var r "co"countunt") ") (Nu(Num m 1)) ";" 1)) ";" -> -> JusJust(At(Add dd (Va(Var r "co"countunt") ") (Nu(Num m 1))1)), , ";"";")) The
Parser operators Parser operators
The first parser operator will be an infix operator denoted by
The first parser operator will be an infix operator denoted by ??. . If If m :: Parser am :: Parser a is a parseris a parser and
and p :: a -> Boolp :: a -> Bool is a predicate thenis a predicate then (m ? p)(m ? p) is a parser which appliesis a parser which applies mm to the input stringto the input string and tests if the result satisfies
and tests if the result satisfies pp.. infix 7 ?
infix 7 ? (?
(?) ) :: Par:: Parseser r a a -> (a -> (a -> Boo-> Bool) -> l) -> PaParsrser aer a (m ? p) cs =
(m ? p) cs = case m cs of case m cs of
Noth
Nothing ing -> -> NothNothinging Jus
Just(at(a,cs,cs) ) -> -> if if p p a a thethen n JusJust(at(a,cs,cs) ) elselse e NotNothinhingg The
The infixinfix directive declaresdirective declares ?? to be an infix operator with precedence 7. The precedence levelsto be an infix operator with precedence 7. The precedence levels for the parser operators will be chosen so that most parentheses may be omitted.
for the parser operators will be chosen so that most parentheses may be omitted. The Prelude contains a predicate for deciding if a character is a digit,
The Prelude contains a predicate for deciding if a character is a digit, isDigitisDigit. . WWe can define can definee a parser accepting one digit
a parser accepting one digit dig
digit it :: :: ParParser Charser Char
digit cs = (char ? isDigit) cs digit cs = (char ? isDigit) cs Examples
Examples
digit "123"
digit "123" -> -> JustJust(’1’,(’1’,"23")"23") dig
digit it "ab"abc" c" -> -> NotNothinhingg
If the right member of a function definition applies an expression not containing the last If the right member of a function definition applies an expression not containing the last argu-men
ment to t to the last argument then this argument may be the last argument then this argument may be remoremoved from both ved from both sides of the sides of the definitdefinition.ion. di
digigit t = = chchar ar ? ? isisDiDigigitt The secon
The second d parsparser er operoperator correator correspondsponds s to to altealternatrnativives es in in the grammathe grammar. r. WWe e wilwill l use an use an infiinfixx operator,
operator, !!, , for this parsfor this parser comber combinainator. tor. If If mm andand nn are parsers of the same type thenare parsers of the same type then (m ! n)(m ! n) will be a parser which first applies
will be a parser which first applies mm to the input string which will be the result unlessto the input string which will be the result unless mm fails.fails. In that case
In that case nn is applied to the input.is applied to the input. The definition is simple:
The definition is simple: in
infifixl 3 xl 3 !! (!
(!) ) :: :: PaParsrser er a a -> -> PaParsrser er a a -> -> PaParsrser er aa (m ! n) cs =
(m ! n) cs = case m cs of case m cs of
No
Noththining g -> -> n n cscs m
mcs cs -> -> mcmcss The
The infixlinfixl directive tells the Haskell systemdirective tells the Haskell system !! will be an infix operator which associates to thewill be an infix operator which associates to the left. Left association means that
left. Left association means that m ! n ! km ! n ! k is evaluated asis evaluated as ( m ! n ) ! k( m ! n ) ! k. Some examples:. Some examples: (ch
(char ar ! ! digdigit) it) "ab"abc" c" -> -> JusJust(’t(’a’, a’, "bc"bc")") (di
(digit git ! ! chachar) r) "ab"abc" c" -> -> JusJust(’t(’a’, a’, "bc"bc")") (di
(digit git ! ! retreturn urn ’0’’0’) ) "ab"abc" c" -> -> JusJust(’t(’0’, 0’, "ab"abc")c") (d
We have assigned precedences to the operators so that
We have assigned precedences to the operators so that m ? p ! nm ? p ! n will meanwill mean ((m ? m ? pp) ! ) ! nn andand not
not m ? ( p ! n )m ? ( p ! n ) which would give a type mismatch error.which would give a type mismatch error.
Exer
Exercise cise 44 The Prelude defines the predicatesThe Prelude defines the predicates isAisAlphlpha, a, isSisSpacpace:: e:: ChaChar r -> -> BooBooll which decidewhich decide
if
if a a chacharracteacter r is is a a lettletter er or or spspacace e chacharracteacter r (bl(blankank, , tab, newline)tab, newline). . DefiDefine ne pparsarsers accers acceptepting a ing a letter and a space character,
letter and a space character, letletterter, , spaspace ce :: :: ParParser ser ChaCharr..
Exer
Exercise cise 55 Define a parser Define a parser alpalphanhanum um :: :: ParParser ser ChaCharr which accepts a letter or a digit.which accepts a letter or a digit.
A parser accepting a given character is easily defined. A parser accepting a given character is easily defined.
li
lit t :: :: ChChar ar -> -> PaParsrser er ChCharar lit c = char ? (==c)
lit c = char ? (==c) The operator section
The operator section (==c)(==c) is equivalent to the predicateis equivalent to the predicate (\x -> (\x -> x==x==c)c). . The predicThe predicate decideate decidess if the given character is the the same as the one accepted by
if the given character is the the same as the one accepted by charchar.. lit ’a’
lit ’a’ "ab"abc" c" -> -> JusJust t (’a(’a’,"’,"bc"bc")) lit ’b’
lit ’b’ "ab"abc" c" -> -> NotNothinhingg
Exer
Exercise cise 66 RedefineRedefine semsemicoicolon lon :: :: ParParser ser ChaCharr using using litlit. The defin. The definitioition should not incln should not includeude
the string argument. the string argument.
The next parser combinator applies two parsers in sequence where the remainder string from The next parser combinator applies two parsers in sequence where the remainder string from the first one is fed into the other. The results of the two parsers are combined into a pair. the first one is fed into the other. The results of the two parsers are combined into a pair.
in
infifixl 6 xl 6 ## (#
(#) ) :: Pars:: Parser a er a -> Pars-> Parser b er b -> Pars-> Parser (a, er (a, b)b) (m # n) cs =
(m # n) cs = case m cs of case m cs of
Nothi
Nothing ng -> -> NothNothinging Jus
Just(pt(p, , cs’cs’) ) ->-> ca
case n se n cscs’ ’ ofof Noth
Nothing ing -> -> NothNothinging Just
Just(q, (q, cs’’cs’’) ) -> -> Just(Just((p,q)(p,q), , cs’’cs’’)) A
A parser accepting two parser accepting two chacharacters followsracters follows.. (ch
(char ar # # chachar) r) ":=":=1" 1" -> -> JusJust((t((’:’’:’, , ’=’’=’), ), "1""1")) We may name it:
We may name it: two
twochachars rs :: :: ParParser ser (Ch(Char, ar, ChaChar)r) tw
twocochahars rs = = chchar ar # # chcharar
Exer
Exercise cise 77 RedefineRedefine becbecomeomes s :: :: ParParser ser (Ch(Char, ar, ChaChar)r)using using twocharstwochars and theand the ?? operator. Theoperator. The
definition should not include the string argument. definition should not include the string argument.
Sometimes we would like to transform the result of a parser. This may be done with Sometimes we would like to transform the result of a parser. This may be done with
inf
infixl 5 ixl 5 >->>-> (>
(>->->) ) :: Par:: Parseser r a a -> (a -> (a -> b) -> b) -> Par-> Parseser r bb (m >-> k) cs =
(m >-> k) cs = case m cs of case m cs of
Jus
Just(at(a,cs,cs’) ’) -> -> JusJust(k t(k a, a, cs’cs’)) Noth
Nothing ing -> -> NothNothinging The right operand of
The right operand of >->>-> namednamed kk is the function defining the transformation. The result of theis the function defining the transformation. The result of the oper
operatioation n is a is a new parsnew parser. er. WWe e mamay use y use it to it to defidefine a ne a parparser whicser which h accacceptepts s a a digdigit and it and retreturnurnss an
an IntInt usingusing digitToIntdigitToIntfrom the Prelude.from the Prelude. dig
digitVitVal al :: :: ParParser ser IntInt dig
digitVitVal al = = digdigit it >-> >-> digdigitTitToInoIntt Example
Example dig
digitVitVal al "12"123" 3" -> -> JusJust(1t(1, , "23"23")") digit
digitVal Val "abc"abc" " -> -> NothNothinging
Exer
Exercise cise 88 Define a parser that accepts a letter and returns an upper case letter. The PreludeDefine a parser that accepts a letter and returns an upper case letter. The Prelude
function
function toUtoUppepper r :: :: ChaChar r -> -> ChaCharr transforms any letter to its uppercase form.transforms any letter to its uppercase form.
Exer
Exercise cise 99 DefineDefinesndsndchachar r :: :: ParParser ser ChaCharrwhich accepts two characters and returns the second which accepts two characters and returns the second
one. Use
one. Use twocharstwochars and the transformation operator. Examplesand the transformation operator. Examples snd
sndchachar r "ab"abc" c" -> -> JusJust(’t(’b’, b’, "c""c")) snd
sndchachar r "a" "a" -> -> NotNothinhingg
Exer
Exercise cise 1010 RedefineRedefine twoctwochars :: hars :: ParseParser r StriStringng so that it returns a so that it returns a StringString with two charac-with two
charac-ters instead of a pair of characcharac-ters. ters instead of a pair of characters.
Exer
Exercise cise 1111 Define an operator Define an operator (-(-#) :: #) :: PaParsrser a er a -> Pars-> Parser b er b -> Par-> Parseser r bbwhich applies twowhich applies two
parsers in sequence as
parsers in sequence as ## but thrbut throws away the ows away the rresuesult from the first one. lt from the first one. UseUse ## and and >->>-> in thein the definition. Examples
definition. Examples (ch
(char ar -# -# chachar) r) "ab"abc" c" -> -> JusJust(’t(’b’, b’, "c""c")) (ch
(char ar -# -# chachar) r) "a" -> "a" -> NotNothinhingg
Exer
Exercise cise 1212 Define a similar operator Define a similar operator (#(#-) :: -) :: PaParsrser a er a -> Par-> Parseser r b b -> Pars-> Parser aer a which ap-which
ap-plies two parsers in sequence as
plies two parsers in sequence as ## but throws away the result from the second one.but throws away the result from the second one. It is useful to be able to apply a parser iteratively to the input string. First we define
It is useful to be able to apply a parser iteratively to the input string. First we define ititereratate e m m ii which will apply the parser
which will apply the parser mmto the input stringto the input string iitimes with the result in a list withtimes with the result in a list with ii elements.elements. Example
Example ite
iteratrate e digdigitVitVal al 3 3 "12"123453456" 6" -> -> JusJust([t([1, 1, 2, 2, 3], 3], "45"456")6") ite
iteratrate e letletter ter 3 3 "a1"a123" 23" -> -> NotNothinhingg The
it
itereratate e :: :: PaParsrser er a a -> -> InInt t -> -> PaParsrser er [a[a]] it
itereratate e m m 0 0 = = reretuturn []rn [] it
itereratate e m m i i = = m m # # ititereratate e m m (i(i-1-1) ) >->-> > coconsns where
where co
cons :: ns :: (a(a, , [a[a]) -> ]) -> [a[a]] co
cons ns (h(hd, d, tltl) ) = = hdhd:t:tll The name
The name iterateiterate is defined in the Prelude so it is necessary to hide it or to rename the newis defined in the Prelude so it is necessary to hide it or to rename the new function.
function. It is
It is eveven more usefuen more useful l to bto be e ablable e to iteratto iterate e a a parsparser as er as long as it long as it sucsucceeceeds. ds. AssAssumiuming thatng that iteriter does this we could use it like follows
does this we could use it like follows ite
iter r digdigitVitVal al "12"123ab3abc" c" -> -> JusJust([t([1, 1, 2, 2, 3], 3], "ab"abc")c") ite
iter r digdigit it "12"123ab3abc" c" -> -> JusJust([t([’1’’1’, , ’2’’2’, , ’3’’3’], ], "ab"abc") c") -> -> JusJust("t("123123", ", "ab"abc")c") ite
iter r digdigit it "ab"abc" c" -> -> JusJust([t([], ], "ab"abc") c") -> -> JusJust("t("", ", "ab"abc")c")
The definition will again be recursive but termination will be signaled by the failure of the The definition will again be recursive but termination will be signaled by the failure of the recursive branch. In that case we just return the empty list.
recursive branch. In that case we just return the empty list. it
iter er :: :: PaParsrser er a a -> -> PaParsrser er [a[a]]
iter m = m # iter m >-> cons ! return [] iter m = m # iter m >-> cons ! return []
Inserting parentheses to indicate precedences of the operators we get Inserting parentheses to indicate precedences of the operators we get
it
iter er m m = = ((((m m # # (i(iteter r m)m)) ) >->-> > coconsns) ) ! ! (r(retetururn n [][]))
Exer
Exercise cise 1313 Why is it an error to take the alternatives in the opposite order? Why is it an error to take the alternatives in the opposite order?
iter m = return [] ! m # iter m >-> cons iter m = return [] ! m # iter m >-> cons We can use
We can use iteriter to define a to define a parsparser for er for a a strstring of ing of letletterters. s. Our first atteOur first attempt ismpt is let
letterters s :: :: ParParser ser StrStringing le
letttterers s = ite= iter letr letteterr
This is OK provided we are willing to accept an empty string.
This is OK provided we are willing to accept an empty string. If this is If this is not the case we definenot the case we define it as
it as le
letttterers s = = leletttter # er # ititer leter letteter r >->-> > coconsns W
We now consider the problem of e now consider the problem of whiteswhitespace, i.e. pace, i.e. blanks and newline chablanks and newline charactersracters, , in the in the input.input. We define
We define tokentoken which for a given parser will return a parser which will remove any whitespacewhich for a given parser will return a parser which will remove any whitespace after the accepted string.
after the accepted string. to
tokeken n :: :: PaParsrser er a a -> Pars-> Parser er aa to
tokeken m n m = m = m #- i#- iteter spr spaacece We now define two very useful parsers. We now define two very useful parsers.
wor
word d :: :: ParParser ser StrStringing wor
word d = = toktoken en letletterterss acc
accept ept :: :: StrString ing -> -> ParParser ser StrStringing ac
accecept pt w w = = totokeken n (i(iteterarate te chchar ar (s(sizize e w) w) ? ? (=(==w=w)))) Examples
Examples wor
word d "co"count unt := := coucount+nt+1" 1" -> -> JusJust("t("coucount"nt", , ":= ":= coucount+nt+1")1") wor
word d ":= ":= coucount+nt+1" 1" -> -> NotNothinhingg acc
accept ept "wh"whileile" " "wh"while ile x x do do x:=x:=x-1x-1" " -> -> JusJust("t("whiwhile"le", , "x "x do do x:=x:=x-1x-1")") acc
accept "if" ept "if" "wh"while x ile x do do x:=x:=x-1x-1" " -> -> NotNothinhingg Som
Sometietimes we mes we neeneed d to to makmake e the resulthe result t from one from one parparser avser availaailable to ble to anotanotherher. . The folloThe followinwingg operator provides the means.
operator provides the means. infix 4 #>
infix 4 #> (#
(#>) >) :: :: PaParsrser er a a -> -> (a (a -> -> PaParsrser er b) b) -> -> PaParsrser er bb (m #> k) cs =
(m #> k) cs = case m cs of case m cs of
Nothi
Nothing ng -> -> NothiNothingng Ju
Justst(a(a,c,cs’s’) ) -> -> k k a a cscs’’ After applying the parser
After applying the parser mm to the input string both the result and the remainder input stringto the input string both the result and the remainder input string are given to the second operand,
are given to the second operand, k :: a -> Parser bk :: a -> Parser b..
This operator is very useful for parsing numbers and arithmetic expressions with operators that This operator is very useful for parsing numbers and arithmetic expressions with operators that associate to the left like
associate to the left like -- andand //. These parsers will be nontrivial so we start with a simpler and. These parsers will be nontrivial so we start with a simpler and not so useful parser.
not so useful parser. It will accept two chaIt will accept two characters proracters provided they are equal and returning justvided they are equal and returning just one character.
one character. dou
double ble :: :: ParParser ser ChaCharr do
doububle le = = chchar ar #> #> lilitt Remember that
Remember that lilit t :: :: ChChar ar -> -> PaParsrser er ChChararneeds on argument to become a parser which willneeds on argument to become a parser which will accept this character. The character is provided as the result of the first parser. Examples accept this character. The character is provided as the result of the first parser. Examples
dou
double ble "aa"aab" b" -> -> JusJust(’t(’a’, a’, "b""b")) dou
double ble "ab"abb" b" -> -> NotNothinhingg
In order to construct a number parser we define three functions,
In order to construct a number parser we define three functions, bldNumberbldNumber,,number’number’andand numbernumber.. bl
bldNdNumumbeber r :: :: InInt t -> -> InInt t -> -> InIntt bl
bldNdNumumbeber r n n d d = = 1010*n*n+d+d bld
bldNumNumber n ber n dd just “appends” the digitjust “appends” the digit dd afterafter nn.. bld
bldNumNumber 123 ber 123 4 4 -> -> 12312344 nu
numbmberer’ ’ :: :: InInt t -> -> PaParsrser er InIntt nu
numbmberer’ ’ n n == dig
digitVitVal al >-> >-> bldbldNumNumber ber n n #> #> numnumberber’’ !
! reretuturn nrn n num
number ber :: :: ParParser ser IntInt num
The argument in
The argument innumber’number’will serve as an accumulator which will be returned when no more inputwill serve as an accumulator which will be returned when no more input can be
can be consuconsumed. med. OtherwOtherwise the next ise the next digit is “appended” to digit is “appended” to the accumuthe accumulator and recursivlator and recursivelyely given to
given tonumber’number’. The. The numbernumberparser accepst the parser accepst the first digit withfirst digit with digitValdigitValand sends it toand sends it to number’number’ and finally removes any trailing whitespace. Examples
and finally removes any trailing whitespace. Examples num
numberber’ ’ 1 1 "ab"abc" c" -> -> JusJust(1t(1, , "ab"abc")c") num
numberber’ ’ 1 1 "23 abc" -> "23 abc" -> JusJust(1t(123, " 23, " abcabc")") num
number ber "12"123 3 abcabc" " -> -> JusJust(1t(123, 23, "ab"abc")c")
Exer
Exercise cise 1414 Define the operator Define the operator ## using using #>#>. The solution should not contain . The solution should not contain JustJust or or NothingNothing..
An expression parser An expression parser
We are going to construct a parser for arithmetic expressions with integer numbers, variables, We are going to construct a parser for arithmetic expressions with integer numbers, variables, ad-ditions, subtractions, multiplications, divisions and parentheses with the usual laws for operator ditions, subtractions, multiplications, divisions and parentheses with the usual laws for operator precedence. A grammar is given by
precedence. A grammar is given by ex
expr pr ::::= te= term erm expxpr’r’ ex
exprpr’ ’ ::::= = adaddOdOp p teterm rm exexprpr’ ’ | | ememptptyy te
term rm ::::= fac= factotor terr term’m’ ter
term’ m’ ::= mulOp facto::= mulOp factor r terterm’ m’ | | empemptyty fa
factctoror::::= = nunum m | | vavar r | | "("(" " exexpr pr ")")"" ad
addOdOp p ::::= = "+"+" " | | "-"-"" m
mululOp Op ::::= = "*"*" " | | "/"/"" The word
The word emptyempty denotedenotes the empty string. s the empty string. In the definition of In the definition of factorfactor,, numnum is a string of digitsis a string of digits and
and varvar a string of letters. There are seva string of letters. There are several grammars desceral grammars describing ordinaribing ordinary expressry expressions. ions. TheThe current one has been chosen since it will be possible to define a parser which is very similar. current one has been chosen since it will be possible to define a parser which is very similar. The result of the parser will be a value of the following type.
The result of the parser will be a value of the following type. da
data ta ExExpr pr == Nu
Num m InInt t | | VaVar r StStriring | ng | AdAdd d ExExpr Exppr Expr r || Su
Sub b ExExpr Expr | pr Expr | MuMul l ExExpr Expr | pr Expr | DiDiv v ExExpr Exprpr Expr
We are going to build the parser from the bottom starting with parsers for
We are going to build the parser from the bottom starting with parsers for mulOpmulOp andand addOpaddOp.. mulOp
mulOp will accept eitherwill accept either ** oror // and will returnand will return MulMul oror DivDiv. . TheThese parsese parsers are triviars are trivial to l to defidefinene without our parser operators:
without our parser operators: mulO
mulOp p (’*’:(’*’:rest) = rest) = Just(Just(Mul, rest)Mul, rest) mulO
mulOp p (’/’:(’/’:rest) = rest) = Just(Just(Div, rest)Div, rest) mu
mulOp _ lOp _ = = NotNothinhingg W
We prefer, howee prefer, however, to make the definition on the ver, to make the definition on the abstracabstract level. t level. The reason is that The reason is that if we do if we do notnot exploit the fact that the representation by the
exploit the fact that the representation by the MaybeMaybe type is visible outside thetype is visible outside the ParserParser modulemodule then we may change it without changing any module using the
then we may change it without changing any module using the ParserParser module. module. Such a chaSuch a changenge is suggested at the end of this paper.
is suggested at the end of this paper. mu
mulOplOp, , addaddOp Op :: :: ParParser ser ((E((Exprxpr, , ExpExpr) r) -> -> ExpExpr)r) mulOp = lit ’*’ >-> (\_ -> Mul)
mulOp = lit ’*’ >-> (\_ -> Mul) !
! lilit t ’/’/’ ’ >->-> > (\(\_ _ -> Div-> Div)) Examples
mu
mulOp lOp "*2"*2*3" *3" -> -> JusJust(Mt(Mul, ul, "2*"2*3")3") mu
mulOp lOp "/2"/2*3" *3" -> -> JusJust(Dt(Div, iv, "2*"2*2")2") mu
mulop lop "+2"+2*3" *3" -> -> NotNothinhingg The
The addOpaddOp parser is analogous.parser is analogous. The
Thenumnum andand varvar parsers are simple; we just give the result fromparsers are simple; we just give the result from wordword andand numbernumberto the relevantto the relevant Expr
Expr constructor.constructor. var ::
var :: ParParser Exprser Expr va
var r = = woword rd >->-> > VaVarr num ::
num :: ParParser Exprser Expr nu
num m = = nunumbmber >-> er >-> NuNumm Examples
Examples va
var r "c"couount nt + + 1" 1" -> -> JuJustst(V(Var ar "c"couountnt", ", "+ "+ 1"1")) num
num "12"123*43*456" 56" -> -> JusJust(Nt(Num um 123123, , "*4"*456"56")) The
Theexprexpr,,termtermandand factorfactorparsers are going to be mutually recursive. It may be simpler to defineparsers are going to be mutually recursive. It may be simpler to define and test such functions if one temporarily eliminates the recursion. We do it by simplifying the and test such functions if one temporarily eliminates the recursion. We do it by simplifying the factor
factor productioproduction.n. fa
factctor :: or :: nunum m | | vavar r | | "("(" " vavar r ")")""
We already have parsers for the two first alternatives. A parser for the last alternative is given We already have parsers for the two first alternatives. A parser for the last alternative is given by
by li
lit t ’(’(’ ’ -# var -# var #- lit ’)’#- lit ’)’ We conclude with
We conclude with fac
factor tor :: :: ParParser ser ExpExprr
factor = num ! var ! lit ’(’ -# var #- lit ’)’ factor = num ! var ! lit ’(’ -# var #- lit ’)’ A straightf
A straightforwarorward implemend implementation of the tation of the rule for terms rule for terms woulwould parse e.g. d parse e.g. the expressthe expression 6ion 6//33//22 as
as 66//(3(3//2) contrary to the ordinary rules. In order to get the interpretation (62) contrary to the ordinary rules. In order to get the interpretation (6 //3)3)//2 we may use2 we may use the same technique as in the definition of
the same technique as in the definition of number’number’ with a parameter to accumulate the result.with a parameter to accumulate the result. te
termrm’ ’ :: :: ExExpr pr -> -> PaParsrser er ExExprpr term’ e =
term’ e = m
mululOp Op # # fafactctor or >->-> > blbldOdOp p e e #> #> tetermrm’ ’ !! ret
return urn ee The parameter
The parameter eewill be the result of the previous parser. If the input doesn’t start with awill be the result of the previous parser. If the input doesn’t start with a mulOpmulOp we just return
we just return ee. . OtheOtherwisrwisee mulmulOp Op # # facfactortor results in a pair which is given toresults in a pair which is given to bldbldOp Op ee. . TThhee transformed result is given recursively to
transformed result is given recursively to term’term’. The transformation. The transformation bldOpbldOp is defined byis defined by bl
bldOdOp p :: :: ExExpr pr -> -> (E(Expxpr r -> -> ExExpr pr -> -> ExExprpr, , ExExprpr) ) -> -> ExExprpr bl
bldOdOp p e e (o(opeper,r,e’e’) ) = = opoper er e e e’e’ Example
bl
bldOdOp p (N(Num 1) um 1) (M(Mulul, , NuNum m 2) -> 2) -> MuMul l NuNum m 1 1 NuNum m 22 The result of all this is given recursively to
The result of all this is given recursively to term’term’ which will find a newwhich will find a new mulOpmulOp or or terminterminate.ate. te
termrm’ ’ (N(Num um 1) 1) "*"*2" 2" ->-> (m
(mululOp Op # # fafactctor or >->-> > blbldOdOp p (N(Num um 1) 1) #> #> tetermrm’) ’) "*"*2" 2" ->-> (t
(tererm’ m’ (b(bldldOp Op (N(Num um 1) 1) (M(Mulul, , NuNum m 2)2))) )) "" "" ->-> (term’ (Mul (Num 1) (Num 2))) "" ->
(term’ (Mul (Num 1) (Num 2))) "" -> Jus
Just(Mt(Mul ul (Nu(Num m 1) 1) (Nu(Num m 2), "")2), "") The definition of
The definition of termterm is simple; parse a factor and send the result tois simple; parse a factor and send the result to term’term’.. ter
term m :: :: ParParser Exprser Expr te
term rm = = fafactctor or #> #> tetermrm’’ Examples
Examples te
termrm’ ’ (N(Num um 1) 1) "" "" -> -> JuJustst(N(Num um 1, 1, """")) te
term rm "1"1*2*2" " -> -> JuJustst(M(Mul ul (N(Num um 1) 1) (N(Num um 2)2), , """")) te
term rm "6"6/3/3/2/2" " -> -> JuJustst(D(Div iv (D(Div iv (N(Num um 6) 6) (N(Num um 3)3)) ) (N(Num um 2)2), , """")) The definitions of
The definitions of expr’expr’ andand exprexpr are are analogouanalogous.s. ex
exprpr’ ’ :: :: ExExpr pr -> -> PaParsrser er ExExprpr expr’ e =
expr’ e = ad
addOdOp p # # teterm rm >->-> > blbldOdOp p e e #> #> exexprpr’ ’ !! ret
return urn ee exp
expr r :: :: ParParser Exprser Expr ex
expr pr = = teterm rm #> #> exexprpr’’ We may now compose the
We may now compose the factorfactor,, termterm andand exprexpr parsers resetting the first one to make themparsers resetting the first one to make them mutually recursive. mutually recursive. fac factor tor == num ! num ! var ! var !
lit ’(’ -# expr #- lit ’)’ ! lit ’(’ -# expr #- lit ’)’ ! err
err "ille"illegal gal factfactor"or" term’ e =
term’ e = m
mululOp Op # # fafactctor or >->-> > blbldOdOp p e e #> #> tetermrm’ ’ !! ret
return urn ee te
term rm = = fafactctor or #> #> tetermrm’’ expr’ e =
expr’ e = ad
addOdOp p # # teterm rm >->-> > blbldOdOp p e e #> #> exexprpr’ ’ !! ret
return urn ee ex
expr pr = = teterm rm #> #> exexprpr’’
We have added another alternative parser in
We have added another alternative parser in factorfactor in order to get an informative message forin order to get an informative message for illegal input. Without it, any invalid string argument will just return
illegal input. Without it, any invalid string argument will just return NothingNothing.. The
The errerr parser is defined byparser is defined by er
err r :: :: StStriring ng -> -> PaParsrser er aa er
It will print the message and the offending input and abort the computation. It will print the message and the offending input and abort the computation.
Exer
Exercise cise 1515 Define a parser Define a parser reqrequiruire e :: :: StrString -> ing -> ParParser aser a that behaves asthat behaves as acceptaccept but emitsbut emits
an error message instead of returning
an error message instead of returning NothingNothing.. Program structure
Program structure The program parts that
The program parts that we havwe have developed so far e developed so far should be divided into two parser modules andshould be divided into two parser modules and one expres
one expression module. sion module. The first parser module should contThe first parser module should contain the parser type, the basic parsersain the parser type, the basic parsers and parser operators, while the second one contains derived parsers and parser operators. The and parser operators, while the second one contains derived parsers and parser operators. The representation of parsers as values of type
representation of parsers as values of type StrinString g -> -> MaybMaybe(a, String)e(a, String) will not be exploitedwill not be exploited outside the first parser module.
outside the first parser module.
Since we are going to define parsers for more than one data type it is useful to have a type class Since we are going to define parsers for more than one data type it is useful to have a type class decla
declaring the ring the namesnamesparseparseandand toStringtoStringand definingand defining fromStringfromString. We can then use these names. We can then use these names for all the data types and the relevant one will be chosen using the context where the name is for all the data types and the relevant one will be chosen using the context where the name is used. We use the name
used. We use the name ParseParse for the type class.for the type class.
It is inconvenient to use qualified import for the parser module, so we refrain from giving the It is inconvenient to use qualified import for the parser module, so we refrain from giving the parser type
parser type the the convconvententional nameional name TT..
mod
module ule CoreCoreParsParser(Per(Parsearser, r, charchar, , retureturn, rn, failfail, , (#), (!), (#), (!), (?), (#>), (?), (#>), (>->(>->),), Pars
Parse, e, parsparse, e, toSttoStringring, , fromfromStriString) ng) wherwheree impo
import rt PrelPrelude ude hidihiding ng (ret(return, urn, failfail)) in infifixl xl 3 3 !! in infifixl xl 7 7 ?? in infifixl xl 6 6 ## in infifixl xl 5 5 >->->> in infifixl xl 4 4 #>#> cla
class ss ParParse se a a whewherere pa
parsrse e :: :: PaParsrser er aa fro
fromStmStriring ng :: :: StrString ing -> -> aa fro
fromStmStriring ng cs cs == ca
case se paparsrse e cs cs ofof Ju
Justst(s(s, , [][]) ) -> -> ss Just
Just(s, (s, cs) cs) -> -> erroerror r ("ga("garbagrbage e ’"++’"++cs++cs++"’")"’") Noth
Nothing ing -> -> erroerror r "Not"Nothinghing"" to
toStStriring ng :: :: a a -> -> StStriringng
ty
type pe PaParsrser er a a = = StStriring ng -> -> MaMaybybe e (a(a, , StStriringng))
cha
char r :: :: ParParser ser ChCharar cha
char r [][]= = NotNothinhingg ch
char ar (c(c:c:cs) s) = = JuJust st (c(c, , cscs))
re
retuturn :: rn :: a a -> -> PaParsrser aer a re
retuturn rn a a cs cs = = JuJust st (a(a, , cscs))
f
faiail :: l :: PaParrseser ar a fa
(!
(!) ) :: :: PaParsrser er a a -> -> PaParsrser er a a -> -> PaParsrser er aa (m ! n) cs = case m cs of
(m ! n) cs = case m cs of No
Noththining g -> -> n n cscs m
mcs cs -> -> mcmcss
(?
(?) ) :: Par:: Parseser r a a -> (a -> (a -> -> BoBoolol) ) -> Par-> Parseser r aa (m ? p) cs =
(m ? p) cs = case m cs of case m cs of Noth
Nothing ing -> -> NothNothinging Ju
Justst(r(r, , s) s) -> -> if if p p r r ththen en JuJustst(r(r, , s) s) elelse se NoNoththiningg
(#
(#) ) :: :: PaParsrser er a a -> -> PaParsrser er b b -> -> PaParsrser er (a(a, , b)b) (m # n) cs =
(m # n) cs = case m cs of case m cs of Noth
Nothing ing -> -> NothNothinging Jus
Just(at(a, , cs’cs’) ) ->-> ca
case n se n cscs’ ’ ofof Noth
Nothing ing -> -> NothNothinging Jus
Just(bt(b, , cs’cs’’) ’) -> -> JuJust(st((a, (a, b), b), cscs’’)’’)
(>
(>->->) ) :: Par:: Parseser r a a -> (a -> (a -> b) -> b) -> -> PaParsrser ber b (m >-> b) cs =
(m >-> b) cs = case m cs of case m cs of Jus
Just(at(a, , cs’cs’) ) -> -> JuJust(st(b b a, a, cs’cs’)) Noth
Nothing ing -> -> NothNothinging
(#
(#>) >) :: :: PaParsrser er a a -> -> (a (a -> -> PaParsrser er b) b) -> -> PaParsrser er bb (p #> k) cs =
(p #> k) cs = case p cs of case p cs of Noth
Nothing ing -> -> NothNothinging Ju
Justst(a(a, , cscs’) ’) -> -> k k a a cscs’’
The derived parsers appear in
The derived parsers appear in Parser.hsParser.hs.. modu
module le ParseParser(modr(module ule CoreCoreParseParser, r, digitdigit, , digidigitVal, tVal, charschars, , letteletter, r, err,err, lit, number,
lit, number, iteriter, , accepaccept, t, requirequire, re, tokentoken,, spa
spacesces, , worword, d, (-#(-#), ), (#-(#-)) )) whewherere impo
import rt PreluPrelude de hidinhiding g (retu(return, rn, fail)fail) import CoreParser import CoreParser in infifixl xl 7 7 -#-#, , #- #-type T a = Parser a type T a = Parser a er
err r :: :: StStriring ng -> -> PaParsrser er aa err
err mesmessagsage e cs cs = = errerror or (me(messassage+ge++" +" neanear r "++"++cs+cs++"\+"\n")n") it
iter er :: :: PaParsrser er a a -> -> PaParsrser er [a[a]]
iter m = m # iter m >-> cons ! return [] iter m = m # iter m >-> cons ! return [] co
consns(a(a, , b) b) = = a:a:bb .
The expression module will have a few other functions not mentioned previously. We rename the The expression module will have a few other functions not mentioned previously. We rename the main type to
main type to TT. The. The valuevalue function should evalufunction should evaluate an ate an expreexpression. ssion. It will It will need a need a “dicti“dictionary”onary” to find the values of the variables.
to find the values of the variables.
The expression module will make heavy use of the parser functions so it is convenient to import The expression module will make heavy use of the parser functions so it is convenient to import the parser module without qualification.
the parser module without qualification. modu
module le Expr(Expr(Expr, T, Expr, T, parsparse, e, fromSfromStringtring, , valuvalue, e, toStrtoString) ing) wherewhere impo
import rt PreluPrelude de hidinhiding g (retu(return, rn, fail)fail) impo
import rt ParseParserr impo
import rt qualiqualified fied DictiDictionaronaryy da
data ta ExExpr pr = = NuNum m InIntetegeger r | | VaVar r StStriring ng | | AdAdd d ExExpr pr ExExprpr |
| SuSub b ExExpr pr ExExpr pr | | MuMul l ExExpr pr ExExpr pr | | DiDiv v ExExpr pr ExExprpr deriv
deriving ing ShowShow ty
type T pe T = = ExExprpr ... ... inst
instance Parse ance Parse Expr whereExpr where par
parse se = = expexprr to
toStStriring ng = = shshw w 00
Parsing with ambiguous grammars Parsing with ambiguous grammars This descri
This description is mainly inspired by an ption is mainly inspired by an articlarticle by e by Phil WPhil Wadler, [4]. adler, [4]. He uses an He uses an eleganelegant trickt trick to code
to code the failthe failure and the ure and the sucsuccescess s of a of a parparser and findinser and finding g all possiall possible parsble parses of es of a a stristring. ng. TheThe Maybe
Maybe data type may be viewed as a special case of the list type, but with the list either beingdata type may be viewed as a special case of the list type, but with the list either being empty,
empty, NothingNothing, or having one element,, or having one element, JuJust st aa. . FFailuailure will be re will be reprepresresenented by the ted by the empempty listty list while a nonempty list contains all possible successes. He defines the parser type by
while a nonempty list contains all possible successes. He defines the parser type by typ
type e ParParser ser a a = = StrString -> ing -> [(a[(a,St,Strinring)]g)] As an example the number parser will find As an example the number parser will find
number "123abc"
number "123abc" -> -> [(123,"abc"),(1[(123,"abc"),(12,"3abc"),(1,"232,"3abc"),(1,"23abc")]abc")] num
number "abc" -> ber "abc" -> [][] A
A few modificfew modificatiations are ons are neeneeded in ded in the Parsthe Parser moduleer module. . The basiThe basic c parparser musser must t be be chchanganged toed to return lists instead of
return lists instead of MaybeMaybe values.values. char [] = []
char [] = [] cha
char r (c:(c:cs) cs) = = [(c[(c,cs,cs)])] re
retuturn rn a a cs cs = = [([(a,a,cscs)])] fail cs = []
fail cs = []
The operators must also be adapted. Using list comprehension they become “one-liners” The operators must also be adapted. Using list comprehension they become “one-liners”
(m ? p) cs = [(a,cs’) | (a,cs’) <- m cs, p a] (m ? p) cs = [(a,cs’) | (a,cs’) <- m cs, p a] (m ! n) cs = (m cs) ++ (n cs)
(m
(m # # n) n) cs cs = = [([((a(a,b,b),),cscs’’’’) ) | | (a(a,c,cs’s’) ) <- <- m m cscs, , (b(b,c,cs’s’’) <- ’) <- n n cscs’]’] (m >-> b) cs = [(b a, cs’) | (a,cs’) <- m cs]
(m >-> b) cs = [(b a, cs’) | (a,cs’) <- m cs] (p
(p #> #> k) k) cs cs = = [([(b,b,cscs’’’’) ) | | (a(a,c,cs’s’) ) <- <- p p cscs, , (b(b,c,cs’s’’) ’) <- k <- k a a cscs’]’] The
The errerr and hence theand hence the requirerequire parsers can no longer be used inparsers can no longer be used in ExprExpr andand ProgramProgram to giveto give adequate error messages.
adequate error messages. Parsec
Parsec
There is a “industrial stength” parser library called
There is a “industrial stength” parser library called ParsecParsec, , [2[2]. ]. It useIt uses basis basicacalllly the samy the samee technique as above, but is larger and uses
technique as above, but is larger and uses dodo notation notation extensextensiveivelyly. . An An expreexpression parser usingssion parser using this
this library follows.library follows. impo
import rt ParseParsecc import ParsecExpr import ParsecExpr da
data ta ExExpr pr = = NuNum m InInt t | | VaVar r StStriring | ng | AdAdd d ExExpr pr ExExprpr |
| SuSub b ExExpr pr ExExpr pr | | MuMul l ExExpr pr ExExpr pr | | DiDiv v ExExpr pr ExExprpr deriv
deriving ing ShowShow e
exxppr r ::: : PPaarrsseer r EExxpprr ex
expr pr = b= buiuildldExExprpresessisiononPaParsrser er tatablble fe facactotorr <?>
<?> "expr"expressioession"n" ta
tablble e = [[op "*= [[op "*" " MuMul Assol AssocLcLefeft, op "/" Div Ast, op "/" Div AssosocLcLefeft]t],, [op "+"
[op "+" Add AssocAdd AssocLefLeft, t, op op "-" Sub "-" Sub AssAssocLocLefteft]]]] where
where
op s f assoc op s f assoc
=
= InInfifix x (d(do{ stro{ strining g s; rets; retururn n f}f}) ) asassosocc fa
factctor or = do= do{ cha{ char ’(’r ’(’ ; x <- expr ; x <- expr ; ; chchar ’)’ar ’)’ ; ; reretuturn rn x}x} <|> <|> numbnumberer <|>
<|> varivariableable <?>
<?> "sim"simple ple exprexpressioession"n" nu
numbmber er :: Pa:: Parsrser Exer Exprpr nu
numbmber er = do{ ds<= do{ ds<- many- many1 dig1 digitit ;
; retreturn urn (Nu(Num m (re(read ad ds)ds))})} <?>
<?> "num"number"ber" var
variabiable le :: :: ParParser ser ExpExprr var
variabiable le = = do{ ds<- do{ ds<- manmany1 y1 letletterter ;
; retreturn (Var urn (Var ds)ds)}} <?>
<?> "var"variableiable""
The library has special support for
The library has special support for parsinparsing expressiog expressions. ns. Operator precedeOperator precedences and associativnces and associativ--ities are given using a table.
ities are given using a table. dodo notation is used to express sequential composition of parsers.notation is used to express sequential composition of parsers. Alternatives are separated by
References References
[1] Hutton, G. and Meier, E.,
[1] Hutton, G. and Meier, E., Monadic Parser CombinatorsMonadic Parser Combinators, Technical report NOTTCS-TR-, Technical report NOTTCS-TR-96-4, Department of Computer Science, University of Nottingham, 1996.
96-4, Department of Computer Science, University of Nottingham, 1996. [2] Leijen, D.,
[2] Leijen, D., Parsec, a fast combinator parser Parsec, a fast combinator parser , Oct 2001,, Oct 2001, http://www.cs.ruu.nl/˜daan/parsec.html.
http://www.cs.ruu.nl/˜daan/parsec.html. [3] Wadler P.,
[3] Wadler P., How to replace failure by a list of successesHow to replace failure by a list of successes, 2nd International Conference on Func-, 2nd International Conference on Func-tional
tional ProgrammProgramming ing LanguagLanguages es and and ComputComputer er ArchArchitectuitecture, re, SpringSpringer-Ver-Verlag, erlag, Nancy Nancy FFrance,rance, September 1985. http://cm.bell-labs.com/cm/cs/who/wadler/.
September 1985. http://cm.bell-labs.com/cm/cs/who/wadler/. [4] Wadler, P.,
[4] Wadler, P., Monads for functional programming Monads for functional programming , Marktoberdorf Summer School on Program, Marktoberdorf Summer School on Program Design Calculi, Springer Verlag, NATO ASI Series F: Computer and systems sciences, Volume Design Calculi, Springer Verlag, NATO ASI Series F: Computer and systems sciences, Volume 118, August 1992. http://cm.bell-labs.com/cm/cs/who/wadler/.