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.Foldable (foldMap', toList, null)
19 import Data.Function (($), (.), id, const, on)
20 import Data.Functor (Functor, (<$>), (<$))
22 import Data.List.NonEmpty (NonEmpty(..))
24 import Data.Maybe (Maybe(..))
25 import Data.Ord (Ord(..), Ordering(..))
26 import Data.Proxy (Proxy(..))
27 import Data.Semigroup (Semigroup(..))
29 import Data.String (String)
30 import Data.Traversable (Traversable(..))
31 import Data.Typeable (Typeable)
32 import Data.Word (Word8)
33 import GHC.Generics (Generic)
34 import GHC.Show (showCommaSpace)
35 import Language.Haskell.TH (CodeQ)
36 import Prelude ((+), (-), error)
37 import Text.Show (Show(..), showParen, showString)
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 Data.STRef as ST
46 import qualified Language.Haskell.TH as TH
47 import qualified Language.Haskell.TH.Syntax as TH
49 import Symantic.Derive
50 import Symantic.ObserveSharing
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.Lang as Prod
56 import qualified Symantic.Optimize as Prod
60 -- | Convenient utility to generate some final 'TH.CodeQ'.
61 genCode :: Splice a -> CodeQ a
62 genCode = derive . Prod.normalOrderReduction
65 -- | Generate the 'CodeQ' parsing the input.
66 data Gen inp vs a = Gen
67 { genAnalysisByLet :: LetMapFix (CallTrace -> GenAnalysis)
68 -- ^ 'genAnalysis' for each 'defLet' and 'defJoin' of the 'Machine'.
69 , genAnalysis :: LetMapTo (CallTrace -> GenAnalysis)
70 -- ^ Synthetized (bottom-up) static genAnalysis of the 'Machine'.
73 CodeQ (Either (ParsingError inp) a)
76 -- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
77 -- parsing the given 'input' according to the given 'Machine'.
80 Eq (InputToken inp) =>
81 NFData (InputToken inp) =>
82 Show (InputToken inp) =>
83 Typeable (InputToken inp) =>
84 TH.Lift (InputToken inp) =>
86 -- InputToken inp ~ Char =>
90 CodeQ (inp -> Either (ParsingError inp) a)
91 generateCode k = [|| \(input :: inp) ->
92 -- Pattern bindings containing unlifted types
93 -- should use an outermost bang pattern.
94 let !(# init, readMore, readNext #) = $$(cursorOf [||input||])
95 finalRet = \_farInp _farExp v _inp -> Right v
96 finalRaise :: forall b. (Catcher inp b)
97 = \ !exn _failInp !farInp !farExp ->
99 { parsingErrorOffset = offset farInp
100 , parsingErrorException = exn
101 , parsingErrorUnexpected =
103 then Just (let (# c, _ #) = readNext farInp in c)
105 , parsingErrorExpecting = farExp
109 let defInputTokenProxy exprCode =
110 TH.unsafeCodeCoerce $ do
111 value <- TH.unTypeQ $ TH.examineCode [||Proxy :: Proxy (InputToken inp)||]
112 expr <- TH.unTypeQ (TH.examineCode exprCode)
114 TH.FunD inputTokenProxy [TH.Clause [] (TH.NormalB value) []]
116 in defInputTokenProxy $
118 { valueStack = ValueStackEmpty
119 , catchStackByLabel = Map.empty
120 , defaultCatch = [||finalRaise||]
122 , retCode = [||finalRet||]
124 , nextInput = [||readNext||]
125 , moreInput = [||readMore||]
126 -- , farthestError = [||Nothing||]
127 , farthestInput = [||init||]
128 , farthestExpecting = [||Set.empty||]
131 , finalGenAnalysisByLet = runGenAnalysis (genAnalysisByLet k)
136 -- ** Type 'ParsingError'
137 data ParsingError inp
139 { parsingErrorOffset :: Offset
140 , parsingErrorException :: Exception
141 -- | Note: if a 'FailureHorizon' greater than 1
142 -- is amongst the 'parsingErrorExpecting'
143 -- then 'parsingErrorUnexpected' is only the 'InputToken'
144 -- at the begining of the expected 'Horizon'.
145 , parsingErrorUnexpected :: Maybe (InputToken inp)
146 , parsingErrorExpecting :: Set SomeFailure
148 deriving instance NFData (InputToken inp) => NFData (ParsingError inp)
149 --deriving instance Show (InputToken inp) => Show (ParsingError inp)
150 instance Show (InputToken inp) => Show (ParsingError inp) where
151 showsPrec p ParsingError{..} =
152 showParen (p >= 11) $
153 showString "ParsingErrorStandard {" .
154 showString "parsingErrorOffset = " .
155 showsPrec 0 parsingErrorOffset .
157 showString "parsingErrorException = " .
158 showsPrec 0 parsingErrorException .
160 showString "parsingErrorUnexpected = " .
161 showsPrec 0 parsingErrorUnexpected .
163 showString "parsingErrorExpecting = fromList " .
165 -- Sort on the string representation
166 -- because the 'Ord' of the 'SomeFailure'
167 -- is based upon hashes ('typeRepFingerprint')
168 -- depending on packages' ABI and whether
169 -- cabal-install's setup is --inplace or not,
170 -- and that would be too unstable for golden tests.
171 List.sortBy (compare `on` show) $
172 Set.toList parsingErrorExpecting
176 -- ** Type 'ErrorLabel'
177 type ErrorLabel = String
179 -- * Type 'GenAnalysis'
180 data GenAnalysis = GenAnalysis
181 { minReads :: Either Exception Horizon
182 , mayRaise :: Map Exception ()
185 -- | Tie the knot between mutually recursive 'TH.Name's
186 -- introduced by 'defLet' and 'defJoin'.
187 -- and provide the empty initial 'CallTrace' stack
189 LetMapFix (CallTrace -> GenAnalysis) ->
191 runGenAnalysis ga = (($ []) <$>) $ polyfix ga
193 -- | Poly-variadic fixpoint combinator.
194 -- Used to express mutual recursion and to transparently introduce memoization,
195 -- more precisely to "tie the knot"
196 -- between observed sharing ('defLet', 'call', 'jump')
197 -- and also between join points ('defJoin', 'refJoin').
198 -- Because it's enough for its usage here,
199 -- all mutually dependent functions are restricted to the same polymorphic type @(a)@.
200 -- See http://okmij.org/ftp/Computation/fixed-point-combinators.html#Poly-variadic
201 polyfix :: Functor f => f (f a -> a) -> f a
202 polyfix fs = fix $ \finals -> ($ finals) <$> fs
205 fix f = final where final = f final
207 type LetMap = HM.HashMap TH.Name
208 type LetMapTo a = LetMap a -> a
209 type LetMapFix a = LetMap (LetMap a -> a)
211 -- | Call trace stack updated by 'call' and 'refJoin'.
212 -- Used to avoid infinite loops when tying the knot with 'polyfix'.
213 type CallTrace = [TH.Name]
218 -- | Minimal input length required for a successful parsing.
219 type Horizon = Offset
221 -- altGenAnalysis = List.foldl' (\acc x -> either Left (\h -> Right (either (const h) (min h) acc)) x)
222 -- | Merge given 'GenAnalysis' as sequences.
223 seqGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
224 seqGenAnalysis aas@(a:|as) = GenAnalysis
225 { minReads = List.foldl' (\acc x ->
226 acc >>= \r -> (r +) <$> minReads x
228 , mayRaise = sconcat (mayRaise <$> aas)
230 -- | Merge given 'GenAnalysis' as alternatives.
231 altGenAnalysis :: NonEmpty GenAnalysis -> GenAnalysis
232 altGenAnalysis aas@(a:|as) = GenAnalysis
233 { minReads = List.foldl' (\acc x ->
235 (\l -> either (const (Left l)) Right)
236 (\r -> either (const (Right r)) (Right . min r))
239 , mayRaise = sconcat (mayRaise <$> aas)
244 -- *** Type 'FarthestError'
245 data FarthestError inp = FarthestError
246 { farthestInput :: Cursor inp
247 , farthestExpecting :: [Failure (InputToken inp)]
252 -- | This is an inherited (top-down) context
253 -- only present at compile-time, to build TemplateHaskell splices.
254 data GenCtx inp vs a =
255 ( Cursorable (Cursor inp)
257 , TH.Lift (InputToken inp)
258 , Show (InputToken inp)
259 , Eq (InputToken inp)
260 , Typeable (InputToken inp)
261 , NFData (InputToken inp)
264 { valueStack :: ValueStack vs
265 , catchStackByLabel :: Map Exception (NonEmpty (CodeQ (Catcher inp a)))
266 -- | Default 'Catcher' defined at the begining of the generated 'CodeQ',
267 -- hence a constant within the 'Gen'eration.
268 , defaultCatch :: forall b. CodeQ (Catcher inp b)
269 -- | Used by 'checkToken' to get 'GenAnalysis' from 'genAnalysis'.
270 , callStack :: [TH.Name]
271 , retCode :: CodeQ (Cont inp a a)
272 , input :: CodeQ (Cursor inp)
273 , moreInput :: CodeQ (Cursor inp -> Bool)
274 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
275 , farthestInput :: CodeQ (Cursor inp)
276 , farthestExpecting :: CodeQ (Set SomeFailure)
277 -- | Remaining horizon already checked.
278 -- Use to factorize 'input' length checks,
279 -- instead of checking the 'input' length
280 -- one 'InputToken' at a time at each 'read'.
281 -- Updated by 'checkHorizon'
282 -- and reset elsewhere when needed.
283 , checkedHorizon :: Horizon
284 -- | Used by 'pushInput' and 'loadInput'
285 -- to restore the 'Horizon' at the restored 'input'.
286 , horizonStack :: [Horizon]
287 -- | Output of 'runGenAnalysis'.
288 , finalGenAnalysisByLet :: LetMap GenAnalysis
291 -- ** Type 'ValueStack'
292 data ValueStack vs where
293 ValueStackEmpty :: ValueStack '[]
295 { valueStackHead :: Splice v
296 , valueStackTail :: ValueStack vs
297 } -> ValueStack (v ': vs)
299 instance InstrValuable Gen where
301 { unGen = \ctx -> {-trace "unGen.pushValue" $-} unGen k ctx
302 { valueStack = ValueStackCons x (valueStack ctx) }
305 { unGen = \ctx -> {-trace "unGen.popValue" $-} unGen k ctx
306 { valueStack = valueStackTail (valueStack ctx) }
309 { unGen = \ctx -> {-trace "unGen.lift2Value" $-} unGen k ctx
311 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
312 ValueStackCons (f Prod..@ x Prod..@ y) vs
316 { unGen = \ctx -> {-trace "unGen.swapValue" $-} unGen k ctx
318 let ValueStackCons y (ValueStackCons x vs) = valueStack ctx in
319 ValueStackCons x (ValueStackCons y vs)
322 instance InstrBranchable Gen where
323 caseBranch kx ky = Gen
324 { genAnalysisByLet = genAnalysisByLet kx <> genAnalysisByLet ky
325 , genAnalysis = \final ct -> altGenAnalysis $ genAnalysis kx final ct :| [genAnalysis ky final ct]
326 , unGen = \ctx -> {-trace "unGen.caseBranch" $-}
327 let ValueStackCons v vs = valueStack ctx in
329 case $$(genCode v) of
330 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (splice [||x||]) vs })
331 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (splice [||y||]) vs })
334 choicesBranch fs ks kd = Gen
335 { genAnalysisByLet = sconcat $ genAnalysisByLet kd :| (genAnalysisByLet <$> ks)
336 , genAnalysis = \final ct -> altGenAnalysis $ (\k -> genAnalysis k final ct) <$> (kd:|ks)
337 , unGen = \ctx -> {-trace "unGen.choicesBranch" $-}
338 let ValueStackCons v vs = valueStack ctx in
339 go ctx{valueStack = vs} v fs ks
342 go ctx x (f:fs') (k:ks') = [||
343 if $$(genCode (f Prod..@ x))
345 let _ = "choicesBranch.then" in
346 $$({-trace "unGen.choicesBranch.k" $-} unGen k ctx)
348 let _ = "choicesBranch.else" in
351 go ctx _ _ _ = unGen kd ctx
352 instance InstrExceptionable Gen where
354 { genAnalysisByLet = HM.empty
355 , genAnalysis = \_final _ct -> GenAnalysis
356 { minReads = Left (ExceptionLabel exn)
357 , mayRaise = Map.singleton (ExceptionLabel exn) ()
359 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.raise: "<>show exn) $-} [||
360 $$(raiseException ctx (ExceptionLabel exn))
361 (ExceptionLabel $$(TH.liftTyped exn))
362 {-failInp-}$$(input ctx)
363 {-farInp-}$$(input ctx)
364 $$(farthestExpecting ctx)
368 { genAnalysisByLet = HM.empty
369 , genAnalysis = \_final _ct -> GenAnalysis
370 { minReads = Left ExceptionFailure
371 , mayRaise = Map.singleton ExceptionFailure ()
373 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.fail: "<>show exn) $-}
375 then [|| -- Raise without updating the farthest error.
376 $$(raiseException ctx ExceptionFailure)
378 {-failInp-}$$(input ctx)
379 $$(farthestInput ctx)
380 $$(farthestExpecting ctx)
382 else raiseFailure ctx [||fs||]
385 { unGen = \ctx -> {-trace ("unGen.commit: "<>show exn) $-}
386 unGen k ctx{catchStackByLabel =
388 _r0:|(r1:rs) -> Just (r1:|rs)
391 exn (catchStackByLabel ctx)
394 catch exn ok ko = Gen
395 { genAnalysisByLet = genAnalysisByLet ok <> genAnalysisByLet ko
396 , genAnalysis = \final ct ->
397 let okGA = genAnalysis ok final ct in
399 okGA{ mayRaise = Map.delete exn (mayRaise okGA) } :|
400 [ genAnalysis ko final ct ]
401 , unGen = \ctx@GenCtx{} -> {-trace ("unGen.catch: "<>show exn) $-} [||
402 let _ = $$(liftTypedString ("catch "<>show exn)) in
403 let catchHandler !_exn !failInp !farInp !farExp =
404 let _ = $$(liftTypedString ("catch.ko "<>show exn)) in
405 $$({-trace ("unGen.catch.ko: "<>show exn) $-} unGen ko ctx
406 -- Push 'input' and 'checkedHorizon'
407 -- as they were when entering 'catch',
408 -- they will be available to 'loadInput', if any.
410 ValueStackCons (splice (input ctx)) $
411 --ValueStackCons (Prod.var [||exn||]) $
414 checkedHorizon ctx : horizonStack ctx
415 -- Note that 'catchStackByLabel' is reset.
416 -- Move the input to the failing position.
417 , input = [||failInp||]
418 -- The 'checkedHorizon' at the 'raise's are not known here.
419 -- Nor whether 'failInp' is after 'checkedHorizon' or not.
420 -- Hence fallback to a safe value.
422 -- Set the farthestInput to the farthest computed in 'fail'.
423 , farthestInput = [||farInp||]
424 , farthestExpecting = [||farExp||]
427 $$({-trace ("unGen.catch.ok: "<>show es) $-} unGen ok ctx
428 { catchStackByLabel =
429 Map.insertWith (<>) exn
430 (NE.singleton [||catchHandler||])
431 (catchStackByLabel ctx)
435 instance InstrInputable Gen where
438 {-trace "unGen.pushInput" $-}
440 { valueStack = splice (input ctx) `ValueStackCons` valueStack ctx
441 , horizonStack = checkedHorizon ctx : horizonStack ctx
446 {-trace "unGen.loadInput" $-}
447 let ValueStackCons input vs = valueStack ctx in
448 let (h, hs) = case horizonStack ctx of
454 , input = genCode input
457 , genAnalysis = \final ct -> GenAnalysis
458 { minReads = 0 <$ minReads (genAnalysis k final ct)
459 , mayRaise = mayRaise (genAnalysis k final ct)
462 instance InstrCallable Gen where
464 { unGen = \ctx@GenCtx{} ->
465 {-trace ("unGen.defLet: defs="<>show (HM.keys defs)) $-}
466 TH.unsafeCodeCoerce $ do
467 decls <- traverse (makeDecl ctx) (HM.toList defs)
468 body <- TH.unTypeQ $ TH.examineCode $
469 {-trace "unGen.defLet.body" $-}
472 -- | Try to output more deterministic code to be able to golden test it,
473 -- at the cost of more computations (at compile-time only though).
474 List.sortBy (compare `on` TH.hideName) $
478 foldMap' (\(SomeLet sub) -> genAnalysisByLet sub) defs <>
479 ((\(SomeLet sub) -> genAnalysis sub) <$> defs) <>
483 makeDecl ctx (n, SomeLet sub) = do
484 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
485 -- Called by 'call' or 'jump'.
486 \ !ok{-from generateSuspend or retCode-}
488 !koByLabel{- 'catchStackByLabel' from the 'call'-site -} ->
489 $$({-trace ("unGen.defLet.sub: "<>show n) $-} unGen sub ctx
490 { valueStack = ValueStackEmpty
491 -- Build a 'catchStackByLabel' from the one available at the 'call'-site.
492 -- Note that all the 'mayRaise' of the 'sub'routine may not be available,
493 -- hence 'Map.findWithDefault' is used instead of 'Map.!'.
494 , catchStackByLabel = Map.mapWithKey
495 (\lbl () -> NE.singleton [||Map.findWithDefault $$(defaultCatch ctx) lbl koByLabel||])
496 ({-trace ("mayRaise: "<>show n) $-}
497 mayRaise (finalGenAnalysisByLet ctx HM.! n))
499 , retCode = {-trace ("unGen.defLet.sub.retCode: "<>show n) $-} [||ok||]
501 -- These are passed by the caller via 'ok' or 'ko'
503 -- , farthestExpecting =
505 -- Some callers can call this 'defLet'
506 -- with zero 'checkedHorizon', hence use this minimum.
507 -- TODO: maybe it could be improved a bit
508 -- by taking the minimum of the checked horizons
509 -- before all the 'call's and 'jump's to this 'defLet'.
513 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
515 jump (LetName n) = Gen
516 { genAnalysisByLet = HM.empty
517 , genAnalysis = \final ct ->
521 , mayRaise = Map.empty
523 else (final HM.! n) (n:ct)
524 , unGen = \ctx -> {-trace ("unGen.jump: "<>show n) $-} [||
526 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
527 {-ok-}$$(retCode ctx)
529 $$(liftTypedRaiseByLabel $
530 catchStackByLabel ctx
531 -- Pass only the labels raised by the 'defLet'.
533 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
537 call (LetName n) k = k
538 { genAnalysis = \final ct ->
542 , mayRaise = Map.empty
544 else seqGenAnalysis $
545 (final HM.! n) (n:ct) :|
546 [ genAnalysis k final ct ]
547 , unGen = {-trace ("unGen.call: "<>show n) $-} \ctx ->
548 -- let ks = (Map.keys (catchStackByLabel ctx)) in
550 -- let _ = $$(liftTypedString $ "call exceptByLet("<>show n<>")="<>show (Map.keys (Map.findWithDefault Map.empty n (exceptByLet ctx))) <> " catchStackByLabel(ctx)="<> show ks) in
551 $$(TH.unsafeCodeCoerce (return (TH.VarE n)))
552 {-ok-}$$(generateSuspend k ctx{callStack = n : callStack ctx})
554 $$(liftTypedRaiseByLabel $
555 catchStackByLabel ctx
556 -- Pass only the labels raised by the 'defLet'.
558 (mayRaise $ finalGenAnalysisByLet ctx HM.! n)
563 { genAnalysisByLet = HM.empty
564 , genAnalysis = \_final _ct -> GenAnalysis
566 , mayRaise = Map.empty
568 , unGen = \ctx -> {-trace "unGen.ret" $-} unGen ({-trace "unGen.ret.generateResume" $-} generateResume ({-trace "unGen.ret.retCode" $-} retCode ctx)) ctx
571 -- | Like 'TH.liftString' but on 'TH.Code'.
572 -- Useful to get a 'TH.StringL' instead of a 'TH.ListE'.
573 liftTypedString :: String -> TH.Code TH.Q a
574 liftTypedString = TH.unsafeCodeCoerce . TH.liftString
576 -- | Like 'TH.liftTyped' but adjusted to work on 'catchStackByLabel'
577 -- which already contains 'CodeQ' terms.
578 -- Moreover, only the 'Catcher' at the top of the stack
579 -- is needed and thus generated in the resulting 'CodeQ'.
581 -- TODO: Use an 'Array' instead of a 'Map'?
582 liftTypedRaiseByLabel :: TH.Lift k => Map k (NonEmpty (CodeQ a)) -> CodeQ (Map k a)
583 liftTypedRaiseByLabel Map_.Tip = [|| Map_.Tip ||]
584 liftTypedRaiseByLabel (Map_.Bin s k (h:|_hs) l r) =
585 [|| Map_.Bin s k $$h $$(liftTypedRaiseByLabel l) $$(liftTypedRaiseByLabel r) ||]
587 instance TH.Lift a => TH.Lift (Set a) where
588 liftTyped Set_.Tip = [|| Set_.Tip ||]
589 liftTyped (Set_.Bin s a l r) = [|| Set_.Bin $$(TH.liftTyped s) $$(TH.liftTyped a) $$(TH.liftTyped l) $$(TH.liftTyped r) ||]
593 {-farthestInput-}Cursor inp ->
594 {-farthestExpecting-}(Set SomeFailure) ->
597 Either (ParsingError inp) a
599 -- | Generate a 'retCode' 'Cont'inuation to be called with 'generateResume'.
600 -- Used when 'call' 'ret'urns.
601 -- The return 'v'alue is 'pushValue'-ed on the 'valueStack'.
603 {-k-}Gen inp (v ': vs) a ->
606 generateSuspend k ctx = [||
607 let _ = $$(liftTypedString $ "suspend") in
608 \farInp farExp v !inp ->
609 $$({-trace "unGen.generateSuspend" $-} unGen k ctx
610 { valueStack = ValueStackCons ({-trace "unGen.generateSuspend.value" $-} splice [||v||]) (valueStack ctx)
612 , farthestInput = [||farInp||]
613 , farthestExpecting = [||farExp||]
619 -- | Generate a call to the 'generateSuspend' continuation.
620 -- Used when 'call' 'ret'urns.
622 CodeQ (Cont inp v a) ->
624 generateResume k = Gen
625 { genAnalysisByLet = HM.empty
626 , genAnalysis = \_final _ct -> GenAnalysis
628 , mayRaise = Map.empty
630 , unGen = \ctx -> {-trace "unGen.generateResume" $-} [||
633 $$(farthestInput ctx)
634 $$(farthestExpecting ctx)
635 (let _ = "resume.genCode" in $$({-trace "unGen.generateResume.genCode" $-}
636 genCode $ valueStackHead $ valueStack ctx))
644 {-failInp-}Cursor inp ->
645 {-farInp-}Cursor inp ->
646 {-farExp-}(Set SomeFailure) ->
647 Either (ParsingError inp) a
649 instance InstrJoinable Gen where
650 defJoin (LetName n) sub k = k
653 {-trace ("unGen.defJoin: "<>show n) $-}
654 TH.unsafeCodeCoerce $ do
655 next <- TH.unTypeQ $ TH.examineCode $ [||
656 -- Called by 'generateResume'.
657 \farInp farExp v !inp ->
658 $$({-trace ("unGen.defJoin.next: "<>show n) $-} unGen sub ctx
659 { valueStack = ValueStackCons (splice [||v||]) (valueStack ctx)
661 , farthestInput = [||farInp||]
662 , farthestExpecting = [||farExp||]
665 , catchStackByLabel = Map.mapWithKey
666 (\lbl () -> NE.singleton [||koByLabel Map.! lbl||])
667 (mayRaise sub raiseLabelsByLetButSub)
671 let decl = TH.FunD n [TH.Clause [] (TH.NormalB next) []]
672 expr <- TH.unTypeQ (TH.examineCode ({-trace ("unGen.defJoin.expr: "<>show n) $-} unGen k ctx))
673 return (TH.LetE [decl] expr)
675 (genAnalysisByLet sub <>) $
676 HM.insert n (genAnalysis sub) $
679 refJoin (LetName n) = Gen
681 {-trace ("unGen.refJoin: "<>show n) $-}
682 unGen (generateResume
683 (TH.unsafeCodeCoerce (return (TH.VarE n)))) ctx
684 , genAnalysisByLet = HM.empty
685 , genAnalysis = \final ct ->
686 if n`List.elem`ct -- FIXME: useless
689 , mayRaise = Map.empty
691 else HM.findWithDefault
692 (error (show (n,ct,HM.keys final)))
695 instance InstrReadable Char Gen where
696 read fs p = checkHorizon . checkToken fs p
697 instance InstrReadable Word8 Gen where
698 read fs p = checkHorizon . checkToken fs p
702 -- Those constraints are not used anyway
703 -- because (TH.Lift SomeFailure) uses 'inputTokenProxy'.
704 Ord (InputToken inp) =>
705 Show (InputToken inp) =>
706 TH.Lift (InputToken inp) =>
707 NFData (InputToken inp) =>
708 Typeable (InputToken inp) =>
709 {-ok-}Gen inp vs a ->
712 { genAnalysis = \final ct -> seqGenAnalysis $
713 GenAnalysis { minReads = Right 1
714 , mayRaise = Map.singleton ExceptionFailure ()
716 [ genAnalysis ok final ct ]
717 , unGen = \ctx0@GenCtx{} ->
718 {-trace "unGen.checkHorizon" $-}
719 let raiseFail = raiseException ctx0 ExceptionFailure in
721 -- Factorize generated code for raising the "fail".
722 let readFail = $$(raiseFail) in
724 let ctx = ctx0{catchStackByLabel =
725 Map.adjust (\(_r:|rs) -> [||readFail||] :| rs)
726 ExceptionFailure (catchStackByLabel ctx0)} in
727 if checkedHorizon ctx >= 1
728 then unGen ok ctx0{checkedHorizon = checkedHorizon ctx - 1}
730 either (\_err -> 0) id $
731 minReads $ finalGenAnalysis ctx ok in
735 then [||$$shiftRight minHoriz $$(input ctx)||]
737 then $$(unGen ok ctx{checkedHorizon = minHoriz})
738 else let _ = "checkHorizon.else" in
739 -- TODO: return a resuming continuation (eg. Partial)
740 $$(unGen (fail (Set.singleton $ SomeFailure $ FailureHorizon @(InputToken inp) (minHoriz + 1))) ctx)
746 -- | @('raiseFailure' ctx fs)@ raises 'ExceptionFailure'
747 -- with farthest parameters set to or updated with @(fs)@
748 -- according to the relative position of 'input' wrt. 'farthestInput'.
750 Cursorable (Cursor inp) =>
752 TH.CodeQ (Set SomeFailure) ->
753 TH.CodeQ (Either (ParsingError inp) a)
754 raiseFailure ctx fs = [||
755 let failExp = $$fs in
756 let (# farInp, farExp #) =
757 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
758 LT -> (# $$(input ctx), failExp #)
759 EQ -> (# $$(farthestInput ctx), failExp <> $$(farthestExpecting ctx) #)
760 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #)
761 in $$(raiseException ctx ExceptionFailure)
763 {-failInp-}$$(input ctx) farInp farExp
765 -- | @('raiseException' ctx exn)@ raises exception @(exn)@
766 -- using any entry in 'catchStackByLabel', or 'defaultCatch' if none.
768 GenCtx inp vs a -> Exception ->
769 CodeQ (Exception -> Cursor inp -> Cursor inp -> Set SomeFailure -> Either (ParsingError inp) a)
770 raiseException ctx exn =
771 NE.head $ Map.findWithDefault
772 (NE.singleton (defaultCatch ctx))
773 exn (catchStackByLabel ctx)
775 finalGenAnalysis :: GenCtx inp vs a -> Gen inp cs a -> GenAnalysis
776 finalGenAnalysis ctx k =
777 --(\f -> f (error "callTrace")) $
778 (\f -> f (callStack ctx)) $
780 ((\f _ct -> f) <$>) $
781 finalGenAnalysisByLet ctx
785 {-predicate-}Splice (InputToken inp -> Bool) ->
786 {-ok-}Gen inp (InputToken inp ': vs) a ->
788 checkToken fs p ok = ok
789 { unGen = \ctx -> {-trace "unGen.read" $-} [||
790 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
793 (p Prod..@ splice [||c||])
794 (splice $ unGen ok ctx
795 { valueStack = ValueStackCons (splice [||c||]) (valueStack ctx)
799 let _ = "checkToken.else" in
800 $$(unGen (fail fs) ctx)