History

Initial structure

TT types and values, some text manipulation, basics of Core

Edwin Brady committed 7 years ago

Commit 082bc5cNo parent (first commit)18 files · +3139 −0Browse files

Unified · Split

18 files
  1. src/Control/Delayed.idr +26 −0
  2. src/Core/Core.idr +433 −0
  3. src/Core/Env.idr +25 −0
  4. src/Core/FC.idr +37 −0
  5. src/Core/Name.idr +23 −0
  6. src/Core/TT.idr +381 −0
  7. src/Core/Value.idr +49 −0
  8. src/Data/Bool/Extra.idr +32 −0
  9. src/Data/StringMap.idr +303 −0
  10. src/Parser/Lexer.idr +176 −0
  11. src/Parser/Support.idr +521 −0
  12. src/Text/Lexer.idr +324 −0
  13. src/Text/Lexer/Core.idr +192 −0
  14. src/Text/Parser.idr +226 −0
  15. src/Text/Parser/Core.idr +284 −0
  16. src/Text/Quantity.idr +38 −0
  17. src/Text/Token.idr +38 −0
  18. yaffle.ipkg +31 −0
src/Control/Delayed.idr+26−0
@@ -0,0 +1,26 @@
1||| Utilities functions for contitionally delaying values.
2module Control.Delayed
3
4%default total
5
6||| Type-level function for a conditionally delayed type (`Lazy` or `Inf`).
7public export
8delayed : DelayReason -> Bool -> Type -> Type
9delayed r False a = a
10delayed r True a = Delayed r a
11
12||| Type-level function for a conditionally infinite type.
13public export
14inf : Bool -> Type -> Type
15inf = delayed Infinite
16
17||| Type-level function for a conditionally lazy type.
18public export
19lazy : Bool -> Type -> Type
20lazy = delayed LazyValue
21
22||| Conditionally delay a value.
23export
24delay : (d : Bool) -> a -> delayed r d a
25delay False x = x
26delay True x = Delay x
src/Core/Core.idr+433−0
@@ -0,0 +1,433 @@
1module Core.Core
2
3import Core.Env
4import Core.TT
5import Parser.Support
6
7import public Control.Catchable
8import public Data.IORef
9
10%default covering
11
12public export
13data TTCErrorMsg
14 = FormatOlder
15 | FormatNewer
16 | EndOfBuffer String
17 | Corrupt String
18
19public export
20data CaseError = DifferingArgNumbers
21 | DifferingTypes
22 | MatchErased (vars ** (Env Term vars, Term vars))
23 | UnknownType
24
25-- All possible errors, carrying a location
26public export
27data Error
28 = Fatal Error -- flag as unrecoverable (so don't postpone awaiting further info)
29 | CantConvert FC (Env Term vars) (Term vars) (Term vars)
30 | CantSolveEq FC (Env Term vars) (Term vars) (Term vars)
31 | Cycle FC (Env Term vars) (Term vars) (Term vars)
32 | WhenUnifying FC (Env Term vars) (Term vars) (Term vars) Error
33 | ValidCase FC (Env Term vars) (Either (Term vars) Error)
34 | UndefinedName FC Name
35 | InvisibleName FC Name
36 | BadTypeConType FC Name
37 | BadDataConType FC Name Name
38-- | NotCovering FC Name Covering
39-- | NotTotal FC Name PartialReason
40 | LinearUsed FC Nat Name
41 | LinearMisuse FC Name RigCount RigCount
42 | BorrowPartial FC (Env Term vars) (Term vars) (Term vars)
43 | BorrowPartialType FC (Env Term vars) (Term vars)
44 | AmbiguousName FC (List Name)
45 | AmbiguousElab FC (Env Term vars) (List (Term vars))
46 | AmbiguousSearch FC (Env Term vars) (List (Term vars))
47 | AllFailed (List (Maybe Name, Error))
48 | RecordTypeNeeded FC (Env Term vars)
49 | NotRecordField FC String (Maybe Name)
50 | NotRecordType FC Name
51 | IncompatibleFieldUpdate FC (List String)
52 | InvalidImplicit FC (Env Term vars) Name (Term vars)
53 | TryWithImplicits FC (Env Term vars) (List (Name, Term vars))
54 | CantSolveGoal FC (Term [])
55 | DeterminingArg FC Name (Env Term vars) (Term vars)
56 | UnsolvedHoles (List (FC, Name))
57 | CantInferArgType FC (Env Term vars) Name Name (Term vars)
58 | SolvedNamedHole FC (Env Term vars) Name (Term vars)
59 | VisibilityError FC Visibility Name Visibility Name
60 | NonLinearPattern FC Name
61 | BadPattern FC Name
62 | NoDeclaration FC Name
63 | AlreadyDefined FC Name
64 | NotFunctionType FC (Env Term vars) (Term vars)
65 | RewriteNoChange FC (Env Term vars) (Term vars) (Term vars)
66 | NotRewriteRule FC (Env Term vars) (Term vars)
67 | CaseCompile FC Name CaseError
68 | BadDotPattern FC (Env Term vars) String (Term vars) (Term vars)
69 | BadImplicit FC String
70 | BadRunElab FC (Env Term vars) (Term vars)
71 | GenericMsg FC String
72 | TTCError TTCErrorMsg
73 | FileErr String FileError
74 | ParseFail FC ParseError
75 | ModuleNotFound FC (List String)
76 | CyclicImports (List (List String))
77 | ForceNeeded
78 | InternalError String
79
80 | InType FC Name Error
81 | InCon FC Name Error
82 | InLHS FC Name Error
83 | InRHS FC Name Error
84
85export
86Show TTCErrorMsg where
87 show FormatOlder = "TTC data is in an older format"
88 show FormatNewer = "TTC data is in a newer format"
89 show (EndOfBuffer when) = "End of buffer when reading " ++ when
90 show (Corrupt ty) = "Corrupt TTC data for " ++ ty
91
92-- Simplest possible display - higher level languages should unelaborate names
93-- and display annotations appropriately
94export
95Show Error where
96 show (Fatal err) = show err
97 show (CantConvert fc env x y)
98 = show fc ++ ":Type mismatch: " ++ show x ++ " and " ++ show y
99 show (CantSolveEq fc env x y)
100 = show fc ++ ":" ++ show x ++ " and " ++ show y ++ " are not equal"
101 show (Cycle fc env x y)
102 = show fc ++ ":Occurs check failed: " ++ show x ++ " and " ++ show y
103 show (WhenUnifying fc _ x y err)
104 = show fc ++ ":When unifying: " ++ show x ++ " and " ++ show y ++ "\n\t" ++ show err
105 show (ValidCase fc _ prob)
106 = show fc ++ ":" ++
107 case prob of
108 Left tm => assert_total (show tm) ++ " is not a valid impossible pattern because it typechecks"
109 Right err => "Not a valid impossible pattern:\n\t" ++ assert_total (show err)
110 show (UndefinedName fc x) = show fc ++ ":Undefined name " ++ show x
111 show (InvisibleName fc (NS ns x))
112 = show fc ++ ":Name " ++ show x ++ " is inaccessible since " ++
113 showSep "." (reverse ns) ++ " is not explicitly imported"
114 show (InvisibleName fc x) = show fc ++ ":Name " ++ show x ++ " is inaccessible"
115 show (BadTypeConType fc n)
116 = show fc ++ ":Return type of " ++ show n ++ " must be Type"
117 show (BadDataConType fc n fam)
118 = show fc ++ ":Return type of " ++ show n ++ " must be in " ++ show fam
119-- show (NotCovering fc n cov)
120-- = show fc ++ ":" ++ show n ++ " is not covering:\n\t" ++
121-- case cov of
122-- IsCovering => "Oh yes it is (Internal error!)"
123-- MissingCases cs => "Missing cases:\n\t" ++
124-- showSep "\n\t" (map show cs)
125-- NonCoveringCall ns => "Calls non covering function"
126-- ++ (case ns of
127-- [fn] => " " ++ show fn
128-- _ => "s: " ++ showSep ", " (map show ns))
129--
130-- show (NotTotal fc n r)
131-- = show fc ++ ":" ++ show n ++ " is not total"
132 show (LinearUsed fc count n)
133 = show fc ++ ":There are " ++ show count ++ " uses of linear name " ++ show n
134 show (LinearMisuse fc n exp ctx)
135 = show fc ++ ":Trying to use " ++ showRig exp ++ " name " ++ show n ++
136 " in " ++ showRel ctx ++ " context"
137 where
138 showRig : RigCount -> String
139 showRig Rig0 = "irrelevant"
140 showRig Rig1 = "linear"
141 showRig RigW = "unrestricted"
142
143 showRel : RigCount -> String
144 showRel Rig0 = "irrelevant"
145 showRel Rig1 = "relevant"
146 showRel RigW = "non-linear"
147 show (BorrowPartial fc env t arg)
148 = show fc ++ ":" ++ show t ++ " borrows argument " ++ show arg ++
149 " so must be fully applied"
150 show (BorrowPartialType fc env t)
151 = show fc ++ ":" ++ show t ++ " borrows, so must return a concrete type"
152
153 show (AmbiguousName fc ns) = show fc ++ ":Ambiguous name " ++ show ns
154 show (AmbiguousElab fc env ts) = show fc ++ ":Ambiguous elaboration " ++ show ts
155 show (AmbiguousSearch fc env ts) = show fc ++ ":Ambiguous search " ++ show ts
156 show (AllFailed ts) = "No successful elaboration: " ++ assert_total (show ts)
157 show (RecordTypeNeeded fc env)
158 = show fc ++ ":Can't infer type of record to update"
159 show (NotRecordField fc fld Nothing)
160 = show fc ++ ":" ++ fld ++ " is not part of a record type"
161 show (NotRecordField fc fld (Just ty))
162 = show fc ++ ":Record type " ++ show ty ++ " has no field " ++ fld
163 show (NotRecordType fc ty)
164 = show fc ++ ":" ++ show ty ++ " is not a record type"
165 show (IncompatibleFieldUpdate fc flds)
166 = show fc ++ ":Field update " ++ showSep "->" flds ++ " not compatible with other updates"
167 show (InvalidImplicit fc env n tm) = show fc ++ ":" ++ show n ++ " is not a valid implicit argument in " ++ show tm
168 show (TryWithImplicits fc env imps)
169 = show fc ++ ":Need to bind implicits "
170 ++ showSep "," (map (\x => show (fst x) ++ " : " ++ show (snd x)) imps)
171 ++ "\n(The front end should probably have done this for you. Please report!)"
172 show (CantSolveGoal fc g) = show fc ++ ":Can't solve goal " ++ assert_total (show g)
173 show (DeterminingArg fc n env g)
174 = show fc ++ ":Can't solve goal " ++ assert_total (show g) ++
175 " since argument " ++ show n ++ " can't be inferred"
176 show (UnsolvedHoles hs) = "Unsolved holes " ++ show hs
177 show (CantInferArgType fc env n h ty)
178 = show fc ++ ":Can't infer type for " ++ show n ++
179 " (got " ++ show ty ++ " with hole " ++ show h ++ ")"
180 show (SolvedNamedHole fc _ h _) = show fc ++ ":Named hole " ++ show h ++ " is solved by unification"
181 show (VisibilityError fc vx x vy y)
182 = show fc ++ ":" ++ show vx ++ " " ++ show x ++ " cannot refer to "
183 ++ show vy ++ " " ++ show y
184 show (NonLinearPattern fc n) = show fc ++ ":Non linear pattern variable " ++ show n
185 show (BadPattern fc n) = show fc ++ ":Pattern not allowed here: " ++ show n
186 show (NoDeclaration fc x) = show fc ++ ":No type declaration for " ++ show x
187 show (AlreadyDefined fc x) = show fc ++ ":" ++ show x ++ " is already defined"
188 show (NotFunctionType fc env tm) = show fc ++ ":Not a function type: " ++ show tm
189 show (RewriteNoChange fc env rule ty)
190 = show fc ++ ":Rewriting by " ++ show rule ++ " did not change type " ++ show ty
191 show (NotRewriteRule fc env rule)
192 = show fc ++ ":" ++ show rule ++ " is not a rewrite rule type"
193 show (CaseCompile fc n DifferingArgNumbers)
194 = show fc ++ ":Patterns for " ++ show n ++ " have different numbers of arguments"
195 show (CaseCompile fc n DifferingTypes)
196 = show fc ++ ":Patterns for " ++ show n ++ " require matching on different types"
197 show (CaseCompile fc n UnknownType)
198 = show fc ++ ":Can't infer type to match in " ++ show n
199 show (CaseCompile fc n (MatchErased (_ ** (env, tm))))
200 = show fc ++ ":Attempt to match on erased argument " ++ show tm ++
201 " in " ++ show n
202 show (BadDotPattern fc env reason x y)
203 = show fc ++ ":Can't match on " ++ show x ++
204 (if reason /= "" then " (" ++ reason ++ ")" else "") ++
205 " - it elaborates to " ++ show y
206 show (BadImplicit fc str) = show fc ++ ":" ++ str ++ " can't be bound here"
207 show (BadRunElab fc env script) = show fc ++ ":Bad elaborator script " ++ show script
208 show (GenericMsg fc str) = show fc ++ ":" ++ str
209 show (TTCError msg) = "Error in TTC file: " ++ show msg
210 show (FileErr fname err) = "File error (" ++ fname ++ "): " ++ show err
211 show (ParseFail fc err) = "Parse error (" ++ show err ++ ")"
212 show (ModuleNotFound fc ns)
213 = show fc ++ ":" ++ showSep "." (reverse ns) ++ " not found"
214 show (CyclicImports ns)
215 = "Module imports form a cycle: " ++ showSep " -> " (map showMod ns)
216 where
217 showMod : List String -> String
218 showMod ns = showSep "." (reverse ns)
219 show ForceNeeded = "Internal error when resolving implicit laziness"
220 show (InternalError str) = "INTERNAL ERROR: " ++ str
221
222 show (InType fc n err)
223 = show fc ++ ":When elaborating type of " ++ show n ++ ":\n" ++
224 show err
225 show (InCon fc n err)
226 = show fc ++ ":When elaborating type of constructor " ++ show n ++ ":\n" ++
227 show err
228 show (InLHS fc n err)
229 = show fc ++ ":When elaborating left hand side of " ++ show n ++ ":\n" ++
230 show err
231 show (InRHS fc n err)
232 = show fc ++ ":When elaborating right hand side of " ++ show n ++ ":\n" ++
233 show err
234
235export
236getErrorLoc : Error -> Maybe FC
237getErrorLoc (Fatal err) = getErrorLoc err
238getErrorLoc (CantConvert loc env tm y) = Just loc
239getErrorLoc (CantSolveEq loc env tm y) = Just loc
240getErrorLoc (Cycle loc env tm y) = Just loc
241getErrorLoc (WhenUnifying loc env tm y z) = Just loc
242getErrorLoc (ValidCase loc env y) = Just loc
243getErrorLoc (UndefinedName loc y) = Just loc
244getErrorLoc (InvisibleName loc y) = Just loc
245getErrorLoc (BadTypeConType loc y) = Just loc
246getErrorLoc (BadDataConType loc y z) = Just loc
247-- getErrorLoc (NotCovering loc _ _) = Just loc
248-- getErrorLoc (NotTotal loc _ _) = Just loc
249getErrorLoc (LinearUsed loc k y) = Just loc
250getErrorLoc (LinearMisuse loc y z w) = Just loc
251getErrorLoc (BorrowPartial loc _ _ _) = Just loc
252getErrorLoc (BorrowPartialType loc _ _) = Just loc
253getErrorLoc (AmbiguousName loc xs) = Just loc
254getErrorLoc (AmbiguousElab loc _ xs) = Just loc
255getErrorLoc (AmbiguousSearch loc _ xs) = Just loc
256getErrorLoc (AllFailed ((_, x) :: xs)) = getErrorLoc x
257getErrorLoc (AllFailed []) = Nothing
258getErrorLoc (RecordTypeNeeded loc _) = Just loc
259getErrorLoc (NotRecordField loc _ _) = Just loc
260getErrorLoc (NotRecordType loc _) = Just loc
261getErrorLoc (IncompatibleFieldUpdate loc _) = Just loc
262getErrorLoc (InvalidImplicit loc _ y tm) = Just loc
263getErrorLoc (TryWithImplicits loc _ _) = Just loc
264getErrorLoc (CantSolveGoal loc tm) = Just loc
265getErrorLoc (DeterminingArg loc y env tm) = Just loc
266getErrorLoc (UnsolvedHoles ((loc, _) :: xs)) = Just loc
267getErrorLoc (UnsolvedHoles []) = Nothing
268getErrorLoc (CantInferArgType loc _ y z tm) = Just loc
269getErrorLoc (SolvedNamedHole loc _ _ y) = Just loc
270getErrorLoc (VisibilityError loc y z w s) = Just loc
271getErrorLoc (NonLinearPattern loc y) = Just loc
272getErrorLoc (BadPattern loc y) = Just loc
273getErrorLoc (NoDeclaration loc y) = Just loc
274getErrorLoc (AlreadyDefined loc y) = Just loc
275getErrorLoc (NotFunctionType loc _ tm) = Just loc
276getErrorLoc (RewriteNoChange loc _ tm ty) = Just loc
277getErrorLoc (NotRewriteRule loc _ ty) = Just loc
278getErrorLoc (CaseCompile loc y z) = Just loc
279getErrorLoc (BadDotPattern loc _ y tm z) = Just loc
280getErrorLoc (BadImplicit loc y) = Just loc
281getErrorLoc (BadRunElab loc _ tm) = Just loc
282getErrorLoc (GenericMsg loc y) = Just loc
283getErrorLoc (TTCError x) = Nothing
284getErrorLoc (FileErr x y) = Nothing
285getErrorLoc (ParseFail loc x) = Just loc
286getErrorLoc (ModuleNotFound loc xs) = Just loc
287getErrorLoc (CyclicImports xs) = Nothing
288getErrorLoc ForceNeeded = Nothing
289getErrorLoc (InternalError x) = Nothing
290getErrorLoc (InType x y err) = getErrorLoc err
291getErrorLoc (InCon x y err) = getErrorLoc err
292getErrorLoc (InLHS x y err) = getErrorLoc err
293getErrorLoc (InRHS x y err) = getErrorLoc err
294
295public export
296record GlobalOpts where
297 constructor MkGlobalOpts
298 logLevel : Nat
299
300export
301defaultOpts : GlobalOpts
302defaultOpts = MkGlobalOpts 0
303
304-- Core is a wrapper around IO that is specialised for efficiency.
305export
306record Core t where
307 constructor MkCore
308 runCore : IORef GlobalOpts -> IO (Either Error t)
309
310export
311coreRun : GlobalOpts ->
312 Core a ->
313 (Error -> IO b) -> (a -> IO b) -> IO b
314coreRun opts (MkCore act) err ok
315 = do oref <- newIORef opts
316 either err ok !(act oref)
317
318export
319coreFail : Error -> Core a
320coreFail e = MkCore (\oref => pure (Left e))
321
322export
323wrapError : (Error -> Error) -> Core a -> Core a
324wrapError fe (MkCore prog)
325 = MkCore (\oref =>
326 prog oref >>=
327 (\x => case x of
328 Left err => pure (Left (fe err))
329 Right val => pure (Right val)))
330
331-- This would be better if we restrict it to a limited set of IO operations
332export
333%inline
334coreLift : IO a -> Core a
335coreLift op = MkCore (\oref => map Right op)
336
337{- Monad, Applicative, Traversable are specialised by hand for Core.
338In theory, this shouldn't be necessary, but it turns out that Idris 1 doesn't
339specialise interfaces under 'case' expressions, and this has a significant
340impact on both compile time and run time.
341
342Of course it would be a good idea to fix this in Idris, but it's not an urgent
343thing on the road to self hosting, and we can make sure this isn't a problem
344in the next version (i.e., in this project...)! -}
345
346-- Monad (specialised)
347export %inline
348(>>=) : Core a -> (a -> Core b) -> Core b
349(>>=) (MkCore act) f
350 = MkCore (\oref =>
351 act oref >>=
352 (\x => case x of
353 Left err => pure (Left err)
354 Right val => runCore (f val) oref))
355
356-- Applicative (specialised)
357export %inline
358pure : a -> Core a
359pure x = MkCore (\oref => pure (pure x))
360
361export
362(<*>) : Core (a -> b) -> Core a -> Core b
363(<*>) (MkCore f) (MkCore a) = MkCore (\oref => [| f oref <*> a oref |])
364
365export %inline
366when : Bool -> Lazy (Core ()) -> Core ()
367when True f = f
368when False f = pure ()
369
370export
371Catchable Core Error where
372 catch (MkCore prog) h
373 = MkCore (\oref =>
374 do p' <- prog oref
375 case p' of
376 Left e => let MkCore he = h e in he oref
377 Right val => pure (Right val))
378 throw = coreFail
379
380-- Traversable (specialised)
381traverse' : (a -> Core b) -> List a -> List b -> Core (List b)
382traverse' f [] acc = pure (reverse acc)
383traverse' f (x :: xs) acc
384 = traverse' f xs (!(f x) :: acc)
385
386export
387traverse : (a -> Core b) -> List a -> Core (List b)
388traverse f xs = traverse' f xs []
389
390export
391data Ref : label -> Type -> Type where
392 MkRef : IORef a -> Ref x a
393
394export
395newRef : (x : label) -> t -> Core (Ref x t)
396newRef x val
397 = do ref <- coreLift (newIORef val)
398 pure (MkRef ref)
399
400export %inline
401get : (x : label) -> {auto ref : Ref x a} -> Core a
402get x {ref = MkRef io} = coreLift (readIORef io)
403
404export %inline
405put : (x : label) -> {auto ref : Ref x a} -> a -> Core ()
406put x {ref = MkRef io} val = coreLift (writeIORef io val)
407
408export %inline
409getOpts : Core GlobalOpts
410getOpts = MkCore (\oref => pure (Right !(readIORef oref)))
411
412export %inline
413putOpts : GlobalOpts -> Core ()
414putOpts opts = MkCore (\oref => pure (Right !(writeIORef oref opts)))
415
416export
417cond : List (Lazy Bool, Lazy a) -> a -> a
418cond [] def = def
419cond ((x, y) :: xs) def = if x then y else cond xs def
420
421export
422log : Nat -> String -> Core ()
423log lvl msg
424 = do opts <- getOpts
425 if logLevel opts >= lvl
426 then coreLift $ putStrLn $ "LOG " ++ show lvl ++ ": " ++ msg
427 else pure ()
428export
429setLogLevel : Nat -> Core ()
430setLogLevel n
431 = do opts <- getOpts
432 putOpts (record { logLevel = n } opts)
433
src/Core/Env.idr+25−0
@@ -0,0 +1,25 @@
1module Core.Env
2
3import Core.TT
4
5-- Environment containing types and values of local variables
6namespace Env
7 public export
8 data Env : (tm : List Name -> Type) -> List Name -> Type where
9 Nil : Env tm []
10 (::) : Binder (tm vars) -> Env tm vars -> Env tm (x :: vars)
11
12 export
13 extend : (x : Name) -> Binder (tm vars) -> Env tm vars -> Env tm (x :: vars)
14 extend x = (::) {x}
15
16 export
17 length : Env tm xs -> Nat
18 length [] = 0
19 length (x :: xs) = S (length xs)
20
21export
22bindEnv : FC -> Env Term vars -> (tm : Term vars) -> ClosedTerm
23bindEnv loc [] tm = tm
24bindEnv loc (b :: env) tm
25 = bindEnv loc env (Bind loc _ b tm)
src/Core/FC.idr+37−0
@@ -0,0 +1,37 @@
1module Core.FC
2
3public export
4FilePos : Type
5FilePos = (Int, Int)
6
7showPos : FilePos -> String
8showPos (l, c) = show (l + 1) ++ ":" ++ show (c + 1)
9
10public export
11FileName : Type
12FileName = String
13
14public export
15record FC where
16 constructor MkFC
17 file : FileName
18 startPos : FilePos
19 endPos : FilePos
20
21export
22emptyFC : FC
23emptyFC = MkFC "(no file)" (0, 0) (0, 0)
24
25export
26toplevelFC : FC
27toplevelFC = MkFC "(toplevel)" (0, 0) (0, 0)
28
29%name FC fc
30
31export
32Show FC where
33 show loc = file loc ++ ":" ++
34 showPos (startPos loc) ++ "--" ++
35 showPos (endPos loc)
36
37
src/Core/Name.idr+23−0
@@ -0,0 +1,23 @@
1module Core.Name
2
3%default total
4
5public export
6data Name : Type where
7 NS : List String -> Name -> Name -- in a namespace
8 UN : String -> Name -- user defined name
9 MN : String -> Int -> Name -- machine generated name
10 PV : Name -> Name -> Name -- pattern variable name
11 MV : Int -> Name -- metavariable reference
12 Nested : Name -> Name -> Name -- nested function name
13 Resolved : Int -> Name -- resolved, index into context
14
15export Show Name where
16 show n = "[not done yet]"
17
18export
19showSep : String -> List String -> String
20showSep sep [] = ""
21showSep sep [x] = x
22showSep sep (x :: xs) = x ++ sep ++ showSep sep xs
23
src/Core/TT.idr+381−0
@@ -0,0 +1,381 @@
1module Core.TT
2
3import public Core.FC
4import public Core.Name
5
6%default covering
7
8%hide Raw -- from Reflection in the Prelude
9%hide Binder
10%hide NameType
11%hide Case
12
13public export
14data NameType : Type where
15 Bound : NameType
16 Func : NameType
17 DataCon : (tag : Int) -> (arity : Nat) -> NameType
18 TyCon : (tag : Int) -> (arity : Nat) -> NameType
19
20public export
21data Constant
22 = I Int
23 | BI Integer
24 | Str String
25 | Ch Char
26 | Db Double
27 | WorldVal
28
29 | IntType
30 | IntegerType
31 | StringType
32 | CharType
33 | DoubleType
34 | WorldType
35
36export
37constantEq : (x, y : Constant) -> Maybe (x = y)
38constantEq (I x) (I y) = case decEq x y of
39 Yes Refl => Just Refl
40 No contra => Nothing
41constantEq (BI x) (BI y) = case decEq x y of
42 Yes Refl => Just Refl
43 No contra => Nothing
44constantEq (Str x) (Str y) = case decEq x y of
45 Yes Refl => Just Refl
46 No contra => Nothing
47constantEq (Ch x) (Ch y) = case decEq x y of
48 Yes Refl => Just Refl
49 No contra => Nothing
50constantEq (Db x) (Db y) = Nothing -- no DecEq for Doubles!
51constantEq WorldVal WorldVal = Just Refl
52constantEq IntType IntType = Just Refl
53constantEq IntegerType IntegerType = Just Refl
54constantEq StringType StringType = Just Refl
55constantEq CharType CharType = Just Refl
56constantEq DoubleType DoubleType = Just Refl
57constantEq WorldType WorldType = Just Refl
58constantEq _ _ = Nothing
59
60export
61Show Constant where
62 show (I x) = show x
63 show (BI x) = show x
64 show (Str x) = show x
65 show (Ch x) = show x
66 show (Db x) = show x
67 show WorldVal = "%MkWorld"
68 show IntType = "Int"
69 show IntegerType = "Integer"
70 show StringType = "String"
71 show CharType = "Char"
72 show DoubleType = "Double"
73 show WorldType = "%World"
74
75export
76Eq Constant where
77 (I x) == (I y) = x == y
78 (BI x) == (BI y) = x == y
79 (Str x) == (Str y) = x == y
80 (Ch x) == (Ch y) = x == y
81 (Db x) == (Db y) = x == y
82 WorldVal == WorldVal = True
83 IntType == IntType = True
84 IntegerType == IntegerType = True
85 StringType == StringType = True
86 CharType == CharType = True
87 DoubleType == DoubleType = True
88 WorldType == WorldType = True
89 _ == _ = False
90
91public export
92data PiInfo = Implicit | Explicit | AutoImplicit
93
94public export
95data RigCount = Rig0 | Rig1 | RigW
96
97export
98isLinear : RigCount -> Bool
99isLinear Rig1 = True
100isLinear _ = False
101
102export
103Eq RigCount where
104 (==) Rig0 Rig0 = True
105 (==) Rig1 Rig1 = True
106 (==) RigW RigW = True
107 (==) _ _ = False
108
109export
110Ord RigCount where
111 compare Rig0 Rig0 = EQ
112 compare Rig0 Rig1 = LT
113 compare Rig0 RigW = LT
114
115 compare Rig1 Rig0 = GT
116 compare Rig1 Rig1 = EQ
117 compare Rig1 RigW = LT
118
119 compare RigW Rig0 = GT
120 compare RigW Rig1 = GT
121 compare RigW RigW = EQ
122
123export
124Show RigCount where
125 show Rig0 = "Rig0"
126 show Rig1 = "Rig1"
127 show RigW = "RigW"
128
129export
130rigPlus : RigCount -> RigCount -> RigCount
131rigPlus Rig0 Rig0 = Rig0
132rigPlus Rig0 Rig1 = Rig1
133rigPlus Rig0 RigW = RigW
134rigPlus Rig1 Rig0 = Rig1
135rigPlus Rig1 Rig1 = RigW
136rigPlus Rig1 RigW = RigW
137rigPlus RigW Rig0 = RigW
138rigPlus RigW Rig1 = RigW
139rigPlus RigW RigW = RigW
140
141export
142rigMult : RigCount -> RigCount -> RigCount
143rigMult Rig0 Rig0 = Rig0
144rigMult Rig0 Rig1 = Rig0
145rigMult Rig0 RigW = Rig0
146rigMult Rig1 Rig0 = Rig0
147rigMult Rig1 Rig1 = Rig1
148rigMult Rig1 RigW = RigW
149rigMult RigW Rig0 = Rig0
150rigMult RigW Rig1 = RigW
151rigMult RigW RigW = RigW
152
153public export
154data Binder : Type -> Type where
155 -- Lambda bound variables with their implicitness
156 Lam : RigCount -> PiInfo -> (ty : type) -> Binder type
157 -- Let bound variables with their value
158 Let : RigCount -> (val : type) -> (ty : type) -> Binder type
159 -- Forall/pi bound variables with their implicitness
160 Pi : RigCount -> PiInfo -> (ty : type) -> Binder type
161 -- pattern bound variables
162 PVar : RigCount -> (ty : type) -> Binder type
163 -- the type of pattern bound variables
164 PVTy : RigCount -> (ty : type) -> Binder type
165
166export
167binderType : Binder tm -> tm
168binderType (Lam _ x ty) = ty
169binderType (Let _ val ty) = ty
170binderType (Pi _ x ty) = ty
171binderType (PVar _ ty) = ty
172binderType (PVTy _ ty) = ty
173
174export
175multiplicity : Binder tm -> RigCount
176multiplicity (Lam c x ty) = c
177multiplicity (Let c val ty) = c
178multiplicity (Pi c x ty) = c
179multiplicity (PVar c ty) = c
180multiplicity (PVTy c ty) = c
181
182export
183setMultiplicity : Binder tm -> RigCount -> Binder tm
184setMultiplicity (Lam c x ty) c' = Lam c' x ty
185setMultiplicity (Let c val ty) c' = Let c' val ty
186setMultiplicity (Pi c x ty) c' = Pi c' x ty
187setMultiplicity (PVar c ty) c' = PVar c' ty
188setMultiplicity (PVTy c ty) c' = PVTy c' ty
189
190showCount : RigCount -> String
191showCount Rig0 = "0 "
192showCount Rig1 = "1 "
193showCount RigW = ""
194
195Show ty => Show (Binder ty) where
196 show (Lam c _ t) = "\\" ++ showCount c ++ show t
197 show (Pi c _ t) = "Pi " ++ showCount c ++ show t
198 show (Let c v t) = "let " ++ showCount c ++ show v ++ " : " ++ show t
199 show (PVar c t) = "pat " ++ showCount c ++ show t
200 show (PVTy c t) = "pty " ++ showCount c ++ show t
201
202export
203setType : Binder tm -> tm -> Binder tm
204setType (Lam c x _) ty = Lam c x ty
205setType (Let c val _) ty = Let c val ty
206setType (Pi c x _) ty = Pi c x ty
207setType (PVar c _) ty = PVar c ty
208setType (PVTy c _) ty = PVTy c ty
209
210export
211Functor Binder where
212 map func (Lam c x ty) = Lam c x (func ty)
213 map func (Let c val ty) = Let c (func val) (func ty)
214 map func (Pi c x ty) = Pi c x (func ty)
215 map func (PVar c ty) = PVar c (func ty)
216 map func (PVTy c ty) = PVTy c (func ty)
217
218public export
219data IsVar : Name -> Nat -> List Name -> Type where
220 First : IsVar n Z (n :: ns)
221 Later : IsVar n i ns -> IsVar n (S i) (m :: ns)
222
223public export
224data Var : List Name -> Type where
225 MkVar : {i : Nat} -> IsVar n i vars -> Var vars
226
227export
228sameVar : {i, j : _} -> IsVar n i xs -> IsVar m j xs -> Bool
229sameVar {i} {j} _ _ = i == j
230
231public export
232record AppInfo where
233 constructor MkAppInfo
234 plicit : PiInfo
235
236export
237appInf : PiInfo -> AppInfo
238appInf p = MkAppInfo p
239
240export
241implApp : AppInfo
242implApp = appInf Implicit
243
244export
245explApp : AppInfo
246explApp = appInf Explicit
247
248namespace CList
249 -- A list correspoding to another list
250 public export
251 data CList : List a -> Type -> Type where
252 Nil : CList [] ty
253 (::) : (x : ty) -> CList cs ty -> CList (c :: cs) ty
254
255-- Typechecked terms
256-- These are guaranteed to be well-scoped wrt local variables, because they are
257-- indexed by the names of local variables in scope
258mutual
259 public export
260 data Term : List Name -> Type where
261 Local : FC -> Maybe RigCount ->
262 (idx : Nat) -> IsVar name idx vars -> Term vars
263 Ref : FC -> NameType -> (name : Name) -> Term vars
264 -- Metavariable solution binding
265 ULet : Int -> Term vars -> Term vars -> Term vars
266 Bind : FC -> (x : Name) ->
267 (b : Binder (Term vars)) ->
268 (scope : Term (x :: vars)) -> Term vars
269 App : FC -> (fn : Term vars) -> (p : AppInfo) -> (arg : Term vars) -> Term vars
270 Case : FC -> (cs : List (Var vars)) ->
271 (ty : Term vars) ->
272 Maybe (CaseTree vars) ->
273 (alts : List (PatAlt cs vars)) ->
274 Term vars
275 PrimVal : FC -> (c : Constant) -> Term vars
276 Erased : FC -> Term vars
277 TType : FC -> Term vars
278
279 public export
280 data Pat : List Name -> Type where
281 PAs : FC -> (idx : Nat) -> IsVar name idx vars -> Pat vars -> Pat vars
282 PCon : FC -> Name -> (tag : Int) -> (arity : Nat) ->
283 List (Pat vars) -> Pat vars
284 PLoc : FC -> (idx : Nat) -> IsVar name idx vars -> Pat vars
285 PUnmatchable : FC -> Term vars -> Pat vars
286
287 public export
288 data PatAlt : List (Var vs) -> List Name -> Type where
289 CBind : RigCount -> (x : Name) -> (ty : Term vars) ->
290 PatAlt cs (x :: vars) -> PatAlt cs vars
291 CPats : CList cs (Pat vars) -> Term vars -> PatAlt cs vars
292
293 public export
294 data CaseTree : List Name -> Type where
295 Switch : (idx : Nat) -> IsVar name idx vars ->
296 (scTy : Term vars) -> List (CaseAlt vars) ->
297 CaseTree vars
298 STerm : Term vars -> CaseTree vars
299 Unmatched : (msg : String) -> CaseTree vars
300 Impossible : CaseTree vars
301
302 public export
303 data CaseAlt : List Name -> Type where
304 ConCase : Name -> (tag : Int) -> (args : List Name) ->
305 CaseTree (args ++ vars) -> CaseAlt vars
306 ConstCase : Constant -> CaseTree vars -> CaseAlt vars
307 DefaultCase : CaseTree vars -> CaseAlt vars
308
309export Show (Term vars) where
310 show tm = "[not done yet]"
311
312public export
313ClosedTerm : Type
314ClosedTerm = Term []
315
316export
317apply : FC -> AppInfo -> Term vars -> List (Term vars) -> Term vars
318apply loc p fn [] = fn
319apply loc p fn (a :: args) = apply loc p (App loc fn p a) args
320
321public export
322data Visibility = Private | Export | Public
323
324export
325Show Visibility where
326 show Private = "private"
327 show Export = "export"
328 show Public = "public export"
329
330export
331Eq Visibility where
332 Private == Private = True
333 Export == Export = True
334 Public == Public = True
335 _ == _ = False
336
337export
338Ord Visibility where
339 compare Private Export = LT
340 compare Private Public = LT
341 compare Export Public = LT
342
343 compare Private Private = EQ
344 compare Export Export = EQ
345 compare Public Public = EQ
346
347 compare Export Private = GT
348 compare Public Private = GT
349 compare Public Export = GT
350
351--- Some test stuff
352loc : (n : Name) -> {auto prf : IsVar n idx vars} -> Term vars
353loc n {prf} = Local emptyFC Nothing _ prf
354
355cvar : (n : Name) -> {auto prf : IsVar n idx vars} -> Var vars
356cvar n {prf} = MkVar prf
357
358ploc : (n : Name) -> {auto prf : IsVar n idx vars} -> Pat vars
359ploc n {prf} = PLoc emptyFC _ prf
360
361lam : (n : Name) -> Term vars -> Term (n :: vars) -> Term vars
362lam n ty sc = Bind emptyFC n (Lam RigW Explicit ty) sc
363
364NatTy : Term vs
365NatTy = Ref emptyFC (TyCon 0 2) (UN "Nat")
366
367testPlus : Term []
368testPlus
369 = lam (UN "x") NatTy $
370 lam (UN "y") NatTy $
371 Case emptyFC [cvar (UN "x")] NatTy Nothing
372 [CPats [PCon emptyFC (UN "Z") 0 0 []] (loc (UN "y")),
373 CBind RigW (UN "k") NatTy
374 (CPats [PCon emptyFC (UN "S") 1 1 [ploc (UN "k")]]
375 (apply emptyFC explApp (Ref emptyFC (DataCon 1 1) (UN "S"))
376 [apply emptyFC explApp (Ref emptyFC Func (UN "plus"))
377 [loc (UN "k"), loc (UN "y")]]))]
378
379
380
381
src/Core/Value.idr+49−0
@@ -0,0 +1,49 @@
1module Core.Value
2
3import Core.TT
4import Core.Env
5
6public export
7record EvalOpts where
8 constructor MkEvalOpts
9 holesOnly : Bool -- only evaluate hole solutions
10 evalAll : Bool -- evaluate everything, including private names
11 tcInline : Bool -- inline for totality checking
12 fuel : Maybe Nat -- Limit for recursion depth
13
14mutual
15 public export
16 data LocalEnv : List Name -> List Name -> Type where
17 Nil : LocalEnv free []
18 (::) : Closure free -> LocalEnv free vars -> LocalEnv free (x :: vars)
19
20 public export
21 data Closure : List Name -> Type where
22 MkClosure : (opts : EvalOpts) ->
23 LocalEnv free vars ->
24 Env Term free ->
25 Term (vars ++ free) -> Closure free
26 MkNFClosure : NF free -> Closure free
27
28 -- The head of a value: things you can apply arguments to
29 public export
30 data NHead : List Name -> Type where
31 NLocal : Maybe RigCount -> (idx : Nat) -> IsVar name idx vars ->
32 NHead vars
33 NRef : NameType -> Name -> NHead vars
34 NBlocked : Closure vars -> NHead vars -- complex blocked term, e.g. Case
35
36 -- Values themselves
37 public export
38 data NF : List Name -> Type where
39 NBind : FC -> (x : Name) -> Binder (NF vars) ->
40 (Closure vars -> NF vars) -> NF vars
41 NApp : FC -> NHead vars -> List (Closure vars) -> NF vars
42 NDCon : FC -> Name -> (tag : Int) -> (arity : Nat) ->
43 List (Closure vars) -> NF vars
44 NTCon : FC -> Name -> (tag : Int) -> (arity : Nat) ->
45 List (Closure vars) -> NF vars
46 NPrimVal : FC -> Constant -> NF vars
47 NErased : FC -> NF vars
48 NType : FC -> NF vars
49
src/Data/Bool/Extra.idr+32−0
@@ -0,0 +1,32 @@
1module Data.Bool.Extra
2
3%access public export
4%default total
5
6andSameNeutral : (x : Bool) -> x && x = x
7andSameNeutral False = Refl
8andSameNeutral True = Refl
9
10andFalseFalse : (x : Bool) -> x && False = False
11andFalseFalse False = Refl
12andFalseFalse True = Refl
13
14andTrueNeutral : (x : Bool) -> x && True = x
15andTrueNeutral False = Refl
16andTrueNeutral True = Refl
17
18orSameNeutral : (x : Bool) -> x || x = x
19orSameNeutral False = Refl
20orSameNeutral True = Refl
21
22orFalseNeutral : (x : Bool) -> x || False = x
23orFalseNeutral False = Refl
24orFalseNeutral True = Refl
25
26orTrueTrue : (x : Bool) -> x || True = True
27orTrueTrue False = Refl
28orTrueTrue True = Refl
29
30orSameAndRightNeutral : (x, right : Bool) -> x || (x && right) = x
31orSameAndRightNeutral False _ = Refl
32orSameAndRightNeutral True _ = Refl
src/Data/StringMap.idr+303−0
@@ -0,0 +1,303 @@
1module Data.StringMap
2
3-- Hand specialised map, for efficiency...
4
5%default covering
6
7Key : Type
8Key = String
9
10-- TODO: write split
11
12private
13data Tree : Nat -> Type -> Type where
14 Leaf : Key -> v -> Tree Z v
15 Branch2 : Tree n v -> Key -> Tree n v -> Tree (S n) v
16 Branch3 : Tree n v -> Key -> Tree n v -> Key -> Tree n v -> Tree (S n) v
17
18branch4 :
19 Tree n v -> Key ->
20 Tree n v -> Key ->
21 Tree n v -> Key ->
22 Tree n v ->
23 Tree (S (S n)) v
24branch4 a b c d e f g =
25 Branch2 (Branch2 a b c) d (Branch2 e f g)
26
27branch5 :
28 Tree n v -> Key ->
29 Tree n v -> Key ->
30 Tree n v -> Key ->
31 Tree n v -> Key ->
32 Tree n v ->
33 Tree (S (S n)) v
34branch5 a b c d e f g h i =
35 Branch2 (Branch2 a b c) d (Branch3 e f g h i)
36
37branch6 :
38 Tree n v -> Key ->
39 Tree n v -> Key ->
40 Tree n v -> Key ->
41 Tree n v -> Key ->
42 Tree n v -> Key ->
43 Tree n v ->
44 Tree (S (S n)) v
45branch6 a b c d e f g h i j k =
46 Branch3 (Branch2 a b c) d (Branch2 e f g) h (Branch2 i j k)
47
48branch7 :
49 Tree n v -> Key ->
50 Tree n v -> Key ->
51 Tree n v -> Key ->
52 Tree n v -> Key ->
53 Tree n v -> Key ->
54 Tree n v -> Key ->
55 Tree n v ->
56 Tree (S (S n)) v
57branch7 a b c d e f g h i j k l m =
58 Branch3 (Branch3 a b c d e) f (Branch2 g h i) j (Branch2 k l m)
59
60merge1 : Tree n v -> Key -> Tree (S n) v -> Key -> Tree (S n) v -> Tree (S (S n)) v
61merge1 a b (Branch2 c d e) f (Branch2 g h i) = branch5 a b c d e f g h i
62merge1 a b (Branch2 c d e) f (Branch3 g h i j k) = branch6 a b c d e f g h i j k
63merge1 a b (Branch3 c d e f g) h (Branch2 i j k) = branch6 a b c d e f g h i j k
64merge1 a b (Branch3 c d e f g) h (Branch3 i j k l m) = branch7 a b c d e f g h i j k l m
65
66merge2 : Tree (S n) v -> Key -> Tree n v -> Key -> Tree (S n) v -> Tree (S (S n)) v
67merge2 (Branch2 a b c) d e f (Branch2 g h i) = branch5 a b c d e f g h i
68merge2 (Branch2 a b c) d e f (Branch3 g h i j k) = branch6 a b c d e f g h i j k
69merge2 (Branch3 a b c d e) f g h (Branch2 i j k) = branch6 a b c d e f g h i j k
70merge2 (Branch3 a b c d e) f g h (Branch3 i j k l m) = branch7 a b c d e f g h i j k l m
71
72merge3 : Tree (S n) v -> Key -> Tree (S n) v -> Key -> Tree n v -> Tree (S (S n)) v
73merge3 (Branch2 a b c) d (Branch2 e f g) h i = branch5 a b c d e f g h i
74merge3 (Branch2 a b c) d (Branch3 e f g h i) j k = branch6 a b c d e f g h i j k
75merge3 (Branch3 a b c d e) f (Branch2 g h i) j k = branch6 a b c d e f g h i j k
76merge3 (Branch3 a b c d e) f (Branch3 g h i j k) l m = branch7 a b c d e f g h i j k l m
77
78treeLookup : Key -> Tree n v -> Maybe v
79treeLookup k (Leaf k' v) =
80 if k == k' then
81 Just v
82 else
83 Nothing
84treeLookup k (Branch2 t1 k' t2) =
85 if k <= k' then
86 treeLookup k t1
87 else
88 treeLookup k t2
89treeLookup k (Branch3 t1 k1 t2 k2 t3) =
90 if k <= k1 then
91 treeLookup k t1
92 else if k <= k2 then
93 treeLookup k t2
94 else
95 treeLookup k t3
96
97treeInsert' : Key -> v -> Tree n v -> Either (Tree n v) (Tree n v, Key, Tree n v)
98treeInsert' k v (Leaf k' v') =
99 case compare k k' of
100 LT => Right (Leaf k v, k, Leaf k' v')
101 EQ => Left (Leaf k v)
102 GT => Right (Leaf k' v', k', Leaf k v)
103treeInsert' k v (Branch2 t1 k' t2) =
104 if k <= k' then
105 case treeInsert' k v t1 of
106 Left t1' => Left (Branch2 t1' k' t2)
107 Right (a, b, c) => Left (Branch3 a b c k' t2)
108 else
109 case treeInsert' k v t2 of
110 Left t2' => Left (Branch2 t1 k' t2')
111 Right (a, b, c) => Left (Branch3 t1 k' a b c)
112treeInsert' k v (Branch3 t1 k1 t2 k2 t3) =
113 if k <= k1 then
114 case treeInsert' k v t1 of
115 Left t1' => Left (Branch3 t1' k1 t2 k2 t3)
116 Right (a, b, c) => Right (Branch2 a b c, k1, Branch2 t2 k2 t3)
117 else
118 if k <= k2 then
119 case treeInsert' k v t2 of
120 Left t2' => Left (Branch3 t1 k1 t2' k2 t3)
121 Right (a, b, c) => Right (Branch2 t1 k1 a, b, Branch2 c k2 t3)
122 else
123 case treeInsert' k v t3 of
124 Left t3' => Left (Branch3 t1 k1 t2 k2 t3')
125 Right (a, b, c) => Right (Branch2 t1 k1 t2, k2, Branch2 a b c)
126
127treeInsert : Key -> v -> Tree n v -> Either (Tree n v) (Tree (S n) v)
128treeInsert k v t =
129 case treeInsert' k v t of
130 Left t' => Left t'
131 Right (a, b, c) => Right (Branch2 a b c)
132
133delType : Nat -> Type -> Type
134delType Z v = ()
135delType (S n) v = Tree n v
136
137treeDelete : Key -> Tree n v -> Either (Tree n v) (delType n v)
138treeDelete k (Leaf k' v) =
139 if k == k' then
140 Right ()
141 else
142 Left (Leaf k' v)
143treeDelete {n=S Z} k (Branch2 t1 k' t2) =
144 if k <= k' then
145 case treeDelete k t1 of
146 Left t1' => Left (Branch2 t1' k' t2)
147 Right () => Right t2
148 else
149 case treeDelete k t2 of
150 Left t2' => Left (Branch2 t1 k' t2')
151 Right () => Right t1
152treeDelete {n=S Z} k (Branch3 t1 k1 t2 k2 t3) =
153 if k <= k1 then
154 case treeDelete k t1 of
155 Left t1' => Left (Branch3 t1' k1 t2 k2 t3)
156 Right () => Left (Branch2 t2 k2 t3)
157 else if k <= k2 then
158 case treeDelete k t2 of
159 Left t2' => Left (Branch3 t1 k1 t2' k2 t3)
160 Right () => Left (Branch2 t1 k1 t3)
161 else
162 case treeDelete k t3 of
163 Left t3' => Left (Branch3 t1 k1 t2 k2 t3')
164 Right () => Left (Branch2 t1 k1 t2)
165treeDelete {n=S (S _)} k (Branch2 t1 k' t2) =
166 if k <= k' then
167 case treeDelete k t1 of
168 Left t1' => Left (Branch2 t1' k' t2)
169 Right t1' =>
170 case t2 of
171 Branch2 a b c => Right (Branch3 t1' k' a b c)
172 Branch3 a b c d e => Left (branch4 t1' k' a b c d e)
173 else
174 case treeDelete k t2 of
175 Left t2' => Left (Branch2 t1 k' t2')
176 Right t2' =>
177 case t1 of
178 Branch2 a b c => Right (Branch3 a b c k' t2')
179 Branch3 a b c d e => Left (branch4 a b c d e k' t2')
180treeDelete {n=(S (S _))} k (Branch3 t1 k1 t2 k2 t3) =
181 if k <= k1 then
182 case treeDelete k t1 of
183 Left t1' => Left (Branch3 t1' k1 t2 k2 t3)
184 Right t1' => Left (merge1 t1' k1 t2 k2 t3)
185 else if k <= k2 then
186 case treeDelete k t2 of
187 Left t2' => Left (Branch3 t1 k1 t2' k2 t3)
188 Right t2' => Left (merge2 t1 k1 t2' k2 t3)
189 else
190 case treeDelete k t3 of
191 Left t3' => Left (Branch3 t1 k1 t2 k2 t3')
192 Right t3' => Left (merge3 t1 k1 t2 k2 t3')
193
194treeToList : Tree n v -> List (Key, v)
195treeToList = treeToList' (:: [])
196 where
197 treeToList' : ((Key, v) -> List (Key, v)) -> Tree n v -> List (Key, v)
198 treeToList' cont (Leaf k v) = cont (k, v)
199 treeToList' cont (Branch2 t1 _ t2) = treeToList' (:: treeToList' cont t2) t1
200 treeToList' cont (Branch3 t1 _ t2 _ t3) = treeToList' (:: treeToList' (:: treeToList' cont t3) t2) t1
201
202export
203data StringMap : Type -> Type where
204 Empty : StringMap v
205 M : (n : Nat) -> Tree n v -> StringMap v
206
207export
208empty : StringMap v
209empty = Empty
210
211export
212lookup : String -> StringMap v -> Maybe v
213lookup _ Empty = Nothing
214lookup k (M _ t) = treeLookup k t
215
216export
217insert : String -> v -> StringMap v -> StringMap v
218insert k v Empty = M Z (Leaf k v)
219insert k v (M _ t) =
220 case treeInsert k v t of
221 Left t' => (M _ t')
222 Right t' => (M _ t')
223
224export
225insertFrom : Foldable f => f (String, v) -> StringMap v -> StringMap v
226insertFrom = flip $ foldl $ flip $ uncurry insert
227
228export
229delete : String -> StringMap v -> StringMap v
230delete _ Empty = Empty
231delete k (M Z t) =
232 case treeDelete k t of
233 Left t' => (M _ t')
234 Right () => Empty
235delete k (M (S _) t) =
236 case treeDelete k t of
237 Left t' => (M _ t')
238 Right t' => (M _ t')
239
240export
241fromList : List (String, v) -> StringMap v
242fromList l = foldl (flip (uncurry insert)) empty l
243
244export
245toList : StringMap v -> List (String, v)
246toList Empty = []
247toList (M _ t) = treeToList t
248
249||| Gets the Keyeys of the map.
250export
251keys : StringMap v -> List String
252keys = map fst . toList
253
254||| Gets the values of the map. Could contain duplicates.
255export
256values : StringMap v -> List v
257values = map snd . toList
258
259treeMap : (a -> b) -> Tree n a -> Tree n b
260treeMap f (Leaf k v) = Leaf k (f v)
261treeMap f (Branch2 t1 k t2) = Branch2 (treeMap f t1) k (treeMap f t2)
262treeMap f (Branch3 t1 k1 t2 k2 t3)
263 = Branch3 (treeMap f t1) k1 (treeMap f t2) k2 (treeMap f t3)
264
265export
266implementation Functor (StringMap) where
267 map _ Empty = Empty
268 map f (M n t) = M _ (treeMap f t)
269
270||| Merge two maps. When encountering duplicate keys, using a function to combine the values.
271||| Uses the ordering of the first map given.
272export
273mergeWith : (v -> v -> v) -> StringMap v -> StringMap v -> StringMap v
274mergeWith f x y = insertFrom inserted x where
275 inserted : List (Key, v)
276 inserted = do
277 (k, v) <- toList y
278 let v' = (maybe id f $ lookup k x) v
279 pure (k, v')
280
281||| Merge two maps using the Semigroup (and by extension, Monoid) operation.
282||| Uses mergeWith internally, so the ordering of the left map is kept.
283export
284merge : Semigroup v => StringMap v -> StringMap v -> StringMap v
285merge = mergeWith (<+>)
286
287||| Left-biased merge, also keeps the ordering specified by the left map.
288export
289mergeLeft : StringMap v -> StringMap v -> StringMap v
290mergeLeft = mergeWith const
291
292-- TODO: is this the right variant of merge to use for this? I think it is, but
293-- I could also see the advantages of using `mergeLeft`. The current approach is
294-- strictly more powerful I believe, because `mergeLeft` can be emulated with
295-- the `First` monoid. However, this does require more code to do the same
296-- thing.
297export
298Semigroup v => Semigroup (StringMap v) where
299 (<+>) = merge
300
301export
302(Semigroup v) => Monoid (StringMap v) where
303 neutral = empty
src/Parser/Lexer.idr+176−0
@@ -0,0 +1,176 @@
1module Parser.Lexer
2
3import public Text.Lexer
4
5%default total
6
7public export
8data Token = Ident String
9 | HoleIdent String
10 | Literal Integer
11 | StrLit String
12 | CharLit String
13 | DoubleLit Double
14 | Symbol String
15 | Keyword String
16 | Unrecognised String
17 | Comment String
18 | DocComment String
19 | CGDirective String
20 | EndInput
21
22export
23Show Token where
24 show (Ident x) = "identifier " ++ x
25 show (HoleIdent x) = "hole identifier " ++ x
26 show (Literal x) = "literal " ++ show x
27 show (StrLit x) = "string " ++ show x
28 show (CharLit x) = "character " ++ show x
29 show (DoubleLit x) = "double " ++ show x
30 show (Symbol x) = "symbol " ++ x
31 show (Keyword x) = x
32 show (Unrecognised x) = "Unrecognised " ++ x
33 show (Comment x) = "comment"
34 show (DocComment x) = "doc comment"
35 show (CGDirective x) = "CGDirective " ++ x
36 show EndInput = "end of input"
37
38export
39Show (TokenData Token) where
40 show t = show (line t, col t, tok t)
41
42comment : Lexer
43comment = is '-' <+> is '-' <+> many (isNot '\n')
44
45toEndComment : (k : Nat) -> Recognise (k /= 0)
46toEndComment Z = empty
47toEndComment (S k)
48 = some (pred (\c => c /= '-' && c /= '{'))
49 <+> toEndComment (S k)
50 <|> is '{' <+> is '-' <+> toEndComment (S (S k))
51 <|> is '-' <+> is '}' <+> toEndComment k
52 <|> is '{' <+> toEndComment (S k)
53 <|> is '-' <+> toEndComment (S k)
54
55blockComment : Lexer
56blockComment = is '{' <+> is '-' <+> toEndComment 1
57
58docComment : Lexer
59docComment = is '|' <+> is '|' <+> is '|' <+> many (isNot '\n')
60
61ident : Lexer
62ident = pred startIdent <+> many (pred validIdent)
63 where
64 startIdent : Char -> Bool
65 startIdent '_' = True
66 startIdent x = isAlpha x
67
68 validIdent : Char -> Bool
69 validIdent '_' = True
70 validIdent '\'' = True
71 validIdent x = isAlphaNum x
72
73holeIdent : Lexer
74holeIdent = is '?' <+> ident
75
76doubleLit : Lexer
77doubleLit
78 = digits <+> is '.' <+> digits <+> opt
79 (is 'e' <+> opt (is '-' <|> is '+') <+> digits)
80
81-- Do this as an entire token, because the contents will be processed by
82-- a specific back end
83cgDirective : Lexer
84cgDirective
85 = exact "%cg" <+>
86 ((some space <+>
87 some (pred isAlphaNum) <+> many space <+>
88 is '{' <+> many (isNot '}') <+>
89 is '}')
90 <|> many (isNot '\n'))
91
92mkDirective : String -> Token
93mkDirective str = CGDirective (trim (substr 3 (length str) str))
94
95-- Reserved words
96keywords : List String
97keywords = ["data", "module", "where", "let", "in", "do", "record",
98 "auto", "implicit", "mutual", "namespace", "parameters",
99 "with", "impossible", "case", "of",
100 "if", "then", "else", "forall", "rewrite",
101 "using", "interface", "implementation", "open", "import",
102 "public", "export", "private",
103 "infixl", "infixr", "infix", "prefix",
104 "Type", "Int", "Integer", "String", "Char", "Double",
105 "total", "partial", "covering"]
106
107-- Reserved words for internal syntax
108special : List String
109special = ["%lam", "%pi", "%imppi", "%let"]
110
111-- Special symbols - things which can't be a prefix of another symbol, and
112-- don't match 'validSymbol'
113export
114symbols : List String
115symbols
116 = [".(", -- for things such as Foo.Bar.(+)
117 "@{",
118 "(", ")", "{", "}", "[", "]", ",", ";", "_",
119 "`(", "`", "~"]
120
121export
122opChars : String
123opChars = ":!#$%&*+./<=>?@\\^|-~"
124
125validSymbol : Lexer
126validSymbol = some (oneOf opChars)
127
128-- Valid symbols which have a special meaning so can't be operators
129export
130reservedSymbols : List String
131reservedSymbols
132 = symbols ++
133 ["%", "\\", ":", "=", "|", "|||", "<-", "->", "=>", "?", "&", "**"]
134
135symbolChar : Char -> Bool
136symbolChar c = c `elem` unpack opChars
137
138rawTokens : TokenMap Token
139rawTokens =
140 [(comment, Comment),
141 (blockComment, Comment),
142 (docComment, DocComment),
143 (cgDirective, mkDirective),
144 (holeIdent, \x => HoleIdent (assert_total (strTail x)))] ++
145 map (\x => (exact x, Symbol)) symbols ++
146 [(doubleLit, \x => DoubleLit (cast x)),
147 (digits, \x => Literal (cast x)),
148 (stringLit, \x => StrLit (stripQuotes x)),
149 (charLit, \x => CharLit (stripQuotes x)),
150 (ident, \x => if x `elem` keywords then Keyword x else Ident x),
151 (space, Comment),
152 (validSymbol, Symbol),
153 (symbol, Unrecognised)]
154 where
155 stripQuotes : String -> String
156 -- ASSUMPTION! Only total because we know we're getting quoted strings.
157 stripQuotes = assert_total (strTail . reverse . strTail . reverse)
158
159export
160lex : String -> Either (Int, Int, String) (List (TokenData Token))
161lex str
162 = case lex rawTokens str of
163 -- Add the EndInput token so that we'll have a line and column
164 -- number to read when storing spans in the file
165 (tok, (l, c, "")) => Right (filter notComment tok ++
166 [MkToken l c EndInput])
167 (_, fail) => Left fail
168 where
169 notComment : TokenData Token -> Bool
170 notComment t = case tok t of
171 Comment _ => False
172 DocComment _ => False -- TODO!
173 _ => True
174
175testLex : String -> String
176testLex inp = show (Lexer.lex inp)
src/Parser/Support.idr+521−0
@@ -0,0 +1,521 @@
1module Parser.Support
2
3import public Text.Lexer
4import public Parser.Lexer
5import public Text.Parser
6
7import Core.TT
8import Data.List.Views
9
10%default total
11
12public export
13Rule : Type -> Type
14Rule ty = Grammar (TokenData Token) True ty
15
16public export
17EmptyRule : Type -> Type
18EmptyRule ty = Grammar (TokenData Token) False ty
19
20public export
21data ParseError = ParseFail String (Maybe (Int, Int)) (List Token)
22 | LexFail (Int, Int, String)
23 | FileFail FileError
24
25export
26Show ParseError where
27 show (ParseFail err loc toks)
28 = "Parse error: " ++ err ++ " (next tokens: "
29 ++ show (take 10 toks) ++ ")"
30 show (LexFail (c, l, str))
31 = "Lex error at " ++ show (c, l) ++ " input: " ++ str
32 show (FileFail err)
33 = "File error: " ++ show err
34
35export
36eoi : EmptyRule ()
37eoi
38 = do nextIs "Expected end of input" (isEOI . tok)
39 pure ()
40 where
41 isEOI : Token -> Bool
42 isEOI EndInput = True
43 isEOI _ = False
44
45export
46runParser : String -> Grammar (TokenData Token) e ty -> Either ParseError ty
47runParser str p
48 = case lex str of
49 Left err => Left $ LexFail err
50 Right toks =>
51 case parse p toks of
52 Left (Error err []) =>
53 Left $ ParseFail err Nothing []
54 Left (Error err (t :: ts)) =>
55 Left $ ParseFail err (Just (line t, col t))
56 (map tok (t :: ts))
57 Right (val, _) => Right val
58
59export
60parseFile : (fn : String) -> Rule ty -> IO (Either ParseError ty)
61parseFile fn p
62 = do Right str <- readFile fn
63 | Left err => pure (Left (FileFail err))
64 pure (runParser str p)
65
66
67-- Some basic parsers used by all the intermediate forms
68
69export
70location : EmptyRule (Int, Int)
71location
72 = do tok <- peek
73 pure (line tok, col tok)
74
75export
76column : EmptyRule Int
77column
78 = do (line, col) <- location
79 pure col
80
81hex : Char -> Maybe Int
82hex '0' = Just 0
83hex '1' = Just 1
84hex '2' = Just 2
85hex '3' = Just 3
86hex '4' = Just 4
87hex '5' = Just 5
88hex '6' = Just 6
89hex '7' = Just 7
90hex '8' = Just 8
91hex '9' = Just 9
92hex 'a' = Just 10
93hex 'b' = Just 11
94hex 'c' = Just 12
95hex 'd' = Just 13
96hex 'e' = Just 14
97hex 'f' = Just 15
98hex _ = Nothing
99
100dec : Char -> Maybe Int
101dec '0' = Just 0
102dec '1' = Just 1
103dec '2' = Just 2
104dec '3' = Just 3
105dec '4' = Just 4
106dec '5' = Just 5
107dec '6' = Just 6
108dec '7' = Just 7
109dec '8' = Just 8
110dec '9' = Just 9
111dec _ = Nothing
112
113oct : Char -> Maybe Int
114oct '0' = Just 0
115oct '1' = Just 1
116oct '2' = Just 2
117oct '3' = Just 3
118oct '4' = Just 4
119oct '5' = Just 5
120oct '6' = Just 6
121oct '7' = Just 7
122oct _ = Nothing
123
124getEsc : String -> Maybe Char
125getEsc "NUL" = Just '\NUL'
126getEsc "SOH" = Just '\SOH'
127getEsc "STX" = Just '\STX'
128getEsc "ETX" = Just '\ETX'
129getEsc "EOT" = Just '\EOT'
130getEsc "ENQ" = Just '\ENQ'
131getEsc "ACK" = Just '\ACK'
132getEsc "BEL" = Just '\BEL'
133getEsc "BS" = Just '\BS'
134getEsc "HT" = Just '\HT'
135getEsc "LF" = Just '\LF'
136getEsc "VT" = Just '\VT'
137getEsc "FF" = Just '\FF'
138getEsc "CR" = Just '\CR'
139getEsc "SO" = Just '\SO'
140getEsc "SI" = Just '\SI'
141getEsc "DLE" = Just '\DLE'
142getEsc "DC1" = Just '\DC1'
143getEsc "DC2" = Just '\DC2'
144getEsc "DC3" = Just '\DC3'
145getEsc "DC4" = Just '\DC4'
146getEsc "NAK" = Just '\NAK'
147getEsc "SYN" = Just '\SYN'
148getEsc "ETB" = Just '\ETB'
149getEsc "CAN" = Just '\CAN'
150getEsc "EM" = Just '\EM'
151getEsc "SUB" = Just '\SUB'
152getEsc "ESC" = Just '\ESC'
153getEsc "FS" = Just '\FS'
154getEsc "GS" = Just '\GS'
155getEsc "RS" = Just '\RS'
156getEsc "US" = Just '\US'
157getEsc "SP" = Just '\SP'
158getEsc "DEL" = Just '\DEL'
159getEsc str = Nothing
160
161escape' : List Char -> Maybe (List Char)
162escape' [] = pure []
163escape' ('\\' :: '\\' :: xs) = pure $ '\\' :: !(escape' xs)
164escape' ('\\' :: '&' :: xs) = pure !(escape' xs)
165escape' ('\\' :: 'a' :: xs) = pure $ '\a' :: !(escape' xs)
166escape' ('\\' :: 'b' :: xs) = pure $ '\b' :: !(escape' xs)
167escape' ('\\' :: 'f' :: xs) = pure $ '\f' :: !(escape' xs)
168escape' ('\\' :: 'n' :: xs) = pure $ '\n' :: !(escape' xs)
169escape' ('\\' :: 'r' :: xs) = pure $ '\r' :: !(escape' xs)
170escape' ('\\' :: 't' :: xs) = pure $ '\t' :: !(escape' xs)
171escape' ('\\' :: 'v' :: xs) = pure $ '\v' :: !(escape' xs)
172escape' ('\\' :: '\'' :: xs) = pure $ '\'' :: !(escape' xs)
173escape' ('\\' :: '\"' :: xs) = pure $ '\"' :: !(escape' xs)
174escape' ('\\' :: 'x' :: xs)
175 = case span isHexDigit xs of
176 ([], rest) => assert_total (escape' rest)
177 (ds, rest) => pure $ cast !(toHex 1 (reverse ds)) ::
178 !(assert_total (escape' rest))
179 where
180 toHex : Int -> List Char -> Maybe Int
181 toHex _ [] = Just 0
182 toHex m (d :: ds)
183 = pure $ !(hex (toLower d)) * m + !(toHex (m*16) ds)
184escape' ('\\' :: 'o' :: xs)
185 = case span isOctDigit xs of
186 ([], rest) => assert_total (escape' rest)
187 (ds, rest) => pure $ cast !(toOct 1 (reverse ds)) ::
188 !(assert_total (escape' rest))
189 where
190 toOct : Int -> List Char -> Maybe Int
191 toOct _ [] = Just 0
192 toOct m (d :: ds)
193 = pure $ !(oct (toLower d)) * m + !(toOct (m*8) ds)
194escape' ('\\' :: xs)
195 = case span isDigit xs of
196 ([], (a :: b :: c :: rest)) =>
197 case getEsc (pack (the (List _) [a, b, c])) of
198 Just v => Just (v :: !(assert_total (escape' rest)))
199 Nothing => case getEsc (pack (the (List _) [a, b])) of
200 Just v => Just (v :: !(assert_total (escape' (c :: rest))))
201 Nothing => escape' xs
202 ([], (a :: b :: [])) =>
203 case getEsc (pack (the (List _) [a, b])) of
204 Just v => Just (v :: [])
205 Nothing => escape' xs
206 ([], rest) => assert_total (escape' rest)
207 (ds, rest) => Just $ cast (cast {to=Int} (pack ds)) ::
208 !(assert_total (escape' rest))
209escape' (x :: xs) = Just $ x :: !(escape' xs)
210
211escape : String -> Maybe String
212escape x = pure $ pack !(escape' (unpack x))
213
214getCharLit : String -> Maybe Char
215getCharLit str
216 = do e <- escape str
217 if length e == 1
218 then Just (assert_total (strHead e))
219 else if length e == 0 -- parsed the NULL character that terminated the string!
220 then Just '\0000'
221 else Nothing
222
223export
224constant : Rule Constant
225constant
226 = terminal "Expected constant"
227 (\x => case tok x of
228 Literal i => Just (BI i)
229 StrLit s => case escape s of
230 Nothing => Nothing
231 Just s' => Just (Str s')
232 CharLit c => case getCharLit c of
233 Nothing => Nothing
234 Just c' => Just (Ch c')
235 DoubleLit d => Just (Db d)
236 Keyword "Int" => Just IntType
237 Keyword "Integer" => Just IntegerType
238 Keyword "String" => Just StringType
239 Keyword "Char" => Just CharType
240 Keyword "Double" => Just DoubleType
241 _ => Nothing)
242
243export
244intLit : Rule Integer
245intLit
246 = terminal "Expected integer literal"
247 (\x => case tok x of
248 Literal i => Just i
249 _ => Nothing)
250
251export
252strLit : Rule String
253strLit
254 = terminal "Expected string literal"
255 (\x => case tok x of
256 StrLit s => Just s
257 _ => Nothing)
258
259export
260symbol : String -> Rule ()
261symbol req
262 = terminal ("Expected '" ++ req ++ "'")
263 (\x => case tok x of
264 Symbol s => if s == req then Just ()
265 else Nothing
266 _ => Nothing)
267
268export
269keyword : String -> Rule ()
270keyword req
271 = terminal ("Expected '" ++ req ++ "'")
272 (\x => case tok x of
273 Keyword s => if s == req then Just ()
274 else Nothing
275 _ => Nothing)
276
277export
278exactIdent : String -> Rule ()
279exactIdent req
280 = terminal ("Expected " ++ req)
281 (\x => case tok x of
282 Ident s => if s == req then Just ()
283 else Nothing
284 _ => Nothing)
285
286export
287operator : Rule String
288operator
289 = terminal "Expected operator"
290 (\x => case tok x of
291 Symbol s =>
292 if s `elem` reservedSymbols
293 then Nothing
294 else Just s
295 _ => Nothing)
296
297identPart : Rule String
298identPart
299 = terminal "Expected name"
300 (\x => case tok x of
301 Ident str => Just str
302 _ => Nothing)
303
304export
305namespace_ : Rule (List String)
306namespace_
307 = do ns <- sepBy1 (do col <- column
308 symbol "."
309 col' <- column
310 if (col' - col == 1)
311 then pure ()
312 else fail "No whitepace allowed after namespace separator")
313 identPart
314 pure (reverse ns) -- innermost first, so reverse
315
316export
317unqualifiedName : Rule String
318unqualifiedName = identPart
319
320export
321holeName : Rule String
322holeName
323 = terminal "Expected hole name"
324 (\x => case tok x of
325 HoleIdent str => Just str
326 _ => Nothing)
327
328export
329name : Rule Name
330name
331 = do ns <- namespace_
332 (do symbol ".("
333 op <- operator
334 symbol ")"
335 pure (NS ns (UN op))) <|>
336 (pure (mkFullName ns))
337 <|> do symbol "("
338 op <- operator
339 symbol ")"
340 pure (UN op)
341 where
342 mkFullName : List String -> Name
343 mkFullName [] = UN "NONE" -- Can't happen :)
344 mkFullName [n] = UN n
345 mkFullName (n :: ns) = NS ns (UN n)
346
347export
348IndentInfo : Type
349IndentInfo = Int
350
351export
352init : IndentInfo
353init = 0
354
355continueF : EmptyRule () -> (indent : IndentInfo) -> EmptyRule ()
356continueF err indent
357 = do eoi; err
358 <|> do col <- column
359 if (col <= indent)
360 then err
361 else pure ()
362
363-- Fail if this is the end of a block entry or end of file
364export
365continue : (indent : IndentInfo) -> EmptyRule ()
366continue = continueF (fail "Unexpected end of expression")
367
368-- As 'continue' but failing is fatal (i.e. entire parse fails)
369export
370mustContinue : (indent : IndentInfo) -> Maybe String -> EmptyRule ()
371mustContinue indent Nothing
372 = continueF (fatalError "Unexpected end of expression") indent
373mustContinue indent (Just req)
374 = continueF (fatalError ("Expected '" ++ req ++ "'")) indent
375
376data ValidIndent
377 = AnyIndent -- In {}, entries can begin in any column
378 | AtPos Int -- Entry must begin in a specific column
379 | AfterPos Int -- Entry can begin in this column or later
380 | EndOfBlock -- Block is finished
381
382Show ValidIndent where
383 show AnyIndent = "[any]"
384 show (AtPos i) = "[col " ++ show i ++ "]"
385 show (AfterPos i) = "[after " ++ show i ++ "]"
386 show EndOfBlock = "[EOB]"
387
388checkValid : ValidIndent -> Int -> EmptyRule ()
389checkValid AnyIndent c = pure ()
390checkValid (AtPos x) c = if c == x
391 then pure ()
392 else fail "Invalid indentation"
393checkValid (AfterPos x) c = if c >= x
394 then pure ()
395 else fail "Invalid indentation"
396checkValid EndOfBlock c = fail "End of block"
397
398-- Any token which indicates the end of a statement/block
399isTerminator : Token -> Bool
400isTerminator (Symbol ",") = True
401isTerminator (Symbol "]") = True
402isTerminator (Symbol ";") = True
403isTerminator (Symbol "}") = True
404isTerminator (Symbol ")") = True
405isTerminator (Symbol "|") = True
406isTerminator (Keyword "in") = True
407isTerminator EndInput = True
408isTerminator _ = False
409
410-- Check we're at the end of a block entry, given the start column
411-- of the block.
412-- It's the end if we have a terminating token, or the next token starts
413-- in or before indent. Works by looking ahead but not consuming.
414export
415atEnd : (indent : IndentInfo) -> EmptyRule ()
416atEnd indent
417 = eoi
418 <|> do nextIs "Expected end of block" (isTerminator . tok)
419 pure ()
420 <|> do col <- column
421 if (col <= indent)
422 then pure ()
423 else fail "Not the end of a block entry"
424
425-- Parse a terminator, return where the next block entry
426-- must start, given where the current block entry started
427terminator : ValidIndent -> Int -> EmptyRule ValidIndent
428terminator valid laststart
429 = do eoi
430 pure EndOfBlock
431 <|> do symbol ";"
432 pure (afterSemi valid)
433 <|> do col <- column
434 afterDedent valid col
435 <|> pure EndOfBlock
436 where
437 -- Expected indentation for the next token can either be anything (if
438 -- we're inside a brace delimited block) or anywhere after the initial
439 -- column (if we're inside an indentation delimited block)
440 afterSemi : ValidIndent -> ValidIndent
441 afterSemi AnyIndent = AnyIndent -- in braces, anything goes
442 afterSemi (AtPos c) = AfterPos c -- not in braces, after the last start position
443 afterSemi (AfterPos c) = AfterPos c
444 afterSemi EndOfBlock = EndOfBlock
445
446 -- Expected indentation for the next token can either be anything (if
447 -- we're inside a brace delimited block) or in exactly the initial column
448 -- (if we're inside an indentation delimited block)
449 afterDedent : ValidIndent -> Int -> EmptyRule ValidIndent
450 afterDedent AnyIndent col
451 = if col <= laststart
452 then pure AnyIndent
453 else fail "Not the end of a block entry"
454 afterDedent (AfterPos c) col
455 = if col <= laststart
456 then pure (AtPos c)
457 else fail "Not the end of a block entry"
458 afterDedent (AtPos c) col
459 = if col <= laststart
460 then pure (AtPos c)
461 else fail "Not the end of a block entry"
462 afterDedent EndOfBlock col = pure EndOfBlock
463
464-- Parse an entry in a block
465blockEntry : ValidIndent -> (IndentInfo -> Rule ty) ->
466 Rule (ty, ValidIndent)
467blockEntry valid rule
468 = do col <- column
469 checkValid valid col
470 p <- rule col
471 valid' <- terminator valid col
472 pure (p, valid')
473
474blockEntries : ValidIndent -> (IndentInfo -> Rule ty) ->
475 EmptyRule (List ty)
476blockEntries valid rule
477 = do eoi; pure []
478 <|> do res <- blockEntry valid rule
479 ts <- blockEntries (snd res) rule
480 pure (fst res :: ts)
481 <|> pure []
482
483export
484block : (IndentInfo -> Rule ty) -> EmptyRule (List ty)
485block item
486 = do symbol "{"
487 commit
488 ps <- blockEntries AnyIndent item
489 symbol "}"
490 pure ps
491 <|> do col <- column
492 blockEntries (AtPos col) item
493
494export
495blockAfter : Int -> (IndentInfo -> Rule ty) -> EmptyRule (List ty)
496blockAfter mincol item
497 = do symbol "{"
498 commit
499 ps <- blockEntries AnyIndent item
500 symbol "}"
501 pure ps
502 <|> do col <- column
503 if col <= mincol
504 then pure []
505 else blockEntries (AtPos col) item
506
507export
508nonEmptyBlock : (IndentInfo -> Rule ty) -> Rule (List ty)
509nonEmptyBlock item
510 = do symbol "{"
511 commit
512 res <- blockEntry AnyIndent item
513 ps <- blockEntries (snd res) item
514 symbol "}"
515 pure (fst res :: ps)
516 <|> do col <- column
517 res <- blockEntry (AtPos col) item
518 ps <- blockEntries (snd res) item
519 pure (fst res :: ps)
520
521
src/Text/Lexer.idr+324−0
@@ -0,0 +1,324 @@
1module Text.Lexer
2
3import Data.Bool.Extra
4
5import public Text.Lexer.Core
6import public Text.Quantity
7import public Text.Token
8
9%access export
10%default total
11
12toTokenMap : List (Lexer, k) -> TokenMap (Token k)
13toTokenMap = map $ \(l, kind) => (l, Tok kind)
14
15||| Recognise any character.
16||| /./
17any : Lexer
18any = pred (const True)
19
20||| Recognise a lexer or recognise no input. This is not guaranteed
21||| to consume input.
22||| /`l`?/
23opt : (l : Lexer) -> Recognise False
24opt l = l <|> empty
25
26||| Recognise any character if the sub-lexer `l` fails.
27||| /(?!`l`)./
28non : (l : Lexer) -> Lexer
29non l = reject l <+> any
30
31||| Produce recognisers by applying a function to elements of a container, and
32||| recognise the first match. Consumes input if the function produces consuming
33||| recognisers. Fails if the container is empty.
34choiceMap : {c : Bool} ->
35 Foldable t => (a -> Recognise c) -> t a -> Recognise c
36choiceMap {c} f xs = foldr (\x, acc => rewrite sym (andSameNeutral c) in
37 f x <|> acc)
38 fail xs
39
40||| Recognise the first matching recogniser in a container. Consumes input if
41||| recognisers in the list consume. Fails if the container is empty.
42choice : Foldable t => t (Recognise c) -> Recognise c
43choice = choiceMap id
44
45||| Sequence a list of recognisers. Guaranteed to consume input if the list is
46||| non-empty and the recognisers consume.
47concat : (xs : List (Recognise c)) -> Recognise (c && isCons xs)
48concat = concatMap id
49
50||| Recognise a specific character.
51||| /[`x`]/
52is : (x : Char) -> Lexer
53is x = pred (==x)
54
55||| Recognise anything but the given character.
56||| /[\^`x`]/
57isNot : (x : Char) -> Lexer
58isNot x = pred (/=x)
59
60||| Recognise a specific character (case-insensitive).
61||| /[`x`]/i
62like : (x : Char) -> Lexer
63like x = pred (\y => toUpper x == toUpper y)
64
65||| Recognise anything but the given character (case-insensitive).
66||| /[\^`x`]/i
67notLike : (x : Char) -> Lexer
68notLike x = pred (\y => toUpper x /= toUpper y)
69
70||| Recognise a specific string.
71||| Fails if the string is empty.
72||| /`str`/
73exact : (str : String) -> Lexer
74exact str = case unpack str of
75 [] => fail
76 (x :: xs) => concatMap is (x :: xs)
77
78||| Recognise a specific string (case-insensitive).
79||| Fails if the string is empty.
80||| /`str`/i
81approx : (str : String) -> Lexer
82approx str = case unpack str of
83 [] => fail
84 (x :: xs) => concatMap like (x :: xs)
85
86||| Recognise any of the characters in the given string.
87||| /[`chars`]/
88oneOf : (chars : String) -> Lexer
89oneOf chars = pred (\x => x `elem` unpack chars)
90
91||| Recognise a character range. Also works in reverse!
92||| /[`start`-`end`]/
93range : (start : Char) -> (end : Char) -> Lexer
94range start end = pred (\x => (x >= min start end)
95 && (x <= max start end))
96
97mutual
98 ||| Recognise a sequence of at least one sub-lexers
99 ||| /`l`+/
100 some : Lexer -> Lexer
101 some l = l <+> many l
102
103 ||| Recognise a sequence of at zero or more sub-lexers. This is not
104 ||| guaranteed to consume input
105 ||| /`l`\*/
106 many : Lexer -> Recognise False
107 many l = opt (some l)
108
109||| Repeat the sub-lexer `l` zero or more times until the lexer
110||| `stopBefore` is encountered. `stopBefore` will not be consumed.
111||| Not guaranteed to consume input.
112||| /((?!`stopBefore`)`l`)\*/
113manyUntil : (stopBefore : Recognise c) -> (l : Lexer) -> Recognise False
114manyUntil stopBefore l = many (reject stopBefore <+> l)
115
116||| Repeat the sub-lexer `l` zero or more times until the lexer
117||| `stopAfter` is encountered, and consume it. Guaranteed to
118||| consume if `stopAfter` consumes.
119||| /`l`\*?`stopAfter`/
120manyThen : (stopAfter : Recognise c) -> (l : Lexer) -> Recognise c
121manyThen stopAfter l = manyUntil stopAfter l <+> stopAfter
122
123||| Recognise many instances of `l` until an instance of `end` is
124||| encountered.
125|||
126||| Useful for defining comments.
127manyTill : (l : Lexer) -> (end : Lexer) -> Recognise False
128manyTill l end = end <|> opt (l <+> manyTill l end)
129%deprecate manyTill
130 "Prefer `lineComment`, or `manyUntil`/`manyThen` (argument order is flipped)."
131
132||| Recognise a sub-lexer repeated as specified by `q`. Fails if `q` has
133||| `min` and `max` in the wrong order. Consumes input unless `min q` is zero.
134||| /`l`{`q`}/
135count : (q : Quantity) -> (l : Lexer) -> Recognise (isSucc (min q))
136count (Qty Z Nothing) l = many l
137count (Qty Z (Just Z)) _ = empty
138count (Qty Z (Just (S max))) l = opt $ l <+> count (atMost max) l
139count (Qty (S min) Nothing) l = l <+> count (atLeast min) l
140count (Qty (S min) (Just Z)) _ = fail
141count (Qty (S min) (Just (S max))) l = l <+> count (between min max) l
142
143||| Recognise a single digit 0-9
144||| /[0-9]/
145digit : Lexer
146digit = pred isDigit
147
148||| Recognise one or more digits
149||| /[0-9]+/
150digits : Lexer
151digits = some digit
152
153||| Recognise a single hexidecimal digit
154||| /[0-9A-Fa-f]/
155hexDigit : Lexer
156hexDigit = pred isHexDigit
157
158||| Recognise one or more hexidecimal digits
159||| /[0-9A-Fa-f]+/
160hexDigits : Lexer
161hexDigits = some hexDigit
162
163||| Recognise a single octal digit
164||| /[0-8]/
165octDigit : Lexer
166octDigit = pred isHexDigit
167
168||| Recognise one or more octal digits
169||| /[0-8]+/
170octDigits : Lexer
171octDigits = some hexDigit
172
173||| Recognise a single alpha character
174||| /[A-Za-z]/
175alpha : Lexer
176alpha = pred isAlpha
177
178||| Recognise one or more alpha characters
179||| /[A-Za-z]+/
180alphas : Lexer
181alphas = some alpha
182
183||| Recognise a lowercase alpha character
184||| /[a-z]/
185lower : Lexer
186lower = pred isLower
187
188||| Recognise one or more lowercase alpha characters
189||| /[a-z]+/
190lowers : Lexer
191lowers = some lower
192
193||| Recognise an uppercase alpha character
194||| /[A-Z]/
195upper : Lexer
196upper = pred isUpper
197
198||| Recognise one or more uppercase alpha characters
199||| /[A-Z]+/
200uppers : Lexer
201uppers = some upper
202
203||| Recognise an alphanumeric character
204||| /[A-Za-z0-9]/
205alphaNum : Lexer
206alphaNum = pred isAlphaNum
207
208||| Recognise one or more alphanumeric characters
209||| /[A-Za-z0-9]+/
210alphaNums : Lexer
211alphaNums = some alphaNum
212
213||| Recognise a single whitespace character
214||| /\\s/
215space : Lexer
216space = pred isSpace
217
218||| Recognise one or more whitespace characters
219||| /\\s+/
220spaces : Lexer
221spaces = some space
222
223||| Recognise a single newline sequence. Understands CRLF, CR, and LF
224||| /\\r\\n|[\\r\\n]/
225newline : Lexer
226newline = let crlf = "\r\n" in
227 exact crlf <|> oneOf crlf
228
229||| Recognise one or more newline sequences. Understands CRLF, CR, and LF
230||| /(\\r\\n|[\\r\\n])+)/
231newlines : Lexer
232newlines = some newline
233
234||| Recognise a single non-whitespace, non-alphanumeric character
235||| /[\^\\sA-Za-z0-9]/
236symbol : Lexer
237symbol = pred (\x => not (isSpace x || isAlphaNum x))
238
239||| Recognise one or more non-whitespace, non-alphanumeric characters
240||| /[\^\\sA-Za-z0-9]+/
241symbols : Lexer
242symbols = some symbol
243
244||| Recognise a single control character
245||| /[\\x00-\\x1f\\x7f-\\x9f]/
246control : Lexer
247control = pred isControl
248
249||| Recognise one or more control characters
250||| /[\\x00-\\x1f\\x7f-\\x9f]+/
251controls : Lexer
252controls = some control
253
254||| Recognise zero or more occurrences of a sub-lexer between
255||| delimiting lexers
256||| /`start`(`l`)\*?`end`/
257surround : (start : Lexer) -> (end : Lexer) -> (l : Lexer) -> Lexer
258surround start end l = start <+> manyThen end l
259
260||| Recognise zero or more occurrences of a sub-lexer surrounded
261||| by the same quote lexer on both sides (useful for strings)
262||| /`q`(`l`)\*?`q`/
263quote : (q : Lexer) -> (l : Lexer) -> Lexer
264quote q l = surround q q l
265
266||| Recognise an escape character (often '\\') followed by a sub-lexer
267||| /[`esc`]`l`/
268escape : (esc : Char) -> Lexer -> Lexer
269escape esc l = is esc <+> l
270
271||| Recognise a string literal, including escaped characters.
272||| (Note: doesn't yet handle escape sequences such as \123)
273||| /"(\\\\.|.)\*?"/
274stringLit : Lexer
275stringLit = quote (is '"') (escape '\\' any <|> any)
276
277||| Recognise a character literal, including escaped characters.
278||| (Note: doesn't yet handle escape sequences such as \123)
279||| /'(\\\\.|[\^'])'/
280charLit : Lexer
281charLit = let q = '\'' in
282 is q <+> (escape '\\' (control <|> any) <|> isNot q) <+> is q
283 where
284 lexStr : List String -> Lexer
285 lexStr [] = fail
286 lexStr (t :: ts) = exact t <|> lexStr ts
287
288 control : Lexer
289 control = lexStr ["NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
290 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
291 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
292 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US",
293 "SP", "DEL"]
294 <|> (is 'x' <+> hexDigits)
295 <|> (is 'o' <+> octDigits)
296 <|> digits
297
298||| Recognise an integer literal (possibly with a '-' prefix)
299||| /-?[0-9]+/
300intLit : Lexer
301intLit = opt (is '-') <+> digits
302
303||| Recognise a hexidecimal literal, prefixed by "0x" or "0X"
304||| /0[Xx][0-9A-Fa-f]+/
305hexLit : Lexer
306hexLit = approx "0x" <+> hexDigits
307
308||| Recognise `start`, then recognise all input until a newline is encountered,
309||| and consume the newline. Will succeed if end-of-input is encountered before
310||| a newline.
311||| /`start`[\^\\r\\n]+(\\r\\n|[\\r\\n])?/
312lineComment : (start : Lexer) -> Lexer
313lineComment start = start <+> manyUntil newline any <+> opt newline
314
315||| Recognise all input between `start` and `end` lexers.
316||| Supports balanced nesting.
317|||
318||| For block comments that don't support nesting (such as C-style comments),
319||| use `surround`
320blockComment : (start : Lexer) -> (end : Lexer) -> Lexer
321blockComment start end = start <+> middle <+> end
322 where
323 middle : Recognise False
324 middle = manyUntil end (blockComment start end <|> any)
src/Text/Lexer/Core.idr+192−0
@@ -0,0 +1,192 @@
1module Text.Lexer.Core
2
3import Data.Bool.Extra
4
5import public Control.Delayed
6
7%default total
8
9||| A language of token recognisers.
10||| @ consumes If `True`, this recogniser is guaranteed to consume at
11||| least one character of input when it succeeds.
12export
13data Recognise : (consumes : Bool) -> Type where
14 Empty : Recognise False
15 Fail : Recognise c
16 Lookahead : (positive : Bool) -> Recognise c -> Recognise False
17 Pred : (Char -> Bool) -> Recognise True
18 SeqEat : Recognise True -> Inf (Recognise e) -> Recognise True
19 SeqEmpty : Recognise e1 -> Recognise e2 -> Recognise (e1 || e2)
20 Alt : Recognise e1 -> Recognise e2 -> Recognise (e1 && e2)
21
22||| A token recogniser. Guaranteed to consume at least one character.
23public export
24Lexer : Type
25Lexer = Recognise True
26
27||| Sequence two recognisers. If either consumes a character, the sequence
28||| is guaranteed to consume a character.
29export %inline
30(<+>) : {c1 : Bool} ->
31 Recognise c1 -> inf c1 (Recognise c2) -> Recognise (c1 || c2)
32(<+>) {c1 = False} = SeqEmpty
33(<+>) {c1 = True} = SeqEat
34
35||| Alternative recognisers. If both consume, the combination is guaranteed
36||| to consumer a character.
37export
38(<|>) : Recognise c1 -> Recognise c2 -> Recognise (c1 && c2)
39(<|>) = Alt
40
41||| A recogniser that always fails.
42export
43fail : Recognise c
44fail = Fail
45
46||| Recognise no input (doesn't consume any input)
47export
48empty : Recognise False
49empty = Empty
50
51||| Recognise a character that matches a predicate
52export
53pred : (Char -> Bool) -> Lexer
54pred = Pred
55
56||| Positive lookahead. Never consumes input.
57export
58expect : Recognise c -> Recognise False
59expect = Lookahead True
60
61||| Negative lookahead. Never consumes input.
62export
63reject : Recognise c -> Recognise False
64reject = Lookahead False
65
66||| Sequence the recognisers resulting from applying a function to each element
67||| of a list. The resulting recogniser will consume input if the produced
68||| recognisers consume and the list is non-empty.
69export
70concatMap : {c : Bool} ->
71 (a -> Recognise c) -> (xs : List a) -> Recognise (c && isCons xs)
72concatMap {c} _ [] = rewrite andFalseFalse c in Empty
73concatMap {c} f (x :: xs) = rewrite andTrueNeutral c in
74 rewrite sym (orSameAndRightNeutral c (isCons xs)) in
75 SeqEmpty (f x) (concatMap f xs)
76
77data StrLen : Type where
78 MkStrLen : String -> Nat -> StrLen
79
80getString : StrLen -> String
81getString (MkStrLen str n) = str
82
83strIndex : StrLen -> Nat -> Maybe Char
84strIndex (MkStrLen str len) i
85 = if cast {to = Integer} i >= cast len then Nothing
86 else Just (assert_total (prim__strIndex str (cast i)))
87
88mkStr : String -> StrLen
89mkStr str = MkStrLen str (length str)
90
91strTail : Nat -> StrLen -> StrLen
92strTail start (MkStrLen str len)
93 = MkStrLen (substr start len str) (minus len start)
94
95-- If the string is recognised, returns the index at which the token
96-- ends
97scan : Recognise c -> Nat -> StrLen -> Maybe Nat
98scan Empty idx str = pure idx
99scan Fail idx str = Nothing
100scan (Lookahead positive r) idx str
101 = if isJust (scan r idx str) == positive
102 then Just idx
103 else Nothing
104scan (Pred f) idx (MkStrLen str len)
105 = if cast {to = Integer} idx >= cast len
106 then Nothing
107 else if f (assert_total (prim__strIndex str (cast idx)))
108 then Just (idx + 1)
109 else Nothing
110scan (SeqEat r1 r2) idx str
111 = do idx' <- scan r1 idx str
112 -- TODO: Can we prove totality instead by showing idx has increased?
113 assert_total (scan r2 idx' str)
114scan (SeqEmpty r1 r2) idx str
115 = do idx' <- scan r1 idx str
116 scan r2 idx' str
117scan (Alt r1 r2) idx str
118 = maybe (scan r2 idx str) Just (scan r1 idx str)
119
120takeToken : Lexer -> StrLen -> Maybe (String, StrLen)
121takeToken lex str
122 = do i <- scan lex 0 str -- i must be > 0 if successful
123 pure (substr 0 i (getString str), strTail i str)
124
125||| A mapping from lexers to the tokens they produce.
126||| This is a list of pairs `(Lexer, String -> tokenType)`
127||| For each Lexer in the list, if a substring in the input matches, run
128||| the associated function to produce a token of type `tokenType`
129public export
130TokenMap : (tokenType : Type) -> Type
131TokenMap tokenType = List (Lexer, String -> tokenType)
132
133||| A token, and the line and column where it was in the input
134public export
135record TokenData a where
136 constructor MkToken
137 line : Int
138 col : Int
139 tok : a
140
141fspanEnd : Nat -> (Char -> Bool) -> String -> (Nat, String)
142fspanEnd k p "" = (k, "")
143fspanEnd k p xxs
144 = assert_total $
145 let x = prim__strHead xxs
146 xs = prim__strTail xxs in
147 if p x then fspanEnd (S k) p xs
148 else (k, xxs)
149
150-- Faster version of 'span' from the prelude (avoids unpacking)
151export
152fspan : (Char -> Bool) -> String -> (String, String)
153fspan p xs
154 = let (end, rest) = fspanEnd 0 p xs in
155 (substr 0 end xs, rest)
156
157tokenise : (line : Int) -> (col : Int) ->
158 List (TokenData a) -> TokenMap a ->
159 StrLen -> (List (TokenData a), (Int, Int, StrLen))
160tokenise line col acc tmap str
161 = case getFirstToken tmap str of
162 Just (tok, line', col', rest) =>
163 -- assert total because getFirstToken must consume something
164 assert_total (tokenise line' col' (tok :: acc) tmap rest)
165 Nothing => (reverse acc, (line, col, str))
166 where
167 countNLs : List Char -> Nat
168 countNLs str = List.length (filter (== '\n') str)
169
170 getCols : String -> Int -> Int
171 getCols x c
172 = case fspan (/= '\n') (reverse x) of
173 (incol, "") => c + cast (length incol)
174 (incol, _) => cast (length incol)
175
176 getFirstToken : TokenMap a -> StrLen -> Maybe (TokenData a, Int, Int, StrLen)
177 getFirstToken [] str = Nothing
178 getFirstToken ((lex, fn) :: ts) str
179 = case takeToken lex str of
180 Just (tok, rest) => Just (MkToken line col (fn tok),
181 line + cast (countNLs (unpack tok)),
182 getCols tok col, rest)
183 Nothing => getFirstToken ts str
184
185||| Given a mapping from lexers to token generating functions (the
186||| TokenMap a) and an input string, return a list of recognised tokens,
187||| and the line, column, and remainder of the input at the first point in the
188||| string where there are no recognised tokens.
189export
190lex : TokenMap a -> String -> (List (TokenData a), (Int, Int, String))
191lex tmap str = let (ts, (l, c, str')) = tokenise 0 0 [] tmap (mkStr str) in
192 (ts, (l, c, getString str'))
src/Text/Parser.idr+226−0
@@ -0,0 +1,226 @@
1module Text.Parser
2
3import Data.Bool.Extra
4
5import public Text.Parser.Core
6import public Text.Quantity
7import public Text.Token
8
9%access export
10%default total
11
12||| Parse a terminal based on a kind of token.
13match : (Eq k, TokenKind k) =>
14 (kind : k) ->
15 Grammar (Token k) True (TokType kind)
16match kind = terminal "Unrecognised input" $
17 \(Tok kind' text) => if kind' == kind
18 then Just $ tokValue kind text
19 else Nothing
20
21||| Optionally parse a thing, with a default value if the grammar doesn't
22||| match. May match the empty input.
23option : {c : Bool} ->
24 (def : a) -> (p : Grammar tok c a) ->
25 Grammar tok False a
26option {c = False} def p = p <|> pure def
27option {c = True} def p = p <|> pure def
28
29||| Optionally parse a thing.
30||| To provide a default value, use `option` instead.
31optional : (p : Grammar tok c a) ->
32 Grammar tok False (Maybe a)
33optional p = option Nothing (map Just p)
34
35||| Try to parse one thing or the other, producing a value that indicates
36||| which option succeeded. If both would succeed, the left option
37||| takes priority.
38choose : (l : Grammar tok c1 a) ->
39 (r : Grammar tok c2 b) ->
40 Grammar tok (c1 && c2) (Either a b)
41choose l r = map Left l <|> map Right r
42
43||| Produce a grammar by applying a function to each element of a container,
44||| then try each resulting grammar until the first one succeeds. Fails if the
45||| container is empty.
46choiceMap : {c : Bool} ->
47 (a -> Grammar tok c b) ->
48 Foldable t => t a ->
49 Grammar tok c b
50choiceMap {c} f xs = foldr (\x, acc => rewrite sym (andSameNeutral c) in
51 f x <|> acc)
52 (fail "No more options") xs
53
54||| Try each grammar in a container until the first one succeeds.
55||| Fails if the container is empty.
56choice : Foldable t => t (Grammar tok c a) ->
57 Grammar tok c a
58choice = choiceMap id
59
60mutual
61 ||| Parse one or more things
62 some : Grammar tok True a ->
63 Grammar tok True (List a)
64 some p = pure (!p :: !(many p))
65
66 ||| Parse zero or more things (may match the empty input)
67 many : Grammar tok True a ->
68 Grammar tok False (List a)
69 many p = option [] (some p)
70
71||| Parse one or more instances of `p`, returning the parsed items and proof
72||| that the resulting list is non-empty.
73some' : (p : Grammar tok True a) ->
74 Grammar tok True (xs : List a ** NonEmpty xs)
75some' p = pure (!p :: !(many p) ** IsNonEmpty)
76
77mutual
78 private
79 count1 : (q : Quantity) ->
80 (p : Grammar tok True a) ->
81 Grammar tok True (List a)
82 count1 q p = do x <- p
83 seq (count q p)
84 (\xs => pure (x :: xs))
85
86 ||| Parse `p`, repeated as specified by `q`, returning the list of values.
87 count : (q : Quantity) ->
88 (p : Grammar tok True a) ->
89 Grammar tok (isSucc (min q)) (List a)
90 count (Qty Z Nothing) p = many p
91 count (Qty Z (Just Z)) _ = pure []
92 count (Qty Z (Just (S max))) p = option [] $ count1 (atMost max) p
93 count (Qty (S min) Nothing) p = count1 (atLeast min) p
94 count (Qty (S min) (Just Z)) _ = fail "Quantity out of order"
95 count (Qty (S min) (Just (S max))) p = count1 (between (S min) max) p
96
97mutual
98 ||| Parse one or more instances of `p` until `end` succeeds, returning the
99 ||| list of values from `p`. Guaranteed to consume input.
100 someTill : {c : Bool} ->
101 (end : Grammar tok c e) ->
102 (p : Grammar tok True a) ->
103 Grammar tok True (List a)
104 someTill {c} end p = do x <- p
105 seq (manyTill end p)
106 (\xs => pure (x :: xs))
107
108 ||| Parse zero or more instances of `p` until `end` succeeds, returning the
109 ||| list of values from `p`. Guaranteed to consume input if `end` consumes.
110 manyTill : {c : Bool} ->
111 (end : Grammar tok c e) ->
112 (p : Grammar tok True a) ->
113 Grammar tok c (List a)
114 manyTill {c} end p = rewrite sym (andTrueNeutral c) in
115 map (const []) end <|> someTill end p
116
117||| Parse one or more instances of `p` until `end` succeeds, returning the
118||| list of values from `p`, along with a proof that the resulting list is
119||| non-empty.
120someTill' : {c : Bool} ->
121 (end : Grammar tok c e) ->
122 (p : Grammar tok True a) ->
123 Grammar tok True (xs : List a ** NonEmpty xs)
124someTill' end p
125 = do x <- p
126 seq (manyTill end p)
127 (\xs => pure (x :: xs ** IsNonEmpty))
128
129mutual
130 ||| Parse one or more instance of `skip` until `p` is encountered,
131 ||| returning its value.
132 afterSome : (skip : Grammar tok True s) ->
133 (p : Grammar tok c a) ->
134 Grammar tok True a
135 afterSome skip p = do skip
136 afterMany skip p
137
138 ||| Parse zero or more instance of `skip` until `p` is encountered,
139 ||| returning its value.
140 afterMany : {c : Bool} ->
141 (skip : Grammar tok True s) ->
142 (p : Grammar tok c a) ->
143 Grammar tok c a
144 afterMany {c} skip p = rewrite sym (andTrueNeutral c) in
145 p <|> afterSome skip p
146
147||| Parse one or more things, each separated by another thing.
148sepBy1 : {c : Bool} ->
149 (sep : Grammar tok True s) ->
150 (p : Grammar tok c a) ->
151 Grammar tok c (List a)
152sepBy1 {c} sep p = rewrite sym (orFalseNeutral c) in
153 [| p :: many (sep *> p) |]
154
155||| Parse zero or more things, each separated by another thing. May
156||| match the empty input.
157sepBy : (sep : Grammar tok True s) ->
158 (p : Grammar tok c a) ->
159 Grammar tok False (List a)
160sepBy sep p = option [] $ sepBy1 sep p
161
162||| Parse one or more instances of `p` separated by `sep`, returning the
163||| parsed items and proof that the resulting list is non-empty.
164sepBy1' : {c : Bool} ->
165 (sep : Grammar tok True s) ->
166 (p : Grammar tok c a) ->
167 Grammar tok c (xs : List a ** NonEmpty xs)
168sepBy1' {c} sep p
169 = rewrite sym (orFalseNeutral c) in
170 seq p (\x => do xs <- many (sep *> p)
171 pure (x :: xs ** IsNonEmpty))
172
173||| Parse one or more instances of `p` separated by and optionally terminated by
174||| `sep`.
175sepEndBy1 : {c : Bool} ->
176 (sep : Grammar tok True s) ->
177 (p : Grammar tok c a) ->
178 Grammar tok c (List a)
179sepEndBy1 {c} sep p = rewrite sym (orFalseNeutral c) in
180 sepBy1 sep p <* optional sep
181
182||| Parse zero or more instances of `p`, separated by and optionally terminated
183||| by `sep`. Will not match a separator by itself.
184sepEndBy : (sep : Grammar tok True s) ->
185 (p : Grammar tok c a) ->
186 Grammar tok False (List a)
187sepEndBy sep p = option [] $ sepEndBy1 sep p
188
189||| Parse zero or more instances of `p`, separated by and optionally terminated
190||| by `sep`, returning the parsed items and a proof that the resulting list
191||| is non-empty.
192sepEndBy1' : {c : Bool} ->
193 (sep : Grammar tok True s) ->
194 (p : Grammar tok c a) ->
195 Grammar tok c (xs : List a ** NonEmpty xs)
196sepEndBy1' {c} sep p = rewrite sym (orFalseNeutral c) in
197 sepBy1' sep p <* optional sep
198
199||| Parse one or more instances of `p`, separated and terminated by `sep`.
200endBy1 : {c : Bool} ->
201 (sep : Grammar tok True s) ->
202 (p : Grammar tok c a) ->
203 Grammar tok True (List a)
204endBy1 {c} sep p = some $ rewrite sym (orTrueTrue c) in
205 p <* sep
206
207endBy : (sep : Grammar tok True s) ->
208 (p : Grammar tok c a) ->
209 Grammar tok False (List a)
210endBy sep p = option [] $ endBy1 sep p
211
212||| Parse zero or more instances of `p`, separated and terminated by `sep`,
213||| returning the parsed items and a proof that the resulting list is non-empty.
214endBy1' : {c : Bool} ->
215 (sep : Grammar tok True s) ->
216 (p : Grammar tok c a) ->
217 Grammar tok True (xs : List a ** NonEmpty xs)
218endBy1' {c} sep p = some' $ rewrite sym (orTrueTrue c) in
219 p <* sep
220
221||| Parse an instance of `p` that is between `left` and `right`.
222between : (left : Grammar tok True l) ->
223 (right : Grammar tok True r) ->
224 (p : Grammar tok c a) ->
225 Grammar tok True a
226between left right contents = left *> contents <* right
src/Text/Parser/Core.idr+284−0
@@ -0,0 +1,284 @@
1module Text.Parser.Core
2
3import public Control.Delayed
4
5%default total
6
7-- TODO: Add some primitives for helping with error messages.
8-- e.g. perhaps set a string to state what we're currently trying to
9-- parse, or to say what the next expected token is in words
10
11||| Description of a language's grammar. The `tok` parameter is the type
12||| of tokens, and the `consumes` flag is True if the language is guaranteed
13||| to be non-empty - that is, successfully parsing the language is guaranteed
14||| to consume some input.
15export
16data Grammar : (tok : Type) -> (consumes : Bool) -> Type -> Type where
17 Empty : (val : ty) -> Grammar tok False ty
18 Terminal : String -> (tok -> Maybe a) -> Grammar tok True a
19 NextIs : String -> (tok -> Bool) -> Grammar tok False tok
20 EOF : Grammar tok False ()
21
22 Fail : Bool -> String -> Grammar tok c ty
23 Commit : Grammar tok False ()
24 MustWork : Grammar tok c a -> Grammar tok c a
25
26 SeqEat : Grammar tok True a -> Inf (a -> Grammar tok c2 b) ->
27 Grammar tok True b
28 SeqEmpty : {c1, c2 : Bool} ->
29 Grammar tok c1 a -> (a -> Grammar tok c2 b) ->
30 Grammar tok (c1 || c2) b
31 Alt : {c1, c2 : Bool} ->
32 Grammar tok c1 ty -> Grammar tok c2 ty ->
33 Grammar tok (c1 && c2) ty
34
35||| Sequence two grammars. If either consumes some input, the sequence is
36||| guaranteed to consume some input. If the first one consumes input, the
37||| second is allowed to be recursive (because it means some input has been
38||| consumed and therefore the input is smaller)
39export %inline
40(>>=) : {c1 : Bool} ->
41 Grammar tok c1 a ->
42 inf c1 (a -> Grammar tok c2 b) ->
43 Grammar tok (c1 || c2) b
44(>>=) {c1 = False} = SeqEmpty
45(>>=) {c1 = True} = SeqEat
46
47||| Sequence two grammars. If either consumes some input, the sequence is
48||| guaranteed to consume input. This is an explicitly non-infinite version
49||| of `>>=`.
50export
51seq : Grammar tok c1 a ->
52 (a -> Grammar tok c2 b) ->
53 Grammar tok (c1 || c2) b
54seq = SeqEmpty
55
56||| Sequence a grammar followed by the grammar it returns.
57export
58join : {c1 : Bool} ->
59 Grammar tok c1 (Grammar tok c2 a) ->
60 Grammar tok (c1 || c2) a
61join {c1 = False} p = SeqEmpty p id
62join {c1 = True} p = SeqEat p id
63
64||| Give two alternative grammars. If both consume, the combination is
65||| guaranteed to consume.
66export
67(<|>) : Grammar tok c1 ty ->
68 Grammar tok c2 ty ->
69 Grammar tok (c1 && c2) ty
70(<|>) = Alt
71
72||| Allows the result of a grammar to be mapped to a different value.
73export
74Functor (Grammar tok c) where
75 map f (Empty val) = Empty (f val)
76 map f (Fail fatal msg) = Fail fatal msg
77 map f (MustWork g) = MustWork (map f g)
78 map f (Terminal msg g) = Terminal msg (\t => map f (g t))
79 map f (Alt x y) = Alt (map f x) (map f y)
80 map f (SeqEat act next)
81 = SeqEat act (\val => map f (next val))
82 map f (SeqEmpty act next)
83 = SeqEmpty act (\val => map f (next val))
84 -- The remaining constructors (NextIs, EOF, Commit) have a fixed type,
85 -- so a sequence must be used.
86 map {c = False} f p = SeqEmpty p (Empty . f)
87
88||| Sequence a grammar with value type `a -> b` and a grammar
89||| with value type `a`. If both succeed, apply the function
90||| from the first grammar to the value from the second grammar.
91||| Guaranteed to consume if either grammar consumes.
92export
93(<*>) : Grammar tok c1 (a -> b) ->
94 Grammar tok c2 a ->
95 Grammar tok (c1 || c2) b
96(<*>) x y = SeqEmpty x (\f => map f y)
97
98||| Sequence two grammars. If both succeed, use the value of the first one.
99||| Guaranteed to consume if either grammar consumes.
100export
101(<*) : Grammar tok c1 a ->
102 Grammar tok c2 b ->
103 Grammar tok (c1 || c2) a
104(<*) x y = map const x <*> y
105
106||| Sequence two grammars. If both succeed, use the value of the second one.
107||| Guaranteed to consume if either grammar consumes.
108export
109(*>) : Grammar tok c1 a ->
110 Grammar tok c2 b ->
111 Grammar tok (c1 || c2) b
112(*>) x y = map (const id) x <*> y
113
114||| Produce a grammar that can parse a different type of token by providing a
115||| function converting the new token type into the original one.
116export
117mapToken : (a -> b) -> Grammar b c ty -> Grammar a c ty
118mapToken f (Empty val) = Empty val
119mapToken f (Terminal msg g) = Terminal msg (g . f)
120mapToken f (NextIs msg g) = SeqEmpty (NextIs msg (g . f)) (Empty . f)
121mapToken f EOF = EOF
122mapToken f (Fail fatal msg) = Fail fatal msg
123mapToken f (MustWork g) = MustWork (mapToken f g)
124mapToken f Commit = Commit
125mapToken f (SeqEat act next) = SeqEat (mapToken f act) (\x => mapToken f (next x))
126mapToken f (SeqEmpty act next) = SeqEmpty (mapToken f act) (\x => mapToken f (next x))
127mapToken f (Alt x y) = Alt (mapToken f x) (mapToken f y)
128
129||| Always succeed with the given value.
130export
131pure : (val : ty) -> Grammar tok False ty
132pure = Empty
133
134||| Check whether the next token satisfies a predicate
135export
136nextIs : String -> (tok -> Bool) -> Grammar tok False tok
137nextIs = NextIs
138
139||| Look at the next token in the input
140export
141peek : Grammar tok False tok
142peek = nextIs "Unrecognised token" (const True)
143
144||| Succeeds if running the predicate on the next token returns Just x,
145||| returning x. Otherwise fails.
146export
147terminal : String -> (tok -> Maybe a) -> Grammar tok True a
148terminal = Terminal
149
150||| Always fail with a message
151export
152fail : String -> Grammar tok c ty
153fail = Fail False
154
155export
156fatalError : String -> Grammar tok c ty
157fatalError = Fail True
158
159||| Succeed if the input is empty
160export
161eof : Grammar tok False ()
162eof = EOF
163
164||| Commit to an alternative; if the current branch of an alternative
165||| fails to parse, no more branches will be tried
166export
167commit : Grammar tok False ()
168commit = Commit
169
170||| If the parser fails, treat it as a fatal error
171export
172mustWork : Grammar tok c ty -> Grammar tok c ty
173mustWork = MustWork
174
175data ParseResult : List tok -> (consumes : Bool) -> Type -> Type where
176 Failure : {xs : List tok} ->
177 (committed : Bool) -> (fatal : Bool) ->
178 (err : String) -> (rest : List tok) -> ParseResult xs c ty
179 EmptyRes : (committed : Bool) ->
180 (val : ty) -> (more : List tok) -> ParseResult more False ty
181 NonEmptyRes : (committed : Bool) ->
182 (val : ty) -> (more : List tok) ->
183 ParseResult (x :: xs ++ more) c ty
184
185-- Take the result of an alternative branch, reset the commit flag to
186-- the commit flag from the outer alternative, and weaken the 'consumes'
187-- flag to take both alternatives into account
188weakenRes : {whatever, c : Bool} -> {xs : List tok} ->
189 (com' : Bool) ->
190 ParseResult xs c ty -> ParseResult xs (whatever && c) ty
191weakenRes com' (Failure com fatal msg ts) = Failure com' fatal msg ts
192weakenRes {whatever=True} com' (EmptyRes com val xs) = EmptyRes com' val xs
193weakenRes {whatever=False} com' (EmptyRes com val xs) = EmptyRes com' val xs
194weakenRes com' (NonEmptyRes com val more) = NonEmptyRes com' val more
195
196shorter : (more : List tok) -> .(ys : List tok) ->
197 LTE (S (length more)) (S (length (ys ++ more)))
198shorter more [] = lteRefl
199shorter more (x :: xs) = LTESucc (lteSuccLeft (shorter more xs))
200
201doParse : {c : Bool} ->
202 (commit : Bool) -> (xs : List tok) -> (act : Grammar tok c ty) ->
203 ParseResult xs c ty
204-- doParse com xs act with (sizeAccessible xs)
205doParse com xs (Empty val) = EmptyRes com val xs
206doParse com [] (Fail fatal str) = Failure com fatal str []
207doParse com (x :: xs) (Fail fatal str) = Failure com fatal str (x :: xs)
208doParse com xs Commit = EmptyRes True () xs
209doParse com xs (MustWork g) =
210 let p' = doParse com xs g in
211 case p' of
212 Failure com' _ msg ts => Failure com' True msg ts
213 res => res
214doParse com [] (Terminal err f) = Failure com False "End of input" []
215doParse com (x :: xs) (Terminal err f)
216 = maybe
217 (Failure com False err (x :: xs))
218 (\a => NonEmptyRes com {xs=[]} a xs)
219 (f x)
220doParse com [] EOF = EmptyRes com () []
221doParse com (x :: xs) EOF
222 = Failure com False "Expected end of input" (x :: xs)
223doParse com [] (NextIs err f) = Failure com False "End of input" []
224doParse com (x :: xs) (NextIs err f)
225 = if f x
226 then EmptyRes com x (x :: xs)
227 else Failure com False err (x :: xs)
228doParse com xs (Alt x y)
229 = let p' = doParse False xs x in
230 case p' of
231 Failure com' fatal msg ts
232 => if com' || fatal
233 -- If the alternative had committed, don't try the
234 -- other branch (and reset commit flag)
235 then Failure com fatal msg ts
236 else weakenRes com (doParse False xs y)
237 -- Successfully parsed the first option, so use the outer commit flag
238 EmptyRes _ val xs => EmptyRes com val xs
239 NonEmptyRes _ val more => NonEmptyRes com val more
240doParse com xs (SeqEmpty act next)
241 = let p' = assert_total (doParse com xs act) in
242 case p' of
243 Failure com fatal msg ts => Failure com fatal msg ts
244 EmptyRes com val xs =>
245 case assert_total (doParse com xs (next val)) of
246 Failure com' fatal msg ts => Failure com' fatal msg ts
247 EmptyRes com' val xs => EmptyRes com' val xs
248 NonEmptyRes com' val more => NonEmptyRes com' val more
249 NonEmptyRes {x} {xs=ys} com val more =>
250 case (assert_total (doParse com more (next val))) of
251 Failure com' fatal msg ts => Failure com' fatal msg ts
252 EmptyRes com' val _ => NonEmptyRes com' val more
253 NonEmptyRes {x=x1} {xs=xs1} com' val more' =>
254 rewrite appendAssociative (x :: ys) (x1 :: xs1) more' in
255 NonEmptyRes com' val more'
256doParse com xs (SeqEat act next) with (doParse com xs act)
257 doParse com xs (SeqEat act next) | Failure com' fatal msg ts
258 = Failure com' fatal msg ts
259 doParse com (x :: (ys ++ more)) (SeqEat act next) | (NonEmptyRes com' val more)
260 = let p' = assert_total (doParse com' more (next val)) in
261 case p' of
262 Failure com' fatal msg ts => Failure com' fatal msg ts
263 EmptyRes com' val _ => NonEmptyRes com' val more
264 NonEmptyRes {x=x1} {xs=xs1} com' val more' =>
265 rewrite appendAssociative (x :: ys) (x1 :: xs1) more' in
266 NonEmptyRes com' val more'
267-- This next line is not strictly necessary, but it stops the coverage
268-- checker taking a really long time and eating lots of memory...
269-- doParse _ _ _ | sml = Failure True "Help the coverage checker!" []
270
271public export
272data ParseError tok = Error String (List tok)
273
274||| Parse a list of tokens according to the given grammar. If successful,
275||| returns a pair of the parse result and the unparsed tokens (the remaining
276||| input).
277export
278parse : (act : Grammar tok c ty) -> (xs : List tok) ->
279 Either (ParseError tok) (ty, List tok)
280parse act xs
281 = case doParse False xs act of
282 Failure _ _ msg ts => Left (Error msg ts)
283 EmptyRes _ val rest => pure (val, rest)
284 NonEmptyRes _ val rest => pure (val, rest)
src/Text/Quantity.idr+38−0
@@ -0,0 +1,38 @@
1module Text.Quantity
2
3%access public export
4%default total
5
6record Quantity where
7 constructor Qty
8 min : Nat
9 max : Maybe Nat
10
11Show Quantity where
12 show (Qty Z Nothing) = "*"
13 show (Qty Z (Just (S Z))) = "?"
14 show (Qty (S Z) Nothing) = "+"
15 show (Qty min max) = "{" ++ show min ++ showMax ++ "}"
16 where
17 showMax : String
18 showMax = case max of
19 Nothing => ","
20 Just max' => if min == max'
21 then ""
22 else "," ++ show max'
23
24between : Nat -> Nat -> Quantity
25between min max = Qty min (Just max)
26
27atLeast : Nat -> Quantity
28atLeast min = Qty min Nothing
29
30atMost : Nat -> Quantity
31atMost max = Qty 0 (Just max)
32
33exactly : Nat -> Quantity
34exactly n = Qty n (Just n)
35
36inOrder : Quantity -> Bool
37inOrder (Qty min Nothing) = True
38inOrder (Qty min (Just max)) = min <= max
src/Text/Token.idr+38−0
@@ -0,0 +1,38 @@
1module Text.Token
2
3%access public export
4%default total
5
6||| For a type `kind`, specify a way of converting the recognised
7||| string into a value.
8|||
9||| ```idris example
10||| data SimpleKind = SKString | SKInt | SKComma
11|||
12||| TokenKind SimpleKind where
13||| TokType SKString = String
14||| TokType SKInt = Int
15||| TokType SKComma = ()
16|||
17||| tokValue SKString x = x
18||| tokValue SKInt x = cast x
19||| tokValue SKComma x = ()
20||| ```
21interface TokenKind (k : Type) where
22 ||| The type that a token of this kind converts to.
23 TokType : k -> Type
24
25 ||| Convert a recognised string into a value. The type is determined
26 ||| by the kind of token.
27 tokValue : (kind : k) -> String -> TokType kind
28
29||| A token of a particular kind and the text that was recognised.
30record Token k where
31 constructor Tok
32 kind : k
33 text : String
34
35||| Get the value of a `Token k`. The resulting type depends upon
36||| the kind of token.
37value : TokenKind k => (t : Token k) -> TokType (kind t)
38value (Tok k x) = tokValue k x
yaffle.ipkg+31−0
@@ -0,0 +1,31 @@
1package yaffle
2
3modules =
4 Control.Delayed,
5
6 Core.Core,
7 Core.Env,
8 Core.FC,
9 Core.Name,
10 Core.TT,
11 Core.Value,
12
13 Data.Bool.Extra,
14 Data.StringMap,
15
16 Parser.Lexer,
17 Parser.Support,
18
19 Text.Lexer,
20 Text.Lexer.Core,
21 Text.Parser,
22 Text.Parser.Core,
23 Text.Quantity,
24 Text.Token
25
26sourcedir = src
27-- executable = ttimp
28opts = "--partial-eval"
29
30-- main = TTImp.Main
31

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t