pass continuations as arguments, use normal stack
build / build (push) Successful in 28s

This commit is contained in:
2026-08-24 23:41:52 -06:00
parent 796b967686
commit 87baed9efc
13 changed files with 158 additions and 136 deletions
+1 -1
View File
@@ -38,4 +38,4 @@ close = transformM \case
e -> pure e e -> pure e
closeProgram :: GenSym :> es => Program -> Eff es Program closeProgram :: GenSym :> es => Program -> Eff es Program
closeProgram = traverseOf #body close closeProgram = traverseOf (#body . #body) close
+5 -2
View File
@@ -109,8 +109,11 @@ convertLambda bs m = do
pure [cps|(λ (##{bs} #{ktail}) #{m'})|] pure [cps|(λ (##{bs} #{ktail}) #{m'})|]
convertProgram :: forall es. (GenSym :> es) => Scm.Program -> Eff es Program convertProgram :: forall es. (GenSym :> es) => Scm.Program -> Eff es Program
convertProgram p = convertProgram p = do
MkProgram <$> telescope (convert @es) (p ^.. each . _Left) (pure . Halt) ktail <- gensym' "start-ktail"
m <- telescope (convert @es) (p ^.. each . _Left)
(pure . ExpContinue (ValVar ktail))
pure . MkProgram $ MkLambda [] ktail m
convertExp :: forall es. (GenSym :> es) => Scm.Exp -> Eff es Exp convertExp :: forall es. (GenSym :> es) => Scm.Exp -> Eff es Exp
convertExp e = convert e (pure . Halt1) convertExp e = convert e (pure . Halt1)
+9 -2
View File
@@ -2,6 +2,7 @@
module Gyehoek.CPS.Eval module Gyehoek.CPS.Eval
( evalProgram ( evalProgram
, module Gyehoek.CPS.Syntax , module Gyehoek.CPS.Syntax
, evalExp
) where ) where
import Gyehoek.CPS.Syntax import Gyehoek.CPS.Syntax
@@ -22,7 +23,7 @@ eval :: Env -> Exp -> List Obj
eval g (Halt xs) = evalVal g <$> xs eval g (Halt xs) = evalVal g <$> xs
eval g (ExpContinue k xs) = eval g (ExpContinue ((^?! #ValVar) -> k) xs) =
case g ^. #labels . at k of case g ^. #labels . at k of
Just (h, AbsKappa' bs m) -> eval h' m Just (h, AbsKappa' bs m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs) where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
@@ -75,5 +76,11 @@ emptyEnv = MkEnv
) )
} }
evalExp :: Exp -> List Obj
evalExp = eval emptyEnv
evalProgram :: Program -> List Obj evalProgram :: Program -> List Obj
evalProgram (MkProgram e) = eval emptyEnv e evalProgram (MkProgram lam) = eval emptyEnv [cps|
(letrec ((start #{lam}))
(apply start halt))
|]
+53 -56
View File
@@ -1,7 +1,6 @@
{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedLists #-}
module Gyehoek.CPS.Stackify module Gyehoek.CPS.Stackify
( stackifyExp ( stackifyProgram
, stackifyProgram
, module Gyehoek.CPS.Syntax , module Gyehoek.CPS.Syntax
) where ) where
@@ -13,8 +12,11 @@ import Gyehoek.GenSym
import Effectful.Writer.Static.Shared import Effectful.Writer.Static.Shared
import Data.Foldable import Data.Foldable
import qualified Data.HashMap.Strict as H import qualified Data.HashMap.Strict as H
import Data.List (elemIndex) import Data.List (elemIndex, nub)
import Data.Text qualified as T
import Gyehoek.Prelude import Gyehoek.Prelude
import Debug.Pretty.Simple
import qualified Gyehoek.Sexp as S
type Stackify = Writer Stk.Program type Stackify = Writer Stk.Program
@@ -23,9 +25,10 @@ runStackify :: Eff (Stackify : es) a -> Eff es (a, Stk.Program)
runStackify = runWriter runStackify = runWriter
live :: Free a => Env -> a -> List Name live :: Free a => Env -> a -> List Name
live g e = free' e & filter \x -> -- TODO: free' should return an OSet lol
live g e = nub (free' e) & filter \x ->
x `H.member` g.bound x `H.member` g.bound
&& not (x `elem` g.contStack) -- && not (x `elem` g.contStack)
data BlockBuilder data BlockBuilder
= Code (List Stk.Instr) BlockBuilder = Code (List Stk.Instr) BlockBuilder
@@ -47,20 +50,18 @@ stackify
stackify g (ExpLetRec [(f, kap@(AbsKappa' xs m))] e) = do stackify g (ExpLetRec [(f, kap@(AbsKappa' xs m))] e) = do
let vs = (f, Stk.ValLabel f) : (bindReg <$> xs) let vs = (f, Stk.ValLabel f) : (bindReg <$> xs)
let ls = live g kap let ls = live g kap
m' <- stackify (g & #bound .~ H.fromList (vs ++ (bindReg <$> ls))) m m' <- stackify (g & #bound <>~ H.fromList (vs ++ (bindReg <$> ls))) m
emitRoutine $ emitRoutine $
Stk.MkRoutine f xs . buildBlock $ Stk.MkRoutine f xs . buildBlock $
Code [Stk.Pop x | x <- ls] m' -- pop in the opposite order we push
Code [Stk.Pop x | x <- reverse ls] m'
let g' = g & #bound . at f ?~ Stk.ValLabel f let g' = g & #bound . at f ?~ Stk.ValLabel f
& #liveness . at f ?~ ls & #liveness . at f ?~ ls
stackify g' e stackify g' e
stackify g (ExpLetRec [(f, AbsLambda' xs k m)] e) = do stackify g (ExpLetRec [(f, AbsLambda lam)] e) = do
let vs = (k:xs) <&> \x -> (x, Stk.ValReg x) emitRoutine =<< stackifyLambda g f lam
m' <- stackify (g & #bound .~ H.fromList vs stackify (g & #bound . at f ?~ Stk.ValLabel f) e
& #contStack %~ (k:)) m
emitRoutine $ Stk.MkRoutine f xs (buildBlock m')
stackify g e
stackify g (ExpIf c t f) = do stackify g (ExpIf c t f) = do
let c' = stackifyVal g c let c' = stackifyVal g c
@@ -69,36 +70,41 @@ stackify g (ExpIf c t f) = do
pure . Tail $ Stk.If c' t' f' pure . Tail $ Stk.If c' t' f'
stackify g (ExpApply f xs ktail) = pure $ stackify g (ExpApply f xs ktail) = pure $
Code [ Stk.PushCont k ] $
Code [ Stk.Push (Stk.ValReg l) | l <- ls ] $ Code [ Stk.Push (Stk.ValReg l) | l <- ls ] $
Tail (Stk.TailCall (stackifyVal g f) (stackifyVal g <$> xs)) Tail (Stk.TailCall (stackifyVal g f) (k : (stackifyVal g <$> xs)))
where where
k = var g ktail k = var g ktail
ls = fold $ (k ^? #ValImm . #ImmLabel) ls = fold $ (k ^? #ValImm . #ImmLabel)
>>= \klbl -> g ^. #liveness . at klbl >>= \klbl -> g ^. #liveness . at klbl
stackify g (ExpContinue k xs) = stackify g e@(ExpContinue k xs) = do
-- return continuations require popping the stack. how do we know pure $
-- when a continuation is a return continuation? is this a correct Code [ Stk.Push (Stk.ValReg l) | l <- ls ] $
-- test? Tail (Stk.TailCall k' (stackifyVal g <$> xs))
case elemIndex k g.contStack of where
Nothing -> pure . Tail $ Stk.TailCall (Stk.ValLabel k) xs' k' = stackifyVal g k
Just j -> do ls = fold $ (k' ^? #ValImm . #ImmLabel)
ktail <- gensym' @Name $ k ^. _Wrapped' >>= \klbl -> g ^. #liveness . at klbl
pure $
Code (replicate j $ Stk.PopCont "_") $
Code [Stk.PopCont ktail] $
Tail (Stk.TailCall (Stk.ValReg ktail) xs')
where xs' = stackifyVal g <$> xs
stackify g (ExpPrim p (MkKappa [x] e)) = do stackify g (ExpPrim p (MkKappa [x] e)) = do
e' <- stackify (g & #bound . at x ?~ Stk.ValReg x) e e' <- stackify (g & #bound . at x ?~ Stk.ValReg x) e
pure $ pure $
Code [ Stk.Prim x (stackifyVal g <$> p) ] $ Code [ Stk.Prim x (stackifyVal g <$> p) ] e'
e'
stackify _ e = error [i|unimplemented exp: #{e}|] stackify _ e = error [i|unimplemented exp: #{e}|]
-- affine
_ValName :: Traversal' Val Name
_ValName = failing #ValVar (#ValImm . #ImmLabel)
stackifyLambda
:: (Stackify :> es, GenSym :> es)
=> Env -> Name -> Lambda -> Eff es Stk.Routine
stackifyLambda g name (MkLambda xs k m) = do
let vs = [ (x, Stk.ValReg x) | x <- k:xs ]
m' <- stackify (g & #bound <>~ H.fromList vs) m
pure $ Stk.MkRoutine name (k:xs) (buildBlock m')
stackifyVal :: Env -> Val -> Stk.Val stackifyVal :: Env -> Val -> Stk.Val
stackifyVal g = \case stackifyVal g = \case
ValImm imm -> Stk.ValImm imm ValImm imm -> Stk.ValImm imm
@@ -121,39 +127,30 @@ data Env = MkEnv
-- entry @(k,ls)@ where @ls@ is the sequence of registers @k@ -- entry @(k,ls)@ where @ls@ is the sequence of registers @k@
-- expects to find saved on the stack. -- expects to find saved on the stack.
, liveness :: HashMap Name (List Name) , liveness :: HashMap Name (List Name)
, contStack :: List Name
} }
deriving (Show, Generic) deriving (Show, Generic)
emptyEnv :: Env emptyEnv :: Env
emptyEnv = MkEnv mempty mempty ["halt"] emptyEnv = MkEnv mempty mempty
stackifyExp :: GenSym :> es => Name -> Exp -> Eff es Stk.Program
stackifyExp lbl e = do
(code,p) <- runStackify $ stackify emptyEnv e
pure $ p <> [ Stk.MkRoutine lbl [] (buildBlock code) ]
stackifyProgram :: GenSym :> es => Program -> Eff es Stk.Program stackifyProgram :: GenSym :> es => Program -> Eff es Stk.Program
stackifyProgram (MkProgram e) = stackifyExp "main" e stackifyProgram (MkProgram lam) = do
let g = emptyEnv
(start,p) <- runStackify $ stackifyLambda g "start" lam
pure $ p <> [ start ]
letfn :: Program
letfn = [cps|
fac :: Program (λ (start-ktail0)
fac = [cps| (letrec ((lambda-body1
(letrec ((fac (λ (n ktail) (λ (x lambda-tail2)
(prim (zero? n) (prim (* x x) (κ (r3) (continue lambda-tail2 r3))))))
(κ (x0) (letrec ((let-body6
(if x0 (κ (square)
(continue ktail 1) (letrec ((r4 (κ (x5) (continue start-ktail0 x5))))
(prim (- n 1) (square 4 r4)))))
(κ (x1) (continue let-body6 lambda-body1))))
(letrec ((fac-k0
(κ (x2)
(prim (* n x2)
(κ (x3)
(continue ktail x3))))))
(fac x1 fac-k0))))))))))
(fac 6 halt))
|] |]
+13 -18
View File
@@ -121,7 +121,7 @@ data Def = DefConstant Name Exp
deriving (Show, Generic, Data) deriving (Show, Generic, Data)
data Program = MkProgram data Program = MkProgram
{ body :: Exp { body :: Lambda
} }
deriving (Show, Generic, Data) deriving (Show, Generic, Data)
@@ -190,28 +190,23 @@ instance S.DatumIso Hob where
(const . Left $ mempty) (const . Left $ mempty)
instance S.DatumIso Lambda where instance S.DatumIso Lambda where
datumIso = S.match datumIso = S.with (lam >>>)
$ S.With (. lambda)
$ S.End
where where
lambda = S.list $ lam :: forall t. G (Datum :- t) (Exp :- Name :- List Name :- t)
S.el S.lambdaKeyword lam = S.lambdaLike
>>> S.el binders S.lambdaKeyword
>>> S.el S.datumIso binders
(S.el $ S.datumIso @Exp)
binders :: forall t. G (Datum :- t) (Name :- List Name :- t) binders :: forall t. G (Datum :- t) (Name :- List Name :- t)
binders = S.list $ binders =
S.rest (S.datumIso @Name) S.list (S.rest $ S.datumIso @Name)
>>> S.onTail (S.flipped $ IG.PartialIso >>> S.flipped S.snoced
(\(ktail:-args:-t) -> (args ++ [ktail]) :- t) >>> S.swap
(\(args:-t) -> case args ^? _Snoc of
Just (args',ktail) -> Right $ ktail :- args' :- t
Nothing -> Left $ S.expected "cont param")
)
instance S.DatumIso Kappa where instance S.DatumIso Kappa where
datumIso = S.with \g -> datumIso = S.with \g ->
S.lambdaLike S.kappaKeyword S.lambdaLike S.kappaKeyword
(S.list $ S.rest (S.datumIso @Name)) (S.datumIso @(List Name))
(S.el $ S.datumIso @Exp) (S.el $ S.datumIso @Exp)
>>> g >>> g
@@ -260,7 +255,7 @@ instance S.DatumIso Exp where
>>> S.el S.datumIso >>> S.el S.datumIso
instance S.DatumIso Program where instance S.DatumIso Program where
datumIso = S.with \prog -> S.datumIso @Exp >>> prog datumIso = S.with \prog -> S.datumIso @Lambda >>> prog
-- quasiquoters -- quasiquoters
+3 -3
View File
@@ -365,9 +365,9 @@ letLike kw name rhs e = listWithIndentation (NSpecial 1) $
lambdaLike lambdaLike
:: (forall t. G (Datum :- t) t) :: (forall t. G (Datum :- t) t)
-> DatumGrammar a -> G (Datum :- t1) (a :- t2)
-> G (ListContext :- a :- t) (ListContext :- t') -> G (ListContext :- a :- t2) (ListContext :- t3)
-> G (Datum :- t) t' -> G (Datum :- t1) t3
lambdaLike kw formals body = listWithIndentation (NSpecial 1) $ lambdaLike kw formals body = listWithIndentation (NSpecial 1) $
el (decorate SynBuiltin >>> kw) el (decorate SynBuiltin >>> kw)
>>> el formals >>> el formals
+2 -2
View File
@@ -16,9 +16,9 @@ lowerBlock = _
lowerInstr :: Instr -> Wasm.Expr lowerInstr :: Instr -> Wasm.Expr
lowerInstr = \case lowerInstr = \case
PopCont ktail -> [wat| -- PopCont ktail -> [wat|
|] -- |]
lowerProgram :: Program -> Eff es Wasm.Module lowerProgram :: Program -> Eff es Wasm.Module
lowerProgram p = pure [watM| lowerProgram p = pure [watM|
+2 -4
View File
@@ -60,6 +60,7 @@ data Block = MkBlock
data Tail data Tail
= TailCall Val (List Val) = TailCall Val (List Val)
| PushCall Val Val (List Val)
| If Val Block Block | If Val Block Block
deriving stock (Show, Generic, Data) deriving stock (Show, Generic, Data)
deriving anyclass (NFData) deriving anyclass (NFData)
@@ -67,8 +68,6 @@ data Tail
data Instr data Instr
= Pop Name = Pop Name
| Push Val | Push Val
| PopCont Name
| PushCont Val
| Prim Name (Prim Val) | Prim Name (Prim Val)
deriving stock (Show, Generic, Data) deriving stock (Show, Generic, Data)
deriving anyclass (NFData) deriving anyclass (NFData)
@@ -91,8 +90,6 @@ instance S.DatumIso Instr where
datumIso = S.match datumIso = S.match
$ S.With (S.headTagged1 "pop!" regName >>>) $ S.With (S.headTagged1 "pop!" regName >>>)
$ S.With (S.headTagged1 "push!" S.datumIso >>>) $ S.With (S.headTagged1 "push!" S.datumIso >>>)
$ S.With (S.headTagged1 "pop-cont!" regName >>>)
$ S.With (S.headTagged1 "push-cont!" S.datumIso >>>)
$ S.With (S.headTagged2 "prim" regName S.datumIso >>>) $ S.With (S.headTagged2 "prim" regName S.datumIso >>>)
$ S.End $ S.End
where where
@@ -108,6 +105,7 @@ instance S.DataIso Block where
instance S.DatumIso Tail where instance S.DatumIso Tail where
datumIso = S.match datumIso = S.match
$ S.With (S.headTagged1' "tail-call" S.datumIso S.datumIso >>>) $ S.With (S.headTagged1' "tail-call" S.datumIso S.datumIso >>>)
$ S.With (S.headTagged2' "push-call" S.datumIso S.datumIso S.datumIso >>>)
$ S.With (if_ >>>) $ S.With (if_ >>>)
$ S.End $ S.End
where where
+24 -10
View File
@@ -17,7 +17,6 @@ import Gyehoek.Prelude
data VM = MkVM data VM = MkVM
{ stack :: List Obj { stack :: List Obj
, kstack :: List Name
, code :: List Instr , code :: List Instr
, tail :: Tail , tail :: Tail
, registers :: HashMap Name Obj , registers :: HashMap Name Obj
@@ -40,8 +39,6 @@ stepI :: Env -> VM -> Instr -> VM
stepI e vm (Push v) = vm & #stack %~ (evalVal e vm v :) stepI e vm (Push v) = vm & #stack %~ (evalVal e vm v :)
stepI e vm (PushCont k) = vm & #kstack %~ (evalToLabel e vm k :)
stepI e vm (Prim r p) = case evalVal e vm <$> p of stepI e vm (Prim r p) = case evalVal e vm <$> p of
PrimZeroP x -> case x of PrimZeroP x -> case x of
ObjImm (ImmInt n) -> ret . ObjImm . ImmBool $ n == 0 ObjImm (ImmInt n) -> ret . ObjImm . ImmBool $ n == 0
@@ -74,11 +71,6 @@ stepI e vm (Pop r) = case vm ^. #stack of
(x:xs) -> vm & #registers . at r ?~ x (x:xs) -> vm & #registers . at r ?~ x
& #stack .~ xs & #stack .~ xs
stepI e vm ins@(PopCont r) = case vm ^. #kstack of
[] -> error [i|empty cont stack: #{ins}|]
(x:xs) -> vm & #registers . at r ?~ ObjImm (ImmLabel x)
& #kstack .~ xs
stepI e vm ins = error [i|unimplemented instruction: #{ins}|] stepI e vm ins = error [i|unimplemented instruction: #{ins}|]
stepT :: Env -> VM -> Tail -> VM stepT :: Env -> VM -> Tail -> VM
@@ -95,6 +87,9 @@ stepT g vm (TailCall f xs) =
Nothing -> error [i|undefined label: #{l}|] Nothing -> error [i|undefined label: #{l}|]
Just x -> x Just x -> x
stepT g vm (PushCall k f xs) =
_
stepT g vm (If c t f) = vm & #code .~ branch.code & #tail .~ branch.tail stepT g vm (If c t f) = vm & #code .~ branch.code & #tail .~ branch.tail
where where
branch = case evalVal g vm c of branch = case evalVal g vm c of
@@ -116,9 +111,8 @@ evalVal e vm = \case
initialVM :: VM initialVM :: VM
initialVM = MkVM initialVM = MkVM
{ stack = [] { stack = []
, kstack = ["halt"]
, code = [] , code = []
, tail = TailCall (ValLabel "main") [] , tail = TailCall (ValLabel "start") [ValLabel "halt"]
, registers = mempty , registers = mempty
, stdout = "" , stdout = ""
, result = Nothing , result = Nothing
@@ -154,3 +148,23 @@ writeObj (ObjImm im) = case im of
ImmLabel l -> "#<procedure>" ImmLabel l -> "#<procedure>"
writeObj (ObjHob h) = case h of writeObj (ObjHob h) = case h of
HobClosure code env -> "#<procedure>" HobClosure code env -> "#<procedure>"
blah = [stkP|
(define ($lambda-body0-code7 %lambda-tail1 %lambda-body0 %x)
(prim %r2 (* %x %x))
(tail-call %lambda-tail1 %r2))
(define ($r3 %x4)
(pop! %main-ktail)
(tail-call %main-ktail %x4))
(define ($main %main-ktail)
(prim %lambda-body0 (make-closure $lambda-body0-code7))
(tail-call $let-body5 %lambda-body0))
(define ($let-body5 %square)
(pop! %main-ktail)
(prim %code6 (env-code %square))
(push! %main-ktail)
(tail-call %code6 $r3 %square 4))
|]
+2 -2
View File
@@ -34,8 +34,8 @@ test_cpsInterpreter = testGroup "cps interpreter" $
|] |]
] ]
evalsTo :: HasCallStack => List Obj -> Sut.Program -> Assertion evalsTo :: HasCallStack => List Obj -> Sut.Exp -> Assertion
evalsTo rs p = Sut.evalProgram p @?= rs evalsTo rs e = Sut.evalExp e @?= rs
primitives = testGroup "primitives" primitives = testGroup "primitives"
[ testGroup "arith" [ testGroup "arith"
+7 -4
View File
@@ -4,10 +4,11 @@ import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit import Test.Tasty.HUnit
import qualified Gyehoek.CPS.Stackify as Sut import qualified Gyehoek.CPS.Stackify as Sut
import Gyehoek.Stack.VM as Stk import Gyehoek.Stack.VM as Stk
import Data.List (List)
import Gyehoek.CPS.Syntax (cps) import Gyehoek.CPS.Syntax (cps)
import Gyehoek.CPS.Syntax qualified as CPS
import Gyehoek.GenSym (runGenSym) import Gyehoek.GenSym (runGenSym)
import Effectful import Effectful
import Gyehoek.Prelude
test_stackify = test_stackify =
@@ -19,9 +20,11 @@ test_stackify =
] ]
evalsTo :: List Obj -> Sut.Exp -> Assertion evalsTo :: List Obj -> Sut.Exp -> Assertion
evalsTo rs e = evalsTo rs e = Stk.eval e' @?= rs
Stk.eval e' @?= rs where
where e' = runPureEff . runGenSym $ Sut.stackifyExp "main" e e' = e & CPS.MkLambda [] "_ktail"
& CPS.MkProgram
& Sut.stackifyProgram & runGenSym & runPureEff
trivialReturn = testGroup "trivial return" trivialReturn = testGroup "trivial return"
[ testCase "return int" do [ testCase "return int" do
+11 -6
View File
@@ -28,15 +28,20 @@ free = testGroup "free"
qq :: TestTree qq :: TestTree
qq = testGroup "parser" qq = testGroup "parser"
[ testCase "lambda" do [ testCase "lambda" do
assertEqual "" (Sut.MkLambda ["x","y"] "ktail" assertEqual ""
(Sut.ExpContinue (Sut.ValLabel "ktail") [Sut.ValVar "x"])) (Sut.MkLambda ["x","y"] "ktail"
(Sut.ExpContinue (Sut.ValVar "ktail") [Sut.ValVar "x"]))
[cps|(λ (x y ktail) (continue ktail x))|] [cps|(λ (x y ktail) (continue ktail x))|]
assertEqual "" (Sut.MkLambda [] "ktail" assertEqual ""
(Sut.ExpContinue (Sut.ValLabel "ktail") [Sut.ValVar "x"])) (Sut.MkLambda [] "ktail"
(Sut.ExpContinue (Sut.ValVar "ktail") [Sut.ValVar "x"]))
[cps|(λ (ktail) (continue ktail x))|] [cps|(λ (ktail) (continue ktail x))|]
, testCase "kappa" do , testCase "kappa" do
assertEqual "" (Sut.MkKappa ["x","y"] assertEqual ""
(Sut.ExpContinue (Sut.ValLabel "k123") [Sut.ValVar "x", Sut.ValVar "y"])) (Sut.MkKappa ["x","y"]
(Sut.ExpContinue
(Sut.ValVar "k123")
[Sut.ValVar "x", Sut.ValVar "y"]))
[cps|(κ (x y) (continue k123 x y))|] [cps|(κ (x y) (continue k123 x y))|]
, testCase "application" do , testCase "application" do
assertEqual "" (Sut.ExpApply (Sut.ValVar "f") assertEqual "" (Sut.ExpApply (Sut.ValVar "f")
+26 -26
View File
@@ -14,58 +14,58 @@ evalsTo rs p = Sut.eval p @?= rs
test_root = testGroup "stack machine" test_root = testGroup "stack machine"
[ testCase "lit int" do [ testCase "lit int" do
evalsTo [ObjImm (ImmInt 3)] [stkP| evalsTo [ObjImm (ImmInt 3)] [stkP|
(define ($main) (define ($start %ktail)
(pop-cont! %ktail)
(tail-call %ktail 3)) (tail-call %ktail 3))
|] |]
, testCase "return constant" do , testCase "return constant" do
evalsTo [ObjImm (ImmInt 123)] [stkP| evalsTo [ObjImm (ImmInt 123)] [stkP|
(define ($main) (define ($start %ktail)
(tail-call $silly)) (tail-call $silly %ktail))
(define ($silly) (define ($silly %ktail)
(pop-cont! %ktail)
(tail-call %ktail 123)) (tail-call %ktail 123))
|] |]
, testCase "identity continuation" do
evalsTo [ObjImm (ImmInt 45)] [stkP|
(define ($start %ktail)
(push! %ktail)
(tail-call $id 45))
(define ($id %x)
(pop! %ktail)
(tail-call %ktail %x))
|]
, testCase "identity function" do , testCase "identity function" do
evalsTo [ObjImm (ImmInt 45)] [stkP| evalsTo [ObjImm (ImmInt 45)] [stkP|
(define ($main) (define ($start %ktail)
(tail-call $id 45)) (tail-call $id 45 %ktail))
(define ($id %x) (define ($id %x %ktail)
(pop-cont! %ktail)
(tail-call %ktail %x)) (tail-call %ktail %x))
|] |]
-- , testCase "square" do
-- evalsTo [ObjImm (ImmInt 16)] [stkP|
-- (define ($main))
-- |]
, testCase "square" do , testCase "square" do
evalsTo [ObjImm (ImmInt 16)] [stkP| evalsTo [ObjImm (ImmInt 16)] [stkP|
(define ($main) (define ($start %ktail)
(tail-call $square 4)) (tail-call $square 4 %ktail))
(define ($square %x) (define ($square %x %ktail)
(prim %x2 (* %x %x)) (prim %x2 (* %x %x))
(pop-cont! %ktail)
(tail-call %ktail %x2)) (tail-call %ktail %x2))
|] |]
, testCase "factorial" do , testCase "factorial" do
let hsfac (n :: Int) = foldr (*) (1) [1..n] let hsfac (n :: Int) = foldr (*) (1) [1..n]
let fac (n :: Int) = [stkP| let fac (n :: Int) = [stkP|
(define ($fac %n) (define ($fac %n %ktail)
(prim %x0 (zero? %n)) (prim %x0 (zero? %n))
(if %x0 (if %x0
(then (pop-cont! %ktail) (then (tail-call %ktail 1))
(tail-call %ktail 1))
(else (push! %n) (else (push! %n)
(push! %ktail)
(prim %x1 (- %n 1)) (prim %x1 (- %n 1))
(push-cont! $fac-k0) (tail-call $fac %x1 $fac-k0))))
(tail-call $fac %x1))))
(define ($fac-k0 %x2) (define ($fac-k0 %x2)
(pop! %ktail)
(pop! %n) (pop! %n)
(prim %x3 (* %x2 %n)) (prim %x3 (* %x2 %n))
(pop-cont! %ktail)
(tail-call %ktail %x3)) (tail-call %ktail %x3))
(define ($main) (define ($start %ktail)
(tail-call $fac #{n})) (tail-call $fac #{n} %ktail))
|] |]
evalsTo [ObjImm (ImmInt 1)] $ fac 0 evalsTo [ObjImm (ImmInt 1)] $ fac 0
evalsTo [ObjImm (ImmInt 1)] $ fac 1 evalsTo [ObjImm (ImmInt 1)] $ fac 1