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
52 repr inp (v ': vs) a ->
58 TermInstr (x -> y -> z) ->
59 repr inp (z ': vs) a ->
60 repr inp (y ': x ': vs) a
62 repr inp (x ': y ': vs) a ->
63 repr inp (y ': x ': vs) a
64 -- | @('mapValue' f k)@.
67 repr inp (y ': vs) a ->
69 mapValue f = pushValue f . lift2Value (H.flip H..@ (H.$))
70 -- | @('applyValue' k)@ pops @(x)@ and @(x2y)@ from the 'valueStack',
71 -- pushes @(x2y x)@ and continues with the next 'Instr'uction @(k)@.
73 repr inp (y ': vs) a ->
74 repr inp (x ': (x -> y) ': vs) a
75 applyValue = lift2Value (H.$)
77 -- ** Class 'InstrExceptionable'
78 class InstrExceptionable (repr::ReprInstr) where
79 -- | @('raiseException' lbl es)@ raises labeled error from the 'failStack'.
83 [ErrorItem (InputToken inp)] ->
85 -- | Like using 'raiseException' with the @"fail"@ label.
87 [ErrorItem (InputToken inp)] ->
89 fail = raiseException (Proxy @"fail")
90 -- | @('popException' lbl k)@ removes a 'Catcher'
91 -- from the @catchStackByLabel@ at given label,
92 -- and continues with the next 'Instr'uction @(k)@.
98 -- | @('catchException' lbl l r)@ tries the @(l)@ 'Instr'uction
99 -- in a new failure scope such that if @(l)@ raises a failure, it is caught,
100 -- then the input is pushed as it was before trying @(l)@ on the 'valueStack',
101 -- and the control flow goes on with the @(r)@ 'Instr'uction.
106 repr inp (Cursor inp ': vs) ret ->
109 -- ** Class 'InstrBranchable'
110 class InstrBranchable (repr::ReprInstr) where
111 -- | @('caseBranch' l r)@.
113 repr inp (x ': vs) r ->
114 repr inp (y ': vs) r ->
115 repr inp (Either x y ': vs) r
116 -- | @('choicesBranch' ps bs d)@.
118 [TermInstr (v -> Bool)] ->
122 -- | @('ifBranch' ok ko)@ pops a 'Bool' from the 'valueStack'
123 -- and continues either with the 'Instr'uction @(ok)@ if it is 'True'
124 -- or @(ko)@ otherwise.
128 repr inp (Bool ': vs) a
129 ifBranch ok ko = choicesBranch [H.id] [ok] ko
131 -- ** Class 'InstrLetable'
132 class InstrLetable (repr::ReprInstr) where
133 -- | @('defLet' n v k)@ binds the 'LetName' @(n)@ to the 'Instr'uction's @(v)@,
135 -- continues with the next 'Instr'uction @(k)@.
137 LetName v -> repr inp '[] v ->
140 -- | @('call' n k)@ pass the control-flow to the 'DefLet' named @(n)@,
141 -- and when it 'Ret'urns, continues with the next 'Instr'uction @(k)@.
143 LetName v -> repr inp (v ': vs) a ->
145 -- | @('ret')@ returns the value stored in a singleton 'valueStack'.
148 -- | @('jump' n k)@ pass the control-flow to the 'DefLet' named @(n)@.
153 -- ** Class 'InstrJoinable'
154 class InstrJoinable (repr::ReprInstr) where
156 LetName v -> repr inp (v ': vs) a ->
163 -- ** Class 'InstrInputable'
164 class InstrInputable (repr::ReprInstr) where
165 -- | @('loadInput' k)@ removes the input from the 'valueStack'
166 -- and continues with the next 'Instr'uction @(k)@ using that input.
169 repr inp (Cursor inp ': vs) a
170 -- | @('pushInput' k)@ pushes the input @(inp)@ on the 'valueStack'
171 -- and continues with the next 'Instr'uction @(k)@.
173 repr inp (Cursor inp ': vs) a ->
176 -- ** Class 'InstrReadable'
177 class InstrReadable (tok::Type) (repr::ReprInstr) where
178 -- | @('read' expected p k)@ reads a 'Char' @(c)@ from the 'inp'ut,
179 -- if @(p c)@ is 'True' then continues with the next 'Instr'uction @(k)@ on,
180 -- otherwise 'RaiseException'.
182 tok ~ InputToken inp =>
184 TermInstr (tok -> Bool) ->
185 repr inp (tok ': vs) a ->