1.. _parserLibraryParser:
6To run our parser we call 'parse'. This requires a Grammar and the output
7from the lexer (a list of tokens).
11 parse : (act : Grammar tok c ty) -> (xs : List tok) ->
12 Either (ParseError tok) (ty, List tok)
14If successful this returns 'Right' with a pair of
17- the unparsed tokens (the remaining input).
19otherwise it returns 'Left' with the error message.
21So we need to define the Grammar for our parser, this is done using the following
22'Grammar' data structure. This is a combinator structure, similar in principle
23to the recogniser combinator for the lexer, which was discussed on the
26As with the Recogniser the Grammar type is dependent on a boolean 'consumes'
27value which allows us to ensure that complicated Grammar structures will always
32 ||| Description of a language's grammar. The `tok` parameter is the type
33 ||| of tokens, and the `consumes` flag is True if the language is guaranteed
34 ||| to be non-empty - that is, successfully parsing the language is guaranteed
35 ||| to consume some input.
37 data Grammar : (tok : Type) -> (consumes : Bool) -> Type -> Type where
38 Empty : (val : ty) -> Grammar tok False ty
39 Terminal : String -> (tok -> Maybe a) -> Grammar tok True a
40 NextIs : String -> (tok -> Bool) -> Grammar tok False tok
41 EOF : Grammar tok False ()
43 Fail : Bool -> String -> Grammar tok c ty
44 Commit : Grammar tok False ()
45 MustWork : Grammar tok c a -> Grammar tok c a
47 SeqEat : Grammar tok True a -> Inf (a -> Grammar tok c2 b) ->
49 SeqEmpty : {c1, c2 : Bool} ->
50 Grammar tok c1 a -> (a -> Grammar tok c2 b) ->
51 Grammar tok (c1 || c2) b
52 Alt : {c1, c2 : Bool} ->
53 Grammar tok c1 ty -> Grammar tok c2 ty ->
54 Grammar tok (c1 && c2) ty
56So an example of a Grammer type may look something like this:
57'Grammar (TokenData ExpressionToken) True Integer'.
59This is a complicated type name and a given parser will need to use it a lot.
60So to reduce the amount of typing we can use the following type synonym (similar
61to what is done in Idris 2
62: https://github.com/edwinb/Idris2/blob/master/src/Parser/Support.idr
68 Rule ty = Grammar (TokenData ExpressionToken) True ty
73On the previous page we implemented a lexer to 'lex' a very simple expression, on
74this page we will go on to implement a parser for this running example.
78 * - Expressed in Backus–Naur form (BNF) the syntax we are aiming at is something
80 - .. code-block:: idris
82 <expr> ::= <integer literal>
88 <integer literal> ::= <digit>|<integer literal><digit>
92 * - To start, here is a Grammar to parse an integer literal (that is, a
95 - .. code-block:: idris
98 intLiteral : Rule Integer
100 = terminal (\x => case tok x of
104In order to try this out, here is a temporary function, this calls
105parse which takes two parameters:
107- The grammar (in this case intLiteral)
108- The token list from the lexer.
112 test1 : String -> Either (ParseError (TokenData ExpressionToken))
113 (Integer, List (TokenData ExpressionToken))
114 test1 s = parse intLiteral (fst (lex expressionTokens s))
116As required, if we pass it a string which is a number literal then it will
117return the number in the 'Right' option.
121 *parserEx> test1 "123"
122 Right (123, []) : Either (ParseError (TokenData ExpressionToken))
123 (Integer, List (TokenData ExpressionToken))
125If we pass it a string which is not a number literal then it will return an
131 Left (Error "End of input"
132 []) : Either (ParseError (TokenData ExpressionToken))
133 (Integer, List (TokenData ExpressionToken))
135If we pass it a number followed by something else, then it will still be
136successful, this is because we are not specifically checking for end-of-file.
140 *parserEx> test1 "123a"
141 Right (123, []) : Either (ParseError (TokenData ExpressionToken))
142 (Integer, List (TokenData ExpressionToken))
147 * - The 'intLiteral' function above uses the 'terminal' function to
148 construct the grammar
149 - .. code-block:: idris
151 ||| Succeeds if running the predicate on the
152 ||| next token returns Just x, returning x.
155 terminal : (tok -> Maybe a) -> Grammar tok True a
159: https://github.com/idris-lang/Idris-dev/blob/master/libs/contrib/Text/Parser/Core.idr
160Idris 2 uses a slightly different version which stores an error message like
161"Expected integer literal" which can be output if the rule fails
162: https://github.com/edwinb/Idris2/blob/master/src/Text/Parser/Core.idr
166 * - The 'terminal' function is also used to construct the other
167 elements of the grammar that we require, for instance,
170 - .. code-block:: idris
172 openParen : Rule Integer
173 openParen = terminal (\x => case tok x of
177Integer value is not really relevant for parenthesis so '0' is used as
180As before, we can test this out with a function like this:
184 test2 : String -> Either (ParseError (TokenData ExpressionToken))
185 (Integer, List (TokenData ExpressionToken))
186 test2 s = parse openParen (fst (lex expressionTokens s))
188We can see below that it correctly parses an open parenthesis and gives an
189error for anything else:
194 Right (0, []) : Either (ParseError (TokenData ExpressionToken))
195 (Integer, List (TokenData ExpressionToken))
196 *parserEx> test2 "123"
197 Left (Error "Unrecognised token"
200 (Number 123)]) : Either (ParseError (TokenData ExpressionToken))
202 List (TokenData ExpressionToken))
204Now we have two Grammars we can try combining them. The following test looks
205for 'openParen' followed by 'intLiteral', the two Grammars are combined using
206'<*>'. The 'map const' part uses the integer value from the first.
208The following test is looking for '(' followed by a number:
212 test3 : String -> Either (ParseError (TokenData ExpressionToken))
213 (Integer, List (TokenData ExpressionToken))
214 test3 s = parse (map const openParen <*> intLiteral) (fst (lex expressionTokens s))
216We can see below that '(' followed by a number is successfully parsed but other
221 *parserEx> test3 "(123"
222 Right (0, []) : Either (ParseError (TokenData ExpressionToken))
223 (Integer, List (TokenData ExpressionToken))
224 *parserEx> test3 "(("
225 Left (Error "Unrecognised token"
227 (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n')))
229 (incol, "") => c + cast (length incol)
230 (incol, b) => cast (length incol))
231 OParen]) : Either (ParseError (TokenData ExpressionToken))
232 (Integer, List (TokenData ExpressionToken))
234 *parserEx> test3 "123"
235 Left (Error "Unrecognised token"
238 (Number 123)]) : Either (ParseError (TokenData ExpressionToken))
240 List (TokenData ExpressionToken))
241 *parserEx> test3 "123("
242 Left (Error "Unrecognised token"
243 [MkToken 0 0 (Number 123),
245 (case fspan (\ARG => not (intToBool (prim__eqChar ARG '\n')))
247 (incol, "") => c + cast (length incol)
248 (incol, b) => cast (length incol))
249 OParen]) : Either (ParseError (TokenData ExpressionToken))
250 (Integer, List (TokenData ExpressionToken))
254 * - The closing parenthesis is constructed in the same way.
256 - .. code-block:: idris
258 closeParen : Rule Integer
259 closeParen = terminal (\x => case tok x of
265 * - Now we can generate a Grammar for an expression inside parenthesis
268 - .. code-block:: idris
270 paren : Rule Integer -> Rule Integer
271 paren exp = openParen *> exp <* closeParen
273The use of '*>' and '<*' instead of '<*>' is an easy way to use the integer
274value from the inner expression.
278 * - Now for the operations, in this case: '+', '-' and '*'.
279 The syntax we require is that operators like '+' are infix operators, which
280 would require a definition like this:
282 - .. code-block:: idris
284 expr = (add expr (op "+") expr)
286This is a potentially infinite structure which is not total.
287In order to work up to this gradually I will start with prefix operators
288(sometimes known as Polish notation) then modify later for infix operators.
292 * - So prefix operators would have this sort of form:
294 - .. code-block:: idris
296 expr = (add (op "+") expr expr)
298where 'op' is defined like this:
302 ||| Matches if this is an operator token and string matches, that is,
303 ||| it is the required type of operator.
304 op : String -> Rule Integer
305 op s = terminal (\x => case tok x of
306 (Operator s1) => if s==s1 then Just 0 else Nothing
311 * - and 'add' is defined like this:
313 - .. code-block:: idris
315 addInt : Integer -> Integer -> Integer
319 add : Grammar tok c1 Integer ->
320 Grammar tok c2 Integer ->
321 Grammar tok c3 Integer ->
322 Grammar tok ((c1 || c2) || c3) Integer
323 add x y z = map addInt (x *> y) <*> z
327- x is the add operator.
328- y is the first operand.
329- z is the second operand.
331The resulting integer will be the sum of the two operands.
333The other operators are defined in a similar way:
337 subInt : Integer -> Integer -> Integer
341 sub : Grammar tok c1 Integer ->
342 Grammar tok c2 Integer ->
343 Grammar tok c3 Integer ->
344 Grammar tok ((c1 || c2) || c3) Integer
345 sub x y z = map subInt (x *> y) <*> z
348 multInt : Integer -> Integer -> Integer
352 mult : Grammar tok c1 Integer ->
353 Grammar tok c2 Integer ->
354 Grammar tok c3 Integer ->
355 Grammar tok ((c1 || c2) || c3) Integer
356 mult x y z = map multInt (x *> y) <*> z
358So the top level Grammar can now be defined as follows. Note that this is
359partial as it is a potentially infinite structure and so not total.
365 expr = (add (op "+") expr expr)
366 <|> (sub (op "-") expr expr)
367 <|> (mult (op "*") expr expr)
368 <|> intLiteral <|> (paren expr)
372 * - To make the testing easier we can use this function:
374 - .. code-block:: idris
377 test : String -> Either (ParseError (TokenData ExpressionToken))
378 (Integer, List (TokenData ExpressionToken))
379 test s = parse expr (fst (lex expressionTokens s))
383 * - First test a valid (prefix) expression:
385 - .. code-block:: idris
387 *parserEx> test "+1*6(4)"
389 []) : Either (ParseError (TokenData ExpressionToken))
390 (Integer, List (TokenData ExpressionToken))
392Then an invalid syntax:
397 Left (Error "Unrecognised token"
401 not (intToBool (prim__eqChar ARG
404 (incol, "") => c + cast (length incol)
405 (incol, b) => cast (length incol))
406 CParen]) : Either (ParseError (TokenData ExpressionToken))
408 List (TokenData ExpressionToken))
410However if we try something that is invalid, but starts with a valid token,
411then it will return 'Right' (to indicate success)
415 *parserEx> test "1))"
419 not (intToBool (prim__eqChar ARG '\n')))
421 (incol, "") => c + cast (length incol)
422 (incol, b) => cast (length incol))
426 not (intToBool (prim__eqChar ARG '\n')))
428 (incol, "") => c + cast (length incol)
429 (incol, b) => cast (length incol))
430 CParen]) : Either (ParseError (TokenData ExpressionToken))
432 List (TokenData ExpressionToken))
437So far we have implemented a prefix notation for operators (like this:
438'+ expr expr') but the aim is to implemented an infix notation (like this:
439'expr + expr'). To do this we must be able to deal with potentially
440infinite data structures (see Codata Types here :ref:`sect-typefuns`).
442First alter the grammar to have infix operations:
446 addInt : Integer -> Integer -> Integer
450 add : Grammar tok c1 Integer ->
451 Grammar tok c2 Integer ->
452 Grammar tok c3 Integer ->
453 Grammar tok ((c1 || c2) || c3) Integer
454 add x y z = map addInt (x <* y) <*> z
456 subInt : Integer -> Integer -> Integer
460 sub : Grammar tok c1 Integer ->
461 Grammar tok c2 Integer ->
462 Grammar tok c3 Integer ->
463 Grammar tok ((c1 || c2) || c3) Integer
464 sub x y z = map subInt (x <* y) <*> z
467 multInt : Integer -> Integer -> Integer
471 mult : Grammar tok c1 Integer ->
472 Grammar tok c2 Integer ->
473 Grammar tok c3 Integer ->
474 Grammar tok ((c1 || c2) || c3) Integer
475 mult x y z = map multInt (x <* y) <*> z
479 expr = (add expr (op "+") expr)
480 <|> (sub expr (op "-") expr)
481 <|> (mult expr (op "*") expr)
482 <|> intLiteral <|> (paren expr)
484However, if this was run, the code would not terminate.
486Implement Left Recursion Elimination
487------------------------------------
489Left factoring, like this, is a general problem.
491If we have a rule like this:
495 A -> (x<*>y) <|> (x<*>z)
497If 'x<*>y' fails but 'x<*>z' would succeed a problem is that, 'x<*>y' has
498already consumed 'x', so now 'x<*>z' will fail.
500so we could write code to backtrack. That is 'try' 'x<*>y' without consuming
501so that, if the first token succeeds but the following tokens fail, then the
502first tokens would not be consumed.
504Another approach is left factoring:
509Replace the rule with two rules (that is we add a non-terminal symbol) so
510for example, instead of:
514 A -> (x<*>y) <|> (x<*>z)
516we add an extra rule to give:
523That is we convert a general context-free grammar to a LL(1) grammar. Although
524not every context-free grammar can be converted to a LL(1) grammar.
526This still does not solve the infinite recursion issue and there is another
527problem: the precedence of the operators +, - and * is not explicit.
529To resolve this we can alter the example like this:
533 paren : Rule Integer -> Rule Integer
534 paren exp = openParen *> exp <* closeParen
536 addInt : Integer -> Integer -> Integer
539 subInt : Integer -> Integer -> Integer
542 multInt : Integer -> Integer -> Integer
549 exprAtom : Rule Integer
550 exprAtom = intLiteral <|> (paren expr)
554 expr1 = map multInt exprAtom <*> ((op "*") *> exprAtom)
557 exprMult : Rule Integer
558 exprMult = expr1 <|> exprAtom
562 expr2 = map addInt exprMult <*> ((op "+") *> exprMult)
565 exprAdd : Rule Integer
566 exprAdd = expr2 <|> exprMult
570 expr3 = map subInt exprAdd <*> ((op "-") *> expr)
572 expr = expr3 <|> exprAdd
574As the following tests show, this now handles infix operators and precedence.
578 * - For example '*' is an infix operator:
580 - .. code-block:: idris
582 *parserExInfix> test "2*3"
584 []) : Either (ParseError (TokenData ExpressionToken))
585 (Integer, List (TokenData ExpressionToken))
587 * - Check that atomic number literals work:
589 - .. code-block:: idris
591 *parserExInfix> test "2"
593 []) : Either (ParseError (TokenData ExpressionToken))
594 (Integer, List (TokenData ExpressionToken))
596 * - Check that '*' has a higher precedence than '+'.
598 - .. code-block:: idris
600 *parserExInfix> test "2+3*4"
602 []) : Either (ParseError (TokenData ExpressionToken))
603 (Integer, List (TokenData ExpressionToken))
605 * - Also '*' has a higher precedence than '+' when the order is reversed.
607 - .. code-block:: idris
609 *parserExInfix> test "3*4+2"
611 []) : Either (ParseError (TokenData ExpressionToken))
612 (Integer, List (TokenData ExpressionToken))
614 * - Also precedence can be overridden by parenthesis.
616 - .. code-block:: idris
618 *parserExInfix> test "(2+3)*4"
620 []) : Either (ParseError (TokenData ExpressionToken))
621 (Integer, List (TokenData ExpressionToken))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list