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.Map.Strict as Map
25 import qualified Data.Set as Set
26 import qualified Language.Haskell.TH.Syntax as TH
27 -- import qualified Control.Monad.Trans.Writer as Writer
29 import Symantic.Univariant.Trans
30 import Symantic.Parser.Grammar.Combinators (ErrorItem(..))
31 import Symantic.Parser.Machine.Input
32 import Symantic.Parser.Machine.Instructions
33 import qualified Symantic.Parser.Haskell as H
35 genCode :: TermInstr a -> CodeQ a
39 -- | Generate the 'CodeQ' parsing the input.
40 data Gen inp vs es a = Gen
41 { minHorizon :: Map TH.Name Horizon -> Horizon
42 -- ^ Minimal input length required by the parser to not fail.
43 -- This requires to be given an 'horizonByName'
44 -- containing the 'Horizon's of all the 'TH.Name's
45 -- this parser 'call's, 'jump's or 'refJoin's to.
48 CodeQ (Either (ParsingError inp) a)
51 -- ** Type 'ParsingError'
53 = ParsingErrorStandard
54 { parsingErrorOffset :: Offset
55 -- | Note that if an 'ErrorItemHorizon' greater than 1
56 -- is amongst the 'parsingErrorExpecting'
57 -- then this is only the 'InputToken'
58 -- at the begining of the expected 'Horizon'.
59 , parsingErrorUnexpected :: Maybe (InputToken inp)
60 , parsingErrorExpecting :: Set (ErrorItem (InputToken inp))
62 deriving instance Show (InputToken inp) => Show (ParsingError inp)
68 -- | Synthetized minimal input length
69 -- required for a successful parsing.
70 -- Used with 'horizon' to factorize input length checks,
71 -- instead of checking the input length
72 -- one 'InputToken' by one 'InputToken' at each 'read'.
77 {-farthestInput-}Cursor inp ->
78 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
81 Either (ParsingError inp) a
83 -- ** Type 'SubRoutine'
84 type SubRoutine inp v a =
87 {-ko-}FailHandler inp a ->
88 Either (ParsingError inp) a
90 -- ** Type 'FailHandler'
91 type FailHandler inp a =
92 {-failureInput-}Cursor inp ->
93 {-farthestInput-}Cursor inp ->
94 {-farthestExpecting-}[ErrorItem (InputToken inp)] ->
95 Either (ParsingError inp) a
98 -- *** Type 'FarthestError'
99 data FarthestError inp = FarthestError
100 { farthestInput :: Cursor inp
101 , farthestExpecting :: [ErrorItem (InputToken inp)]
105 -- | @('generate' input mach)@ generates @TemplateHaskell@ code
106 -- parsing given 'input' according to given 'mach'ine.
109 Ord (InputToken inp) =>
110 Show (InputToken inp) =>
111 TH.Lift (InputToken inp) =>
112 -- InputToken inp ~ Char =>
116 Gen inp '[] ('Succ 'Zero) ret ->
117 CodeQ (Either (ParsingError inp) ret)
118 generate input k = [||
119 -- Pattern bindings containing unlifted types
120 -- should use an outermost bang pattern.
121 let !(# init, readMore, readNext #) = $$(cursorOf input) in
122 let finalRet = \_farInp _farExp v _inp -> Right v in
123 let finalFail _failInp !farInp !farExp =
124 Left ParsingErrorStandard
125 { parsingErrorOffset = offset farInp
126 , parsingErrorUnexpected =
128 then Just (let (# c, _ #) = readNext farInp in c)
130 , parsingErrorExpecting = Set.fromList farExp
133 { valueStack = ValueStackEmpty
134 , failStack = FailStackCons [||finalFail||] FailStackEmpty
135 , retCode = [||finalRet||]
137 , nextInput = [||readNext||]
138 , moreInput = [||readMore||]
139 -- , farthestError = [||Nothing||]
140 , farthestInput = [||init||]
141 , farthestExpecting = [|| [] ||]
143 , horizonByName = Map.empty
148 -- | This is a context only present at compile-time.
149 data GenCtx inp vs (es::Peano) a =
150 ( TH.Lift (InputToken inp)
151 , Cursorable (Cursor inp)
152 , Show (InputToken inp)
153 -- , InputToken inp ~ Char
155 { valueStack :: ValueStack vs
156 , failStack :: FailStack inp es a
157 , retCode :: CodeQ (Cont inp a a)
158 , input :: CodeQ (Cursor inp)
159 , moreInput :: CodeQ (Cursor inp -> Bool)
160 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
161 , farthestInput :: CodeQ (Cursor inp)
162 , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
163 -- | Remaining horizon
165 -- | Horizon for each 'call' or 'jump'.
166 , horizonByName :: Map TH.Name Offset
169 -- ** Type 'ValueStack'
170 data ValueStack vs where
171 ValueStackEmpty :: ValueStack '[]
173 { valueStackHead :: TermInstr v
174 , valueStackTail :: ValueStack vs
175 } -> ValueStack (v ': vs)
177 -- ** Type 'FailStack'
178 data FailStack inp es a where
179 FailStackEmpty :: FailStack inp 'Zero a
181 { failStackHead :: CodeQ (FailHandler inp a)
182 , failStackTail :: FailStack inp es a
184 FailStack inp ('Succ es) a
186 instance Stackable Gen where
188 { unGen = \ctx -> unGen k ctx
189 { valueStack = ValueStackCons x (valueStack ctx) }
192 { unGen = \ctx -> unGen k ctx
193 { valueStack = valueStackTail (valueStack ctx) }
196 { unGen = \ctx -> unGen k ctx
198 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
199 ValueStackCons (f H.:@ x H.:@ y) xs
203 { unGen = \ctx -> unGen k ctx
205 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
206 ValueStackCons x (ValueStackCons y xs)
209 instance Branchable Gen where
211 { minHorizon = \ls ->
212 minHorizon kx ls `min` minHorizon ky ls
214 let ValueStackCons v vs = valueStack ctx in
216 case $$(genCode v) of
217 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (H.Term [||x||]) vs })
218 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (H.Term [||y||]) vs })
221 choices fs ks kd = Gen
222 { minHorizon = \ls -> minimum $
224 (($ ls) . minHorizon <$> ks)
226 let ValueStackCons v vs = valueStack ctx in
227 go ctx{valueStack = vs} v fs ks
230 go ctx x (f:fs') (k:ks') = [||
231 if $$(genCode (f H.:@ x))
233 else $$(go ctx x fs' ks')
235 go ctx _ _ _ = unGen kd ctx
236 instance Failable Gen where
238 { minHorizon = \_hs -> 0
239 , unGen = \ctx@GenCtx{} -> [||
240 let (# farInp, farExp #) =
241 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
242 LT -> (# $$(input ctx), failExp #)
243 EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
244 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
245 $$(failStackHead (failStack ctx))
246 $$(input ctx) farInp farExp
251 let FailStackCons _e es = failStack ctx in
252 unGen k ctx{failStack = es}
254 catchFail ok ko = Gen
255 { minHorizon = \ls -> minHorizon ok ls `min` minHorizon ko ls
256 , unGen = \ctx@GenCtx{} -> [||
257 let _ = "catchFail" in $$(unGen ok ctx
258 { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
259 -- trace ("catchFail: " <> "farExp="<>show farExp) $
261 -- Push the input as it was when entering the catchFail.
262 { valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)
263 -- Move the input to the failing position.
264 , input = [||failInp||]
265 -- Set the farthestInput to the farthest computed by 'fail'
266 , farthestInput = [||farInp||]
267 , farthestExpecting = [||farExp||]
273 instance Inputable Gen where
276 let ValueStackCons input vs = valueStack ctx in
279 , input = genCode input
285 unGen k ctx{valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)}
287 instance Routinable Gen where
288 call (LetName n) k = k
289 { minHorizon = (Map.! n)
290 , unGen = \ctx -> [||
292 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
293 {-ok-}$$(generateSuspend k ctx)
295 $! $$(failStackHead (failStack ctx))
298 jump (LetName n) = Gen
299 { minHorizon = (Map.! n)
300 , unGen = \ctx -> [||
302 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
303 {-ok-}$$(retCode ctx)
305 $! $$(failStackHead (failStack ctx))
309 { minHorizon = \_hs -> 0
310 , unGen = \ctx -> unGen (generateResume (retCode ctx)) ctx
312 subroutine (LetName n) sub k = Gen
313 { minHorizon = \hs ->
315 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
316 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
317 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
319 -- Why using $! at call site and not ! here on ko?
322 { valueStack = ValueStackEmpty
323 , failStack = FailStackCons [||ko||] FailStackEmpty
326 -- , farthestInput = [|inp|]
327 -- , farthestExpecting = [|| [] ||]
329 , horizonByName = Map.insert n 0 (horizonByName ctx)
332 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
333 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
337 (Map.insert n 0 (horizonByName ctx)))
340 return (TH.LetE [decl] expr)
343 -- | Generate a continuation to be called with 'generateResume',
344 -- used when 'call' 'ret'urns.
346 {-k-}Gen inp (v ': vs) es a ->
347 GenCtx inp vs es a ->
349 generateSuspend k ctx = [||
351 \farInp farExp v !inp ->
353 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
355 , farthestInput = [||farInp||]
356 , farthestExpecting = [||farExp||]
362 -- | Generate a call to the 'generateSuspend' continuation,
363 -- used when 'call' 'ret'urns.
365 CodeQ (Cont inp v a) ->
366 Gen inp (v ': vs) es a
367 generateResume k = Gen
368 { minHorizon = \_hs -> 0
369 , unGen = \ctx -> [||
372 $$(farthestInput ctx)
373 $$(farthestExpecting ctx)
374 (let _ = "resume.genCode" in $$(genCode (valueStackHead (valueStack ctx))))
379 instance Joinable Gen where
380 defJoin (LetName n) sub k = k
381 { minHorizon = \hs ->
383 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
384 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
385 body <- TH.unTypeQ $ TH.examineCode $ [||
386 \farInp farExp v !inp ->
388 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
390 , farthestInput = [||farInp||]
391 , farthestExpecting = [||farExp||]
393 , horizonByName = Map.insert n 0 (horizonByName ctx)
396 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
397 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
401 (Map.insert n 0 (horizonByName ctx)))
404 return (TH.LetE [decl] expr)
406 refJoin (LetName n) = (generateResume (Code (TH.unsafeTExpCoerce (return (TH.VarE n)))))
407 { minHorizon = (Map.! n)
409 instance Readable Gen Char where
410 read farExp p = checkHorizon . checkToken farExp p
413 TH.Lift (InputToken inp) =>
414 {-ok-}Gen inp vs ('Succ es) a ->
415 Gen inp vs ('Succ es) a
417 { minHorizon = \hs -> 1 + minHorizon ok hs
418 , unGen = \ctx0@GenCtx{failStack = FailStackCons e es} -> [||
419 -- Factorize failure code
420 let readFail = $$(e) in
422 let ctx = ctx0{ failStack = FailStackCons [||readFail||] es } in
424 then unGen ok ctx0{horizon = horizon ctx - 1}
425 else let minHoz = minHorizon ok (horizonByName ctx) in
429 then [||$$shiftRight minHoz $$(input ctx)||]
431 then $$(unGen ok ctx{horizon = minHoz})
432 else let _ = "checkHorizon.else" in
433 $$(unGen (fail [ErrorItemHorizon (minHoz + 1)]) ctx)
441 Ord (InputToken inp) =>
442 TH.Lift (InputToken inp) =>
443 [ErrorItem (InputToken inp)] ->
444 {-predicate-}TermInstr (InputToken inp -> Bool) ->
445 {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
446 Gen inp vs ('Succ es) a
447 checkToken farExp p ok = ok
448 { unGen = \ctx -> [||
449 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
452 { valueStack = ValueStackCons (H.Term [||c||]) (valueStack ctx)
455 else let _ = "checkToken.else" in $$(unGen (fail farExp) ctx)