1 {-# LANGUAGE ConstraintKinds #-} -- For Executable
2 {-# LANGUAGE DerivingStrategies #-} -- For Show (LetName a)
3 {-# LANGUAGE UndecidableInstances #-} -- For Cursorable (Cursor inp)
4 module Symantic.Parser.Machine.Instructions where
6 import Data.Bool (Bool(..))
7 import Data.Either (Either)
8 import Data.Eq (Eq(..))
10 import Data.Function (($), (.))
11 import Data.Kind (Type)
12 import System.IO.Unsafe (unsafePerformIO)
13 import Text.Show (Show(..))
14 import qualified Data.Functor as Functor
15 import qualified Language.Haskell.TH as TH
16 import qualified Language.Haskell.TH.Syntax as TH
17 import qualified Symantic.Parser.Haskell as H
19 import Symantic.Parser.Grammar
20 import Symantic.Parser.Machine.Input
21 import Symantic.Univariant.Trans
24 type TermInstr = H.Term TH.CodeQ
27 -- | 'Instr'uctions for the 'Machine'.
28 data Instr input valueStack (failStack::Peano) returnValue where
29 -- | @('Push' x k)@ pushes @(x)@ on the 'valueStack'
30 -- and continues with the next 'Instr'uction @(k)@.
33 Instr inp (v ': vs) es ret ->
35 -- | @('Pop' k)@ pushes @(x)@ on the 'valueStack'.
37 Instr inp vs es ret ->
38 Instr inp (v ': vs) es ret
39 -- | @('LiftI2' f k)@ pops two values from the 'valueStack',
40 -- and pushes the result of @(f)@ applied to them.
42 TermInstr (x -> y -> z) ->
43 Instr inp (z : vs) es ret ->
44 Instr inp (y : x : vs) es ret
45 -- | @('Fail')@ raises an error from the 'failStack'.
47 [ErrorItem (InputToken inp)] ->
48 Instr inp vs ('Succ es) ret
49 -- | @('PopFail' k)@ removes a 'FailHandler' from the 'failStack'
50 -- and continues with the next 'Instr'uction @(k)@.
52 Instr inp vs es ret ->
53 Instr inp vs ('Succ es) ret
54 -- | @('CatchFail' l r)@ tries the @(l)@ 'Instr'uction
55 -- in a new failure scope such that if @(l)@ raises a failure, it is caught,
56 -- then the input is pushed as it was before trying @(l)@ on the 'valueStack',
57 -- and the control flow goes on with the @(r)@ 'Instr'uction.
59 Instr inp vs ('Succ es) ret ->
60 Instr inp (Cursor inp ': vs) es ret ->
62 -- | @('LoadInput' k)@ removes the input from the 'valueStack'
63 -- and continues with the next 'Instr'uction @(k)@ using that input.
66 Instr inp (Cursor inp : vs) es r
67 -- | @('PushInput' k)@ pushes the input @(inp)@ on the 'valueStack'
68 -- and continues with the next 'Instr'uction @(k)@.
70 Instr inp (Cursor inp ': vs) es ret ->
74 Instr inp (x ': vs) es r ->
75 Instr inp (y ': vs) es r ->
76 Instr inp (Either x y ': vs) es r
77 -- | @('Swap' k)@ pops two values on the 'valueStack',
78 -- pushes the first popped-out, then the second,
79 -- and continues with the next 'Instr'uction @(k)@.
81 Instr inp (x ': y ': vs) es r ->
82 Instr inp (y ': x ': vs) es r
83 -- | @('Choices' ps bs d)@.
85 [TermInstr (v -> Bool)] ->
86 [Instr inp vs es ret] ->
87 Instr inp vs es ret ->
88 Instr inp (v ': vs) es ret
89 -- | @('Subroutine' n v k)@ binds the 'LetName' @(n)@ to the 'Instr'uction's @(v)@,
91 -- continues with the next 'Instr'uction @(k)@.
93 LetName v -> Instr inp '[] ('Succ 'Zero) v ->
94 Instr inp vs ('Succ es) ret ->
95 Instr inp vs ('Succ es) ret
96 -- | @('Jump' n k)@ pass the control-flow to the 'Subroutine' named @(n)@.
99 Instr inp '[] ('Succ es) ret
100 -- | @('Call' n k)@ pass the control-flow to the 'Subroutine' named @(n)@,
101 -- and when it 'Ret'urns, continues with the next 'Instr'uction @(k)@.
104 Instr inp (v ': vs) ('Succ es) ret ->
105 Instr inp vs ('Succ es) ret
106 -- | @('Ret')@ returns the value stored in a singleton 'valueStack'.
108 Instr inp '[ret] es ret
109 -- | @('Read' expected p k)@ reads a 'Char' @(c)@ from the 'inp'ut,
110 -- if @(p c)@ is 'True' then continues with the next 'Instr'uction @(k)@ on,
113 [ErrorItem (InputToken inp)] ->
114 TermInstr (InputToken inp -> Bool) ->
115 Instr inp (InputToken inp ': vs) ('Succ es) ret ->
116 Instr inp vs ('Succ es) ret
118 LetName v -> Instr inp (v ': vs) es ret ->
119 Instr inp vs es ret ->
123 Instr inp (v ': vs) es ret
126 newtype LetName a = LetName { unLetName :: TH.Name }
128 deriving newtype Show
130 -- * Class 'Executable'
131 type Executable repr =
140 -- ** Class 'Stackable'
141 class Stackable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
144 repr inp (v ': vs) n ret ->
148 repr inp (v ': vs) n ret
150 TermInstr (x -> y -> z) ->
151 repr inp (z ': vs) es ret ->
152 repr inp (y ': x ': vs) es ret
154 repr inp (x ': y ': vs) n r ->
155 repr inp (y ': x ': vs) n r
157 -- ** Class 'Branchable'
158 class Branchable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
160 repr inp (x ': vs) n r ->
161 repr inp (y ': vs) n r ->
162 repr inp (Either x y ': vs) n r
164 [TermInstr (v -> Bool)] ->
165 [repr inp vs es ret] ->
166 repr inp vs es ret ->
167 repr inp (v ': vs) es ret
169 -- ** Class 'Failable'
170 class Failable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
171 fail :: [ErrorItem (InputToken inp)] -> repr inp vs ('Succ es) ret
173 repr inp vs es ret ->
174 repr inp vs ('Succ es) ret
176 repr inp vs ('Succ es) ret ->
177 repr inp (Cursor inp ': vs) es ret ->
180 -- ** Class 'Inputable'
181 class Inputable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
184 repr inp (Cursor inp ': vs) es r
186 repr inp (Cursor inp ': vs) es ret ->
189 -- ** Class 'Routinable'
190 class Routinable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
192 LetName v -> repr inp '[] ('Succ 'Zero) v ->
193 repr inp vs ('Succ es) ret ->
194 repr inp vs ('Succ es) ret
196 LetName v -> repr inp (v ': vs) ('Succ es) ret ->
197 repr inp vs ('Succ es) ret
199 repr inp '[ret] es ret
202 repr inp '[] ('Succ es) ret
204 -- ** Class 'Joinable'
205 class Joinable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
208 repr inp (v ': vs) es ret ->
209 repr inp vs es ret ->
213 repr inp (v ': vs) es ret
215 -- ** Class 'Readable'
216 class Readable (repr :: Type -> [Type] -> Peano -> Type -> Type) (tok::Type) where
218 tok ~ InputToken inp =>
220 TermInstr (tok -> Bool) ->
221 repr inp (tok ': vs) ('Succ es) ret ->
222 repr inp vs ('Succ es) ret
226 , Readable repr (InputToken inp)
227 ) => Trans (Instr inp vs es) (repr inp vs es) where
229 Push x k -> push x (trans k)
230 Pop k -> pop (trans k)
231 LiftI2 f k -> liftI2 f (trans k)
233 PopFail k -> popFail (trans k)
234 CatchFail l r -> catchFail (trans l) (trans r)
235 LoadInput k -> loadInput (trans k)
236 PushInput k -> pushInput (trans k)
237 Case l r -> case_ (trans l) (trans r)
238 Swap k -> swap (trans k)
239 Choices ps bs d -> choices ps (trans Functor.<$> bs) (trans d)
240 Subroutine n sub k -> subroutine n (trans sub) (trans k)
242 Call n k -> call n (trans k)
244 Read es p k -> read es p (trans k)
245 DefJoin n sub k -> defJoin n (trans sub) (trans k)
246 RefJoin n -> refJoin n
249 -- | Type-level natural numbers, using the Peano recursive encoding.
250 data Peano = Zero | Succ Peano
252 -- | @('instrFmap' f k)@.
254 TermInstr (x -> y) ->
255 Instr inp (y ': xs) es ret ->
256 Instr inp (x ': xs) es ret
257 instrFmap f k = Push f (LiftI2 (H.flip H..@ (H.$)) k)
259 -- | @('instrApp' k)@ pops @(x)@ and @(x2y)@ from the 'valueStack',
260 -- pushes @(x2y x)@ and continues with the next 'Instr'uction @(k)@.
262 Instr inp (y : vs) es ret ->
263 Instr inp (x : (x -> y) : vs) es ret
264 instrApp k = LiftI2 (H.$) k
266 -- | @('If' ok ko)@ pops a 'Bool' from the 'valueStack'
267 -- and continues either with the 'Instr'uction @(ok)@ if it is 'True'
268 -- or @(ko)@ otherwise.
270 Instr inp vs es ret ->
271 Instr inp vs es ret ->
272 Instr inp (Bool ': vs) es ret
273 instrIf ok ko = Choices [H.id] [ok] ko
276 -- | Making the control-flow explicit.
277 data Machine inp v = Machine { unMachine ::
279 {-k-}Instr inp (v ': vs) ('Succ es) ret ->
280 Instr inp vs ('Succ es) ret
284 forall inp v es repr.
286 Readable repr (InputToken inp) =>
287 Machine inp v -> repr inp '[] ('Succ es) v
288 runMachine (Machine auto) =
289 trans @(Instr inp '[] ('Succ es)) $
292 instance Applicable (Machine inp) where
293 pure x = Machine $ Push (trans x)
294 Machine f <*> Machine x = Machine $ f . x . instrApp
295 liftA2 f (Machine x) (Machine y) = Machine $
296 x . y . LiftI2 (trans f)
297 Machine x *> Machine y = Machine $ x . Pop . y
298 Machine x <* Machine y = Machine $ x . y . Pop
300 Cursorable (Cursor inp) =>
301 Alternable (Machine inp) where
302 empty = Machine $ \_k -> Fail []
303 Machine l <|> Machine r = Machine $ \k ->
307 (instrFailIfConsumed (r j))
308 try (Machine x) = Machine $ \k ->
309 CatchFail (x (PopFail k))
310 -- On exception, reset the input,
311 -- and propagate the failure.
312 (LoadInput (Fail []))
314 -- | If no input has been consumed by the failing alternative
315 -- then continue with the given continuation.
316 -- Otherwise, propagate the 'Fail'ure.
317 instrFailIfConsumed ::
318 Cursorable (Cursor inp) =>
319 Instr inp vs ('Succ es) ret ->
320 Instr inp (Cursor inp : vs) ('Succ es) ret
321 instrFailIfConsumed k = PushInput (LiftI2 (H.Term sameOffset) (instrIf k (Fail [])))
323 -- | @('makeJoin' k f)@ factorizes @(k)@ in @(f)@,
324 -- by introducing a 'DefJoin' if necessary,
325 -- and passing the corresponding 'RefJoin' to @(f)@,
326 -- or @(k)@ as is when factorizing is useless.
328 Instr inp (v : vs) es ret ->
329 (Instr inp (v : vs) es ret -> Instr inp vs es ret) ->
331 -- Double RefJoin Optimization:
332 -- If a join-node points directly to another join-node,
334 makeJoin k@RefJoin{} = ($ k)
335 -- Terminal RefJoin Optimization:
336 -- If a join-node points directly to a terminal operation,
337 -- then it's useless to introduce a join-point.
338 makeJoin k@Ret = ($ k)
340 let joinName = LetName $ unsafePerformIO $ TH.qNewName "join" in
341 \f -> DefJoin joinName k (f (RefJoin joinName))
343 instance tok ~ InputToken inp => Satisfiable (Machine inp) tok where
344 satisfy es p = Machine $ Read es (trans p)
345 instance Selectable (Machine inp) where
346 branch (Machine lr) (Machine l) (Machine r) = Machine $ \k ->
348 lr (Case (l (Swap (instrApp j)))
349 (r (Swap (instrApp j))))
350 instance Matchable (Machine inp) where
351 conditional(Machine a) ps bs (Machine default_) = Machine $ \k ->
353 a (Choices (trans Functor.<$> ps)
354 ((\b -> unMachine b j) Functor.<$> bs)
357 ( Ord (InputToken inp)
358 , Cursorable (Cursor inp)
359 ) => Lookable (Machine inp) where
360 look (Machine x) = Machine $ \k ->
361 PushInput (x (Swap (LoadInput k)))
362 eof = negLook (satisfy [{-discarded by negLook-}] (H.lam1 (\_x -> H.bool True)))
363 -- This sets a better failure message
364 <|> (Machine $ \_k -> Fail [ErrorItemEnd])
365 negLook (Machine x) = Machine $ \k ->
367 -- On x success, discard the result,
368 -- and replace this 'CatchFail''s failure handler
369 -- by a 'Fail'ure whose 'farthestExpecting' is negated,
370 -- then a failure is raised from the input
371 -- when entering 'negLook', to avoid odd cases:
372 -- - where the failure that made (negLook x)
373 -- succeed can get the blame for the overall
374 -- failure of the grammar.
375 -- - where the overall failure of
376 -- the grammar might be blamed on something in x
377 -- that, if corrected, still makes x succeed and
379 (PushInput (x (Pop (PopFail (LoadInput (Fail []))))))
380 -- On x failure, reset the input,
381 -- and go on with the next 'Instr'uctions.
382 (LoadInput (Push H.unit k))
383 instance Letable TH.Name (Machine inp) where
384 def n v = Machine $ \k ->
385 Subroutine (LetName n) (unMachine v Ret) (Call (LetName n) k)
386 ref _isRec n = Machine $ \case
387 Ret -> Jump (LetName n)
388 k -> Call (LetName n) k
389 instance Cursorable (Cursor inp) => Foldable (Machine inp) where
391 chainPre op p = go <*> p
392 where go = (H..) <$> op <*> go <|> pure H.id
393 chainPost p op = p <**> go
394 where go = (H..) <$> op <*> go <|> pure H.id