@@ -7,7 +7,7 @@ The development of the Imp language in `Imp.lidr` completely ignores issues of
7
7
concrete syntax -- how an ascii string that a programmer might write gets
8
8
translated into abstract syntax trees defined by the datatypes `aexp` , `bexp` ,
9
9
and `com` . In this chapter, we illustrate how the rest of the story can be
10
- filled in by building a simple lexical analyzer and parser using Coq 's
10
+ filled in by building a simple lexical analyzer and parser using Idris 's
11
11
functional programming facilities.
12
12
13
13
It is not important to understand all the details here (and accordingly, the
@@ -18,15 +18,6 @@ code -- most of it is not very complicated, though the parser relies on some
18
18
most readers will probably want to just skim down to the Examples section at the
19
19
very end to get the punchline.
20
20
21
-
22
- Set Warnings " -notation-overridden,-parsing,-deprecated-implicit-arguments" .
23
- Require Import Coq . Strings . String .
24
- Require Import Coq . Strings . Ascii .
25
- Require Import Coq . Arith . Arith .
26
- Require Import Coq . Arith . EqNat .
27
- Require Import Coq . Lists . List .
28
- Import ListNotations .
29
-
30
21
> import Maps
31
22
> import Imp
32
23
>
@@ -37,9 +28,9 @@ Import ListNotations.
37
28
38
29
> isLowerAlpha : (c : Char) -> Bool
39
30
> isLowerAlpha c = isLower c || isDigit c
40
-
31
+ >
41
32
> data Chartype = White | Alpha | Digit | Other
42
-
33
+ >
43
34
> classifyChar : (c : Char) -> Chartype
44
35
> classifyChar c =
45
36
> if isSpace c then
@@ -50,10 +41,10 @@ Import ListNotations.
50
41
> Digit
51
42
> else
52
43
> Other
53
-
44
+ >
54
45
> Token : Type
55
46
> Token = String
56
-
47
+ >
57
48
> tokenizeHelper : (cls : Chartype) -> (acc, xs : List Char) -> List (List Char)
58
49
> tokenizeHelper cls acc xs =
59
50
> case xs of
@@ -79,12 +70,13 @@ Import ListNotations.
79
70
> tk = case acc of
80
71
> [] => []
81
72
> (_ :: _ ) => [reverse acc]
82
-
73
+ >
83
74
> tokenize : (s : String) -> List String
84
75
> tokenize s = map pack (tokenizeHelper White [] (unpack s))
85
-
76
+ >
86
77
> tokenizeEx1 : tokenize "abc12==3 223*(3+(a+c))" = ["abc","12","==","3","223","*","(","3","+","(","a","+","c",")",")"]
87
78
> tokenizeEx1 = Refl
79
+ >
88
80
89
81
=== Parsing
90
82
@@ -95,6 +87,7 @@ An `option` type with error messages:
95
87
> data OptionE : (x : Type ) -> Type where
96
88
> SomeE : x -> OptionE x
97
89
> NoneE : String -> OptionE x
90
+ >
98
91
99
92
Some interface instances to make writing nested match- expressions on `OptionE`
100
93
more convenient.
@@ -104,69 +97,75 @@ more convenient.
104
97
> Functor OptionE where
105
98
> map f (SomeE x) = SomeE (f x)
106
99
> map _ (NoneE err) = NoneE err
107
-
100
+ >
108
101
> Applicative OptionE where
109
102
> pure = SomeE
110
103
> (SomeE f) <*> (SomeE x) = SomeE (f x)
111
104
> (SomeE _ ) <*> (NoneE e2) = NoneE e2
112
105
> (NoneE e1) <*> (SomeE _ ) = NoneE e1
113
106
> (NoneE e1) <*> (NoneE e2) = NoneE (e1 ++ " ;" ++ e2)
114
-
107
+ >
115
108
> Alternative OptionE where
116
- > empty = NoneE " "
117
- > (SomeE x) <|> _ = SomeE x
118
- > (NoneE _ ) <|> v = v
119
-
109
+ > empty = NoneE " "
110
+ > (SomeE x) <|> _ = SomeE x
111
+ > (NoneE _ ) <|> v = v
112
+ >
120
113
> Monad OptionE where
121
- > (NoneE e) >>= _ = NoneE e
122
- > (SomeE x) >>= k = k x
114
+ > (NoneE e) >>= _ = NoneE e
115
+ > (SomeE x) >>= k = k x
116
+ >
123
117
124
118
==== Generic Combinators for Building Parsers
125
119
126
120
> Parser : (t : Type ) -> Type
127
121
> Parser t = List Token -> OptionE (t, List Token )
128
-
122
+ >
129
123
> manyHelper : (p : Parser t) -> (acc : List t) -> (steps : Nat ) -> Parser (List t)
130
124
> manyHelper p acc Z _ = NoneE " Too many recursive calls"
131
125
> manyHelper p acc (S steps') xs with (p xs)
132
126
> | NoneE _ = SomeE (reverse acc, xs)
133
127
> | SomeE (t', xs') = manyHelper p (t':: acc) steps' xs'
128
+ >
134
129
135
130
A (step- indexed) parser that expects zero or more `p` s :
136
131
137
132
> many : (p : Parser t) -> (steps : Nat ) -> Parser (List t)
138
133
> many p steps = manyHelper p [] steps
134
+ >
139
135
140
136
A parser that expects a given token, followed by `p` :
141
137
142
138
> firstExpect : (a : Token) -> (p : Parser t) -> Parser t
143
- > firstExpect a p (x:: xs) = if x == a then p xs else NoneE (" expected '" ++ a ++ " '." )
144
- > firstExpect a _ [] = NoneE (" expected '" ++ a ++ " '." )
139
+ > firstExpect a p (x:: xs) = if x == a then p xs else NoneE (" Expected '" ++ a ++ " '" )
140
+ > firstExpect a _ [] = NoneE (" Expected '" ++ a ++ " '" )
141
+ >
145
142
146
143
A parser that expects a particular token :
147
144
148
145
> expect : (t : Token) -> Parser ()
149
146
> expect t = firstExpect t (\ xs => SomeE (() , xs))
150
-
147
+ >
151
148
==== A Recursive - Descent Parser for Imp
152
149
153
150
Identifiers :
154
151
155
152
> parseIdentifier : Parser Id
156
153
> parseIdentifier [] = NoneE " Expected identifier"
157
- > parseIdentifier (x:: xs' ) =
154
+ > parseIdentifier (x:: xs) =
158
155
> if all isLowerAlpha (unpack x)
159
- > then SomeE (MkId x, xs' )
156
+ > then SomeE (MkId x, xs)
160
157
> else NoneE (" Illegal identifier:'" ++ x ++ " '" )
158
+ >
161
159
162
160
Numbers :
163
161
164
162
> parseNumber : Parser Nat
165
163
> parseNumber [] = NoneE " Expected number"
166
- > parseNumber (x:: xs' ) =
164
+ > parseNumber (x:: xs) =
167
165
> if all isDigit (unpack x)
168
- > then SomeE (foldl (\ n, d => 10 * n + (cast (ord d - ord ' 0' ))) 0 (unpack x), xs' )
166
+ > then SomeE (foldl (\ n, d => 10 * n + (cast (ord d - ord ' 0' ))) 0 (unpack x), xs)
169
167
> else NoneE " Expected number"
168
+ >
170
169
171
170
Parse arithmetic expressions
172
171
@@ -213,24 +212,25 @@ Parse arithmetic expressions
213
212
>
214
213
> parseAExp : (steps : Nat ) -> Parser AExp
215
214
> parseAExp = parseSumExp
215
+ >
216
216
217
217
Parsing boolean expressions :
218
218
219
219
> mutual
220
220
> parseAtomicExp : (steps : Nat ) -> Parser BExp
221
221
> parseAtomicExp Z _ = NoneE " Too many recursive calls"
222
222
> parseAtomicExp (S steps') xs =
223
- > (do (u , rest) <- expect " true" xs
223
+ > (do (_ , rest) <- expect " true" xs
224
224
> pure (BTrue , rest))
225
225
> <|>
226
- > (do (u , rest) <- expect " false" xs
226
+ > (do (_ , rest) <- expect " false" xs
227
227
> pure (BFalse , rest))
228
228
> <|>
229
229
> (do (e, rest) <- firstExpect " not" (parseAtomicExp steps') xs
230
230
> pure (BNot e, rest))
231
231
> <|>
232
232
> (do (e, rest) <- firstExpect " (" (parseConjunctionExp steps') xs
233
- > (u , rest') <- expect " )" rest
233
+ > (_ , rest') <- expect " )" rest
234
234
> pure (e, rest'))
235
235
> <|>
236
236
> (do (e, rest) <- parseProductExp steps' xs
@@ -251,85 +251,58 @@ Parsing boolean expressions:
251
251
>
252
252
> parseBExp : (steps : Nat ) -> Parser BExp
253
253
> parseBExp = parseConjunctionExp
254
+ >
255
+ > testParsing : (p : Nat -> Parser t) -> (s : String) -> OptionE (t, List Token)
256
+ > testParsing p s = p 100 (tokenize s)
257
+ >
254
258
255
- `` coq
256
- Check parseConjunctionExp.
257
-
258
- Definition testParsing {X : Type }
259
- (p : nat ->
260
- list token ->
261
- optionE (X * list token))
262
- (s : string) :=
263
- let t : = tokenize s in
264
- p 100 t.
259
+ \ todo[inline]{The second one seems designed to fail }
265
260
266
- (*
267
- Eval compute in
268
- testParsing parseProductExp " x*y*(x*x)*x" .
261
+ `` `idris
262
+ λΠ> testParsing parseProductExp " x*y*(x*x)*x"
269
263
270
- Eval compute in
271
- testParsing parseConjunctionExp " not((x==x||x*x<=(x*x)*x)&&x==x" .
272
- * )
264
+ λΠ> testParsing parseConjunctionExp " not((x==x||x*x<=(x*x)*x)&&x==x"
273
265
`` `
274
266
275
267
Parsing commands :
276
268
277
- `` `coq
278
- Fixpoint parseSimpleCommand (steps : nat)
279
- (xs : list token) :=
280
- match steps with
281
- | 0 => NoneE " Too many recursive calls"
282
- | S steps' =>
283
- DO (u, rest) <-- expect " SKIP" xs;
284
- SomeE (SKIP , rest)
285
- OR DO (e,rest) <--
286
- firstExpect " IF" (parseBExp steps') xs;
287
- DO (c,rest') <==
288
- firstExpect " THEN"
289
- (parseSequencedCommand steps') rest;
290
- DO (c',rest'' ) <==
291
- firstExpect "ELSE"
292
- (parseSequencedCommand steps' ) rest';
293
- DO (u,rest'' ' ) <==
294
- expect " END" rest'' ;
295
- SomeE(IFB e THEN c ELSE c' FI , rest'' ' )
296
- OR DO (e,rest) <--
297
- firstExpect " WHILE"
298
- (parseBExp steps') xs;
299
- DO (c,rest') <==
300
- firstExpect " DO"
301
- (parseSequencedCommand steps') rest;
302
- DO (u,rest'' ) <==
303
- expect "END" rest' ;
304
- SomeE (WHILE e DO c END , rest'' )
305
- OR DO (i, rest) <==
306
- parseIdentifier xs;
307
- DO (e, rest' ) <==
308
- firstExpect " :=" (parseAExp steps') rest;
309
- SomeE (i ::= e, rest')
310
- end
311
-
312
- with parseSequencedCommand (steps : nat)
313
- (xs : list token) :=
314
- match steps with
315
- | 0 => NoneE " Too many recursive calls"
316
- | S steps' =>
317
- DO (c, rest) <==
318
- parseSimpleCommand steps' xs;
319
- DO (c', rest') <--
320
- firstExpect " ;;"
321
- (parseSequencedCommand steps') rest;
322
- SomeE (c ;; c', rest')
323
- OR
324
- SomeE (c, rest)
325
- end.
326
-
327
- Definition bignumber : = 1000.
328
-
329
- Definition parse (str : string) : optionE (com * list token) :=
330
- let tokens : = tokenize str in
331
- parseSequencedCommand bignumber tokens.
332
- `` `
269
+ > mutual
270
+ > parseSimpleCommand : (steps : Nat ) -> Parser Com
271
+ > parseSimpleCommand Z _ = NoneE " Too many recursive calls"
272
+ > parseSimpleCommand (S steps') xs =
273
+ > (do (_ , rest) <- expect " SKIP" xs
274
+ > pure (SKIP , rest))
275
+ > <|>
276
+ > (do (e, rest) <- firstExpect " IF" (parseBExp steps') xs
277
+ > (c, rest') <- firstExpect " THEN" (parseSequencedCommand steps') rest
278
+ > (c', rest'' ) <- firstExpect "ELSE" (parseSequencedCommand steps' ) rest'
279
+ > (_ , rest'' ' ) <- expect " END" rest''
280
+ > pure (IFB e THEN c ELSE c' FI , rest'' ' ))
281
+ > <|>
282
+ > (do (e, rest) <- firstExpect " WHILE" (parseBExp steps') xs
283
+ > (c, rest') <- firstExpect " DO" (parseSequencedCommand steps') rest
284
+ > (_ , rest'' ) <- expect "END" rest'
285
+ > pure (WHILE e DO c END , rest'' ))
286
+ > <|>
287
+ > (do (i, rest) <- parseIdentifier xs;
288
+ > (e, rest' ) <- firstExpect " :=" (parseAExp steps') rest
289
+ > pure (i ::= e, rest'))
290
+ >
291
+ > parseSequencedCommand : (steps : Nat ) -> Parser Com
292
+ > parseSequencedCommand Z _ = NoneE " Too many recursive calls"
293
+ > parseSequencedCommand (S steps') xs =
294
+ > do (c, rest) <- parseSimpleCommand steps' xs
295
+ > ((do (c', rest') <- firstExpect " ;;" (parseSequencedCommand steps') rest
296
+ > pure (c ;; c', rest'))
297
+ > <|>
298
+ > (pure (c, rest)))
299
+ >
300
+ > bignumber : Nat
301
+ > bignumber = 1000
302
+ >
303
+ > parse : (str : String) -> OptionE (Com, List Token)
304
+ > parse str = parseSequencedCommand bignumber (tokenize str)
305
+ >
333
306
334
307
== Examples
335
308
0 commit comments