{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveAnyClass #-} module Gyehoek.Stack.Syntax ( Program(..) , Routine(..) , Instr(..) , Block(..) , Tail(..) , Val(..) , Lit(..) , Obj(..) , Imm(..) , Hob(..) , Prim(..) , Name , pattern ValLabel , pattern ObjLabel , stkP ) where import Control.Lens import qualified Gyehoek.Sexp as S import Gyehoek.Scheme.Syntax (Name(..), Lit(..), Prim(..)) import GHC.Exts (IsList(..)) import Data.List (intersperse) import Gyehoek.CPS.Syntax (Imm(..), Obj(..), Hob(..), labelName, pattern ObjLabel) import Gyehoek.Prelude import Gyehoek.Sexp ((:-)((:-))) newtype Program = MkProgram { routines :: HashMap Name Routine } deriving stock (Show, Generic, Data) deriving newtype (Semigroup, Monoid) deriving anyclass (NFData) instance IsList Program where type Item Program = Routine fromList rs = MkProgram { routines = fromList [ (r.label, r) | r <- rs ] } toList = toListOf $ #routines . each data Routine = MkRoutine { label :: Name , start :: Block } deriving stock (Show, Generic, Data) deriving anyclass (NFData) data Block = MkBlock { code :: List Instr , tail :: Tail } deriving stock (Show, Generic, Data) deriving anyclass (NFData) data Tail -- | call the procedure at stack index `n` supplied with `n` -- arguments on top of the stack, then return by calling the -- continuation at stack index `n+1`. = TailCall Int | Call Int | If Val Block Block | Return Int deriving stock (Show, Generic, Data) deriving anyclass (NFData) data Instr = Pop Name | Push Val | Load Name | Prim Name (Prim Val) deriving stock (Show, Generic, Data) deriving anyclass (NFData) data Val = ValReg Name | ValImm Imm deriving stock (Show, Generic, Data, Eq) deriving anyclass (NFData) pattern ValLabel :: Name -> Val pattern ValLabel x = ValImm (ImmLabel x) --- sexp work pure [] instance S.DatumIso Instr where datumIso = S.match $ S.With (S.headTagged1 "pop!" regName >>>) $ S.With (S.headTagged1 "push!" S.datumIso >>>) $ S.With (S.headTagged1 "load!" regName >>>) $ S.With (S.headTagged2 "prim" regName S.datumIso >>>) $ S.End where instance S.DataIso Block where dataIso = S.with \g -> S.flipped S.snoced >>> S.onHead (S.traversed $ S.sealed S.datumIso) >>> S.onTail (S.datumIso @Tail) >>> S.swap >>> g instance S.DatumIso Tail where datumIso = S.match $ S.With (S.headTagged1 "tail-call" S.datumIso >>>) $ S.With (S.headTagged1 "call" S.datumIso >>>) $ S.With (if_ >>>) $ S.With (S.headTagged1 "return" S.datumIso >>>) $ S.End where -- if_ = S.ifLike "if" (S.datumIso @Val) S.datumIso S.datumIso if_ = S.ifLike "if" (S.datumIso @Val) (branch "then") (branch "else") branch :: Text -> S.DatumGrammar Block branch s = S.listWithIndentation (S.NSpecial 0) $ S.el (S.decorate S.SynBuiltin >>> S.sym s) >>> S.restData (S.dataIso @Block) instance S.DatumIso Val where datumIso = S.match $ S.With (regName >>>) $ S.With (S.datumIso >>>) $ S.End instance S.DatumIso Routine where datumIso = S.with \rout -> S.listWithIndentation (S.NSpecial 1) ( S.el (S.decorate S.SynBuiltin >>> S.sym "define") >>> S.el labelName >>> S.restData (S.dataIso @Block) ) >>> rout regName :: S.DatumGrammar Name regName = S.decorate S.SynVariable >>> S.datumIso @Name >>> S.prismIso (S.expected "register") (prefixed @Name "%") instance S.DataIso Program where dataIso = S.dataIso @(List Routine) >>> S.iso fromList toList stkP :: S.QuasiQuoter stkP = S.makeSxs [|| S.fromDataUnsafe (S.dataIso @Program) ||]