impl: make `HideName` support newer constructors
[haskell/symantic-parser.git] / src / Symantic / Parser / Machine / Optimize.hs
index 4fb9220d6302d15de625c994ecc6f25593608985..9f71142b3bbaba8751e33cfe5c0f0f158100fb57 100644 (file)
@@ -1,43 +1,45 @@
 {-# LANGUAGE PatternSynonyms #-} -- For Instr
 {-# LANGUAGE ViewPatterns #-} -- For unSomeInstr
-{-# LANGUAGE UndecidableInstances #-}
 -- | Initial encoding with bottom-up optimizations of 'Instr'uctions,
 -- re-optimizing downward as needed after each optimization.
--- There is only one optimization (for 'push') so far,
+-- There is only one optimization (for 'pushValue') so far,
 -- but the introspection enabled by the 'Instr' data-type
 -- is also useful to optimize with more context in the 'Machine'.
 module Symantic.Parser.Machine.Optimize where
 
+import Data.Bifunctor (second)
 import Data.Bool (Bool(..))
 import Data.Either (Either)
-import Data.Maybe (Maybe(..))
 import Data.Function ((.))
 import Data.Kind (Constraint)
+import Data.Maybe (Maybe(..))
+import Data.Set (Set)
+import Data.String (String)
 import Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..))
--- import GHC.TypeLits (Symbol)
 import qualified Data.Functor as Functor
+import qualified Language.Haskell.TH as TH
 
+import Symantic.Syntaxes.Derive
 import Symantic.Parser.Grammar
 import Symantic.Parser.Machine.Input
 import Symantic.Parser.Machine.Instructions
-import Symantic.Univariant.Trans
 
 -- * Data family 'Instr'
 -- | 'Instr'uctions of the 'Machine'.
 -- This is an extensible data-type.
 data family Instr
   (instr :: ReprInstr -> Constraint)
-  (repr :: ReprInstr)
-  :: ReprInstr
+  :: ReprInstr -> ReprInstr
+type instance Derived (Instr instr repr inp vs) = repr inp vs
 
 -- | Convenient utility to pattern-match a 'SomeInstr'.
-pattern Instr :: Typeable comb =>
-  Instr comb repr inp vs es a ->
-  SomeInstr repr inp vs es a
+pattern Instr :: Typeable instr =>
+  Instr instr repr inp vs a ->
+  SomeInstr repr inp vs a
 pattern Instr x <- (unSomeInstr -> Just x)
 
 -- ** Type 'SomeInstr'
--- | Some 'Instr'uction existantialized over the actual instruction symantic class.
+-- | Some 'Instr'uction existentialized over the actual instruction symantic class.
 -- Useful to handle a list of 'Instr'uctions
 -- without requiring impredicative quantification.
 -- Must be used by pattern-matching
@@ -47,204 +49,243 @@ pattern Instr x <- (unSomeInstr -> Just x)
 -- As in 'SomeComb', a first pass of optimizations
 -- is directly applied in it
 -- to avoid introducing an extra newtype,
--- this also give a more comprehensible code.
-data SomeInstr repr inp vs es a =
+-- this also gives a more understandable code.
+data SomeInstr repr inp vs a =
   forall instr.
-  (Trans (Instr instr repr inp vs es) (repr inp vs es), Typeable instr) =>
-  SomeInstr (Instr instr repr inp vs es a)
+  ( Derivable (Instr instr repr inp vs)
+  , Typeable instr
+  ) => SomeInstr (Instr instr repr inp vs a)
 
-instance Trans (SomeInstr repr inp vs es) (repr inp vs es) where
-  trans (SomeInstr x) = trans x
+type instance Derived (SomeInstr repr inp vs) = repr inp vs
+instance Derivable (SomeInstr repr inp vs) where
+  derive (SomeInstr x) = derive x
 
--- | @(unSomeInstr i :: 'Maybe' ('Instr' comb repr inp vs es a))@
+-- | @(unSomeInstr i :: 'Maybe' ('Instr' instr repr inp vs a))@
 -- extract the data-constructor from the given 'SomeInstr'
--- iif. it belongs to the @('Instr' comb repr a)@ data-instance.
+-- iif. it belongs to the @('Instr' instr repr a)@ data-instance.
 unSomeInstr ::
-  forall instr repr inp vs es a.
+  forall instr repr inp vs a.
   Typeable instr =>
-  SomeInstr repr inp vs es a ->
-  Maybe (Instr instr repr inp vs es a)
-unSomeInstr (SomeInstr (i::Instr i repr inp vs es a)) =
+  SomeInstr repr inp vs a ->
+  Maybe (Instr instr repr inp vs a)
+unSomeInstr (SomeInstr (i::Instr i repr inp vs a)) =
   case typeRep @instr `eqTypeRep` typeRep @i of
     Just HRefl -> Just i
-    Nothing -> Nothing
-
--- Stackable
-data instance Instr Stackable repr inp vs fs a where
-  -- | @('Push' x k)@ pushes @(x)@ on the 'valueStack'
-  -- and continues with the next 'Instr'uction @(k)@.
-  Push ::
-    TermInstr v ->
-    SomeInstr repr inp (v ': vs) es a ->
-    Instr Stackable repr inp vs es a
-  -- | @('Pop' k)@ pushes @(x)@ on the 'valueStack'.
-  Pop ::
-    SomeInstr repr inp vs es a ->
-    Instr Stackable repr inp (v ': vs) es a
-  -- | @('LiftI2' f k)@ pops two values from the 'valueStack',
-  -- and pushes the result of @(f)@ applied to them.
-  LiftI2 ::
-    TermInstr (x -> y -> z) ->
-    SomeInstr repr inp (z : vs) es a ->
-    Instr Stackable repr inp (y : x : vs) es a
-  -- | @('Swap' k)@ pops two values on the 'valueStack',
-  -- pushes the first popped-out, then the second,
-  -- and continues with the next 'Instr'uction @(k)@.
-  Swap ::
-    SomeInstr repr inp (x ': y ': vs) es a ->
-    Instr Stackable repr inp (y ': x ': vs) es a
-instance Stackable repr => Trans (Instr Stackable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    Push x k -> push x (trans k)
-    Pop k -> pop (trans k)
-    LiftI2 f k -> liftI2 f (trans k)
-    Swap k -> swap (trans k)
-instance Stackable repr => Stackable (SomeInstr repr) where
-  push _v (Instr (Pop i)) = i
-  push v i = SomeInstr (Push v i)
-  pop = SomeInstr . Pop
-  liftI2 f = SomeInstr . LiftI2 f
-  swap = SomeInstr . Swap
-
--- Routinable
-data instance Instr Routinable repr inp vs fs a where
-  -- | @('Subroutine' n v k)@ binds the 'LetName' @(n)@ to the 'Instr'uction's @(v)@,
-  -- 'Call's @(n)@ and
-  -- continues with the next 'Instr'uction @(k)@.
-  Subroutine ::
-    LetName v ->
-    SomeInstr repr inp '[] ('Succ 'Zero) v ->
-    SomeInstr repr inp vs ('Succ es) a ->
-    Instr Routinable repr inp vs ('Succ es) a
-  -- | @('Jump' n k)@ pass the control-flow to the 'Subroutine' named @(n)@.
-  Jump ::
-    LetName a ->
-    Instr Routinable repr inp '[] ('Succ es) a
-  -- | @('Call' n k)@ pass the control-flow to the 'Subroutine' named @(n)@,
-  -- and when it 'Ret'urns, continues with the next 'Instr'uction @(k)@.
+    Nothing ->
+      case typeRep @InstrComment `eqTypeRep` typeRep @i of
+        Just HRefl | Comment _msg x <- i -> unSomeInstr x
+        Nothing -> Nothing
+
+-- InstrComment
+data instance Instr InstrComment repr inp vs a where
+  Comment ::
+    String ->
+    SomeInstr repr inp vs a ->
+    Instr InstrComment repr inp vs a
+instance InstrComment repr => Derivable (Instr InstrComment repr inp vs) where
+  derive = \case
+    Comment msg k -> comment msg (derive k)
+instance InstrComment repr => InstrComment (SomeInstr repr) where
+  comment msg = SomeInstr . Comment msg
+
+-- InstrValuable
+data instance Instr InstrValuable repr inp vs a where
+  PushValue ::
+    Splice v ->
+    SomeInstr repr inp (v ': vs) a ->
+    Instr InstrValuable repr inp vs a
+  PopValue ::
+    SomeInstr repr inp vs a ->
+    Instr InstrValuable repr inp (v ': vs) a
+  Lift2Value ::
+    Splice (x -> y -> z) ->
+    SomeInstr repr inp (z : vs) a ->
+    Instr InstrValuable repr inp (y : x : vs) a
+  SwapValue ::
+    SomeInstr repr inp (x ': y ': vs) a ->
+    Instr InstrValuable repr inp (y ': x ': vs) a
+instance InstrValuable repr => Derivable (Instr InstrValuable repr inp vs) where
+  derive = \case
+    PushValue v k -> pushValue v (derive k)
+    PopValue k -> popValue (derive k)
+    Lift2Value v k -> lift2Value v (derive k)
+    SwapValue k -> swapValue (derive k)
+instance InstrValuable repr => InstrValuable (SomeInstr repr) where
+  -- 'PopValue' after a 'PushValue' is a no-op.
+  pushValue _v (Instr (PopValue i)) = i
+  pushValue v i = SomeInstr (PushValue v i)
+  popValue = SomeInstr . PopValue
+  lift2Value f = SomeInstr . Lift2Value f
+  swapValue = SomeInstr . SwapValue
+
+-- InstrExceptionable
+data instance Instr InstrExceptionable repr inp vs a where
+  Raise ::
+    ExceptionLabel ->
+    Instr InstrExceptionable repr inp vs a
+  Fail ::
+    Set SomeFailure ->
+    Instr InstrExceptionable repr inp vs a
+  Commit ::
+    Exception ->
+    SomeInstr repr inp vs ret ->
+    Instr InstrExceptionable repr inp vs ret
+  Catch ::
+    Exception ->
+    SomeInstr repr inp vs ret ->
+    SomeInstr repr inp (InputPosition inp ': vs) ret ->
+    Instr InstrExceptionable repr inp vs ret
+instance InstrExceptionable repr => Derivable (Instr InstrExceptionable repr inp vs) where
+  derive = \case
+    Raise exn -> raise exn
+    Fail fs -> fail fs
+    Commit exn k -> commit exn (derive k)
+    Catch exn l r -> catch exn (derive l) (derive r)
+instance InstrExceptionable repr => InstrExceptionable (SomeInstr repr) where
+  raise = SomeInstr . Raise
+  fail = SomeInstr . Fail
+  commit exn = SomeInstr . Commit exn
+  catch exn x = SomeInstr . Catch exn x
+
+-- InstrBranchable
+data instance Instr InstrBranchable repr inp vs a where
+  CaseBranch ::
+    SomeInstr repr inp (x ': vs) a ->
+    SomeInstr repr inp (y ': vs) a ->
+    Instr InstrBranchable repr inp (Either x y ': vs) a
+  ChoicesBranch ::
+    [(Splice (v -> Bool), SomeInstr repr inp vs a)] ->
+    SomeInstr repr inp vs a ->
+    Instr InstrBranchable repr inp (v ': vs) a
+instance InstrBranchable repr => Derivable (Instr InstrBranchable repr inp vs) where
+  derive = \case
+    CaseBranch l r -> caseBranch (derive l) (derive r)
+    ChoicesBranch bs d -> choicesBranch (second derive Functor.<$> bs) (derive d)
+instance InstrBranchable repr => InstrBranchable (SomeInstr repr) where
+  caseBranch l = SomeInstr . CaseBranch l
+  choicesBranch bs = SomeInstr . ChoicesBranch bs
+
+-- InstrCallable
+data instance Instr InstrCallable repr inp vs a where
+  DefLet ::
+    LetBindings TH.Name (SomeInstr repr inp '[]) ->
+    SomeInstr repr inp vs a ->
+    Instr InstrCallable repr inp vs a
   Call ::
+    Bool ->
     LetName v ->
-    SomeInstr repr inp (v ': vs) ('Succ es) a ->
-    Instr Routinable repr inp vs ('Succ es) a
-  -- | @('Ret')@ returns the value stored in a singleton 'valueStack'.
+    SomeInstr repr inp (v ': vs) a ->
+    Instr InstrCallable repr inp vs a
   Ret ::
-    Instr Routinable repr inp '[a] es a
-instance Routinable repr => Trans (Instr Routinable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    Subroutine n sub k -> subroutine n (trans sub) (trans k)
-    Jump n -> jump n
-    Call n k -> call n (trans k)
+    Instr InstrCallable repr inp '[a] a
+  Jump ::
+    Bool ->
+    LetName a ->
+    Instr InstrCallable repr inp '[] a
+instance InstrCallable repr => Derivable (Instr InstrCallable repr inp vs) where
+  derive = \case
+    DefLet subs k -> defLet ((\(SomeLet sub) -> SomeLet (derive sub)) Functor.<$> subs) (derive k)
+    Jump isRec n -> jump isRec n
+    Call isRec n k -> call isRec n (derive k)
     Ret -> ret
-instance Routinable repr => Routinable (SomeInstr repr) where
-  subroutine n sub = SomeInstr . Subroutine n sub
-  jump = SomeInstr . Jump
-  call n = SomeInstr . Call n
+instance InstrCallable repr => InstrCallable (SomeInstr repr) where
+  defLet subs = SomeInstr . DefLet subs
+  jump isRec = SomeInstr . Jump isRec
+  call isRec n = SomeInstr . Call isRec n
   ret = SomeInstr Ret
 
--- Branchable
-data instance Instr Branchable repr inp vs fs a where
-  -- | @('Case' l r)@.
-  Case ::
-    SomeInstr repr inp (x ': vs) es a ->
-    SomeInstr repr inp (y ': vs) es a ->
-    Instr Branchable repr inp (Either x y ': vs) es a
-  -- | @('Choices' ps bs d)@.
-  Choices ::
-    [TermInstr (v -> Bool)] ->
-    [SomeInstr repr inp vs es a] ->
-    SomeInstr repr inp vs es a ->
-    Instr Branchable repr inp (v ': vs) es a
-instance Branchable repr => Trans (Instr Branchable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    Case l r -> caseI (trans l) (trans r)
-    Choices ps bs d -> choices ps (trans Functor.<$> bs) (trans d)
-instance Branchable repr => Branchable (SomeInstr repr) where
-  caseI l = SomeInstr . Case l
-  choices ps bs = SomeInstr . Choices ps bs
-
--- Failable
-data instance Instr Failable repr inp vs fs a where
-  -- | @('Fail')@ raises an error from the 'failStack'.
-  Fail ::
-    [ErrorItem (InputToken inp)] ->
-    Instr Failable repr inp vs ('Succ es) a
-  -- | @('PopFail' k)@ removes a 'FailHandler' from the 'failStack'
-  -- and continues with the next 'Instr'uction @(k)@.
-  PopFail ::
-    SomeInstr repr inp vs es ret ->
-    Instr Failable repr inp vs ('Succ es) ret
-  -- | @('CatchFail' l r)@ tries the @(l)@ 'Instr'uction
-  -- in a new failure scope such that if @(l)@ raises a failure, it is caught,
-  -- then the input is pushed as it was before trying @(l)@ on the 'valueStack',
-  -- and the control flow goes on with the @(r)@ 'Instr'uction.
-  CatchFail ::
-    SomeInstr repr inp vs ('Succ es) ret ->
-    SomeInstr repr inp (Cursor inp ': vs) es ret ->
-    Instr Failable repr inp vs es ret
-instance Failable repr => Trans (Instr Failable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    Fail err -> fail err
-    PopFail k -> popFail (trans k)
-    CatchFail l r -> catchFail (trans l) (trans r)
-instance Failable repr => Failable (SomeInstr repr) where
-  fail = SomeInstr . Fail
-  popFail = SomeInstr . PopFail
-  catchFail x = SomeInstr . CatchFail x
-
--- Inputable
-data instance Instr Inputable repr inp vs fs a where
-  -- | @('LoadInput' k)@ removes the input from the 'valueStack'
-  -- and continues with the next 'Instr'uction @(k)@ using that input.
-  LoadInput ::
-    SomeInstr repr inp vs es a ->
-    Instr Inputable repr inp (Cursor inp : vs) es a
-  -- | @('PushInput' k)@ pushes the input @(inp)@ on the 'valueStack'
-  -- and continues with the next 'Instr'uction @(k)@.
-  PushInput ::
-    SomeInstr repr inp (Cursor inp ': vs) es a ->
-    Instr Inputable repr inp vs es a
-instance Inputable repr => Trans (Instr Inputable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    LoadInput k -> loadInput (trans k)
-    PushInput k -> pushInput (trans k)
-instance Inputable repr => Inputable (SomeInstr repr) where
-  loadInput = SomeInstr . LoadInput
-  pushInput = SomeInstr . PushInput
-
--- Joinable
-data instance Instr Joinable repr inp vs fs a where
+-- InstrJoinable
+data instance Instr InstrJoinable repr inp vs a where
   DefJoin ::
     LetName v ->
-    SomeInstr repr inp (v ': vs) es a ->
-    SomeInstr repr inp vs es a ->
-    Instr Joinable repr inp vs es a
+    SomeInstr repr inp (v ': vs) a ->
+    SomeInstr repr inp vs a ->
+    Instr InstrJoinable repr inp vs a
   RefJoin ::
     LetName v ->
-    Instr Joinable repr inp (v ': vs) es a
-instance Joinable repr => Trans (Instr Joinable repr inp vs es) (repr inp vs es) where
-  trans = \case
-    DefJoin n sub k -> defJoin n (trans sub) (trans k)
+    Instr InstrJoinable repr inp (v ': vs) a
+instance InstrJoinable repr => Derivable (Instr InstrJoinable repr inp vs) where
+  derive = \case
+    DefJoin n sub k -> defJoin n (derive sub) (derive k)
     RefJoin n -> refJoin n
-instance Joinable repr => Joinable (SomeInstr repr) where
+instance InstrJoinable repr => InstrJoinable (SomeInstr repr) where
   defJoin n sub = SomeInstr . DefJoin n sub
   refJoin = SomeInstr . RefJoin
 
--- Readable
-data instance Instr (Readable tok) repr inp vs fs a where
-  -- | @('Read' expected p k)@ reads a 'Char' @(c)@ from the 'inp'ut,
-  -- if @(p c)@ is 'True' then continues with the next 'Instr'uction @(k)@ on,
-  -- otherwise 'Fail'.
+-- InstrInputable
+data instance Instr InstrInputable repr inp vs a where
+  PushInput ::
+    SomeInstr repr inp (InputPosition inp ': vs) a ->
+    Instr InstrInputable repr inp vs a
+  LoadInput ::
+    SomeInstr repr inp vs a ->
+    Instr InstrInputable repr inp (InputPosition inp ': vs) a
+instance InstrInputable repr => Derivable (Instr InstrInputable repr inp vs) where
+  derive = \case
+    PushInput k -> saveInput (derive k)
+    LoadInput k -> loadInput (derive k)
+instance InstrInputable repr => InstrInputable (SomeInstr repr) where
+  saveInput = SomeInstr . PushInput
+  loadInput = SomeInstr . LoadInput
+
+-- InstrReadable
+data instance Instr (InstrReadable tok) repr inp vs a where
   Read ::
-    [ErrorItem (InputToken inp)] ->
-    TermInstr (InputToken inp -> Bool) ->
-    SomeInstr repr inp (InputToken inp ': vs) ('Succ es) a ->
-    Instr (Readable tok) repr inp vs ('Succ es) a
+    Set SomeFailure ->
+    Splice (InputToken inp -> Bool) ->
+    SomeInstr repr inp (InputToken inp ': vs) a ->
+    Instr (InstrReadable tok) repr inp vs a
+instance
+  ( InstrReadable tok repr, tok ~ InputToken inp ) =>
+  Derivable (Instr (InstrReadable tok) repr inp vs) where
+  derive = \case
+    Read fs p k -> read fs p (derive k)
+instance
+  ( InstrReadable tok repr, Typeable tok ) =>
+  InstrReadable tok (SomeInstr repr) where
+  read fs p = SomeInstr . Read fs p
+
+-- InstrIterable
+data instance Instr InstrIterable repr inp vs a where
+  Iter ::
+    LetName a ->
+    SomeInstr repr inp '[] a ->
+    SomeInstr repr inp (InputPosition inp ': vs) a ->
+    Instr InstrIterable repr inp vs a
+instance
+  InstrIterable repr =>
+  Derivable (Instr InstrIterable repr inp vs) where
+  derive = \case
+    Iter n op k -> iter n (derive op) (derive k)
+instance
+  InstrIterable repr =>
+  InstrIterable (SomeInstr repr) where
+  iter n op = SomeInstr . Iter n op
+
+-- InstrRegisterable
+data instance Instr InstrRegisterable repr inp vs a where
+  NewRegister ::
+    UnscopedRegister v ->
+    SomeInstr repr inp vs a ->
+    Instr InstrRegisterable repr inp (v : vs) a
+  ReadRegister ::
+    UnscopedRegister v ->
+    SomeInstr repr inp (v : vs) a ->
+    Instr InstrRegisterable repr inp vs a
+  WriteRegister ::
+    UnscopedRegister v ->
+    SomeInstr repr inp vs a ->
+    Instr InstrRegisterable repr inp (v : vs) a
 instance
-  ( Readable tok repr, tok ~ InputToken inp ) =>
-  Trans (Instr (Readable tok) repr inp vs es) (repr inp vs es) where
-  trans = \case
-    Read es p k -> read es p (trans k)
+  InstrRegisterable repr =>
+  Derivable (Instr InstrRegisterable repr inp vs) where
+  derive = \case
+    NewRegister r k -> newRegister r (derive k)
+    ReadRegister r k -> readRegister r (derive k)
+    WriteRegister r k -> writeRegister r (derive k)
 instance
-  ( Readable tok repr, Typeable tok ) =>
-  Readable tok (SomeInstr repr) where
-  read es p = SomeInstr . Read es p
+  InstrRegisterable repr =>
+  InstrRegisterable (SomeInstr repr) where
+  newRegister r = SomeInstr . NewRegister r
+  readRegister r = SomeInstr . ReadRegister r
+  writeRegister r = SomeInstr . WriteRegister r