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 -- | @('generateCode' input mach)@ generates @TemplateHaskell@ code
106 -- parsing the given 'input' according to the given 'Machine'.
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 generateCode 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 a es
157 --, failStacks :: FailStack inp es a
158 , retCode :: CodeQ (Cont inp a a)
159 , input :: CodeQ (Cursor inp)
160 , moreInput :: CodeQ (Cursor inp -> Bool)
161 , nextInput :: CodeQ (Cursor inp -> (# InputToken inp, Cursor inp #))
162 , farthestInput :: CodeQ (Cursor inp)
163 , farthestExpecting :: CodeQ [ErrorItem (InputToken inp)]
164 -- | Remaining horizon
166 -- | Horizon for each 'call' or 'jump'.
167 , horizonByName :: Map TH.Name Offset
170 -- ** Type 'ValueStack'
171 data ValueStack vs where
172 ValueStackEmpty :: ValueStack '[]
174 { valueStackHead :: TermInstr v
175 , valueStackTail :: ValueStack vs
176 } -> ValueStack (v ': vs)
178 -- ** Type 'FailStack'
179 data FailStack inp a es where
180 FailStackEmpty :: FailStack inp a 'Zero
182 { failStackHead :: CodeQ (FailHandler inp a)
183 , failStackTail :: FailStack inp a es
185 FailStack inp a ('Succ es)
187 instance Stackable Gen where
189 { unGen = \ctx -> unGen k ctx
190 { valueStack = ValueStackCons x (valueStack ctx) }
193 { unGen = \ctx -> unGen k ctx
194 { valueStack = valueStackTail (valueStack ctx) }
197 { unGen = \ctx -> unGen k ctx
199 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
200 ValueStackCons (f H.:@ x H.:@ y) xs
204 { unGen = \ctx -> unGen k ctx
206 let ValueStackCons y (ValueStackCons x xs) = valueStack ctx in
207 ValueStackCons x (ValueStackCons y xs)
210 instance Branchable Gen where
212 { minHorizon = \ls ->
213 minHorizon kx ls `min` minHorizon ky ls
215 let ValueStackCons v vs = valueStack ctx in
217 case $$(genCode v) of
218 Left x -> $$(unGen kx ctx{ valueStack = ValueStackCons (H.Term [||x||]) vs })
219 Right y -> $$(unGen ky ctx{ valueStack = ValueStackCons (H.Term [||y||]) vs })
222 choices fs ks kd = Gen
223 { minHorizon = \ls -> minimum $
225 (($ ls) . minHorizon <$> ks)
227 let ValueStackCons v vs = valueStack ctx in
228 go ctx{valueStack = vs} v fs ks
231 go ctx x (f:fs') (k:ks') = [||
232 if $$(genCode (f H.:@ x))
234 else $$(go ctx x fs' ks')
236 go ctx _ _ _ = unGen kd ctx
237 instance Failable Gen where
239 { minHorizon = \_hs -> 0
240 , unGen = \ctx@GenCtx{} -> [||
241 let (# farInp, farExp #) =
242 case $$compareOffset $$(farthestInput ctx) $$(input ctx) of
243 LT -> (# $$(input ctx), failExp #)
244 EQ -> (# $$(farthestInput ctx), ($$(farthestExpecting ctx) <> failExp) #)
245 GT -> (# $$(farthestInput ctx), $$(farthestExpecting ctx) #) in
246 $$(failStackHead (failStack ctx))
247 $$(input ctx) farInp farExp
252 unGen k ctx{failStack = failStackTail (failStack ctx)}
254 catchFail ok ko = Gen
255 { minHorizon = \ls -> minHorizon ok ls `min` minHorizon ko ls
256 , unGen = \ctx@GenCtx{} -> unGen ok ctx
257 { failStack = FailStackCons [|| \(!failInp) (!farInp) (!farExp) ->
258 -- trace ("catchFail: " <> "farExp="<>show farExp) $
260 -- Push the input as it was when entering the catchFail.
261 { valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)
262 -- Move the input to the failing position.
263 , input = [||failInp||]
264 -- Set the farthestInput to the farthest computed by 'fail'
265 , farthestInput = [||farInp||]
266 , farthestExpecting = [||farExp||]
271 instance Inputable Gen where
274 let ValueStackCons input vs = valueStack ctx in
277 , input = genCode input
283 unGen k ctx{valueStack = ValueStackCons (H.Term (input ctx)) (valueStack ctx)}
285 instance Routinable Gen where
286 call (LetName n) k = k
287 { minHorizon = (Map.! n)
288 , unGen = \ctx -> [||
290 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
291 {-ok-}$$(generateSuspend k ctx)
293 $! $$(failStackHead (failStack ctx))
296 jump (LetName n) = Gen
297 { minHorizon = (Map.! n)
298 , unGen = \ctx -> [||
300 $$(Code (TH.unsafeTExpCoerce (return (TH.VarE n))))
301 {-ok-}$$(retCode ctx)
303 $! $$(failStackHead (failStack ctx))
307 { minHorizon = \_hs -> 0
308 , unGen = \ctx -> unGen (generateResume (retCode ctx)) ctx
310 subroutine (LetName n) sub k = Gen
311 { minHorizon = \hs ->
313 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
314 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
315 body <- TH.unTypeQ $ TH.examineCode $ [|| -- buildRec in Parsley
317 -- Why using $! at call site and not ! here on ko?
320 { valueStack = ValueStackEmpty
321 , failStack = FailStackCons [||ko||] FailStackEmpty
324 -- , farthestInput = [|inp|]
325 -- , farthestExpecting = [|| [] ||]
327 , horizonByName = Map.insert n 0 (horizonByName ctx)
330 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
331 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
335 (Map.insert n 0 (horizonByName ctx)))
338 return (TH.LetE [decl] expr)
341 -- | Generate a continuation to be called with 'generateResume',
342 -- used when 'call' 'ret'urns.
344 {-k-}Gen inp (v ': vs) es a ->
345 GenCtx inp vs es a ->
347 generateSuspend k ctx = [||
349 \farInp farExp v !inp ->
351 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
353 , farthestInput = [||farInp||]
354 , farthestExpecting = [||farExp||]
360 -- | Generate a call to the 'generateSuspend' continuation,
361 -- used when 'call' 'ret'urns.
363 CodeQ (Cont inp v a) ->
364 Gen inp (v ': vs) es a
365 generateResume k = Gen
366 { minHorizon = \_hs -> 0
367 , unGen = \ctx -> [||
370 $$(farthestInput ctx)
371 $$(farthestExpecting ctx)
372 (let _ = "resume.genCode" in $$(genCode (valueStackHead (valueStack ctx))))
377 instance Joinable Gen where
378 defJoin (LetName n) sub k = k
379 { minHorizon = \hs ->
381 Map.insert n (minHorizon sub (Map.insert n 0 hs)) hs
382 , unGen = \ctx -> Code $ TH.unsafeTExpCoerce $ do
383 body <- TH.unTypeQ $ TH.examineCode $ [||
384 \farInp farExp v !inp ->
386 { valueStack = ValueStackCons (H.Term [||v||]) (valueStack ctx)
388 , farthestInput = [||farInp||]
389 , farthestExpecting = [||farExp||]
391 , horizonByName = Map.insert n 0 (horizonByName ctx)
394 let decl = TH.FunD n [TH.Clause [] (TH.NormalB body) []]
395 expr <- TH.unTypeQ (TH.examineCode (unGen k ctx
399 (Map.insert n 0 (horizonByName ctx)))
402 return (TH.LetE [decl] expr)
404 refJoin (LetName n) = (generateResume (Code (TH.unsafeTExpCoerce (return (TH.VarE n)))))
405 { minHorizon = (Map.! n)
407 instance Readable Char Gen where
408 read farExp p = checkHorizon . checkToken farExp p
411 TH.Lift (InputToken inp) =>
412 {-ok-}Gen inp vs ('Succ es) a ->
413 Gen inp vs ('Succ es) a
415 { minHorizon = \hs -> 1 + minHorizon ok hs
416 , unGen = \ctx0@GenCtx{failStack = FailStackCons e es} -> [||
417 -- Factorize failure code
418 let readFail = $$(e) in
420 let ctx = ctx0{ failStack = FailStackCons [||readFail||] es } in
422 then unGen ok ctx0{horizon = horizon ctx - 1}
423 else let minHoz = minHorizon ok (horizonByName ctx) in
427 then [||$$shiftRight minHoz $$(input ctx)||]
429 then $$(unGen ok ctx{horizon = minHoz})
430 else let _ = "checkHorizon.else" in
431 $$(unGen (fail [ErrorItemHorizon (minHoz + 1)]) ctx)
439 Ord (InputToken inp) =>
440 TH.Lift (InputToken inp) =>
441 [ErrorItem (InputToken inp)] ->
442 {-predicate-}TermInstr (InputToken inp -> Bool) ->
443 {-ok-}Gen inp (InputToken inp ': vs) ('Succ es) a ->
444 Gen inp vs ('Succ es) a
445 checkToken farExp p ok = ok
446 { unGen = \ctx -> [||
447 let !(# c, cs #) = $$(nextInput ctx) $$(input ctx) in
450 { valueStack = ValueStackCons (H.Term [||c||]) (valueStack ctx)
453 else let _ = "checkToken.else" in $$(unGen (fail farExp) ctx)