6 Commits
Author SHA1 Message Date
msyds 6b723cf91e
build / build (push) Failing after 5m41s
2026-08-17 21:06:19 -06:00
msyds ea370270f6 2026-08-17 18:52:43 -06:00
msyds fa9621542a 2026-08-17 18:38:05 -06:00
msyds 9ed9100373 2026-08-17 17:28:35 -06:00
msyds f4b891f241 2026-08-17 17:19:34 -06:00
msyds 2914774ef5 2026-08-17 16:17:53 -06:00
16 changed files with 662 additions and 20 deletions
+218
View File
@@ -0,0 +1,218 @@
#+title: ABI
largely based on the Guile Hoot's [[https://codeberg.org/spritely/hoot/src/branch/main/design/ABI.md][ABI]].
* calling convention
** non-tail calls
- set the global variable ~$current-closure~ to the callee's closure.
- load arguments into globals ~$arg0~, ~$arg1~, ~$arg2~, …
- push return continuation onto ~$cont-stack~
* scratchpad
#+begin_src scheme
;; Scheme source
(define (silly f g h x)
(f (h x) (g x)))
;; continuation-passing style
(define (silly f g h x ktail)
(h x (κ (x0)
(g x (κ (x1)
(f x0 x1 ktail))))))
;; with explicit stacks
(define (silly)
(define f (pop!))
(define g (pop!))
(define h (pop!))
(define x (pop!))
(define ktail (pop-cont!))
(push-cont! (κ (x0)
(define x* (pop!))
(define g* (pop!))
(push-cont! (κ (x1)
(define f* (pop!))
(define x0* (pop!))
(push-cont! ktail)
(push! x0*)
(push! x1)
(call! f)))
(push! x*)
(call! g)))
(push! x)
(call! h))
#+end_src
** fac
*** Scheme source
#+begin_src scheme
(define fac
(λ (n)
(if (zero? n)
1
(* n (fac (- n 1))))))
(fac 3)
#+end_src
*** CPS
#+begin_src scheme
(define fac
(λ (n ktail)
(zero? n (κ (x0)
(if x0
1
(- n 1
(κ (x1)
(fac x1
(κ (x2)
(* n x2 ktail))))))))))
(fac 3 halt)
#+end_src
*** tailified
#+begin_src scheme
(define (fac-k1)
(define n (pop!))
(define x2 (pop!))
(define x3 (* n x2))
(define ktail (pop-cont!))
(push! x3)
(call! ktail))
(define (fac-k0)
(define x0 (pop!))
(define n (pop!))
(if x0
(begin (define ktail (pop-cont!))
(push! 1)
(call! ktail))
(begin (define x1 (- n 1))
(push! x1)
(push-cont! fac-k1)
(call! fac))))
(define (fac)
(define n (pop!))
(push! n)
(push-cont! fac-k0)
(push! n)
(call! zero?))
(push! 3)
(push-cont! halt)
(call! fac)
#+end_src
evaluation of ~(fac 0)~:
#+begin_src scheme
(push! 0) ; [] []
(push-cont! halt) ; [0] []
(call! fac) ; [0] [halt]
(define n (pop!)) ; [0] [halt]
(push! n) ; [] [halt]
(push-cont! fac-k0) ; [0] [halt]
(push! n) ; [0] [halt fac-k0]
(call! zero?) ; [0 0] [halt fac-k0]
#<internals of zero?> ; [0 0] [halt fac-k0]
(define x0 (pop!)) ; [0 #t] [halt]
(define n (pop!)) ; [0] [halt]
(define ktail (pop-cont!)) ; [] [halt]
(push! 1) ; [] []
(call! ktail) ; [1] []
#+end_src
evaluation of ~(fac 3)~
#+begin_src scheme
(push! 3) ; [] []
(push-cont! halt) ; [3] []
(call! fac) ; [3] [halt]
(define n (pop!)) ; [3] [halt]
(push! n) ; [] [halt]
(push-cont! fac-k0) ; [3] [halt]
(push! n) ; [3] [halt fac-k0]
(call! zero?) ; [3 3] [halt fac-k0]
#<internals of zero?> ; [3 3] [halt fac-k0]
(define x0 (pop!)) ; [3 #f] [halt]
(define n (pop!)) ; [3] [halt]
(define x1 (- n 1)) ; [] [halt]
(push! n) ; [] [halt]
(push! x1) ; [3] [halt]
(push-cont! fac-k1) ; [3 2] [halt]
(call! fac) ; [3 2] [halt fac-k1]
(define n (pop!)) ; [3 2] [halt fac-k1]
(push! n) ; [3 ] [halt fac-k1]
(push-cont! fac-k0) ; [3 2] [halt fac-k1]
(push! n) ; [3 2] [halt fac-k1 fac-k0]
(call! zero?) ; [3 2 2] [halt fac-k1 fac-k0]
#<internals of zero?> ; [3 2 2] [halt fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 #f] [halt fac-k1]
(define n (pop!)) ; [3 2] [halt fac-k1]
(define x1 (- n 1)) ; [3] [halt fac-k1]
(push! n) ; [3] [halt fac-k1]
(push! x1) ; [3 2] [halt fac-k1]
(push-cont! fac-k1) ; [3 2 1] [halt fac-k1]
(call! fac) ; [3 2 1] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(push! n) ; [3 2] [halt fac-k1 fac-k1]
(push-cont! fac-k0) ; [3 2 1] [halt fac-k1 fac-k1]
(push! n) ; [3 2 1] [halt fac-k1 fac-k1 fac-k0]
(call! zero?) ; [3 2 1 1] [halt fac-k1 fac-k1 fac-k0]
#<internals of zero?> ; [3 2 1 1] [halt fac-k1 fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 1 #f] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(define x1 (- n 1)) ; [3 2] [halt fac-k1 fac-k1]
(push! n) ; [3 2] [halt fac-k1]
(push! x1) ; [3 2 1] [halt fac-k1 fac-k1]
(push-cont! fac-k1) ; [3 2 1 0] [halt fac-k1 fac-k1]
(call! fac) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(push! n) ; [3 2 1] [halt fac-k1 fac-k1 fac-k1]
(push-cont! fac-k0) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(push! n) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
(call! zero?) ; [3 2 1 0 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
#<internals of zero?> ; [3 2 1 0 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 1 0 #t] [halt fac-k1 fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(define ktail (pop-cont!)) ; [3 2 1] [halt fac-k1 fac-k1 fac-k1]
(push! 1) ; [3 2 1 1] [halt fac-k1 fac-k1]
(call! ktail) ; [3 2 1 1] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 1] [halt fac-k1 fac-k1]
(define x2 (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(define x3 (* n x2)) ; [3 2] [halt fac-k1 fac-k1]
(define ktail (pop-cont!)) ; [3 2] [halt fac-k1 fac-k1]
(push! x3) ; [3 2] [halt fac-k1]
(call! ktail) ; [3 2 1] [halt fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1]
(define x2 (pop!)) ; [3 2] [halt fac-k1]
(define x3 (* n x2)) ; [3] [halt fac-k1]
(define ktail (pop-cont!)) ; [3] [halt fac-k1]
(push! x3) ; [3] [halt]
(call! ktail) ; [3 2] [halt]
(define n (pop!)) ; [3 2] [halt]
(define x2 (pop!)) ; [3] [halt]
(define x3 (* n x2)) ; [] [halt]
(define ktail (pop-cont!)) ; [] [halt]
(push! x3) ; [] []
(call! ktail) ; [6] []
;; => (halt 6)
#+end_src
+83
View File
@@ -0,0 +1,83 @@
#+title: closure-conversion
the closure-conversion phase makes closed-over variables explicit by addition of the primitive ~make-closure~, taking a code pointer (in the CPS language, bare lambda) and the environment.
* scratchpad
#+begin_src scheme
(letrec ((make-adder
(lambda (n)
(lambda (x)
(+ n x)))))
((make-adder 3) 2))
#+end_src
#+begin_src scheme
(define add-code
(lambda (n env)
(+ n (env-ref env 'x))))
(define make-adder-code
(lambda (n)
(make-closure add-code ('x n))))
(define make-adder (make-closure make-adder-code))
(apply-closure (apply-closure make-addder 3) 2)
#+end_src
#+begin_src wat
(module
(type $heap-object (sub (struct (field $hash (mut i32)))))
(type $closure (sub $heap-object
(struct (field $hash (mut i32))
(field $code (ref $cont-type)))))
(type $closure1 (sub $closure
(struct (field $hash (mut i32))
(field $code (ref $cont-type))
(field $env0 (ref eq)))))
(global $arg0 (mut (ref null eq)) (ref.null eq))
(global $arg1 (mut (ref null eq)) (ref.null eq))
(global $arg2 (mut (ref null eq)) (ref.null eq))
(global $arg3 (mut (ref null eq)) (ref.null eq))
(global $arg4 (mut (ref null eq)) (ref.null eq))
(global $arg5 (mut (ref null eq)) (ref.null eq))
;; ⋮
;; (global $argn (mut (ref null eq)) (ref.null eq))
(global $current-closure (mut (ref null $closure)) (ref.null $closure))
(func $add-code (param $nargs i32)
(local $n (ref eq))
(local $x (ref eq))
(local.set $n (global.get $arg0))
(local.set $x (struct.get $closure1
(global.get $current-closure)
$env0))
(return (i32.add $n $x)))
(func $make-adder-code (param $nargs i32)
(local $n (ref eq))
(local.set $n (global.get $arg0))
(return (struct.new $closure1
0
$add-code)))
(func $main
(local.set $make-adder
(struct.new $closure
0
$make-adder-code))
(global.set $current-closure $make-adder)
(global.set $arg0 (i32.const 3))
(local.set $f (call (struct.get $closure
$make-adder
$code)
1))
(global.set $current-closure $f)
(global.set $arg0 (i32.const 2))
(return (call (struct.get $closure
$f
$code)
1))))
#+end_src
+53
View File
@@ -0,0 +1,53 @@
#+title: assorted notes on compilation
* letrec
consider:
#+begin_src scheme
(letrec ((even? (lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd? (lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 12))
#+end_src
#+RESULTS:
: #t
since ~letrec~ is a primitive construct in the CPS language, the translation of mutually recursive functions is straightforward:
#+begin_src scheme
(define (-& x y k) (k (- x y)))
(define (zero?& x k) (k (zero? x)))
(define (halt x) x)
(letrec ((even? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#t
(-& n 1
(lambda (x2)
(odd? x2 ktail))))))))
(odd? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#f
(-& n 1
(lambda (x2)
(even? x2 ktail)))))))))
(even? 12 halt))
#+end_src
#+RESULTS:
: #t
however, Scheme permits ~letrec~-expressions with non-lambda right-hand sides, while the CPS language permits only kappa and lambda forms. thus, the handling of these forms is less trivial.
for now we'll just reject any ~letrec~ forms with non-lambda right-hand sides, lol. they aren't very important.
+5
View File
@@ -0,0 +1,5 @@
((λ (f g x)
(f (g x)))
(λ (x) (+ x 4))
(λ (x) (* x 2))
3)
+3 -1
View File
@@ -1 +1,3 @@
(((λ (f) f) (λ (x) (* x 4))) 32) (((λ (f) f)
(λ (x) (* x 4)))
32)
+2
View File
@@ -0,0 +1,2 @@
(let ((square (λ (x) (* x x))))
(square 4))
+7
View File
@@ -61,6 +61,8 @@ library
Gyehoek.Options Gyehoek.Options
Gyehoek.Scheme.Syntax Gyehoek.Scheme.Syntax
Gyehoek.Sexp Gyehoek.Sexp
Gyehoek.Stack.Syntax
Gyehoek.Stack.VM
Gyehoek.Wasm Gyehoek.Wasm
build-depends: build-depends:
@@ -100,16 +102,21 @@ test-suite test
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
hs-source-dirs: test hs-source-dirs: test
main-is: Main.hs main-is: Main.hs
-- cabal-fmt: expand test
other-modules: other-modules:
Gyehoek.Test.CPS.Syntax Gyehoek.Test.CPS.Syntax
Gyehoek.Test.Golden Gyehoek.Test.Golden
Gyehoek.Test.Sexp Gyehoek.Test.Sexp
Gyehoek.Test.Stack.VM
build-depends: build-depends:
, base , base
, directory , directory
, filepath , filepath
, generic-lens
, gyehoek , gyehoek
, lens
, process-extras , process-extras
, sexp-grammar , sexp-grammar
, tasty , tasty
+4 -1
View File
@@ -2,6 +2,7 @@
{- HLINT ignore "Use camelCase" -} {- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Convert module Gyehoek.CPS.Convert
( convertProgram ( convertProgram
, convertExp
) where ) where
import Gyehoek.CPS.Syntax import Gyehoek.CPS.Syntax
@@ -79,7 +80,6 @@ convert (Scm.ExpLet bs e) k =
(continue #{kbody} ##{rhss'})) (continue #{kbody} ##{rhss'}))
|] |]
convert _ k = _ convert _ k = _
convertProgram :: forall es. (GenSym :> es) => Scm.Program -> Eff es Program convertProgram :: forall es. (GenSym :> es) => Scm.Program -> Eff es Program
@@ -88,3 +88,6 @@ convertProgram p =
pure . Halt1 $ case NE.nonEmpty exps of pure . Halt1 $ case NE.nonEmpty exps of
Nothing -> ValLit Void Nothing -> ValLit Void
Just es -> NE.last es Just es -> NE.last es
convertExp :: forall es. (GenSym :> es) => Scm.Exp -> Eff es Exp
convertExp e = convert e (pure . Halt1)
+35 -14
View File
@@ -309,32 +309,53 @@ instance Vars Exp where
data Scope data Scope
= Bind (List Name) Scope = Bind (List Name) (List Scope)
| Use (List Name) Scope | Use (List Name) (List Scope)
| Leaf
deriving (Show, Eq) deriving (Show, Eq)
makeBaseFunctor ''Scope makeBaseFunctor ''Scope
class Subst a where
substWith :: (Name -> Val) -> a -> a
instance Subst Exp where
substWith sub = cata \e ->
_
class Scoped a where class Scoped a where
scope :: a -> Scope scope :: a -> Scope
instance Scoped Kappa where instance Scoped Kappa where
scope (MkKappa bs e) = scope (MkKappa bs e) =
Bind bs (scope e) Bind bs [scope e]
instance Scoped Lambda where
scope (MkLambda bs k e) = Bind (bs ++ [k]) [scope e]
instance Scoped Abs where
scope = \case
AbsKappa k -> scope k
AbsLambda l -> scope l
instance Scoped Val where instance Scoped Val where
scope = \case scope = \case
ValVar x -> Use [x] Leaf ValVar x -> Use [x] []
_ -> Leaf _ -> Use [] []
instance Scoped Exp where instance Scoped Exp where
scope = \case scope = \case
ExpApply f xs k = _ ExpApply f xs k ->
Use (((f:xs) ^.. each . _ValVar) ++ [k]) []
ExpLetRec bs e ->
Bind (bs ^.. each . _1) $
(bs ^.. each . _2 . to scope)
++ [scope e]
ExpPrim p k ->
Use (p ^.. each . _ValVar) [scope k]
ExpContinue k xs ->
Use (k : (xs ^.. each . _ValVar)) []
ExpIf c t f ->
Use (c ^.. _ValVar) [ scope t, scope f ]
class Subst a where
substWith :: (Name -> Maybe Val) -> a -> a
instance Subst Exp where
substWith f = go HS.empty where
go bound e = case scope e of
Use xs ss -> _
+30 -1
View File
@@ -23,6 +23,8 @@ module Gyehoek.Scheme.Syntax
, subst , subst
, getName , getName
, scm , scm
, readExp
, readProgram
) )
where where
@@ -33,6 +35,7 @@ import Language.SexpGrammar
import Language.SexpGrammar qualified as Sexp import Language.SexpGrammar qualified as Sexp
import Language.Sexp.Located qualified as S import Language.Sexp.Located qualified as S
import Language.SexpGrammar.Generic import Language.SexpGrammar.Generic
import Effectful
import GHC.Generics import GHC.Generics
import Prelude hiding ((.), id) import Prelude hiding ((.), id)
import Control.Category import Control.Category
@@ -49,6 +52,10 @@ import Data.HashSet (HashSet)
import qualified Data.HashSet as HS import qualified Data.HashSet as HS
import Data.Foldable (fold) import Data.Foldable (fold)
import Language.Haskell.TH.Quote (QuasiQuoter) import Language.Haskell.TH.Quote (QuasiQuoter)
import Effectful.FileSystem (runFileSystem)
import qualified Effectful.FileSystem.IO as FS
import qualified Data.Text.Encoding as T
import qualified Effectful.FileSystem.IO.ByteString as FB
newtype Name = MkName { inner :: Text } newtype Name = MkName { inner :: Text }
@@ -72,7 +79,8 @@ data Prim e
| PrimWrite e | PrimWrite e
| PrimZeroP e | PrimZeroP e
| PrimNewline | PrimNewline
| PrimMakeClosure { code :: e, upvals :: List e } | PrimMakeClosure { code :: e, env :: List e }
| PriEnvRef e Int
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'
@@ -157,6 +165,7 @@ primSexpIso namefn a = match
$ With (. unop "zero?") $ With (. unop "zero?")
$ With (. nullop "newline") $ With (. nullop "newline")
$ With (. mkclosure) $ With (. mkclosure)
$ With (. envref)
$ End $ End
where where
idn s = el (sym (namefn s)) idn s = el (sym (namefn s))
@@ -164,6 +173,7 @@ primSexpIso namefn a = match
unop s = list $ idn s >>> el a unop s = list $ idn s >>> el a
binop s = list $ idn s >>> el a >>> el a binop s = list $ idn s >>> el a >>> el a
mkclosure = list $ idn "make-closure" >>> el a >>> rest a mkclosure = list $ idn "make-closure" >>> el a >>> rest 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
@@ -259,3 +269,22 @@ subst f = \e -> cata go e mempty where
go (ExpLetF _ _) _ = error "todo lol" go (ExpLetF _ _) _ = error "todo lol"
go (ExpLambdaF bs e) bound = e $ insertFrom bs bound go (ExpLambdaF bs e) bound = e $ insertFrom bs bound
go e bound = embed $ fmap ($ bound) e go e bound = embed $ fmap ($ bound) e
fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
hGetContents :: FS.FileSystem :> es => FS.Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readProgram :: IOE :> es => FilePath -> Eff es Program
readProgram fp = runFileSystem $
FS.withFile fp FS.ReadMode $ \h ->
Gyehoek.Sexp.parseSexps @CommandOrDef (fileName fp) <$> hGetContents h
>>= either error (pure . MkProgram)
readExp :: IOE :> es => FilePath -> Eff es Program
readExp fp = readProgram fp <&>
(^?! (#commandsAndDefs . _head . _Comm))
+28
View File
@@ -38,10 +38,12 @@ module Gyehoek.Sexp
, makeSx' , makeSx'
, toSexp , toSexp
, fromSexp , fromSexp
, fromSexp'
, stripLocation , stripLocation
, format , format
, equivalent , equivalent
, encodeOrShow , encodeOrShow
, readSxs
) )
where where
@@ -87,6 +89,10 @@ import qualified Data.Vector as V
import qualified Data.Vector.Strict import qualified Data.Vector.Strict
import Data.Function (on) import Data.Function (on)
import Data.String (IsString (fromString)) import Data.String (IsString (fromString))
import Effectful
import qualified Effectful.FileSystem.IO as FS
import qualified Effectful.FileSystem.IO.ByteString as FB
import qualified Data.Text.Encoding as T
sexp :: SexpIso a => Iso' a Text sexp :: SexpIso a => Iso' a Text
@@ -120,6 +126,10 @@ parseSexps :: SexpIso a => FilePath -> Text -> Either String (List a)
parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8 parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp sexpIso) where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp sexpIso)
parseSexpsWith :: SexpGrammar a -> FilePath -> Text -> Either String (List a)
parseSexpsWith g f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp g)
parseSexp :: SexpIso a => FilePath -> Text -> Either String a parseSexp :: SexpIso a => FilePath -> Text -> Either String a
parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8 parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (Sexp.fromSexp sexpIso) where marshal = join . traverseOf _Right (Sexp.fromSexp sexpIso)
@@ -140,6 +150,24 @@ parseSexpWithPos g pos =
marshal . SL.parseSexpWithPos pos . view lazy . encodeUtf8 marshal . SL.parseSexpWithPos pos . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (Sexp.fromSexp g) where marshal = join . traverseOf _Right (Sexp.fromSexp g)
fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
hGetContents :: FS.FileSystem :> es => FS.Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readSxs
:: IOE :> es
=> SexpGrammar a
-> FilePath -> Eff es (List a)
readSxs g fp = FS.runFileSystem $
FS.withFile fp FS.ReadMode $ \h ->
parseSexpsWith g (fileName fp) <$> hGetContents h
>>= either error pure
nonEmptyGrammar :: Grammar p (NonEmpty x :- t) (List x :- x :- t) nonEmptyGrammar :: Grammar p (NonEmpty x :- t) (List x :- x :- t)
nonEmptyGrammar = IGB.Iso nonEmptyGrammar = IGB.Iso
(\((x:|xs) :- t) -> reverse xs :- x :- t) (\((x:|xs) :- t) -> reverse xs :- x :- t)
+57
View File
@@ -0,0 +1,57 @@
{-# LANGUAGE TemplateHaskellQuotes #-}
module Gyehoek.Stack.Syntax
( Program(..)
, Block(..)
, Instr(..)
, Val(..)
, Lit(..)
, Name
) where
import Control.Lens
import Data.List (List)
import GHC.Generics (Generic)
import Data.HashMap.Strict (HashMap)
import Language.SexpGrammar (SexpIso, (>>>), (:-))
import Language.SexpGrammar qualified as S
import Language.SexpGrammar.Generic
import Data.Coerce (coerce)
import Data.Text (Text)
import qualified Gyehoek.Sexp
import Language.Haskell.TH.Quote (QuasiQuoter)
import Data.Data (Data)
import qualified Data.HashMap.Strict as H
import Effectful
import Gyehoek.Scheme.Syntax (Name(..), Lit(..), Prim(..))
newtype Program = MkProgram
{ blocks :: List Block
}
deriving stock (Show, Generic, Data)
data Block = MkBlock
{ label :: Name
, params :: List Name
, code :: List Instr
}
deriving stock (Show, Generic, Data)
instance Each Block Block Instr Instr where
each = #code . each
data Instr
= Pop Name
| Push Val
| PopCont Name
| PushCont Name
| Prim Name (Prim Val)
| CallLabel Name (List Val)
| CallReg Name (List Val)
deriving stock (Show, Generic, Data)
data Val
= ValLabel Name
| ValReg Name
| ValLit Lit
deriving stock (Show, Generic, Data, Eq)
+84
View File
@@ -0,0 +1,84 @@
module Gyehoek.Stack.VM
( VM(..)
, Env(..)
, eval
) where
import Gyehoek.Stack.Syntax
import Data.List (List)
import GHC.Generics (Generic)
import Control.Lens
import Data.HashMap.Strict (HashMap)
import Data.Text (Text)
import qualified Data.HashMap.Strict as H
import Data.String.Interpolate (i)
data VM = MkVM
{ stack :: List Val
, kstack :: List Name
, code :: List Instr
, registers :: HashMap Name Val
, stdout :: Text
}
deriving (Show, Generic)
data Env = MkEnv
{ blocks :: HashMap Name Block
}
deriving (Show, Generic)
step :: Env -> VM -> Either (List Val) VM
step e vm = case vm ^. #code of
CallLabel "halt" xs :_ -> Left xs
i:is -> Right $ stepI e (vm & #code .~ is) i
_ -> error "halt never called"
stepI :: Env -> VM -> Instr -> VM
stepI e vm (Push v) = vm & #stack %~ (v:)
stepI e vm (Pop r) = case vm ^. #stack of
[] -> error "empty stack"
(x:xs) -> vm & #registers . at r ?~ x
& #stack .~ xs
stepI e vm (PopCont r) = case vm ^. #kstack of
[] -> error "empty stack"
(x:xs) -> vm & #registers . at r ?~ ValLabel x
& #kstack .~ xs
stepI e vm (CallReg r xs) = stepI e vm (CallLabel l xs)
where l = vm ^?! #registers . at r . _Just . #ValLabel
stepI e vm (CallLabel l xs) =
vm & #code .~ b.code
& #registers .~ H.fromList (b.params `zip` xs)
where
b = case e ^. #blocks . at l of
Just x -> x
Nothing -> error [i|undefined label: #{l}|]
stepI e vm _ = _
initialVM :: VM
initialVM = MkVM
{ stack = []
, kstack = ["halt"]
, code = [CallLabel "main" []]
, registers = mempty
, stdout = ""
}
initialEnv :: Program -> Env
initialEnv (MkProgram bs) = MkEnv
{ blocks = bs & foldMap \b -> H.singleton b.label b
}
loop :: (a -> Either b a) -> a -> b
loop f a = case f a of
Right a' -> loop f a'
Left b -> b
eval :: Program -> List Val
eval p = loop (step $ initialEnv p) initialVM
+21 -3
View File
@@ -1,3 +1,21 @@
(letrec ((x 3) (define (-& x y k) (k (- x y)))
(y 4)) (define (zero?& x k) (k (zero? x)))
(values x y)) (define (halt x) x)
(letrec ((even? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#t
(-& n 1
(lambda (x2)
(odd? x2 ktail))))))))
(odd? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#f
(-& n 1
(lambda (x2)
(even? x2 ktail)))))))))
(even? 12 halt))
+30
View File
@@ -0,0 +1,30 @@
module Gyehoek.Test.Stack.VM (root) where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Gyehoek.Stack.Syntax
import Gyehoek.Stack.VM qualified as Sut
import Data.List (List)
import Control.Lens
import Data.Generics.Labels
root :: IO TestTree
root = pure . testGroup "stack machine" $
[ add
]
evalsTo :: List Val -> List Block -> Assertion
evalsTo rs bs = Sut.eval (MkProgram bs) @?= rs
add = testCase "lit int" do
evalsTo [ValLit (LitInt 3)]
[ MkBlock "main" []
[ PopCont "ktail"
, CallReg "ktail" [ValLit (LitInt 3)]
]]
+2
View File
@@ -5,6 +5,7 @@ import Test.Tasty.Silver.Interactive (defaultMain)
import qualified Gyehoek.Test.Golden import qualified Gyehoek.Test.Golden
import qualified Gyehoek.Test.Sexp import qualified Gyehoek.Test.Sexp
import qualified Gyehoek.Test.CPS.Syntax import qualified Gyehoek.Test.CPS.Syntax
import qualified Gyehoek.Test.Stack.VM
main :: IO () main :: IO ()
@@ -15,5 +16,6 @@ root = testGroup "test" <$> sequenceA
[ Gyehoek.Test.Golden.root [ Gyehoek.Test.Golden.root
, Gyehoek.Test.Sexp.root , Gyehoek.Test.Sexp.root
, Gyehoek.Test.CPS.Syntax.root , Gyehoek.Test.CPS.Syntax.root
, Gyehoek.Test.Stack.VM.root
] ]