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.Maybe (Maybe(..))
16 import Data.Ord (Ord, Ordering(..))
17 import Data.Semigroup (Semigroup(..))
19 import Language.Haskell.TH (CodeQ, Code(..))
21 import Text.Show (Show(..))
22 import qualified Data.Eq as Eq
23 import qualified Data.Set as Set
24 import qualified Language.Haskell.TH.Syntax as TH
26 import Symantic.Univariant.Trans
27 import Symantic.Parser.Grammar.Combinators (ErrorItem(..))
28 import Symantic.Parser.Machine.Input
29 import Symantic.Parser.Machine.Instructions
30 import qualified Symantic.Parser.Haskell as H
33 -- | Generate the 'CodeQ' parsing the input.
34 newtype Gen inp vs es a = Gen { unGen ::
36 CodeQ (Either (ParsingError inp) a)
39 -- ** Type 'ParsingError'
41 = ParsingErrorStandard
42 { parsingErrorOffset :: Offset
43 , parsingErrorUnexpected :: Maybe (InputToken inp)
44 , parsingErrorExpecting :: Set (ErrorItem (InputToken inp))
46 deriving instance Show (InputToken inp) => Show (ParsingError inp)
53 {-farthestInput-}Cursor inp ->
54 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
57 Either (ParsingError inp) a
59 -- ** Type 'SubRoutine'
60 type SubRoutine inp v a =
63 {-ko-}FailHandler inp a ->
64 Either (ParsingError inp) a
66 -- ** Type 'FailHandler'
67 type FailHandler inp a =
68 {-failureInput-}Cursor inp ->
69 {-farthestInput-}Cursor inp ->
70 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
71 Either (ParsingError inp) a
74 -- *** Type 'FarthestError'
75 data FarthestError inp = FarthestError
76 { farthestInput :: Cursor inp
77 , farthestExpecting :: [ErrorItem (InputToken inp)]
81 -- | @('generate' input mach)@ generates @TemplateHaskell@ code
82 -- parsing given 'input' according to given 'mach'ine.
85 Ord (InputToken inp) =>
86 Show (InputToken inp) =>
87 TH.Lift (InputToken inp) =>
88 -- InputToken inp ~ Char =>
92 Gen inp '[] ('Succ 'Zero) ret ->
93 CodeQ (Either (ParsingError inp) ret)
94 generate input (Gen k) = [||
95 -- Pattern bindings containing unlifted types
96 -- should use an outermost bang pattern.
97 let !(# init, readMore, readNext #) = $$(cursorOf input) in
98 let finalRet = \_farInp _farExp v _inp -> Right v in
99 let finalFail _failInp !farInp !farExp =
100 Left ParsingErrorStandard
101 { parsingErrorOffset = offset farInp
102 , parsingErrorUnexpected =
104 then Just (let (# c, _ #) = readNext farInp in c)
106 , parsingErrorExpecting = Set.fromList farExp
109 { valueStack = ValueStackEmpty
110 , failStack = FailStackCons [||finalFail||] FailStackEmpty
111 , retCode = [||finalRet||]
113 , nextInput = [||readNext||]
114 , moreInput = [||readMore||]
115 -- , farthestError = [||Nothing||]
116 , farthestInput = [||init||]
117 , farthestExpecting = [|| [] ||]
122 -- | This is a context only present at compile-time.
123 data GenCtx inp vs (es::Peano) a =
124 ( TH.Lift (InputToken inp)
125 , Cursorable (Cursor inp)
126 , Show (InputToken inp)
127 -- , InputToken inp ~ Char
129 { valueStack :: ValueStack vs
130 , failStack :: FailStack inp es a
131 , retCode :: CodeQ (Cont inp a a)
132 , input :: CodeQ (Cursor inp)
133 , moreInput :: CodeQ (Cursor inp -> Bool)
134 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
135 , farthestInput :: CodeQ (Cursor inp)
136 , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
139 -- ** Type 'ValueStack'
140 data ValueStack vs where
141 ValueStackEmpty :: ValueStack '[]
143 -- TODO: maybe use H.Haskell instead of CodeQ ?
144 -- as in https://github.com/j-mie6/ParsleyHaskell/popFail/3ec0986a5017866919a6404c14fe78678b7afb46
145 { valueStackHead :: CodeQ v
146 , valueStackTail :: ValueStack vs
147 } -> ValueStack (v ': vs)
149 -- ** Type 'FailStack'
150 data FailStack inp es a where
151 FailStackEmpty :: FailStack inp 'Zero a
153 { failStackHead :: CodeQ (FailHandler inp a)
154 , failStackTail :: FailStack inp es a
156 FailStack inp ('Succ es) a
158 instance Stackable Gen where
159 push x k = Gen $ \ctx -> unGen k ctx
160 { valueStack = ValueStackCons (liftCode x) (valueStack ctx) }
161 pop k = Gen $ \ctx -> unGen k ctx
162 { valueStack = valueStackTail (valueStack ctx) }
163 liftI2 f k = Gen $ \ctx -> unGen k ctx
165 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
166 ValueStackCons (liftCode2 f x y) xs
168 swap k = Gen $ \ctx -> unGen k ctx
170 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
171 ValueStackCons x (ValueStackCons y xs)
173 instance Branchable Gen where
174 case_ kx ky = Gen $ \ctx ->
175 let ValueStackCons v vs = valueStack ctx in
178 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons [||x||] vs })
179 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons [||y||] vs })
181 choices fs ks kd = Gen $ \ctx ->
182 let ValueStackCons v vs = valueStack ctx in
183 go ctx{valueStack = vs} v fs ks
185 go ctx x (f:fs') (Gen k:ks') = [||
186 if $$(liftCode1 f x) then $$(k ctx)
187 else $$(go ctx x fs' ks')
189 go ctx _ _ _ = unGen kd ctx
190 instance Failable Gen where
191 fail failExp = Gen $ \ctx@GenCtx{} -> [||
192 let (# farInp, farExp #) =
193 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
194 LT -> (# $$(input ctx), failExp #)
195 EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
196 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
199 <>" failExp="<>show @[ErrorItem Char] failExp
200 <>" farthestExpecting="<>show @[ErrorItem Char] ($$(farthestExpecting ctx))
201 <>" farExp="<>show @[ErrorItem Char] farExp) $
203 $$(failStackHead (failStack ctx))
204 $$(input ctx) farInp farExp
206 popFail k = Gen $ \ctx ->
207 let FailStackCons _e es = failStack ctx in
208 unGen k ctx{failStack = es}
209 catchFail ok ko = Gen $ \ctx@GenCtx{} -> [||
210 let _ = "catchFail" in $$(unGen ok ctx
211 { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
212 -- trace ("catchFail: " <> "farExp="<>show farExp) $
214 -- Push the input as it was when entering the catchFail.
215 { valueStack = ValueStackCons (input ctx) (valueStack ctx)
216 -- Move the input to the failing position.
217 , input = [||failInp||]
218 -- Set the farthestInput to the farthest computed by 'fail'
219 , farthestInput = [||farInp||]
220 , farthestExpecting = [||farExp||]
225 instance Inputable Gen where
226 loadInput k = Gen $ \ctx ->
227 let ValueStackCons input vs = valueStack ctx in
228 unGen k ctx{valueStack = vs, input}
229 pushInput k = Gen $ \ctx ->
230 unGen k ctx{valueStack = ValueStackCons (input ctx) (valueStack ctx)}
231 instance Routinable Gen where
232 call (LetName n) k = Gen $ \ctx -> [||
234 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
237 $! $$(failStackHead (failStack ctx))
239 jump (LetName n) = Gen $ \ctx -> [||
241 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
244 $! $$(failStackHead (failStack ctx))
246 ret = Gen $ \ctx -> unGen (resume (retCode ctx)) ctx
247 subroutine (LetName n) sub k = Gen $ \ctx -> Code $ TH.unsafeTExpCoerce $ do
248 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
250 -- Why using $! at call site and not ! here on ko?
253 { valueStack = ValueStackEmpty
254 , failStack = FailStackCons [||ko||] FailStackEmpty
257 -- , farthestInput = [|inp|]
258 -- , farthestExpecting = [|| [] ||]
261 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
262 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx))
263 return (TH.LetE [decl] expr)
266 {-k-}Gen inp (v ': vs) es a ->
267 GenCtx inp vs es a ->
271 \farInp farExp v !inp ->
273 { valueStack = ValueStackCons [||v||] (valueStack ctx)
275 , farthestInput = [||farInp||]
276 , farthestExpecting = [||farExp||]
281 resume :: CodeQ (Cont inp v a) -> Gen inp (v ': vs) es a
282 resume k = Gen $ \ctx -> [||
285 $$(farthestInput ctx)
286 $$(farthestExpecting ctx)
287 $$(valueStackHead (valueStack ctx))
291 instance Joinable Gen where
292 defJoin (LetName n) sub k = Gen $ \ctx -> Code $ TH.unsafeTExpCoerce $ do
293 body <- TH.unTypeQ $ TH.examineCode $ [||
294 \farInp farExp v !inp ->
296 { valueStack = ValueStackCons [||v||] (valueStack ctx)
298 , farthestInput = [||farInp||]
299 , farthestExpecting = [||farExp||]
302 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
303 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx))
304 return (TH.LetE [decl] expr)
305 refJoin (LetName n) =
306 resume (Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
307 instance Readable Gen Char where
310 maybeEmitCheck (Just 1) k
312 maybeEmitCheck Nothing ok = sat (liftCode p) ok (fail farExp)
313 maybeEmitCheck (Just n) ok = Gen $ \ctx ->
314 let FailStackCons e es = failStack ctx in
316 let readFail = $$(e) in -- Factorize failure code
317 $$((`unGen` ctx{failStack = FailStackCons [||readFail||] es}) $ emitLengthCheck n
318 {-ok-}(sat (liftCode p) ok
325 -- Cursorable (Cursor inp) =>
326 -- InputToken inp ~ Char =>
327 Ord (InputToken inp) =>
328 TH.Lift (InputToken inp) =>
329 {-predicate-}CodeQ (InputToken inp -> Bool) ->
330 {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
331 {-ko-}Gen inp vs ('Succ es) a ->
332 Gen inp vs ('Succ es) a
333 sat p ok ko = Gen $ \ctx -> [||
334 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
337 { valueStack = ValueStackCons [||c||] (valueStack ctx)
340 else let _ = "sat.else" in $$(unGen ko ctx)
347 InstrPure (Char -> Bool) ->
348 Gen inp (Char ': vs) ('Succ es) a ->
349 Gen inp vs ('Succ es) a
351 bankrupt <- asks isBankrupt
352 hasChange <- asks hasCoin
353 if | bankrupt -> maybeEmitCheck (Just 1) <$> k
354 | hasChange -> maybeEmitCheck Nothing <$> local spendCoin k
355 | otherwise -> local breakPiggy (maybeEmitCheck . Just <$> asks coins <*> local spendCoin k)
357 maybeEmitCheck Nothing mk ctx = sat (genDefunc p) mk (raise ctx) ctx
358 maybeEmitCheck (Just n) mk ctx =
359 [|| let bad = $$(raise ctx) in $$(emitLengthCheck n (sat (genDefunc p) mk [||bad||]) [||bad||] ctx)||]
363 TH.Lift (InputToken inp) =>
364 Int -> Gen inp vs es a -> Gen inp vs es a -> Gen inp vs es a
365 emitLengthCheck 0 ok _ko = ok
366 emitLengthCheck 1 ok ko = Gen $ \ctx -> [||
367 if $$(moreInput ctx) $$(input ctx)
368 then $$(unGen ok ctx)
369 else let _ = "sat.length-check.else" in $$(unGen ko ctx)
372 emitLengthCheck n ok ko ctx = Gen $ \ctx -> [||
373 if $$moreInput ($$shiftRight $$(input ctx) (n - 1))
374 then $$(unGen ok ctx)
375 else $$(unGen ko ctx {farthestExpecting = [||farExp||]})
380 liftCode :: InstrPure a -> CodeQ a
382 {-# INLINE liftCode #-}
384 liftCode1 :: InstrPure (a -> b) -> CodeQ a -> CodeQ b
385 liftCode1 p a = case p of
386 InstrPureSameOffset -> [|| $$sameOffset $$a ||]
387 InstrPureHaskell h -> go a h
389 go :: CodeQ a -> H.Haskell (a -> b) -> CodeQ b
391 (H.:$) -> [|| \x -> $$qa x ||]
392 (H.:.) -> [|| \g x -> $$qa (g x) ||]
393 H.Flip -> [|| \x y -> $$qa y x ||]
394 (H.:.) H.:@ f H.:@ g -> [|| $$(go (go qa g) f) ||]
395 H.Const -> [|| \_ -> $$qa ||]
396 H.Flip H.:@ H.Const -> H.id
397 h@(H.Flip H.:@ _f) -> [|| \x -> $$(liftCode2 (InstrPureHaskell h) qa [||x||]) ||]
398 H.Eq x -> [|| $$(trans x) Eq.== $$qa ||]
400 h -> [|| $$(trans h) $$qa ||]
402 liftCode2 :: InstrPure (a -> b -> c) -> CodeQ a -> CodeQ b -> CodeQ c
403 liftCode2 p a b = case p of
404 InstrPureSameOffset -> [|| $$sameOffset $$a $$b ||]
405 InstrPureHaskell h -> go a b h
407 go :: CodeQ a -> CodeQ b -> H.Haskell (a -> b -> c) -> CodeQ c
409 (H.:$) -> [|| $$qa $$qb ||]
410 (H.:.) -> [|| \x -> $$qa ($$qb x) ||]
411 H.Flip -> [|| \x -> $$qa x $$qb ||]
412 H.Flip H.:@ H.Const -> [|| $$qb ||]
413 H.Flip H.:@ f -> go qb qa f
414 H.Const -> [|| $$qa ||]
415 H.Cons -> [|| $$qa : $$qb ||]
416 h -> [|| $$(trans h) $$qa $$qb ||]