1 {-# LANGUAGE ConstraintKinds #-} -- For Machine
2 {-# LANGUAGE DerivingStrategies #-} -- For Show (LetName a)
3 -- | Semantic of the parsing instructions used
4 -- to make the parsing control-flow explicit,
5 -- in the convenient tagless-final encoding.
6 module Symantic.Parser.Machine.Instructions where
8 import Data.Bool (Bool(..))
9 import Data.Either (Either)
10 import Data.Eq (Eq(..))
11 import Data.Function ((.))
12 import Data.Kind (Type)
13 import GHC.TypeLits (KnownSymbol)
14 import Text.Show (Show(..))
15 import Data.Proxy (Proxy(..))
16 import qualified Language.Haskell.TH as TH
17 import qualified Symantic.Parser.Haskell as H
19 import Symantic.Parser.Grammar
20 import Symantic.Parser.Machine.Input
23 type TermInstr = H.Term TH.CodeQ
26 -- | All the 'Instr'uctions.
27 type Machine tok repr =
28 ( InstrBranchable repr
29 , InstrExceptionable repr
34 , InstrReadable tok repr
37 -- ** Type 'ReprInstr'
38 type ReprInstr = {-input-}Type -> {-valueStack-}[Type] -> {-a-}Type -> Type
41 -- | 'TH.Name' of a 'defLet' or 'defJoin'
42 -- indexed by the return type of the factorized 'Instr'uctions.
43 -- This helps type-inferencing.
44 newtype LetName a = LetName { unLetName :: TH.Name }
48 -- ** Class 'InstrValuable'
49 class InstrValuable (repr::ReprInstr) where
50 -- | @('pushValue' x k)@ pushes @(x)@ on the 'valueStack'
51 -- and continues with the next 'Instr'uction @(k)@.
54 repr inp (v ': vs) a ->
56 -- | @('popValue' k)@ pushes @(x)@ on the 'valueStack'.
60 -- | @('lift2Value' f k)@ pops two values from the 'valueStack',
61 -- and pushes the result of @(f)@ applied to them.
63 TermInstr (x -> y -> z) ->
64 repr inp (z ': vs) a ->
65 repr inp (y ': x ': vs) a
66 -- | @('swapValue' k)@ pops two values on the 'valueStack',
67 -- pushes the first popped-out, then the second,
68 -- and continues with the next 'Instr'uction @(k)@.
70 repr inp (x ': y ': vs) a ->
71 repr inp (y ': x ': vs) a
72 -- | @('mapValue' f k)@.
75 repr inp (y ': vs) a ->
77 mapValue f = pushValue f . lift2Value (H.flip H..@ (H.$))
78 -- | @('applyValue' k)@ pops @(x)@ and @(x2y)@ from the 'valueStack',
79 -- pushes @(x2y x)@ and continues with the next 'Instr'uction @(k)@.
81 repr inp (y ': vs) a ->
82 repr inp (x ': (x -> y) ': vs) a
83 applyValue = lift2Value (H.$)
85 -- ** Class 'InstrExceptionable'
86 class InstrExceptionable (repr::ReprInstr) where
87 -- | @('raiseException' lbl es)@ raises labeled error from the 'failStack'.
91 [ErrorItem (InputToken inp)] ->
93 -- | Like using 'raiseException' with the @"fail"@ label.
95 [ErrorItem (InputToken inp)] ->
97 fail = raiseException (Proxy @"fail")
98 -- | @('popException' lbl k)@ removes a 'Catcher'
99 -- from the @catchStackByLabel@ at given label,
100 -- and continues with the next 'Instr'uction @(k)@.
106 -- | @('catchException' lbl l r)@ tries the @(l)@ 'Instr'uction
107 -- in a new failure scope such that if @(l)@ raises a failure, it is caught,
108 -- then the input (and its 'Horizon') is pushed as it was before trying @(l)@ on the 'valueStack',
109 -- and the control flow goes on with the @(r)@ 'Instr'uction.
114 repr inp (Cursor inp ': vs) ret ->
117 -- ** Class 'InstrBranchable'
118 class InstrBranchable (repr::ReprInstr) where
119 -- | @('caseBranch' l r)@.
121 repr inp (x ': vs) r ->
122 repr inp (y ': vs) r ->
123 repr inp (Either x y ': vs) r
124 -- | @('choicesBranch' ps bs d)@.
126 [TermInstr (v -> Bool)] ->
130 -- | @('ifBranch' ok ko)@ pops a 'Bool' from the 'valueStack'
131 -- and continues either with the 'Instr'uction @(ok)@ if it is 'True'
132 -- or @(ko)@ otherwise.
136 repr inp (Bool ': vs) a
137 ifBranch ok ko = choicesBranch [H.id] [ok] ko
139 -- ** Class 'InstrCallable'
140 class InstrCallable (repr::ReprInstr) where
141 -- | @('defLet' n v k)@ binds the 'LetName' @(n)@ to the 'Instr'uction's @(v)@,
143 -- continues with the next 'Instr'uction @(k)@.
145 LetBindings TH.Name (repr inp '[]) ->
148 -- | @('call' n k)@ pass the control-flow to the 'DefLet' named @(n)@,
149 -- and when it 'Ret'urns, continues with the next 'Instr'uction @(k)@.
151 LetName v -> repr inp (v ': vs) a ->
153 -- | @('ret')@ returns the value stored in a singleton 'valueStack'.
156 -- | @('jump' n k)@ pass the control-flow to the 'DefLet' named @(n)@.
161 -- ** Class 'InstrJoinable'
162 class InstrJoinable (repr::ReprInstr) where
164 LetName v -> repr inp (v ': vs) a ->
171 -- ** Class 'InstrInputable'
172 class InstrInputable (repr::ReprInstr) where
173 -- | @('pushInput' k)@ pushes the input @(inp)@ on the 'valueStack'
174 -- and continues with the next 'Instr'uction @(k)@.
176 repr inp (Cursor inp ': vs) a ->
178 -- | @('loadInput' k)@ removes the input from the 'valueStack'
179 -- and continues with the next 'Instr'uction @(k)@ using that input.
182 repr inp (Cursor inp ': vs) a
184 -- ** Class 'InstrReadable'
185 class InstrReadable (tok::Type) (repr::ReprInstr) where
186 -- | @('read' expected p k)@ reads a 'Char' @(c)@ from the 'inp'ut,
187 -- if @(p c)@ is 'True' then continues with the next 'Instr'uction @(k)@ on,
188 -- otherwise 'RaiseException'.
190 tok ~ InputToken inp =>
192 TermInstr (tok -> Bool) ->
193 repr inp (tok ': vs) a ->