1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ParsingError inp)
3 {-# LANGUAGE TemplateHaskell #-}
4 {-# LANGUAGE UnboxedTuples #-} -- For nextInput
5 {-# LANGUAGE UndecidableInstances #-} -- For Show (ParsingError inp)
6 module Symantic.Parser.Machine.Generate where
8 import Control.Monad (Monad(..))
9 import Data.Bool (Bool)
10 import Data.Char (Char)
11 import Data.Either (Either(..))
12 import Data.Function (($), (.))
13 import Data.Functor ((<$>))
15 import Data.List (minimum)
17 import Data.Maybe (Maybe(..))
18 import Data.Ord (Ord(..), Ordering(..))
19 import Data.Semigroup (Semigroup(..))
21 import Language.Haskell.TH (CodeQ, Code(..))
22 import Prelude (($!), (+), (-))
23 import Text.Show (Show(..))
24 import qualified Data.Map.Strict as Map
25 import qualified Data.Set as Set
26 import qualified Language.Haskell.TH.Syntax as TH
27 -- import qualified Control.Monad.Trans.Writer as Writer
29 import Symantic.Univariant.Trans
30 import Symantic.Parser.Grammar.Combinators (ErrorItem(..))
31 import Symantic.Parser.Machine.Input
32 import Symantic.Parser.Machine.Instructions
33 import qualified Symantic.Parser.Haskell as H
35 genCode :: TermInstr a -> CodeQ a
39 -- | Generate the 'CodeQ' parsing the input.
40 data Gen inp vs es a = Gen
41 { minHorizon :: Map TH.Name Horizon -> Horizon
44 CodeQ (Either (ParsingError inp) a)
47 -- ** Type 'ParsingError'
49 = ParsingErrorStandard
50 { parsingErrorOffset :: Offset
51 -- | Note that if an 'ErrorItemHorizon' greater than 1
52 -- is amongst the 'parsingErrorExpecting'
53 -- then this is only the 'InputToken'
54 -- at the begining of the expected 'Horizon'.
55 , parsingErrorUnexpected :: Maybe (InputToken inp)
56 , parsingErrorExpecting :: Set (ErrorItem (InputToken inp))
58 deriving instance Show (InputToken inp) => Show (ParsingError inp)
64 -- | Synthetized minimal input length
65 -- required for a successful parsing.
66 -- Used with 'horizon' to factorize input length checks,
67 -- instead of checking the input length
68 -- one 'InputToken' by one 'InputToken' at each 'read'.
73 {-farthestInput-}Cursor inp ->
74 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
77 Either (ParsingError inp) a
79 -- ** Type 'SubRoutine'
80 type SubRoutine inp v a =
83 {-ko-}FailHandler inp a ->
84 Either (ParsingError inp) a
86 -- ** Type 'FailHandler'
87 type FailHandler inp a =
88 {-failureInput-}Cursor inp ->
89 {-farthestInput-}Cursor inp ->
90 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
91 Either (ParsingError inp) a
94 -- *** Type 'FarthestError'
95 data FarthestError inp = FarthestError
96 { farthestInput :: Cursor inp
97 , farthestExpecting :: [ErrorItem (InputToken inp)]
101 -- | @('generate' input mach)@ generates @TemplateHaskell@ code
102 -- parsing given 'input' according to given 'mach'ine.
105 Ord (InputToken inp) =>
106 Show (InputToken inp) =>
107 TH.Lift (InputToken inp) =>
108 -- InputToken inp ~ Char =>
112 Gen inp '[] ('Succ 'Zero) ret ->
113 CodeQ (Either (ParsingError inp) ret)
114 generate input k = [||
115 -- Pattern bindings containing unlifted types
116 -- should use an outermost bang pattern.
117 let !(# init, readMore, readNext #) = $$(cursorOf input) in
118 let finalRet = \_farInp _farExp v _inp -> Right v in
119 let finalFail _failInp !farInp !farExp =
120 Left ParsingErrorStandard
121 { parsingErrorOffset = offset farInp
122 , parsingErrorUnexpected =
124 then Just (let (# c, _ #) = readNext farInp in c)
126 , parsingErrorExpecting = Set.fromList farExp
129 { valueStack = ValueStackEmpty
130 , failStack = FailStackCons [||finalFail||] FailStackEmpty
131 , retCode = [||finalRet||]
133 , nextInput = [||readNext||]
134 , moreInput = [||readMore||]
135 -- , farthestError = [||Nothing||]
136 , farthestInput = [||init||]
137 , farthestExpecting = [|| [] ||]
139 , horizonByName = Map.empty
144 -- | This is a context only present at compile-time.
145 data GenCtx inp vs (es::Peano) a =
146 ( TH.Lift (InputToken inp)
147 , Cursorable (Cursor inp)
148 , Show (InputToken inp)
149 -- , InputToken inp ~ Char
151 { valueStack :: ValueStack vs
152 , failStack :: FailStack inp es a
153 , retCode :: CodeQ (Cont inp a a)
154 , input :: CodeQ (Cursor inp)
155 , moreInput :: CodeQ (Cursor inp -> Bool)
156 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
157 , farthestInput :: CodeQ (Cursor inp)
158 , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
159 -- | Remaining horizon
161 -- | Horizon for each 'call' or 'jump'.
162 , horizonByName :: Map TH.Name Offset
165 -- ** Type 'ValueStack'
166 data ValueStack vs where
167 ValueStackEmpty :: ValueStack '[]
169 { valueStackHead :: TermInstr v
170 , valueStackTail :: ValueStack vs
171 } -> ValueStack (v ': vs)
173 -- ** Type 'FailStack'
174 data FailStack inp es a where
175 FailStackEmpty :: FailStack inp 'Zero a
177 { failStackHead :: CodeQ (FailHandler inp a)
178 , failStackTail :: FailStack inp es a
180 FailStack inp ('Succ es) a
182 instance Stackable Gen where
184 { unGen = \ctx -> unGen k ctx
185 { valueStack = ValueStackCons x (valueStack ctx) }
188 { unGen = \ctx -> unGen k ctx
189 { valueStack = valueStackTail (valueStack ctx) }
192 { unGen = \ctx -> unGen k ctx
194 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
195 ValueStackCons (f H.:@ x H.:@ y) xs
199 { unGen = \ctx -> unGen k ctx
201 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
202 ValueStackCons x (ValueStackCons y xs)
205 instance Branchable Gen where
207 { minHorizon = \ls ->
208 minHorizon kx ls `min` minHorizon ky ls
210 let ValueStackCons v vs = valueStack ctx in
212 case $$(genCode v) of
213 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (H.Term [||x||]) vs })
214 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (H.Term [||y||]) vs })
217 choices fs ks kd = Gen
218 { minHorizon = \ls -> minimum $
220 (($ ls) . minHorizon <$> ks)
222 let ValueStackCons v vs = valueStack ctx in
223 go ctx{valueStack = vs} v fs ks
226 go ctx x (f:fs') (k:ks') = [||
227 if $$(genCode (f H.:@ x))
229 else $$(go ctx x fs' ks')
231 go ctx _ _ _ = unGen kd ctx
232 instance Failable Gen where
234 { minHorizon = \_hs -> 0
235 , unGen = \ctx@GenCtx{} -> [||
236 let (# farInp, farExp #) =
237 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
238 LT -> (# $$(input ctx), failExp #)
239 EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
240 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
241 $$(failStackHead (failStack ctx))
242 $$(input ctx) farInp farExp
247 let FailStackCons _e es = failStack ctx in
248 unGen k ctx{failStack = es}
250 catchFail ok ko = Gen
251 { minHorizon = \ls -> minHorizon ok ls `min` minHorizon ko ls
252 , unGen = \ctx@GenCtx{} -> [||
253 let _ = "catchFail" in $$(unGen ok ctx
254 { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
255 -- trace ("catchFail: " <> "farExp="<>show farExp) $
257 -- Push the input as it was when entering the catchFail.
258 { valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)
259 -- Move the input to the failing position.
260 , input = [||failInp||]
261 -- Set the farthestInput to the farthest computed by 'fail'
262 , farthestInput = [||farInp||]
263 , farthestExpecting = [||farExp||]
269 instance Inputable Gen where
272 let ValueStackCons input vs = valueStack ctx in
275 , input = genCode input
281 unGen k ctx{valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)}
283 instance Routinable Gen where
284 call (LetName n) k = k
285 { minHorizon = \hs -> hs Map.! n
286 , unGen = \ctx -> [||
288 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
289 {-ok-}$$(generateSuspend k ctx)
291 $! $$(failStackHead (failStack ctx))
294 jump (LetName n) = Gen
295 { minHorizon = \hs -> hs Map.! n
296 , unGen = \ctx -> [||
298 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
299 {-ok-}$$(retCode ctx)
301 $! $$(failStackHead (failStack ctx))
305 { minHorizon = \_hs -> 0
306 , unGen = \ctx -> unGen (generateResume (retCode ctx)) ctx
308 subroutine (LetName n) sub k = Gen
309 { minHorizon = \hs ->
311 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
312 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
313 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
315 -- Why using $! at call site and not ! here on ko?
318 { valueStack = ValueStackEmpty
319 , failStack = FailStackCons [||ko||] FailStackEmpty
322 -- , farthestInput = [|inp|]
323 -- , farthestExpecting = [|| [] ||]
325 , horizonByName = Map.insert n 0 (horizonByName ctx)
328 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
329 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
333 (Map.insert n 0 (horizonByName ctx)))
336 return (TH.LetE [decl] expr)
339 -- | Generate a continuation to be called with 'generateResume',
340 -- used when 'call' 'ret'urns.
342 {-k-}Gen inp (v ': vs) es a ->
343 GenCtx inp vs es a ->
345 generateSuspend k ctx = [||
347 \farInp farExp v !inp ->
349 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
351 , farthestInput = [||farInp||]
352 , farthestExpecting = [||farExp||]
358 -- | Generate a call to the 'generateSuspend' continuation,
359 -- used when 'call' 'ret'urns.
361 CodeQ (Cont inp v a) ->
362 Gen inp (v ': vs) es a
363 generateResume k = Gen
364 { minHorizon = \_hs -> 0
365 , unGen = \ctx -> [||
368 $$(farthestInput ctx)
369 $$(farthestExpecting ctx)
370 (let _ = "resume.genCode" in $$(genCode (valueStackHead (valueStack ctx))))
375 instance Joinable Gen where
376 defJoin (LetName n) sub k = k
377 { minHorizon = \hs ->
379 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
380 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
381 body <- TH.unTypeQ $ TH.examineCode $ [||
382 \farInp farExp v !inp ->
384 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
386 , farthestInput = [||farInp||]
387 , farthestExpecting = [||farExp||]
389 , horizonByName = Map.insert n 0 (horizonByName ctx)
392 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
393 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
397 (Map.insert n 0 (horizonByName ctx)))
400 return (TH.LetE [decl] expr)
402 refJoin (LetName n) =
403 generateResume (Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
404 instance Readable Gen Char where
405 read farExp p = checkHorizon . checkToken farExp p
408 TH.Lift (InputToken inp) =>
409 {-ok-}Gen inp vs ('Succ es) a ->
410 Gen inp vs ('Succ es) a
412 { minHorizon = \hs -> 1 + minHorizon ok hs
413 , unGen = \ctx0@GenCtx{failStack = FailStackCons e es} -> [||
414 -- Factorize failure code
415 let readFail = $$(e) in
417 let ctx = ctx0{ failStack = FailStackCons [||readFail||] es } in
419 then unGen ok ctx0{horizon = horizon ctx - 1}
420 else let minHoz = minHorizon ok (horizonByName ctx) in
424 then [||$$shiftRight minHoz $$(input ctx)||]
426 then $$(unGen ok ctx{horizon = minHoz})
427 else let _ = "checkHorizon.else" in
428 $$(unGen (fail [ErrorItemHorizon (minHoz + 1)]) ctx)
436 Ord (InputToken inp) =>
437 TH.Lift (InputToken inp) =>
438 [ErrorItem (InputToken inp)] ->
439 {-predicate-}TermInstr (InputToken inp -> Bool) ->
440 {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
441 Gen inp vs ('Succ es) a
442 checkToken farExp p ok = ok
443 { unGen = \ctx -> [||
444 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
447 { valueStack = ValueStackCons (H.Term [||c||]) (valueStack ctx)
450 else let _ = "checkToken.else" in $$(unGen (fail farExp) ctx)