4 Commits
Author SHA1 Message Date
msyds aa5b45ec76 rust runtime
build / build (push) Failing after 1m1s
2026-07-17 23:17:38 -06:00
msyds 85d34883a6 beautiful
build / build (push) Failing after 1m1s
2026-07-17 01:34:13 -06:00
msyds f09a63f11c top-level unquote-splice
build / build (push) Failing after 1m23s
2026-07-17 00:46:43 -06:00
msyds 2dffdf112c qq!
build / build (push) Failing after 1m15s
2026-07-16 14:16:01 -06:00
14 changed files with 2429 additions and 209 deletions
+6
View File
@@ -15,3 +15,9 @@ XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX00
zero indicates a 30-bit fixnum /
in the upper bits
#+end_example
| type/value | low bits |
|------------+----------|
| small int | 0 |
| ~false~ | 01 |
| ~true~ | 11 |
+11 -9
View File
@@ -21,8 +21,7 @@
haskellNix.overlay
(final: prev: {
gyehoek-wasmtime-wrapper = final.callPackage ./wasmtime.nix {};
})
(final: prev: {
gyehoek-runtime = final.callPackage ./runtime {};
gyehoek = final.haskell-nix.project' {
src = ./.;
compiler-nix-name = "ghc912";
@@ -42,7 +41,9 @@
})];
shell = {
withHoogle = true;
inputsFrom = [];
inputsFrom = [
final.gyehoek-runtime
];
tools = {
cabal = {};
haskell-language-server = {};
@@ -50,12 +51,13 @@
buildInputs = with final; [
haskellPackages.cabal-fmt
self.packages.${final.stdenv.hostPlatform.system}.shake
final.wabt
final.nodejs
final.wasm-tools
final.wac-cli
final.guile
final.gyehoek-wasmtime-wrapper
wabt
nodejs
wasm-tools
wac-cli
guile
gyehoek-wasmtime-wrapper
rust-analyzer
];
};
};
+1
View File
@@ -0,0 +1 @@
target/
+1865
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
[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"] }
wasmtime = "46.0.1"
+12
View File
@@ -0,0 +1,12 @@
{ rustPlatform
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "gyehoek-runtime";
version = "0.1.0";
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
};
doCheck = true;
})
+5
View File
@@ -0,0 +1,5 @@
use wasmtime::*;
pub fn say_hi (_caller : Caller<'_, u32>) {
println! ("hiiii~")
}
+35
View File
@@ -0,0 +1,35 @@
mod gyehoek;
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)
}
pub fn main () -> wasmtime::Result<()> {
let args = Args::parse ();
let engine = Engine::default ();
let module = Module::new (&engine, read (args.wasm)?)?;
let mut linker = Linker::new (&engine);
linker.func_wrap ("gyehoek", "say-hi", gyehoek::say_hi)?;
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 (())
}
+6
View File
@@ -0,0 +1,6 @@
(module
(import "gyehoek" "say-hi" (func $say-hi))
(func (export "main")
(call $say-hi)
(call $say-hi)
(call $say-hi)))
+221 -144
View File
@@ -6,6 +6,7 @@
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ApplicativeDo #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Lower
(lower, lowerProgram) where
@@ -14,6 +15,7 @@ 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 Control.Monad.Cont qualified as Cont
import Effectful.Writer.Static.Local
@@ -32,14 +34,15 @@ import Data.IntMap.Strict (IntMap)
import Data.String.Interpolate
import Gyehoek.Wasm qualified as Wasm
import Gyehoek.Wasm hiding (Expr)
import Language.Sexp.Located (pattern ParenList)
import Language.Sexp.Located qualified as SL
import Debug.Pretty.Simple
import Control.Monad.Fix
import Language.Sexp.Located (Sexp)
import Data.Functor.Foldable (cata)
data Env = MkEnv
{ runtime :: Runtime
, vars :: Vector Name
{ vars :: Vector Name
, kvars :: Vector Name
}
deriving (Show, Generic)
@@ -50,48 +53,48 @@ type instance IxValue Env = Name
instance Ixed Env where
ix i = #vars . ix (fromIntegral i)
data Runtime = MkRuntime
{ argArrayType :: Idx
, argArray :: Idx
, contType :: Idx
, contStackType :: Idx
, contStackTop :: Idx
, contStack :: Idx
, result :: Idx
, halt :: Idx
}
deriving (Show, Generic)
-- data Runtime = MkRuntime
-- { argArrayType :: Idx
-- , argArray :: Idx
-- , contType :: Idx
-- , contStackType :: Idx
-- , contStackTop :: Idx
-- , contStack :: Idx
-- , result :: Idx
-- , halt :: Idx
-- }
-- deriving (Show, Generic)
-- | @makeSmallFixnum@ emits an expression injecting the i32 on top
-- of the stack into the SCM unitype.
makeSmallFixnum :: Wasm.Expr
makeSmallFixnum = mconcat
[ ins "i32.const" [sxp @Int 1]
, ins "i32.shl" []
, ins "ref.i31" []
]
makeSmallFixnum = [expr|
(i32.const 1)
i32.shl
ref.i31
|]
-- | Given an expression @e@ leaving a @ref eq@ atop the stack,
-- @pushArg rt n e@ sets the nth slot of the arg-passing array to the
-- result of @e@.
pushArg :: Runtime -> Int -> Wasm.Expr -> Wasm.Expr
pushArg (MkRuntime {argArrayType,argArray}) n e = mconcat
[ ins "global.get" [sxp argArray]
, ins "i32.const" [sxp n]
, e
, ins "array.set" [sxp argArrayType]
]
pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
pushArg n e = [expr|
(global.get $arg-array)
(global.get #{n})
##{e}
(array.set $arg-array-type)
|]
-- | Pop the nth arg from the arg-passing array onto the stack.
popArg :: Runtime -> Int -> Wasm.Expr
popArg (MkRuntime {argArrayType,argArray}) n = mconcat
[ ins "global.get" [sxp argArray]
, ins "i32.const" [sxp n]
, ins "array.get" [sxp argArrayType]
, ins "ref.as_non_null" []
]
popArg :: Int -> Wasm.Expr
popArg n = [expr|
(global.get $arg-array)
(i32.const #{n})
(array.get $arg-array-type)
ref.as_non_null
|]
@@ -99,24 +102,28 @@ lowerVal :: Env -> Val -> Wasm.Expr
lowerVal g (ValLit l) =
case l of
LitInt n ->
ins "i32.const" [sxp n]
<> makeSmallFixnum
LitBool b ->
ins "i32.const" [sxp @Int $ if b then 1 else 0]
<> ins "ref.i31" []
LitInt n -> [expr|
(i32.const #{n})
##{makeSmallFixnum}
|]
LitBool b -> [expr|
(i32.const #{b'})
ref.i31
|]
where b' :: Int = if b then 0b11 else 0b01
_ -> _
lowerVal g (ValVar x) = ins "local.get" [sxp (1+l)]
lowerVal g (ValVar x) = [expr|(local.get #{l})|]
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
lower' g (Halt [v]) = pure . mconcat $
[ pushArg g.runtime 0 (lowerVal g v)
, ins "return_call" [sxp @Int 1]
]
lower' g (Halt [v]) = pure [expr|
##{arg}
(return_call $halt)
|]
where arg = pushArg 0 (lowerVal g v)
lower' g (ExpPrim p rs e) =
case p of
@@ -125,126 +132,196 @@ lower' g (ExpPrim p rs e) =
where
r = head rs
lower' g (ExpIf c t f) = do
t' <- lower' g t
f' <- lower' g f
pure $ lowerVal g c
<> Wasm.if' (Wasm.result [i32]) t' f'
-- lower' g (ExpIf c t f) = do
-- t' <- lower' g t
-- f' <- lower' g f
-- pure $ lowerVal g c
-- <> Wasm.if' (Wasm.result [i32]) t' f'
lower' g (ExpContinue k [x]) = pure . mconcat $
[ pushArg rt 0 (lowerVal g x)
, ins "i32.const" [sxp @Int 1] -- nargs
-- get the return continuation.
, ins "global.get" [sxp rt.contStack]
, ins "global.get" [sxp rt.contStackTop]
, ins "array.get" [sxp rt.contStackType]
, ins "ref.as_non_null" []
-- decrement contStackTop, completing the "pop."
, ins "global.get" [sxp rt.contStackTop]
, ins "i32.const" [sxp @Int (1 + l)]
, ins "i32.sub" []
, ins "global.set" [sxp rt.contStackTop]
, ins "return_call_ref" [sxp rt.contType]
]
where
rt = g.runtime
l = V.elemIndex k g.kvars ^?! _Just
-- lower' g (ExpContinue k [x]) = pure . mconcat $
-- [ pushArg rt 0 (lowerVal g x)
-- , ins "i32.const" [sxp @Int 1] -- nargs
-- -- get the return continuation.
-- , ins "global.get" [sxp rt.contStack]
-- , ins "global.get" [sxp rt.contStackTop]
-- , ins "array.get" [sxp rt.contStackType]
-- , ins "ref.as_non_null" []
-- -- decrement contStackTop, completing the "pop."
-- , ins "global.get" [sxp rt.contStackTop]
-- , ins "i32.const" [sxp @Int (1 + l)]
-- , ins "i32.sub" []
-- , ins "global.set" [sxp rt.contStackTop]
-- , ins "return_call_ref" [sxp rt.contType]
-- ]
-- where
-- rt = g.runtime
-- l = V.elemIndex k g.kvars ^?! _Just
lower' g (ExpLet [(r,MkLambda xs ktail m)] e) = do
idx <- defun [i32] [] (replicate 5 scm) \_ -> do
let g' = g & #vars <>~ V.fromList xs
& #kvars <>~ [ktail]
m' <- lower' g' m
pure . mconcat $
[ xs & ifoldMap \n _ ->
popArg g.runtime n <> ins "local.set" [sxp (1+n)]
, m'
]
declareFuncref idx
let g' = g & #vars <>~ [r]
let n = length g.vars
e' <- lower' g' e
pure . mconcat $
[ ins "ref.func" [sxp idx]
, ins "local.set" [sxp (n+1)]
, e'
]
-- lower' g (ExpLet [(r,MkLambda xs ktail m)] e) = do
-- idx <- defun [i32] [] (replicate 5 scm) \_ -> do
-- let g' = g & #vars <>~ V.fromList xs
-- & #kvars <>~ [ktail]
-- m' <- lower' g' m
-- pure . mconcat $
-- [ xs & ifoldMap \n _ ->
-- popArg g.runtime n <> ins "local.set" [sxp (1+n)]
-- , m'
-- ]
-- declareFuncref idx
-- let g' = g & #vars <>~ [r]
-- let n = length g.vars
-- e' <- lower' g' e
-- pure . mconcat $
-- [ ins "ref.func" [sxp idx]
-- , ins "local.set" [sxp (n+1)]
-- , e'
-- ]
lower' g e = error . show $ e
-- lower' g e = error . show $ e
lowerBinOp
:: (GenMod :> es)
=> Text -> Env -> Val -> Val -> Name -> Exp -> Eff es Wasm.Expr
lowerBinOp op g x y r e = do
let g' = g & #vars <>~ [r]
let n = succ $ length (g ^. #vars)
let x' = lowerVal g x
let y' = lowerVal g y
e' <- lower' g' e
pure . mconcat $
[ lowerVal g x
, ins "ref.cast" [sxp $ ref i31]
, ins "i31.get_s" []
, lowerVal g y
, ins "ref.cast" [sxp $ ref i31]
, ins "i31.get_s" []
, ins op []
, ins "ref.i31" []
, ins "local.set" [sxp (1+n)]
, e'
]
where
g' = g & #vars <>~ [r]
n = length (g ^. #vars)
pure [expr|
##{x'}
(i31.get_s (ref.cast (ref i31)))
##{y'}
(i31.get_s (ref.cast (ref i31)))
(local.set #{n} (ref.i31 #{op}))
##{e'}
|]
scm = ref eq
-- scm = ref eq
emitRuntime :: GenMod :> es => Eff es Runtime
-- emitRuntime :: GenMod :> es => Eff es Runtime
-- emitRuntime = mfix \runtime -> do
-- 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"
emitRuntime :: GenMod :> es => Eff es ()
emitRuntime = mfix \runtime -> do
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]
Wasm.defineType [wat|
(type $heap-object (sub (struct (field $hash (mut i32)))))
|]
Wasm.defineType [wat|
(type $cont-type (func (param i32)))
|]
Wasm.defineType [wat|
(type $cont-stack-type (array (mut (ref null $cont-type))))
|]
Wasm.defineGlobal [wat|
(global $cont-stack-top (mut i32) (i32.const 0))
|]
Wasm.defineGlobal [wat|
(global $cont-stack (ref $cont-stack-type)
(array.new_default $cont-stack-type (i32.const 128)))
|]
-- arg array
argArrayType <- Wasm.deftype $ Wasm.array $ mut $ refnull eq
argArray <- Wasm.defglobal (ref (Wasm.fromIdx argArrayType)) $
ins "i32.const" [sxp @Int 32]
<> ins "array.new_default" [sxp argArrayType]
-- consIdx <- Wasm.defun _ _ _ _
result <- Wasm.defglobal (mut (refnull eq)) $ ins "ref.null" [sxp eq]
halt <- Wasm.defun [i32] [] (replicate 5 scm) \_ ->
pure . mconcat $
[ popArg runtime 0
, ins "global.set" [sxp result]
]
pure $ MkRuntime
{argArray,argArrayType
,contStack,contStackTop,contStackType,contType
,result,halt}
-- pure $ error "todo"
Wasm.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 runtime mempty mempty
scm_entry <- Wasm.defun [i32] [] (replicate 5 scm) \_ ->
lower' g e
main <- Wasm.defun [] [scm] [scm, scm, scm, scm, scm] \_ ->
pure . mconcat $
-- push return cont
[-- ins "ref.func" [sxp halt]
-- make call
ins "i32.const" [sxp @Int 0]
, ins "call" [sxp scm_entry]
, ins "global.get" [sxp runtime.result]
, ins "ref.as_non_null" []
]
Wasm.export "main" "func" main
let g = MkEnv mempty mempty
e' <- lower' g e
Wasm.defineFunction [wat|
(func $scm-entry (param i32)
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{e'})
|]
Wasm.defineFunction [wat|
(func (export "main") (result (ref eq))
(call $scm-entry (i32.const 0))
(ref.as_non_null (global.get $result)))
|]
lowerProgram :: Program -> Eff es Text
lowerProgram (MkProgram e) = lower e
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 :: Wasm.Expr
metavars = [expr|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)
+1
View File
@@ -1,5 +1,6 @@
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
module Gyehoek.CPS.Syntax
( Val(..)
, Kappa(..)
-11
View File
@@ -23,7 +23,6 @@ module Gyehoek.Scheme.Syntax
, qexp
, qprog
, subst
, freeVariables
)
where
@@ -254,13 +253,3 @@ subst f = \e -> cata go e mempty where
go (ExpLetF _ _) _ = error "todo lol"
go (ExpLambdaF bs e) bound = e $ insertFrom bs bound
go e bound = embed $ fmap ($ bound) e
-- | Unlawful!
freeVariables :: Traversal Exp Exp Name Exp
freeVariables k = \e -> cataA go e mempty where
go (ExpVarF x) bound
| not (x `HS.member` bound) = k x
| otherwise = pure $ ExpVar x
go (ExpLetF _ _) _ = error "todo lol"
go (ExpLambdaF bs e) bound = e $ insertFrom bs bound
go e bound = embed <$> traverse ($ bound) e
+130 -41
View File
@@ -27,6 +27,7 @@ module Gyehoek.Sexp
, encodePretty
, UglySexpIso(..)
, AsSexpIso(..)
, SpliceSexp(..)
, parseSexpsWithPos
, parseSexpWithPos
, parseSexp
@@ -34,11 +35,15 @@ module Gyehoek.Sexp
, sxs
, makeSx
, makeSxs
, toSexp
, fromSexp
, stripLocation
, format
)
where
where
import Data.Text (Text)
import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty)
import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty, fromSexp)
import Language.SexpGrammar qualified as Sexp
import Language.Sexp qualified as S
import Language.SexpGrammar.Generic
@@ -62,11 +67,20 @@ import Data.Void (absurd, Void)
import Data.Coerce (coerce)
import qualified Data.Map
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 Control.Category
import Data.Data (Data, Typeable, cast)
import Data.Data (Data (..), Typeable, cast)
import Language.Haskell.TH.Syntax (lift, Lift)
import GHC.IsList (fromList)
import Data.Functor.Foldable (cata)
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
sexp :: SexpIso a => Iso' a Text
@@ -74,6 +88,9 @@ sexp = iso
(either error id . encode)
(either error id . decode)
format :: Sexp -> Text
format = decodeUtf8 . view strict . SL.format
encode :: SexpIso a => a -> Either String Text
encode = encodeWith sexpIso
@@ -91,25 +108,31 @@ decodeWith g = Sexp.decodeWith g "FILE" . view lazy . encodeUtf8
encodePrettyWith :: SexpGrammar a -> a -> Either String Text
encodePrettyWith g =
(_Right %~ decodeUtf8 . view strict) . Sexp.encodePrettyWith g
(_Right %~ decodeUtf8 . view strict) . Sexp.encodePrettyWith g
parseSexps :: SexpIso a => FilePath -> Text -> Either String (List a)
parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (fromSexp sexpIso)
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp sexpIso)
parseSexp :: SexpIso a => FilePath -> Text -> Either String a
parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (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 g pos =
marshal . SL.parseSexpsWithPos pos . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (fromSexp g)
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp g)
parseSexpWithPos :: SexpGrammar a -> Position -> Text -> Either String a
parseSexpWithPos g pos =
marshal . SL.parseSexpWithPos pos . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (fromSexp g)
where marshal = join . traverseOf _Right (Sexp.fromSexp g)
nonEmptyGrammar :: Grammar p (NonEmpty x :- t) (List x :- x :- t)
nonEmptyGrammar = IGB.Iso
@@ -231,21 +254,18 @@ getPos = do
Loc {loc_filename,loc_start} <- location
pure $ SL.Position loc_filename (fst loc_start) (snd loc_start)
makeSxs :: Data b => SexpGrammar a -> (List a -> b) -> QuasiQuoter
makeSxs g f = QuasiQuoter
{ quoteExp = \str -> do
pos <- getPos
case parseSexpsWithPos g pos (T.pack str) of
Left e -> fail e
Right xs -> dataToExpQ (const Nothing) (f xs)
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
fromSexp :: SexpIso a => Sexp -> a
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 = either error id . Sexp.toSexp sexpIso
toSexps :: (Foldable f, SexpIso a) => f a -> List Sexp
toSexps = foldMap \x -> [toSexp x]
pattern Unquote x =
SL.Modified Hash (SL.BraceList [SL.Symbol x])
pattern UnquoteSplicing x =
@@ -262,22 +282,71 @@ instance Each Sexp Sexp Sexp Sexp where
each k (SL.BraceList xs) = SL.BraceList <$> traverse k xs
each _ e@(SL.Atom _; SL.Modified _ _) = pure e
metaSexp :: Sexp.Sexp -> Maybe ExpQ
metaSexp (Unquote x) =
Just [| toSexp $(varE (mkName (T.unpack x))) |]
metaSexp (SL.ParenList xs)
| (_:_) <- xs ^.. each . _UnquoteSplicing
= Just [| SL.ParenList (mconcat $(listE spans)) |]
stripLocation :: Sexp -> Sexp
stripLocation = cata \case
SL.Compose (a SL.:< e) ->
SL.Fix . SL.Compose $ SL.dummyPos SL.:< e
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 (Vector a) where
spliceSexp = toSexps
instance SexpIso a => SpliceSexp (List a) where
spliceSexp = toSexps
instance SpliceSexp Sexp where
spliceSexp = toListOf each
unquoteSplicing :: List Sexp.Sexp -> Maybe ExpQ
unquoteSplicing xs
| (_:_) <- xs ^.. folded . _UnquoteSplicing
= Just [| mconcat $(spans) |]
where
spans = xs
& groupBy \cases
(UnquoteSplicing _) _ -> False
_ (UnquoteSplicing _) -> False
_ _ -> True
(UnquoteSplicing _; Unquote _) _ -> False
_ (UnquoteSplicing _; Unquote _) -> False
_ _ -> True
& fmap \case
[UnquoteSplicing x] -> varE (mkName (T.unpack x))
x -> lift x
metaSexp _ = Nothing
[e@(Unquote _)] ->
case unquote e of
Just x -> [| [$(x)] |]
Nothing -> error "unreachable"
[UnquoteSplicing x] ->
[| stripLocation <$> spliceSexp $(varE (mkName (T.unpack x))) |]
x -> [| stripLocation <$> x |]
& listE
unquoteSplicing _ = Nothing
unquote :: Sexp.Sexp -> Maybe ExpQ
unquote (Unquote x) =
Just [| stripLocation . toSexp $ $(varE (mkName (T.unpack x))) |]
unquote _ = Nothing
_ParenList :: Prism' Sexp (List Sexp)
_ParenList = prism' SL.ParenList \case
SL.ParenList xs -> Just xs
_ -> Nothing
metaSexps :: List Sexp.Sexp -> Maybe ExpQ
metaSexps = unquoteSplicing
metaSexpsV :: Vector Sexp.Sexp -> Maybe ExpQ
metaSexpsV = unquoteSplicing . V.toList
metaSexp :: Sexp.Sexp -> Maybe ExpQ
metaSexp x = unquote x
-- 뻘짓뻘짓뻘짓뻘짓뻘짓
class Lift1 f where
@@ -287,10 +356,10 @@ lift1 :: (Lift1 f, Lift a, Quote m) => f a -> m Exp
lift1 = liftLift lift
instance Lift1 f => Lift (SL.Fix f) where
lift (SL.Fix inner) = appE [|Fix|] (lift1 inner)
lift (SL.Fix inner) = appE [|SL.Fix|] (lift1 inner)
instance (Lift1 f, Lift1 g) => Lift1 (SL.Compose f g) where
liftLift l (SL.Compose fga) = [|Compose $(liftLift (liftLift l) fga)|]
liftLift l (SL.Compose fga) = [|SL.Compose $(liftLift (liftLift l) fga)|]
instance Lift a => Lift1 (SL.LocatedBy a) where
liftLift l (a SL.:< e) = [|(SL.:<) $(lift a) $(l e)|]
@@ -304,7 +373,7 @@ instance Lift1 SL.SexpF where
SL.ParenListF es -> [|SL.ParenListF $(liftLift l es)|]
SL.BracketListF es -> [|SL.BracketListF $(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 SL.Atom
@@ -314,17 +383,37 @@ deriving instance Lift SL.Prefix
extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
extQ f g a = maybe (f a) g (cast a)
makeSx :: Data a => SexpGrammar a -> QuasiQuoter
makeSx g = QuasiQuoter
makeSxs :: Data r => Code Q (List Sexp -> r) -> QuasiQuoter
makeSxs f = QuasiQuoter
{ quoteExp = \str -> do
pos <- getPos
case parseSexpWithPos g pos (T.pack str) of
case readSexpsWithPos pos (T.pack str) of
Left e -> fail e
Right x -> dataToExpQ (const Nothing `extQ` metaSexp) x
Right xs -> [| $(unTypeCode f) $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
xs
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
sxs = makeSxs (sexpIso @Sexp) id
sx = makeSx (sexpIso @Sexp)
makeSx :: Data r => Code Q (Sexp -> r) -> QuasiQuoter
makeSx f = QuasiQuoter
{ quoteExp = \str -> do
pos <- getPos
case readSexpWithPos pos (T.pack str) of
Left e -> fail e
Right x -> [| $(unTypeCode f) $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
x
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
sxs = makeSxs [||id||]
sx = makeSx [||id||]
+127 -4
View File
@@ -10,8 +10,27 @@
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
module Gyehoek.Wasm
( Module
(
-- * syntax
Module
, Idx
, Expr
-- ** quasiquoters
, expr
, Gyehoek.Sexp.sx
, Gyehoek.Sexp.sxs
-- * GenMod effect
, GenMod
, runGenMod
, execGenMod
, defineFunction
, defineType
, defineGlobal
, declare
, renderModule
, wat
)
where
@@ -45,18 +64,31 @@ import GHC.IsList (IsList(..))
import Data.Coerce (coerce)
import qualified Control.Category
import Data.Functor (void)
import Language.Haskell.TH.Quote (QuasiQuoter)
import Data.Data (Data)
import Data.Functor.Foldable (cata)
import Gyehoek.Sexp (sx)
import qualified Language.Sexp as SL
newtype Module = MkModule { inner :: Vector Sexp }
deriving (Show, Generic)
deriving newtype (Semigroup, Monoid)
newtype Expr = MkExpr { inner :: Vector Sexp }
deriving (Show, Generic)
newtype Expr = MkExpr { inner :: Vector Instr }
deriving (Show, Generic, Data, Eq)
deriving newtype (Semigroup, Monoid)
instance IsList Expr where
type Item Expr = Instr
fromList = MkExpr . V.fromList
toList = V.toList . view #inner
newtype Instr = MkInstr { inner :: Sexp }
deriving (Show, Generic, Data, Eq)
newtype Idx = MkIdx { inner :: Natural }
deriving (Generic)
deriving (Generic, Data)
deriving newtype (Show)
@@ -68,9 +100,100 @@ data GenModState = MkGenModState
{ mod :: Module
, funcs :: Natural
, types :: Natural
, globals :: Natural
}
deriving (Show, Generic)
instance Semigroup GenModState where
m1 <> m2 = MkGenModState
{ mod = m1.mod <> m2.mod
, funcs = m1.funcs + m2.funcs
, types = m1.types + m2.types
, globals = m1.globals + m2.globals
}
instance Monoid GenModState where
mempty = MkGenModState
{ mod = mempty
, funcs = 0
, types = 0
, globals = 0
}
data GenMod :: Effect where
DefineFunction :: Sexp -> GenMod m Idx
DefineType :: Sexp -> GenMod m Idx
DefineGlobal :: Sexp -> GenMod m Idx
Declare :: Sexp -> GenMod m ()
type instance DispatchOf GenMod = Dynamic
defineFunction :: GenMod :> es => Sexp -> Eff es Idx
defineFunction = send . DefineFunction
defineType :: GenMod :> es => Sexp -> Eff es Idx
defineType = send . DefineType
defineGlobal :: GenMod :> es => Sexp -> Eff es Idx
defineGlobal = send . DefineGlobal
declare :: GenMod :> es => Sexp -> Eff es ()
declare = send . Declare
appendAndIncrement
:: State GenModState :> es
=> LensLike' ((,) _) GenModState Natural
-> Sexp
-> Eff es Idx
appendAndIncrement l s =
state \st -> st
& #mod . #inner <>~ V.singleton s
& l <<%~ succ
& _1 %~ MkIdx
runGenMod :: Eff (GenMod : es) a -> Eff es (a, Module)
runGenMod =
let run = (mapped . _2 %~ view #mod) . runStateLocal (mempty @GenModState)
in reinterpret run \cases
_ (DefineFunction s) -> appendAndIncrement #funcs s
_ (DefineType s) -> appendAndIncrement #types s
_ (DefineGlobal s) -> appendAndIncrement #globals s
_ (Declare s) -> #mod . #inner <>= V.singleton s
execGenMod :: Eff (GenMod : es) a -> Eff es Module
execGenMod = fmap snd . runGenMod
renderModule :: Module -> Text
renderModule (MkModule ss) = Gyehoek.Sexp.format [sx|
(module ##{ss})
|]
-- SexpIso instances
instance SexpIso Idx where
sexpIso = with \idx ->
Sexp.integer >>> Sexp.partialOsi f g
>>> idx
where
f n | n < 0 = Left $ Sexp.unexpected "negative"
<> Sexp.expected "natural"
| otherwise = Right $ fromIntegral n
g = fromIntegral
instance SexpIso Instr where
sexpIso = with id
instance Gyehoek.Sexp.SpliceSexp Expr where
spliceSexp = toListOf $ #inner . each . #inner
-- quasiquoters
expr :: QuasiQuoter
expr = Gyehoek.Sexp.makeSxs
[||MkExpr . V.fromList . (each . #inner %~ Gyehoek.Sexp.stripLocation)
. fmap (Gyehoek.Sexp.fromSexp @Instr) ||]
wat :: QuasiQuoter
wat = Gyehoek.Sexp.makeSx [|| id ||]