1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ParsingError inp)
3 {-# LANGUAGE ConstraintKinds #-} -- For Dict
4 {-# LANGUAGE TemplateHaskell #-}
5 {-# LANGUAGE TupleSections #-}
6 {-# LANGUAGE UnboxedTuples #-} -- For nextInput
7 {-# LANGUAGE UndecidableInstances #-} -- For Show (ParsingError inp)
8 {-# OPTIONS_GHC -fno-warn-orphans #-}
9 module Symantic.Parser.Machine.Generate where
11 import Control.Monad (Monad(..))
12 import Data.Bool (Bool)
13 import Data.Char (Char)
14 import Data.Either (Either(..), either)
15 import Data.Function (($), (.), id, const, on)
16 import Data.Functor (Functor, (<$>), (<$))
17 import Data.Foldable (foldMap', toList, null)
19 import Data.List.NonEmpty (NonEmpty(..))
21 import Data.Maybe (Maybe(..))
22 import Data.Eq (Eq(..))
23 import Data.Ord (Ord(..), Ordering(..))
24 import Data.Proxy (Proxy(..))
25 import Data.Semigroup (Semigroup(..))
27 import Data.String (String)
28 import Data.Traversable (Traversable(..))
29 import Data.Typeable (Typeable)
30 import Language.Haskell.TH (CodeQ)
31 import Prelude ((+), (-), error)
32 import Text.Show (Show(..))
33 -- import qualified Control.Monad.Trans.State.Strict as MT
34 import qualified Data.HashMap.Strict as HM
35 import qualified Data.List as List
36 import qualified Data.List.NonEmpty as NE
37 import qualified Data.Map.Internal as Map_
38 import qualified Data.Set.Internal as Set_
39 import qualified Data.Map.Strict as Map
40 import qualified Data.Set as Set
41 import qualified Language.Haskell.TH as TH
42 import qualified Language.Haskell.TH.Syntax as TH
44 import Symantic.Univariant.Letable
45 import Symantic.Univariant.Trans
46 import Symantic.Parser.Grammar.Combinators (Exception(..), Failure(..), SomeFailure(..), inputTokenProxy)
47 import Symantic.Parser.Machine.Input
48 import Symantic.Parser.Machine.Instructions
49 import qualified Language.Haskell.TH.HideName as TH
50 import qualified Symantic.Parser.Haskell as H
54 genCode :: TermInstr a -> CodeQ a
58 -- | Generate the 'CodeQ' parsing the input.
59 data Gen inp vs a = Gen
60 { genAnalysisByLet :: LetMapFix (CallTrace -> GenAnalysis)
61 -- ^ 'genAnalysis' for each 'defLet' and 'defJoin' of the 'Machine'.
62 , genAnalysis :: LetMapTo (CallTrace -> GenAnalysis)
63 -- ^ Synthetized (bottom-up) static genAnalysis of the 'Machine'.
66 CodeQ (Either (ParsingError inp) a)
69 -- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
70 -- parsing the given 'input' according to the given 'Machine'.
72 Ord (InputToken inp) =>
73 Show (InputToken inp) =>
74 Typeable (InputToken inp) =>
75 TH.Lift (InputToken inp) =>
76 -- InputToken inp ~ Char =>
80 CodeQ (inp -> Either (ParsingError inp) a)
81 generateCode k = [|| \(input :: inp) ->
82 -- Pattern bindings containing unlifted types
83 -- should use an outermost bang pattern.
84 let !(# init, readMore, readNext #) = $$(cursorOf [||input||])
85 finalRet = \_farInp _farExp v _inp -> Right v
86 finalRaise :: forall b. (Catcher inp b)
87 = \ !exn _failInp !farInp !farExp ->
88 Left ParsingErrorStandard
89 { parsingErrorOffset = offset farInp
90 , parsingErrorException = exn
91 , parsingErrorUnexpected =
93 then Just (let (# c, _ #) = readNext farInp in c)
95 , parsingErrorExpecting = farExp
99 let defInputTokenProxy exprCode =
100 TH.unsafeCodeCoerce $ do
101 value <- TH.unTypeQ $ TH.examineCode [||Proxy :: Proxy (InputToken inp)||]
102 expr <- TH.unTypeQ (TH.examineCode exprCode)
104 TH.FunD inputTokenProxy [TH.Clause [] (TH.NormalB value) []]
106 in defInputTokenProxy $
108 { valueStack = ValueStackEmpty
109 , catchStackByLabel = Map.empty
110 , defaultCatch = [||finalRaise||]
112 , retCode = [||finalRet||]
114 , nextInput = [||readNext||]
115 , moreInput = [||readMore||]
116 -- , farthestError = [||Nothing||]
117 , farthestInput = [||init||]
118 , farthestExpecting = [||Set.empty||]
121 , finalGenAnalysisByLet = runGenAnalysis (genAnalysisByLet k)
127 -- ** Type 'ParsingError'
128 data ParsingError inp
129 = ParsingErrorStandard
130 { parsingErrorOffset :: Offset
131 , parsingErrorException :: Exception
132 -- | Note that if an 'FailureHorizon' greater than 1
133 -- is amongst the 'parsingErrorExpecting'
134 -- then this is only the 'InputToken'
135 -- at the begining of the expected 'Horizon'.
136 , parsingErrorUnexpected :: Maybe (InputToken inp)
137 , parsingErrorExpecting :: Set SomeFailure
139 deriving instance Show (InputToken inp) => Show (ParsingError inp)
141 -- ** Type 'ErrorLabel'
142 type ErrorLabel = String
144 -- * Type 'GenAnalysis'
145 data GenAnalysis = GenAnalysis
146 { minReads :: Either Exception Horizon
147 , mayRaise :: Map Exception ()
150 -- | Tie the knot between mutually recursive 'TH.Name's
151 -- introduced by 'defLet' and 'defJoin'.
152 -- and provide the empty initial 'CallTrace' stack
154 LetMapFix (CallTrace -> GenAnalysis) ->
156 runGenAnalysis ga = (($ []) <$>) $ polyfix ga
158 -- | Poly-variadic fixpoint combinator.
159 -- Used to express mutual recursion and to transparently introduce memoization.
160 -- Used to "tie the knot" between observed sharing ('defLet', 'call', 'jump')
161 -- and join points ('defJoin', 'refJoin').
162 -- All mutually dependent functions are restricted to the same polymorphic type @(a)@.
163 -- See http://okmij.org/ftp/Computation/fixed-point-combinators.html#Poly-variadic
164 polyfix :: Functor f => f (f a -> a) -> f a
165 polyfix fs = fix $ \finals -> ($ finals) <$> fs
168 fix f = final where final = f final
170 type LetMap = HM.HashMap TH.Name
171 type LetMapTo a = LetMap a -> a
172 type LetMapFix a = LetMap (LetMap a -> a)
174 -- | Call trace stack updated by 'call' and 'refJoin'.
175 -- Used to avoid infinite loops when tying the knot with 'polyfix'.
176 type CallTrace = [TH.Name]
181 -- | Minimal input length required for a successful parsing.
182 type Horizon = Offset
185 -- altGenAnalysis = List.foldl' (\acc x -> either Left (\h -> Right (either (const h) (min h) acc)) x)
186 seqGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
187 seqGenAnalysis aas@(a:|as) = GenAnalysis
188 { minReads = List.foldl' (\acc x ->
189 acc >>= \r -> (r +) <$> minReads x
191 , mayRaise = sconcat (mayRaise <$> aas)
193 altGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
194 altGenAnalysis aas@(a:|as) = GenAnalysis
195 { minReads = List.foldl' (\acc x ->
197 (\l -> either (const (Left l)) Right)
198 (\r -> either (const (Right r)) (Right . min r))
201 , mayRaise = sconcat (mayRaise <$> aas)
206 -- *** Type 'FarthestError'
207 data FarthestError inp = FarthestError
208 { farthestInput :: Cursor inp
209 , farthestExpecting :: [Failure (InputToken inp)]
214 -- | This is an inherited (top-down) context
215 -- only present at compile-time, to build TemplateHaskell splices.
216 data GenCtx inp vs a =
217 ( TH.Lift (InputToken inp)
218 , Cursorable (Cursor inp)
219 , Show (InputToken inp)
221 { valueStack :: ValueStack vs
222 , catchStackByLabel :: Map Exception (NonEmpty (CodeQ (Catcher inp a)))
223 -- | Default 'Catcher' defined at the begining of the generated 'CodeQ',
224 -- hence a constant within the 'Gen'eration.
225 , defaultCatch :: forall b. CodeQ (Catcher inp b)
226 -- | Used by 'checkToken' to get 'GenAnalysis' from 'genAnalysis'.
227 , callStack :: [TH.Name]
228 , retCode :: CodeQ (Cont inp a a)
229 , input :: CodeQ (Cursor inp)
230 , moreInput :: CodeQ (Cursor inp -> Bool)
231 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
232 , farthestInput :: CodeQ (Cursor inp)
233 , farthestExpecting :: CodeQ (Set SomeFailure)
234 -- | Remaining horizon already checked.
235 -- Use to factorize 'input' length checks,
236 -- instead of checking the 'input' length
237 -- one 'InputToken' at a time at each 'read'.
238 -- Updated by 'checkHorizon'
239 -- and reset elsewhere when needed.
240 , checkedHorizon :: Horizon
241 -- | Used by 'pushInput' and 'loadInput'
242 -- to restore the 'Horizon' at the restored 'input'.
243 , horizonStack :: [Horizon]
244 -- | Output of 'runGenAnalysis'.
245 , finalGenAnalysisByLet :: LetMap GenAnalysis
248 -- ** Type 'ValueStack'
249 data ValueStack vs where
250 ValueStackEmpty :: ValueStack '[]
252 { valueStackHead :: TermInstr v
253 , valueStackTail :: ValueStack vs
254 } -> ValueStack (v ': vs)
256 instance InstrValuable Gen where
258 { unGen = \ctx -> {-trace "unGen.pushValue" $-} unGen k ctx
259 { valueStack = ValueStackCons x (valueStack ctx) }
262 { unGen = \ctx -> {-trace "unGen.popValue" $-} unGen k ctx
263 { valueStack = valueStackTail (valueStack ctx) }
266 { unGen = \ctx -> {-trace "unGen.lift2Value" $-} unGen k ctx
268 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
269 ValueStackCons (f H.:@ x H.:@ y) vs
273 { unGen = \ctx -> {-trace "unGen.swapValue" $-} unGen k ctx
275 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
276 ValueStackCons x (ValueStackCons y vs)
279 instance InstrBranchable Gen where
280 caseBranch kx ky = Gen
281 { genAnalysisByLet = genAnalysisByLet kx <> genAnalysisByLet ky
282 , genAnalysis = \final ct -> altGenAnalysis $ genAnalysis kx final ct :| [genAnalysis ky final ct]
283 , unGen = \ctx -> {-trace "unGen.caseBranch" $-}
284 let ValueStackCons v vs = valueStack ctx in
286 case $$(genCode v) of
287 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (H.Term [||x||]) vs })
288 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (H.Term [||y||]) vs })
291 choicesBranch fs ks kd = Gen
292 { genAnalysisByLet = sconcat $ genAnalysisByLet kd :| (genAnalysisByLet <$> ks)
293 , genAnalysis = \final ct -> altGenAnalysis $ (\k -> genAnalysis k final ct) <$> (kd:|ks)
294 , unGen = \ctx -> {-trace "unGen.choicesBranch" $-}
295 let ValueStackCons v vs = valueStack ctx in
296 go ctx{valueStack = vs} v fs ks
299 go ctx x (f:fs') (k:ks') = [||
300 if $$(genCode (H.optimizeTerm (f H.:@ x)))
302 let _ = "choicesBranch.then" in
303 $$({-trace "unGen.choicesBranch.k" $-} unGen k ctx)
305 let _ = "choicesBranch.else" in
308 go ctx _ _ _ = unGen kd ctx
309 instance InstrExceptionable Gen where
311 { genAnalysisByLet = HM.empty
312 , genAnalysis = \_final _ct -> GenAnalysis
313 { minReads = Left (ExceptionLabel exn)
314 , mayRaise = Map.singleton (ExceptionLabel exn) ()
316 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.raise: "<>show exn) $-} [||
317 $$(NE.head $ Map.findWithDefault
318 (NE.singleton (defaultCatch ctx))
319 (ExceptionLabel exn) (catchStackByLabel ctx))
320 (ExceptionLabel $$(TH.liftTyped exn))
321 {-failInp-}$$(input ctx)
322 {-farInp-}$$(input ctx)
323 $$(farthestExpecting ctx)
327 { genAnalysisByLet = HM.empty
328 , genAnalysis = \_final _ct -> GenAnalysis
329 { minReads = Left ExceptionFailure
330 , mayRaise = Map.singleton ExceptionFailure ()
332 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.fail: "<>show exn) $-}
335 $$(NE.head $ Map.findWithDefault
336 (NE.singleton (defaultCatch ctx))
337 ExceptionFailure (catchStackByLabel ctx))
339 {-failInp-}$$(input ctx)
340 $$(farthestInput ctx)
341 $$(farthestExpecting ctx)
343 else raiseCode ctx [||fs||]
346 { unGen = \ctx -> {-trace ("unGen.commit: "<>show exn) $-}
347 unGen k ctx{catchStackByLabel =
349 _r0:|(r1:rs) -> Just (r1:|rs)
352 exn (catchStackByLabel ctx)
355 catch exn ok ko = Gen
356 { genAnalysisByLet = genAnalysisByLet ok <> genAnalysisByLet ko
357 , genAnalysis = \final ct ->
358 let ga = altGenAnalysis $ genAnalysis ok final ct :| [ genAnalysis ko final ct ] in
359 ga { mayRaise = Map.delete exn (mayRaise ga) }
360 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.catch: "<>show exn) $-} [||
361 let _ = $$(liftTypedString ("catch "<>show exn)) in
362 let catchHandler !_exn !failInp !farInp !farExp =
363 let _ = $$(liftTypedString ("catch.ko "<>show exn)) in
364 $$({-trace ("unGen.catch.ko: "<>show exn) $-} unGen ko ctx
365 -- Push 'input' and 'checkedHorizon'
366 -- as they were when entering 'catch'.
368 ValueStackCons (H.Term (input ctx)) $
369 --ValueStackCons (H.Term [||exn||]) $
372 checkedHorizon ctx : horizonStack ctx
373 -- Note that 'catchStackByLabel' is reset.
374 -- Move the input to the failing position.
375 , input = [||failInp||]
376 -- The 'checkedHorizon' at the 'raise's
377 -- are not known here.
378 -- Nor whether 'failInp' is after
379 -- 'checkedHorizon' 'ctx' or not.
381 -- Set the farthestInput to the farthest computed by 'fail'.
382 , farthestInput = [||farInp||]
383 , farthestExpecting = [||farExp||]
386 $$({-trace ("unGen.catch.ok: "<>show es) $-} unGen ok ctx
387 { catchStackByLabel =
388 Map.insertWith (<>) exn
389 (NE.singleton [||catchHandler||])
390 (catchStackByLabel ctx)
398 {-failInp-}Cursor inp ->
399 {-farInp-}Cursor inp ->
400 {-farExp-}(Set SomeFailure) ->
401 Either (ParsingError inp) a
402 instance InstrInputable Gen where
405 {-trace "unGen.pushInput" $-}
407 { valueStack = H.Term (input ctx) `ValueStackCons` valueStack ctx
408 , horizonStack = checkedHorizon ctx : horizonStack ctx
413 {-trace "unGen.loadInput" $-}
414 let ValueStackCons input vs = valueStack ctx in
415 let (h, hs) = case horizonStack ctx of
421 , input = genCode input
424 , genAnalysis = \final ct -> GenAnalysis
425 { minReads = 0 <$ minReads (genAnalysis k final ct)
426 , mayRaise = mayRaise (genAnalysis k final ct)
429 instance InstrCallable Gen where
431 { unGen = \ctx@GenCtx{} ->
432 {-trace ("unGen.defLet: defs="<>show (HM.keys defs)) $-}
433 TH.unsafeCodeCoerce $ do
434 decls <- traverse (makeDecl ctx) (HM.toList defs)
435 body <- TH.unTypeQ $ TH.examineCode $
436 {-trace "unGen.defLet.body" $-}
439 -- | Try to output more deterministic code to be able to golden test it,
440 -- at the cost of more computations (at compile-time only though).
441 List.sortBy (compare `on` TH.hideName) $
445 foldMap' (\(SomeLet sub) -> genAnalysisByLet sub) defs <>
446 ((\(SomeLet sub) -> genAnalysis sub) <$> defs) <>
450 makeDecl ctx (n, SomeLet sub) = do
451 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
452 -- Called by 'call' or 'jump'.
453 \ !ok{-from generateSuspend or retCode-}
455 !koByLabel{- 'catchStackByLabel' from the 'call'-site -} ->
456 $$({-trace ("unGen.defLet.sub: "<>show n) $-} unGen sub ctx
457 { valueStack = ValueStackEmpty
458 -- Build a 'catchStackByLabel' from the one available at the 'call'-site.
459 -- Note that all the 'mayRaise' of the 'sub'routine may not be available,
460 -- hence 'Map.findWithDefault' is used instead of 'Map.!'.
461 , catchStackByLabel = Map.mapWithKey
462 (\lbl () -> NE.singleton [||Map.findWithDefault $$(defaultCatch ctx) lbl koByLabel||])
463 ({-trace ("mayRaise: "<>show n) $-}
464 mayRaise (finalGenAnalysisByLet ctx HM.! n))
466 , retCode = {-trace ("unGen.defLet.sub.retCode: "<>show n) $-} [||ok||]
468 -- These are passed by the caller via 'ok' or 'ko'
470 -- , farthestExpecting =
472 -- Some callers can call this 'defLet'
473 -- with zero 'checkedHorizon', hence use this minimum.
474 -- TODO: maybe it could be improved a bit
475 -- by taking the minimum of the checked horizons
476 -- before all the 'call's and 'jump's to this 'defLet'.
480 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
482 jump (LetName n) = Gen
483 { genAnalysisByLet = HM.empty
484 , genAnalysis = \final ct ->
488 , mayRaise = Map.empty
490 else (final HM.! n) (n:ct)
491 , unGen = \ctx -> {-trace ("unGen.jump: "<>show n) $-} [||
493 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
494 {-ok-}$$(retCode ctx)
496 $$(liftTypedRaiseByLabel $
497 catchStackByLabel ctx
498 -- Pass only the labels raised by the 'defLet'.
500 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
504 call (LetName n) k = k
505 { genAnalysis = \final ct ->
509 , mayRaise = Map.empty
511 else seqGenAnalysis $
512 (final HM.! n) (n:ct) :|
513 [ genAnalysis k final ct ]
514 , unGen = {-trace ("unGen.call: "<>show n) $-} \ctx ->
515 -- let ks = (Map.keys (catchStackByLabel ctx)) in
517 -- let _ = $$(liftTypedString $ "call exceptByLet("<>show n<>")="<>show (Map.keys (Map.findWithDefault Map.empty n (exceptByLet ctx))) <> " catchStackByLabel(ctx)="<> show ks) in
518 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
519 {-ok-}$$(generateSuspend k ctx{callStack = n : callStack ctx})
521 $$(liftTypedRaiseByLabel $
522 catchStackByLabel ctx
523 -- Pass only the labels raised by the 'defLet'.
525 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
530 { genAnalysisByLet = HM.empty
531 , genAnalysis = \_final _ct -> GenAnalysis
533 , mayRaise = Map.empty
535 , unGen = \ctx -> {-trace "unGen.ret" $-} unGen ({-trace "unGen.ret.generateResume" $-} generateResume ({-trace "unGen.ret.retCode" $-} retCode ctx)) ctx
538 -- | Like 'TH.liftString' but on 'TH.Code'.
539 -- Useful to get a 'TH.StringL' instead of a 'TH.ListE'.
540 liftTypedString :: String -> TH.Code TH.Q a
541 liftTypedString = TH.unsafeCodeCoerce . TH.liftString
543 -- | Like 'TH.liftTyped' but adjusted to work on 'catchStackByLabel'
544 -- which already contains 'CodeQ' terms.
545 -- Moreover, only the 'Catcher' at the top of the stack
546 -- is needed and thus generated in the resulting 'CodeQ'.
548 -- TODO: Use an 'Array' instead of a 'Map'?
549 liftTypedRaiseByLabel :: TH.Lift k => Map k (NonEmpty (CodeQ a)) -> CodeQ (Map k a)
550 liftTypedRaiseByLabel Map_.Tip = [|| Map_.Tip ||]
551 liftTypedRaiseByLabel (Map_.Bin s k (h:|_hs) l r) =
552 [|| Map_.Bin s k $$h $$(liftTypedRaiseByLabel l) $$(liftTypedRaiseByLabel r) ||]
554 instance TH.Lift a => TH.Lift (Set a) where
555 liftTyped Set_.Tip = [|| Set_.Tip ||]
556 liftTyped (Set_.Bin s a l r) = [|| Set_.Bin $$(TH.liftTyped s) $$(TH.liftTyped a) $$(TH.liftTyped l) $$(TH.liftTyped r) ||]
560 {-farthestInput-}Cursor inp ->
561 {-farthestExpecting-}(Set SomeFailure) ->
564 Either (ParsingError inp) a
566 -- | Generate a 'retCode' 'Cont'inuation to be called with 'generateResume'.
567 -- Used when 'call' 'ret'urns.
568 -- The return 'v'alue is 'pushValue'-ed on the 'valueStack'.
570 {-k-}Gen inp (v ': vs) a ->
573 generateSuspend k ctx = [||
574 let _ = $$(liftTypedString $ "suspend") in
575 \farInp farExp v !inp ->
576 $$({-trace "unGen.generateSuspend" $-} unGen k ctx
577 { valueStack = ValueStackCons ({-trace "unGen.generateSuspend.value" $-} H.Term [||v||]) (valueStack ctx)
579 , farthestInput = [||farInp||]
580 , farthestExpecting = [||farExp||]
586 -- | Generate a call to the 'generateSuspend' continuation.
587 -- Used when 'call' 'ret'urns.
589 CodeQ (Cont inp v a) ->
591 generateResume k = Gen
592 { genAnalysisByLet = HM.empty
593 , genAnalysis = \_final _ct -> GenAnalysis
595 , mayRaise = Map.empty
597 , unGen = \ctx -> {-trace "unGen.generateResume" $-} [||
600 $$(farthestInput ctx)
601 $$(farthestExpecting ctx)
602 (let _ = "resume.genCode" in $$({-trace "unGen.generateResume.genCode" $-} genCode $ H.optimizeTerm $
603 valueStackHead $ valueStack ctx))
608 instance InstrJoinable Gen where
609 defJoin (LetName n) sub k = k
612 {-trace ("unGen.defJoin: "<>show n) $-}
613 TH.unsafeCodeCoerce $ do
614 next <- TH.unTypeQ $ TH.examineCode $ [||
615 -- Called by 'generateResume'.
616 \farInp farExp v !inp ->
617 $$({-trace ("unGen.defJoin.next: "<>show n) $-} unGen sub ctx
618 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
620 , farthestInput = [||farInp||]
621 , farthestExpecting = [||farExp||]
624 , catchStackByLabel = Map.mapWithKey
625 (\lbl () -> NE.singleton [||koByLabel Map.! lbl||])
626 (mayRaise sub raiseLabelsByLetButSub)
630 let decl = TH.FunD n [TH.Clause [] (TH.NormalB next) []]
631 expr <- TH.unTypeQ (TH.examineCode ({-trace ("unGen.defJoin.expr: "<>show n) $-} unGen k ctx))
632 return (TH.LetE [decl] expr)
634 (genAnalysisByLet sub <>) $
635 HM.insert n (genAnalysis sub) $
638 refJoin (LetName n) = Gen
640 {-trace ("unGen.refJoin: "<>show n) $-}
641 unGen (generateResume
642 (TH.unsafeCodeCoerce (return (TH.VarE n)))) ctx
643 , genAnalysisByLet = HM.empty
644 , genAnalysis = \final ct ->
645 if n`List.elem`ct -- FIXME: useless
648 , mayRaise = Map.empty
650 else HM.findWithDefault
651 (error (show (n,ct,HM.keys final)))
654 instance InstrReadable Char Gen where
655 read fs p = checkHorizon . checkToken fs p
659 Eq (InputToken inp) =>
660 Ord (InputToken inp) =>
661 Typeable (InputToken inp) =>
662 TH.Lift (InputToken inp) =>
663 {-ok-}Gen inp vs a ->
666 { genAnalysis = \final ct -> seqGenAnalysis $
667 GenAnalysis { minReads = Right 1
668 , mayRaise = Map.singleton ExceptionFailure ()
670 [ genAnalysis ok final ct ]
671 , unGen = \ctx0@GenCtx{} ->
672 {-trace "unGen.checkHorizon" $-}
674 NE.head (Map.findWithDefault
675 (NE.singleton (defaultCatch ctx0))
676 ExceptionFailure (catchStackByLabel ctx0)) in
678 -- Factorize generated code for raising the "fail".
679 let readFail = $$(raiseFail) in
681 let ctx = ctx0{catchStackByLabel =
682 Map.adjust (\(_r:|rs) -> [||readFail||] :| rs)
683 ExceptionFailure (catchStackByLabel ctx0)} in
684 if checkedHorizon ctx >= 1
685 then unGen ok ctx0{checkedHorizon = checkedHorizon ctx - 1}
687 either (\err -> 0) id $
688 minReads $ finalGenAnalysis ctx ok in
692 then [||$$shiftRight minHoriz $$(input ctx)||]
694 then $$(unGen ok ctx{checkedHorizon = minHoriz})
695 else let _ = "checkHorizon.else" in
696 -- TODO: return a resuming continuation (eg. Partial)
697 $$(unGen (fail (Set.singleton $ SomeFailure $ FailureHorizon @(InputToken inp) (minHoriz + 1))) ctx)
704 Cursorable (Cursor inp) =>
706 TH.CodeQ (Set SomeFailure) ->
707 TH.CodeQ (Either (ParsingError inp) a)
708 raiseCode ctx fs = [||
710 (# farInp, farExp #) =
711 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
712 LT -> (# $$(input ctx), failExp #)
713 EQ -> (# $$(farthestInput ctx), failExp <> $$(farthestExpecting ctx) #)
714 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #)
715 in $$(NE.head $ Map.findWithDefault
716 (NE.singleton (defaultCatch ctx))
717 ExceptionFailure (catchStackByLabel ctx))
719 {-failInp-}$$(input ctx) farInp farExp
722 finalGenAnalysis :: GenCtx inp vs a -> Gen inp cs a -> GenAnalysis
723 finalGenAnalysis ctx k =
724 --(\f -> f (error "callTrace")) $
725 (\f -> f (callStack ctx)) $
727 ((\f _ct -> f) <$>) $
728 finalGenAnalysisByLet ctx
731 Ord (InputToken inp) =>
732 TH.Lift (InputToken inp) =>
734 {-predicate-}TermInstr (InputToken inp -> Bool) ->
735 {-ok-}Gen inp (InputToken inp ': vs) a ->
737 checkToken fs p ok = ok
738 { unGen = \ctx -> {-trace "unGen.read" $-} [||
739 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
742 { valueStack = ValueStackCons (H.Term [||c||]) (valueStack ctx)
745 else let _ = "checkToken.else" in
746 $$(unGen (fail fs) ctx)