call/cc
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
ret > ExitSuccess
|
||||||
|
out > 123
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
(call/cc (λ (cc) (cc 123)))
|
||||||
@@ -42,6 +42,23 @@ convert
|
|||||||
convert (Scm.ExpVar x) k = k $ ValVar x
|
convert (Scm.ExpVar x) k = k $ ValVar x
|
||||||
convert (Scm.ExpLit l) k = k $ ValLit l
|
convert (Scm.ExpLit l) k = k $ ValLit l
|
||||||
|
|
||||||
|
-- special case: call/cc is desugared during cps-conversion...
|
||||||
|
convert (Scm.ExpPrim (PrimCallCC withcc)) k = do
|
||||||
|
convert withcc \withcc' -> do
|
||||||
|
callcc <- gensym' @Name "call/cc"
|
||||||
|
cc <- gensym' @Name "cc"
|
||||||
|
r <- gensym' "r"
|
||||||
|
m <- k $ ValVar r
|
||||||
|
ccish <- gensym' @Name "cc-ish"
|
||||||
|
x <- gensym' @Name "x"
|
||||||
|
pure [cps|
|
||||||
|
(letrec ((#{cc} (κ (#{r}) #{m})))
|
||||||
|
(letrec ((#{ccish} (λ (#{x} _) (continue #{cc} #{x}))))
|
||||||
|
(#{withcc'} #{ccish} #{cc})))
|
||||||
|
|]
|
||||||
|
|
||||||
|
-- ...while all other prims are left as-is for later stages to
|
||||||
|
-- handle..
|
||||||
convert (Scm.ExpPrim p) k =
|
convert (Scm.ExpPrim p) k =
|
||||||
telescope (convert @es) p \p' -> do
|
telescope (convert @es) p \p' -> do
|
||||||
r <- gensym' "r"
|
r <- gensym' "r"
|
||||||
|
|||||||
+13
-13
@@ -14,13 +14,10 @@ import Effectful.Writer.Static.Shared
|
|||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Gyehoek.Stack.Syntax (Imm(..))
|
import Gyehoek.Stack.Syntax (Imm(..))
|
||||||
import Data.HashSet (HashSet)
|
|
||||||
import qualified Data.HashSet as HS
|
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import qualified Data.HashMap.Strict as H
|
import qualified Data.HashMap.Strict as H
|
||||||
import Data.HashSet.Lens (hashMap)
|
|
||||||
import Data.List (List)
|
import Data.List (List)
|
||||||
import GHC.Exts (IsList(fromList))
|
import GHC.Exts (IsList(fromList))
|
||||||
|
|
||||||
@@ -31,7 +28,9 @@ 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 -> x `H.member` g.bound && x /= g.returnLabel
|
live g e = free' e & filter \x ->
|
||||||
|
x `H.member` g.bound
|
||||||
|
&& not (x `elem` g.contStack)
|
||||||
|
|
||||||
stackify
|
stackify
|
||||||
:: (GenSym :> es, Stackify :> es)
|
:: (GenSym :> es, Stackify :> es)
|
||||||
@@ -52,7 +51,7 @@ stackify g (ExpLetRec [(f, AbsLambda' xs k m)] e) = do
|
|||||||
lam_body <- gensym' "lambda-body"
|
lam_body <- gensym' "lambda-body"
|
||||||
m' <- stackify (g & #bound .~ H.fromList vs
|
m' <- stackify (g & #bound .~ H.fromList vs
|
||||||
& #bound . at f ?~ Stk.ValLabel lam_body
|
& #bound . at f ?~ Stk.ValLabel lam_body
|
||||||
& #returnLabel .~ k) m
|
& #contStack %~ (k:)) m
|
||||||
tell [Stk.MkBlock lam_body xs . toList $ m']
|
tell [Stk.MkBlock lam_body xs . toList $ m']
|
||||||
stackify (g & #bound . at f ?~ Stk.ValLabel lam_body) e
|
stackify (g & #bound . at f ?~ Stk.ValLabel lam_body) e
|
||||||
|
|
||||||
@@ -63,21 +62,22 @@ stackify g (ExpIf c t f) = do
|
|||||||
|
|
||||||
stackify g (ExpApply f xs ktail) = do
|
stackify g (ExpApply f xs ktail) = do
|
||||||
pure $
|
pure $
|
||||||
[ Stk.PushCont (Stk.ValLabel k) ]
|
[ Stk.PushCont k ]
|
||||||
<> fromList [ Stk.Push (Stk.ValReg l) | l <- ls ]
|
<> fromList [ Stk.Push (Stk.ValReg l) | l <- ls ]
|
||||||
<> [ Stk.Call (stackifyVal g f) (stackifyVal g <$> xs) ]
|
<> [ Stk.Call (stackifyVal g f) (stackifyVal g <$> xs) ]
|
||||||
where
|
where
|
||||||
k = case var g ktail of
|
k = var g ktail
|
||||||
Stk.ValLabel x -> x
|
ls = fold $ (k ^? #ValImm . #ImmLabel)
|
||||||
x -> error [i|expected a label, got #{x} (i guess)|]
|
>>= \klbl -> g ^. #liveness . at klbl
|
||||||
ls = fold $ g ^. #liveness . at k
|
|
||||||
|
|
||||||
-- this probably won't work for call/cc, for cps-converted code it'll
|
-- this probably won't work for call/cc, for cps-converted code it'll
|
||||||
-- be fine i think. notice how, instead of calling `var g k`, we just
|
-- be fine i think. notice how, instead of calling `var g k`, we just
|
||||||
-- assume it's the return continuation on top of the stack.
|
-- assume it's the return continuation on top of the stack.
|
||||||
stackify g (ExpContinue k xs) = do
|
stackify g (ExpContinue k xs) = do
|
||||||
ktail <- gensym' $ k ^. _Wrapped'
|
ktail <- gensym' $ k ^. _Wrapped'
|
||||||
pure [ Stk.PopCont ktail
|
pure $
|
||||||
|
fromList [ Stk.PopCont "_" | _ <- takeWhile (/= k) g.contStack ]
|
||||||
|
<> [ Stk.PopCont ktail
|
||||||
, Stk.Call (Stk.ValReg ktail) (stackifyVal g <$> xs)
|
, Stk.Call (Stk.ValReg ktail) (stackifyVal g <$> xs)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -106,16 +106,16 @@ bindReg x = (x, Stk.ValReg x)
|
|||||||
|
|
||||||
data Env = MkEnv
|
data Env = MkEnv
|
||||||
{ bound :: HashMap Name Stk.Val
|
{ bound :: HashMap Name Stk.Val
|
||||||
, returnLabel :: Name
|
|
||||||
-- | for each locally-bound continuation @k@, @liveness@ has an
|
-- | for each locally-bound continuation @k@, @liveness@ has an
|
||||||
-- 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 "halt" mempty
|
emptyEnv = MkEnv mempty mempty ["halt"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import GHC.Generics (Generic)
|
|||||||
import Prelude hiding ((.), id)
|
import Prelude hiding ((.), id)
|
||||||
import Control.Category
|
import Control.Category
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
import Gyehoek.Sexp qualified
|
import Gyehoek.Sexp qualified as GS
|
||||||
import Gyehoek.GenSym (Gen)
|
import Gyehoek.GenSym (Gen)
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Data.Generics.Labels ()
|
import Data.Generics.Labels ()
|
||||||
@@ -85,7 +85,8 @@ data Prim e
|
|||||||
| PrimZeroP e
|
| PrimZeroP e
|
||||||
| PrimNewline
|
| PrimNewline
|
||||||
| PrimMakeClosure { code :: e, env :: List e }
|
| PrimMakeClosure { code :: e, env :: List e }
|
||||||
| PriEnvRef e Int
|
| PrimEnvRef e Int
|
||||||
|
| PrimCallCC e
|
||||||
deriving (Show, Generic, Functor, Foldable, Traversable, Data, Eq)
|
deriving (Show, Generic, Functor, Foldable, Traversable, Data, Eq)
|
||||||
|
|
||||||
instance Each (Prim e) (Prim e') e e'
|
instance Each (Prim e) (Prim e') e e'
|
||||||
@@ -156,29 +157,29 @@ instance SexpIso Name where
|
|||||||
|
|
||||||
primSexpIso :: (Text -> Text) -> Sexp.SexpGrammar a -> Sexp.SexpGrammar (Prim a)
|
primSexpIso :: (Text -> Text) -> Sexp.SexpGrammar a -> Sexp.SexpGrammar (Prim a)
|
||||||
primSexpIso namefn a = match
|
primSexpIso namefn a = match
|
||||||
$ With (. binop "+")
|
$ With (. ht2 "+")
|
||||||
$ With (. binop "-")
|
$ With (. ht2 "-")
|
||||||
$ With (. binop "*")
|
$ With (. ht2 "*")
|
||||||
$ With (. binop "/")
|
$ With (. ht2 "/")
|
||||||
$ With (. binop "cons")
|
$ With (. ht2 "cons")
|
||||||
$ With (. unop "car")
|
$ With (. ht1 "car")
|
||||||
$ With (. unop "cdr")
|
$ With (. ht1 "cdr")
|
||||||
$ With (. unop "immediate?")
|
$ With (. ht1 "immediate?")
|
||||||
$ With (. unop "cons?")
|
$ With (. ht1 "cons?")
|
||||||
$ With (. unop "integer?")
|
$ With (. ht1 "integer?")
|
||||||
$ With (. unop "write")
|
$ With (. ht1 "write")
|
||||||
$ With (. unop "zero?")
|
$ With (. ht1 "zero?")
|
||||||
$ With (. nullop "newline")
|
$ With (. nullop "newline")
|
||||||
$ With (. mkclosure)
|
$ With (. ht1' "make-closure")
|
||||||
$ With (. envref)
|
$ With (. GS.headTagged2 (namefn "env-ref") a Sexp.int)
|
||||||
|
$ With (. ht1 "call/cc")
|
||||||
$ End
|
$ End
|
||||||
where
|
where
|
||||||
idn s = el (sym (namefn s))
|
idn s = el (sym (namefn s))
|
||||||
nullop s = list $ idn s
|
nullop s = list $ idn s
|
||||||
unop s = list $ idn s >>> el a
|
ht1 s = GS.headTagged1 (namefn s) a
|
||||||
binop s = list $ idn s >>> el a >>> el a
|
ht2 s = GS.headTagged2 (namefn s) a a
|
||||||
mkclosure = list $ idn "make-closure" >>> el a >>> rest a
|
ht1' s = GS.headTagged1' (namefn s) a a
|
||||||
envref = list $ idn "env-ref" >>> el a >>> el Sexp.int
|
|
||||||
|
|
||||||
instance SexpIso a => SexpIso (Prim a) where
|
instance SexpIso a => SexpIso (Prim a) where
|
||||||
-- sexpIso = primSexpIso ("prim:"<>) sexpIso
|
-- sexpIso = primSexpIso ("prim:"<>) sexpIso
|
||||||
@@ -188,14 +189,14 @@ instance SexpIso Lit where
|
|||||||
sexpIso = match
|
sexpIso = match
|
||||||
$ With (. sexpIso)
|
$ With (. sexpIso)
|
||||||
$ With (. sym "nil")
|
$ With (. sym "nil")
|
||||||
$ With (. Gyehoek.Sexp.schemeBool)
|
$ With (. GS.schemeBool)
|
||||||
$ With (. sexpIso)
|
$ With (. sexpIso)
|
||||||
$ With (. Gyehoek.Sexp.prefixSugar "quote" Sexp.Quote sexpIso)
|
$ With (. GS.prefixSugar "quote" Sexp.Quote sexpIso)
|
||||||
$ End
|
$ End
|
||||||
|
|
||||||
instance SexpIso Sexp where
|
instance SexpIso Sexp where
|
||||||
sexpIso = match
|
sexpIso = match
|
||||||
$ With (\conss -> conss . Gyehoek.Sexp.todo)
|
$ With (\conss -> conss . GS.todo)
|
||||||
$ With (\s -> s . symbol)
|
$ With (\s -> s . symbol)
|
||||||
$ With (\lit -> lit . sexpIso)
|
$ With (\lit -> lit . sexpIso)
|
||||||
$ End
|
$ End
|
||||||
@@ -212,8 +213,8 @@ instance SexpIso Def where
|
|||||||
|
|
||||||
instance SexpIso Exp where
|
instance SexpIso Exp where
|
||||||
sexpIso = match
|
sexpIso = match
|
||||||
$ With (. Gyehoek.Sexp.let_ "let" sexpIso sexpIso sexpIso)
|
$ With (. GS.let_ "let" sexpIso sexpIso sexpIso)
|
||||||
$ With (. Gyehoek.Sexp.let_ "letrec" sexpIso sexpIso sexpIso)
|
$ With (. GS.let_ "letrec" sexpIso sexpIso sexpIso)
|
||||||
$ With (. sexpIso)
|
$ With (. sexpIso)
|
||||||
$ With (\bgn -> bgn . list (el (sym "begin") >>> rest sexpIso))
|
$ With (\bgn -> bgn . list (el (sym "begin") >>> rest sexpIso))
|
||||||
$ With (. if_)
|
$ With (. if_)
|
||||||
@@ -225,7 +226,7 @@ instance SexpIso Exp where
|
|||||||
where
|
where
|
||||||
if_ = list $ el (sym "if") >>> el sexpIso >>> el sexpIso >>> el sexpIso
|
if_ = list $ el (sym "if") >>> el sexpIso >>> el sexpIso >>> el sexpIso
|
||||||
lam = list
|
lam = list
|
||||||
( el Gyehoek.Sexp.lambdaKeyword
|
( el GS.lambdaKeyword
|
||||||
>>> el (sexpIso @(List Name))
|
>>> el (sexpIso @(List Name))
|
||||||
>>> el sexpIso )
|
>>> el sexpIso )
|
||||||
|
|
||||||
@@ -242,7 +243,7 @@ instance SexpIso CommandOrDef where
|
|||||||
-- utilities
|
-- utilities
|
||||||
|
|
||||||
scm :: QuasiQuoter
|
scm :: QuasiQuoter
|
||||||
scm = Gyehoek.Sexp.makeSx [|| Gyehoek.Sexp.fromSexp @Exp ||]
|
scm = GS.makeSx [|| GS.fromSexp @Exp ||]
|
||||||
|
|
||||||
free :: Exp -> HashSet Name
|
free :: Exp -> HashSet Name
|
||||||
free = cata \case
|
free = cata \case
|
||||||
@@ -278,7 +279,7 @@ hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
|
|||||||
readProgram :: IOE :> es => FilePath -> Eff es Program
|
readProgram :: IOE :> es => FilePath -> Eff es Program
|
||||||
readProgram fp = runFileSystem $
|
readProgram fp = runFileSystem $
|
||||||
FS.withFile fp FS.ReadMode $ \h ->
|
FS.withFile fp FS.ReadMode $ \h ->
|
||||||
Gyehoek.Sexp.parseSexps @CommandOrDef (fileName fp) <$> hGetContents h
|
GS.parseSexps @CommandOrDef (fileName fp) <$> hGetContents h
|
||||||
>>= either error (pure . MkProgram)
|
>>= either error (pure . MkProgram)
|
||||||
|
|
||||||
readExp :: IOE :> es => FilePath -> Eff es Exp
|
readExp :: IOE :> es => FilePath -> Eff es Exp
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ prim = testGroup "prim"
|
|||||||
evalsTo [ObjImm (ImmInt 9)]
|
evalsTo [ObjImm (ImmInt 9)]
|
||||||
[cps|(prim (+ 4 5)
|
[cps|(prim (+ 4 5)
|
||||||
(κ (x) (continue halt x)))|]
|
(κ (x) (continue halt x)))|]
|
||||||
|
-- , testGroup "call/cc"
|
||||||
|
-- [ testCase "trivial" do
|
||||||
|
-- evalsTo [ObjImm (ImmInt 123)]
|
||||||
|
-- [cps|(letrec ((f (λ (cc ktail) (continue cc 123))))
|
||||||
|
-- (prim (call/cc f)))|]
|
||||||
|
-- ]
|
||||||
]
|
]
|
||||||
|
|
||||||
condition = testCase "if" do
|
condition = testCase "if" do
|
||||||
|
|||||||
@@ -19,13 +19,7 @@ import Test.Tasty.ExpectedFailure (expectFail)
|
|||||||
|
|
||||||
brokenWasmTests :: List String
|
brokenWasmTests :: List String
|
||||||
brokenWasmTests =
|
brokenWasmTests =
|
||||||
[ "adder"
|
[
|
||||||
, "apply-twice"
|
|
||||||
, "square"
|
|
||||||
, "fn-of-fn"
|
|
||||||
, "let-fn"
|
|
||||||
, "apply2"
|
|
||||||
, "factorial"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
brokenStackifyTests :: List String
|
brokenStackifyTests :: List String
|
||||||
@@ -42,8 +36,8 @@ root = do
|
|||||||
let tests = all_cases
|
let tests = all_cases
|
||||||
& fmap ("golden"</>)
|
& fmap ("golden"</>)
|
||||||
testGroup "golden" <$> sequenceA
|
testGroup "golden" <$> sequenceA
|
||||||
[ wasmTests tests
|
[ {-wasmTests tests
|
||||||
, stackifyTests tests
|
, -}stackifyTests tests
|
||||||
]
|
]
|
||||||
|
|
||||||
maybeBroken name broken = applyWhen (name `elem` broken) expectFail
|
maybeBroken name broken = applyWhen (name `elem` broken) expectFail
|
||||||
|
|||||||
Reference in New Issue
Block a user