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 / 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 |
+11 -9
View File
@@ -21,8 +21,7 @@
haskellNix.overlay haskellNix.overlay
(final: prev: { (final: prev: {
gyehoek-wasmtime-wrapper = final.callPackage ./wasmtime.nix {}; gyehoek-wasmtime-wrapper = final.callPackage ./wasmtime.nix {};
}) gyehoek-runtime = final.callPackage ./runtime {};
(final: prev: {
gyehoek = final.haskell-nix.project' { gyehoek = final.haskell-nix.project' {
src = ./.; src = ./.;
compiler-nix-name = "ghc912"; compiler-nix-name = "ghc912";
@@ -42,7 +41,9 @@
})]; })];
shell = { shell = {
withHoogle = true; withHoogle = true;
inputsFrom = []; inputsFrom = [
final.gyehoek-runtime
];
tools = { tools = {
cabal = {}; cabal = {};
haskell-language-server = {}; haskell-language-server = {};
@@ -50,12 +51,13 @@
buildInputs = with final; [ buildInputs = with final; [
haskellPackages.cabal-fmt haskellPackages.cabal-fmt
self.packages.${final.stdenv.hostPlatform.system}.shake self.packages.${final.stdenv.hostPlatform.system}.shake
final.wabt wabt
final.nodejs nodejs
final.wasm-tools wasm-tools
final.wac-cli wac-cli
final.guile guile
final.gyehoek-wasmtime-wrapper 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 OverloadedLists #-}
{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE ApplicativeDo #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Lower module Gyehoek.CPS.Lower
(lower, lowerProgram) where (lower, lowerProgram) where
@@ -14,6 +15,7 @@ import Data.Generics.Labels
import Gyehoek.Scheme.Syntax qualified as Scm import Gyehoek.Scheme.Syntax qualified as Scm
import Gyehoek.GenSym import Gyehoek.GenSym
import Data.List.NonEmpty (NonEmpty((:|))) import Data.List.NonEmpty (NonEmpty((:|)))
import Data.List (List)
import Effectful import Effectful
import Control.Monad.Cont qualified as Cont import Control.Monad.Cont qualified as Cont
import Effectful.Writer.Static.Local import Effectful.Writer.Static.Local
@@ -32,14 +34,15 @@ import Data.IntMap.Strict (IntMap)
import Data.String.Interpolate 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 (pattern ParenList) import Language.Sexp.Located qualified as SL
import Debug.Pretty.Simple import Debug.Pretty.Simple
import Control.Monad.Fix import Control.Monad.Fix
import Language.Sexp.Located (Sexp)
import Data.Functor.Foldable (cata)
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)
@@ -50,48 +53,48 @@ 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 -- data Runtime = MkRuntime
{ argArrayType :: Idx -- { argArrayType :: Idx
, argArray :: Idx -- , argArray :: Idx
, contType :: Idx -- , contType :: Idx
, contStackType :: Idx -- , contStackType :: Idx
, contStackTop :: Idx -- , contStackTop :: Idx
, contStack :: Idx -- , contStack :: Idx
, result :: Idx -- , result :: Idx
, halt :: Idx -- , halt :: Idx
} -- }
deriving (Show, Generic) -- deriving (Show, Generic)
-- | @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] (i32.const 1)
, ins "i32.shl" [] i32.shl
, ins "ref.i31" [] 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] (global.get $arg-array)
, ins "i32.const" [sxp n] (global.get #{n})
, e ##{e}
, ins "array.set" [sxp argArrayType] (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] (global.get $arg-array)
, ins "i32.const" [sxp n] (i32.const #{n})
, ins "array.get" [sxp argArrayType] (array.get $arg-array-type)
, ins "ref.as_non_null" [] ref.as_non_null
] |]
@@ -99,24 +102,28 @@ lowerVal :: Env -> Val -> Wasm.Expr
lowerVal g (ValLit l) = lowerVal g (ValLit l) =
case l of 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) = [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 lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
lower' g (Halt [v]) = pure . mconcat $ lower' g (Halt [v]) = pure [expr|
[ pushArg g.runtime 0 (lowerVal g v) ##{arg}
, ins "return_call" [sxp @Int 1] (return_call $halt)
] |]
where arg = pushArg 0 (lowerVal g v)
lower' g (ExpPrim p rs e) = lower' g (ExpPrim p rs e) =
case p of case p of
@@ -125,126 +132,196 @@ lower' g (ExpPrim p rs e) =
where where
r = head rs r = head rs
lower' g (ExpIf c t f) = do -- lower' g (ExpIf c t f) = do
t' <- lower' g t -- t' <- lower' g t
f' <- lower' g f -- f' <- lower' g f
pure $ lowerVal g c -- pure $ lowerVal g c
<> Wasm.if' (Wasm.result [i32]) t' f' -- <> Wasm.if' (Wasm.result [i32]) t' f'
lower' g (ExpContinue k [x]) = pure . mconcat $ -- lower' g (ExpContinue k [x]) = pure . mconcat $
[ pushArg rt 0 (lowerVal g x) -- [ pushArg rt 0 (lowerVal g x)
, ins "i32.const" [sxp @Int 1] -- nargs -- , ins "i32.const" [sxp @Int 1] -- nargs
-- get the return continuation. -- -- get the return continuation.
, ins "global.get" [sxp rt.contStack] -- , ins "global.get" [sxp rt.contStack]
, ins "global.get" [sxp rt.contStackTop] -- , ins "global.get" [sxp rt.contStackTop]
, ins "array.get" [sxp rt.contStackType] -- , ins "array.get" [sxp rt.contStackType]
, ins "ref.as_non_null" [] -- , ins "ref.as_non_null" []
-- decrement contStackTop, completing the "pop." -- -- decrement contStackTop, completing the "pop."
, ins "global.get" [sxp rt.contStackTop] -- , ins "global.get" [sxp rt.contStackTop]
, ins "i32.const" [sxp @Int (1 + l)] -- , ins "i32.const" [sxp @Int (1 + l)]
, ins "i32.sub" [] -- , ins "i32.sub" []
, ins "global.set" [sxp rt.contStackTop] -- , ins "global.set" [sxp rt.contStackTop]
, ins "return_call_ref" [sxp rt.contType] -- , ins "return_call_ref" [sxp rt.contType]
] -- ]
where -- where
rt = g.runtime -- rt = g.runtime
l = V.elemIndex k g.kvars ^?! _Just -- l = V.elemIndex k g.kvars ^?! _Just
lower' g (ExpLet [(r,MkLambda xs ktail m)] e) = do -- lower' g (ExpLet [(r,MkLambda xs ktail m)] e) = do
idx <- defun [i32] [] (replicate 5 scm) \_ -> do -- idx <- defun [i32] [] (replicate 5 scm) \_ -> do
let g' = g & #vars <>~ V.fromList xs -- let g' = g & #vars <>~ V.fromList xs
& #kvars <>~ [ktail] -- & #kvars <>~ [ktail]
m' <- lower' g' m -- m' <- lower' g' m
pure . mconcat $ -- pure . mconcat $
[ xs & ifoldMap \n _ -> -- [ xs & ifoldMap \n _ ->
popArg g.runtime n <> ins "local.set" [sxp (1+n)] -- popArg g.runtime n <> ins "local.set" [sxp (1+n)]
, m' -- , m'
] -- ]
declareFuncref idx -- declareFuncref idx
let g' = g & #vars <>~ [r] -- let g' = g & #vars <>~ [r]
let n = length g.vars -- let n = length g.vars
e' <- lower' g' e -- e' <- lower' g' e
pure . mconcat $ -- pure . mconcat $
[ ins "ref.func" [sxp idx] -- [ ins "ref.func" [sxp idx]
, ins "local.set" [sxp (n+1)] -- , ins "local.set" [sxp (n+1)]
, e' -- , e'
] -- ]
lower' g e = error . show $ e -- lower' g e = error . show $ e
lowerBinOp lowerBinOp
:: (GenMod :> es) :: (GenMod :> es)
=> Text -> Env -> Val -> Val -> Name -> Exp -> Eff es Wasm.Expr => Text -> Env -> Val -> Val -> Name -> Exp -> Eff es Wasm.Expr
lowerBinOp op g x y r e = do 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 e' <- lower' g' e
pure . mconcat $ pure [expr|
[ lowerVal g x ##{x'}
, ins "ref.cast" [sxp $ ref i31] (i31.get_s (ref.cast (ref i31)))
, ins "i31.get_s" [] ##{y'}
, lowerVal g y (i31.get_s (ref.cast (ref i31)))
, ins "ref.cast" [sxp $ ref i31] (local.set #{n} (ref.i31 #{op}))
, ins "i31.get_s" [] ##{e'}
, ins op [] |]
, ins "ref.i31" []
, ins "local.set" [sxp (1+n)]
, e'
]
where
g' = g & #vars <>~ [r]
n = length (g ^. #vars)
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 emitRuntime = mfix \runtime -> do
heapObjectIdx <- Wasm.deftypeNamed "$heap-object" $ Wasm.sub [] $ Wasm.struct
[ Wasm.mut i32 ]
-- cont stack -- cont stack
contType <- Wasm.deftype $ Wasm.func [i32] [] Wasm.defineType [wat|
contStackType <- Wasm.deftype $ array $ mut $ refnull (fromIdx contType) (type $heap-object (sub (struct (field $hash (mut i32)))))
contStackTop <- Wasm.defglobal (mut i32) $ ins "i32.const" [sxp @Int 0] |]
contStack <- Wasm.defglobal (ref (Wasm.fromIdx contStackType)) $ Wasm.defineType [wat|
ins "i32.const" [sxp @Int 128] (type $cont-type (func (param i32)))
<> ins "array.new_default" [sxp contStackType] |]
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 -- arg array
argArrayType <- Wasm.deftype $ Wasm.array $ mut $ refnull eq Wasm.defineType [wat|
argArray <- Wasm.defglobal (ref (Wasm.fromIdx argArrayType)) $ (type $arg-array-type (array (mut (ref null eq))))
ins "i32.const" [sxp @Int 32] |]
<> ins "array.new_default" [sxp argArrayType] Wasm.defineGlobal [wat|
-- consIdx <- Wasm.defun _ _ _ _ (global $arg-array (ref $arg-array-type)
result <- Wasm.defglobal (mut (refnull eq)) $ ins "ref.null" [sxp eq] (array.new_default $arg-array-type) (i32.const 32))
halt <- Wasm.defun [i32] [] (replicate 5 scm) \_ -> |]
pure . mconcat $ -- other things 😼
[ popArg runtime 0 Wasm.defineGlobal [wat|
, ins "global.set" [sxp result] (global $result (mut (ref null eq))
] (ref.null eq))
pure $ MkRuntime |]
{argArray,argArrayType -- procedures
,contStack,contStackTop,contStackType,contType let arg = popArg 0
,result,halt} Wasm.defineFunction [wat|
-- pure $ error "todo" (func $halt (param i32)
##{arg}
(global.set $result))
|]
pure ()
lower :: Exp -> Eff es Text lower :: Exp -> Eff es Text
lower e = fmap Wasm.renderModule . Wasm.execGenMod $ do lower e = fmap Wasm.renderModule . Wasm.execGenMod $ do
runtime <- emitRuntime runtime <- emitRuntime
let g = MkEnv runtime mempty mempty let g = MkEnv mempty mempty
scm_entry <- Wasm.defun [i32] [] (replicate 5 scm) \_ -> e' <- lower' g e
lower' g e Wasm.defineFunction [wat|
main <- Wasm.defun [] [scm] [scm, scm, scm, scm, scm] \_ -> (func $scm-entry (param i32)
pure . mconcat $ (local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
-- push return cont ##{e'})
[-- ins "ref.func" [sxp halt] |]
-- make call Wasm.defineFunction [wat|
ins "i32.const" [sxp @Int 0] (func (export "main") (result (ref eq))
, ins "call" [sxp scm_entry] (call $scm-entry (i32.const 0))
, ins "global.get" [sxp runtime.result] (ref.as_non_null (global.get $result)))
, ins "ref.as_non_null" [] |]
]
Wasm.export "main" "func" main
lowerProgram :: Program -> Eff es Text lowerProgram :: Program -> Eff es Text
lowerProgram (MkProgram e) = lower e 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 OverloadedLabels #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
module Gyehoek.CPS.Syntax module Gyehoek.CPS.Syntax
( Val(..) ( Val(..)
, Kappa(..) , Kappa(..)
-11
View File
@@ -23,7 +23,6 @@ module Gyehoek.Scheme.Syntax
, qexp , qexp
, qprog , qprog
, subst , subst
, freeVariables
) )
where where
@@ -254,13 +253,3 @@ subst f = \e -> cata go e mempty where
go (ExpLetF _ _) _ = error "todo lol" go (ExpLetF _ _) _ = error "todo lol"
go (ExpLambdaF bs e) bound = e $ insertFrom bs bound go (ExpLambdaF bs e) bound = e $ insertFrom bs bound
go e bound = embed $ fmap ($ bound) e go e bound = embed $ fmap ($ bound) e
-- | 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 , encodePretty
, UglySexpIso(..) , UglySexpIso(..)
, AsSexpIso(..) , AsSexpIso(..)
, SpliceSexp(..)
, parseSexpsWithPos , parseSexpsWithPos
, parseSexpWithPos , parseSexpWithPos
, parseSexp , parseSexp
@@ -34,11 +35,15 @@ module Gyehoek.Sexp
, sxs , sxs
, makeSx , makeSx
, makeSxs , makeSxs
, toSexp
, fromSexp
, stripLocation
, format
) )
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) import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty, fromSexp)
import Language.SexpGrammar qualified as Sexp import Language.SexpGrammar qualified as Sexp
import Language.Sexp qualified as S import Language.Sexp qualified as S
import Language.SexpGrammar.Generic import Language.SexpGrammar.Generic
@@ -62,11 +67,20 @@ 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)
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 sexp :: SexpIso a => Iso' a Text
@@ -74,6 +88,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
@@ -91,25 +108,31 @@ decodeWith g = Sexp.decodeWith g "FILE" . view lazy . encodeUtf8
encodePrettyWith :: SexpGrammar a -> a -> Either String Text encodePrettyWith :: SexpGrammar a -> a -> Either String Text
encodePrettyWith g = 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 :: SexpIso a => FilePath -> Text -> Either String (List a)
parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8 parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (fromSexp sexpIso) where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp sexpIso)
parseSexp :: SexpIso a => FilePath -> Text -> Either String a parseSexp :: SexpIso a => FilePath -> Text -> Either String a
parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8 parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (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
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 :: SexpGrammar a -> Position -> Text -> Either String a
parseSexpWithPos g pos = parseSexpWithPos g pos =
marshal . SL.parseSexpWithPos pos . view lazy . encodeUtf8 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 :: Grammar p (NonEmpty x :- t) (List x :- x :- t)
nonEmptyGrammar = IGB.Iso nonEmptyGrammar = IGB.Iso
@@ -231,21 +254,18 @@ getPos = do
Loc {loc_filename,loc_start} <- location Loc {loc_filename,loc_start} <- location
pure $ SL.Position loc_filename (fst loc_start) (snd loc_start) pure $ SL.Position loc_filename (fst loc_start) (snd loc_start)
makeSxs :: Data b => SexpGrammar a -> (List a -> b) -> QuasiQuoter fromSexp :: SexpIso a => Sexp -> a
makeSxs g f = QuasiQuoter fromSexp = either error id . Sexp.fromSexp sexpIso
{ quoteExp = \str -> do
pos <- getPos fromSexp' :: SexpGrammar a -> Sexp -> a
case parseSexpsWithPos g pos (T.pack str) of fromSexp' g = either error id . Sexp.fromSexp g
Left e -> fail e
Right xs -> dataToExpQ (const Nothing) (f xs)
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
toSexp :: SexpIso a => a -> Sexp toSexp :: SexpIso a => a -> Sexp
toSexp = either error id . Sexp.toSexp sexpIso toSexp = either error id . Sexp.toSexp sexpIso
toSexps :: (Foldable f, SexpIso a) => f a -> List Sexp
toSexps = foldMap \x -> [toSexp x]
pattern Unquote x = pattern Unquote x =
SL.Modified Hash (SL.BraceList [SL.Symbol x]) SL.Modified Hash (SL.BraceList [SL.Symbol x])
pattern UnquoteSplicing 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 k (SL.BraceList xs) = SL.BraceList <$> traverse k xs
each _ e@(SL.Atom _; SL.Modified _ _) = pure e each _ e@(SL.Atom _; SL.Modified _ _) = pure e
metaSexp :: Sexp.Sexp -> Maybe ExpQ stripLocation :: Sexp -> Sexp
metaSexp (Unquote x) = stripLocation = cata \case
Just [| toSexp $(varE (mkName (T.unpack x))) |] SL.Compose (a SL.:< e) ->
metaSexp (SL.ParenList xs) SL.Fix . SL.Compose $ SL.dummyPos SL.:< e
| (_:_) <- xs ^.. each . _UnquoteSplicing
= Just [| 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 (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 where
spans = xs spans = xs
& groupBy \cases & groupBy \cases
(UnquoteSplicing _) _ -> False (UnquoteSplicing _; Unquote _) _ -> False
_ (UnquoteSplicing _) -> False _ (UnquoteSplicing _; Unquote _) -> False
_ _ -> True _ _ -> True
& fmap \case & fmap \case
[UnquoteSplicing x] -> varE (mkName (T.unpack x)) [e@(Unquote _)] ->
x -> lift x case unquote e of
metaSexp _ = Nothing 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 class Lift1 f where
@@ -287,10 +356,10 @@ lift1 :: (Lift1 f, Lift a, Quote m) => f a -> m Exp
lift1 = liftLift lift lift1 = liftLift lift
instance Lift1 f => Lift (SL.Fix f) where 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 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 instance Lift a => Lift1 (SL.LocatedBy a) where
liftLift l (a SL.:< e) = [|(SL.:<) $(lift a) $(l e)|] 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.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
@@ -314,17 +383,37 @@ deriving instance Lift SL.Prefix
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)
makeSx :: Data a => SexpGrammar a -> QuasiQuoter makeSxs :: Data r => Code Q (List Sexp -> r) -> QuasiQuoter
makeSx g = QuasiQuoter makeSxs f = QuasiQuoter
{ quoteExp = \str -> do { quoteExp = \str -> do
pos <- getPos pos <- getPos
case parseSexpWithPos g pos (T.pack str) of case readSexpsWithPos pos (T.pack str) of
Left e -> fail e 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 , quotePat = undefined
, quoteType = undefined , quoteType = undefined
, quoteDec = undefined , quoteDec = undefined
} }
sxs = makeSxs (sexpIso @Sexp) id makeSx :: Data r => Code Q (Sexp -> r) -> QuasiQuoter
sx = makeSx (sexpIso @Sexp) 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 OverloadedLists #-}
{-# LANGUAGE ImpredicativeTypes #-} {-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE DerivingVia #-} {-# LANGUAGE DerivingVia #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
module Gyehoek.Wasm 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 where
@@ -45,18 +64,31 @@ import GHC.IsList (IsList(..))
import Data.Coerce (coerce) import Data.Coerce (coerce)
import qualified Control.Category import qualified Control.Category
import Data.Functor (void) 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 } newtype Module = MkModule { inner :: Vector Sexp }
deriving (Show, Generic) deriving (Show, Generic)
deriving newtype (Semigroup, Monoid) deriving newtype (Semigroup, Monoid)
newtype Expr = MkExpr { inner :: Vector Sexp } newtype Expr = MkExpr { inner :: Vector Instr }
deriving (Show, Generic) deriving (Show, Generic, Data, Eq)
deriving newtype (Semigroup, Monoid) 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 } newtype Idx = MkIdx { inner :: Natural }
deriving (Generic) deriving (Generic, Data)
deriving newtype (Show) deriving newtype (Show)
@@ -68,9 +100,100 @@ data GenModState = MkGenModState
{ mod :: Module { mod :: Module
, funcs :: Natural , funcs :: Natural
, types :: Natural , types :: Natural
, globals :: Natural
} }
deriving (Show, Generic) 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 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
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 ||]