1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DeriveAnyClass #-} -- For NFData instances
3 {-# LANGUAGE DeriveGeneric #-} -- For NFData instances
4 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ParsingError inp)
5 {-# LANGUAGE ConstraintKinds #-} -- For Dict
6 {-# LANGUAGE TemplateHaskell #-}
7 {-# LANGUAGE TupleSections #-}
8 {-# LANGUAGE UnboxedTuples #-} -- For nextInput
9 {-# LANGUAGE UndecidableInstances #-} -- For Show (ParsingError inp)
10 {-# OPTIONS_GHC -fno-warn-orphans #-}
11 module Symantic.Parser.Machine.Generate where
13 import Control.DeepSeq (NFData(..))
14 import Control.Monad (Monad(..))
15 import Data.Bool (Bool)
16 import Data.Char (Char)
17 import Data.Either (Either(..), either)
18 import Data.Eq (Eq(..))
19 import Data.Foldable (foldMap', toList, null)
20 import Data.Function (($), (.), id, const, on)
21 import Data.Functor (Functor, (<$>), (<$))
23 import Data.List.NonEmpty (NonEmpty(..))
25 import Data.Maybe (Maybe(..))
26 import Data.Ord (Ord(..), Ordering(..))
27 import Data.Proxy (Proxy(..))
28 import Data.Semigroup (Semigroup(..))
30 import Data.String (String)
31 import Data.Traversable (Traversable(..))
32 import Data.Typeable (Typeable)
33 import Data.Word (Word8)
34 import GHC.Generics (Generic)
35 import Language.Haskell.TH (CodeQ)
36 import Prelude ((+), (-), error)
37 import Text.Show (Show(..))
38 import qualified Data.HashMap.Strict as HM
39 import qualified Data.List as List
40 import qualified Data.List.NonEmpty as NE
41 import qualified Data.Map.Internal as Map_
42 import qualified Data.Map.Strict as Map
43 import qualified Data.Set as Set
44 import qualified Data.Set.Internal as Set_
45 import qualified Language.Haskell.TH as TH
46 import qualified Language.Haskell.TH.Syntax as TH
48 import Symantic.Typed.Letable
49 import Symantic.Typed.Trans
50 import Symantic.Typed.Optim
51 import Symantic.Parser.Grammar.Combinators (Exception(..), Failure(..), SomeFailure(..), inputTokenProxy)
52 import Symantic.Parser.Machine.Input
53 import Symantic.Parser.Machine.Instructions
54 import qualified Language.Haskell.TH.HideName as TH
55 import qualified Symantic.Typed.Lang as Prod
59 genCode :: Splice a -> CodeQ a
63 -- | Generate the 'CodeQ' parsing the input.
64 data Gen inp vs a = Gen
65 { genAnalysisByLet :: LetMapFix (CallTrace -> GenAnalysis)
66 -- ^ 'genAnalysis' for each 'defLet' and 'defJoin' of the 'Machine'.
67 , genAnalysis :: LetMapTo (CallTrace -> GenAnalysis)
68 -- ^ Synthetized (bottom-up) static genAnalysis of the 'Machine'.
71 CodeQ (Either (ParsingError inp) a)
74 -- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
75 -- parsing the given 'input' according to the given 'Machine'.
78 Eq (InputToken inp) =>
79 NFData (InputToken inp) =>
80 Show (InputToken inp) =>
81 Typeable (InputToken inp) =>
82 TH.Lift (InputToken inp) =>
84 -- InputToken inp ~ Char =>
88 CodeQ (inp -> Either (ParsingError inp) a)
89 generateCode k = [|| \(input :: inp) ->
90 -- Pattern bindings containing unlifted types
91 -- should use an outermost bang pattern.
92 let !(# init, readMore, readNext #) = $$(cursorOf [||input||])
93 finalRet = \_farInp _farExp v _inp -> Right v
94 finalRaise :: forall b. (Catcher inp b)
95 = \ !exn _failInp !farInp !farExp ->
96 Left ParsingErrorStandard
97 { parsingErrorOffset = offset farInp
98 , parsingErrorException = exn
99 , parsingErrorUnexpected =
101 then Just (let (# c, _ #) = readNext farInp in c)
103 , parsingErrorExpecting = farExp
107 let defInputTokenProxy exprCode =
108 TH.unsafeCodeCoerce $ do
109 value <- TH.unTypeQ $ TH.examineCode [||Proxy :: Proxy (InputToken inp)||]
110 expr <- TH.unTypeQ (TH.examineCode exprCode)
112 TH.FunD inputTokenProxy [TH.Clause [] (TH.NormalB value) []]
114 in defInputTokenProxy $
116 { valueStack = ValueStackEmpty
117 , catchStackByLabel = Map.empty
118 , defaultCatch = [||finalRaise||]
120 , retCode = [||finalRet||]
122 , nextInput = [||readNext||]
123 , moreInput = [||readMore||]
124 -- , farthestError = [||Nothing||]
125 , farthestInput = [||init||]
126 , farthestExpecting = [||Set.empty||]
129 , finalGenAnalysisByLet = runGenAnalysis (genAnalysisByLet k)
135 -- ** Type 'ParsingError'
136 data ParsingError inp
137 = ParsingErrorStandard
138 { parsingErrorOffset :: Offset
139 , parsingErrorException :: Exception
140 -- | Note that if an 'FailureHorizon' greater than 1
141 -- is amongst the 'parsingErrorExpecting'
142 -- then this is only the 'InputToken'
143 -- at the begining of the expected 'Horizon'.
144 , parsingErrorUnexpected :: Maybe (InputToken inp)
145 , parsingErrorExpecting :: Set SomeFailure
147 deriving instance Show (InputToken inp) => Show (ParsingError inp)
148 deriving instance NFData (InputToken inp) => NFData (ParsingError inp)
150 -- ** Type 'ErrorLabel'
151 type ErrorLabel = String
153 -- * Type 'GenAnalysis'
154 data GenAnalysis = GenAnalysis
155 { minReads :: Either Exception Horizon
156 , mayRaise :: Map Exception ()
159 -- | Tie the knot between mutually recursive 'TH.Name's
160 -- introduced by 'defLet' and 'defJoin'.
161 -- and provide the empty initial 'CallTrace' stack
163 LetMapFix (CallTrace -> GenAnalysis) ->
165 runGenAnalysis ga = (($ []) <$>) $ polyfix ga
167 -- | Poly-variadic fixpoint combinator.
168 -- Used to express mutual recursion and to transparently introduce memoization,
169 -- more precisely to "tie the knot"
170 -- between observed sharing ('defLet', 'call', 'jump')
171 -- and also between join points ('defJoin', 'refJoin').
172 -- Because it's enough for its usage here,
173 -- all mutually dependent functions are restricted to the same polymorphic type @(a)@.
174 -- See http://okmij.org/ftp/Computation/fixed-point-combinators.html#Poly-variadic
175 polyfix :: Functor f => f (f a -> a) -> f a
176 polyfix fs = fix $ \finals -> ($ finals) <$> fs
179 fix f = final where final = f final
181 type LetMap = HM.HashMap TH.Name
182 type LetMapTo a = LetMap a -> a
183 type LetMapFix a = LetMap (LetMap a -> a)
185 -- | Call trace stack updated by 'call' and 'refJoin'.
186 -- Used to avoid infinite loops when tying the knot with 'polyfix'.
187 type CallTrace = [TH.Name]
192 -- | Minimal input length required for a successful parsing.
193 type Horizon = Offset
195 -- altGenAnalysis = List.foldl' (\acc x -> either Left (\h -> Right (either (const h) (min h) acc)) x)
196 -- | Merge given 'GenAnalysis' as sequences.
197 seqGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
198 seqGenAnalysis aas@(a:|as) = GenAnalysis
199 { minReads = List.foldl' (\acc x ->
200 acc >>= \r -> (r +) <$> minReads x
202 , mayRaise = sconcat (mayRaise <$> aas)
204 -- | Merge given 'GenAnalysis' as alternatives.
205 altGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
206 altGenAnalysis aas@(a:|as) = GenAnalysis
207 { minReads = List.foldl' (\acc x ->
209 (\l -> either (const (Left l)) Right)
210 (\r -> either (const (Right r)) (Right . min r))
213 , mayRaise = sconcat (mayRaise <$> aas)
218 -- *** Type 'FarthestError'
219 data FarthestError inp = FarthestError
220 { farthestInput :: Cursor inp
221 , farthestExpecting :: [Failure (InputToken inp)]
226 -- | This is an inherited (top-down) context
227 -- only present at compile-time, to build TemplateHaskell splices.
228 data GenCtx inp vs a =
229 ( Cursorable (Cursor inp)
231 , TH.Lift (InputToken inp)
232 , Show (InputToken inp)
233 , Eq (InputToken inp)
234 , Typeable (InputToken inp)
235 , NFData (InputToken inp)
238 { valueStack :: ValueStack vs
239 , catchStackByLabel :: Map Exception (NonEmpty (CodeQ (Catcher inp a)))
240 -- | Default 'Catcher' defined at the begining of the generated 'CodeQ',
241 -- hence a constant within the 'Gen'eration.
242 , defaultCatch :: forall b. CodeQ (Catcher inp b)
243 -- | Used by 'checkToken' to get 'GenAnalysis' from 'genAnalysis'.
244 , callStack :: [TH.Name]
245 , retCode :: CodeQ (Cont inp a a)
246 , input :: CodeQ (Cursor inp)
247 , moreInput :: CodeQ (Cursor inp -> Bool)
248 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
249 , farthestInput :: CodeQ (Cursor inp)
250 , farthestExpecting :: CodeQ (Set SomeFailure)
251 -- | Remaining horizon already checked.
252 -- Use to factorize 'input' length checks,
253 -- instead of checking the 'input' length
254 -- one 'InputToken' at a time at each 'read'.
255 -- Updated by 'checkHorizon'
256 -- and reset elsewhere when needed.
257 , checkedHorizon :: Horizon
258 -- | Used by 'pushInput' and 'loadInput'
259 -- to restore the 'Horizon' at the restored 'input'.
260 , horizonStack :: [Horizon]
261 -- | Output of 'runGenAnalysis'.
262 , finalGenAnalysisByLet :: LetMap GenAnalysis
265 -- ** Type 'ValueStack'
266 data ValueStack vs where
267 ValueStackEmpty :: ValueStack '[]
269 { valueStackHead :: Splice v
270 , valueStackTail :: ValueStack vs
271 } -> ValueStack (v ': vs)
273 instance InstrValuable Gen where
275 { unGen = \ctx -> {-trace "unGen.pushValue" $-} unGen k ctx
276 { valueStack = ValueStackCons x (valueStack ctx) }
279 { unGen = \ctx -> {-trace "unGen.popValue" $-} unGen k ctx
280 { valueStack = valueStackTail (valueStack ctx) }
283 { unGen = \ctx -> {-trace "unGen.lift2Value" $-} unGen k ctx
285 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
286 ValueStackCons (f Prod..@ x Prod..@ y) vs
290 { unGen = \ctx -> {-trace "unGen.swapValue" $-} unGen k ctx
292 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
293 ValueStackCons x (ValueStackCons y vs)
296 instance InstrBranchable Gen where
297 caseBranch kx ky = Gen
298 { genAnalysisByLet = genAnalysisByLet kx <> genAnalysisByLet ky
299 , genAnalysis = \final ct -> altGenAnalysis $ genAnalysis kx final ct :| [genAnalysis ky final ct]
300 , unGen = \ctx -> {-trace "unGen.caseBranch" $-}
301 let ValueStackCons v vs = valueStack ctx in
303 case $$(genCode v) of
304 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (splice [||x||]) vs })
305 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (splice [||y||]) vs })
308 choicesBranch fs ks kd = Gen
309 { genAnalysisByLet = sconcat $ genAnalysisByLet kd :| (genAnalysisByLet <$> ks)
310 , genAnalysis = \final ct -> altGenAnalysis $ (\k -> genAnalysis k final ct) <$> (kd:|ks)
311 , unGen = \ctx -> {-trace "unGen.choicesBranch" $-}
312 let ValueStackCons v vs = valueStack ctx in
313 go ctx{valueStack = vs} v fs ks
316 go ctx x (f:fs') (k:ks') = [||
317 if $$(genCode (normalOrderReduction (f Prod..@ x)))
319 let _ = "choicesBranch.then" in
320 $$({-trace "unGen.choicesBranch.k" $-} unGen k ctx)
322 let _ = "choicesBranch.else" in
325 go ctx _ _ _ = unGen kd ctx
326 instance InstrExceptionable Gen where
328 { genAnalysisByLet = HM.empty
329 , genAnalysis = \_final _ct -> GenAnalysis
330 { minReads = Left (ExceptionLabel exn)
331 , mayRaise = Map.singleton (ExceptionLabel exn) ()
333 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.raise: "<>show exn) $-} [||
334 $$(raiseException ctx (ExceptionLabel exn))
335 (ExceptionLabel $$(TH.liftTyped exn))
336 {-failInp-}$$(input ctx)
337 {-farInp-}$$(input ctx)
338 $$(farthestExpecting ctx)
342 { genAnalysisByLet = HM.empty
343 , genAnalysis = \_final _ct -> GenAnalysis
344 { minReads = Left ExceptionFailure
345 , mayRaise = Map.singleton ExceptionFailure ()
347 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.fail: "<>show exn) $-}
349 then [|| -- Raise without updating the farthest error.
350 $$(raiseException ctx ExceptionFailure)
352 {-failInp-}$$(input ctx)
353 $$(farthestInput ctx)
354 $$(farthestExpecting ctx)
356 else raiseFailure ctx [||fs||]
359 { unGen = \ctx -> {-trace ("unGen.commit: "<>show exn) $-}
360 unGen k ctx{catchStackByLabel =
362 _r0:|(r1:rs) -> Just (r1:|rs)
365 exn (catchStackByLabel ctx)
368 catch exn ok ko = Gen
369 { genAnalysisByLet = genAnalysisByLet ok <> genAnalysisByLet ko
370 , genAnalysis = \final ct ->
371 let okGA = genAnalysis ok final ct in
373 okGA{ mayRaise = Map.delete exn (mayRaise okGA) } :|
374 [ genAnalysis ko final ct ]
375 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.catch: "<>show exn) $-} [||
376 let _ = $$(liftTypedString ("catch "<>show exn)) in
377 let catchHandler !_exn !failInp !farInp !farExp =
378 let _ = $$(liftTypedString ("catch.ko "<>show exn)) in
379 $$({-trace ("unGen.catch.ko: "<>show exn) $-} unGen ko ctx
380 -- Push 'input' and 'checkedHorizon'
381 -- as they were when entering 'catch',
382 -- they will be available to 'loadInput', if any.
384 ValueStackCons (splice (input ctx)) $
385 --ValueStackCons (Prod.var [||exn||]) $
388 checkedHorizon ctx : horizonStack ctx
389 -- Note that 'catchStackByLabel' is reset.
390 -- Move the input to the failing position.
391 , input = [||failInp||]
392 -- The 'checkedHorizon' at the 'raise's are not known here.
393 -- Nor whether 'failInp' is after 'checkedHorizon' or not.
394 -- Hence fallback to a safe value.
396 -- Set the farthestInput to the farthest computed in 'fail'.
397 , farthestInput = [||farInp||]
398 , farthestExpecting = [||farExp||]
401 $$({-trace ("unGen.catch.ok: "<>show es) $-} unGen ok ctx
402 { catchStackByLabel =
403 Map.insertWith (<>) exn
404 (NE.singleton [||catchHandler||])
405 (catchStackByLabel ctx)
409 instance InstrInputable Gen where
412 {-trace "unGen.pushInput" $-}
414 { valueStack = splice (input ctx) `ValueStackCons` valueStack ctx
415 , horizonStack = checkedHorizon ctx : horizonStack ctx
420 {-trace "unGen.loadInput" $-}
421 let ValueStackCons input vs = valueStack ctx in
422 let (h, hs) = case horizonStack ctx of
428 , input = genCode input
431 , genAnalysis = \final ct -> GenAnalysis
432 { minReads = 0 <$ minReads (genAnalysis k final ct)
433 , mayRaise = mayRaise (genAnalysis k final ct)
436 instance InstrCallable Gen where
438 { unGen = \ctx@GenCtx{} ->
439 {-trace ("unGen.defLet: defs="<>show (HM.keys defs)) $-}
440 TH.unsafeCodeCoerce $ do
441 decls <- traverse (makeDecl ctx) (HM.toList defs)
442 body <- TH.unTypeQ $ TH.examineCode $
443 {-trace "unGen.defLet.body" $-}
446 -- | Try to output more deterministic code to be able to golden test it,
447 -- at the cost of more computations (at compile-time only though).
448 List.sortBy (compare `on` TH.hideName) $
452 foldMap' (\(SomeLet sub) -> genAnalysisByLet sub) defs <>
453 ((\(SomeLet sub) -> genAnalysis sub) <$> defs) <>
457 makeDecl ctx (n, SomeLet sub) = do
458 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
459 -- Called by 'call' or 'jump'.
460 \ !ok{-from generateSuspend or retCode-}
462 !koByLabel{- 'catchStackByLabel' from the 'call'-site -} ->
463 $$({-trace ("unGen.defLet.sub: "<>show n) $-} unGen sub ctx
464 { valueStack = ValueStackEmpty
465 -- Build a 'catchStackByLabel' from the one available at the 'call'-site.
466 -- Note that all the 'mayRaise' of the 'sub'routine may not be available,
467 -- hence 'Map.findWithDefault' is used instead of 'Map.!'.
468 , catchStackByLabel = Map.mapWithKey
469 (\lbl () -> NE.singleton [||Map.findWithDefault $$(defaultCatch ctx) lbl koByLabel||])
470 ({-trace ("mayRaise: "<>show n) $-}
471 mayRaise (finalGenAnalysisByLet ctx HM.! n))
473 , retCode = {-trace ("unGen.defLet.sub.retCode: "<>show n) $-} [||ok||]
475 -- These are passed by the caller via 'ok' or 'ko'
477 -- , farthestExpecting =
479 -- Some callers can call this 'defLet'
480 -- with zero 'checkedHorizon', hence use this minimum.
481 -- TODO: maybe it could be improved a bit
482 -- by taking the minimum of the checked horizons
483 -- before all the 'call's and 'jump's to this 'defLet'.
487 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
489 jump (LetName n) = Gen
490 { genAnalysisByLet = HM.empty
491 , genAnalysis = \final ct ->
495 , mayRaise = Map.empty
497 else (final HM.! n) (n:ct)
498 , unGen = \ctx -> {-trace ("unGen.jump: "<>show n) $-} [||
500 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
501 {-ok-}$$(retCode ctx)
503 $$(liftTypedRaiseByLabel $
504 catchStackByLabel ctx
505 -- Pass only the labels raised by the 'defLet'.
507 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
511 call (LetName n) k = k
512 { genAnalysis = \final ct ->
516 , mayRaise = Map.empty
518 else seqGenAnalysis $
519 (final HM.! n) (n:ct) :|
520 [ genAnalysis k final ct ]
521 , unGen = {-trace ("unGen.call: "<>show n) $-} \ctx ->
522 -- let ks = (Map.keys (catchStackByLabel ctx)) in
524 -- let _ = $$(liftTypedString $ "call exceptByLet("<>show n<>")="<>show (Map.keys (Map.findWithDefault Map.empty n (exceptByLet ctx))) <> " catchStackByLabel(ctx)="<> show ks) in
525 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
526 {-ok-}$$(generateSuspend k ctx{callStack = n : callStack ctx})
528 $$(liftTypedRaiseByLabel $
529 catchStackByLabel ctx
530 -- Pass only the labels raised by the 'defLet'.
532 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
537 { genAnalysisByLet = HM.empty
538 , genAnalysis = \_final _ct -> GenAnalysis
540 , mayRaise = Map.empty
542 , unGen = \ctx -> {-trace "unGen.ret" $-} unGen ({-trace "unGen.ret.generateResume" $-} generateResume ({-trace "unGen.ret.retCode" $-} retCode ctx)) ctx
545 -- | Like 'TH.liftString' but on 'TH.Code'.
546 -- Useful to get a 'TH.StringL' instead of a 'TH.ListE'.
547 liftTypedString :: String -> TH.Code TH.Q a
548 liftTypedString = TH.unsafeCodeCoerce . TH.liftString
550 -- | Like 'TH.liftTyped' but adjusted to work on 'catchStackByLabel'
551 -- which already contains 'CodeQ' terms.
552 -- Moreover, only the 'Catcher' at the top of the stack
553 -- is needed and thus generated in the resulting 'CodeQ'.
555 -- TODO: Use an 'Array' instead of a 'Map'?
556 liftTypedRaiseByLabel :: TH.Lift k => Map k (NonEmpty (CodeQ a)) -> CodeQ (Map k a)
557 liftTypedRaiseByLabel Map_.Tip = [|| Map_.Tip ||]
558 liftTypedRaiseByLabel (Map_.Bin s k (h:|_hs) l r) =
559 [|| Map_.Bin s k $$h $$(liftTypedRaiseByLabel l) $$(liftTypedRaiseByLabel r) ||]
561 instance TH.Lift a => TH.Lift (Set a) where
562 liftTyped Set_.Tip = [|| Set_.Tip ||]
563 liftTyped (Set_.Bin s a l r) = [|| Set_.Bin $$(TH.liftTyped s) $$(TH.liftTyped a) $$(TH.liftTyped l) $$(TH.liftTyped r) ||]
567 {-farthestInput-}Cursor inp ->
568 {-farthestExpecting-}(Set SomeFailure) ->
571 Either (ParsingError inp) a
573 -- | Generate a 'retCode' 'Cont'inuation to be called with 'generateResume'.
574 -- Used when 'call' 'ret'urns.
575 -- The return 'v'alue is 'pushValue'-ed on the 'valueStack'.
577 {-k-}Gen inp (v ': vs) a ->
580 generateSuspend k ctx = [||
581 let _ = $$(liftTypedString $ "suspend") in
582 \farInp farExp v !inp ->
583 $$({-trace "unGen.generateSuspend" $-} unGen k ctx
584 { valueStack = ValueStackCons ({-trace "unGen.generateSuspend.value" $-} splice [||v||]) (valueStack ctx)
586 , farthestInput = [||farInp||]
587 , farthestExpecting = [||farExp||]
593 -- | Generate a call to the 'generateSuspend' continuation.
594 -- Used when 'call' 'ret'urns.
596 CodeQ (Cont inp v a) ->
598 generateResume k = Gen
599 { genAnalysisByLet = HM.empty
600 , genAnalysis = \_final _ct -> GenAnalysis
602 , mayRaise = Map.empty
604 , unGen = \ctx -> {-trace "unGen.generateResume" $-} [||
607 $$(farthestInput ctx)
608 $$(farthestExpecting ctx)
609 (let _ = "resume.genCode" in $$({-trace "unGen.generateResume.genCode" $-}
610 genCode $ normalOrderReduction $ valueStackHead $ valueStack ctx))
618 {-failInp-}Cursor inp ->
619 {-farInp-}Cursor inp ->
620 {-farExp-}(Set SomeFailure) ->
621 Either (ParsingError inp) a
623 instance InstrJoinable Gen where
624 defJoin (LetName n) sub k = k
627 {-trace ("unGen.defJoin: "<>show n) $-}
628 TH.unsafeCodeCoerce $ do
629 next <- TH.unTypeQ $ TH.examineCode $ [||
630 -- Called by 'generateResume'.
631 \farInp farExp v !inp ->
632 $$({-trace ("unGen.defJoin.next: "<>show n) $-} unGen sub ctx
633 { valueStack = ValueStackCons (splice [||v||]) (valueStack ctx)
635 , farthestInput = [||farInp||]
636 , farthestExpecting = [||farExp||]
639 , catchStackByLabel = Map.mapWithKey
640 (\lbl () -> NE.singleton [||koByLabel Map.! lbl||])
641 (mayRaise sub raiseLabelsByLetButSub)
645 let decl = TH.FunD n [TH.Clause [] (TH.NormalB next) []]
646 expr <- TH.unTypeQ (TH.examineCode ({-trace ("unGen.defJoin.expr: "<>show n) $-} unGen k ctx))
647 return (TH.LetE [decl] expr)
649 (genAnalysisByLet sub <>) $
650 HM.insert n (genAnalysis sub) $
653 refJoin (LetName n) = Gen
655 {-trace ("unGen.refJoin: "<>show n) $-}
656 unGen (generateResume
657 (TH.unsafeCodeCoerce (return (TH.VarE n)))) ctx
658 , genAnalysisByLet = HM.empty
659 , genAnalysis = \final ct ->
660 if n`List.elem`ct -- FIXME: useless
663 , mayRaise = Map.empty
665 else HM.findWithDefault
666 (error (show (n,ct,HM.keys final)))
669 instance InstrReadable Char Gen where
670 read fs p = checkHorizon . checkToken fs p
671 instance InstrReadable Word8 Gen where
672 read fs p = checkHorizon . checkToken fs p
676 -- Those constraints are not used anyway
677 -- because (TH.Lift SomeFailure) uses 'inputTokenProxy'.
678 Eq (InputToken inp) =>
679 Show (InputToken inp) =>
680 TH.Lift (InputToken inp) =>
681 NFData (InputToken inp) =>
682 Typeable (InputToken inp) =>
683 {-ok-}Gen inp vs a ->
686 { genAnalysis = \final ct -> seqGenAnalysis $
687 GenAnalysis { minReads = Right 1
688 , mayRaise = Map.singleton ExceptionFailure ()
690 [ genAnalysis ok final ct ]
691 , unGen = \ctx0@GenCtx{} ->
692 {-trace "unGen.checkHorizon" $-}
693 let raiseFail = raiseException ctx0 ExceptionFailure in
695 -- Factorize generated code for raising the "fail".
696 let readFail = $$(raiseFail) in
698 let ctx = ctx0{catchStackByLabel =
699 Map.adjust (\(_r:|rs) -> [||readFail||] :| rs)
700 ExceptionFailure (catchStackByLabel ctx0)} in
701 if checkedHorizon ctx >= 1
702 then unGen ok ctx0{checkedHorizon = checkedHorizon ctx - 1}
704 either (\_err -> 0) id $
705 minReads $ finalGenAnalysis ctx ok in
709 then [||$$shiftRight minHoriz $$(input ctx)||]
711 then $$(unGen ok ctx{checkedHorizon = minHoriz})
712 else let _ = "checkHorizon.else" in
713 -- TODO: return a resuming continuation (eg. Partial)
714 $$(unGen (fail (Set.singleton $ SomeFailure $ FailureHorizon @(InputToken inp) (minHoriz + 1))) ctx)
720 -- | @('raiseFailure' ctx fs)@ raises 'ExceptionFailure'
721 -- with farthest parameters set to or updated with @(fs)@
722 -- according to the relative position of 'input' wrt. 'farthestInput'.
724 Cursorable (Cursor inp) =>
726 TH.CodeQ (Set SomeFailure) ->
727 TH.CodeQ (Either (ParsingError inp) a)
728 raiseFailure ctx fs = [||
729 let failExp = $$fs in
730 let (# farInp, farExp #) =
731 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
732 LT -> (# $$(input ctx), failExp #)
733 EQ -> (# $$(farthestInput ctx), failExp <> $$(farthestExpecting ctx) #)
734 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #)
735 in $$(raiseException ctx ExceptionFailure)
737 {-failInp-}$$(input ctx) farInp farExp
739 -- | @('raiseException' ctx exn)@ raises exception @(exn)@
740 -- using any entry in 'catchStackByLabel', or 'defaultCatch' if none.
742 GenCtx inp vs a -> Exception ->
743 CodeQ (Exception -> Cursor inp -> Cursor inp -> Set SomeFailure -> Either (ParsingError inp) a)
744 raiseException ctx exn =
745 NE.head $ Map.findWithDefault
746 (NE.singleton (defaultCatch ctx))
747 exn (catchStackByLabel ctx)
749 finalGenAnalysis :: GenCtx inp vs a -> Gen inp cs a -> GenAnalysis
750 finalGenAnalysis ctx k =
751 --(\f -> f (error "callTrace")) $
752 (\f -> f (callStack ctx)) $
754 ((\f _ct -> f) <$>) $
755 finalGenAnalysisByLet ctx
759 {-predicate-}Splice (InputToken inp -> Bool) ->
760 {-ok-}Gen inp (InputToken inp ': vs) a ->
762 checkToken fs p ok = ok
763 { unGen = \ctx -> {-trace "unGen.read" $-} [||
764 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
767 { valueStack = ValueStackCons (splice [||c||]) (valueStack ctx)
770 else let _ = "checkToken.else" in
771 $$(unGen (fail fs) ctx)