Compare commits
7
Commits
idk
...
6949ff7fdf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6949ff7fdf | ||
|
|
a73b3ed89b | ||
|
|
1c13de4153 | ||
|
|
d91e059a84 | ||
|
|
c4bcf38374 | ||
|
|
745277ed1a | ||
|
|
c3c4866fa8 |
+218
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
(let ((make-adder (lambda (x)
|
||||||
|
(lambda (y)
|
||||||
|
(+ x y)))))
|
||||||
|
((make-adder 4) 5))
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
((λ (f g x)
|
||||||
|
(f (g x)))
|
||||||
|
(λ (x) (+ x 4))
|
||||||
|
(λ (x) (* x 2))
|
||||||
|
3)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ret > ExitSuccess
|
||||||
|
out > 720
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
(letrec ((fac (λ (n)
|
||||||
|
(if (zero? n)
|
||||||
|
1
|
||||||
|
(* n (fac (- n 1)))))))
|
||||||
|
(fac 6))
|
||||||
@@ -1 +1,3 @@
|
|||||||
(((λ (f) f) (λ (x) (* x 4))) 32)
|
(((λ (f) f)
|
||||||
|
(λ (x) (* x 4)))
|
||||||
|
32)
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
(let ((square (λ (x) (* x x))))
|
||||||
|
(square 4))
|
||||||
+13
-2
@@ -52,21 +52,25 @@ library
|
|||||||
|
|
||||||
-- cabal-fmt: expand src
|
-- cabal-fmt: expand src
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
|
Gyehoek.CPS.Close
|
||||||
Gyehoek.CPS.Convert
|
Gyehoek.CPS.Convert
|
||||||
Gyehoek.CPS.Lower
|
Gyehoek.CPS.Lower
|
||||||
|
Gyehoek.CPS.Stackify
|
||||||
Gyehoek.CPS.Syntax
|
Gyehoek.CPS.Syntax
|
||||||
Gyehoek.Driver
|
Gyehoek.Driver
|
||||||
Gyehoek.GenSym
|
Gyehoek.GenSym
|
||||||
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:
|
||||||
, base ^>=4.21.2.0
|
, base ^>=4.21.2.0
|
||||||
, binary
|
, binary
|
||||||
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, typed-process
|
|
||||||
, effectful
|
, effectful
|
||||||
, effectful-core
|
, effectful-core
|
||||||
, effectful-plugin
|
, effectful-plugin
|
||||||
@@ -87,9 +91,9 @@ library
|
|||||||
, template-haskell
|
, template-haskell
|
||||||
, text
|
, text
|
||||||
, text-short
|
, text-short
|
||||||
|
, typed-process
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
, vector
|
, vector
|
||||||
, bytestring
|
|
||||||
|
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: GHC2024
|
default-language: GHC2024
|
||||||
@@ -100,19 +104,26 @@ test-suite test
|
|||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
|
Gyehoek.Test.CPS.Stackify
|
||||||
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
|
||||||
|
, effectful
|
||||||
, filepath
|
, filepath
|
||||||
|
, generic-lens
|
||||||
, gyehoek
|
, gyehoek
|
||||||
|
, lens
|
||||||
, process-extras
|
, process-extras
|
||||||
|
, text
|
||||||
, sexp-grammar
|
, sexp-grammar
|
||||||
, tasty
|
, tasty
|
||||||
, tasty-hunit
|
, tasty-hunit
|
||||||
, tasty-silver
|
, tasty-silver
|
||||||
|
, tasty-expected-failure
|
||||||
|
|
||||||
default-language: GHC2024
|
default-language: GHC2024
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
module Gyehoek.CPS.Close
|
||||||
|
( closeProgram
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Gyehoek.CPS.Syntax
|
||||||
|
import Effectful
|
||||||
|
import Data.Functor.Foldable
|
||||||
|
import Control.Monad ((>=>))
|
||||||
|
import Control.Lens
|
||||||
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
|
import qualified Data.HashSet as HS
|
||||||
|
|
||||||
|
|
||||||
|
cataM
|
||||||
|
:: (Monad m, Traversable (Base t), Recursive t)
|
||||||
|
=> (Base t a -> m a) -> t -> m a
|
||||||
|
cataM f = cata (sequenceA >=> f)
|
||||||
|
|
||||||
|
close :: Exp -> Exp
|
||||||
|
close = cata \case
|
||||||
|
ExpLetRecF {bindersF,bodyF} -> ExpLetRec binders bodyF
|
||||||
|
where
|
||||||
|
binders = bindersF & (each . _2 . _AbsLambda' . _3) %~ \e -> _
|
||||||
|
e -> embed e
|
||||||
|
|
||||||
|
-- let frees = freeWithBound' (HS.fromList $ ktail : bs) e'
|
||||||
|
|
||||||
|
closeProgram :: Program -> Eff es Program
|
||||||
|
closeProgram (MkProgram e) = pure . MkProgram . close $ e
|
||||||
@@ -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
|
||||||
@@ -13,6 +14,10 @@ import Control.Monad.Cont qualified as Cont
|
|||||||
import Control.Lens
|
import Control.Lens
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Gyehoek.Sexp
|
import qualified Gyehoek.Sexp
|
||||||
|
import Data.String.Interpolate (i)
|
||||||
|
import Data.Functor (unzip)
|
||||||
|
import Data.List (List)
|
||||||
|
import Prelude hiding (unzip)
|
||||||
|
|
||||||
|
|
||||||
-- 뻘짓이어라
|
-- 뻘짓이어라
|
||||||
@@ -44,11 +49,10 @@ convert (Scm.ExpPrim p) k =
|
|||||||
|
|
||||||
convert (Scm.ExpLambda xs e) k = do
|
convert (Scm.ExpLambda xs e) k = do
|
||||||
f <- gensym' "λ-body"
|
f <- gensym' "λ-body"
|
||||||
ktail <- gensym' "λ-tail"
|
lam <- convertLambda xs e
|
||||||
m <- convert e $ \e' -> pure $ ExpContinue ktail [e']
|
|
||||||
ke <- k $ ValVar f
|
ke <- k $ ValVar f
|
||||||
pure [cps|
|
pure [cps|
|
||||||
(letrec ((#{f} (λ (##{xs} #{ktail}) #{m})))
|
(letrec ((#{f} #{lam}))
|
||||||
#{ke})
|
#{ke})
|
||||||
|]
|
|]
|
||||||
|
|
||||||
@@ -65,7 +69,38 @@ convert (Scm.ExpIf c t f) k =
|
|||||||
convert c \c' ->
|
convert c \c' ->
|
||||||
ExpIf c' <$> convert t k <*> convert f k
|
ExpIf c' <$> convert t k <*> convert f k
|
||||||
|
|
||||||
convert _ k = _
|
-- let-bindings are desugared into continuation calls whose parameters
|
||||||
|
-- are the left-hand sides and whose arguments are the right-hand
|
||||||
|
-- sides.
|
||||||
|
convert (Scm.ExpLet bs e) k =
|
||||||
|
let rhss = bs ^.. each . _2
|
||||||
|
in telescope (convert @es) rhss \rhss' -> do
|
||||||
|
e' <- convert e k
|
||||||
|
kbody <- gensym' @Name "letrec-body"
|
||||||
|
let bs' = bs ^.. each . _1
|
||||||
|
pure [cps|
|
||||||
|
(letrec ((#{kbody} (κ #{bs'} #{e'})))
|
||||||
|
(continue #{kbody} ##{rhss'}))
|
||||||
|
|]
|
||||||
|
|
||||||
|
convert (Scm.ExpLetRec bs m) k = do
|
||||||
|
let bs' = bs & each . _2 %~ (^?! #ExpLambda)
|
||||||
|
let conv = traverseOf _2 (uncurry $ convertLambda @es)
|
||||||
|
bs'' <- traverse conv bs'
|
||||||
|
m' <- convert m k
|
||||||
|
pure [cps|
|
||||||
|
(letrec #{bs''} #{m'})
|
||||||
|
|]
|
||||||
|
|
||||||
|
-- convert e k = error [i|unimplemented expr: #{e}|]
|
||||||
|
|
||||||
|
convertLambda
|
||||||
|
:: GenSym :> es
|
||||||
|
=> List Name -> Scm.Exp -> Eff es Lambda
|
||||||
|
convertLambda bs m = do
|
||||||
|
ktail <- gensym' "lambda-tail"
|
||||||
|
m' <- convert m $ pure . ExpContinue ktail . (:[])
|
||||||
|
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 =
|
||||||
@@ -73,3 +108,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)
|
||||||
|
|||||||
+44
-44
@@ -26,9 +26,12 @@ import Language.Sexp.Located qualified as SL
|
|||||||
import Control.Monad.Fix
|
import Control.Monad.Fix
|
||||||
import qualified Gyehoek.Sexp
|
import qualified Gyehoek.Sexp
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
|
import Data.List qualified
|
||||||
import Data.Foldable (fold)
|
import Data.Foldable (fold)
|
||||||
import Gyehoek.Sexp (encodeOrShow, toSexp)
|
import Gyehoek.Sexp (encodeOrShow, toSexp)
|
||||||
import Debug.Pretty.Simple
|
import Debug.Pretty.Simple
|
||||||
|
import GHC.Stack (HasCallStack)
|
||||||
|
import Data.String.Interpolate
|
||||||
|
|
||||||
|
|
||||||
data Env = MkEnv
|
data Env = MkEnv
|
||||||
@@ -58,31 +61,34 @@ makeSmallFixnum = [expr|
|
|||||||
ref.i31
|
ref.i31
|
||||||
|]
|
|]
|
||||||
|
|
||||||
|
getArgRegister :: Natural -> SL.Sexp
|
||||||
|
getArgRegister n = SL.Symbol [i|$arg#{n}|]
|
||||||
|
|
||||||
-- | Given an expression @e@ leaving a @ref eq@ atop the stack,
|
-- | Given an expression @e@ leaving a @ref eq@ atop the stack,
|
||||||
-- @pushArg rt n e@ sets the nth slot of the arg-passing array to the
|
-- @pushArg rt n e@ sets the nth slot of the arg-passing array to the
|
||||||
-- result of @e@.
|
-- result of @e@.
|
||||||
pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
|
pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
|
||||||
pushArg n e = [expr|
|
pushArg n e = [expr|
|
||||||
(@gyehoek "push argument")
|
(@gyehoek begin pushArg)
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const #{n})
|
|
||||||
##{e}
|
##{e}
|
||||||
(array.set $arg-array-type)
|
(global.set #{reg})
|
||||||
|
(@gyehoek end pushArg)
|
||||||
|]
|
|]
|
||||||
|
where reg = getArgRegister n
|
||||||
|
|
||||||
-- | Pop the nth arg from the arg-passing array onto the stack.
|
-- | Pop the nth arg from the arg-passing array onto the stack.
|
||||||
popArg :: Int -> Wasm.Expr
|
popArg :: Natural -> Wasm.Expr
|
||||||
popArg n = [expr|
|
popArg n = [expr|
|
||||||
(@gyehoek "pop argument")
|
(@gyehoek begin popArg)
|
||||||
(global.get $arg-array)
|
(global.get #{reg})
|
||||||
(i32.const #{n})
|
|
||||||
(array.get $arg-array-type)
|
|
||||||
ref.as_non_null
|
ref.as_non_null
|
||||||
|
(@gyehoek end popArg)
|
||||||
|]
|
|]
|
||||||
|
where reg = getArgRegister n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
lowerVal :: GenMod :> es => Env -> Val -> Eff es Wasm.Expr
|
lowerVal :: (HasCallStack, GenMod :> es) => Env -> Val -> Eff es Wasm.Expr
|
||||||
|
|
||||||
lowerVal g (ValLit l) =
|
lowerVal g (ValLit l) =
|
||||||
pure $ case l of
|
pure $ case l of
|
||||||
@@ -97,17 +103,10 @@ lowerVal g (ValLit l) =
|
|||||||
where b' :: Int = if b then 0b11 else 0b01
|
where b' :: Int = if b then 0b11 else 0b01
|
||||||
_ -> _
|
_ -> _
|
||||||
|
|
||||||
lowerVal g (ValVar x) = pure $ [expr|(local.get #{l})|]
|
lowerVal g (ValVar x) = do
|
||||||
|
pure [expr|(global.get #{l})|]
|
||||||
where
|
where
|
||||||
l = succ $ V.elemIndex x g.vars ^?! _Just
|
l = getArgRegister . fromIntegral . succ $ V.elemIndex x g.vars ^?! _Just
|
||||||
|
|
||||||
-- lowerVal g (ValLambda lam) = do
|
|
||||||
-- idx <- lowerLambda g lam
|
|
||||||
-- pure [expr|
|
|
||||||
-- (i32.const 0)
|
|
||||||
-- (ref.func #{idx})
|
|
||||||
-- (struct.new $closure)
|
|
||||||
-- |]
|
|
||||||
|
|
||||||
lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
|
lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
|
||||||
|
|
||||||
@@ -159,11 +158,12 @@ lower' g (ExpLetRec [(r,AbsLambda lam)] e) = do
|
|||||||
let g' = g & #vars <>~ [r]
|
let g' = g & #vars <>~ [r]
|
||||||
let n = succ $ length g.vars
|
let n = succ $ length g.vars
|
||||||
e' <- lower' g' e
|
e' <- lower' g' e
|
||||||
|
let reg = getArgRegister . fromIntegral $ n
|
||||||
pure [expr|
|
pure [expr|
|
||||||
(i32.const 0)
|
(i32.const 0)
|
||||||
(ref.func #{idx})
|
(ref.func #{idx})
|
||||||
(struct.new $closure)
|
(struct.new $closure)
|
||||||
(local.set #{n})
|
(global.set #{reg})
|
||||||
##{e'}
|
##{e'}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
@@ -219,20 +219,14 @@ lower' g e = error $ case Gyehoek.Sexp.encode e of
|
|||||||
|
|
||||||
lowerKappa :: GenMod :> es => Env -> Kappa -> Eff es Idx
|
lowerKappa :: GenMod :> es => Env -> Kappa -> Eff es Idx
|
||||||
lowerKappa g e@(MkKappa xs m) = do
|
lowerKappa g e@(MkKappa xs m) = do
|
||||||
let g' = g & #vars .~ V.fromList xs
|
let g' = g & #vars <>~ V.fromList xs
|
||||||
m' <- lower' g' m
|
m' <- lower' g' m
|
||||||
let body = mconcat
|
|
||||||
[ xs & ifoldMap \n _ ->
|
|
||||||
let n' = succ n
|
|
||||||
in popArg n <> [expr|(local.set #{n'})|]
|
|
||||||
, m'
|
|
||||||
]
|
|
||||||
let origin = encodeOrShow @_ @Text e
|
let origin = encodeOrShow @_ @Text e
|
||||||
idx <- Wasm.defineFunction [wat|
|
idx <- Wasm.defineFunction [wat|
|
||||||
(func (param i32)
|
(func (param i32)
|
||||||
(@gyehoek :origin #{origin})
|
(@gyehoek :origin #{origin})
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
||||||
##{body})
|
##{m'})
|
||||||
|]
|
|]
|
||||||
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
|
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
|
||||||
pure idx
|
pure idx
|
||||||
@@ -242,18 +236,12 @@ lowerLambda g e@(MkLambda xs ktail m) = do
|
|||||||
let g' = g & #vars .~ V.fromList xs
|
let g' = g & #vars .~ V.fromList xs
|
||||||
& #kvars <>~ [ktail]
|
& #kvars <>~ [ktail]
|
||||||
m' <- lower' g' m
|
m' <- lower' g' m
|
||||||
let body = mconcat
|
|
||||||
[ xs & ifoldMap \n _ ->
|
|
||||||
let n' = succ n
|
|
||||||
in popArg n <> [expr|(local.set #{n'})|]
|
|
||||||
, m'
|
|
||||||
]
|
|
||||||
let origin = encodeOrShow @_ @Text e
|
let origin = encodeOrShow @_ @Text e
|
||||||
idx <- Wasm.defineFunction [wat|
|
idx <- Wasm.defineFunction [wat|
|
||||||
(func (param i32)
|
(func (param i32)
|
||||||
(@gyehoek :origin #{origin})
|
(@gyehoek :origin #{origin})
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
||||||
##{body})
|
##{m'})
|
||||||
|]
|
|]
|
||||||
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
|
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
|
||||||
pure idx
|
pure idx
|
||||||
@@ -265,6 +253,7 @@ lowerBinOp op g x y (MkKappa [r] e) = do
|
|||||||
let op' = SL.Symbol op
|
let op' = SL.Symbol op
|
||||||
let g' = g & #vars <>~ [r]
|
let g' = g & #vars <>~ [r]
|
||||||
let n = succ $ length (g ^. #vars)
|
let n = succ $ length (g ^. #vars)
|
||||||
|
let reg = getArgRegister . fromIntegral $ n
|
||||||
x' <- lowerVal g x
|
x' <- lowerVal g x
|
||||||
y' <- lowerVal g y
|
y' <- lowerVal g y
|
||||||
e' <- lower' g' e
|
e' <- lower' g' e
|
||||||
@@ -279,7 +268,7 @@ lowerBinOp op g x y (MkKappa [r] e) = do
|
|||||||
i32.shr_u
|
i32.shr_u
|
||||||
#{op'}
|
#{op'}
|
||||||
##{makeSmallFixnum}
|
##{makeSmallFixnum}
|
||||||
(local.set #{n})
|
(global.set #{reg})
|
||||||
##{e'}
|
##{e'}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
@@ -306,13 +295,24 @@ emitRuntime = mfix \runtime -> do
|
|||||||
(global $cont-stack (ref $cont-stack-type)
|
(global $cont-stack (ref $cont-stack-type)
|
||||||
(array.new_default $cont-stack-type (i32.const 128)))
|
(array.new_default $cont-stack-type (i32.const 128)))
|
||||||
|]
|
|]
|
||||||
-- arg array
|
-- arg registers
|
||||||
Wasm.defineType [wat|
|
Wasm.defineGlobals [wats|
|
||||||
(type $arg-array-type (array (mut (ref null eq))))
|
(global $arg0 (mut (ref null eq)) (ref.null eq))
|
||||||
|]
|
(global $arg1 (mut (ref null eq)) (ref.null eq))
|
||||||
Wasm.defineGlobal [wat|
|
(global $arg2 (mut (ref null eq)) (ref.null eq))
|
||||||
(global $arg-array (ref $arg-array-type)
|
(global $arg3 (mut (ref null eq)) (ref.null eq))
|
||||||
(array.new_default $arg-array-type (i32.const 32)))
|
(global $arg4 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg5 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg6 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg7 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg8 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg9 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg10 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg11 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg12 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg13 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg14 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg15 (mut (ref null eq)) (ref.null eq))
|
||||||
|]
|
|]
|
||||||
-- other things 😼
|
-- other things 😼
|
||||||
Wasm.defineGlobal [wat|
|
Wasm.defineGlobal [wat|
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Gyehoek.CPS.Stackify
|
||||||
|
( stackifyExp
|
||||||
|
, stackifyProgram
|
||||||
|
, module Gyehoek.CPS.Syntax
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Gyehoek.CPS.Syntax
|
||||||
|
import Gyehoek.Stack.Syntax qualified as Stk
|
||||||
|
import Data.Sequence (Seq)
|
||||||
|
import Effectful
|
||||||
|
import Gyehoek.GenSym
|
||||||
|
import Effectful.Writer.Static.Shared
|
||||||
|
import Control.Lens
|
||||||
|
import Data.String.Interpolate
|
||||||
|
import Gyehoek.Stack.Syntax (Imm(..))
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import qualified Data.HashSet as HS
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import qualified Data.HashMap.Strict as H
|
||||||
|
import Data.HashSet.Lens (hashMap)
|
||||||
|
import Data.List (List)
|
||||||
|
import GHC.Exts (IsList(fromList))
|
||||||
|
|
||||||
|
|
||||||
|
type Stackify = Writer Stk.Program
|
||||||
|
|
||||||
|
runStackify :: Eff (Stackify : es) a -> Eff es (a, Stk.Program)
|
||||||
|
runStackify = runWriter
|
||||||
|
|
||||||
|
live :: Free a => Env -> a -> List Name
|
||||||
|
live g e = free' e & filter \x -> x `H.member` g.bound && x /= g.returnLabel
|
||||||
|
|
||||||
|
stackify
|
||||||
|
:: (GenSym :> es, Stackify :> es)
|
||||||
|
=> Env -> Exp -> Eff es (Seq Stk.Instr)
|
||||||
|
|
||||||
|
stackify g (ExpLetRec [(f, kap@(AbsKappa' xs m))] e) = do
|
||||||
|
let vs = (f, Stk.ValLabel f) : (bindReg <$> xs)
|
||||||
|
let ls = live g kap
|
||||||
|
m' <- stackify (g & #bound .~ H.fromList (vs ++ (bindReg <$> ls))) m
|
||||||
|
tell [Stk.MkBlock f xs $
|
||||||
|
[Stk.Pop x | x <- ls] <> toList m']
|
||||||
|
let g' = g & #bound . at f ?~ Stk.ValLabel f
|
||||||
|
& #liveness . at f ?~ ls
|
||||||
|
stackify g' e
|
||||||
|
|
||||||
|
stackify g (ExpLetRec [(f, AbsLambda' xs k m)] e) = do
|
||||||
|
let vs = (k:xs) <&> \x -> (x, Stk.ValReg x)
|
||||||
|
lam_body <- gensym' "lambda-body"
|
||||||
|
m' <- stackify (g & #bound .~ H.fromList vs
|
||||||
|
& #bound . at f ?~ Stk.ValLabel lam_body
|
||||||
|
& #returnLabel .~ k) m
|
||||||
|
tell [Stk.MkBlock lam_body xs . toList $ m']
|
||||||
|
stackify (g & #bound . at f ?~ Stk.ValLabel lam_body) e
|
||||||
|
|
||||||
|
stackify g (ExpIf c t f) = do
|
||||||
|
t' <- stackify g t
|
||||||
|
f' <- stackify g f
|
||||||
|
pure [ Stk.If (stackifyVal g c) (toList t') (toList f') ]
|
||||||
|
|
||||||
|
stackify g (ExpApply f xs ktail) = do
|
||||||
|
pure $
|
||||||
|
[ Stk.PushCont (Stk.ValLabel k) ]
|
||||||
|
<> fromList [ Stk.Push (Stk.ValReg l) | l <- ls ]
|
||||||
|
<> [ Stk.Call (stackifyVal g f) (stackifyVal g <$> xs) ]
|
||||||
|
where
|
||||||
|
k = case var g ktail of
|
||||||
|
Stk.ValLabel x -> x
|
||||||
|
x -> error [i|expected a label, got #{x} (i guess)|]
|
||||||
|
ls = fold $ g ^. #liveness . at k
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
-- assume it's the return continuation on top of the stack.
|
||||||
|
stackify g (ExpContinue k xs) = do
|
||||||
|
ktail <- gensym' $ k ^. _Wrapped'
|
||||||
|
pure [ Stk.PopCont ktail
|
||||||
|
, Stk.Call (Stk.ValReg ktail) (stackifyVal g <$> xs)
|
||||||
|
]
|
||||||
|
|
||||||
|
stackify g (ExpPrim p (MkKappa [x] e)) = do
|
||||||
|
e' <- stackify (g & #bound . at x ?~ Stk.ValReg x) e
|
||||||
|
pure $ [ Stk.Prim x (stackifyVal g <$> p) ] <> e'
|
||||||
|
|
||||||
|
stackify _ e = error [i|unimplemented exp: #{e}|]
|
||||||
|
|
||||||
|
stackifyVal :: Env -> Val -> Stk.Val
|
||||||
|
stackifyVal g = \case
|
||||||
|
ValLit (LitInt n) -> Stk.ValImm (ImmInt n)
|
||||||
|
ValLit (LitBool b) -> Stk.ValImm (ImmBool b)
|
||||||
|
ValVar v -> var g v
|
||||||
|
v -> error [i|unimplemented val: #{v}|]
|
||||||
|
|
||||||
|
var :: Env -> Name -> Stk.Val
|
||||||
|
var g v = case g ^. #bound . at v of
|
||||||
|
Just x -> x
|
||||||
|
Nothing -> Stk.ValLabel v
|
||||||
|
|
||||||
|
bindReg :: Name -> (Name, Stk.Val)
|
||||||
|
bindReg x = (x, Stk.ValReg x)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data Env = MkEnv
|
||||||
|
{ bound :: HashMap Name Stk.Val
|
||||||
|
, returnLabel :: Name
|
||||||
|
-- | for each locally-bound continuation @k@, @liveness@ has an
|
||||||
|
-- entry @(k,ls)@ where @ls@ is the sequence of registers @k@
|
||||||
|
-- expects to find saved on the stack.
|
||||||
|
, liveness :: HashMap Name (List Name)
|
||||||
|
}
|
||||||
|
deriving (Show, Generic)
|
||||||
|
|
||||||
|
emptyEnv :: Env
|
||||||
|
emptyEnv = MkEnv mempty "halt" mempty
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
stackifyExp :: GenSym :> es => Name -> Exp -> Eff es Stk.Program
|
||||||
|
stackifyExp lbl e = do
|
||||||
|
(code,p) <- runStackify $ stackify emptyEnv e
|
||||||
|
pure $ p <> Stk.MkProgram [ Stk.MkBlock lbl [] (toList code) ]
|
||||||
|
|
||||||
|
stackifyProgram :: GenSym :> es => Program -> Eff es Stk.Program
|
||||||
|
stackifyProgram (MkProgram e) = stackifyExp "main" e
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fac :: Program
|
||||||
|
fac = [cps|
|
||||||
|
(letrec ((fac (λ (n ktail)
|
||||||
|
(prim (zero? n)
|
||||||
|
(κ (x0)
|
||||||
|
(if x0
|
||||||
|
(continue ktail 1)
|
||||||
|
(prim (- n 1)
|
||||||
|
(κ (x1)
|
||||||
|
(letrec ((fac-k0
|
||||||
|
(κ (x2)
|
||||||
|
(prim (* n x2)
|
||||||
|
(κ (x3)
|
||||||
|
(continue ktail x3))))))
|
||||||
|
(fac x1 fac-k0))))))))))
|
||||||
|
(fac 6 halt))
|
||||||
|
|]
|
||||||
+122
-42
@@ -9,6 +9,7 @@ module Gyehoek.CPS.Syntax
|
|||||||
, Kappa(..)
|
, Kappa(..)
|
||||||
, Lambda(..)
|
, Lambda(..)
|
||||||
, Exp(..)
|
, Exp(..)
|
||||||
|
, ExpF(..)
|
||||||
, Name(..)
|
, Name(..)
|
||||||
, Prim(..)
|
, Prim(..)
|
||||||
, Program(..)
|
, Program(..)
|
||||||
@@ -20,6 +21,7 @@ module Gyehoek.CPS.Syntax
|
|||||||
, _ExpPrim
|
, _ExpPrim
|
||||||
, _ExpLetRec
|
, _ExpLetRec
|
||||||
, _ExpApply
|
, _ExpApply
|
||||||
|
, _AbsLambda'
|
||||||
, binders
|
, binders
|
||||||
, body
|
, body
|
||||||
, op
|
, op
|
||||||
@@ -29,8 +31,9 @@ module Gyehoek.CPS.Syntax
|
|||||||
, pattern AbsLambda'
|
, pattern AbsLambda'
|
||||||
, pattern AbsKappa'
|
, pattern AbsKappa'
|
||||||
, Abs(..)
|
, Abs(..)
|
||||||
, free
|
, Free(..)
|
||||||
, free'
|
, Vars(..)
|
||||||
|
, Subst(..)
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@@ -55,6 +58,8 @@ import qualified Data.HashSet as HS
|
|||||||
import Data.Hashable (Hashable)
|
import Data.Hashable (Hashable)
|
||||||
import Data.Monoid (Endo)
|
import Data.Monoid (Endo)
|
||||||
import Data.Containers.ListUtils (nubOrd)
|
import Data.Containers.ListUtils (nubOrd)
|
||||||
|
import Data.Functor.Foldable.TH
|
||||||
|
import Data.Functor.Foldable (Recursive(..), Corecursive (..))
|
||||||
|
|
||||||
-- Data types
|
-- Data types
|
||||||
|
|
||||||
@@ -114,6 +119,7 @@ makePrisms ''Exp
|
|||||||
makeFieldsId ''Exp
|
makeFieldsId ''Exp
|
||||||
makeFieldsId ''Kappa
|
makeFieldsId ''Kappa
|
||||||
makeFieldsId ''Lambda
|
makeFieldsId ''Lambda
|
||||||
|
makeBaseFunctor ''Exp
|
||||||
|
|
||||||
instance HasBinders Abs (List Name) where
|
instance HasBinders Abs (List Name) where
|
||||||
binders k (AbsKappa kap) = AbsKappa <$> binders k kap
|
binders k (AbsKappa kap) = AbsKappa <$> binders k kap
|
||||||
@@ -123,6 +129,12 @@ instance HasBody Abs Exp where
|
|||||||
body k (AbsKappa kap) = AbsKappa <$> body k kap
|
body k (AbsKappa kap) = AbsKappa <$> body k kap
|
||||||
body k (AbsLambda lam) = AbsLambda <$> body k lam
|
body k (AbsLambda lam) = AbsLambda <$> body k lam
|
||||||
|
|
||||||
|
_AbsLambda' :: Prism' Abs (List Name, Name, Exp)
|
||||||
|
_AbsLambda' = prism'
|
||||||
|
(\(bs,ktail,e) -> AbsLambda' bs ktail e)
|
||||||
|
(\case AbsLambda' bs ktail e -> Just (bs,ktail,e)
|
||||||
|
_ -> Nothing)
|
||||||
|
|
||||||
|
|
||||||
-- SexpIso instances
|
-- SexpIso instances
|
||||||
|
|
||||||
@@ -219,6 +231,7 @@ instance CPS Val where toCPS = Gyehoek.Sexp.fromSexp
|
|||||||
instance CPS Kappa where toCPS = Gyehoek.Sexp.fromSexp
|
instance CPS Kappa where toCPS = Gyehoek.Sexp.fromSexp
|
||||||
instance CPS Lambda where toCPS = Gyehoek.Sexp.fromSexp
|
instance CPS Lambda where toCPS = Gyehoek.Sexp.fromSexp
|
||||||
instance CPS Abs where toCPS = Gyehoek.Sexp.fromSexp
|
instance CPS Abs where toCPS = Gyehoek.Sexp.fromSexp
|
||||||
|
instance CPS Program where toCPS = Gyehoek.Sexp.fromSexp
|
||||||
|
|
||||||
cps :: QuasiQuoter
|
cps :: QuasiQuoter
|
||||||
cps = Gyehoek.Sexp.makeSx' [| toCPS |]
|
cps = Gyehoek.Sexp.makeSx' [| toCPS |]
|
||||||
@@ -234,49 +247,116 @@ insertFrom = flip $ foldr HS.insert
|
|||||||
toHashSetOf :: Hashable a => Getting (Endo (HashSet a)) s a -> s -> HashSet a
|
toHashSetOf :: Hashable a => Getting (Endo (HashSet a)) s a -> s -> HashSet a
|
||||||
toHashSetOf l = foldrOf l HS.insert mempty
|
toHashSetOf l = foldrOf l HS.insert mempty
|
||||||
|
|
||||||
free :: Exp -> HashSet Name
|
class Free a where
|
||||||
free = go where
|
free :: a -> HashSet Name
|
||||||
gokap (MkKappa xs m) = go m & deleteFrom xs
|
free = freeWithBound mempty
|
||||||
golam (MkLambda xs k m) = go m & deleteFrom xs & sans k
|
|
||||||
goabs = \case
|
freeWithBound :: HashSet Name -> a -> HashSet Name
|
||||||
AbsKappa kap -> gokap kap
|
freeWithBound bound = HS.fromList . freeWithBound' bound
|
||||||
AbsLambda lam -> golam lam
|
|
||||||
go = \case
|
-- | Free variables given in the order of their appearance.
|
||||||
|
free' :: a -> List Name
|
||||||
|
free' = freeWithBound' mempty
|
||||||
|
|
||||||
|
freeWithBound' :: HashSet Name -> a -> List Name
|
||||||
|
|
||||||
|
instance Free Abs where
|
||||||
|
freeWithBound' bound (AbsKappa kap) = freeWithBound' bound kap
|
||||||
|
freeWithBound' bound (AbsLambda lam) = freeWithBound' bound lam
|
||||||
|
|
||||||
|
instance Free Exp where
|
||||||
|
freeWithBound' bound = \case
|
||||||
ExpPrim p k ->
|
ExpPrim p k ->
|
||||||
p & toHashSetOf (folded . #ValVar)
|
p & toListOf (folded . #ValVar . filtered (`notElem` bound))
|
||||||
& HS.union (gokap k)
|
& (<> freeWithBound' bound k)
|
||||||
ExpLetRec bs m ->
|
ExpLetRec bs m ->
|
||||||
foldMapOf (each . _2) goabs bs <> go m
|
foldMapOf (each . _2) (freeWithBound' bound') bs
|
||||||
& deleteFrom (bs ^.. each . _1)
|
<> freeWithBound' bound' m
|
||||||
ExpContinue k xs -> HS.fromList $ k : xs ^.. each . #ValVar
|
where bound' = bound & insertFrom (bs ^.. each . _1)
|
||||||
ExpIf c t f -> toHashSetOf #ValVar c <> go t <> go f
|
ExpContinue k xs -> filter (`notElem` bound) (k : xs ^.. each . #ValVar)
|
||||||
ExpApply f xs k -> toHashSetOf (each . #ValVar) (f:xs) <> HS.singleton k
|
ExpIf c t f ->
|
||||||
|
(c ^.. #ValVar . filtered (`notElem` bound))
|
||||||
|
<> freeWithBound' bound t <> freeWithBound' bound f
|
||||||
|
ExpApply f xs k ->
|
||||||
|
(f:xs) ^.. (each . #ValVar . filtered (`notElem` bound))
|
||||||
|
<> (k ^.. filtered (`notElem` bound))
|
||||||
|
|
||||||
-- | Free variables given in the order of their appearance.
|
instance Free Kappa where
|
||||||
free' :: Exp -> List Name
|
freeWithBound' bound (MkKappa xs m) =
|
||||||
free' = nubOrd . goFree HS.empty where
|
freeWithBound' (bound & insertFrom xs) m
|
||||||
|
|
||||||
goFreeKap bound (MkKappa xs m) = goFree (bound & insertFrom xs) m
|
instance Free Lambda where
|
||||||
goFreeLam bound (MkLambda xs k m) = goFree (bound & insertFrom (k:xs)) m
|
freeWithBound' bound (MkLambda xs k m) =
|
||||||
goFreeAbs bound = \case
|
freeWithBound' (bound & insertFrom (k:xs)) m
|
||||||
AbsKappa kap -> goFreeKap bound kap
|
|
||||||
AbsLambda lam -> goFreeLam bound lam
|
|
||||||
|
|
||||||
goFree :: HashSet Name -> Exp -> List Name
|
|
||||||
goFree bound = \case
|
|
||||||
ExpPrim p k ->
|
|
||||||
p & toListOf (folded . #ValVar . filtered (`notElem` bound))
|
|
||||||
& (<> goFreeKap bound k)
|
|
||||||
ExpLetRec bs m ->
|
|
||||||
foldMapOf (each . _2) (goFreeAbs bound') bs <> goFree bound' m
|
|
||||||
where bound' = bound & insertFrom (bs ^.. each . _1)
|
|
||||||
ExpContinue k xs -> filter (`notElem` bound) (k : xs ^.. each . #ValVar)
|
|
||||||
ExpIf c t f ->
|
|
||||||
(c ^.. #ValVar . filtered (`notElem` bound))
|
|
||||||
<> goFree bound t <> goFree bound f
|
|
||||||
ExpApply f xs k ->
|
|
||||||
(f:xs) ^.. (each . #ValVar . filtered (`notElem` bound))
|
|
||||||
<> (k ^.. filtered (`notElem` bound))
|
|
||||||
|
|
||||||
freeLambda :: Lambda -> List Name
|
class Vars a where
|
||||||
freeLambda (MkLambda {binders,ktail,body}) = _
|
-- | Traverse the immediate variables of an expression.
|
||||||
|
vars :: Traversal' a Name
|
||||||
|
|
||||||
|
instance Vars Val where
|
||||||
|
vars k (ValVar x) = ValVar <$> k x
|
||||||
|
vars _ x = pure x
|
||||||
|
|
||||||
|
instance Vars a => Vars (Prim a) where
|
||||||
|
vars k p = traverseOf (each . vars) k p
|
||||||
|
|
||||||
|
instance Vars Exp where
|
||||||
|
vars k (ExpPrim p kap) = ExpPrim <$> vars k p <*> pure kap
|
||||||
|
vars k (ExpContinue kname xs) = ExpContinue <$> k kname <*> pure xs
|
||||||
|
vars _ e = pure e
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data Scope
|
||||||
|
= Bind (List Name) (List Scope)
|
||||||
|
| Use (List Name) (List Scope)
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
makeBaseFunctor ''Scope
|
||||||
|
|
||||||
|
class Scoped a where
|
||||||
|
scope :: a -> Scope
|
||||||
|
|
||||||
|
instance Scoped Kappa where
|
||||||
|
scope (MkKappa bs 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
|
||||||
|
scope = \case
|
||||||
|
ValVar x -> Use [x] []
|
||||||
|
_ -> Use [] []
|
||||||
|
|
||||||
|
instance Scoped Exp where
|
||||||
|
scope = \case
|
||||||
|
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 -> _
|
||||||
|
|||||||
+23
-5
@@ -1,5 +1,5 @@
|
|||||||
module Gyehoek.Driver
|
module Gyehoek.Driver
|
||||||
(main, lower_e2e, convert_e2e, parse_e2e)
|
(main, lower_e2e, convert_e2e, parse_e2e, readScm, eval_e2e)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Gyehoek.Options
|
import Gyehoek.Options
|
||||||
@@ -28,6 +28,12 @@ import System.Environment.Blank (getEnvDefault)
|
|||||||
import GHC.Conc (atomically)
|
import GHC.Conc (atomically)
|
||||||
import qualified Data.Text.IO as TIO
|
import qualified Data.Text.IO as TIO
|
||||||
import qualified Data.ByteString.Lazy as BS
|
import qualified Data.ByteString.Lazy as BS
|
||||||
|
import Gyehoek.CPS.Stackify (stackifyProgram)
|
||||||
|
import Text.Pretty.Simple (pShow)
|
||||||
|
import Gyehoek.Stack.VM (eval, writeObj, Obj)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Data.List (List)
|
||||||
|
import Gyehoek.Stack.Syntax (encodeProgram)
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
@@ -101,12 +107,19 @@ driver opts = do
|
|||||||
cps <- convertProgram scm
|
cps <- convertProgram scm
|
||||||
when opts.dumpCPS do
|
when opts.dumpCPS do
|
||||||
hPutStrLn FS.stdout $ Sexp.encodePretty cps ^?! _Right
|
hPutStrLn FS.stdout $ Sexp.encodePretty cps ^?! _Right
|
||||||
wat <- lowerProgram cps
|
stk <- stackifyProgram cps
|
||||||
if not opts.inspectWasm then
|
if opts.dumpStackified then do
|
||||||
|
hPutStrLn FS.stdout . encodeProgram $ stk
|
||||||
|
else if opts.stackify then do
|
||||||
|
eval stk & fmap writeObj
|
||||||
|
& T.unwords
|
||||||
|
& hPutStrLn FS.stdout
|
||||||
|
else do
|
||||||
|
wat <- lowerProgram cps
|
||||||
withFile opts.output FS.WriteMode \h ->
|
withFile opts.output FS.WriteMode \h ->
|
||||||
hPutStrLn h wat
|
hPutStrLn h wat
|
||||||
else
|
when opts.inspectWasm do
|
||||||
inspectWasm wat
|
inspectWasm wat
|
||||||
|
|
||||||
parse_e2e :: FilePath -> IO Scm.Program
|
parse_e2e :: FilePath -> IO Scm.Program
|
||||||
parse_e2e = runEff . runFileSystem . readScm
|
parse_e2e = runEff . runFileSystem . readScm
|
||||||
@@ -118,3 +131,8 @@ lower_e2e :: FilePath -> IO Text
|
|||||||
lower_e2e =
|
lower_e2e =
|
||||||
runEff . runFileSystem . runGenSym
|
runEff . runFileSystem . runGenSym
|
||||||
. (lowerProgram <=< convertProgram <=< readScm)
|
. (lowerProgram <=< convertProgram <=< readScm)
|
||||||
|
|
||||||
|
eval_e2e :: FilePath -> IO (List Obj)
|
||||||
|
eval_e2e fp = runEff . runFileSystem . runGenSym $ do
|
||||||
|
stk <- stackifyProgram <=< convertProgram <=< readScm $ fp
|
||||||
|
pure . eval $ stk
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import GHC.Generics (Generic)
|
|||||||
|
|
||||||
|
|
||||||
data Options = MkOptions
|
data Options = MkOptions
|
||||||
{ -- dumpANF :: Maybe FilePath
|
{ dumpCPS :: Bool
|
||||||
-- , dumpQBE :: Maybe FilePath
|
|
||||||
dumpCPS :: Bool
|
|
||||||
, dumpParsed :: Bool
|
, dumpParsed :: Bool
|
||||||
|
, dumpStackified :: Bool
|
||||||
|
, stackify :: Bool
|
||||||
, inspectWasm :: Bool
|
, inspectWasm :: Bool
|
||||||
, output :: FilePath
|
, output :: FilePath
|
||||||
, sourceFile :: FilePath
|
, sourceFile :: FilePath
|
||||||
@@ -49,6 +49,8 @@ parseOutput = strOption
|
|||||||
)
|
)
|
||||||
|
|
||||||
parseDumpCPS = switch (long "dump-cps")
|
parseDumpCPS = switch (long "dump-cps")
|
||||||
|
parseDumpStackified = switch (long "dump-stackified")
|
||||||
|
parseStackify = switch (long "stackify")
|
||||||
parseDumpParsed = switch (long "dump-parsed")
|
parseDumpParsed = switch (long "dump-parsed")
|
||||||
parseInspectWasm = switch $ long "inspect-wasm" <> short 'p'
|
parseInspectWasm = switch $ long "inspect-wasm" <> short 'p'
|
||||||
|
|
||||||
@@ -56,6 +58,8 @@ parser :: Parser Options
|
|||||||
parser = MkOptions
|
parser = MkOptions
|
||||||
<$> parseDumpCPS
|
<$> parseDumpCPS
|
||||||
<*> parseDumpParsed
|
<*> parseDumpParsed
|
||||||
|
<*> parseDumpStackified
|
||||||
|
<*> parseStackify
|
||||||
<*> parseInspectWasm
|
<*> parseInspectWasm
|
||||||
<*> parseOutput
|
<*> parseOutput
|
||||||
<*> argument str (metavar "FILE")
|
<*> argument str (metavar "FILE")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
{-# LANGUAGE DerivingStrategies #-}
|
{-# LANGUAGE DerivingStrategies #-}
|
||||||
{-# LANGUAGE OrPatterns #-}
|
{-# LANGUAGE OrPatterns #-}
|
||||||
{-# LANGUAGE PatternSynonyms #-}
|
{-# LANGUAGE PatternSynonyms #-}
|
||||||
|
{-# LANGUAGE DeriveAnyClass #-}
|
||||||
module Gyehoek.Scheme.Syntax
|
module Gyehoek.Scheme.Syntax
|
||||||
( Name(..)
|
( Name(..)
|
||||||
, Prim(..)
|
, Prim(..)
|
||||||
@@ -23,6 +24,8 @@ module Gyehoek.Scheme.Syntax
|
|||||||
, subst
|
, subst
|
||||||
, getName
|
, getName
|
||||||
, scm
|
, scm
|
||||||
|
, readExp
|
||||||
|
, readProgram
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@@ -33,7 +36,8 @@ 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 GHC.Generics
|
import Effectful
|
||||||
|
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)
|
||||||
@@ -49,11 +53,19 @@ 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 }
|
||||||
deriving newtype (Show, Eq, Ord, IsString, Gen, Hashable)
|
deriving newtype (Show, Eq, Ord, IsString, Gen, Hashable)
|
||||||
deriving stock (Generic, Data)
|
deriving stock (Generic, Data)
|
||||||
|
deriving anyclass (Wrapped)
|
||||||
|
|
||||||
|
instance Prefixed Name where
|
||||||
|
prefixed (MkName s) = _Wrapped' . prefixed @Text s . from _Wrapped'
|
||||||
|
|
||||||
getName :: Name -> Text
|
getName :: Name -> Text
|
||||||
getName (MkName x) = x
|
getName (MkName x) = x
|
||||||
@@ -72,6 +84,8 @@ data Prim e
|
|||||||
| PrimWrite e
|
| PrimWrite e
|
||||||
| PrimZeroP e
|
| PrimZeroP e
|
||||||
| PrimNewline
|
| PrimNewline
|
||||||
|
| 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'
|
||||||
@@ -94,6 +108,7 @@ data Def
|
|||||||
|
|
||||||
data Exp
|
data Exp
|
||||||
= ExpLet (NonEmpty (Name, Exp)) Exp
|
= ExpLet (NonEmpty (Name, Exp)) Exp
|
||||||
|
| ExpLetRec (NonEmpty (Name, Exp)) Exp
|
||||||
| ExpPrim (Prim Exp)
|
| ExpPrim (Prim Exp)
|
||||||
| ExpBegin (List Exp)
|
| ExpBegin (List Exp)
|
||||||
| ExpIf Exp Exp Exp
|
| ExpIf Exp Exp Exp
|
||||||
@@ -154,12 +169,16 @@ primSexpIso namefn a = match
|
|||||||
$ With (. unop "write")
|
$ With (. unop "write")
|
||||||
$ With (. unop "zero?")
|
$ With (. unop "zero?")
|
||||||
$ With (. nullop "newline")
|
$ With (. nullop "newline")
|
||||||
|
$ With (. mkclosure)
|
||||||
|
$ With (. envref)
|
||||||
$ 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
|
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
|
||||||
|
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
|
||||||
@@ -169,19 +188,10 @@ instance SexpIso Lit where
|
|||||||
sexpIso = match
|
sexpIso = match
|
||||||
$ With (. sexpIso)
|
$ With (. sexpIso)
|
||||||
$ With (. sym "nil")
|
$ With (. sym "nil")
|
||||||
$ With (. bool)
|
$ With (. Gyehoek.Sexp.schemeBool)
|
||||||
$ With (. sexpIso)
|
$ With (. sexpIso)
|
||||||
$ With (. Gyehoek.Sexp.prefixSugar "quote" Sexp.Quote sexpIso)
|
$ With (. Gyehoek.Sexp.prefixSugar "quote" Sexp.Quote sexpIso)
|
||||||
$ End
|
$ End
|
||||||
where
|
|
||||||
bool :: Sexp.SexpGrammar Bool
|
|
||||||
bool = Sexp.hashed $ Sexp.partialOsi f g
|
|
||||||
where
|
|
||||||
f (S.Symbol ("t";"true")) = Right True
|
|
||||||
f (S.Symbol ("f";"false")) = Right False
|
|
||||||
f _ = Left $ Sexp.expected "bool"
|
|
||||||
g True = S.Symbol "true"
|
|
||||||
g False = S.Symbol "false"
|
|
||||||
|
|
||||||
instance SexpIso Sexp where
|
instance SexpIso Sexp where
|
||||||
sexpIso = match
|
sexpIso = match
|
||||||
@@ -203,6 +213,7 @@ 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 (. Gyehoek.Sexp.let_ "let" sexpIso sexpIso sexpIso)
|
||||||
|
$ With (. Gyehoek.Sexp.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_)
|
||||||
@@ -254,3 +265,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))
|
||||||
|
|||||||
@@ -38,10 +38,17 @@ module Gyehoek.Sexp
|
|||||||
, makeSx'
|
, makeSx'
|
||||||
, toSexp
|
, toSexp
|
||||||
, fromSexp
|
, fromSexp
|
||||||
|
, fromSexp'
|
||||||
, stripLocation
|
, stripLocation
|
||||||
, format
|
, format
|
||||||
, equivalent
|
, equivalent
|
||||||
, encodeOrShow
|
, encodeOrShow
|
||||||
|
, readSxs
|
||||||
|
, prismIso
|
||||||
|
, schemeBool
|
||||||
|
, headTagged1'
|
||||||
|
, headTagged1
|
||||||
|
, headTagged2
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@@ -87,6 +94,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 +131,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 +155,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)
|
||||||
@@ -210,12 +243,41 @@ lambda name e = list $
|
|||||||
isoIso :: Iso' s a -> Grammar p (s :- t) (a :- t)
|
isoIso :: Iso' s a -> Grammar p (s :- t) (a :- t)
|
||||||
isoIso l = Sexp.iso (view l) (review l)
|
isoIso l = Sexp.iso (view l) (review l)
|
||||||
|
|
||||||
|
prismIso :: Mismatch -> Prism' s a -> Grammar p (s :- t) (a :- t)
|
||||||
|
prismIso mm p = Sexp.partialOsi
|
||||||
|
(maybe (Left mm) Right . preview p)
|
||||||
|
(review p)
|
||||||
|
|
||||||
kappaKeyword :: Grammar Position (Sexp :- t) t
|
kappaKeyword :: Grammar Position (Sexp :- t) t
|
||||||
kappaKeyword = coproduct [ sym "κ", sym "kappa" ]
|
kappaKeyword = coproduct [ sym "κ", sym "kappa" ]
|
||||||
|
|
||||||
lambdaKeyword :: Grammar Position (Sexp :- t) t
|
lambdaKeyword :: Grammar Position (Sexp :- t) t
|
||||||
lambdaKeyword = coproduct [ sym "λ", sym "lambda" ]
|
lambdaKeyword = coproduct [ sym "λ", sym "lambda" ]
|
||||||
|
|
||||||
|
schemeBool :: SexpGrammar Bool
|
||||||
|
schemeBool = Sexp.hashed $ Sexp.partialOsi f g
|
||||||
|
where
|
||||||
|
f (SL.Symbol ("t";"true")) = Right True
|
||||||
|
f (SL.Symbol ("f";"false")) = Right False
|
||||||
|
f _ = Left $ Sexp.expected "bool"
|
||||||
|
g True = SL.Symbol "true"
|
||||||
|
g False = SL.Symbol "false"
|
||||||
|
|
||||||
|
headTagged1 :: Text -> SexpGrammar a -> Grammar Position (Sexp :- t) (a :- t)
|
||||||
|
headTagged1 s g1 = list $ el (sym s) >>> el g1
|
||||||
|
|
||||||
|
headTagged1'
|
||||||
|
:: Text
|
||||||
|
-> SexpGrammar a -> SexpGrammar b
|
||||||
|
-> Grammar Position (Sexp :- t) (List b :- a :- t)
|
||||||
|
headTagged1' s g1 gt = list $ el (sym s) >>> el g1 >>> rest gt
|
||||||
|
|
||||||
|
headTagged2
|
||||||
|
:: Text
|
||||||
|
-> SexpGrammar a -> SexpGrammar b
|
||||||
|
-> Grammar Position (Sexp :- t) (b :- a :- t)
|
||||||
|
headTagged2 s g1 g2 = list $ el (sym s) >>> el g1 >>> el g2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UglySexpIso a where
|
class UglySexpIso a where
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskellQuotes #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
module Gyehoek.Stack.Syntax
|
||||||
|
( Program(..)
|
||||||
|
, Block(..)
|
||||||
|
, Instr(..)
|
||||||
|
, Val(..)
|
||||||
|
, Lit(..)
|
||||||
|
, Obj(..)
|
||||||
|
, Imm(..)
|
||||||
|
, Prim(..)
|
||||||
|
, Name
|
||||||
|
, pattern ValLabel
|
||||||
|
, encodeProgram
|
||||||
|
) 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(..))
|
||||||
|
import GHC.Exts (IsList(..))
|
||||||
|
import Data.List (intersperse)
|
||||||
|
|
||||||
|
|
||||||
|
newtype Program = MkProgram
|
||||||
|
{ blocks :: List Block
|
||||||
|
}
|
||||||
|
deriving stock (Show, Generic, Data)
|
||||||
|
deriving newtype (Semigroup, Monoid)
|
||||||
|
|
||||||
|
instance IsList Program where
|
||||||
|
type Item Program = Block
|
||||||
|
fromList = MkProgram
|
||||||
|
toList = view #blocks
|
||||||
|
|
||||||
|
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 Val
|
||||||
|
| Prim Name (Prim Val)
|
||||||
|
| Call Val (List Val)
|
||||||
|
| If Val (List Instr) (List Instr)
|
||||||
|
deriving stock (Show, Generic, Data)
|
||||||
|
|
||||||
|
data Val
|
||||||
|
= ValReg Name
|
||||||
|
| ValImm Imm
|
||||||
|
deriving stock (Show, Generic, Data, Eq)
|
||||||
|
|
||||||
|
pattern ValLabel :: Name -> Val
|
||||||
|
pattern ValLabel x = ValImm (ImmLabel x)
|
||||||
|
|
||||||
|
data Imm
|
||||||
|
= ImmInt Int
|
||||||
|
| ImmBool Bool
|
||||||
|
| ImmLabel Name
|
||||||
|
deriving stock (Show, Generic, Data, Eq)
|
||||||
|
|
||||||
|
data Obj
|
||||||
|
= ObjImm Imm
|
||||||
|
deriving (Show, Generic, Data, Eq)
|
||||||
|
|
||||||
|
|
||||||
|
--- sexp work
|
||||||
|
|
||||||
|
pure []
|
||||||
|
|
||||||
|
instance SexpIso Instr where
|
||||||
|
sexpIso = match
|
||||||
|
$ With (Gyehoek.Sexp.headTagged1 "pop!" regName >>>)
|
||||||
|
$ With (Gyehoek.Sexp.headTagged1 "push!" S.sexpIso >>>)
|
||||||
|
$ With (Gyehoek.Sexp.headTagged1 "pop-cont!" regName >>>)
|
||||||
|
$ With (Gyehoek.Sexp.headTagged1 "push-cont!" S.sexpIso >>>)
|
||||||
|
$ With (Gyehoek.Sexp.headTagged2 "prim" regName S.sexpIso >>>)
|
||||||
|
$ With (Gyehoek.Sexp.headTagged1' "call" S.sexpIso S.sexpIso >>>)
|
||||||
|
$ With (if_ >>>)
|
||||||
|
$ End
|
||||||
|
where
|
||||||
|
if_ = S.list $ S.el (S.sym "if")
|
||||||
|
>>> S.el (S.sexpIso @Val)
|
||||||
|
>>> S.el (S.list $ S.el (S.sym "then") >>> S.rest (S.sexpIso @Instr))
|
||||||
|
>>> S.el (S.list $ S.el (S.sym "else") >>> S.rest (S.sexpIso @Instr))
|
||||||
|
|
||||||
|
instance SexpIso Val where
|
||||||
|
sexpIso = match
|
||||||
|
$ With (regName >>>)
|
||||||
|
$ With (S.sexpIso >>>)
|
||||||
|
$ End
|
||||||
|
|
||||||
|
instance SexpIso Imm where
|
||||||
|
sexpIso = match
|
||||||
|
$ With (S.sexpIso @Int >>>)
|
||||||
|
$ With (Gyehoek.Sexp.schemeBool >>>)
|
||||||
|
$ With (labelName >>>)
|
||||||
|
$ End
|
||||||
|
|
||||||
|
instance SexpIso Block where
|
||||||
|
sexpIso = with (block >>>)
|
||||||
|
where
|
||||||
|
block = S.list $
|
||||||
|
S.el (S.sym "define")
|
||||||
|
>>> S.el (S.list $ S.el labelName >>> S.rest regName)
|
||||||
|
>>> S.rest (S.sexpIso @Instr)
|
||||||
|
|
||||||
|
encodeProgram :: Program -> Text
|
||||||
|
encodeProgram p = p.blocks
|
||||||
|
& fmap ((^?! _Right) . Gyehoek.Sexp.encodePretty)
|
||||||
|
& intersperse "\n\n"
|
||||||
|
& mconcat
|
||||||
|
|
||||||
|
regName :: S.SexpGrammar Name
|
||||||
|
regName = S.sexpIso @Name >>> Gyehoek.Sexp.prismIso
|
||||||
|
(S.expected "register")
|
||||||
|
(prefixed @Name "%")
|
||||||
|
|
||||||
|
labelName :: S.SexpGrammar Name
|
||||||
|
labelName = S.sexpIso @Name >>> Gyehoek.Sexp.prismIso
|
||||||
|
(S.expected "label")
|
||||||
|
(prefixed @Name "$")
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
module Gyehoek.Stack.VM
|
||||||
|
( VM(..)
|
||||||
|
, Env(..)
|
||||||
|
, eval
|
||||||
|
, trace
|
||||||
|
, module Gyehoek.Stack.Syntax
|
||||||
|
, writeObj
|
||||||
|
) 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)
|
||||||
|
import Gyehoek.Scheme.Syntax (Sexp(..))
|
||||||
|
import Debug.Pretty.Simple (pTraceShowIdForceColor)
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.Functor (($>))
|
||||||
|
import Data.List (unfoldr)
|
||||||
|
|
||||||
|
|
||||||
|
data VM = MkVM
|
||||||
|
{ stack :: List Obj
|
||||||
|
, kstack :: List Name
|
||||||
|
, code :: List Instr
|
||||||
|
, registers :: HashMap Name Obj
|
||||||
|
, stdout :: Text
|
||||||
|
, result :: Maybe (List Obj)
|
||||||
|
}
|
||||||
|
deriving (Show, Generic)
|
||||||
|
|
||||||
|
data Env = MkEnv
|
||||||
|
{ blocks :: HashMap Name Block
|
||||||
|
}
|
||||||
|
deriving (Show, Generic)
|
||||||
|
|
||||||
|
step :: Env -> VM -> VM
|
||||||
|
step e vm = case vm ^. #code of
|
||||||
|
c:cs -> stepI e (vm & #code .~ cs) c
|
||||||
|
_ -> error "halt never called"
|
||||||
|
|
||||||
|
stepI :: Env -> VM -> Instr -> VM
|
||||||
|
|
||||||
|
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
|
||||||
|
PrimZeroP x -> case x of
|
||||||
|
ObjImm (ImmInt n) -> ret . ObjImm . ImmBool $ n == 0
|
||||||
|
_ -> error [i|bad arg to zero?: #{x}|]
|
||||||
|
PrimAdd x y -> arith_binop (+) x y
|
||||||
|
PrimMul x y -> arith_binop (*) x y
|
||||||
|
PrimSub x y -> arith_binop (-) x y
|
||||||
|
PrimDiv x y -> arith_binop div x y
|
||||||
|
x -> error [i|unimplemented prim: #{p}|]
|
||||||
|
where
|
||||||
|
ret v = vm & #registers . at r ?~ v
|
||||||
|
arith_binop op (ObjImm (ImmInt x)) (ObjImm (ImmInt y)) =
|
||||||
|
ret $ ObjImm (ImmInt (op x y))
|
||||||
|
arith_binop _ x y = error [i|bad arith: #{x}, #{y}|]
|
||||||
|
|
||||||
|
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 ?~ ObjImm (ImmLabel x)
|
||||||
|
& #kstack .~ xs
|
||||||
|
|
||||||
|
stepI e vm (Call v xs) =
|
||||||
|
case evalToLabel e vm v of
|
||||||
|
"halt" -> vm & #result ?~ fmap (evalVal e vm) xs
|
||||||
|
l -> vm & #code .~ b.code
|
||||||
|
& #registers .~ fmap (evalVal e vm) (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 (If c t f) =
|
||||||
|
case evalVal e vm c of
|
||||||
|
ObjImm (ImmBool False) -> vm & #code .~ f
|
||||||
|
_ -> vm & #code .~ t
|
||||||
|
|
||||||
|
stepI e vm ins = error [i|unimplemented instruction: #{ins}|]
|
||||||
|
|
||||||
|
evalToLabel e vm v =
|
||||||
|
case evalVal e vm v of
|
||||||
|
ObjImm (ImmLabel x) -> x
|
||||||
|
x -> error [i|not a label: #{x}|]
|
||||||
|
|
||||||
|
evalVal :: Env -> VM -> Val -> Obj
|
||||||
|
evalVal e vm = \case
|
||||||
|
ValImm imm -> ObjImm imm
|
||||||
|
ValReg r -> case vm ^. #registers . at r of
|
||||||
|
Just x -> x
|
||||||
|
Nothing -> error [i|undefined register: #{r}|]
|
||||||
|
|
||||||
|
initialVM :: VM
|
||||||
|
initialVM = MkVM
|
||||||
|
{ stack = []
|
||||||
|
, kstack = ["halt"]
|
||||||
|
, code = [Call (ValImm $ ImmLabel "main") []]
|
||||||
|
, registers = mempty
|
||||||
|
, stdout = ""
|
||||||
|
, result = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Obj
|
||||||
|
eval p = initialVM & loop \vm -> case vm ^. #result of
|
||||||
|
Nothing -> Right $ step (initialEnv p) vm
|
||||||
|
Just rs -> Left rs
|
||||||
|
|
||||||
|
trace :: Program -> List VM
|
||||||
|
trace p = initialVM & unfoldr \vm ->
|
||||||
|
case vm.result of
|
||||||
|
Just _ -> Nothing
|
||||||
|
Nothing -> Just (vm, step e vm)
|
||||||
|
where e = initialEnv p
|
||||||
|
|
||||||
|
writeObj :: Obj -> Text
|
||||||
|
writeObj (ObjImm im) = case im of
|
||||||
|
ImmInt n -> [i|#{n}|]
|
||||||
|
ImmBool True -> "#t"
|
||||||
|
ImmBool False -> "#f"
|
||||||
|
ImmLabel l -> "#<procedure>"
|
||||||
@@ -1 +1,21 @@
|
|||||||
(values 1 2)
|
(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))
|
||||||
|
|||||||
@@ -22,38 +22,60 @@
|
|||||||
$cont-stack
|
$cont-stack
|
||||||
(ref $cont-stack-type)
|
(ref $cont-stack-type)
|
||||||
(array.new_default $cont-stack-type (i32.const 128)))
|
(array.new_default $cont-stack-type (i32.const 128)))
|
||||||
(type $arg-array-type (array (mut (ref null eq))))
|
(global $arg0 (mut (ref null eq)) (ref.null eq))
|
||||||
(global
|
(global $arg1 (mut (ref null eq)) (ref.null eq))
|
||||||
$arg-array
|
(global $arg2 (mut (ref null eq)) (ref.null eq))
|
||||||
(ref $arg-array-type)
|
(global $arg3 (mut (ref null eq)) (ref.null eq))
|
||||||
(array.new_default $arg-array-type (i32.const 32)))
|
(global $arg4 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg5 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg6 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg7 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg8 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg9 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg10 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg11 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg12 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg13 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg14 (mut (ref null eq)) (ref.null eq))
|
||||||
|
(global $arg15 (mut (ref null eq)) (ref.null eq))
|
||||||
(global $result (mut (ref null eq)) (ref.null eq))
|
(global $result (mut (ref null eq)) (ref.null eq))
|
||||||
(func
|
(func
|
||||||
$halt
|
$halt
|
||||||
(param i32)
|
(param i32)
|
||||||
(@gyehoek "pop argument")
|
(@gyehoek begin popArg)
|
||||||
(global.get $arg-array)
|
(global.get $arg0)
|
||||||
(i32.const 0)
|
|
||||||
(array.get $arg-array-type)
|
|
||||||
ref.as_non_null
|
ref.as_non_null
|
||||||
|
(@gyehoek end popArg)
|
||||||
(global.set $result))
|
(global.set $result))
|
||||||
(func
|
(func
|
||||||
(param i32)
|
(param i32)
|
||||||
(@gyehoek :origin "(κ (x5) (continue λ-tail1 x5))")
|
(@gyehoek
|
||||||
|
:origin
|
||||||
|
"(λ (x λ-tail1) (prim (* x x) (κ (r2) (continue λ-tail1 r2))))")
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
||||||
(@gyehoek "pop argument")
|
(@gyehoek
|
||||||
(global.get $arg-array)
|
:origin
|
||||||
(i32.const 0)
|
"(prim (* x x) (κ (r2) (continue λ-tail1 r2)))")
|
||||||
(array.get $arg-array-type)
|
(global.get $arg1)
|
||||||
ref.as_non_null
|
(i31.get_s (ref.cast (ref i31)))
|
||||||
(local.set 1)
|
(i32.const 1)
|
||||||
(@gyehoek :origin "(continue λ-tail1 x5)")
|
i32.shr_u
|
||||||
|
(global.get $arg1)
|
||||||
|
(i31.get_s (ref.cast (ref i31)))
|
||||||
|
(i32.const 1)
|
||||||
|
i32.shr_u
|
||||||
|
i32.mul
|
||||||
|
(@gyehoek "construct small fixnum")
|
||||||
|
(i32.const 1)
|
||||||
|
i32.shl
|
||||||
|
ref.i31
|
||||||
|
(global.set $arg2)
|
||||||
|
(@gyehoek :origin "(continue λ-tail1 r2)")
|
||||||
(@gyehoek "push args")
|
(@gyehoek "push args")
|
||||||
(@gyehoek "push argument")
|
(@gyehoek begin pushArg)
|
||||||
(global.get $arg-array)
|
(global.get $arg2)
|
||||||
(i32.const 0)
|
(global.set $arg0)
|
||||||
(local.get 4)
|
(@gyehoek end pushArg)
|
||||||
(array.set $arg-array-type)
|
|
||||||
(@gyehoek "nargs")
|
(@gyehoek "nargs")
|
||||||
(i32.const 1)
|
(i32.const 1)
|
||||||
(@gyehoek "pop cont stack")
|
(@gyehoek "pop cont stack")
|
||||||
@@ -69,58 +91,26 @@
|
|||||||
(elem declare funcref (ref.func 3))
|
(elem declare funcref (ref.func 3))
|
||||||
(func
|
(func
|
||||||
(param i32)
|
(param i32)
|
||||||
(@gyehoek
|
(@gyehoek :origin "(κ (x4) (continue halt x4))")
|
||||||
:origin
|
|
||||||
"(κ (x3) (letrec ((r4 (κ (x5) (continue λ-tail1 x5)))) (f x3 r4)))")
|
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
||||||
(@gyehoek "pop argument")
|
(@gyehoek begin pushArg)
|
||||||
(global.get $arg-array)
|
(global.get $arg2)
|
||||||
(i32.const 0)
|
(global.set $arg0)
|
||||||
(array.get $arg-array-type)
|
(@gyehoek end pushArg)
|
||||||
ref.as_non_null
|
(return_call $halt (i32.const 1)))
|
||||||
(local.set 1)
|
|
||||||
(@gyehoek :origin "(f x3 r4)")
|
|
||||||
(@gyehoek "push cont" :idx 3)
|
|
||||||
(array.set
|
|
||||||
$cont-stack-type
|
|
||||||
(global.get $cont-stack)
|
|
||||||
(global.get $cont-stack-top)
|
|
||||||
(ref.func 3))
|
|
||||||
(global.set
|
|
||||||
$cont-stack-top
|
|
||||||
(i32.add (global.get $cont-stack-top) (i32.const 1)))
|
|
||||||
(@gyehoek :origin "(f x3 r4)")
|
|
||||||
(@gyehoek "load args")
|
|
||||||
(@gyehoek "push argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(local.get 3)
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(i32.const 1)
|
|
||||||
(local.get 1)
|
|
||||||
(ref.cast (ref $closure))
|
|
||||||
(struct.get $closure $code)
|
|
||||||
(return_call_ref $cont-type))
|
|
||||||
(elem declare funcref (ref.func 4))
|
(elem declare funcref (ref.func 4))
|
||||||
(func
|
(func
|
||||||
|
$scm-entry
|
||||||
(param i32)
|
(param i32)
|
||||||
(@gyehoek
|
(@gyehoek
|
||||||
:origin
|
:origin
|
||||||
"(λ (f x λ-tail1) (letrec ((r2 (κ (x3) (letrec ((r4 (κ (x5) (continue λ-tail1 x5)))) (f x3 r4))))) (f x r2)))")
|
"(letrec ((λ-body0 (λ (x λ-tail1) (prim (* x x) (κ (r2) (continue λ-tail1 r2)))))) (letrec ((r3 (κ (x4) (continue halt x4)))) (λ-body0 5 r3)))")
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
||||||
(@gyehoek "pop argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
(i32.const 0)
|
||||||
(array.get $arg-array-type)
|
(ref.func 3)
|
||||||
ref.as_non_null
|
(struct.new $closure)
|
||||||
(local.set 1)
|
(global.set $arg1)
|
||||||
(@gyehoek "pop argument")
|
(@gyehoek :origin "(λ-body0 5 r3)")
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 1)
|
|
||||||
(array.get $arg-array-type)
|
|
||||||
ref.as_non_null
|
|
||||||
(local.set 2)
|
|
||||||
(@gyehoek :origin "(f x r2)")
|
|
||||||
(@gyehoek "push cont" :idx 4)
|
(@gyehoek "push cont" :idx 4)
|
||||||
(array.set
|
(array.set
|
||||||
$cont-stack-type
|
$cont-stack-type
|
||||||
@@ -130,135 +120,22 @@
|
|||||||
(global.set
|
(global.set
|
||||||
$cont-stack-top
|
$cont-stack-top
|
||||||
(i32.add (global.get $cont-stack-top) (i32.const 1)))
|
(i32.add (global.get $cont-stack-top) (i32.const 1)))
|
||||||
(@gyehoek :origin "(f x r2)")
|
(@gyehoek :origin "(λ-body0 5 r3)")
|
||||||
(@gyehoek "load args")
|
(@gyehoek "load args")
|
||||||
(@gyehoek "push argument")
|
(@gyehoek begin pushArg)
|
||||||
(global.get $arg-array)
|
(i32.const 5)
|
||||||
(i32.const 0)
|
(@gyehoek "construct small fixnum")
|
||||||
(local.get 2)
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(i32.const 1)
|
(i32.const 1)
|
||||||
(local.get 1)
|
i32.shl
|
||||||
|
ref.i31
|
||||||
|
(global.set $arg0)
|
||||||
|
(@gyehoek end pushArg)
|
||||||
|
(i32.const 1)
|
||||||
|
(global.get $arg1)
|
||||||
(ref.cast (ref $closure))
|
(ref.cast (ref $closure))
|
||||||
(struct.get $closure $code)
|
(struct.get $closure $code)
|
||||||
(return_call_ref $cont-type))
|
(return_call_ref $cont-type)
|
||||||
(elem declare funcref (ref.func 5))
|
(@gyehoek todo (f' (global.get $arg1)) (ktail 1)))
|
||||||
(func
|
|
||||||
(param i32)
|
|
||||||
(@gyehoek
|
|
||||||
:origin
|
|
||||||
"(λ (x λ-tail7) (prim (+ x 4) (κ (r8) (continue λ-tail7 r8))))")
|
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
|
||||||
(@gyehoek "pop argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(array.get $arg-array-type)
|
|
||||||
ref.as_non_null
|
|
||||||
(local.set 1)
|
|
||||||
(@gyehoek
|
|
||||||
:origin
|
|
||||||
"(prim (+ x 4) (κ (r8) (continue λ-tail7 r8)))")
|
|
||||||
(local.get 1)
|
|
||||||
(i31.get_s (ref.cast (ref i31)))
|
|
||||||
(i32.const 1)
|
|
||||||
i32.shr_u
|
|
||||||
(i32.const 4)
|
|
||||||
(@gyehoek "construct small fixnum")
|
|
||||||
(i32.const 1)
|
|
||||||
i32.shl
|
|
||||||
ref.i31
|
|
||||||
(i31.get_s (ref.cast (ref i31)))
|
|
||||||
(i32.const 1)
|
|
||||||
i32.shr_u
|
|
||||||
i32.add
|
|
||||||
(@gyehoek "construct small fixnum")
|
|
||||||
(i32.const 1)
|
|
||||||
i32.shl
|
|
||||||
ref.i31
|
|
||||||
(local.set 2)
|
|
||||||
(@gyehoek :origin "(continue λ-tail7 r8)")
|
|
||||||
(@gyehoek "push args")
|
|
||||||
(@gyehoek "push argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(local.get 2)
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(@gyehoek "nargs")
|
|
||||||
(i32.const 1)
|
|
||||||
(@gyehoek "pop cont stack")
|
|
||||||
(global.get $cont-stack-top)
|
|
||||||
(i32.const 1)
|
|
||||||
i32.sub
|
|
||||||
(global.set $cont-stack-top)
|
|
||||||
(global.get $cont-stack)
|
|
||||||
(global.get $cont-stack-top)
|
|
||||||
(array.get $cont-stack-type)
|
|
||||||
ref.as_non_null
|
|
||||||
(return_call_ref $cont-type))
|
|
||||||
(elem declare funcref (ref.func 6))
|
|
||||||
(func
|
|
||||||
(param i32)
|
|
||||||
(@gyehoek :origin "(κ (x10) (continue halt x10))")
|
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
|
||||||
(@gyehoek "pop argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(array.get $arg-array-type)
|
|
||||||
ref.as_non_null
|
|
||||||
(local.set 1)
|
|
||||||
(@gyehoek "push argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(local.get 3)
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(return_call $halt (i32.const 1)))
|
|
||||||
(elem declare funcref (ref.func 7))
|
|
||||||
(func
|
|
||||||
$scm-entry
|
|
||||||
(param i32)
|
|
||||||
(@gyehoek
|
|
||||||
:origin
|
|
||||||
"(letrec ((λ-body0 (λ (f x λ-tail1) (letrec ((r2 (κ (x3) (letrec ((r4 (κ (x5) (continue λ-tail1 x5)))) (f x3 r4))))) (f x r2))))) (letrec ((λ-body6 (λ (x λ-tail7) (prim (+ x 4) (κ (r8) (continue λ-tail7 r8)))))) (letrec ((r9 (κ (x10) (continue halt x10)))) (λ-body0 λ-body6 9 r9))))")
|
|
||||||
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
|
|
||||||
(i32.const 0)
|
|
||||||
(ref.func 5)
|
|
||||||
(struct.new $closure)
|
|
||||||
(local.set 1)
|
|
||||||
(i32.const 0)
|
|
||||||
(ref.func 6)
|
|
||||||
(struct.new $closure)
|
|
||||||
(local.set 2)
|
|
||||||
(@gyehoek :origin "(λ-body0 λ-body6 9 r9)")
|
|
||||||
(@gyehoek "push cont" :idx 7)
|
|
||||||
(array.set
|
|
||||||
$cont-stack-type
|
|
||||||
(global.get $cont-stack)
|
|
||||||
(global.get $cont-stack-top)
|
|
||||||
(ref.func 7))
|
|
||||||
(global.set
|
|
||||||
$cont-stack-top
|
|
||||||
(i32.add (global.get $cont-stack-top) (i32.const 1)))
|
|
||||||
(@gyehoek :origin "(λ-body0 λ-body6 9 r9)")
|
|
||||||
(@gyehoek "load args")
|
|
||||||
(@gyehoek "push argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 0)
|
|
||||||
(local.get 2)
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(@gyehoek "push argument")
|
|
||||||
(global.get $arg-array)
|
|
||||||
(i32.const 1)
|
|
||||||
(i32.const 9)
|
|
||||||
(@gyehoek "construct small fixnum")
|
|
||||||
(i32.const 1)
|
|
||||||
i32.shl
|
|
||||||
ref.i31
|
|
||||||
(array.set $arg-array-type)
|
|
||||||
(i32.const 1)
|
|
||||||
(local.get 1)
|
|
||||||
(ref.cast (ref $closure))
|
|
||||||
(struct.get $closure $code)
|
|
||||||
(return_call_ref $cont-type))
|
|
||||||
(func
|
(func
|
||||||
(export "main")
|
(export "main")
|
||||||
(call $scm-entry (i32.const 0))
|
(call $scm-entry (i32.const 0))
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
module Gyehoek.Test.CPS.Stackify (root) where
|
||||||
|
|
||||||
|
import Test.Tasty (TestTree, testGroup)
|
||||||
|
import Test.Tasty.HUnit
|
||||||
|
import qualified Gyehoek.CPS.Stackify as Sut
|
||||||
|
import Gyehoek.Stack.VM as Stk
|
||||||
|
import Data.List (List)
|
||||||
|
import Gyehoek.CPS.Syntax (cps)
|
||||||
|
import Gyehoek.GenSym (runGenSym)
|
||||||
|
import Effectful
|
||||||
|
import Test.Tasty.ExpectedFailure (expectFail)
|
||||||
|
|
||||||
|
|
||||||
|
root :: IO TestTree
|
||||||
|
root = pure . testGroup "stackify" $
|
||||||
|
[ trivialReturn
|
||||||
|
, tailCall
|
||||||
|
, prim
|
||||||
|
, condition
|
||||||
|
, procedure
|
||||||
|
]
|
||||||
|
|
||||||
|
evalsTo :: List Obj -> Sut.Exp -> Assertion
|
||||||
|
evalsTo rs e =
|
||||||
|
Stk.eval e' @?= rs
|
||||||
|
where e' = runPureEff . runGenSym $ Sut.stackifyExp "main" e
|
||||||
|
|
||||||
|
trivialReturn = testGroup "trivial return"
|
||||||
|
[ testCase "return int" do
|
||||||
|
evalsTo [ObjImm (ImmInt 4)]
|
||||||
|
[cps|(continue halt 4)|]
|
||||||
|
, testCase "return bool" do
|
||||||
|
evalsTo [ObjImm (ImmBool True)]
|
||||||
|
[cps|(continue halt #t)|]
|
||||||
|
evalsTo [ObjImm (ImmBool False)]
|
||||||
|
[cps|(continue halt #f)|]
|
||||||
|
]
|
||||||
|
|
||||||
|
tailCall = testGroup "tail call"
|
||||||
|
[ testCase "square" do
|
||||||
|
evalsTo [ObjImm (ImmInt 16)]
|
||||||
|
[cps|(letrec ((square (λ (x ktail)
|
||||||
|
(prim (* x x)
|
||||||
|
(κ (x0) (continue ktail x0))))))
|
||||||
|
(square 4 halt))|]
|
||||||
|
]
|
||||||
|
|
||||||
|
prim = testGroup "prim"
|
||||||
|
[ testCase "multiply" do
|
||||||
|
evalsTo [ObjImm (ImmInt 20)]
|
||||||
|
[cps|(prim (* 4 5)
|
||||||
|
(κ (x) (continue halt x)))|]
|
||||||
|
, testCase "add" do
|
||||||
|
evalsTo [ObjImm (ImmInt 9)]
|
||||||
|
[cps|(prim (+ 4 5)
|
||||||
|
(κ (x) (continue halt x)))|]
|
||||||
|
]
|
||||||
|
|
||||||
|
condition = testCase "if" do
|
||||||
|
evalsTo [ObjImm (ImmInt 123)]
|
||||||
|
[cps|(if #t (continue halt 123) (continue halt 456))|]
|
||||||
|
evalsTo [ObjImm (ImmInt 456)]
|
||||||
|
[cps|(if #f (continue halt 123) (continue halt 456))|]
|
||||||
|
|
||||||
|
procedure = testGroup "procedure"
|
||||||
|
[ testCase "factorial" do
|
||||||
|
evalsTo [ObjImm (ImmInt 720)]
|
||||||
|
[cps|(letrec ((fac (λ (n ktail)
|
||||||
|
(prim (zero? n)
|
||||||
|
(κ (x0)
|
||||||
|
(if x0
|
||||||
|
(continue ktail 1)
|
||||||
|
(prim (- n 1)
|
||||||
|
(κ (x1)
|
||||||
|
(letrec ((fac-k0
|
||||||
|
(κ (x2)
|
||||||
|
(prim (* n x2)
|
||||||
|
(κ (x3)
|
||||||
|
(continue ktail x3))))))
|
||||||
|
(fac x1 fac-k0))))))))))
|
||||||
|
(fac 6 halt))|]
|
||||||
|
]
|
||||||
@@ -18,15 +18,17 @@ root = pure . testGroup "cps syntax" $
|
|||||||
]
|
]
|
||||||
|
|
||||||
freeTree :: TestTree
|
freeTree :: TestTree
|
||||||
freeTree = testCase "free" do
|
freeTree = testGroup "free"
|
||||||
Sut.free [cps|
|
[ testCase "lambda" do
|
||||||
(letrec ((x (lambda (r k1) (continue k1 y)))
|
Sut.free' @Sut.Lambda [cps|
|
||||||
(y (lambda (r k2) (continue k2 x))))
|
(lambda (x y z k1) (continue k1 x a b c y))
|
||||||
(continue x y k3))|] @=? ["k3"]
|
|] @=? ["a","b","c"]
|
||||||
Sut.free' [cps|
|
, testCase "exp" do
|
||||||
(letrec ((x (lambda (r k1) (continue k1 y)))
|
Sut.free' @Sut.Exp [cps|
|
||||||
(y (lambda (r k2) (continue k2 x))))
|
(letrec ((x (lambda (r k1) (continue k1 y)))
|
||||||
(continue x y k3))|] @=? ["k3"]
|
(y (lambda (r k2) (continue k2 x))))
|
||||||
|
(continue x y k3))|] @=? ["k3"]
|
||||||
|
]
|
||||||
|
|
||||||
qqTree :: TestTree
|
qqTree :: TestTree
|
||||||
qqTree = testGroup "parser"
|
qqTree = testGroup "parser"
|
||||||
|
|||||||
@@ -10,35 +10,77 @@ import System.Directory
|
|||||||
import Data.Function
|
import Data.Function
|
||||||
import System.Environment.Blank (getEnvDefault)
|
import System.Environment.Blank (getEnvDefault)
|
||||||
import qualified System.Process.Text as PT
|
import qualified System.Process.Text as PT
|
||||||
|
import Control.Exception (catches, ErrorCall(..), Handler(..))
|
||||||
|
import Gyehoek.Stack.VM (writeObj)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import System.Exit (ExitCode(..))
|
||||||
|
import Test.Tasty.ExpectedFailure (expectFail)
|
||||||
|
|
||||||
|
|
||||||
disabled :: List String
|
brokenWasmTests :: List String
|
||||||
disabled =
|
brokenWasmTests =
|
||||||
[
|
[ "adder"
|
||||||
|
, "apply-twice"
|
||||||
|
, "square"
|
||||||
|
, "fn-of-fn"
|
||||||
|
, "let-fn"
|
||||||
|
, "apply2"
|
||||||
|
, "factorial"
|
||||||
|
]
|
||||||
|
|
||||||
|
brokenStackifyTests :: List String
|
||||||
|
brokenStackifyTests =
|
||||||
|
[ "apply-twice"
|
||||||
|
, "adder"
|
||||||
|
, "apply2"
|
||||||
|
, "let-fn"
|
||||||
]
|
]
|
||||||
|
|
||||||
root :: IO TestTree
|
root :: IO TestTree
|
||||||
root = do
|
root = do
|
||||||
all_cases <- listDirectory "golden"
|
all_cases <- listDirectory "golden"
|
||||||
let tests = all_cases
|
let tests = all_cases
|
||||||
& filter (`notElem` disabled)
|
|
||||||
& fmap ("golden"</>)
|
& fmap ("golden"</>)
|
||||||
testGroup "golden" <$> sequenceA
|
testGroup "golden" <$> sequenceA
|
||||||
[ executionTests tests
|
[ wasmTests tests
|
||||||
|
, stackifyTests tests
|
||||||
]
|
]
|
||||||
|
|
||||||
executionTests :: List FilePath -> IO TestTree
|
maybeBroken name broken = applyWhen (name `elem` broken) expectFail
|
||||||
executionTests files = do
|
wasmTests :: List FilePath -> IO TestTree
|
||||||
|
wasmTests files = do
|
||||||
cmd <- getEnvDefault "GYEHOEK_RUNTIME"
|
cmd <- getEnvDefault "GYEHOEK_RUNTIME"
|
||||||
"runtime/target/debug/gyehoek-runtime"
|
"runtime/target/debug/gyehoek-runtime"
|
||||||
pure $ testGroup "execution" $ files <&> \test ->
|
pure $ testGroup "wasm execution" $ files <&> \test ->
|
||||||
let testname = takeFileName test
|
let testname = takeFileName test
|
||||||
scmfile = test </> "source.scm"
|
scmfile = test </> "source.scm"
|
||||||
resultfile = test </> "exec"
|
resultfile = test </> "exec"
|
||||||
action = do
|
action = do
|
||||||
t <- Driver.lower_e2e scmfile
|
t <- Driver.lower_e2e scmfile
|
||||||
PT.readProcessWithExitCode cmd ["-"] t
|
PT.readProcessWithExitCode cmd ["-"] t
|
||||||
in goldenVsAction
|
in maybeBroken testname brokenWasmTests $
|
||||||
|
goldenVsAction
|
||||||
|
testname
|
||||||
|
resultfile
|
||||||
|
action
|
||||||
|
printProcResult
|
||||||
|
|
||||||
|
stackifyTests :: List FilePath -> IO TestTree
|
||||||
|
stackifyTests files = do
|
||||||
|
pure $ testGroup "stackified execution" $ files <&> \test ->
|
||||||
|
let testname = takeFileName test
|
||||||
|
scmfile = test </> "source.scm"
|
||||||
|
resultfile = test </> "exec"
|
||||||
|
action =
|
||||||
|
catches (do rs <- Driver.eval_e2e scmfile
|
||||||
|
pure ( ExitSuccess
|
||||||
|
, T.unwords . fmap writeObj $ rs
|
||||||
|
, "" ))
|
||||||
|
[ Handler \(ErrorCall s) ->
|
||||||
|
pure (ExitFailure 1, "", T.pack s)
|
||||||
|
]
|
||||||
|
in maybeBroken testname brokenStackifyTests $
|
||||||
|
goldenVsAction
|
||||||
testname
|
testname
|
||||||
resultfile
|
resultfile
|
||||||
action
|
action
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
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" $
|
||||||
|
[ lit_int
|
||||||
|
, procedure
|
||||||
|
, prims
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
evalsTo :: List Obj -> List Block -> Assertion
|
||||||
|
evalsTo rs bs = Sut.eval (MkProgram bs) @?= rs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lit_int = testCase "lit int" do
|
||||||
|
evalsTo [ObjImm (ImmInt 3)]
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Call (ValReg "ktail") [ValImm (ImmInt 3)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
vlb = ValImm . ImmLabel
|
||||||
|
|
||||||
|
procedure = testGroup "procedure"
|
||||||
|
[ testCase "return constant" do
|
||||||
|
evalsTo [ObjImm (ImmInt 123)]
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ Call (ValLabel "silly") []
|
||||||
|
]
|
||||||
|
, MkBlock "silly" []
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Call (ValReg "ktail") [ValImm (ImmInt 123)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, testCase "identity function" do
|
||||||
|
evalsTo [ObjImm (ImmInt 45)]
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ Call (ValLabel "id") [ValImm (ImmInt 45)]
|
||||||
|
]
|
||||||
|
, MkBlock "id" ["x"]
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Call (ValReg "ktail") [ValReg "x"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, testCase "square" do
|
||||||
|
evalsTo [ObjImm (ImmInt 16)]
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ Call (ValLabel "square") [ValImm (ImmInt 4)]
|
||||||
|
]
|
||||||
|
, MkBlock "square" ["x"]
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Prim "x2" $ PrimMul (ValReg "x") (ValReg "x")
|
||||||
|
, Call (ValReg "ktail") [ValReg "x2"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, testCase "factorial" do
|
||||||
|
let fac =
|
||||||
|
[ MkBlock "fac" ["n"]
|
||||||
|
[ Prim "x0" $ PrimZeroP (ValReg "n")
|
||||||
|
, If (ValReg "x0")
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Call (ValReg "ktail") [ValImm (ImmInt 1)]
|
||||||
|
]
|
||||||
|
[ Push (ValReg "n")
|
||||||
|
, Prim "x1" $ PrimSub (ValReg "n") (ValImm (ImmInt 1))
|
||||||
|
, PushCont (ValLabel "fac-k0")
|
||||||
|
, Call (ValLabel "fac") [ValReg "x1"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, MkBlock "fac-k0" ["x2"]
|
||||||
|
[ Pop "n"
|
||||||
|
, Prim "x3" $ PrimMul (ValReg "x2") (ValReg "n")
|
||||||
|
, PopCont "ktail"
|
||||||
|
, Call (ValReg "ktail") [ValReg "x3"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
evalsTo [ObjImm (ImmInt 1)] $
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ Call (ValLabel "fac") [ValImm (ImmInt 0)]
|
||||||
|
]
|
||||||
|
] ++ fac
|
||||||
|
evalsTo [ObjImm (ImmInt 720)] $
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ Call (ValLabel "fac") [ValImm (ImmInt 6)]
|
||||||
|
]
|
||||||
|
] ++ fac
|
||||||
|
]
|
||||||
|
|
||||||
|
prims = testGroup "prims"
|
||||||
|
[ arith
|
||||||
|
, testCase "zero?" do
|
||||||
|
trivialPrimTest [ObjImm (ImmBool True)] $
|
||||||
|
PrimZeroP $ ValImm $ ImmInt 0
|
||||||
|
trivialPrimTest [ObjImm (ImmBool False)] $
|
||||||
|
PrimZeroP $ ValImm $ ImmInt 12
|
||||||
|
]
|
||||||
|
|
||||||
|
trivialPrimTest rs p =
|
||||||
|
evalsTo rs
|
||||||
|
[ MkBlock "main" []
|
||||||
|
[ PopCont "ktail"
|
||||||
|
, Prim "x1" p
|
||||||
|
, Call (ValReg "ktail") [ValReg "x1"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
arith = testGroup "arith"
|
||||||
|
[ testCase "multipy" do
|
||||||
|
trivialPrimTest [ObjImm (ImmInt 12)]
|
||||||
|
(PrimMul (ValImm $ ImmInt 3) (ValImm $ ImmInt 4))
|
||||||
|
, testCase "subtract" do
|
||||||
|
trivialPrimTest [ObjImm (ImmInt 14)]
|
||||||
|
(PrimSub (ValImm $ ImmInt 20) (ValImm $ ImmInt 6))
|
||||||
|
]
|
||||||
@@ -5,6 +5,8 @@ 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
|
||||||
|
import qualified Gyehoek.Test.CPS.Stackify
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
@@ -15,5 +17,7 @@ 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
|
||||||
|
, Gyehoek.Test.CPS.Stackify.root
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user