1 {-# LANGUAGE ConstraintKinds #-} -- For Executable
2 {-# LANGUAGE DerivingStrategies #-} -- For Show (LetName a)
3 {-# LANGUAGE PatternSynonyms #-} -- For Fmap, App, …
4 {-# LANGUAGE UndecidableInstances #-} -- For Cursorable (Cursor inp)
5 module Symantic.Parser.Machine.Instructions where
7 import Data.Bool (Bool(..))
8 import Data.Either (Either)
11 import Data.Function (($), (.))
12 import Data.Kind (Type)
13 import System.IO.Unsafe (unsafePerformIO)
14 import Text.Show (Show(..), showString)
15 import qualified Data.Functor as Functor
16 import qualified Language.Haskell.TH as TH
17 import qualified Language.Haskell.TH.Syntax as TH
18 import qualified Symantic.Parser.Grammar.Pure as H
20 import Symantic.Parser.Grammar
21 import Symantic.Parser.Machine.Input
22 import Symantic.Univariant.Trans
25 -- | 'Instr'uctions for the 'Machine'.
26 data Instr input valueStack (failStack::Peano) returnValue where
27 -- | @('Push' x k)@ pushes @(x)@ on the 'valueStack'
28 -- and continues with the next 'Instr'uction @(k)@.
31 Instr inp (v ': vs) es ret ->
33 -- | @('Pop' k)@ pushes @(x)@ on the 'valueStack'.
35 Instr inp vs es ret ->
36 Instr inp (v ': vs) es ret
37 -- | @('LiftI2' f k)@ pops two values from the 'valueStack',
38 -- and pushes the result of @(f)@ applied to them.
40 InstrPure (x -> y -> z) ->
41 Instr inp (z : vs) es ret ->
42 Instr inp (y : x : vs) es ret
43 -- | @('Fail')@ raises an error from the 'failStack'.
45 [ErrorItem (InputToken inp)] ->
46 Instr inp vs ('Succ es) ret
47 -- | @('PopFail' k)@ removes a 'FailHandler' from the 'failStack'
48 -- and continues with the next 'Instr'uction @(k)@.
50 Instr inp vs es ret ->
51 Instr inp vs ('Succ es) ret
52 -- | @('CatchFail' l r)@ tries the @(l)@ 'Instr'uction
53 -- in a new failure scope such that if @(l)@ raises a failure, it is caught,
54 -- then the input is pushed as it was before trying @(l)@ on the 'valueStack',
55 -- and the control flow goes on with the @(r)@ 'Instr'uction.
57 Instr inp vs ('Succ es) ret ->
58 Instr inp (Cursor inp ': vs) es ret ->
60 -- | @('LoadInput' k)@ removes the input from the 'valueStack'
61 -- and continues with the next 'Instr'uction @(k)@ using that input.
64 Instr inp (Cursor inp : vs) es r
65 -- | @('PushInput' k)@ pushes the input @(inp)@ on the 'valueStack'
66 -- and continues with the next 'Instr'uction @(k)@.
68 Instr inp (Cursor inp ': vs) es ret ->
72 Instr inp (x ': vs) es r ->
73 Instr inp (y ': vs) es r ->
74 Instr inp (Either x y ': vs) es r
75 -- | @('Swap' k)@ pops two values on the 'valueStack',
76 -- pushes the first popped-out, then the second,
77 -- and continues with the next 'Instr'uction @(k)@.
79 Instr inp (x ': y ': vs) es r ->
80 Instr inp (y ': x ': vs) es r
81 -- | @('Choices' ps bs d)@.
83 [InstrPure (v -> Bool)] ->
84 [Instr inp vs es ret] ->
85 Instr inp vs es ret ->
86 Instr inp (v ': vs) es ret
87 -- | @('Subroutine' n v k)@ binds the 'LetName' @(n)@ to the 'Instr'uction's @(v)@,
89 -- continues with the next 'Instr'uction @(k)@.
91 LetName v -> Instr inp '[] ('Succ 'Zero) v ->
92 Instr inp vs ('Succ es) ret ->
93 Instr inp vs ('Succ es) ret
94 -- | @('Jump' n k)@ pass the control-flow to the 'Subroutine' named @(n)@.
97 Instr inp '[] ('Succ es) ret
98 -- | @('Call' n k)@ pass the control-flow to the 'Subroutine' named @(n)@,
99 -- and when it 'Ret'urns, continues with the next 'Instr'uction @(k)@.
102 Instr inp (v ': vs) ('Succ es) ret ->
103 Instr inp vs ('Succ es) ret
104 -- | @('Ret')@ returns the value stored in a singleton 'valueStack'.
106 Instr inp '[ret] es ret
107 -- | @('Read' expected p k)@ reads a 'Char' @(c)@ from the 'inp'ut,
108 -- if @(p c)@ is 'True' then continues with the next 'Instr'uction @(k)@ on,
111 [ErrorItem (InputToken inp)] ->
112 InstrPure (InputToken inp -> Bool) ->
113 Instr inp (InputToken inp ': vs) ('Succ es) ret ->
114 Instr inp vs ('Succ es) ret
116 LetName v -> Instr inp (v ': vs) es ret ->
117 Instr inp vs es ret ->
121 Instr inp (v ': vs) es ret
123 -- ** Type 'InstrPure'
124 data InstrPure a where
125 InstrPure :: H.CombPure a -> InstrPure a
126 InstrPureSameOffset :: CodeQ (cur -> cur -> Bool) -> InstrPure (cur -> cur -> Bool)
129 instance Show (InstrPure a) where
131 InstrPure x -> showsPrec p x
132 InstrPureSameOffset{} -> showString "InstrPureSameOffset"
133 instance Trans InstrPure TH.CodeQ where
135 InstrPure x -> trans x
136 InstrPureSameOffset x -> x
139 newtype LetName a = LetName { unLetName :: TH.Name }
141 deriving newtype Show
143 -- * Class 'Executable'
144 type Executable repr =
153 -- ** Class 'Stackable'
154 class Stackable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
157 repr inp (v ': vs) n ret ->
161 repr inp (v ': vs) n ret
163 InstrPure (x -> y -> z) ->
164 repr inp (z ': vs) es ret ->
165 repr inp (y ': x ': vs) es ret
167 repr inp (x ': y ': vs) n r ->
168 repr inp (y ': x ': vs) n r
170 -- ** Class 'Branchable'
171 class Branchable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
173 repr inp (x ': vs) n r ->
174 repr inp (y ': vs) n r ->
175 repr inp (Either x y ': vs) n r
177 [InstrPure (v -> Bool)] ->
178 [repr inp vs es ret] ->
179 repr inp vs es ret ->
180 repr inp (v ': vs) es ret
182 -- ** Class 'Failable'
183 class Failable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
184 fail :: [ErrorItem (InputToken inp)] -> repr inp vs ('Succ es) ret
186 repr inp vs es ret ->
187 repr inp vs ('Succ es) ret
189 repr inp vs ('Succ es) ret ->
190 repr inp (Cursor inp ': vs) es ret ->
193 -- ** Class 'Inputable'
194 class Inputable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
197 repr inp (Cursor inp ': vs) es r
199 repr inp (Cursor inp ': vs) es ret ->
202 -- ** Class 'Routinable'
203 class Routinable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
205 LetName v -> repr inp '[] ('Succ 'Zero) v ->
206 repr inp vs ('Succ es) ret ->
207 repr inp vs ('Succ es) ret
209 LetName v -> repr inp (v ': vs) ('Succ es) ret ->
210 repr inp vs ('Succ es) ret
212 repr inp '[ret] es ret
215 repr inp '[] ('Succ es) ret
217 -- ** Class 'Joinable'
218 class Joinable (repr :: Type -> [Type] -> Peano -> Type -> Type) where
221 repr inp (v ': vs) es ret ->
222 repr inp vs es ret ->
226 repr inp (v ': vs) es ret
228 -- ** Class 'Readable'
229 class Readable (repr :: Type -> [Type] -> Peano -> Type -> Type) (tok::Type) where
231 tok ~ InputToken inp =>
233 InstrPure (tok -> Bool) ->
234 repr inp (tok ': vs) ('Succ es) ret ->
235 repr inp vs ('Succ es) ret
239 , Readable repr (InputToken inp)
240 ) => Trans (Instr inp vs es) (repr inp vs es) where
242 Push x k -> push x (trans k)
243 Pop k -> pop (trans k)
244 LiftI2 f k -> liftI2 f (trans k)
246 PopFail k -> popFail (trans k)
247 CatchFail l r -> catchFail (trans l) (trans r)
248 LoadInput k -> loadInput (trans k)
249 PushInput k -> pushInput (trans k)
250 Case l r -> case_ (trans l) (trans r)
251 Swap k -> swap (trans k)
252 Choices ps bs d -> choices ps (trans Functor.<$> bs) (trans d)
253 Subroutine n sub k -> subroutine n (trans sub) (trans k)
255 Call n k -> call n (trans k)
257 Read es p k -> read es p (trans k)
258 DefJoin n sub k -> defJoin n (trans sub) (trans k)
259 RefJoin n -> refJoin n
262 -- | Type-level natural numbers, using the Peano recursive encoding.
263 data Peano = Zero | Succ Peano
267 InstrPure (x -> y) ->
268 Instr inp (y ': xs) es ret ->
269 Instr inp (x ': xs) es ret
270 pattern Fmap f k = Push f (LiftI2 (InstrPure (H.Flip H.:@ (H.:$))) k)
272 -- | @('App' k)@ pops @(x)@ and @(x2y)@ from the 'valueStack',
273 -- pushes @(x2y x)@ and continues with the next 'Instr'uction @(k)@.
275 Instr inp (y : vs) es ret ->
276 Instr inp (x : (x -> y) : vs) es ret
277 pattern App k = LiftI2 (InstrPure (H.:$)) k
279 -- | @('If' ok ko)@ pops a 'Bool' from the 'valueStack'
280 -- and continues either with the 'Instr'uction @(ok)@ if it is 'True'
281 -- or @(ko)@ otherwise.
283 Instr inp vs es ret ->
284 Instr inp vs es ret ->
285 Instr inp (Bool ': vs) es ret
286 pattern If ok ko = Choices [InstrPure H.Id] [ok] ko
289 -- | Making the control-flow explicit.
290 data Machine inp v = Machine { unMachine ::
292 {-k-}Instr inp (v ': vs) ('Succ es) ret ->
293 Instr inp vs ('Succ es) ret
297 forall inp v es repr.
299 Readable repr (InputToken inp) =>
300 Machine inp v -> repr inp '[] ('Succ es) v
301 runMachine (Machine auto) =
302 trans @(Instr inp '[] ('Succ es)) $
305 instance Applicable (Machine inp) where
306 pure x = Machine $ Push (InstrPure x)
307 Machine f <*> Machine x = Machine $ f . x . App
308 liftA2 f (Machine x) (Machine y) = Machine $
309 x . y . LiftI2 (InstrPure f)
310 Machine x *> Machine y = Machine $ x . Pop . y
311 Machine x <* Machine y = Machine $ x . y . Pop
313 Cursorable (Cursor inp) =>
314 Alternable (Machine inp) where
315 empty = Machine $ \_k -> Fail []
316 Machine l <|> Machine r = Machine $ \k ->
320 (failIfConsumed (r j))
321 try (Machine x) = Machine $ \k ->
322 CatchFail (x (PopFail k))
323 -- On exception, reset the input,
324 -- and propagate the failure.
325 (LoadInput (Fail []))
327 -- | If no input has been consumed by the failing alternative
328 -- then continue with the given continuation.
329 -- Otherwise, propagate the 'Fail'ure.
331 Cursorable (Cursor inp) =>
332 Instr inp vs ('Succ es) ret ->
333 Instr inp (Cursor inp : vs) ('Succ es) ret
334 failIfConsumed k = PushInput (LiftI2 (InstrPureSameOffset sameOffset) (If k (Fail [])))
336 -- | @('makeJoin' k f)@ factorizes @(k)@ in @(f)@,
337 -- by introducing a 'DefJoin' if necessary,
338 -- and passing the corresponding 'RefJoin' to @(f)@,
339 -- or @(k)@ as is when factorizing is useless.
341 Instr inp (v : vs) es ret ->
342 (Instr inp (v : vs) es ret -> Instr inp vs es ret) ->
344 -- Double RefJoin Optimization:
345 -- If a join-node points directly to another join-node,
347 makeJoin k@RefJoin{} = ($ k)
348 -- Terminal RefJoin Optimization:
349 -- If a join-node points directly to a terminal operation,
350 -- then it's useless to introduce a join-point.
351 makeJoin k@Ret = ($ k)
353 let joinName = LetName $ unsafePerformIO $ TH.qNewName "join" in
354 \f -> DefJoin joinName k (f (RefJoin joinName))
356 instance tok ~ InputToken inp => Satisfiable (Machine inp) tok where
357 satisfy es p = Machine $ Read es (InstrPure p)
358 instance Selectable (Machine inp) where
359 branch (Machine lr) (Machine l) (Machine r) = Machine $ \k ->
361 lr (Case (l (Swap (App j)))
363 instance Matchable (Machine inp) where
364 conditional ps bs (Machine m) (Machine default_) = Machine $ \k ->
366 m (Choices (InstrPure Functor.<$> ps)
367 ((\b -> unMachine b j) Functor.<$> bs)
370 ( Ord (InputToken inp)
371 , Cursorable (Cursor inp)
372 ) => Lookable (Machine inp) where
373 look (Machine x) = Machine $ \k ->
374 PushInput (x (Swap (LoadInput k)))
375 eof = negLook (satisfy [{-discarded by negLook-}] (H.const H..@ H.bool True))
376 -- Set a better failure message
377 <|> (Machine $ \_k -> Fail [ErrorItemEnd])
378 negLook (Machine x) = Machine $ \k ->
380 -- On x success, discard the result,
381 -- and replace this 'CatchFail''s failure handler
382 -- by a 'Fail'ure whose 'farthestExpecting' is negated,
383 -- then a failure is raised from the input
384 -- when entering 'negLook', to avoid odd cases:
385 -- - where the failure that made (negLook x)
386 -- succeed can get the blame for the overall
387 -- failure of the grammar.
388 -- - where the overall failure of
389 -- the grammar might be blamed on something in x
390 -- that, if corrected, still makes x succeed and
392 (PushInput (x (Pop (PopFail (LoadInput (Fail []))))))
393 -- On x failure, reset the input,
394 -- and go on with the next 'Instr'uctions.
395 (LoadInput (Push (InstrPure H.unit) k))
396 instance Letable TH.Name (Machine inp) where
397 def n v = Machine $ \k ->
398 Subroutine (LetName n) (unMachine v Ret) (Call (LetName n) k)
399 ref _isRec n = Machine $ \case
400 Ret -> Jump (LetName n)
401 k -> Call (LetName n) k
402 instance Cursorable (Cursor inp) => Foldable (Machine inp) where
404 chainPre op p = go <*> p
405 where go = (H..) <$> op <*> go <|> pure H.id
406 chainPost p op = p <**> go
407 where go = (H..) <$> op <*> go <|> pure H.id