packages feed

symantic-parser-0.1.0.20210201: src/Symantic/Parser/Machine/Instructions.hs

{-# LANGUAGE ConstraintKinds #-} -- For Machine
{-# LANGUAGE DerivingStrategies #-} -- For Show (LetName a)
-- | Semantic of the parsing instructions used
-- to make the parsing control-flow explicit,
-- in the convenient tagless-final encoding.
module Symantic.Parser.Machine.Instructions where

import Data.Bool (Bool(..))
import Data.Either (Either)
import Data.Eq (Eq(..))
import Data.Function ((.))
import Data.Kind (Type)
-- import GHC.TypeLits (Symbol)
import Text.Show (Show(..))
import qualified Language.Haskell.TH as TH
import qualified Symantic.Parser.Haskell as H

import Symantic.Parser.Grammar
import Symantic.Parser.Machine.Input

-- * Type 'TermInstr'
type TermInstr = H.Term TH.CodeQ

-- * Type 'Peano'
-- | Type-level natural numbers,
-- using the Peano recursive encoding.
data Peano = Zero | Succ Peano

-- * Class 'Machine'
-- | All the 'Instr'uctions.
type Machine tok repr =
  ( Branchable repr
  , Failable repr
  , Inputable repr
  , Joinable repr
  , Routinable repr
  , Stackable repr
  , Readable tok repr
  )

-- ** Type 'ReprInstr'
type ReprInstr = Type -> [Type] -> Peano -> Type -> Type

-- ** Type 'LetName'
-- | 'TH.Name' of a 'subroutine' or 'defJoin'
-- indexed by the return type of the factorized 'Instr'uctions.
-- This helps type-inferencing.
newtype LetName a = LetName { unLetName :: TH.Name }
  deriving Eq
  deriving newtype Show

-- ** Class 'Stackable'
class Stackable (repr::ReprInstr) where
  push ::
    TermInstr v ->
    repr inp (v ': vs) es a ->
    repr inp vs es a
  pop ::
    repr inp vs es a ->
    repr inp (v ': vs) es a
  liftI2 ::
    TermInstr (x -> y -> z) ->
    repr inp (z ': vs) es a ->
    repr inp (y ': x ': vs) es a
  swap ::
    repr inp (x ': y ': vs) es a ->
    repr inp (y ': x ': vs) es a
  -- | @('mapI' f k)@.
  mapI ::
    TermInstr (x -> y) ->
    repr inp (y ': vs) es a ->
    repr inp (x ': vs) es a
  mapI f = push f . liftI2 (H.flip H..@ (H.$))
  -- | @('appI' k)@ pops @(x)@ and @(x2y)@ from the 'valueStack',
  -- pushes @(x2y x)@ and continues with the next 'Instr'uction @(k)@.
  appI ::
    repr inp (y ': vs) es a ->
    repr inp (x ': (x -> y) ': vs) es a
  appI = liftI2 (H.$)

-- ** Class 'Routinable'
class Routinable (repr::ReprInstr) where
  subroutine ::
    LetName v -> repr inp '[] ('Succ 'Zero) v ->
    repr inp vs ('Succ es) a ->
    repr inp vs ('Succ es) a
  call ::
    LetName v -> repr inp (v ': vs) ('Succ es) a ->
    repr inp vs ('Succ es) a
  ret ::
    repr inp '[a] es a
  jump ::
    LetName a ->
    repr inp '[] ('Succ es) a

-- ** Class 'Branchable'
class Branchable (repr::ReprInstr) where
  caseI ::
    repr inp (x ': vs) es r ->
    repr inp (y ': vs) es r ->
    repr inp (Either x y ': vs) es r
  choices ::
    [TermInstr (v -> Bool)] ->
    [repr inp vs es a] ->
    repr inp vs es a ->
    repr inp (v ': vs) es a
  -- | @('ifI' ok ko)@ pops a 'Bool' from the 'valueStack'
  -- and continues either with the 'Instr'uction @(ok)@ if it is 'True'
  -- or @(ko)@ otherwise.
  ifI ::
    repr inp vs es a ->
    repr inp vs es a ->
    repr inp (Bool ': vs) es a
  ifI ok ko = choices [H.id] [ok] ko

-- ** Class 'Failable'
class Failable (repr::ReprInstr) where
  fail ::
    [ErrorItem (InputToken inp)] ->
    repr inp vs ('Succ es) a
  popFail ::
    repr inp vs es a ->
    repr inp vs ('Succ es) a
  catchFail ::
    repr inp vs ('Succ es) a ->
    repr inp (Cursor inp ': vs) es a ->
    repr inp vs es a

-- ** Class 'Inputable'
class Inputable (repr::ReprInstr) where
  loadInput ::
    repr inp vs es a ->
    repr inp (Cursor inp ': vs) es a
  pushInput ::
    repr inp (Cursor inp ': vs) es a ->
    repr inp vs es a

-- ** Class 'Joinable'
class Joinable (repr::ReprInstr) where
  defJoin ::
    LetName v -> repr inp (v ': vs) es a ->
    repr inp vs es a ->
    repr inp vs es a
  refJoin ::
    LetName v ->
    repr inp (v ': vs) es a

-- ** Class 'Readable'
class Readable (tok::Type) (repr::ReprInstr) where
  read ::
    tok ~ InputToken inp =>
    [ErrorItem tok] ->
    TermInstr (tok -> Bool) ->
    repr inp (tok ': vs) ('Succ es) a ->
    repr inp vs ('Succ es) a