19 Commits
Author SHA1 Message Date
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
50 changed files with 3628 additions and 802 deletions
+3 -1
View File
@@ -2,4 +2,6 @@
. ((eval . ((eval
. (progn (defun apply-cabal-fmt-h () . (progn (defun apply-cabal-fmt-h ()
(haskell-mode-buffer-apply-command "cabal-fmt")) (haskell-mode-buffer-apply-command "cabal-fmt"))
(add-hook 'before-save-hook #'apply-cabal-fmt-h nil t)))))) (add-hook 'before-save-hook #'apply-cabal-fmt-h nil t)
(add-to-list 'haskell-font-lock-quasi-quote-modes
'("cps" . scheme-mode)))))))
+6
View File
@@ -15,3 +15,9 @@ XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX00
zero indicates a 30-bit fixnum / zero indicates a 30-bit fixnum /
in the upper bits in the upper bits
#+end_example #+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
Generated
+16
View File
@@ -66,6 +66,21 @@
"type": "github" "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-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -587,6 +602,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"crane": "crane",
"haskellNix": "haskellNix", "haskellNix": "haskellNix",
"nixpkgs": [ "nixpkgs": [
"haskellNix", "haskellNix",
+19 -18
View File
@@ -7,6 +7,7 @@
url = "git+https://git.deertopia.net/msyds/sydpkgs"; url = "git+https://git.deertopia.net/msyds/sydpkgs";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
crane.url = "github:ipetkov/crane";
}; };
outputs = { self, nixpkgs, sydpkgs, haskellNix, ... }@inputs: outputs = { self, nixpkgs, sydpkgs, haskellNix, ... }@inputs:
@@ -16,13 +17,13 @@
"x86_64-darwin" "x86_64-linux" "x86_64-darwin" "x86_64-linux"
]; ];
overlays = [ overlays = [
haskellNix.overlay haskellNix.overlay
(final: prev: { (final: prev: {
gyehoek-wasmtime-wrapper = final.callPackage ./wasmtime.nix {}; shake-wrapper = final.callPackage ./shake-wrapper.nix {};
}) gyehoek-runtime = final.callPackage ./runtime {
(final: prev: { crane-lib = inputs.crane.mkLib final;
};
gyehoek = final.haskell-nix.project' { gyehoek = final.haskell-nix.project' {
src = ./.; src = ./.;
compiler-nix-name = "ghc912"; compiler-nix-name = "ghc912";
@@ -30,32 +31,32 @@
packages.gyehoek.components.tests.test.preCheck = packages.gyehoek.components.tests.test.preCheck =
let let
bin = [ bin = [
pkgs.gyehoek-wasmtime-wrapper pkgs.git # tasty uses git diff
pkgs.git
]; ];
in '' in ''
# Wasmtime requires a cache in $HOME. This is less export GYEHOEK_RUNTIME=${lib.getExe final.gyehoek-runtime}
# painful than reconfiguring the cache location.
export HOME=$(mktemp -d)
export PATH=${lib.makeBinPath bin}:$PATH export PATH=${lib.makeBinPath bin}:$PATH
''; '';
})]; })];
shell = { shell = {
withHoogle = true; withHoogle = true;
inputsFrom = []; inputsFrom = [
final.gyehoek-runtime
];
tools = { tools = {
cabal = {}; cabal = {};
haskell-language-server = {}; haskell-language-server = {};
}; };
buildInputs = with final; [ buildInputs = with final; [
haskellPackages.cabal-fmt haskellPackages.cabal-fmt
self.packages.${final.stdenv.hostPlatform.system}.shake shake-wrapper
final.wabt wabt
final.nodejs nodejs
final.wasm-tools wasm-tools
final.wac-cli wac-cli
final.guile guile
final.gyehoek-wasmtime-wrapper rust-analyzer
wasmtime
]; ];
}; };
}; };
@@ -87,7 +88,7 @@
hf.packages.${system} // lib.fix (packages: { hf.packages.${system} // lib.fix (packages: {
gyehoek = hf.packages.${system}."gyehoek:exe:gyehoek"; gyehoek = hf.packages.${system}."gyehoek:exe:gyehoek";
default = packages.gyehoek; default = packages.gyehoek;
shake = pkgs.callPackage ./shake-wrapper.nix {}; inherit (pkgs) gyehoek-runtime shake-wrapper;
})); }));
devShells = each-system devShells = each-system
+5
View File
@@ -0,0 +1,5 @@
;; apply `f' to `x' twice.
((λ (f x)
(f (f x)))
(λ (x) (+ x 4))
9)
-3
View File
@@ -1,5 +1,2 @@
ret > ExitSuccess ret > ExitSuccess
out > 22 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 > #f
+1
View File
@@ -0,0 +1 @@
#f
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 128
+1
View File
@@ -0,0 +1 @@
(((λ (f) f) (λ (x) (* x 4))) 32)
-3
View File
@@ -1,5 +1,2 @@
ret > ExitSuccess ret > ExitSuccess
out > 555 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)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 777
+1
View File
@@ -0,0 +1 @@
(if 123 777 555)
-3
View File
@@ -1,5 +1,2 @@
ret > ExitSuccess ret > ExitSuccess
out > 777 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)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #<procedure>
+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
+33 -18
View File
@@ -25,6 +25,11 @@ common ghcstuffs
default-extensions: default-extensions:
BlockArguments BlockArguments
DeriveGeneric DeriveGeneric
DerivingVia
DuplicateRecordFields
NoFieldSelectors
OrPatterns
OverloadedLabels
OverloadedRecordDot OverloadedRecordDot
OverloadedStrings OverloadedStrings
PartialTypeSignatures PartialTypeSignatures
@@ -34,9 +39,8 @@ common ghcstuffs
executable gyehoek executable gyehoek
import: ghcstuffs, ghcstuffs-dev import: ghcstuffs, ghcstuffs-dev
main-is: Main.hs main-is: Main.hs
build-depends: build-depends:
, base ^>=4.21.2.0 , base ^>=4.21.2.0
, gyehoek , gyehoek
hs-source-dirs: app hs-source-dirs: app
@@ -44,25 +48,25 @@ executable gyehoek
library library
import: ghcstuffs, ghcstuffs-dev import: ghcstuffs, ghcstuffs-dev
ghc-options: -fplugin=Effectful.Plugin ghc-options: -fplugin=Effectful.Plugin
-- cabal-fmt: expand src -- cabal-fmt: expand src
exposed-modules: exposed-modules:
Gyehoek.CPS.Convert Gyehoek.CPS.Convert
Gyehoek.CPS.Lower Gyehoek.CPS.Lower
Gyehoek.CPS.Syntax Gyehoek.CPS.Syntax
Gyehoek.Driver
Gyehoek.GenSym Gyehoek.GenSym
Gyehoek.Options Gyehoek.Options
Gyehoek.Scheme.Syntax Gyehoek.Scheme.Syntax
Gyehoek.Sexp Gyehoek.Sexp
Gyehoek.Wasm Gyehoek.Wasm
Gyehoek.Driver
build-depends: build-depends:
, base ^>=4.21.2.0 , base ^>=4.21.2.0
, binary , binary
, containers , containers
, cradle , typed-process
, effectful , effectful
, effectful-core , effectful-core
, effectful-plugin , effectful-plugin
@@ -74,30 +78,41 @@ library
, megaparsec , megaparsec
, mtl , mtl
, optparse-applicative , optparse-applicative
, pretty-simple
, prettyprinter , prettyprinter
, process , process
, recursion-schemes , recursion-schemes
, sexp-grammar , sexp-grammar
, string-interpolate
, template-haskell , template-haskell
, text , text
, text-short , text-short
, unordered-containers , unordered-containers
, vector , vector
, string-interpolate , bytestring
, pretty-simple
hs-source-dirs: src hs-source-dirs: src
default-language: GHC2024 default-language: GHC2024
test-suite test test-suite test
import: ghcstuffs, ghcstuffs-dev import: ghcstuffs, ghcstuffs-dev
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
hs-source-dirs: test hs-source-dirs: test
main-is: Main.hs main-is: Main.hs
build-depends: base other-modules:
, gyehoek Gyehoek.Test.CPS.Syntax
, filepath Gyehoek.Test.Golden
, tasty Gyehoek.Test.Sexp
, tasty-silver
, directory build-depends:
default-language: GHC2024 , base
, directory
, filepath
, gyehoek
, process-extras
, sexp-grammar
, tasty
, tasty-hunit
, tasty-silver
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>
+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![],
)
)
)
))
),
]
)?
)
)
}
+19 -7
View File
@@ -1,7 +1,7 @@
{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedLists #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Convert module Gyehoek.CPS.Convert
( convert ( convertProgram
, convertProgram
) where ) where
import Gyehoek.CPS.Syntax import Gyehoek.CPS.Syntax
@@ -12,6 +12,7 @@ import Effectful
import Control.Monad.Cont qualified as Cont 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
-- 뻘짓이어라 -- 뻘짓이어라
@@ -21,6 +22,14 @@ telescope
-> t a -> (t b -> r) -> r -> t a -> (t b -> r) -> r
telescope f = Cont.runCont . traverse (Cont.cont . f) telescope f = Cont.runCont . traverse (Cont.cont . f)
pattern Atomic e <-
e@( Scm.ExpLambda _ _
; Scm.ExpVar _
; Scm.ExpLit _ )
-- | Transform an expression with a meta-continuation.
convert convert
:: forall es. (GenSym :> es) :: forall es. (GenSym :> es)
=> Scm.Exp -> (Val -> Eff es Exp) -> Eff es Exp => Scm.Exp -> (Val -> Eff es Exp) -> Eff es Exp
@@ -31,21 +40,24 @@ convert (Scm.ExpLit l) k = k $ ValLit l
convert (Scm.ExpPrim p) k = convert (Scm.ExpPrim p) k =
telescope (convert @es) p \p' -> do telescope (convert @es) p \p' -> do
r <- gensym' "r" r <- gensym' "r"
ExpPrim p' [r] <$> k (ValVar r) ExpPrim p' . MkKappa [r] <$> k (ValVar r)
convert (Scm.ExpLambda xs e) k = do convert (Scm.ExpLambda xs e) k = do
f <- gensym' "λ-body" f <- gensym' "λ-body"
ktail <- gensym' "λ-tail" ktail <- gensym' "λ-tail"
m <- convert e $ \e' -> m <- convert e $ \e' -> pure $ ExpContinue ktail [e']
pure $ ExpContinue ktail [e'] ke <- k $ ValVar f
ExpLet [(f, MkLambda xs ktail m)] <$> k (ValVar f) pure [cps|
(letrec ((#{f} (λ (##{xs} #{ktail}) #{m})))
#{ke})
|]
convert (Scm.ExpApply f xs) k = convert (Scm.ExpApply f xs) k =
telescope (convert @es) (f:|xs) \(f':|xs') -> do telescope (convert @es) (f:|xs) \(f':|xs') -> do
r <- gensym' "r" r <- gensym' "r"
x <- gensym' "x" x <- gensym' "x"
m <- k (ValVar 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 = _ convert (Scm.ExpBegin xs) k = _
+286 -227
View File
@@ -6,44 +6,33 @@
{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE ApplicativeDo #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{-# LANGUAGE RecursiveDo #-}
{- HLINT ignore "Use camelCase" -} {- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Lower module Gyehoek.CPS.Lower
(lower, lowerProgram) where (lower, lowerProgram) where
import Gyehoek.CPS.Syntax import Gyehoek.CPS.Syntax
import Data.Generics.Labels import Data.Generics.Labels ()
import Gyehoek.Scheme.Syntax qualified as Scm
import Gyehoek.GenSym
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.List (List)
import Effectful import Effectful
import Control.Monad.Cont qualified as Cont
import Effectful.Writer.Static.Local
import Data.Text (Text) import Data.Text (Text)
import Data.Vector.Strict (Vector) import Data.Vector.Strict (Vector)
import Control.Lens import Control.Lens hiding (op)
import Data.Foldable
import Data.HashMap.Strict (HashMap)
import Numeric.Natural import Numeric.Natural
import GHC.Generics (Generic) 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 qualified Data.Vector.Strict as V
import Data.IntMap.Strict (IntMap)
import Data.String.Interpolate
import Gyehoek.Wasm qualified as Wasm import Gyehoek.Wasm qualified as Wasm
import Gyehoek.Wasm hiding (Expr) import Gyehoek.Wasm hiding (Expr)
import Language.Sexp.Located qualified as SL import Language.Sexp.Located qualified as SL
import Debug.Pretty.Simple
import Control.Monad.Fix import Control.Monad.Fix
import Language.Sexp.Located (Sexp) import qualified Gyehoek.Sexp
import Data.Functor.Foldable (cata) import Data.Text qualified as T
import Data.Foldable (fold)
import Gyehoek.Sexp (encodeOrShow, toSexp)
import Debug.Pretty.Simple
data Env = MkEnv data Env = MkEnv
{ runtime :: Runtime { vars :: Vector Name
, vars :: Vector Name
, kvars :: Vector Name , kvars :: Vector Name
} }
deriving (Show, Generic) deriving (Show, Generic)
@@ -54,238 +43,308 @@ type instance IxValue Env = Name
instance Ixed Env where instance Ixed Env where
ix i = #vars . ix (fromIntegral i) 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 -- | @makeSmallFixnum@ emits an expression injecting the i32 on top
-- of the stack into the SCM unitype. -- of the stack into the SCM unitype.
-- makeSmallFixnum :: Wasm.Expr makeSmallFixnum :: Wasm.Expr
-- makeSmallFixnum = mconcat makeSmallFixnum = [expr|
-- [ ins "i32.const" [sxp @Int 1] (@gyehoek "construct small fixnum")
-- , ins "i32.shl" [] (i32.const 1)
-- , ins "ref.i31" [] i32.shl
-- ] ref.i31
|]
-- | 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 :: Runtime -> Int -> Wasm.Expr -> Wasm.Expr pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
-- pushArg (MkRuntime {argArrayType,argArray}) n e = mconcat pushArg n e = [expr|
-- [ ins "global.get" [sxp argArray] (@gyehoek "push argument")
-- , ins "i32.const" [sxp n] (global.get $arg-array)
-- , e (i32.const #{n})
-- , ins "array.set" [sxp argArrayType] ##{e}
-- ] (array.set $arg-array-type)
|]
-- | 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 :: Runtime -> Int -> Wasm.Expr popArg :: Int -> Wasm.Expr
-- popArg (MkRuntime {argArrayType,argArray}) n = mconcat popArg n = [expr|
-- [ ins "global.get" [sxp argArray] (@gyehoek "pop argument")
-- , ins "i32.const" [sxp n] (global.get $arg-array)
-- , ins "array.get" [sxp argArrayType] (i32.const #{n})
-- , ins "ref.as_non_null" [] (array.get $arg-array-type)
-- ] ref.as_non_null
|]
-- lowerVal :: Env -> Val -> Wasm.Expr lowerVal :: GenMod :> es => Env -> Val -> Eff es Wasm.Expr
-- lowerVal g (ValLit l) = lowerVal g (ValLit l) =
-- case l of pure $ case l of
-- LitInt n -> LitInt n -> [expr|
-- ins "i32.const" [sxp n] (i32.const #{n})
-- <> makeSmallFixnum ##{makeSmallFixnum}
-- LitBool b -> |]
-- ins "i32.const" [sxp @Int $ if b then 1 else 0] LitBool b -> [expr|
-- <> ins "ref.i31" [] (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) = pure $ [expr|(local.get #{l})|]
-- where where
-- l = V.elemIndex x g.vars ^?! _Just l = succ $ V.elemIndex x g.vars ^?! _Just
-- lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr -- lowerVal g (ValLambda lam) = do
-- idx <- lowerLambda g lam
-- pure [expr|
-- (i32.const 0)
-- (ref.func #{idx})
-- (struct.new $closure)
-- |]
-- lower' g (Halt [v]) = pure . mconcat $ lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
-- [ pushArg g.runtime 0 (lowerVal g v)
-- , ins "return_call" [sxp @Int 1]
-- ]
-- lower' g (ExpPrim p rs e) = lower' g (Halt [v]) = do
-- case p of arg <- pushArg 0 <$> lowerVal g v
-- PrimAdd x y -> lowerBinOp "i32.add" g x y r e pure [expr|
-- PrimMul x y -> lowerBinOp "i32.mul" g x y r e ##{arg}
-- where (return_call $halt (i32.const 1))
-- r = head rs |]
-- lower' g (ExpIf c t f) = do lower' g e@(ExpPrim p k) =
-- t' <- lower' g t ([expr|(@gyehoek :origin #{origin})|]<>)
-- f' <- lower' g f <$> case p of
-- pure $ lowerVal g c PrimAdd x y -> lowerBinOp "i32.add" g x y k
-- <> Wasm.if' (Wasm.result [i32]) t' f' PrimMul x y -> lowerBinOp "i32.mul" g x y k
where origin = encodeOrShow @_ @Text e
-- lower' g (ExpContinue k [x]) = pure . mconcat $ lower' g (ExpIf c t f) = do
-- [ pushArg rt 0 (lowerVal g x) c' <- lowerVal g c
-- , ins "i32.const" [sxp @Int 1] -- nargs t' <- lower' g t
-- -- get the return continuation. f' <- lower' g f
-- , ins "global.get" [sxp rt.contStack] pure [expr|
-- , ins "global.get" [sxp rt.contStackTop] ##{c'}
-- , ins "array.get" [sxp rt.contStackType] (call $gh-truthy?)
-- , ins "ref.as_non_null" [] (if (then ##{t'})
-- -- decrement contStackTop, completing the "pop." (else ##{f'}))
-- , 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 lower' g (ExpLetRec [(r,AbsKappa kap)] e) = do
-- idx <- defun [i32] [] (replicate 5 scm) \_ -> do idx <- lowerKappa g kap
-- let g' = g & #vars <>~ V.fromList xs let g' = g & #kvars <>~ [r]
-- & #kvars <>~ [ktail] e' <- lower' g' e
-- m' <- lower' g' m let origin = encodeOrShow @_ @Text e
-- pure . mconcat $ pure [expr|
-- [ xs & ifoldMap \n _ -> (@gyehoek :origin #{origin})
-- popArg g.runtime n <> ins "local.set" [sxp (1+n)] (@gyehoek "push cont" :idx #{idx})
-- , m' (array.set $cont-stack-type
-- ] (global.get $cont-stack)
-- declareFuncref idx (global.get $cont-stack-top)
-- let g' = g & #vars <>~ [r] (ref.func #{idx}))
-- let n = length g.vars (global.set $cont-stack-top
-- e' <- lower' g' e (i32.add (global.get $cont-stack-top)
-- pure . mconcat $ (i32.const 1)))
-- [ ins "ref.func" [sxp idx] ##{e'}
-- , ins "local.set" [sxp (n+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
pure [expr|
(i32.const 0)
(ref.func #{idx})
(struct.new $closure)
(local.set #{n})
##{e'}
|]
-- lowerBinOp lower' g e@(ExpApply f xs ktail) = do
-- :: (GenMod :> es) let nargs = length xs
-- => Text -> Env -> Val -> Val -> Name -> Exp -> Eff es Wasm.Expr f' <- lowerVal g f
-- lowerBinOp op g x y r e = do let l = succ $ V.elemIndex ktail g.kvars ^?! _Just
-- e' <- lower' g' e args <- fold <$>
-- pure . mconcat $ itraverse (\i -> fmap (pushArg $ tonat i) . lowerVal g) xs
-- [ lowerVal g x let origin = encodeOrShow @_ @Text e
-- , ins "ref.cast" [sxp $ ref i31] pure [expr|
-- , ins "i31.get_s" [] (@gyehoek :origin #{origin})
-- , lowerVal g y (@gyehoek "load args")
-- , ins "ref.cast" [sxp $ ref i31] ##{args}
-- , ins "i31.get_s" [] (i32.const 1)
-- , ins op [] ##{f'}
-- , ins "ref.i31" [] (ref.cast (ref $closure))
-- , ins "local.set" [sxp (1+n)] (struct.get $closure $code)
-- , e' (return_call_ref $cont-type)
-- ] (@gyehoek todo
-- where (f' ##{f'})
-- g' = g & #vars <>~ [r] (ktail #{l}))
-- n = length (g ^. #vars) |]
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 body = mconcat
[ xs & ifoldMap \n _ ->
let n' = succ n
in popArg n <> [expr|(local.set #{n'})|]
, 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))
##{body})
|]
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 body = mconcat
[ xs & ifoldMap \n _ ->
let n' = succ n
in popArg n <> [expr|(local.set #{n'})|]
, 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))
##{body})
|]
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
pure idx
lowerBinOp
:: (GenMod :> es)
=> 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)
x' <- lowerVal g x
y' <- lowerVal g y
e' <- lower' g' e
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}
(local.set #{n})
##{e'}
|]
-- scm = ref eq emitRuntime :: GenMod :> es => Eff es ()
emitRuntime = mfix \runtime -> do
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
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 array
Wasm.defineType [wat|
(type $arg-array-type (array (mut (ref null eq))))
|]
Wasm.defineGlobal [wat|
(global $arg-array (ref $arg-array-type)
(array.new_default $arg-array-type (i32.const 32)))
|]
-- 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 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))))
|]
-- emitRuntime :: GenMod :> es => Eff es Runtime lowerProgram :: Program -> Eff es Text
-- emitRuntime = mfix \runtime -> do lowerProgram (MkProgram e) = lower e
-- heapObjectIdx <- Wasm.deftypeNamed "$heap-object" $ Wasm.sub [] $ Wasm.struct
-- [ Wasm.mut 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"
-- 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
-- lowerProgram :: Program -> Eff es Text
-- lowerProgram (MkProgram e) = lower e
lower = _
lowerProgram = _
antiquote_example =
let
metavar :: Integer
metavar = 123
e1 :: Wasm.Expr
e1 = [expr|
(func $blah (result i32)
(i32.const #{metavar}))
|]
e2 :: Wasm.Expr
e2 = [expr|
(func $blah (result i32)
(i32.const 123))
|]
in (metavar,e1,e2,e1==e2)
antiquote_splicing_example =
let
metavars :: List Sexp
metavars = [sxs'|i32 i64 f64|]
e1 :: Wasm.Expr
e1 = [expr|
(func $blah (param ##{metavars}))
|]
e2 :: Wasm.Expr
e2 = [expr|
(func $blah (param i32 i64 f64))
|]
in (metavars, e1, e2, e1 == e2)
+181 -39
View File
@@ -1,6 +1,9 @@
{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
module Gyehoek.CPS.Syntax module Gyehoek.CPS.Syntax
( Val(..) ( Val(..)
, Kappa(..) , Kappa(..)
@@ -15,80 +18,119 @@ module Gyehoek.CPS.Syntax
, pattern Halt1 , pattern Halt1
, _MkKappa , _MkKappa
, _ExpPrim , _ExpPrim
, _ExpFix , _ExpLetRec
, _ExpApply , _ExpApply
, binders
, body
, op
, args
, cont
, cps
, pattern AbsLambda'
, pattern AbsKappa'
, Abs(..)
, free
, free'
) )
where where
import Language.SexpGrammar qualified as S import Language.SexpGrammar qualified as S
import Gyehoek.Sexp qualified import Gyehoek.Sexp qualified
import Gyehoek.Scheme.Syntax (Name (..), Prim(..), primSexpIso, Lit(..), pattern Void) import Gyehoek.Scheme.Syntax (Name (..), Prim(..), primSexpIso, Lit(..), pattern Void, getName)
import Data.Text (Text)
import Data.List (List) import Data.List (List)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Language.SexpGrammar.Generic import Language.SexpGrammar.Generic
import Control.Category import Control.Category
import Control.Lens import Control.Lens hiding (op)
import Data.Text qualified as T
import Data.Generics.Labels
import Prelude hiding ((.), id) import Prelude hiding ((.), id)
import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty (NonEmpty)
import Data.InvertibleGrammar.Base qualified as IGB import Language.Haskell.TH.Quote (QuasiQuoter)
import Data.InvertibleGrammar.Base ((:-)((:-))) import Data.Data (Data)
import qualified Data.InvertibleGrammar as IG import Language.Sexp.Located (Sexp)
import qualified Data.InvertibleGrammar.Base as IG
import qualified Gyehoek.Scheme.Syntax as Gyehoek
import Data.InvertibleGrammar.Base (type (:-)((:-)))
import Data.HashSet (HashSet)
import qualified Data.HashSet as HS
import Data.Hashable (Hashable)
import Data.Monoid (Endo)
import Data.Containers.ListUtils (nubOrd)
-- Data types -- Data types
data Val data Val
= ValLabel Name = ValVar Name
| ValVar Name
| ValLit Lit | ValLit Lit
deriving (Show, Generic) deriving (Show, Generic, Data, Eq)
data Kappa = MkKappa (List Name) Exp data Kappa = MkKappa { binders :: List Name, body :: Exp }
deriving (Show, Generic) deriving (Show, Generic, Data, Eq)
data Lambda = MkLambda (List Name) Name Exp data Lambda = MkLambda { binders :: List Name, ktail :: Name, body :: Exp }
deriving (Show, Generic) deriving (Show, Generic, Data, Eq)
data Abs
= AbsKappa Kappa
| AbsLambda Lambda
deriving (Show, Generic, Data, Eq)
pattern AbsKappa' xs e = AbsKappa (MkKappa xs e)
pattern AbsLambda' xs e ktail = AbsLambda (MkLambda xs e ktail)
data Exp data Exp
= ExpPrim (Prim Val) (List Name) Exp = ExpPrim (Prim Val) Kappa
| ExpFix (NonEmpty (Name, Kappa)) Exp | ExpLetRec { binders :: NonEmpty (Name, Abs), body :: Exp }
| ExpLet (NonEmpty (Name, Lambda)) Exp
| ExpContinue Name (List Val) | ExpContinue Name (List Val)
| ExpIf Val Exp Exp | ExpIf Val Exp Exp
| ExpApply Val (List Val) | ExpApply
deriving (Show, Generic) { op :: Val
, args :: List Val
, cont :: Name
}
deriving (Show, Generic, Data, Eq)
pattern Halt :: List Val -> Exp pattern Halt :: List Val -> Exp
pattern Halt xs = ExpApply (ValVar "halt") xs pattern Halt xs = ExpContinue "halt" xs
pattern Halt1 :: Val -> Exp pattern Halt1 :: Val -> Exp
pattern Halt1 x = ExpApply (ValVar "halt") [x] pattern Halt1 x = ExpContinue "halt" [x]
data Def = DefConstant Name Exp data Def = DefConstant Name Exp
deriving (Show, Generic) deriving (Show, Generic, Data)
data Program = MkProgram data Program = MkProgram
{ body :: Exp { body :: Exp
} }
deriving (Show, Generic) deriving (Show, Generic, Data)
makePrisms ''Kappa makePrisms ''Kappa
-- makeLenses ''Kappa
makePrisms ''Exp makePrisms ''Exp
-- makeLenses ''Exp
-- makeFieldsNoPrefix ''Exp
-- makeFieldsNoPrefix ''Kappa
-- makeLensesWith abbreviatedFields ''Exp
-- makeLensesFor [("binders", "_binders"), ("body", "_body")] ''Exp
makeFieldsId ''Exp
makeFieldsId ''Kappa
makeFieldsId ''Lambda
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
-- SexpIso instances -- SexpIso instances
instance S.SexpIso Val where instance S.SexpIso Val where
sexpIso = match sexpIso = match
$ With (. label) $ With (\var -> var . S.sexpIso)
$ With (. var) $ With (\lit -> lit . S.sexpIso)
$ With (. S.sexpIso)
$ End $ End
where
label = S.keyword >>> S.iso MkName getName
var = S.sexpIso
instance S.SexpIso Lambda where instance S.SexpIso Lambda where
sexpIso = match sexpIso = match
@@ -97,9 +139,18 @@ instance S.SexpIso Lambda where
where where
lambda = S.list $ lambda = S.list $
S.el Gyehoek.Sexp.lambdaKeyword S.el Gyehoek.Sexp.lambdaKeyword
>>> S.el (S.list (S.rest S.sexpIso)) >>> S.el binders
>>> S.el S.sexpIso
>>> S.el S.sexpIso >>> 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 instance S.SexpIso Kappa where
sexpIso = match sexpIso = match
@@ -111,11 +162,16 @@ instance S.SexpIso Kappa where
>>> S.el (S.list $ S.rest S.sexpIso) >>> S.el (S.list $ S.rest S.sexpIso)
>>> S.el 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 instance S.SexpIso Exp where
sexpIso = match sexpIso = match
$ With (. prim) $ With (. prim)
$ With (. fix) $ With (. letrec)
$ With (. let_)
$ With (. continue) $ With (. continue)
$ With (. if_) $ With (. if_)
$ With (. app) $ With (. app)
@@ -125,16 +181,102 @@ instance S.SexpIso Exp where
S.el (S.sym "continue") S.el (S.sym "continue")
>>> S.el S.sexpIso >>> S.el S.sexpIso
>>> S.rest S.sexpIso >>> S.rest S.sexpIso
fix = Gyehoek.Sexp.let_ "fix" S.sexpIso S.sexpIso S.sexpIso letrec = Gyehoek.Sexp.let_ "letrec" S.sexpIso S.sexpIso S.sexpIso
let_ = Gyehoek.Sexp.let_ "let" S.sexpIso S.sexpIso S.sexpIso
if_ = S.list $ S.el (S.sym "if") if_ = S.list $ S.el (S.sym "if")
>>> S.el S.sexpIso >>> S.el S.sexpIso >>> S.el S.sexpIso >>> 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 $ prim = S.list $
S.el (S.sym "prim") S.el (S.sym "prim")
>>> S.el (primSexpIso id (S.sexpIso @Val)) >>> S.el (primSexpIso id (S.sexpIso @Val))
>>> S.el S.sexpIso >>> S.el S.sexpIso
>>> S.el S.sexpIso
instance S.SexpIso Program where instance S.SexpIso Program where
sexpIso = with \prog -> S.sexpIso @Exp >>> prog 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
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
free :: Exp -> HashSet Name
free = go where
gokap (MkKappa xs m) = go m & deleteFrom xs
golam (MkLambda xs k m) = go m & deleteFrom xs & sans k
goabs = \case
AbsKappa kap -> gokap kap
AbsLambda lam -> golam lam
go = \case
ExpPrim p k ->
p & toHashSetOf (folded . #ValVar)
& HS.union (gokap k)
ExpLetRec bs m ->
foldMapOf (each . _2) goabs bs <> go m
& deleteFrom (bs ^.. each . _1)
ExpContinue k xs -> HS.fromList $ k : xs ^.. each . #ValVar
ExpIf c t f -> toHashSetOf #ValVar c <> go t <> go f
ExpApply f xs k -> toHashSetOf (each . #ValVar) (f:xs) <> HS.singleton k
-- | Free variables given in the order of their appearance.
free' :: Exp -> List Name
free' = nubOrd . goFree HS.empty where
goFreeKap bound (MkKappa xs m) = goFree (bound & insertFrom xs) m
goFreeLam bound (MkLambda xs k m) = goFree (bound & insertFrom (k:xs)) m
goFreeAbs bound = \case
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
freeLambda (MkLambda {binders,ktail,body}) = _
+48 -27
View File
@@ -1,44 +1,33 @@
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OrPatterns #-}
module Gyehoek.Driver module Gyehoek.Driver
(main, lower_e2e, convert_e2e, parse_e2e) (main, lower_e2e, convert_e2e, parse_e2e)
where where
import Gyehoek.Options import Gyehoek.Options
import qualified Data.Text.IO as TIO
import Data.Text (Text) import Data.Text (Text)
import Prelude hiding (readFile) import Prelude hiding (readFile)
import Options.Applicative import Options.Applicative
import Control.Lens import Control.Lens
import Data.Generics.Labels
import System.OsPath (OsPath)
import System.FilePath ((-<.>), dropExtension)
import Effectful.FileSystem import Effectful.FileSystem
import Effectful import Effectful
import Effectful.FileSystem.IO qualified as FS import Effectful.FileSystem.IO qualified as FS
import Effectful.FileSystem.IO.ByteString qualified as FB 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 qualified Gyehoek.Sexp as Sexp
import Data.Text.Lens
import Data.List (List)
import qualified Gyehoek.Scheme.Syntax as Scm import qualified Gyehoek.Scheme.Syntax as Scm
import Effectful.Exception
import qualified Data.Text as T
import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding as T
import System.IO (Handle) import System.IO (Handle)
import Data.List.NonEmpty (NonEmpty) import System.IO qualified as IO
import qualified Cradle as C
import Gyehoek.CPS.Convert import Gyehoek.CPS.Convert
import Gyehoek.CPS.Lower import Gyehoek.CPS.Lower
import Data.Foldable
import qualified Gyehoek.Scheme.Syntax
import Gyehoek.CPS.Syntax qualified as Cps import Gyehoek.CPS.Syntax qualified as Cps
import Data.Maybe (fromMaybe)
import Control.Monad import Control.Monad
import Text.Pretty.Simple (pShow, pShowNoColor) import Text.Pretty.Simple (pShowNoColor)
import System.Process.Typed
import Data.Text.Encoding (encodeUtf8)
import System.Environment.Blank (getEnvDefault)
import GHC.Conc (atomically)
import qualified Data.Text.IO as TIO
import qualified Data.ByteString.Lazy as BS
main :: IO () main :: IO ()
@@ -48,8 +37,8 @@ main = do
hPutStr :: FileSystem :> es => Handle -> Text -> Eff es () -- hPutStr :: FileSystem :> es => Handle -> Text -> Eff es ()
hPutStr h = FB.hPutStr h . T.encodeUtf8 -- hPutStr h = FB.hPutStr h . T.encodeUtf8
hPutStrLn :: FileSystem :> es => Handle -> Text -> Eff es () hPutStrLn :: FileSystem :> es => Handle -> Text -> Eff es ()
hPutStrLn h = FB.hPutStrLn h . T.encodeUtf8 hPutStrLn h = FB.hPutStrLn h . T.encodeUtf8
@@ -57,8 +46,8 @@ hPutStrLn h = FB.hPutStrLn h . T.encodeUtf8
hGetContents :: FileSystem :> es => Handle -> Eff es Text hGetContents :: FileSystem :> es => Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readFile :: FileSystem :> es => FilePath -> Eff es Text -- readFile :: FileSystem :> es => FilePath -> Eff es Text
readFile f = FS.withFile f FS.ReadMode hGetContents -- readFile f = FS.withFile f FS.ReadMode hGetContents
withFile withFile
:: (FileSystem :> es) :: (FileSystem :> es)
@@ -67,12 +56,41 @@ withFile "-" FS.ReadMode k = k FS.stdin
withFile "-" (FS.WriteMode; FS.AppendMode) k = k FS.stdout withFile "-" (FS.WriteMode; FS.AppendMode) k = k FS.stdout
withFile f m k = FS.withFile f m k 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 :: FileSystem :> es => FilePath -> Eff es Scm.Program
readScm f = readScm f =
withFile f FS.ReadMode $ \h -> 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) >>= 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 (byteStringInput . view lazy . encodeUtf8 $ wat)
-- & setStdout byteStringOutput
& 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)
driver driver
:: (GenSym :> es, FileSystem :> es, IOE :> es) :: (GenSym :> es, FileSystem :> es, IOE :> es)
=> Options -> Eff es () => Options -> Eff es ()
@@ -84,8 +102,11 @@ driver opts = do
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 wat <- lowerProgram cps
withFile opts.output FS.WriteMode \h -> if not opts.inspectWasm then
hPutStrLn h wat withFile opts.output FS.WriteMode \h ->
hPutStrLn h wat
else
inspectWasm wat
parse_e2e :: FilePath -> IO Scm.Program parse_e2e :: FilePath -> IO Scm.Program
parse_e2e = runEff . runFileSystem . readScm parse_e2e = runEff . runFileSystem . readScm
+3
View File
@@ -19,6 +19,7 @@ data Options = MkOptions
-- , dumpQBE :: Maybe FilePath -- , dumpQBE :: Maybe FilePath
dumpCPS :: Bool dumpCPS :: Bool
, dumpParsed :: Bool , dumpParsed :: Bool
, inspectWasm :: Bool
, output :: FilePath , output :: FilePath
, sourceFile :: FilePath , sourceFile :: FilePath
} }
@@ -49,10 +50,12 @@ parseOutput = strOption
parseDumpCPS = switch (long "dump-cps") parseDumpCPS = switch (long "dump-cps")
parseDumpParsed = switch (long "dump-parsed") parseDumpParsed = switch (long "dump-parsed")
parseInspectWasm = switch $ long "inspect-wasm" <> short 'p'
parser :: Parser Options parser :: Parser Options
parser = MkOptions parser = MkOptions
<$> parseDumpCPS <$> parseDumpCPS
<*> parseDumpParsed <*> parseDumpParsed
<*> parseInspectWasm
<*> parseOutput <*> parseOutput
<*> argument str (metavar "FILE") <*> argument str (metavar "FILE")
+16 -15
View File
@@ -20,41 +20,44 @@ module Gyehoek.Scheme.Syntax
, primSexpIso , primSexpIso
, pattern Void , pattern Void
, free , free
, qexp
, qprog
, subst , subst
, getName
, scm
) )
where where
import Data.Text (Text) import Data.Text (Text)
import Data.List (List) import Data.List (List)
import Language.SexpGrammar import Language.SexpGrammar
( SexpIso(..), list, el, (>>>), rest, sym, symbol ) ( SexpIso(..), list, el, rest, sym, symbol )
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 GHC.Generics
import Prelude hiding ((.), id) import Prelude hiding ((.), id)
import Control.Category import Control.Category
import Data.List.NonEmpty (NonEmpty ((:|))) import Data.List.NonEmpty (NonEmpty)
import Gyehoek.Sexp qualified import Gyehoek.Sexp qualified
import Gyehoek.GenSym (Gen) import Gyehoek.GenSym (Gen)
import Control.Lens import Control.Lens
import Data.String (IsString) import Data.String (IsString)
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Control.Lens.Unsound (prismSum)
import Data.Data (Data) import Data.Data (Data)
import Data.Functor.Foldable.TH (makeBaseFunctor) import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.Functor.Foldable hiding (fold) import Data.Functor.Foldable hiding (fold)
import Data.HashSet (HashSet) 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)
newtype Name = MkName { getName :: Text } newtype Name = MkName { inner :: Text }
deriving newtype (Show, Eq, IsString, Gen, Hashable) deriving newtype (Show, Eq, Ord, IsString, Gen, Hashable)
deriving stock (Generic, Data) deriving stock (Generic, Data)
getName :: Name -> Text
getName (MkName x) = x
data Prim e data Prim e
= PrimAdd e e = PrimAdd e e
| PrimSub e e | PrimSub e e
@@ -69,7 +72,7 @@ data Prim e
| PrimWrite e | PrimWrite e
| PrimZeroP e | PrimZeroP e
| PrimNewline | PrimNewline
deriving (Show, Generic, Functor, Foldable, Traversable, Data) deriving (Show, Generic, Functor, Foldable, Traversable, Data, Eq)
instance Each (Prim e) (Prim e') e e' instance Each (Prim e) (Prim e') e e'
@@ -79,7 +82,7 @@ data Lit
| LitBool Bool | LitBool Bool
| LitString Text | LitString Text
| LitQuote Sexp | LitQuote Sexp
deriving (Show, Generic, Data) deriving (Show, Generic, Data, Eq)
pattern Void :: Lit pattern Void :: Lit
pattern Void = LitNil pattern Void = LitNil
@@ -104,7 +107,7 @@ data Sexp
= SexpCons Sexp Sexp = SexpCons Sexp Sexp
| SexpSymbol Text | SexpSymbol Text
| SexpLit Lit | SexpLit Lit
deriving (Show, Generic, Data) deriving (Show, Generic, Data, Eq)
data CommandOrDef data CommandOrDef
= Command Exp = Command Exp
@@ -121,8 +124,6 @@ instance Each Program Program (Either Exp Def) (Either Exp Def) where
each = #commandsAndDefs . each . go each = #commandsAndDefs . each . go
where where
inj = either Command Definition inj = either Command Definition
toeither (Command e) = Left e
toeither (Definition d) = Right d
go :: Traversal' CommandOrDef (Either Exp Def) go :: Traversal' CommandOrDef (Either Exp Def)
go k (Command e) = inj <$> k (Left e) go k (Command e) = inj <$> k (Left e)
go k (Definition d) = inj <$> k (Right d) go k (Definition d) = inj <$> k (Right d)
@@ -184,7 +185,7 @@ instance SexpIso Lit where
instance SexpIso Sexp where instance SexpIso Sexp where
sexpIso = match sexpIso = match
$ With (\cons -> cons . Gyehoek.Sexp.todo) $ With (\conss -> conss . Gyehoek.Sexp.todo)
$ With (\s -> s . symbol) $ With (\s -> s . symbol)
$ With (\lit -> lit . sexpIso) $ With (\lit -> lit . sexpIso)
$ End $ End
@@ -229,8 +230,8 @@ instance SexpIso CommandOrDef where
-- utilities -- utilities
qexp = Gyehoek.Sexp.makeSx $ sexpIso @Exp scm :: QuasiQuoter
qprog = Gyehoek.Sexp.makeSxs (sexpIso @CommandOrDef) MkProgram scm = Gyehoek.Sexp.makeSx [|| Gyehoek.Sexp.fromSexp @Exp ||]
free :: Exp -> HashSet Name free :: Exp -> HashSet Name
free = cata \case free = cata \case
+114 -34
View File
@@ -27,6 +27,7 @@ module Gyehoek.Sexp
, encodePretty , encodePretty
, UglySexpIso(..) , UglySexpIso(..)
, AsSexpIso(..) , AsSexpIso(..)
, SpliceSexp(..)
, parseSexpsWithPos , parseSexpsWithPos
, parseSexpWithPos , parseSexpWithPos
, parseSexp , parseSexp
@@ -34,13 +35,15 @@ module Gyehoek.Sexp
, sxs , sxs
, makeSx , makeSx
, makeSxs , makeSxs
, makeSx'
, toSexp , toSexp
, fromSexp , fromSexp
, stripLocation , stripLocation
, sx' , format
, sxs' , equivalent
, encodeOrShow
) )
where where
import Data.Text (Text) import Data.Text (Text)
import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty, fromSexp) import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty, fromSexp)
@@ -56,7 +59,7 @@ import Data.List (List, groupBy)
import Data.Text.Encoding import Data.Text.Encoding
import Data.Either (either) import Data.Either (either)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Control.Lens import Control.Lens hiding (para)
import Data.Generics.Labels import Data.Generics.Labels
import System.Process import System.Process
import GHC.IO.Unsafe (unsafePerformIO) import GHC.IO.Unsafe (unsafePerformIO)
@@ -67,14 +70,23 @@ import Data.Void (absurd, Void)
import Data.Coerce (coerce) import Data.Coerce (coerce)
import qualified Data.Map import qualified Data.Map
import Language.Haskell.TH.Quote import Language.Haskell.TH.Quote
import Language.Haskell.TH (Quote, location, Loc (..), ExpQ, varE, mkName, listE, Exp, appE, conE) import Language.Haskell.TH (Quote, location, Loc (..), ExpQ, varE, mkName, listE, Exp, appE, conE, Q, Code, unTypeCode)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Control.Category import qualified Control.Category
import Data.Data (Data, Typeable, cast) import Data.Data (Data (..), Typeable, cast)
import Language.Haskell.TH.Syntax (lift, Lift) import Language.Haskell.TH.Syntax (lift, Lift, liftData)
import GHC.IsList (fromList) import GHC.IsList (fromList)
import Data.Functor.Foldable (cata) import Data.Functor.Foldable (cata, para, embed)
import Data.Functor.Classes (Show1(..)) import Data.Functor.Classes (Show1(..))
import Data.Vector (Vector)
import Numeric.Natural (Natural)
import Data.Maybe (fromMaybe)
import Control.Applicative (Alternative((<|>)))
import Debug.Pretty.Simple
import qualified Data.Vector as V
import qualified Data.Vector.Strict
import Data.Function (on)
import Data.String (IsString (fromString))
sexp :: SexpIso a => Iso' a Text sexp :: SexpIso a => Iso' a Text
@@ -82,6 +94,9 @@ sexp = iso
(either error id . encode) (either error id . encode)
(either error id . decode) (either error id . decode)
format :: Sexp -> Text
format = decodeUtf8 . view strict . SL.format
encode :: SexpIso a => a -> Either String Text encode :: SexpIso a => a -> Either String Text
encode = encodeWith sexpIso encode = encodeWith sexpIso
@@ -109,6 +124,12 @@ 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)
readSexpWithPos :: Position -> Text -> Either String Sexp
readSexpWithPos pos = SL.parseSexpWithPos pos . view lazy . encodeUtf8
readSexpsWithPos :: Position -> Text -> Either String (List Sexp)
readSexpsWithPos pos = SL.parseSexpsWithPos pos . view lazy . encodeUtf8
parseSexpsWithPos :: SexpGrammar a -> Position -> Text -> Either String (List a) parseSexpsWithPos :: SexpGrammar a -> Position -> Text -> Either String (List a)
parseSexpsWithPos g pos = parseSexpsWithPos g pos =
marshal . SL.parseSexpsWithPos pos . view lazy . encodeUtf8 marshal . SL.parseSexpsWithPos pos . view lazy . encodeUtf8
@@ -242,6 +263,9 @@ getPos = do
fromSexp :: SexpIso a => Sexp -> a fromSexp :: SexpIso a => Sexp -> a
fromSexp = either error id . Sexp.fromSexp sexpIso fromSexp = either error id . Sexp.fromSexp sexpIso
fromSexp' :: SexpGrammar a -> Sexp -> a
fromSexp' g = either error id . Sexp.fromSexp g
toSexp :: SexpIso a => a -> Sexp toSexp :: SexpIso a => a -> Sexp
toSexp = either error id . Sexp.toSexp sexpIso toSexp = either error id . Sexp.toSexp sexpIso
@@ -269,12 +293,36 @@ stripLocation = cata \case
SL.Compose (a SL.:< e) -> SL.Compose (a SL.:< e) ->
SL.Fix . SL.Compose $ SL.dummyPos SL.:< e SL.Fix . SL.Compose $ SL.dummyPos SL.:< e
metaSexp :: Sexp.Sexp -> Maybe ExpQ -- | @('==')@ for 'Sexp's modulo source location — return true if the
metaSexp (Unquote x) = -- two sexps are equal in all but 'Position' fields.
Just [| stripLocation . toSexp $ $(varE (mkName (T.unpack x))) |] equivalent :: Sexp -> Sexp -> Bool
metaSexp (SL.ParenList xs) equivalent = (==) `on` stripLocation
| (_:_) <- xs ^.. each . _UnquoteSplicing
= Just [| stripLocation . toSexp $ SL.ParenList (mconcat $(listE spans)) |] instance SexpIso Natural where
sexpIso = Sexp.integer >>> Sexp.partialOsi f g
where
f n | n < 0 = Left $ Sexp.unexpected "negative"
<> Sexp.expected "natural"
| otherwise = Right $ fromIntegral n
g n = fromIntegral n
class SpliceSexp a where
spliceSexp :: a -> List Sexp
instance SexpIso a => SpliceSexp (Data.Vector.Strict.Vector a) where
spliceSexp = toSexps
instance SexpIso a => SpliceSexp (Vector a) where
spliceSexp = toSexps
instance SexpIso a => SpliceSexp (List a) where
spliceSexp = toSexps
instance SpliceSexp Sexp where
spliceSexp = toListOf each
unquoteSplicingRecursive :: List Sexp.Sexp -> ExpQ
unquoteSplicingRecursive xs = [| mconcat $(spans) |]
where where
spans = xs spans = xs
& groupBy \cases & groupBy \cases
@@ -282,10 +330,31 @@ metaSexp (SL.ParenList xs)
_ (UnquoteSplicing _) -> False _ (UnquoteSplicing _) -> False
_ _ -> True _ _ -> True
& fmap \case & fmap \case
-- [e@(Unquote _)] ->
-- case unquote e of
-- Just x -> [| [$(x)] |]
-- Nothing -> error "unreachable"
[UnquoteSplicing x] -> [UnquoteSplicing x] ->
[| stripLocation <$> toSexps $(varE (mkName (T.unpack x))) |] [| spliceSexp $(varE (mkName (T.unpack x))) |]
x -> [| stripLocation <$> x |] es -> listE $ unquoteRecursive <$> es
metaSexp _ = Nothing & listE
unquoteRecursive :: Sexp.Sexp -> ExpQ
unquoteRecursive = \case
Unquote x -> [| toSexp $(varE (mkName (T.unpack x))) |]
SL.ParenList xs -> [|SL.ParenList $(unquoteSplicingRecursive xs)|]
e -> liftData e
_ParenList :: Prism' Sexp (List Sexp)
_ParenList = prism' SL.ParenList \case
SL.ParenList xs -> Just xs
_ -> Nothing
metaSexps :: List Sexp.Sexp -> Maybe ExpQ
metaSexps = Just . unquoteSplicingRecursive
metaSexp :: Sexp.Sexp -> Maybe ExpQ
metaSexp = Just . unquoteRecursive
-- 뻘짓뻘짓뻘짓뻘짓뻘짓 -- 뻘짓뻘짓뻘짓뻘짓뻘짓
class Lift1 f where class Lift1 f where
@@ -312,46 +381,57 @@ instance Lift1 SL.SexpF where
SL.ParenListF es -> [|SL.ParenListF $(liftLift l es)|] SL.ParenListF es -> [|SL.ParenListF $(liftLift l es)|]
SL.BracketListF es -> [|SL.BracketListF $(liftLift l es)|] SL.BracketListF es -> [|SL.BracketListF $(liftLift l es)|]
SL.BraceListF es -> [|SL.BraceListF $(liftLift l es)|] SL.BraceListF es -> [|SL.BraceListF $(liftLift l es)|]
SL.ModifiedF p e -> [|SL.Modified $(lift p) $(l e)|] SL.ModifiedF p e -> [|SL.ModifiedF $(lift p) $(l e)|]
-- deriving instance Lift a => Lift (SL.SexpF a) -- deriving instance Lift a => Lift (SL.SexpF a)
deriving instance Lift SL.Atom deriving instance Lift SL.Atom
deriving instance Lift SL.Position deriving instance Lift SL.Position
deriving instance Lift SL.Prefix deriving instance Lift SL.Prefix
encodeOrShow :: (SexpIso a, Show a, IsString s) => a -> s
encodeOrShow a = fromString case encode a of
Left _ -> show a
Right e -> T.unpack e
extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
extQ f g a = maybe (f a) g (cast a) extQ f g a = maybe (f a) g (cast a)
makeSxs makeSxs :: Data r => Code Q (List Sexp -> r) -> QuasiQuoter
:: Data b makeSxs f = QuasiQuoter
=> (List a -> b) -> SexpGrammar a -> QuasiQuoter
makeSxs f g = QuasiQuoter
{ quoteExp = \str -> do { quoteExp = \str -> do
pos <- getPos pos <- getPos
case parseSexpsWithPos g pos (T.pack str) of case readSexpsWithPos pos (T.pack str) of
Left e -> fail e Left e -> fail e
Right xs -> dataToExpQ (const Nothing `extQ` metaSexp) (f xs) Right xs -> [| $(unTypeCode f) $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
xs
, quotePat = undefined , quotePat = undefined
, quoteType = undefined , quoteType = undefined
, quoteDec = undefined , quoteDec = undefined
} }
makeSx -- | An untyped variant of 'makeSx', useful when the user function is
:: (Data a, Data r) -- polymorphic in its return value.
=> (a -> r) -> SexpGrammar a -> QuasiQuoter makeSx' :: ExpQ -> QuasiQuoter
makeSx f g = QuasiQuoter makeSx' f = QuasiQuoter
{ quoteExp = \str -> do { quoteExp = \str -> do
pos <- getPos pos <- getPos
case parseSexpWithPos g pos (T.pack str) of case readSexpWithPos pos (T.pack str) of
Left e -> fail e Left e -> fail e
Right x -> dataToExpQ (const Nothing `extQ` metaSexp) (f x) Right x -> [| $f $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
x
, quotePat = undefined , quotePat = undefined
, quoteType = undefined , quoteType = undefined
, quoteDec = undefined , quoteDec = undefined
} }
sxs = makeSxs id (sexpIso @Sexp) makeSx :: Data r => Code Q (Sexp -> r) -> QuasiQuoter
sx = makeSx id (sexpIso @Sexp) makeSx = makeSx' . unTypeCode
sxs' = makeSxs (fmap stripLocation) (sexpIso @Sexp) sxs = makeSxs [||id||]
sx' = makeSx stripLocation (sexpIso @Sexp) sx = makeSx [||id||]
+44 -37
View File
@@ -1,15 +1,7 @@
{- HLINT ignore "Use newtype instead of data" -} {- HLINT ignore "Use newtype instead of data" -}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeepSubsumption #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE RecordPuns #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ImpredicativeTypes #-} {-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE DerivingVia #-}
module Gyehoek.Wasm module Gyehoek.Wasm
( (
-- * syntax -- * syntax
@@ -20,8 +12,6 @@ module Gyehoek.Wasm
, expr , expr
, Gyehoek.Sexp.sx , Gyehoek.Sexp.sx
, Gyehoek.Sexp.sxs , Gyehoek.Sexp.sxs
, Gyehoek.Sexp.sx'
, Gyehoek.Sexp.sxs'
-- * GenMod effect -- * GenMod effect
, GenMod , GenMod
, runGenMod , runGenMod
@@ -29,43 +19,37 @@ module Gyehoek.Wasm
, defineFunction , defineFunction
, defineType , defineType
, defineGlobal , defineGlobal
, declare , emit
, renderModule
, wat
, wats
, defineFunctions
, defineTypes
, defineGlobals
) )
where where
import Language.SexpGrammar import Language.SexpGrammar
( SexpIso(..), list, el, (>>>), rest, sym, symbol, (:-) ) ( SexpIso(..), (>>>) )
import Language.SexpGrammar qualified as Sexp import Language.SexpGrammar qualified as Sexp
import Language.SexpGrammar.Generic import Language.SexpGrammar.Generic
import Data.List (List) import Data.List (List)
import GHC.Generics (Generic, Generically(..)) import GHC.Generics (Generic)
import Data.Text (Text) import Data.Text (Text)
import Data.String (IsString (fromString))
import Text.Printf
import Effectful import Effectful
import Numeric.Natural (Natural) import Numeric.Natural (Natural)
import Effectful.Dispatch.Dynamic import Effectful.Dispatch.Dynamic
import Effectful.State.Dynamic import Effectful.State.Dynamic
import Control.Lens import Control.Lens
import Data.Generics.Labels import Data.Vector.Strict (Vector)
import Data.Vector (Vector) import qualified Data.Vector.Strict as V
import Data.String.Interpolate
import qualified Data.Vector as V
import qualified Data.Text as T
import Effectful.Writer.Dynamic
import Control.Applicative (Alternative((<|>)))
import Control.Category qualified as Cat
import Data.Vector.Lens
import Data.Either (fromLeft, fromRight)
import Language.Sexp.Located import Language.Sexp.Located
import qualified Gyehoek.Sexp import qualified Gyehoek.Sexp
import GHC.IsList (IsList(..)) import GHC.IsList (IsList(..))
import Data.Coerce (coerce)
import qualified Control.Category
import Data.Functor (void)
import Language.Haskell.TH.Quote (QuasiQuoter) import Language.Haskell.TH.Quote (QuasiQuoter)
import Data.Data (Data) import Data.Data (Data)
import Data.Functor.Foldable (cata) import Gyehoek.Sexp (sx)
import Data.Foldable (traverse_)
newtype Module = MkModule { inner :: Vector Sexp } newtype Module = MkModule { inner :: Vector Sexp }
@@ -121,25 +105,34 @@ data GenMod :: Effect where
DefineFunction :: Sexp -> GenMod m Idx DefineFunction :: Sexp -> GenMod m Idx
DefineType :: Sexp -> GenMod m Idx DefineType :: Sexp -> GenMod m Idx
DefineGlobal :: Sexp -> GenMod m Idx DefineGlobal :: Sexp -> GenMod m Idx
Declare :: Sexp -> GenMod m () Emit :: Sexp -> GenMod m ()
type instance DispatchOf GenMod = Dynamic type instance DispatchOf GenMod = Dynamic
defineFunction :: GenMod :> es => Sexp -> Eff es Idx defineFunction :: GenMod :> es => Sexp -> Eff es Idx
defineFunction = send . DefineFunction defineFunction = send . DefineFunction
defineFunctions :: GenMod :> es => List Sexp -> Eff es (List Idx)
defineFunctions = traverse (send . DefineFunction)
defineType :: GenMod :> es => Sexp -> Eff es Idx defineType :: GenMod :> es => Sexp -> Eff es Idx
defineType = send . DefineType defineType = send . DefineType
defineTypes :: GenMod :> es => List Sexp -> Eff es (List Idx)
defineTypes = traverse (send . DefineType)
defineGlobal :: GenMod :> es => Sexp -> Eff es Idx defineGlobal :: GenMod :> es => Sexp -> Eff es Idx
defineGlobal = send . DefineGlobal defineGlobal = send . DefineGlobal
declare :: GenMod :> es => Sexp -> Eff es () defineGlobals :: GenMod :> es => List Sexp -> Eff es (List Idx)
declare = send . Declare defineGlobals = traverse (send . DefineGlobal)
emit :: GenMod :> es => List Sexp -> Eff es ()
emit = traverse_ (send . Emit)
appendAndIncrement appendAndIncrement
:: State GenModState :> es :: State GenModState :> es
=> LensLike' ((,) _) GenModState Natural => LensLike' ((,) Natural) GenModState Natural
-> Sexp -> Sexp
-> Eff es Idx -> Eff es Idx
appendAndIncrement l s = appendAndIncrement l s =
@@ -155,11 +148,16 @@ runGenMod =
_ (DefineFunction s) -> appendAndIncrement #funcs s _ (DefineFunction s) -> appendAndIncrement #funcs s
_ (DefineType s) -> appendAndIncrement #types s _ (DefineType s) -> appendAndIncrement #types s
_ (DefineGlobal s) -> appendAndIncrement #globals s _ (DefineGlobal s) -> appendAndIncrement #globals s
_ (Declare s) -> #mod . #inner <>= V.singleton s _ (Emit s) -> #mod . #inner <>= V.singleton s
execGenMod :: Eff (GenMod : es) a -> Eff es Module execGenMod :: Eff (GenMod : es) a -> Eff es Module
execGenMod = fmap snd . runGenMod execGenMod = fmap snd . runGenMod
renderModule :: Module -> Text
renderModule (MkModule ss) = Gyehoek.Sexp.format [sx|
(module ##{ss})
|]
-- SexpIso instances -- SexpIso instances
@@ -176,10 +174,19 @@ instance SexpIso Idx where
instance SexpIso Instr where instance SexpIso Instr where
sexpIso = with id sexpIso = with id
instance Gyehoek.Sexp.SpliceSexp Expr where
spliceSexp = toListOf $ #inner . each . #inner
-- quasiquoters -- quasiquoters
expr :: QuasiQuoter expr :: QuasiQuoter
expr = Gyehoek.Sexp.makeSxs expr = Gyehoek.Sexp.makeSxs
(MkExpr . V.fromList . (each . #inner %~ Gyehoek.Sexp.stripLocation)) [||MkExpr . V.fromList . (each . #inner %~ Gyehoek.Sexp.stripLocation)
(sexpIso @Instr) . fmap (Gyehoek.Sexp.fromSexp @Instr) ||]
wat :: QuasiQuoter
wat = Gyehoek.Sexp.makeSx [|| id ||]
wats :: QuasiQuoter
wats = Gyehoek.Sexp.makeSxs [|| id ||]
-18
View File
@@ -1,18 +0,0 @@
const imports = {
guppy: {
print: (arg) => console.log (arg)
}
}
// Assume add.wasm file exists that contains a single function adding 2 provided arguments
const fs = require('node:fs');
// Use the readFileSync function to read the contents of the "add.wasm" file
const wasmBuffer = fs.readFileSync('u.wasm');
// Use the WebAssembly.instantiate method to instantiate the WebAssembly module
WebAssembly.instantiate(wasmBuffer, imports).then(wasmModule => {
// Exported function lives under instance.exports object
const { main } = wasmModule.instance.exports;
main ()
});
+1
View File
@@ -0,0 +1 @@
(values 1 2)
+264 -68
View File
@@ -1,69 +1,265 @@
(module (module
(type $heap-object (sub (struct (field (mut i32))))) (import
(type $open-procedure (func (param i32))) "gyehoek"
(type $closure (sub $heap-object "write"
(struct (field (mut i32)) (func $gh-write (param (ref eq))))
(field (ref $open-procedure))))) (import
(type $cont-stack-type (array (mut (ref null $open-procedure)))) "gyehoek"
(type $arg-array-type (array (mut (ref null eq)))) "truthy?"
(global $cont-stack-top (mut i32) (i32.const 0)) (func $gh-truthy? (param (ref eq)) (result i32)))
(global $cont-stack (ref $cont-stack-type) (type $heap-object (sub (struct (field $hash (mut i32)))))
(i32.const 128) (type $cont-type (func (param i32)))
(array.new_default $cont-stack-type)) (type $cont-stack-type (array (mut (ref null $cont-type))))
(global $arg-array (ref $arg-array-type) (type
(i32.const 32) $closure
(array.new_default $arg-array-type)) (sub
(global (mut (ref null eq)) (ref.null eq)) $heap-object
(elem declare funcref (ref.func 1)) (struct
(func (field $hash (mut i32))
(param i32) (field $code (ref $cont-type)))))
(result) (global $cont-stack-top (mut i32) (i32.const 0))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) (global
(global.get 2) $cont-stack
(i32.const 0) (ref $cont-stack-type)
(array.get 3) (array.new_default $cont-stack-type (i32.const 128)))
ref.as_non_null (type $arg-array-type (array (mut (ref null eq))))
(global.set 3)) (global
(func $arg-array
(param i32) (ref $arg-array-type)
(result) (array.new_default $arg-array-type (i32.const 32)))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) (global $result (mut (ref null eq)) (ref.null eq))
(global.get 2) (func
(i32.const 0) $halt
(array.get 3) (param i32)
ref.as_non_null (@gyehoek "pop argument")
(local.set 1) (global.get $arg-array)
(global.get 2) (i32.const 0)
(i32.const 0) (array.get $arg-array-type)
(local.get 1) ref.as_non_null
(array.set 3) (global.set $result))
(i32.const 1) (func
(global.get 1) (param i32)
(global.get 0) (@gyehoek :origin "(κ (x5) (continue λ-tail1 x5))")
(array.get 2) (local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
ref.as_non_null (@gyehoek "pop argument")
(global.get 0) (global.get $arg-array)
(i32.const 1) (i32.const 0)
i32.sub (array.get $arg-array-type)
(global.set 0) ref.as_non_null
(return_call_ref 1)) (local.set 1)
(func (@gyehoek :origin "(continue λ-tail1 x5)")
(param i32) (@gyehoek "push args")
(result) (@gyehoek "push argument")
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) (global.get $arg-array)
(ref.func 1) (i32.const 0)
(local.set 1) (local.get 4)
(global.get 2) (array.set $arg-array-type)
(i32.const 0) (@gyehoek "nargs")
(local.get 1) (i32.const 1)
(array.set 3) (@gyehoek "pop cont stack")
(return_call 1)) (global.get $cont-stack-top)
(func (i32.const 1)
(param) i32.sub
(result (ref eq)) (global.set $cont-stack-top)
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) (global.get $cont-stack)
(i32.const 0) (global.get $cont-stack-top)
(call 1) (array.get $cont-stack-type)
(global.get 3) ref.as_non_null
ref.as_non_null) (return_call_ref $cont-type))
(export "main" (func 3))) (elem declare funcref (ref.func 3))
(func
(param i32)
(@gyehoek
:origin
"(κ (x3) (letrec ((r4 (κ (x5) (continue λ-tail1 x5)))) (f x3 r4)))")
(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 "(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))
(func
(param i32)
(@gyehoek
:origin
"(λ (f x λ-tail1) (letrec ((r2 (κ (x3) (letrec ((r4 (κ (x5) (continue λ-tail1 x5)))) (f x3 r4))))) (f x r2)))")
(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 "pop argument")
(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)
(array.set
$cont-stack-type
(global.get $cont-stack)
(global.get $cont-stack-top)
(ref.func 4))
(global.set
$cont-stack-top
(i32.add (global.get $cont-stack-top) (i32.const 1)))
(@gyehoek :origin "(f x r2)")
(@gyehoek "load args")
(@gyehoek "push argument")
(global.get $arg-array)
(i32.const 0)
(local.get 2)
(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 5))
(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
(export "main")
(call $scm-entry (i32.const 0))
(call $gh-write (ref.as_non_null (global.get $result)))))
-65
View File
@@ -1,65 +0,0 @@
(module
(type $heap-object (sub (struct (field (mut i32)))))
(type $open-procedure (func (param i32)))
(type $closure (sub $heap-object
(struct (field (mut i32))
(field (ref $open-procedure)))))
(type $cont-stack-type (array (mut (ref null $open-procedure))))
(type $arg-array-type (array (mut eqref)))
(type (func (result (ref eq))))
(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)))
(global $arg-array (ref $arg-array-type)
(array.new_default $arg-array-type (i32.const 32)))
(global (mut eqref) (ref.null eq))
(elem declare funcref (ref.func 1))
(func $halt (param i32)
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(global.set 3
(ref.as_non_null
(array.get $arg-array-type
(global.get $arg-array)
(i32.const 0)))))
(func $f1 (param i32)
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
;; pop arg 0
(local.set
1
(ref.as_non_null
(array.get $arg-array-type
(global.get $arg-array)
(i32.const 0))))
;; push arg 0
(array.set $arg-array-type
(global.get $arg-array)
(i32.const 0)
(local.get 1))
;; pop continuation
(return_call_ref
$open-procedure
(i32.const 1)
(ref.as_non_null (array.get $cont-stack-type
(global.get $cont-stack)
(global.get $cont-stack-top)))
(global.set $cont-stack-top
(i32.sub
(global.get $cont-stack-top)
(i32.const 1)))))
(func $f2 (type $open-procedure) (param i32)
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(local.set 1
(struct.new $closure
(i32.const 0)
(ref.func $f1)))
(array.set $arg-array-type
(global.get $arg-array)
(i32.const 0)
(local.get 1))
(i32.const 1)
(return_call $f1))
(func $main (export "main") (result (ref eq))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(call $f2 (i32.const 0))
(ref.as_non_null
(global.get 3))))
+52
View File
@@ -0,0 +1,52 @@
{-# LANGUAGE OverloadedLists #-}
module Gyehoek.Test.CPS.Syntax (root) where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Language.Sexp.Located qualified as SL
import Language.SexpGrammar ()
import Gyehoek.CPS.Syntax (cps)
import Gyehoek.CPS.Syntax qualified as Sut
import Data.Function (on)
import Gyehoek.Test.Sexp (equivto)
root :: IO TestTree
root = pure . testGroup "cps syntax" $
[ qqTree
, freeTree
]
freeTree :: TestTree
freeTree = testCase "free" do
Sut.free [cps|
(letrec ((x (lambda (r k1) (continue k1 y)))
(y (lambda (r k2) (continue k2 x))))
(continue x y k3))|] @=? ["k3"]
Sut.free' [cps|
(letrec ((x (lambda (r k1) (continue k1 y)))
(y (lambda (r k2) (continue k2 x))))
(continue x y k3))|] @=? ["k3"]
qqTree :: TestTree
qqTree = testGroup "parser"
[ testCase "lambda" do
assertEqual "" (Sut.MkLambda ["x","y"] "ktail"
(Sut.ExpContinue "ktail" [Sut.ValVar "x"]))
[cps|(λ (x y ktail) (continue ktail x))|]
assertEqual "" (Sut.MkLambda [] "ktail"
(Sut.ExpContinue "ktail" [Sut.ValVar "x"]))
[cps|(λ (ktail) (continue ktail x))|]
, testCase "kappa" do
assertEqual "" (Sut.MkKappa ["x","y"]
(Sut.ExpContinue "k123" [Sut.ValVar "x", Sut.ValVar "y"]))
[cps|(κ (x y) (continue k123 x y))|]
, testCase "application" do
assertEqual "" (Sut.ExpApply (Sut.ValVar "f")
[Sut.ValVar "x",Sut.ValVar "y"]
"k")
[cps|(f x y k)|]
assertEqual "" (Sut.ExpApply (Sut.ValVar "f")
[] "k")
[cps|(f k)|]
]
+45
View File
@@ -0,0 +1,45 @@
module Gyehoek.Test.Golden (root) where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Silver
import Gyehoek.Driver qualified as Driver
import System.FilePath
import Data.List (List)
import Data.Functor ((<&>))
import System.Directory
import Data.Function
import System.Environment.Blank (getEnvDefault)
import qualified System.Process.Text as PT
disabled :: List String
disabled =
[
]
root :: IO TestTree
root = do
all_cases <- listDirectory "golden"
let tests = all_cases
& filter (`notElem` disabled)
& fmap ("golden"</>)
testGroup "golden" <$> sequenceA
[ executionTests tests
]
executionTests :: List FilePath -> IO TestTree
executionTests files = do
cmd <- getEnvDefault "GYEHOEK_RUNTIME"
"runtime/target/debug/gyehoek-runtime"
pure $ testGroup "execution" $ files <&> \test ->
let testname = takeFileName test
scmfile = test </> "source.scm"
resultfile = test </> "exec"
action = do
t <- Driver.lower_e2e scmfile
PT.readProcessWithExitCode cmd ["-"] t
in goldenVsAction
testname
resultfile
action
printProcResult
+56
View File
@@ -0,0 +1,56 @@
module Gyehoek.Test.Sexp
( root
, EquivSexp(..)
, assertEquiv
, equivto
)
where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Language.Sexp.Located qualified as SL
import Language.SexpGrammar ()
import Gyehoek.Sexp (sx, equivalent)
import Data.Function (on)
root :: IO TestTree
root = pure . testGroup "sexp" $
[ sxTree
]
newtype EquivSexp = MkEquiv SL.Sexp
deriving newtype (Show)
instance Eq EquivSexp where
MkEquiv x == MkEquiv y = equivalent x y
assertEquiv
:: HasCallStack
=> String -> SL.Sexp -> SL.Sexp -> Assertion
assertEquiv prefix = assertEqual prefix `on` MkEquiv
equivto = assertEquiv ""
sxTree :: TestTree
sxTree = testGroup "sx"
[ testCase "quotation" do
equivto (SL.Symbol "abc") [sx|abc|]
equivto (SL.ParenList [SL.Symbol "a", SL.Symbol "b"]) [sx|(a b)|]
, testCase "antiquotation" do
equivto [sx|123|]
let meta = 123 :: Int
in [sx|#{meta}|]
equivto [sx|(blah (blah blah) blah)|]
let meta = [sx|blah|]
in [sx|(#{meta} (#{meta} #{meta}) #{meta})|]
, testCase "splicing" do
equivto [sx|(a b c d e f g)|]
let metas = SL.Symbol <$> ["c","d","e"]
in [sx|(a b ##{metas} f g)|]
equivto [sx|(a (b c d) e f g)|]
let
e1 = SL.Symbol "c"
e2 = SL.Symbol <$> ["e","f"]
in [sx|(a (b #{e1} d) ##{e2} g)|]
]
+11 -49
View File
@@ -1,57 +1,19 @@
module Main (main) where module Main (main) where
import Test.Tasty (TestTree, testGroup) import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Silver
import Test.Tasty.Silver.Interactive (defaultMain) import Test.Tasty.Silver.Interactive (defaultMain)
import Data.Traversable import qualified Gyehoek.Test.Golden
import Gyehoek.Driver qualified as Driver import qualified Gyehoek.Test.Sexp
import System.FilePath import qualified Gyehoek.Test.CPS.Syntax
import Data.List (List)
import Data.Functor ((<&>))
import System.Directory
import Data.Function
disabled :: List String main :: IO ()
disabled = main = defaultMain =<< root
[ "square"
root :: IO TestTree
root = testGroup "test" <$> sequenceA
[ Gyehoek.Test.Golden.root
, Gyehoek.Test.Sexp.root
, Gyehoek.Test.CPS.Syntax.root
] ]
main :: IO ()
main = defaultMain =<< goldenTests
goldenTests :: IO TestTree
goldenTests = do
all_cases <- listDirectory "golden"
let tests = all_cases
& filter (`notElem` disabled)
& fmap ("golden"</>)
pure $ testGroup "golden"
[ watTests tests
, executionTests tests
]
watTests :: List FilePath -> TestTree
watTests files =
testGroup "wat" $ files <&> \test ->
let source = test </> "source.scm"
golden = test </> "out.wat"
testname = takeFileName test
in goldenVsAction
testname
golden
(Driver.lower_e2e source)
id
executionTests :: List FilePath -> TestTree
executionTests files =
testGroup "execution" $ files <&> \test ->
let wat = test </> "out.wat"
testname = takeFileName test
resultfile = test </> "exec"
in goldenVsProg
testname
resultfile
"wasmtime"
["--invoke", "main", wat]
""
BIN
View File
Binary file not shown.
+124 -75
View File
@@ -1,78 +1,127 @@
(module (module
(func $print (import "guppy" "print") (param i32)) (import "gyehoek" "write" (func $gh-write (param (ref eq))))
(table 2 funcref) (import "gyehoek" "truthy?" (func $gh-truthy? (param (ref eq)) (result i32)))
(elem (i32.const 0) $halt) (type $heap-object (sub (struct (field $hash (mut i32)))))
(type $cont-type (func (param i32)))
(type $cont (func (param i32))) (type $cont-stack-type (array (mut (ref null $cont-type))))
(type $cont-stack-type (array (mut (ref null $cont)))) (type
(global $cont-stack (ref $cont-stack-type) $closure
(array.new_default $cont-stack-type (i32.const 128))) (sub
$heap-object
(struct
(field $hash (mut i32))
(field $code (ref $cont-type)))))
(global $cont-stack-top (mut i32) (i32.const 0)) (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)))
(type $arg-array-type (array (mut (ref null eq)))) (type $arg-array-type (array (mut (ref null eq))))
(global $arg-array (ref $arg-array-type) (global
(array.new_default $arg-array-type (i32.const 32))) $arg-array
(ref $arg-array-type)
;; (memory $memory i32 1) (array.new_default $arg-array-type (i32.const 32)))
;; (global $arg-stack-base i32 (i32.const 0)) (global $result (mut (ref null eq)) (ref.null eq))
;; (global $arg-stack-ptr i32 (global.get $arg-stack-base)) (func
;; (global $cont-stack-base i32 (i32.const 32)) $halt
;; (global $cont-stack-ptr i32 (global.get $cont-stack-base)) (param i32)
(global.get $arg-array)
(func $add (param $nargs i32) (i32.const 0)
(local $x (ref eq)) (array.get $arg-array-type)
(local $y (ref eq)) ref.as_non_null
(local $return (ref $cont)) (global.set $result))
(local.set $x (ref.as_non_null (func
(array.get $arg-array-type (param i32)
(global.get $arg-array) (@gyehoek
(i32.const 0)))) :origin
(local.set $y (ref.as_non_null (lambda (x lambda-tail1)
(array.get $arg-array-type (prim (* x x) (kappa (r2) (continue lambda-tail1 r2)))))
(global.get $arg-array) (local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(i32.const 1)))) (global.get $arg-array)
(array.set $arg-array-type (i32.const 0)
(global.get $arg-array) (array.get $arg-array-type)
(i32.const 0) ref.as_non_null
(ref.i31 (local.set 1)
(i32.add (i31.get_s (ref.cast (ref i31) (local.get $x))) (local.get 1)
(i31.get_s (ref.cast (ref i31) (local.get $y)))))) (i31.get_s (ref.cast (ref i31)))
(return_call_ref (i32.const 1)
$cont i32.shr_u
(i32.const 1) (local.get 1)
(block (result (ref $cont)) (i31.get_s (ref.cast (ref i31)))
(ref.as_non_null (i32.const 1)
(array.get $cont-stack-type i32.shr_u
(global.get $cont-stack) i32.mul
(global.get $cont-stack-top))) (i32.const 1)
(global.set $cont-stack-top i32.shl
(i32.sub (global.get $cont-stack-top) ref.i31
(i32.const 1)))))) (local.set 2)
(func $halt (param $nargs i32) (global.get $arg-array)
(call $print (i32.const 0)
(i31.get_s (local.get 2)
(ref.cast (array.set $arg-array-type)
(ref i31) (i32.const 1)
(ref.as_non_null (global.get $cont-stack)
(array.get $arg-array-type (global.get $cont-stack-top)
(global.get $arg-array) (array.get $cont-stack-type)
(i32.const 0))))))) ref.as_non_null
(func (export "main") (global.get $cont-stack-top)
;; push args (i32.const 1)
(array.set $arg-array-type i32.sub
(global.get $arg-array) (global.set $cont-stack-top)
(i32.const 0) (return_call_ref $cont-type))
(ref.i31 (i32.const 4))) (elem declare funcref (ref.func 3))
(array.set $arg-array-type (func
(global.get $arg-array) (param i32)
(i32.const 1) (@gyehoek :origin (kappa (x4) (continue halt x4)))
(ref.i31 (i32.const 5))) (local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
;; push return continuation (global.get $arg-array)
(array.set $cont-stack-type (i32.const 0)
(global.get $cont-stack) (array.get $arg-array-type)
(i32.const 0) ref.as_non_null
(ref.func $halt)) (local.set 1)
;; make call }:) (global.get $arg-array)
(return_call $add (i32.const 0)
;; inform $add how many arguments we called it with (local.get 1)
(i32.const 2)))) (array.set $arg-array-type)
(return_call $halt (i32.const 1)))
(func
$scm-entry
(param i32)
(@gyehoek
:origin
(letrec ((lambda-body0
(lambda (x lambda-tail1)
(prim (* x x) (kappa (r2) (continue lambda-tail1 r2))))))
(letrec ((r3 (kappa (x4) (continue halt x4))))
(lambda-body0 5 r3))))
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
(i32.const 0)
(ref.func 3)
(struct.new $closure)
(local.set 1)
(@gyehoek "push return cont" :idx 4)
(array.set $cont-stack-type
(global.get $cont-stack)
(global.get $cont-stack-top)
(ref.func 4))
(global.set $cont-stack-top
(i32.add (global.get $cont-stack-top)
(i32.const 1)))
(@gyehoek "load args")
(global.get $arg-array)
(i32.const 0)
(i32.const 5)
(i32.const 1)
i32.shl
ref.i31
(array.set $arg-array-type)
(@gyehoek todo (f' (local.get 1)) (ktail 1))
(return_call_ref $cont-type
(i32.const 1)
(struct.get $closure $code
(ref.cast (ref $closure) (local.get 1)))))
(elem declare funcref (ref.func 4))
(func
(export "main")
(call $scm-entry (i32.const 0))
(call $gh-write (ref.as_non_null (global.get $result)))))