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.Eq as Eq
25 import qualified Data.Map.Strict as Map
26 import qualified Data.Set as Set
27 import qualified Language.Haskell.TH.Syntax as TH
28 -- import qualified Control.Monad.Trans.Writer as Writer
30 import Symantic.Univariant.Trans
31 import Symantic.Parser.Grammar.Combinators (ErrorItem(..))
32 import Symantic.Parser.Machine.Input
33 import Symantic.Parser.Machine.Instructions
34 import qualified Symantic.Parser.Haskell as H
37 -- | Generate the 'CodeQ' parsing the input.
38 data Gen inp vs es a = Gen
39 { minHorizon :: Map TH.Name Horizon -> Horizon
42 CodeQ (Either (ParsingError inp) a)
45 -- ** Type 'ParsingError'
47 = ParsingErrorStandard
48 { parsingErrorOffset :: Offset
49 , parsingErrorUnexpected :: Maybe (InputToken inp)
50 , parsingErrorExpecting :: Set (ErrorItem (InputToken inp))
52 deriving instance Show (InputToken inp) => Show (ParsingError inp)
58 -- | Synthetized minimal input length
59 -- required for a successful parsing.
60 -- Used with 'horizon' to factorize input length checks,
61 -- instead of checking the input length
62 -- one 'InputToken' by one 'InputToken' at each 'read'.
67 {-farthestInput-}Cursor inp ->
68 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
71 Either (ParsingError inp) a
73 -- ** Type 'SubRoutine'
74 type SubRoutine inp v a =
77 {-ko-}FailHandler inp a ->
78 Either (ParsingError inp) a
80 -- ** Type 'FailHandler'
81 type FailHandler inp a =
82 {-failureInput-}Cursor inp ->
83 {-farthestInput-}Cursor inp ->
84 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
85 Either (ParsingError inp) a
88 -- *** Type 'FarthestError'
89 data FarthestError inp = FarthestError
90 { farthestInput :: Cursor inp
91 , farthestExpecting :: [ErrorItem (InputToken inp)]
95 -- | @('generate' input mach)@ generates @TemplateHaskell@ code
96 -- parsing given 'input' according to given 'mach'ine.
99 Ord (InputToken inp) =>
100 Show (InputToken inp) =>
101 TH.Lift (InputToken inp) =>
102 -- InputToken inp ~ Char =>
106 Gen inp '[] ('Succ 'Zero) ret ->
107 CodeQ (Either (ParsingError inp) ret)
108 generate input k = [||
109 -- Pattern bindings containing unlifted types
110 -- should use an outermost bang pattern.
111 let !(# init, readMore, readNext #) = $$(cursorOf input) in
112 let finalRet = \_farInp _farExp v _inp -> Right v in
113 let finalFail _failInp !farInp !farExp =
114 Left ParsingErrorStandard
115 { parsingErrorOffset = offset farInp
116 , parsingErrorUnexpected =
118 then Just (let (# c, _ #) = readNext farInp in c)
120 , parsingErrorExpecting = Set.fromList farExp
123 { valueStack = ValueStackEmpty
124 , failStack = FailStackCons [||finalFail||] FailStackEmpty
125 , retCode = [||finalRet||]
127 , nextInput = [||readNext||]
128 , moreInput = [||readMore||]
129 -- , farthestError = [||Nothing||]
130 , farthestInput = [||init||]
131 , farthestExpecting = [|| [] ||]
133 , horizonByName = Map.empty
138 -- | This is a context only present at compile-time.
139 data GenCtx inp vs (es::Peano) a =
140 ( TH.Lift (InputToken inp)
141 , Cursorable (Cursor inp)
142 , Show (InputToken inp)
143 -- , InputToken inp ~ Char
145 { valueStack :: ValueStack vs
146 , failStack :: FailStack inp es a
147 , retCode :: CodeQ (Cont inp a a)
148 , input :: CodeQ (Cursor inp)
149 , moreInput :: CodeQ (Cursor inp -> Bool)
150 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
151 , farthestInput :: CodeQ (Cursor inp)
152 , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
153 -- | Remaining horizon
155 -- | Horizon for each 'call' or 'jump'.
156 , horizonByName :: Map TH.Name Offset
159 -- ** Type 'ValueStack'
160 data ValueStack vs where
161 ValueStackEmpty :: ValueStack '[]
163 -- TODO: maybe use H.Haskell instead of CodeQ ?
164 -- as in https://github.com/j-mie6/ParsleyHaskell/popFail/3ec0986a5017866919a6404c14fe78678b7afb46
165 { valueStackHead :: CodeQ v
166 , valueStackTail :: ValueStack vs
167 } -> ValueStack (v ': vs)
169 -- ** Type 'FailStack'
170 data FailStack inp es a where
171 FailStackEmpty :: FailStack inp 'Zero a
173 { failStackHead :: CodeQ (FailHandler inp a)
174 , failStackTail :: FailStack inp es a
176 FailStack inp ('Succ es) a
178 instance Stackable Gen where
180 { unGen = \ctx -> unGen k ctx
181 { valueStack = ValueStackCons (liftCode x) (valueStack ctx) }
184 { unGen = \ctx -> unGen k ctx
185 { valueStack = valueStackTail (valueStack ctx) }
188 { unGen = \ctx -> unGen k ctx
190 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
191 ValueStackCons (liftCode2 f x y) xs
195 { unGen = \ctx -> unGen k ctx
197 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
198 ValueStackCons x (ValueStackCons y xs)
201 instance Branchable Gen where
203 { minHorizon = \ls ->
204 minHorizon kx ls `min` minHorizon ky ls
206 let ValueStackCons v vs = valueStack ctx in
209 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons [||x||] vs })
210 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons [||y||] vs })
213 choices fs ks kd = Gen
214 { minHorizon = \ls -> minimum $
216 (($ ls) . minHorizon <$> ks)
218 let ValueStackCons v vs = valueStack ctx in
219 go ctx{valueStack = vs} v fs ks
222 go ctx x (f:fs') (k:ks') = [||
223 if $$(liftCode1 f x) then $$(unGen k ctx)
224 else $$(go ctx x fs' ks')
226 go ctx _ _ _ = unGen kd ctx
227 instance Failable Gen where
229 { minHorizon = \_hs -> 0
230 , unGen = \ctx@GenCtx{} -> [||
231 let (# farInp, farExp #) =
232 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
233 LT -> (# $$(input ctx), failExp #)
234 EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
235 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
236 $$(failStackHead (failStack ctx))
237 $$(input ctx) farInp farExp
242 let FailStackCons _e es = failStack ctx in
243 unGen k ctx{failStack = es}
245 catchFail ok ko = Gen
246 { minHorizon = \ls -> minHorizon ok ls `min` minHorizon ko ls
247 , unGen = \ctx@GenCtx{} -> [||
248 let _ = "catchFail" in $$(unGen ok ctx
249 { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
250 -- trace ("catchFail: " <> "farExp="<>show farExp) $
252 -- Push the input as it was when entering the catchFail.
253 { valueStack = ValueStackCons (input ctx) (valueStack ctx)
254 -- Move the input to the failing position.
255 , input = [||failInp||]
256 -- Set the farthestInput to the farthest computed by 'fail'
257 , farthestInput = [||farInp||]
258 , farthestExpecting = [||farExp||]
264 instance Inputable Gen where
267 let ValueStackCons input vs = valueStack ctx in
276 unGen k ctx{valueStack = ValueStackCons (input ctx) (valueStack ctx)}
278 instance Routinable Gen where
279 call (LetName n) k = k
280 { minHorizon = \hs -> hs Map.! n
281 , unGen = \ctx -> [||
283 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
284 {-ok-}$$(generateSuspend k ctx)
286 $! $$(failStackHead (failStack ctx))
289 jump (LetName n) = Gen
290 { minHorizon = \hs -> hs Map.! n
291 , unGen = \ctx -> [||
293 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
294 {-ok-}$$(retCode ctx)
296 $! $$(failStackHead (failStack ctx))
300 { minHorizon = \_hs -> 0
301 , unGen = \ctx -> unGen (generateResume (retCode ctx)) ctx
303 subroutine (LetName n) sub k = Gen
304 { minHorizon = \hs ->
306 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
307 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
308 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
310 -- Why using $! at call site and not ! here on ko?
313 { valueStack = ValueStackEmpty
314 , failStack = FailStackCons [||ko||] FailStackEmpty
317 -- , farthestInput = [|inp|]
318 -- , farthestExpecting = [|| [] ||]
320 , horizonByName = Map.insert n 0 (horizonByName ctx)
323 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
324 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
328 (Map.insert n 0 (horizonByName ctx)))
331 return (TH.LetE [decl] expr)
334 -- | Generate a continuation to be called with 'generateResume',
335 -- used when 'call' 'ret'urns.
337 {-k-}Gen inp (v ': vs) es a ->
338 GenCtx inp vs es a ->
340 generateSuspend k ctx = [||
342 \farInp farExp v !inp ->
344 { valueStack = ValueStackCons [||v||] (valueStack ctx)
346 , farthestInput = [||farInp||]
347 , farthestExpecting = [||farExp||]
353 -- | Generate a call to the 'generateSuspend' continuation,
354 -- used when 'call' 'ret'urns.
356 CodeQ (Cont inp v a) ->
357 Gen inp (v ': vs) es a
358 generateResume k = Gen
359 { minHorizon = \_hs -> 0
360 , unGen = \ctx -> [||
363 $$(farthestInput ctx)
364 $$(farthestExpecting ctx)
365 $$(valueStackHead (valueStack ctx))
370 instance Joinable Gen where
371 defJoin (LetName n) sub k = k
372 { minHorizon = \hs ->
374 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
375 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
376 body <- TH.unTypeQ $ TH.examineCode $ [||
377 \farInp farExp v !inp ->
379 { valueStack = ValueStackCons [||v||] (valueStack ctx)
381 , farthestInput = [||farInp||]
382 , farthestExpecting = [||farExp||]
384 , horizonByName = Map.insert n 0 (horizonByName ctx)
387 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
388 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
392 (Map.insert n 0 (horizonByName ctx)))
395 return (TH.LetE [decl] expr)
397 refJoin (LetName n) =
398 generateResume (Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
399 instance Readable Gen Char where
400 read farExp p = checkHorizon . checkToken farExp (liftCode p)
403 TH.Lift (InputToken inp) =>
404 {-ok-}Gen inp vs ('Succ es) a ->
405 Gen inp vs ('Succ es) a
407 { minHorizon = \hs -> 1 + minHorizon ok hs
408 , unGen = \ctx0@GenCtx{failStack = FailStackCons e es} -> [||
409 -- Factorize failure code
410 let readFail = $$(e) in
412 let ctx = ctx0{ failStack = FailStackCons [||readFail||] es } in
414 then unGen ok ctx0{horizon = horizon ctx - 1}
415 else let minHoz = minHorizon ok (horizonByName ctx) in
419 then [||$$shiftRight minHoz $$(input ctx)||]
421 then $$(unGen ok ctx{horizon = minHoz})
422 else let _ = "checkHorizon.else" in
423 $$(unGen (fail [ErrorItemHorizon (minHoz + 1)]) ctx)
431 Ord (InputToken inp) =>
432 TH.Lift (InputToken inp) =>
433 [ErrorItem (InputToken inp)] ->
434 {-predicate-}CodeQ (InputToken inp -> Bool) ->
435 {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
436 Gen inp vs ('Succ es) a
437 checkToken farExp p ok = ok
438 { unGen = \ctx -> [||
439 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
442 { valueStack = ValueStackCons [||c||] (valueStack ctx)
445 else let _ = "checkToken.else" in $$(unGen (fail farExp) ctx)
449 liftCode :: InstrPure a -> CodeQ a
451 {-# INLINE liftCode #-}
453 liftCode1 :: InstrPure (a -> b) -> CodeQ a -> CodeQ b
454 liftCode1 p a = case p of
455 InstrPureSameOffset -> [|| $$sameOffset $$a ||]
456 InstrPureHaskell h -> go a h
458 go :: CodeQ a -> H.Haskell (a -> b) -> CodeQ b
460 (H.:$) -> [|| \x -> $$qa x ||]
461 (H.:.) -> [|| \g x -> $$qa (g x) ||]
462 H.Flip -> [|| \x y -> $$qa y x ||]
463 (H.:.) H.:@ f H.:@ g -> [|| $$(go (go qa g) f) ||]
464 H.Const -> [|| \_ -> $$qa ||]
465 H.Flip H.:@ H.Const -> H.id
466 h@(H.Flip H.:@ _f) -> [|| \x -> $$(liftCode2 (InstrPureHaskell h) qa [||x||]) ||]
467 H.Eq x -> [|| $$(trans x) Eq.== $$qa ||]
469 h -> [|| $$(trans h) $$qa ||]
471 liftCode2 :: InstrPure (a -> b -> c) -> CodeQ a -> CodeQ b -> CodeQ c
472 liftCode2 p a b = case p of
473 InstrPureSameOffset -> [|| $$sameOffset $$a $$b ||]
474 InstrPureHaskell h -> go a b h
476 go :: CodeQ a -> CodeQ b -> H.Haskell (a -> b -> c) -> CodeQ c
478 (H.:$) -> [|| $$qa $$qb ||]
479 (H.:.) -> [|| \x -> $$qa ($$qb x) ||]
480 H.Flip -> [|| \x -> $$qa x $$qb ||]
481 H.Flip H.:@ H.Const -> [|| $$qb ||]
482 H.Flip H.:@ f -> go qb qa f
483 H.Const -> [|| $$qa ||]
484 H.Cons -> [|| $$qa : $$qb ||]
485 h -> [|| $$(trans h) $$qa $$qb ||]