41 Commits
Author SHA1 Message Date
msyds b234a52d4b wip: printer
build / build (push) Successful in 1m33s
2026-08-21 15:01:31 -06:00
msyds 6ff01a8607 parse lists
build / build (push) Successful in 1m15s
2026-08-21 02:25:58 -06:00
msyds 5200932944 wip: reader
build / build (push) Successful in 1m19s
2026-08-21 01:07:26 -06:00
msyds f9ed1274d9 r7rs datum ast
build / build (push) Successful in 1m13s
2026-08-20 22:05:57 -06:00
msyds 6c29f3779a fix completions
build / build (push) Successful in 1m12s
2026-08-20 18:45:22 -06:00
msyds 5a173a7a3b fix dir-locals hooks 2026-08-20 18:20:11 -06:00
msyds b329a42b71 remove CPS.ValLit
build / build (push) Successful in 40s
2026-08-20 18:14:46 -06:00
msyds 25ba8c03b8 tasty-discover 2026-08-20 17:21:42 -06:00
msyds fc8cf263aa cleanup
build / build (push) Successful in 1m32s
2026-08-20 05:32:21 -06:00
msyds c5f9bf1850 cli, cps interpreter, stack vm, closure-conversion, fixes, tests, LOL
build / build (push) Successful in 7m49s
2026-08-20 01:05:16 -06:00
msyds 94b1a5fb45 enable some working tests
build / build (push) Successful in 1m13s
2026-08-19 00:13:06 -06:00
msyds ca1b53f3d1 fix catching of exceptions in tests 2026-08-18 23:56:22 -06:00
msyds eb51f4fff7 call/cc 2026-08-18 23:13:32 -06:00
msyds 8bdbfafb9c fix some warnings 2026-08-18 21:14:50 -06:00
msyds 6949ff7fdf convert (limited) letrec
build / build (push) Successful in 20s
2026-08-18 20:38:17 -06:00
msyds a73b3ed89b correctly handle push-calls!!!!!!!!
build / build (push) Successful in 1m24s
2026-08-18 19:25:18 -06:00
msyds 1c13de4153 more stackification
build / build (push) Successful in 1m15s
2026-08-18 16:13:44 -06:00
msyds d91e059a84 wip: stackify
build / build (push) Successful in 1m30s
2026-08-18 02:27:02 -06:00
msyds c4bcf38374 factorial
build / build (push) Successful in 1m9s
2026-08-17 23:44:35 -06:00
msyds 745277ed1a wip: abstract stack/continuation machine
build / build (push) Successful in 1m12s
2026-08-17 22:31:45 -06:00
msyds c3c4866fa8 idk
build / build (push) Failing after 51s
2026-08-06 21:07:33 -06:00
msyds 80164acb96 doc
build / build (push) Failing after 1m25s
2026-07-22 23:48:40 -06:00
msyds 1c7322614c fart 2026-07-22 23:48:02 -06:00
msyds 81a136fcf2 inspect-wasm 2026-07-22 12:22:41 -06:00
msyds be1d7566f4 idk ^w^
build / build (push) Failing after 1m10s
2026-07-20 13:09:59 -06:00
msyds fab29f6fce higher-order
build / build (push) Failing after 1m9s
2026-07-20 05:56:32 -06:00
msyds 7ab98341b9 appy
build / build (push) Successful in 1m11s
2026-07-20 05:33:23 -06:00
msyds 2f471ae4b1 idk ^w^
build / build (push) Failing after 14m11s
2026-07-20 01:56:53 -06:00
msyds 57defed077 bool tests
build / build (push) Successful in 1m5s
2026-07-19 03:29:51 -06:00
msyds 0ba49ed85c fix all haskell warnings (sigh)
build / build (push) Successful in 1m8s
2026-07-19 03:26:36 -06:00
msyds 33fb0f831c tests again
build / build (push) Successful in 1m0s
2026-07-19 03:11:59 -06:00
msyds 8120e21eae remove wat tests
build / build (push) Successful in 1m4s
2026-07-18 17:54:35 -06:00
msyds 6774c08efb lambda and if-number }:3
build / build (push) Successful in 38s
2026-07-18 17:12:55 -06:00
msyds 530a6934ba crane
build / build (push) Successful in 30s
2026-07-18 03:06:18 -06:00
msyds e2e287079c deyuck
build / build (push) Failing after 13m6s
2026-07-18 02:50:13 -06:00
msyds a97a0ad7bb fix tests }:3
build / build (push) Successful in 4m52s
2026-07-18 02:49:34 -06:00
msyds 9334373f96 "fix stuff lol" 2026-07-18 01:50:43 -06:00
msyds aa5b45ec76 rust runtime
build / build (push) Failing after 1m1s
2026-07-17 23:17:38 -06:00
msyds 85d34883a6 beautiful
build / build (push) Failing after 1m1s
2026-07-17 01:34:13 -06:00
msyds f09a63f11c top-level unquote-splice
build / build (push) Failing after 1m23s
2026-07-17 00:46:43 -06:00
msyds 2dffdf112c qq!
build / build (push) Failing after 1m15s
2026-07-16 14:16:01 -06:00
127 changed files with 5827 additions and 965 deletions
+7 -1
View File
@@ -1,4 +1,10 @@
((haskell-cabal-mode
((haskell-mode
. ((eval
. (progn (add-to-list 'haskell-font-lock-quasi-quote-modes
'("cps" . scheme-mode))
(add-to-list 'haskell-font-lock-quasi-quote-modes
'("scm" . scheme-mode))))))
(haskell-cabal-mode
. ((eval
. (progn (defun apply-cabal-fmt-h ()
(haskell-mode-buffer-apply-command "cabal-fmt"))
+2 -2
View File
@@ -1,3 +1,3 @@
# gyehoek-hs (계획)
# 계획
a (wip) toy compiler for a Scheme-like language. currently targetting [QBE](https://c9x.me/compile/). nabbing from GHC and GNU Guile.
a WIP compiler for R⁷RS Scheme targeting WebAssembly.
+218
View File
@@ -0,0 +1,218 @@
#+title: ABI
largely based on the Guile Hoot's [[https://codeberg.org/spritely/hoot/src/branch/main/design/ABI.md][ABI]].
* calling convention
** non-tail calls
- set the global variable ~$current-closure~ to the callee's closure.
- load arguments into globals ~$arg0~, ~$arg1~, ~$arg2~, …
- push return continuation onto ~$cont-stack~
* scratchpad
#+begin_src scheme
;; Scheme source
(define (silly f g h x)
(f (h x) (g x)))
;; continuation-passing style
(define (silly f g h x ktail)
(h x (κ (x0)
(g x (κ (x1)
(f x0 x1 ktail))))))
;; with explicit stacks
(define (silly)
(define f (pop!))
(define g (pop!))
(define h (pop!))
(define x (pop!))
(define ktail (pop-cont!))
(push-cont! (κ (x0)
(define x* (pop!))
(define g* (pop!))
(push-cont! (κ (x1)
(define f* (pop!))
(define x0* (pop!))
(push-cont! ktail)
(push! x0*)
(push! x1)
(call! f)))
(push! x*)
(call! g)))
(push! x)
(call! h))
#+end_src
** fac
*** Scheme source
#+begin_src scheme
(define fac
(λ (n)
(if (zero? n)
1
(* n (fac (- n 1))))))
(fac 3)
#+end_src
*** CPS
#+begin_src scheme
(define fac
(λ (n ktail)
(zero? n (κ (x0)
(if x0
1
(- n 1
(κ (x1)
(fac x1
(κ (x2)
(* n x2 ktail))))))))))
(fac 3 halt)
#+end_src
*** tailified
#+begin_src scheme
(define (fac-k1)
(define n (pop!))
(define x2 (pop!))
(define x3 (* n x2))
(define ktail (pop-cont!))
(push! x3)
(call! ktail))
(define (fac-k0)
(define x0 (pop!))
(define n (pop!))
(if x0
(begin (define ktail (pop-cont!))
(push! 1)
(call! ktail))
(begin (define x1 (- n 1))
(push! x1)
(push-cont! fac-k1)
(call! fac))))
(define (fac)
(define n (pop!))
(push! n)
(push-cont! fac-k0)
(push! n)
(call! zero?))
(push! 3)
(push-cont! halt)
(call! fac)
#+end_src
evaluation of ~(fac 0)~:
#+begin_src scheme
(push! 0) ; [] []
(push-cont! halt) ; [0] []
(call! fac) ; [0] [halt]
(define n (pop!)) ; [0] [halt]
(push! n) ; [] [halt]
(push-cont! fac-k0) ; [0] [halt]
(push! n) ; [0] [halt fac-k0]
(call! zero?) ; [0 0] [halt fac-k0]
#<internals of zero?> ; [0 0] [halt fac-k0]
(define x0 (pop!)) ; [0 #t] [halt]
(define n (pop!)) ; [0] [halt]
(define ktail (pop-cont!)) ; [] [halt]
(push! 1) ; [] []
(call! ktail) ; [1] []
#+end_src
evaluation of ~(fac 3)~
#+begin_src scheme
(push! 3) ; [] []
(push-cont! halt) ; [3] []
(call! fac) ; [3] [halt]
(define n (pop!)) ; [3] [halt]
(push! n) ; [] [halt]
(push-cont! fac-k0) ; [3] [halt]
(push! n) ; [3] [halt fac-k0]
(call! zero?) ; [3 3] [halt fac-k0]
#<internals of zero?> ; [3 3] [halt fac-k0]
(define x0 (pop!)) ; [3 #f] [halt]
(define n (pop!)) ; [3] [halt]
(define x1 (- n 1)) ; [] [halt]
(push! n) ; [] [halt]
(push! x1) ; [3] [halt]
(push-cont! fac-k1) ; [3 2] [halt]
(call! fac) ; [3 2] [halt fac-k1]
(define n (pop!)) ; [3 2] [halt fac-k1]
(push! n) ; [3 ] [halt fac-k1]
(push-cont! fac-k0) ; [3 2] [halt fac-k1]
(push! n) ; [3 2] [halt fac-k1 fac-k0]
(call! zero?) ; [3 2 2] [halt fac-k1 fac-k0]
#<internals of zero?> ; [3 2 2] [halt fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 #f] [halt fac-k1]
(define n (pop!)) ; [3 2] [halt fac-k1]
(define x1 (- n 1)) ; [3] [halt fac-k1]
(push! n) ; [3] [halt fac-k1]
(push! x1) ; [3 2] [halt fac-k1]
(push-cont! fac-k1) ; [3 2 1] [halt fac-k1]
(call! fac) ; [3 2 1] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(push! n) ; [3 2] [halt fac-k1 fac-k1]
(push-cont! fac-k0) ; [3 2 1] [halt fac-k1 fac-k1]
(push! n) ; [3 2 1] [halt fac-k1 fac-k1 fac-k0]
(call! zero?) ; [3 2 1 1] [halt fac-k1 fac-k1 fac-k0]
#<internals of zero?> ; [3 2 1 1] [halt fac-k1 fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 1 #f] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(define x1 (- n 1)) ; [3 2] [halt fac-k1 fac-k1]
(push! n) ; [3 2] [halt fac-k1]
(push! x1) ; [3 2 1] [halt fac-k1 fac-k1]
(push-cont! fac-k1) ; [3 2 1 0] [halt fac-k1 fac-k1]
(call! fac) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(push! n) ; [3 2 1] [halt fac-k1 fac-k1 fac-k1]
(push-cont! fac-k0) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(push! n) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
(call! zero?) ; [3 2 1 0 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
#<internals of zero?> ; [3 2 1 0 0] [halt fac-k1 fac-k1 fac-k1 fac-k0]
(define x0 (pop!)) ; [3 2 1 0 #t] [halt fac-k1 fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 0] [halt fac-k1 fac-k1 fac-k1]
(define ktail (pop-cont!)) ; [3 2 1] [halt fac-k1 fac-k1 fac-k1]
(push! 1) ; [3 2 1 1] [halt fac-k1 fac-k1]
(call! ktail) ; [3 2 1 1] [halt fac-k1 fac-k1]
(define n (pop!)) ; [3 2 1 1] [halt fac-k1 fac-k1]
(define x2 (pop!)) ; [3 2 1] [halt fac-k1 fac-k1]
(define x3 (* n x2)) ; [3 2] [halt fac-k1 fac-k1]
(define ktail (pop-cont!)) ; [3 2] [halt fac-k1 fac-k1]
(push! x3) ; [3 2] [halt fac-k1]
(call! ktail) ; [3 2 1] [halt fac-k1]
(define n (pop!)) ; [3 2 1] [halt fac-k1]
(define x2 (pop!)) ; [3 2] [halt fac-k1]
(define x3 (* n x2)) ; [3] [halt fac-k1]
(define ktail (pop-cont!)) ; [3] [halt fac-k1]
(push! x3) ; [3] [halt]
(call! ktail) ; [3 2] [halt]
(define n (pop!)) ; [3 2] [halt]
(define x2 (pop!)) ; [3] [halt]
(define x3 (* n x2)) ; [] [halt]
(define ktail (pop-cont!)) ; [] [halt]
(push! x3) ; [] []
(call! ktail) ; [6] []
;; => (halt 6)
#+end_src
+134
View File
@@ -0,0 +1,134 @@
#+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.
nice testable properties of closure-converted code:
- code pointers only appear in function position
- no function has free variables
multiple ~env-ref~ calls could probably be replaced with a primitive that loads the entire environment at once, returning multiple variables.
* scratchpad
** example
#+caption: scheme source
#+begin_src scheme
(letrec ((curried-add (λ (n)
(λ (m)
(+ n m)))))
((curried-add 3) 4))
#+end_src
#+caption: cps
#+begin_src scheme
(letrec ((curried-add
(λ (n ktail0)
(letrec ((curried-add-in
(λ (m ktail1)
(prim (+ n m)
(κ (x0) (continue ktail1 x0))))))
(continue ktail0 curried-add-in)))))
(letrec ((k0 (κ (adder) (adder 4 halt))))
(curried-add 3 k0)))
#+end_src
#+caption: closure-converted
#+begin_src scheme
(letrec ((curried-add
(λ (n ktail0)
(letrec ((curried-add-in-code
(λ (env m ktail1)
(prim (env-ref 0 env)
(κ (n)
(prim (+ n m)
(κ (x0) (continue ktail1 x0))))))))
(prim (make-closure curried-add-in-code n)
(κ (curried-add-in)
(continue ktail0 curried-add-in)))))))
(letrec ((k0 (κ (adder-closure)
(prim (closure-code adder-closure)
(κ (adder)
(adder adder-closure 4 halt))))))
(curried-add 3 k0)))
#+end_src
** wasm
#+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
+6
View File
@@ -15,3 +15,9 @@ XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX00
zero indicates a 30-bit fixnum /
in the upper bits
#+end_example
| type/value | low bits |
|------------+----------|
| small int | 0 |
| ~false~ | 01 |
| ~true~ | 11 |
+53
View File
@@ -0,0 +1,53 @@
#+title: Gyehoek Scheme
#+begin_center
(this document is written in present tense as if the project is complete, but Gyehoek is a work-in-progress.)
#+end_center
Gyehoek is an R⁷RS-compliant Scheme compiler targeting WebAssembly 3.0, relying principally on the recently standardised garbage collector and tail call proposals. the Gyehoek compiler is implemented in Haskell, and the Gyehoek runtime is a Rust program providing primitive routines and WebAssembly execution via the Wasmtime library.
primitives are implemented as native Rust functions made available to the guest by Wasmtime. in the future, it would be ideal to provide the primitives as a WASI interface to help decouple ourselves from a specific Wasm runtime, but it is not a priority.
Gyehoek allows separate compilation, ~eval~, first-class continuations, and so on.
* pipeline
a Scheme program's journey through Gyehoek is as follows:
1. read (source code → Scheme data)
2. parse (Scheme data → AST)
3. expand(?) (AST → AST)
4. contify (AST → CPS)
5. close (CPS → CPS)
6. lower (CPS → Wasm)
** read
in the read phase, Gyehoek's reader serialises textual source code into a sequence of tokens, which are then parsed into S-expressions. this phase is completely agnostic towards any interpretation of the data — it's just data, not code (yet). this distinction between reading and parsing is made so that the reader can easily be shared amongst many parsers, allowing convenient definition of human-readable representations for all sorts of compiler internals. Gyehoek's intermediate languages and WebAssembly text format are of particular interest.
the reader may be configured to extend R⁷RS's syntax with a special "antiquotation" notation, used internally in the compiler to elegantly interpolate and splice S-expression literals via Haskell's quasiquotation.
#+begin_src haskell
let meta = 123 :: Int
in [sx|(a b c #{meta} d)|] -- ⇒ (a b c 123 d)
let metas = ["c","d"] :: List Text
in [sx|(a b ##{metas} e f)|] -- ⇒ (a b "c" "d" e f)
#+end_src
Gyehoek's lexer and parser are generated by Alex and Happy, respectively.
unless otherwise noted, the term "parse" will be used in reference to the phase taking S-expressions to ASTs, while "read" refers to the combined Alex/Happy process. if the tokenisation process (Alex) must be distinguished from the "parse" process (Happy), the former is called "lexical analysis" and the latter "syntactic analysis."
** parse
- use invertible-grammar library
** expand
** contify
- procedures are distinguished from continuations, and procedure applications are distinguished from continuation jumps.
- all continuations and lambda will be named i think. the exception is continuations for primitive calls.
** close
** lower
+53
View File
@@ -0,0 +1,53 @@
#+title: assorted notes on compilation
* letrec
consider:
#+begin_src scheme
(letrec ((even? (lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd? (lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 12))
#+end_src
#+RESULTS:
: #t
since ~letrec~ is a primitive construct in the CPS language, the translation of mutually recursive functions is straightforward:
#+begin_src scheme
(define (-& x y k) (k (- x y)))
(define (zero?& x k) (k (zero? x)))
(define (halt x) x)
(letrec ((even? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#t
(-& n 1
(lambda (x2)
(odd? x2 ktail))))))))
(odd? (lambda (n ktail)
(zero?& n
(lambda (x1)
(if x1
#f
(-& n 1
(lambda (x2)
(even? x2 ktail)))))))))
(even? 12 halt))
#+end_src
#+RESULTS:
: #t
however, Scheme permits ~letrec~-expressions with non-lambda right-hand sides, while the CPS language permits only kappa and lambda forms. thus, the handling of these forms is less trivial.
for now we'll just reject any ~letrec~ forms with non-lambda right-hand sides, lol. they aren't very important.
Generated
+16
View File
@@ -66,6 +66,21 @@
"type": "github"
}
},
"crane": {
"locked": {
"lastModified": 1784248371,
"narHash": "sha256-0l0Y4D4wbZhp1Oi6h8OpbLtIm/4FN88oCf54MK2ZgiM=",
"owner": "ipetkov",
"repo": "crane",
"rev": "f7d151ec0bf52cf9662e2f59d7bea28588c2f070",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
@@ -587,6 +602,7 @@
},
"root": {
"inputs": {
"crane": "crane",
"haskellNix": "haskellNix",
"nixpkgs": [
"haskellNix",
+22 -18
View File
@@ -7,6 +7,7 @@
url = "git+https://git.deertopia.net/msyds/sydpkgs";
inputs.nixpkgs.follows = "nixpkgs";
};
crane.url = "github:ipetkov/crane";
};
outputs = { self, nixpkgs, sydpkgs, haskellNix, ... }@inputs:
@@ -16,13 +17,12 @@
"x86_64-darwin" "x86_64-linux"
];
overlays = [
haskellNix.overlay
(final: prev: {
gyehoek-wasmtime-wrapper = final.callPackage ./wasmtime.nix {};
})
(final: prev: {
gyehoek-runtime = final.callPackage ./runtime {
crane-lib = inputs.crane.mkLib final;
};
gyehoek = final.haskell-nix.project' {
src = ./.;
compiler-nix-name = "ghc912";
@@ -30,32 +30,36 @@
packages.gyehoek.components.tests.test.preCheck =
let
bin = [
pkgs.gyehoek-wasmtime-wrapper
pkgs.git
pkgs.git # tasty uses git diff
];
in ''
# Wasmtime requires a cache in $HOME. This is less
# painful than reconfiguring the cache location.
export HOME=$(mktemp -d)
export GYEHOEK_RUNTIME=${lib.getExe final.gyehoek-runtime}
export PATH=${lib.makeBinPath bin}:$PATH
'';
})];
shell = {
withHoogle = true;
inputsFrom = [];
inputsFrom = [
final.gyehoek-runtime
];
tools = {
cabal = {};
haskell-language-server = {};
};
buildInputs = with final; [
haskellPackages.cabal-fmt
self.packages.${final.stdenv.hostPlatform.system}.shake
final.wabt
final.nodejs
final.wasm-tools
final.wac-cli
final.guile
final.gyehoek-wasmtime-wrapper
wabt
nodejs
wasm-tools
wac-cli
guile
rust-analyzer
wasmtime
# bashInteractive is necessary to work around an
# optparse-applicative issue
#
# https://github.com/pcapriotti/optparse-applicative/pull/408
bashInteractive
];
};
};
@@ -87,7 +91,7 @@
hf.packages.${system} // lib.fix (packages: {
gyehoek = hf.packages.${system}."gyehoek:exe:gyehoek";
default = packages.gyehoek;
shake = pkgs.callPackage ./shake-wrapper.nix {};
inherit (pkgs) gyehoek-runtime;
}));
devShells = each-system
-5
View File
@@ -1,5 +0,0 @@
ret > ExitSuccess
out > 22
out >
err > warning: using `--invoke` with a function that returns values is experimental and may break in the future
err >
-47
View File
@@ -1,47 +0,0 @@
(module
(type $heap-object (sub (struct (field (mut i32)))))
(func
(param)
(result (ref eq))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(i32.const 3)
(i32.const 2)
i32.shl
ref.i31
(ref.cast (ref i31))
i31.get_s
(i32.const 4)
(i32.const 2)
i32.shl
ref.i31
(ref.cast (ref i31))
i31.get_s
i32.mul
ref.i31
(local.set 0)
(i32.const 2)
(i32.const 2)
i32.shl
ref.i31
(ref.cast (ref i31))
i31.get_s
(i32.const 5)
(i32.const 2)
i32.shl
ref.i31
(ref.cast (ref i31))
i31.get_s
i32.mul
ref.i31
(local.set 1)
(local.get 0)
(ref.cast (ref i31))
i31.get_s
(local.get 1)
(ref.cast (ref i31))
i31.get_s
i32.add
ref.i31
(local.set 2)
(local.get 2))
(export "main" (func 0)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 9
+4
View File
@@ -0,0 +1,4 @@
(let ((make-adder (lambda (x)
(lambda (y)
(+ x y)))))
((make-adder 4) 5))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 17
+5
View File
@@ -0,0 +1,5 @@
;; apply `f' to `x' twice.
((λ (f x)
(f (f x)))
(λ (x) (+ x 4))
9)
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 10
+5
View File
@@ -0,0 +1,5 @@
((λ (f g x)
(f (g x)))
(λ (x) (+ x 4))
(λ (x) (* x 2))
3)
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 22
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 123
+1
View File
@@ -0,0 +1 @@
(call/cc (λ (cc) (cc 123)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 1234
+1
View File
@@ -0,0 +1 @@
(call/cc (λ (_) 1234))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 456
+5
View File
@@ -0,0 +1,5 @@
(call/cc
(λ (k1)
(call/cc
(λ (k2)
(k1 456)))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 456
+5
View File
@@ -0,0 +1,5 @@
(call/cc
(λ (k1)
(call/cc
(λ (k2)
(k2 456)))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 720
+5
View File
@@ -0,0 +1,5 @@
(letrec ((fac (λ (n)
(if (zero? n)
1
(* n (fac (- n 1)))))))
(fac 6))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #f
+1
View File
@@ -0,0 +1 @@
#f
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 128
+3
View File
@@ -0,0 +1,3 @@
(((λ (f) f)
(λ (x) (* x 4)))
32)
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 555
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 777
+1
View File
@@ -0,0 +1 @@
(if 123 777 555)
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 777
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #<procedure>
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 16
+2
View File
@@ -0,0 +1,2 @@
(let ((square (λ (x) (* x x))))
(square 4))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 16
+2
View File
@@ -0,0 +1,2 @@
(letrec ((square (λ (x) (* x x))))
(square 4))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 25
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
+1
View File
@@ -0,0 +1 @@
#t
-5
View File
@@ -1,5 +0,0 @@
ret > ExitSuccess
out > 555
out >
err > warning: using `--invoke` with a function that returns values is experimental and may break in the future
err >
-13
View File
@@ -1,13 +0,0 @@
(module
(type (sub (struct (field (mut i32)))))
(func
(param)
(result (ref eq))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(i32.const 0)
ref.i31
(if
(result i32)
(then (i32.const 777) ref.i31)
(else (i32.const 555) ref.i31)))
(export "main" (func 0)))
-5
View File
@@ -1,5 +0,0 @@
ret > ExitSuccess
out > 777
out >
err > warning: using `--invoke` with a function that returns values is experimental and may break in the future
err >
-13
View File
@@ -1,13 +0,0 @@
(module
(type (sub (struct (field (mut i32)))))
(func
(param)
(result (ref eq))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(i32.const 1)
ref.i31
(if
(result i32)
(then (i32.const 777) ref.i31)
(else (i32.const 555) ref.i31)))
(export "main" (func 0)))
+1
View File
@@ -0,0 +1 @@
(가 나 다 라)
+4
View File
@@ -0,0 +1,4 @@
(가
나
다
라)
+1
View File
@@ -0,0 +1 @@
()
+5
View File
@@ -0,0 +1,5 @@
[ SynNone :< SimpleF ( SimpleBoolean True )
, SynNone :< SimpleF ( SimpleBoolean True )
, SynNone :< SimpleF ( SimpleBoolean False )
, SynNone :< SimpleF ( SimpleBoolean False )
]
+1
View File
@@ -0,0 +1 @@
#t #true #f #false
+9
View File
@@ -0,0 +1,9 @@
[ SynNone :< SimpleF
( SimpleNumber 45.0 )
, SynNone :< SimpleF
( SimpleNumber 5667.0 )
, SynNone :< SimpleF
( SimpleNumber
( -123.0 )
)
]
+1
View File
@@ -0,0 +1 @@
45 +5667 -123
+5
View File
@@ -0,0 +1,5 @@
[ Fix
( SimpleF
( Symbol "aaaa bc" )
)
]
@@ -0,0 +1 @@
|aaaa bc|
+1
View File
@@ -0,0 +1 @@
[]
View File
+17
View File
@@ -0,0 +1,17 @@
[ SynNone :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( SimpleSymbol "가" )
) :|
[ SynNone :< SimpleF
( SimpleSymbol "나" )
, SynNone :< SimpleF
( SimpleSymbol "다" )
]
)
( SynNone :< SimpleF
( SimpleSymbol "라" )
)
)
]
+1
View File
@@ -0,0 +1 @@
(가 나 다 . 라)
+19
View File
@@ -0,0 +1,19 @@
[ SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
( SimpleSymbol "가" )
, SynNone :< SimpleF
( SimpleSymbol "나" )
, SynNone :< SimpleF
( SimpleSymbol "다" )
, SynNone :< SimpleF
( SimpleSymbol "라" )
, SynNone :< SimpleF
( SimpleNumber 1.0 )
, SynNone :< SimpleF
( SimpleNumber 2.0 )
, SynNone :< SimpleF
( SimpleNumber 3.0 )
]
)
]
+1
View File
@@ -0,0 +1 @@
(가 나 다 라 1 2 3)
+40
View File
@@ -0,0 +1,40 @@
[ SynNone :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( SimpleSymbol "a" )
) :|
[ SynNone :< SimpleF
( SimpleSymbol "b" )
, SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
( SimpleSymbol "c" )
, SynNone :< SimpleF
( SimpleSymbol "d" )
]
)
]
)
( SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
( SimpleSymbol "가" )
, SynNone :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( SimpleSymbol "나" )
) :| []
)
( SynNone :< SimpleF
( SimpleSymbol "다" )
)
)
, SynNone :< SimpleF
( SimpleSymbol "라" )
]
)
)
)
]
+1
View File
@@ -0,0 +1 @@
(a b (c d) . (가 (나 . 다) 라))
+5
View File
@@ -0,0 +1,5 @@
[ SynNone :< SimpleF
( SimpleSymbol "+" )
, SynNone :< SimpleF
( SimpleSymbol "-" )
]
@@ -0,0 +1 @@
+ -
@@ -0,0 +1,5 @@
[ Fix
( SimpleF
( String "가나다라마바" )
)
]
@@ -0,0 +1,2 @@
"가나다\
라마바"
+3
View File
@@ -0,0 +1,3 @@
[ SynNone :< SimpleF
( SimpleString "가나다라" )
]
+1
View File
@@ -0,0 +1 @@
"가나다라"
+19
View File
@@ -0,0 +1,19 @@
[ SynNone :< SimpleF
( SimpleSymbol "abc" )
, SynNone :< SimpleF
( SimpleSymbol "bala-hwa$" )
, SynNone :< SimpleF
( SimpleSymbol "x!!!" )
, SynNone :< SimpleF
( SimpleSymbol "z" )
, SynNone :< SimpleF
( SimpleSymbol "z123" )
, SynNone :< SimpleF
( SimpleSymbol "나는너무졸리다" )
, SynNone :< SimpleF
( SimpleSymbol "學" )
, SynNone :< SimpleF
( SimpleSymbol "車室." )
, SynNone :< SimpleF
( SimpleSymbol "三個女人一臺戲。" )
]
@@ -0,0 +1,5 @@
abc bala-hwa$ x!!! z z123 나는너무졸리다 學
車室.
三個女人一臺戲。
+69 -19
View File
@@ -25,6 +25,11 @@ common ghcstuffs
default-extensions:
BlockArguments
DeriveGeneric
DerivingVia
DuplicateRecordFields
NoFieldSelectors
OrPatterns
OverloadedLabels
OverloadedRecordDot
OverloadedStrings
PartialTypeSignatures
@@ -34,9 +39,8 @@ common ghcstuffs
executable gyehoek
import: ghcstuffs, ghcstuffs-dev
main-is: Main.hs
build-depends:
, base ^>=4.21.2.0
, base ^>=4.21.2.0
, gyehoek
hs-source-dirs: app
@@ -44,29 +48,45 @@ executable gyehoek
library
import: ghcstuffs, ghcstuffs-dev
ghc-options: -fplugin=Effectful.Plugin
ghc-options: -fplugin=Effectful.Plugin
-- cabal-fmt: expand src
exposed-modules:
Gyehoek.CPS.Close
Gyehoek.CPS.Convert
Gyehoek.CPS.Eval
Gyehoek.CPS.Lower
Gyehoek.CPS.Stackify
Gyehoek.CPS.Syntax
Gyehoek.Driver
Gyehoek.GenSym
Gyehoek.Language
Gyehoek.Options
Gyehoek.Prelude
Gyehoek.Scheme.Syntax
Gyehoek.Sexp
Gyehoek.Sexp.Grammar
Gyehoek.Sexp.Print
Gyehoek.Sexp.Read
Gyehoek.Sexp.Syntax
Gyehoek.Stack.Syntax
Gyehoek.Stack.VM
Gyehoek.Wasm
Gyehoek.Driver
build-depends:
, base ^>=4.21.2.0
, base ^>=4.21.2.0
, binary
, bytestring
, comonad
, containers
, cradle
, data-fix
, deepseq
, deriving-compat
, effectful
, effectful-core
, effectful-plugin
, filepath
, free
, generic-lens
, hashable
, invertible-grammar
@@ -74,30 +94,60 @@ library
, megaparsec
, mtl
, optparse-applicative
, ordered-containers
, pretty-simple
, prettyprinter
, prettyprinter-ansi-terminal
, process
, recursion-schemes
, scientific
, sexp-grammar
, string-interpolate
, template-haskell
, text
, text-short
, typed-process
, unordered-containers
, vector
, string-interpolate
, pretty-simple
hs-source-dirs: src
default-language: GHC2024
test-suite test
import: ghcstuffs, ghcstuffs-dev
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
build-depends: base
, gyehoek
, filepath
, tasty
, tasty-silver
, directory
default-language: GHC2024
import: ghcstuffs, ghcstuffs-dev
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
build-tool-depends: tasty-discover:tasty-discover
-- cabal-fmt: expand test -Main
other-modules:
Gyehoek.Test.CPS.Eval
Gyehoek.Test.CPS.Stackify
Gyehoek.Test.CPS.Syntax
Gyehoek.Test.Golden
Gyehoek.Test.Scheme.Syntax
Gyehoek.Test.Sexp
Gyehoek.Test.Sexp.Print
Gyehoek.Test.Stack.VM
Root
build-depends:
, base
, deepseq
, directory
, effectful
, filepath
, generic-lens
, gyehoek
, lens
, pretty-simple
, process-extras
, sexp-grammar
, tasty
, tasty-expected-failure
, tasty-hunit
, tasty-silver
, text
default-language: GHC2024
-20
View File
@@ -1,20 +0,0 @@
<html>
<head>
<script>
const imports = {
guppy: {
print: (arg) => console.log (arg)
}
}
fetch("u.wasm")
.then((response) => response.arrayBuffer())
.then((bytes) => WebAssembly.instantiate(bytes, imports))
.then((results) => {
results.instance.exports.main ();
});
</script>
</head>
<body>
</body>
</html>
-2
View File
@@ -1,2 +0,0 @@
#!/usr/bin/env sh
cabal repl --repl-options "-interactive-print=Text.Pretty.Simple.pPrint" --build-depends pretty-simple
+1
View File
@@ -0,0 +1 @@
target/
+1935
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "gyehoek-runtime"
version = "0.1.0"
edition = "2024"
[dependencies]
clap = { version = "4.6.1", features = ["derive"] }
clio = { version = "0.3.5", features = ["clap-parse"] }
memoize = "0.6.0"
wasmtime = "46.0.1"
+13
View File
@@ -0,0 +1,13 @@
{ rustPlatform
, lib
, crane-lib
}:
crane-lib.buildPackage (lib.fix (finalAttrs: {
pname = "gyehoek-runtime";
version = "0.1.0";
src = ./.;
# cargoLock = ./Cargo.lock;
doCheck = true;
meta.mainProgram = "gyehoek-runtime";
}))
+38
View File
@@ -0,0 +1,38 @@
use wasmtime::*;
use crate::internal as scm;
use crate::internal::{Scm,Immediate,HeapObject};
// pub fn small_fixnum_p (_)
// pub fn immediate_p (caller : Caller<'_, u32>, x : EqRef) -> EqRef {
// x.is_i31 ()
// }
fn write_immediate (_caller : Caller<'_, u32>, imm : Immediate) {
match imm {
Immediate::SmallFixnum (n) => print! ("{}", n),
Immediate::Bool (b) => print! ("{}", if b { "#t" } else { "#f" }),
}
}
pub fn write (caller : Caller<'_, u32>, x : Rooted<EqRef>) {
match scm::interpret (&caller, x).unwrap ().unwrap () {
Scm::Immediate (x) => write_immediate (caller, x),
Scm::HeapObject (x) => write_heap_object (caller, x),
}
}
fn write_heap_object (_caller : Caller<'_, u32>, x : HeapObject) {
match x {
HeapObject::Procedure => print! ("#<procedure>"),
}
}
pub fn truthy_p (caller : Caller<'_, u32>, x : Rooted<EqRef>,) -> u32 {
let r = scm::interpret (&caller, x).unwrap ().unwrap ();
if let Scm::Immediate (Immediate::Bool (false)) = r {
0
} else {
1
}
}
+99
View File
@@ -0,0 +1,99 @@
use wasmtime::*;
use crate::types;
pub fn immediate_p (store : impl AsContext, x : Rooted<EqRef>) -> bool {
x.is_i31 (store).unwrap ()
}
pub enum Immediate {
SmallFixnum (i32),
Bool (bool),
}
pub enum HeapObject {
Procedure
}
pub enum Scm {
Immediate (Immediate),
HeapObject (HeapObject),
}
#[allow(nonstandard_style)]
pub type scm_bits = u32;
#[allow(nonstandard_style)]
pub const scm_false : scm_bits = 0b01;
#[allow(nonstandard_style)]
pub const scm_true : scm_bits = 0b11;
pub fn interpret_immediate (x : scm_bits) -> Option<Immediate> {
if x & 1 == 0 {
Some (Immediate::SmallFixnum ((x >> 1).try_into ().unwrap ()))
} else if x == scm_true {
Some (Immediate::Bool (true))
} else if x == scm_false {
Some (Immediate::Bool (false))
} else {
None
}
}
pub fn interpret_heap_object (
store : impl AsContext,
x : Rooted<EqRef>
) -> Result<Option<HeapObject>> {
if x.matches_ty (&store, &types::closure (&store)?)? {
Ok (Some (HeapObject::Procedure))
} else {
todo! ()
}
}
pub fn interpret (
store : impl AsContext,
x : Rooted<EqRef>
) -> Result<Option<Scm>> {
if let Some (imm) = x.as_i31 (&store)? {
Ok (
interpret_immediate (imm.get_u32 ())
.map (Scm::Immediate)
)
} else {
Ok (
interpret_heap_object (&store, x)?
.map (Scm::HeapObject)
)
}
}
pub fn encode_immediate (
store : impl AsContext,
imm : Immediate
) -> scm_bits {
use Immediate::*;
match imm {
SmallFixnum (n) => (n << 1).try_into ().unwrap (),
Bool (false) => scm_false,
Bool (true) => scm_true,
}
}
pub fn encode (store : impl AsContextMut, x : Scm) -> Rooted<EqRef> {
match x {
Scm::Immediate (imm) => {
let i31 = I31::new_u32 (encode_immediate (&store, imm))
.unwrap ();
EqRef::from_i31 (store, i31)
}
Scm::HeapObject (ho) => {
todo! ()
}
}
}
pub fn encode_bool (store : impl AsContextMut, b : bool) -> Rooted<EqRef> {
let x = if b { scm_true } else { scm_false };
let i31 = I31::new_u32 (x).unwrap ();
EqRef::from_i31 (store, i31)
}
+53
View File
@@ -0,0 +1,53 @@
mod gyehoek;
mod internal;
mod types;
use std::io;
use std::io::Read;
use clio::*;
use clap::Parser;
use wasmtime::*;
/// A runtime for Gyehoek scheme.
#[derive(Parser, Debug)]
#[command(name = "gyehoek", version, about, long_about = None)]
struct Args {
/// Path to Wasm binary or textual source
#[clap(value_parser)]
wasm: Input,
}
fn read<R : Read> (mut rdr : R) -> io::Result<Vec<u8>> {
let mut buf = vec! [];
rdr.read_to_end (&mut buf)?;
Ok (buf)
}
fn get_config () -> Config {
let mut cfg = Config::new ();
cfg.wasm_reference_types (true);
cfg.wasm_function_references (true);
cfg.wasm_tail_call (true);
cfg.wasm_gc (true);
cfg
}
fn link_primitives (linker : &mut Linker<u32>) -> wasmtime::Result<()> {
linker.func_wrap ("gyehoek", "write", gyehoek::write)?;
linker.func_wrap ("gyehoek", "truthy?", gyehoek::truthy_p)?;
Ok (())
}
pub fn main () -> wasmtime::Result<()> {
let args = Args::parse ();
let wasm_config = get_config ();
let engine = Engine::new (&wasm_config)?;
let module = Module::new (&engine, read (args.wasm)?)?;
let mut linker = Linker::new (&engine);
link_primitives (&mut linker)?;
let mut store : Store<u32> = Store::new (&engine, 4);
let instance = linker.instantiate (&mut store, &module)?;
let main = instance.get_typed_func::<(),()> (&mut store, "main")?;
main.call (&mut store, ())?;
Ok (())
}
+62
View File
@@ -0,0 +1,62 @@
use wasmtime::*;
use memoize::memoize;
pub fn heap_object_struct (store : impl AsContext) -> Result<StructType> {
let ctx = store.as_context ();
let engine = ctx.engine ();
Ok (
StructType::with_finality_and_supertype (
engine,
Finality::NonFinal,
None,
vec![
hash_field ()
]
)?
)
}
pub fn heap_object (_store : impl AsContext) -> Result<HeapType> {
todo! ()
}
#[memoize]
pub fn hash_field () -> FieldType {
FieldType::new (
Mutability::Var,
StorageType::ValType (ValType::I32)
)
}
pub fn closure (store : impl AsContext) -> Result<HeapType> {
let ctx = store.as_context ();
let engine = ctx.engine ();
Ok (
HeapType::ConcreteStruct (
StructType::with_finality_and_supertype (
engine,
Finality::NonFinal,
Some (&heap_object_struct (&store)?),
vec![
hash_field (),
FieldType::new (
Mutability::Const,
StorageType::ValType (ValType::Ref (
RefType::new (
false,
HeapType::ConcreteFunc (
FuncType::new (
engine,
vec![ValType::I32],
vec![],
)
)
)
))
),
]
)?
)
)
}
-14
View File
@@ -1,14 +0,0 @@
{ runCommandLocal, makeWrapper, lib, haskellPackages }:
let
our-ghc = haskellPackages.ghc.withPackages (ps: [
ps.shake
]);
in runCommandLocal
"shake-wrapper"
{ nativeBuildInputs = [ makeWrapper ]; }
''
mkdir -p $out/bin
makeWrapper ${lib.getExe haskellPackages.shake} $out/bin/shake \
--prefix PATH : ${lib.makeBinPath [our-ghc]}
''
+41
View File
@@ -0,0 +1,41 @@
{-# LANGUAGE OverloadedLists #-}
module Gyehoek.CPS.Close
( closeProgram
) where
import Gyehoek.CPS.Syntax
import Gyehoek.GenSym
import Gyehoek.Prelude
close :: GenSym :> es => Exp -> Eff es Exp
close = transformM \case
ExpLetRec [(f, AbsLambda lam@(MkLambda bs kb m))] e -> do
f_code <- gensym' @Name $ f ^. _Wrapped'. to (<> "-code")
-- it would probably be most sane to generate a symbol for `env`,
-- but we're reusing the lambda binding so we don't have to
-- explicitly substitute recursive calls.
let frees = freeWithBound' [f] lam
let m' = ifoldr
(\n x q -> [cps|(prim (env-ref #{f} #{n})
(κ (#{x}) #{q}))|])
m frees
pure [cps|
(letrec ((#{f_code} (λ (#{f} ##{bs} #{kb})
#{m'})))
(prim (make-closure ($ #{f_code}) ##{frees})
(κ (#{f}) #{e})))
|]
ExpApply f xs ktail -> do
code <- gensym' @Name "code"
pure [cps|
(prim (env-code #{f})
(κ (#{code})
(#{code} #{f} ##{xs} #{ktail})))
|]
e -> pure e
closeProgram :: GenSym :> es => Program -> Eff es Program
closeProgram = traverseOf #body close
+70 -17
View File
@@ -1,17 +1,17 @@
{-# LANGUAGE OverloadedLists #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Convert
( convert
, convertProgram
( convertProgram
, convertExp
) where
import Gyehoek.CPS.Syntax
import Gyehoek.Scheme.Syntax qualified as Scm
import Gyehoek.GenSym
import Data.List.NonEmpty (NonEmpty((:|)))
import Effectful
import Control.Monad.Cont qualified as Cont
import Control.Lens
import qualified Data.List.NonEmpty as NE
import Gyehoek.Prelude
-- 뻘짓이어라
@@ -21,31 +21,55 @@ telescope
-> t a -> (t b -> r) -> r
telescope f = Cont.runCont . traverse (Cont.cont . f)
-- | Transform an expression with a meta-continuation.
convert
:: forall es. (GenSym :> es)
=> Scm.Exp -> (Val -> Eff es Exp) -> Eff es Exp
convert (Scm.ExpVar x) k = k $ ValVar x
convert (Scm.ExpLit l) k = k $ ValLit l
convert (Scm.ExpLit l) k = k . ValImm $ case l of
LitInt n -> ImmInt n
LitBool b -> ImmBool b
_ -> _
-- special case: call/cc is desugared during cps-conversion...
convert (Scm.ExpPrim (PrimCallCC withcc)) k = do
convert withcc \withcc' -> do
cc <- gensym' @Name "cc"
r <- gensym' "r"
m <- k $ ValVar r
ccish <- gensym' @Name "cc-ish"
x <- gensym' @Name "x"
pure [cps|
(letrec ((#{cc} (κ (#{r}) #{m})))
(letrec ((#{ccish} (λ (#{x} _) (continue #{cc} #{x}))))
(#{withcc'} #{ccish} #{cc})))
|]
-- ...while all other prims are left as-is for later stages to
-- handle..
convert (Scm.ExpPrim p) k =
telescope (convert @es) p \p' -> do
r <- gensym' "r"
ExpPrim p' [r] <$> k (ValVar r)
ExpPrim p' . MkKappa [r] <$> k (ValVar r)
convert (Scm.ExpLambda xs e) k = do
f <- gensym' "λ-body"
ktail <- gensym' "λ-tail"
m <- convert e $ \e' ->
pure $ ExpContinue ktail [e']
ExpLet [(f, MkLambda xs ktail m)] <$> k (ValVar f)
f <- gensym' "lambda-body"
lam <- convertLambda xs e
ke <- k $ ValVar f
pure [cps|
(letrec ((#{f} #{lam}))
#{ke})
|]
convert (Scm.ExpApply f xs) k =
telescope (convert @es) (f:|xs) \(f':|xs') -> do
r <- gensym' "r"
x <- gensym' "x"
m <- k (ValVar x)
pure $ ExpFix [(r, MkKappa [x] m)] $ ExpApply f' (xs' ++ [ValVar r])
pure $ ExpLetRec [(r, AbsKappa' [x] m)] $ ExpApply f' xs' r
convert (Scm.ExpBegin xs) k = _
@@ -53,11 +77,40 @@ convert (Scm.ExpIf c t f) k =
convert c \c' ->
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 "let-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'})
|]
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 p =
MkProgram <$> telescope (convert @es) (p ^.. each . _Left) \exps ->
pure . Halt1 $ case NE.nonEmpty exps of
Nothing -> ValLit Void
Just es -> NE.last es
MkProgram <$> telescope (convert @es) (p ^.. each . _Left) (pure . Halt)
convertExp :: forall es. (GenSym :> es) => Scm.Exp -> Eff es Exp
convertExp e = convert e (pure . Halt1)
+79
View File
@@ -0,0 +1,79 @@
{-# LANGUAGE ViewPatterns #-}
module Gyehoek.CPS.Eval
( evalProgram
, module Gyehoek.CPS.Syntax
) where
import Gyehoek.CPS.Syntax
import Control.Lens
import Data.Maybe (fromMaybe)
import Text.Show.Functions ()
import qualified Data.HashMap.Strict as H
import Gyehoek.Prelude
data Env = MkEnv
{ vars :: HashMap Name Obj
, labels :: HashMap Name (Env, Abs)
}
deriving (Show, Generic)
eval :: Env -> Exp -> List Obj
eval g (Halt xs) = evalVal g <$> xs
eval g (ExpContinue k xs) =
case g ^. #labels . at k of
Just (h, AbsKappa' bs m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
_ -> error [i|not a kappa: #{k}|]
eval g (ExpApply ((^?! #ValVar) -> f) xs ktail) =
case g ^?! #labels . at f of
Just (h,AbsLambda' bs kb m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
& #labels . at kb .~ (g ^. #labels . at ktail)
Nothing -> error [i|undefined label: #{f}|]
eval g (ExpLetRec [(b, ab)] e) = eval g' e
where g' = g & #labels . at b ?~ (g,ab)
eval g (ExpPrim p (MkKappa bs e)) = case evalVal g <$> p of
PrimAdd x y -> arithBinop (+) x y
PrimMul x y -> arithBinop (*) x y
PrimSub x y -> arithBinop (-) x y
PrimDiv x y -> arithBinop div x y
_ -> error [i|unhandled prim: #{p}|]
where
ret rs = eval
(g & #vars <>~ envOfBinds bs rs)
e
arithBinop f (ObjImm (ImmInt x)) (ObjImm (ImmInt y)) =
ret [ObjImm . ImmInt $ f x y]
arithBinop _ x y = error [i|bad arith: #{x}, #{y}|]
eval _ e = error [i|unimplemented case: #{e}|]
envOfBinds bs xs = foldMap (uncurry H.singleton) (zip bs xs)
evalVal :: Env -> Val -> Obj
evalVal g = \case
ValVar x -> fromMaybe (error [i|unbound: #{x}|]) $ g ^?! #vars . at x
ValImm x -> ObjImm x
emptyEnv :: Env
emptyEnv = MkEnv
{ vars = mempty
-- a kinda silly hack to make sure `halt` is handled correctly when
-- it appears as the tail continuation of an application. the
-- special case of `eval` responsible for `halt` only covers terms
-- of the form `(continue $halt xs …)`; other terms such as
-- `($some-fn xs $halt)` just see an undefined label `$halt`.
, labels = H.singleton "halt"
( emptyEnv
, AbsKappa' ["h0"] $ Halt [ValVar "h0"]
)
}
evalProgram :: Program -> List Obj
evalProgram (MkProgram e) = eval emptyEnv e
+268 -176
View File
@@ -5,41 +5,29 @@
{-# LANGUAGE MultilineStrings #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ApplicativeDo #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{-# LANGUAGE RecursiveDo #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Lower
(lower, lowerProgram) where
import Gyehoek.CPS.Syntax
import Data.Generics.Labels
import Gyehoek.Scheme.Syntax qualified as Scm
import Gyehoek.GenSym
import Data.List.NonEmpty (NonEmpty((:|)))
import Effectful
import Control.Monad.Cont qualified as Cont
import Effectful.Writer.Static.Local
import Data.Text (Text)
import Data.Vector.Strict (Vector)
import Control.Lens
import Data.Foldable
import Data.HashMap.Strict (HashMap)
import Control.Lens hiding (op)
import Numeric.Natural
import GHC.Generics (Generic)
import Gyehoek.Scheme.Syntax (Lit(..))
import Text.Printf
import qualified Data.Text as T
import qualified Data.Vector.Strict as V
import Data.IntMap.Strict (IntMap)
import Data.String.Interpolate
import Gyehoek.Wasm qualified as Wasm
import Gyehoek.Wasm hiding (Expr)
import Language.Sexp.Located (pattern ParenList)
import Debug.Pretty.Simple
import Language.Sexp.Located qualified as SL
import Control.Monad.Fix
import qualified Gyehoek.Sexp
import Data.Text qualified as T
import Data.Foldable (fold)
import Gyehoek.Sexp (encodeOrShow)
import Gyehoek.Prelude
data Env = MkEnv
{ runtime :: Runtime
, vars :: Vector Name
{ vars :: Vector Name
, kvars :: Vector Name
}
deriving (Show, Generic)
@@ -50,201 +38,305 @@ type instance IxValue Env = Name
instance Ixed Env where
ix i = #vars . ix (fromIntegral i)
data Runtime = MkRuntime
{ argArrayType :: Idx
, argArray :: Idx
, contType :: Idx
, contStackType :: Idx
, contStackTop :: Idx
, contStack :: Idx
, result :: Idx
, halt :: Idx
}
deriving (Show, Generic)
tonat :: Integral a => a -> Natural
tonat = fromIntegral
-- | @makeSmallFixnum@ emits an expression injecting the i32 on top
-- of the stack into the SCM unitype.
makeSmallFixnum :: Wasm.Expr
makeSmallFixnum = mconcat
[ ins "i32.const" [sxp @Int 1]
, ins "i32.shl" []
, ins "ref.i31" []
]
makeSmallFixnum = [expr|
(@gyehoek "construct small fixnum")
(i32.const 1)
i32.shl
ref.i31
|]
getArgRegister :: Natural -> SL.Sexp
getArgRegister n = SL.Symbol [i|$arg#{n}|]
-- | 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
-- result of @e@.
pushArg :: Runtime -> Int -> Wasm.Expr -> Wasm.Expr
pushArg (MkRuntime {argArrayType,argArray}) n e = mconcat
[ ins "global.get" [sxp argArray]
, ins "i32.const" [sxp n]
, e
, ins "array.set" [sxp argArrayType]
]
pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
pushArg n e = [expr|
(@gyehoek begin pushArg)
##{e}
(global.set #{reg})
(@gyehoek end pushArg)
|]
where reg = getArgRegister n
-- | Pop the nth arg from the arg-passing array onto the stack.
popArg :: Runtime -> Int -> Wasm.Expr
popArg (MkRuntime {argArrayType,argArray}) n = mconcat
[ ins "global.get" [sxp argArray]
, ins "i32.const" [sxp n]
, ins "array.get" [sxp argArrayType]
, ins "ref.as_non_null" []
]
popArg :: Natural -> Wasm.Expr
popArg n = [expr|
(@gyehoek begin popArg)
(global.get #{reg})
ref.as_non_null
(@gyehoek end popArg)
|]
where reg = getArgRegister n
lowerVal :: Env -> Val -> Wasm.Expr
lowerVal :: (HasCallStack, GenMod :> es) => Env -> Val -> Eff es Wasm.Expr
lowerVal g (ValLit l) =
case l of
LitInt n ->
ins "i32.const" [sxp n]
<> makeSmallFixnum
LitBool b ->
ins "i32.const" [sxp @Int $ if b then 1 else 0]
<> ins "ref.i31" []
lowerVal g (ValImm imm) =
pure $ case imm of
ImmInt n -> [expr|
(i32.const #{n})
##{makeSmallFixnum}
|]
ImmBool b -> [expr|
(i32.const #{b'})
ref.i31
|]
where b' :: Int = if b then 0b11 else 0b01
_ -> _
lowerVal g (ValVar x) = ins "local.get" [sxp (1+l)]
lowerVal g (ValVar x) = do
pure [expr|(global.get #{l})|]
where
l = V.elemIndex x g.vars ^?! _Just
l = getArgRegister . fromIntegral . succ $ V.elemIndex x g.vars ^?! _Just
lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
lower' g (Halt [v]) = pure . mconcat $
[ pushArg g.runtime 0 (lowerVal g v)
, ins "return_call" [sxp @Int 1]
]
lower' g (Halt [v]) = do
arg <- pushArg 0 <$> lowerVal g v
pure [expr|
##{arg}
(return_call $halt (i32.const 1))
|]
lower' g (ExpPrim p rs e) =
case p of
PrimAdd x y -> lowerBinOp "i32.add" g x y r e
PrimMul x y -> lowerBinOp "i32.mul" g x y r e
where
r = head rs
lower' g e@(ExpPrim p k) =
([expr|(@gyehoek :origin #{origin})|]<>)
<$> case p of
PrimAdd x y -> lowerBinOp "i32.add" g x y k
PrimMul x y -> lowerBinOp "i32.mul" g x y k
where origin = encodeOrShow @_ @Text e
lower' g (ExpIf c t f) = do
c' <- lowerVal g c
t' <- lower' g t
f' <- lower' g f
pure $ lowerVal g c
<> Wasm.if' (Wasm.result [i32]) t' f'
pure [expr|
##{c'}
(call $gh-truthy?)
(if (then ##{t'})
(else ##{f'}))
|]
lower' g (ExpContinue k [x]) = pure . mconcat $
[ pushArg rt 0 (lowerVal g x)
, ins "i32.const" [sxp @Int 1] -- nargs
-- get the return continuation.
, ins "global.get" [sxp rt.contStack]
, ins "global.get" [sxp rt.contStackTop]
, ins "array.get" [sxp rt.contStackType]
, ins "ref.as_non_null" []
-- decrement contStackTop, completing the "pop."
, ins "global.get" [sxp rt.contStackTop]
, ins "i32.const" [sxp @Int (1 + l)]
, ins "i32.sub" []
, ins "global.set" [sxp rt.contStackTop]
, ins "return_call_ref" [sxp rt.contType]
]
where
rt = g.runtime
l = V.elemIndex k g.kvars ^?! _Just
lower' g (ExpLet [(r,MkLambda xs ktail m)] e) = do
idx <- defun [i32] [] (replicate 5 scm) \_ -> do
let g' = g & #vars <>~ V.fromList xs
& #kvars <>~ [ktail]
m' <- lower' g' m
pure . mconcat $
[ xs & ifoldMap \n _ ->
popArg g.runtime n <> ins "local.set" [sxp (1+n)]
, m'
]
declareFuncref idx
let g' = g & #vars <>~ [r]
let n = length g.vars
lower' g (ExpLetRec [(r,AbsKappa kap)] e) = do
idx <- lowerKappa g kap
let g' = g & #kvars <>~ [r]
e' <- lower' g' e
pure . mconcat $
[ ins "ref.func" [sxp idx]
, ins "local.set" [sxp (n+1)]
, e'
]
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "push cont" :idx #{idx})
(array.set $cont-stack-type
(global.get $cont-stack)
(global.get $cont-stack-top)
(ref.func #{idx}))
(global.set $cont-stack-top
(i32.add (global.get $cont-stack-top)
(i32.const 1)))
##{e'}
|]
lower' g e = error . show $ e
lower' g (ExpLetRec [(r,AbsLambda lam)] e) = do
idx <- lowerLambda g lam
let g' = g & #vars <>~ [r]
let n = succ $ length g.vars
e' <- lower' g' e
let reg = getArgRegister . fromIntegral $ n
pure [expr|
(i32.const 0)
(ref.func #{idx})
(struct.new $closure)
(global.set #{reg})
##{e'}
|]
lower' g e@(ExpApply f xs ktail) = do
let nargs = length xs
f' <- lowerVal g f
let l = succ $ V.elemIndex ktail g.kvars ^?! _Just
args <- fold <$>
itraverse (\i -> fmap (pushArg $ tonat i) . lowerVal g) xs
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "load args")
##{args}
(i32.const 1)
##{f'}
(ref.cast (ref $closure))
(struct.get $closure $code)
(return_call_ref $cont-type)
(@gyehoek todo
(f' ##{f'})
(ktail #{l}))
|]
lower' g e@(ExpContinue k xs) = do
let nargs = length xs
args <- fold <$>
itraverse (\i -> fmap (pushArg $ tonat i) . lowerVal g) xs
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "push args")
##{args}
(@gyehoek "nargs")
(i32.const #{nargs})
(@gyehoek "pop cont stack")
(global.get $cont-stack-top)
(i32.const #{l})
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)
|]
where
l = succ $ V.elemIndex k g.kvars ^?! _Just
lower' g e = error $ case Gyehoek.Sexp.encode e of
Left _ -> show e
Right x -> T.unpack x
lowerKappa :: GenMod :> es => Env -> Kappa -> Eff es Idx
lowerKappa g e@(MkKappa xs m) = do
let g' = g & #vars <>~ V.fromList xs
m' <- lower' g' m
let origin = encodeOrShow @_ @Text e
idx <- Wasm.defineFunction [wat|
(func (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{m'})
|]
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
pure idx
lowerLambda :: GenMod :> es => Env -> Lambda -> Eff es Idx
lowerLambda g e@(MkLambda xs ktail m) = do
let g' = g & #vars .~ V.fromList xs
& #kvars <>~ [ktail]
m' <- lower' g' m
let origin = encodeOrShow @_ @Text e
idx <- Wasm.defineFunction [wat|
(func (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{m'})
|]
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
pure idx
lowerBinOp
:: (GenMod :> es)
=> Text -> Env -> Val -> Val -> Name -> Exp -> Eff es Wasm.Expr
lowerBinOp op g x y r e = do
=> Text -> Env -> Val -> Val -> Kappa -> Eff es Wasm.Expr
lowerBinOp op g x y (MkKappa [r] e) = do
let op' = SL.Symbol op
let g' = g & #vars <>~ [r]
let n = succ $ length (g ^. #vars)
let reg = getArgRegister . fromIntegral $ n
x' <- lowerVal g x
y' <- lowerVal g y
e' <- lower' g' e
pure . mconcat $
[ lowerVal g x
, ins "ref.cast" [sxp $ ref i31]
, ins "i31.get_s" []
, lowerVal g y
, ins "ref.cast" [sxp $ ref i31]
, ins "i31.get_s" []
, ins op []
, ins "ref.i31" []
, ins "local.set" [sxp (1+n)]
, e'
]
where
g' = g & #vars <>~ [r]
n = length (g ^. #vars)
pure [expr|
##{x'}
(i31.get_s (ref.cast (ref i31)))
(i32.const 1)
i32.shr_u
##{y'}
(i31.get_s (ref.cast (ref i31)))
(i32.const 1)
i32.shr_u
#{op'}
##{makeSmallFixnum}
(global.set #{reg})
##{e'}
|]
scm = ref eq
emitRuntime :: GenMod :> es => Eff es Runtime
emitRuntime :: GenMod :> es => Eff es ()
emitRuntime = mfix \runtime -> do
heapObjectIdx <- Wasm.deftypeNamed "$heap-object" $ Wasm.sub [] $ Wasm.struct
[ Wasm.mut i32 ]
Wasm.defineFunctions [wats|
(import "gyehoek" "write" (func $gh-write (param (ref eq))))
(import "gyehoek" "truthy?" (func $gh-truthy? (param (ref eq))
(result i32)))
|]
-- cont stack
contType <- Wasm.deftype $ Wasm.func [i32] []
contStackType <- Wasm.deftype $ array $ mut $ refnull (fromIdx contType)
contStackTop <- Wasm.defglobal (mut i32) $ ins "i32.const" [sxp @Int 0]
contStack <- Wasm.defglobal (ref (Wasm.fromIdx contStackType)) $
ins "i32.const" [sxp @Int 128]
<> ins "array.new_default" [sxp contStackType]
-- arg array
argArrayType <- Wasm.deftype $ Wasm.array $ mut $ refnull eq
argArray <- Wasm.defglobal (ref (Wasm.fromIdx argArrayType)) $
ins "i32.const" [sxp @Int 32]
<> ins "array.new_default" [sxp argArrayType]
-- consIdx <- Wasm.defun _ _ _ _
result <- Wasm.defglobal (mut (refnull eq)) $ ins "ref.null" [sxp eq]
halt <- Wasm.defun [i32] [] (replicate 5 scm) \_ ->
pure . mconcat $
[ popArg runtime 0
, ins "global.set" [sxp result]
]
pure $ MkRuntime
{argArray,argArrayType
,contStack,contStackTop,contStackType,contType
,result,halt}
-- pure $ error "todo"
Wasm.defineTypes [wats|
(type $heap-object (sub (struct (field $hash (mut i32)))))
(type $cont-type (func (param i32)))
(type $cont-stack-type (array (mut (ref null $cont-type))))
(type $closure (sub $heap-object
(struct (field $hash (mut i32))
(field $code (ref $cont-type)))))
|]
Wasm.defineGlobals [wats|
(global $cont-stack-top (mut i32) (i32.const 0))
(global $cont-stack (ref $cont-stack-type)
(array.new_default $cont-stack-type (i32.const 128)))
|]
-- arg registers
Wasm.defineGlobals [wats|
(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 $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 😼
Wasm.defineGlobal [wat|
(global $result (mut (ref null eq))
(ref.null eq))
|]
-- procedures
let arg = popArg 0
Wasm.defineFunction [wat|
(func $halt (param i32)
##{arg}
(global.set $result))
|]
pure ()
lower :: Exp -> Eff es Text
lower e = fmap Wasm.renderModule . Wasm.execGenMod $ do
runtime <- emitRuntime
let g = MkEnv runtime mempty mempty
scm_entry <- Wasm.defun [i32] [] (replicate 5 scm) \_ ->
lower' g e
main <- Wasm.defun [] [scm] [scm, scm, scm, scm, scm] \_ ->
pure . mconcat $
-- push return cont
[-- ins "ref.func" [sxp halt]
-- make call
ins "i32.const" [sxp @Int 0]
, ins "call" [sxp scm_entry]
, ins "global.get" [sxp runtime.result]
, ins "ref.as_non_null" []
]
Wasm.export "main" "func" main
let g = MkEnv mempty mempty
e' <- lower' g e
let origin = encodeOrShow @_ @Text e
Wasm.defineFunction [wat|
(func $scm-entry (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{e'})
|]
Wasm.defineFunction [wat|
(func (export "main")
(call $scm-entry (i32.const 0))
(call $gh-write (ref.as_non_null (global.get $result))))
|]
lowerProgram :: Program -> Eff es Text
lowerProgram (MkProgram e) = lower e
+145
View File
@@ -0,0 +1,145 @@
{-# 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 Data.Sequence qualified as Seq
import Gyehoek.GenSym
import Effectful.Writer.Static.Shared
import Data.Foldable
import qualified Data.HashMap.Strict as H
import Data.List (elemIndex)
import Gyehoek.Prelude
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
&& not (x `elem` g.contStack)
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)
m' <- stackify (g & #bound .~ H.fromList vs
& #contStack %~ (k:)) m
tell [Stk.MkBlock f xs . toList $ m']
stackify g 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 k ]
<> fromList [ Stk.Push (Stk.ValReg l) | l <- ls ]
<> [ Stk.Call (stackifyVal g f) (stackifyVal g <$> xs) ]
where
k = var g ktail
ls = fold $ (k ^? #ValImm . #ImmLabel)
>>= \klbl -> g ^. #liveness . at klbl
stackify g (ExpContinue k xs) =
-- return continuations require popping the stack. how do we know
-- when a continuation is a return continuation? is this a correct
-- test?
case elemIndex k g.contStack of
Nothing -> pure [ Stk.Call (Stk.ValLabel k) xs' ]
Just j -> do
ktail <- gensym' $ k ^. _Wrapped'
pure $
Seq.replicate j (Stk.PopCont "_")
<> [ Stk.PopCont ktail
, Stk.Call (Stk.ValReg ktail) (stackifyVal g <$> xs)
]
where xs' = 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
ValImm imm -> Stk.ValImm imm
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
-- | 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)
, contStack :: List Name
}
deriving (Show, Generic)
emptyEnv :: Env
emptyEnv = MkEnv mempty mempty ["halt"]
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))
|]
+273 -40
View File
@@ -1,93 +1,212 @@
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE DeriveAnyClass #-}
module Gyehoek.CPS.Syntax
( Val(..)
, Kappa(..)
, Lambda(..)
, Exp(..)
, ExpF(..)
, Name(..)
, Prim(..)
, Program(..)
, Lit(..)
, Imm(..)
, Obj(..)
, Hob(..)
, pattern Void
, pattern Halt
, pattern Halt1
, _MkKappa
, _ExpPrim
, _ExpFix
, _ExpLetRec
, _ExpApply
, _AbsLambda'
, binders
, body
, op
, args
, cont
, cps
, pattern AbsLambda'
, pattern AbsKappa'
, Abs(..)
, Free(..)
, pattern ValLabel
, labelName -- don't like that this is part of the api
)
where
import Language.SexpGrammar qualified as S
import Gyehoek.Sexp qualified
import Gyehoek.Scheme.Syntax (Name (..), Prim(..), primSexpIso, Lit(..), pattern Void)
import Data.Text (Text)
import Data.List (List)
import GHC.Generics (Generic)
import Language.SexpGrammar.Generic
import Control.Category
import Control.Lens
import Data.Text qualified as T
import Data.Generics.Labels
import Prelude hiding ((.), id)
import Data.List.NonEmpty (NonEmpty)
import Data.InvertibleGrammar.Base qualified as IGB
import Data.InvertibleGrammar.Base ((:-)((:-)))
import qualified Data.InvertibleGrammar as IG
import Language.Haskell.TH.Quote (QuasiQuoter)
import Language.Sexp.Located (Sexp)
import qualified Data.InvertibleGrammar.Base as IG
import Data.InvertibleGrammar.Base (type (:-)((:-)))
import qualified Data.HashSet as HS
import Data.Monoid (Endo)
import Data.Functor.Foldable.TH
import qualified Gyehoek.Sexp as GS
import qualified Language.Sexp.Located as SL
import Data.Data.Lens (uniplate)
import Gyehoek.Prelude hiding (op)
-- Data types
data Val
= ValLabel Name
= ValImm Imm
| ValVar Name
| ValLit Lit
deriving (Show, Generic)
deriving (Show, Generic, Data, Eq)
data Kappa = MkKappa (List Name) Exp
deriving (Show, Generic)
pattern ValLabel :: Name -> Val
pattern ValLabel x = ValImm (ImmLabel x)
data Lambda = MkLambda (List Name) Name Exp
deriving (Show, Generic)
data Imm
= ImmInt Int
| ImmBool Bool
| ImmLabel Name
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
data Obj
= ObjImm Imm
| ObjHob Hob
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
-- | a heap object.
data Hob
= HobClosure { label :: Name, env :: List Obj }
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
data Kappa = MkKappa { binders :: List Name, body :: Exp }
deriving (Show, Generic, Data, Eq)
data Lambda = MkLambda { binders :: List Name, ktail :: Name, body :: Exp }
deriving (Show, Generic, Data, Eq)
data Abs
= AbsKappa Kappa
| AbsLambda Lambda
deriving (Show, Generic, Data, Eq)
pattern AbsKappa' :: [Name] -> Exp -> Abs
pattern AbsKappa' xs e = AbsKappa (MkKappa xs e)
pattern AbsLambda' :: [Name] -> Name -> Exp -> Abs
pattern AbsLambda' xs e ktail = AbsLambda (MkLambda xs e ktail)
data Exp
= ExpPrim (Prim Val) (List Name) Exp
| ExpFix (NonEmpty (Name, Kappa)) Exp
| ExpLet (NonEmpty (Name, Lambda)) Exp
= ExpPrim (Prim Val) Kappa
| ExpLetRec { binders :: List (Name, Abs), body :: Exp }
| ExpContinue Name (List Val)
| ExpIf Val Exp Exp
| ExpApply Val (List Val)
deriving (Show, Generic)
| ExpApply
{ op :: Val
, args :: List Val
, cont :: Name
}
deriving (Show, Generic, Data, Eq)
pattern Halt :: List Val -> Exp
pattern Halt xs = ExpApply (ValVar "halt") xs
pattern Halt xs = ExpContinue "halt" xs
pattern Halt1 :: Val -> Exp
pattern Halt1 x = ExpApply (ValVar "halt") [x]
pattern Halt1 x = ExpContinue "halt" [x]
data Def = DefConstant Name Exp
deriving (Show, Generic)
deriving (Show, Generic, Data)
data Program = MkProgram
{ body :: Exp
}
deriving (Show, Generic)
deriving (Show, Generic, Data)
makePrisms ''Kappa
-- makeLenses ''Kappa
makePrisms ''Exp
-- makeLenses ''Exp
-- makeFieldsNoPrefix ''Exp
-- makeFieldsNoPrefix ''Kappa
-- makeLensesWith abbreviatedFields ''Exp
-- makeLensesFor [("binders", "_binders"), ("body", "_body")] ''Exp
makeFieldsId ''Exp
makeFieldsId ''Kappa
makeFieldsId ''Lambda
makeBaseFunctor ''Exp
instance HasBinders Abs (List Name) where
binders k (AbsKappa kap) = AbsKappa <$> binders k kap
binders k (AbsLambda lam) = AbsLambda <$> binders k lam
instance HasBody Abs Exp where
body k (AbsKappa kap) = AbsKappa <$> body k kap
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)
instance Plated Exp where
plate = uniplate
-- plate k = \case
-- ExpPrim p kap -> ExpPrim p <$> body k kap
-- ExpLetRec bs e -> ExpLetRec <$> (each . _2 . body) k bs <*> k e
-- ExpContinue c xs -> pure $ ExpContinue c xs
-- ExpIf c t f -> ExpIf c <$> k t <*> k f
-- ExpApply f xs ktail -> pure $ ExpApply f xs ktail
-- SexpIso instances
instance S.SexpIso Val where
sexpIso = match
$ With (. label)
$ With (. var)
$ With (. S.sexpIso)
$ With (\imm -> imm . S.sexpIso)
$ With (\var -> var . S.sexpIso)
$ End
instance S.SexpIso Obj where
sexpIso = match
$ With (\imm -> imm . S.sexpIso)
$ With (\hob -> hob . S.sexpIso)
$ End
instance S.SexpIso Imm where
sexpIso = match
$ With (. S.int)
$ With (. GS.schemeBool)
$ With (. labelName)
$ End
labelName :: S.SexpGrammar Name
labelName = S.coproduct
[ S.sexpIso @Name >>> Gyehoek.Sexp.prismIso
(S.expected "label")
(prefixed @Name "$")
, S.list $ S.el (S.sym "$") >>> S.el (S.sexpIso @Name)
]
instance S.SexpIso Hob where
sexpIso = match
$ With (. closure)
$ End
where
label = S.keyword >>> S.iso MkName getName
var = S.sexpIso
-- closures can be printed, but not parsed.
closure :: S.Grammar S.Position (Sexp :- t) (List Obj :- Name :- t)
closure = IG.Flip $ IG.PartialIso
(\(env:-code:-t) -> SL.Modified SL.Hash [GS.sx|(#{code} ##{env})|] :- t)
(const . Left $ mempty)
instance S.SexpIso Lambda where
sexpIso = match
@@ -96,9 +215,18 @@ instance S.SexpIso Lambda where
where
lambda = S.list $
S.el Gyehoek.Sexp.lambdaKeyword
>>> S.el (S.list (S.rest S.sexpIso))
>>> S.el S.sexpIso
>>> S.el binders
>>> S.el S.sexpIso
binders :: forall t.
IG.Grammar S.Position (Sexp :- t) (Name :- List Name :- t)
binders = S.list $
S.rest (S.sexpIso @Name)
>>> S.onTail (S.flipped $ IG.PartialIso
(\(ktail:-args:-t) -> (args ++ [ktail]) :- t)
(\(args:-t) -> case args ^? _Snoc of
Just (args',ktail) -> Right $ ktail :- args' :- t
Nothing -> Left $ S.expected "cont param")
)
instance S.SexpIso Kappa where
sexpIso = match
@@ -110,11 +238,16 @@ instance S.SexpIso Kappa where
>>> S.el (S.list $ S.rest S.sexpIso)
>>> S.el S.sexpIso
instance S.SexpIso Abs where
sexpIso = match
$ With (\lambda -> lambda . S.sexpIso)
$ With (\kappa -> kappa . S.sexpIso)
$ End
instance S.SexpIso Exp where
sexpIso = match
$ With (. prim)
$ With (. fix)
$ With (. let_)
$ With (. letrec)
$ With (. continue)
$ With (. if_)
$ With (. app)
@@ -124,16 +257,116 @@ instance S.SexpIso Exp where
S.el (S.sym "continue")
>>> S.el S.sexpIso
>>> S.rest S.sexpIso
fix = Gyehoek.Sexp.let_ "fix" S.sexpIso S.sexpIso S.sexpIso
let_ = Gyehoek.Sexp.let_ "let" S.sexpIso S.sexpIso S.sexpIso
letrec = Gyehoek.Sexp.let_ "letrec" S.sexpIso S.sexpIso S.sexpIso
if_ = S.list $ S.el (S.sym "if")
>>> S.el S.sexpIso >>> S.el S.sexpIso >>> S.el S.sexpIso
app = S.list $ S.el S.sexpIso >>> S.rest S.sexpIso
app :: forall t.
IG.Grammar S.Position (Sexp :- t) (Name :- ([Val] :- (Val :- t)))
app = S.list $ S.el (S.sexpIso @Val)
-- >>> S.flipped Gyehoek.Sexp.nonEmptyGrammar
>>> S.rest (S.sexpIso @Val)
-- >>> _
>>> S.onTail (S.flipped $ IG.PartialIso
(\(karg :- args :- op :- t) ->
(args ++ [ValVar karg]) :- op :- t)
(\(xs :- op :- t) -> case xs ^? _Snoc of
Just (args,preview #ValVar -> Just karg) ->
Right $ karg:- args :- op :- t
_ -> Left $ S.expected "continuation arg"
))
where
_ = S.flipped $ Gyehoek.Sexp.nonEmptyGrammar @S.Position @Val
prim = S.list $
S.el (S.sym "prim")
>>> S.el (primSexpIso id (S.sexpIso @Val))
>>> S.el S.sexpIso
>>> S.el S.sexpIso
instance S.SexpIso Program where
sexpIso = with \prog -> S.sexpIso @Exp >>> prog
-- quasiquoters
class Data a => CPS a where
toCPS :: Sexp -> a
instance CPS Exp where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Val where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Kappa where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Lambda where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Abs where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Program where toCPS = Gyehoek.Sexp.fromSexp
cps :: QuasiQuoter
cps = Gyehoek.Sexp.makeSx' [| toCPS |]
deleteFrom :: (Foldable f, Hashable a) => f a -> HashSet a -> HashSet a
deleteFrom = flip $ foldr HS.delete
insertFrom :: (Foldable f, Hashable a) => f a -> HashSet a -> HashSet a
insertFrom = flip $ foldr HS.insert
toHashSetOf :: Hashable a => Getting (Endo (HashSet a)) s a -> s -> HashSet a
toHashSetOf l = foldrOf l HS.insert mempty
class Free a where
free :: a -> HashSet Name
free = freeWithBound mempty
freeWithBound :: HashSet Name -> a -> HashSet Name
freeWithBound bound = HS.fromList . freeWithBound' bound
-- | 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 ->
p & toListOf (folded . #ValVar . filtered (`notElem` bound))
& (<> freeWithBound' bound k)
ExpLetRec bs m ->
foldMapOf (each . _2) (freeWithBound' bound') bs
<> freeWithBound' 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))
<> freeWithBound' bound t <> freeWithBound' bound f
ExpApply f xs k ->
(f:xs) ^.. (each . #ValVar . filtered (`notElem` bound))
<> (k ^.. filtered (`notElem` bound))
instance Free Kappa where
freeWithBound' bound (MkKappa xs m) =
freeWithBound' (bound & insertFrom xs) m
instance Free Lambda where
freeWithBound' bound (MkLambda xs k m) =
freeWithBound' (bound & insertFrom (k:xs)) m
class Vars a where
-- | 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
+90 -34
View File
@@ -1,44 +1,38 @@
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OrPatterns #-}
module Gyehoek.Driver
(main, lower_e2e, convert_e2e, parse_e2e)
(main, lower_e2e, convert_e2e, parse_e2e, readScm, eval_e2e)
where
import Gyehoek.Options
import qualified Data.Text.IO as TIO
import Data.Text (Text)
import Prelude hiding (readFile)
import Options.Applicative
import Control.Lens
import Data.Generics.Labels
import System.OsPath (OsPath)
import System.FilePath ((-<.>), dropExtension)
import Effectful.FileSystem
import Effectful
import Effectful.FileSystem.IO qualified as FS
import Effectful.FileSystem.IO.ByteString qualified as FB
import Gyehoek.GenSym (runGenSym, GenSym, gensym, gensym')
import Gyehoek.GenSym (runGenSym, GenSym)
import qualified Gyehoek.Sexp as Sexp
import Data.Text.Lens
import Data.List (List)
import qualified Gyehoek.Scheme.Syntax as Scm
import Effectful.Exception
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import System.IO (Handle)
import Data.List.NonEmpty (NonEmpty)
import qualified Cradle as C
import System.IO qualified as IO
import Gyehoek.CPS.Convert
import Gyehoek.CPS.Lower
import Data.Foldable
import qualified Gyehoek.Scheme.Syntax
import Gyehoek.CPS.Syntax qualified as Cps
import Data.Maybe (fromMaybe)
import Gyehoek.CPS.Eval qualified as CPS
import Control.Monad
import Text.Pretty.Simple (pShow, pShowNoColor)
import Text.Pretty.Simple (pShowNoColor)
import System.Process.Typed
import System.Environment.Blank (getEnvDefault)
import qualified Data.Text.IO as TIO
import qualified Data.ByteString.Lazy as BS
import Gyehoek.CPS.Stackify (stackifyProgram)
import Gyehoek.Stack.VM (eval, writeObj, Obj)
import qualified Data.Text as T
import Gyehoek.Stack.Syntax qualified as Stk
import Gyehoek.CPS.Close (closeProgram)
import Control.Lens.Extras (is)
import Control.Arrow ((>>>))
import Gyehoek.Prelude
main :: IO ()
@@ -48,8 +42,8 @@ main = do
hPutStr :: FileSystem :> es => Handle -> Text -> Eff es ()
hPutStr h = FB.hPutStr h . T.encodeUtf8
-- hPutStr :: FileSystem :> es => Handle -> Text -> Eff es ()
-- hPutStr h = FB.hPutStr h . T.encodeUtf8
hPutStrLn :: FileSystem :> es => Handle -> Text -> Eff es ()
hPutStrLn h = FB.hPutStrLn h . T.encodeUtf8
@@ -57,8 +51,8 @@ hPutStrLn h = FB.hPutStrLn h . T.encodeUtf8
hGetContents :: FileSystem :> es => Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readFile :: FileSystem :> es => FilePath -> Eff es Text
readFile f = FS.withFile f FS.ReadMode hGetContents
-- readFile :: FileSystem :> es => FilePath -> Eff es Text
-- readFile f = FS.withFile f FS.ReadMode hGetContents
withFile
:: (FileSystem :> es)
@@ -67,12 +61,51 @@ withFile "-" FS.ReadMode k = k FS.stdin
withFile "-" (FS.WriteMode; FS.AppendMode) k = k FS.stdout
withFile f m k = FS.withFile f m k
fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
readScm :: FileSystem :> es => FilePath -> Eff es Scm.Program
readScm f =
withFile f FS.ReadMode $ \h ->
Sexp.parseSexps @Scm.CommandOrDef f <$> hGetContents h
Sexp.parseSexps @Scm.CommandOrDef (fileName f) <$> hGetContents h
>>= either error (pure . Scm.MkProgram)
inspectWasm :: IOE :> es => Text -> Eff es ()
inspectWasm wat = do
pager_cmd <- liftIO $ getEnvDefault "PAGER" "less"
let wasmtools_cfg
= proc "wasm-tools" ["print", "-pf", "--print-operand-stack"
,"--color", "always", "-"]
& setStdin createPipe
& setStdout createPipe
& setStderr inherit
let pager_cfg = proc pager_cmd []
& setStdin createPipe
& setStdout inherit
& setStderr inherit
liftIO $ withProcessWait_ wasmtools_cfg \wasmtools -> do
TIO.hPutStrLn (getStdin wasmtools) wat
IO.hFlush (getStdin wasmtools)
IO.hClose (getStdin wasmtools)
withProcessWait_ pager_cfg \pager -> do
t <- BS.hGetContents (getStdout wasmtools)
BS.hPut (getStdin pager) t
IO.hFlush (getStdin pager)
IO.hClose (getStdin pager)
dumpOrRun
:: Monad m
=> Bool -> Bool
-> m a
-> (a -> m ()) -> (a -> m ())
-> m ()
dumpOrRun dump run acquire do_dump do_run =
when (dump || run) do
x <- acquire
when dump (do_dump x)
when run (do_run x)
driver
:: (GenSym :> es, FileSystem :> es, IOE :> es)
=> Options -> Eff es ()
@@ -83,17 +116,40 @@ driver opts = do
cps <- convertProgram scm
when opts.dumpCPS do
hPutStrLn FS.stdout $ Sexp.encodePretty cps ^?! _Right
wat <- lowerProgram cps
withFile opts.output FS.WriteMode \h ->
hPutStrLn h wat
closedCps <- closeProgram cps
when opts.dumpClosed do
hPutStrLn FS.stdout $ Sexp.encodePretty closedCps ^?! _Right
let rt_is p = is (_Just . p) opts.runtime
dumpOrRun opts.dumpStackified (rt_is #Stackify)
(stackifyProgram closedCps)
(hPutStrLn FS.stdout . Stk.encodeProgram)
(eval >>> fmap writeObj
>>> T.unwords
>>> hPutStrLn FS.stdout)
when (rt_is #CPS) do
closedCps
& CPS.evalProgram
& fmap writeObj
& T.unwords
& hPutStrLn FS.stdout
dumpOrRun opts.inspectWasm (rt_is #Wasm)
(lowerProgram cps)
inspectWasm
(\wat -> withFile opts.output FS.WriteMode \h -> hPutStrLn h wat)
parse_e2e :: FilePath -> IO Scm.Program
parse_e2e = runEff . runFileSystem . readScm
convert_e2e :: FilePath -> IO Cps.Program
convert_e2e = runEff . runFileSystem . runGenSym . (convertProgram <=< readScm)
convert_e2e :: FilePath -> IO CPS.Program
convert_e2e = runEff . runFileSystem . runGenSym
. (closeProgram <=< convertProgram <=< readScm)
lower_e2e :: FilePath -> IO Text
lower_e2e =
runEff . runFileSystem . runGenSym
. (lowerProgram <=< convertProgram <=< readScm)
. (lowerProgram <=< closeProgram <=< convertProgram <=< readScm)
eval_e2e :: FilePath -> IO (List Obj)
eval_e2e fp = runEff . runFileSystem . runGenSym $ do
stk <- stackifyProgram <=< closeProgram <=< convertProgram <=< readScm $ fp
pure . eval $ stk
-1
View File
@@ -8,7 +8,6 @@ import Effectful.Dispatch.Dynamic
import Effectful
import Data.String (IsString(fromString))
import Data.Text (Text)
import qualified Data.Text.Short as ST
class Gen a where
+25
View File
@@ -0,0 +1,25 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Gyehoek.Language
( Language(..)
) where
import Data.Kind (Type)
import Gyehoek.Prelude
import Language.SexpGrammar (Position, Grammar, (:-), Sexp)
class Language l where
type Program l :: Type
languageName :: Text
programGrammar :: forall t. Grammar Position (List Sexp :- t) (Program l :- t)
readProgramFile
:: forall l es. Language l
=> FilePath -> Eff es (Program l)
readProgramFile fp = _
readProgramStringPos
:: forall l. Language l
=> Position -> Text -> Either Text (Program l)
readProgramStringPos pos s = _
+72 -39
View File
@@ -1,58 +1,91 @@
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RecordWildCards #-}
module Gyehoek.Options
( Options(..)
, parser
, Runtime(..)
, Language(..)
)
where
import System.IO (Handle)
import Data.HashSet (HashSet)
import Options.Applicative
import System.FilePath
import qualified Data.HashSet as HS
import Control.Lens hiding (argument)
import GHC.Generics (Generic)
import Data.Foldable
import Gyehoek.Prelude hiding (argument)
data Runtime = Stackify | Wasm | CPS
deriving (Show, Generic, Eq)
data Language
= LanguageScheme
| LanguageCPS
| LanguageClosed
| LanguageStackified
| LanguageWasm
deriving (Show, Generic, Eq)
data Options = MkOptions
{ -- dumpANF :: Maybe FilePath
-- , dumpQBE :: Maybe FilePath
dumpCPS :: Bool
{ dumpClosed :: Bool
, dumpCPS :: Bool
, dumpParsed :: Bool
, dumpStackified :: Bool
, runtime :: Maybe Runtime
, inspectWasm :: Bool
, output :: FilePath
, sourceFile :: FilePath
, sourceLanguage :: Language
}
deriving (Show, Generic)
-- osPath :: ReadM _
-- osPath = eitherReader $
-- (_Left %~ show) . encodeUtf @(Either _)
languageValues = ["scheme","cps","closed","stackified","wasm"]
languageReader = maybeReader \case
"scheme" -> Just LanguageScheme
"cps" -> Just LanguageCPS
"closed" -> Just LanguageClosed
"stackified" -> Just LanguageStackified
"wasm" -> Just LanguageWasm
_ -> Nothing
-- parseDumpQBE =
-- optional $ strOption
-- ( long "dump-qbe"
-- <> metavar "FILE"
-- )
-- parseDumpANF =
-- optional $ strOption
-- ( long "dump-anf"
-- <> metavar "FILE"
-- )
parseOutput = strOption
( long "output"
<> short 'o'
<> metavar "FILE"
<> value "-"
)
parseDumpCPS = switch (long "dump-cps")
parseDumpParsed = switch (long "dump-parsed")
runtimeValues = ["stackify","wasm","cps","none"]
runtimeReader = maybeReader \case
"stackify" -> Just (Just Stackify)
"wasm" -> Just (Just Wasm)
"cps" -> Just (Just CPS)
"none" -> Just Nothing
_ -> Nothing
parser :: Parser Options
parser = MkOptions
<$> parseDumpCPS
<*> parseDumpParsed
<*> parseOutput
<*> argument str (metavar "FILE")
parser = do
dumpClosed <- switch (long "dump-closed")
dumpCPS <- switch (long "dump-cps")
dumpStackified <- switch (long "dump-stackified")
dumpParsed <- switch (long "dump-parsed")
inspectWasm <- switch $ long "inspect-wasm" <> short 'p'
runtime <- option runtimeReader . fold $
[ long "runtime"
, short 'R'
, value (Just Stackify)
, completeWith runtimeValues
, showDefaultWith $ const "stackify"
, metavar "RUNTIME"
]
sourceLanguage <- option languageReader . fold $
[ long "source"
, short 'S'
, value LanguageScheme
, completeWith languageValues
, showDefaultWith $ const "scheme"
, metavar "LANGUAGE"
]
output <- strOption . fold $
[ long "output"
, short 'o'
, metavar "FILE"
, value "-"
, action "file"
]
sourceFile <- argument str . fold $
[ metavar "FILE"
, action "file"
]
pure $ MkOptions {..}

Some files were not shown because too many files have changed in this diff Show More