Compare commits
3
Commits
6ffdf3f7e0
...
idk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80164acb96 | ||
|
|
1c7322614c | ||
|
|
81a136fcf2 |
@@ -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
|
||||
+2
-1
@@ -66,7 +66,7 @@ library
|
||||
, base ^>=4.21.2.0
|
||||
, binary
|
||||
, containers
|
||||
, cradle
|
||||
, typed-process
|
||||
, effectful
|
||||
, effectful-core
|
||||
, effectful-plugin
|
||||
@@ -89,6 +89,7 @@ library
|
||||
, text-short
|
||||
, unordered-containers
|
||||
, vector
|
||||
, bytestring
|
||||
|
||||
hs-source-dirs: src
|
||||
default-language: GHC2024
|
||||
|
||||
@@ -183,10 +183,10 @@ lower' g e@(ExpApply f xs ktail) = do
|
||||
(ref.cast (ref $closure))
|
||||
(struct.get $closure $code)
|
||||
(return_call_ref $cont-type)
|
||||
;; (@gyehoek todo
|
||||
;; (f' ##{f'})
|
||||
;; (ktail #{l}))
|
||||
|]
|
||||
(@gyehoek todo
|
||||
(f' ##{f'})
|
||||
(ktail #{l}))
|
||||
|]
|
||||
|
||||
lower' g e@(ExpContinue k xs) = do
|
||||
let nargs = length xs
|
||||
|
||||
+53
-24
@@ -3,6 +3,7 @@
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE FunctionalDependencies #-}
|
||||
module Gyehoek.CPS.Syntax
|
||||
( Val(..)
|
||||
, Kappa(..)
|
||||
@@ -19,6 +20,11 @@ module Gyehoek.CPS.Syntax
|
||||
, _ExpPrim
|
||||
, _ExpLetRec
|
||||
, _ExpApply
|
||||
, binders
|
||||
, body
|
||||
, op
|
||||
, args
|
||||
, cont
|
||||
, cps
|
||||
, pattern AbsLambda'
|
||||
, pattern AbsKappa'
|
||||
@@ -48,6 +54,7 @@ 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
|
||||
|
||||
@@ -56,10 +63,10 @@ data Val
|
||||
| ValLit Lit
|
||||
deriving (Show, Generic, Data, Eq)
|
||||
|
||||
data Kappa = MkKappa (List Name) Exp
|
||||
data Kappa = MkKappa { binders :: List Name, body :: Exp }
|
||||
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, Data, Eq)
|
||||
|
||||
data Abs
|
||||
@@ -72,7 +79,7 @@ pattern AbsLambda' xs e ktail = AbsLambda (MkLambda xs e ktail)
|
||||
|
||||
data Exp
|
||||
= ExpPrim (Prim Val) Kappa
|
||||
| ExpLetRec (NonEmpty (Name, Abs)) Exp
|
||||
| ExpLetRec { binders :: NonEmpty (Name, Abs), body :: Exp }
|
||||
| ExpContinue Name (List Val)
|
||||
| ExpIf Val Exp Exp
|
||||
| ExpApply
|
||||
@@ -97,7 +104,24 @@ data Program = MkProgram
|
||||
deriving (Show, Generic, Data)
|
||||
|
||||
makePrisms ''Kappa
|
||||
-- makeLenses ''Kappa
|
||||
makePrisms ''Exp
|
||||
-- makeLenses ''Exp
|
||||
-- makeFieldsNoPrefix ''Exp
|
||||
-- makeFieldsNoPrefix ''Kappa
|
||||
-- makeLensesWith abbreviatedFields ''Exp
|
||||
-- makeLensesFor [("binders", "_binders"), ("body", "_body")] ''Exp
|
||||
makeFieldsId ''Exp
|
||||
makeFieldsId ''Kappa
|
||||
makeFieldsId ''Lambda
|
||||
|
||||
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
|
||||
@@ -230,24 +254,29 @@ free = go where
|
||||
|
||||
-- | Free variables given in the order of their appearance.
|
||||
free' :: Exp -> List Name
|
||||
free' = go HS.empty where
|
||||
gokap bound (MkKappa xs m) = go (bound & insertFrom xs) m
|
||||
golam bound (MkLambda xs k m) = go (bound & insertFrom (k:xs)) m
|
||||
goabs bound = \case
|
||||
AbsKappa kap -> gokap bound kap
|
||||
AbsLambda lam -> golam bound lam
|
||||
go :: HashSet Name -> Exp -> List Name
|
||||
go bound = \case
|
||||
ExpPrim p k ->
|
||||
p & toListOf (folded . #ValVar . filtered (`notElem` bound))
|
||||
& (<> gokap bound k)
|
||||
ExpLetRec bs m ->
|
||||
foldMapOf (each . _2) (goabs bound') bs <> go 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))
|
||||
<> go bound t <> go bound f
|
||||
ExpApply f xs k ->
|
||||
(f:xs) ^.. (each . #ValVar . filtered (`notElem` bound))
|
||||
<> (k ^.. filtered (`notElem` bound))
|
||||
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}) = _
|
||||
|
||||
+37
-2
@@ -16,11 +16,18 @@ import qualified Gyehoek.Sexp as Sexp
|
||||
import qualified Gyehoek.Scheme.Syntax as Scm
|
||||
import qualified Data.Text.Encoding as T
|
||||
import System.IO (Handle)
|
||||
import System.IO qualified as IO
|
||||
import Gyehoek.CPS.Convert
|
||||
import Gyehoek.CPS.Lower
|
||||
import Gyehoek.CPS.Syntax qualified as Cps
|
||||
import Control.Monad
|
||||
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 ()
|
||||
@@ -59,6 +66,31 @@ readScm f =
|
||||
Sexp.parseSexps @Scm.CommandOrDef (fileName f) <$> hGetContents h
|
||||
>>= either error (pure . Scm.MkProgram)
|
||||
|
||||
inspectWasm :: IOE :> es => Text -> Eff es ()
|
||||
inspectWasm wat = do
|
||||
pager_cmd <- liftIO $ getEnvDefault "PAGER" "less"
|
||||
let wasmtools_cfg
|
||||
= proc "wasm-tools" ["print", "-pf", "--print-operand-stack"
|
||||
,"--color", "always", "-"]
|
||||
-- & setStdin (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
|
||||
:: (GenSym :> es, FileSystem :> es, IOE :> es)
|
||||
=> Options -> Eff es ()
|
||||
@@ -70,8 +102,11 @@ driver opts = do
|
||||
when opts.dumpCPS do
|
||||
hPutStrLn FS.stdout $ Sexp.encodePretty cps ^?! _Right
|
||||
wat <- lowerProgram cps
|
||||
withFile opts.output FS.WriteMode \h ->
|
||||
hPutStrLn h wat
|
||||
if not opts.inspectWasm then
|
||||
withFile opts.output FS.WriteMode \h ->
|
||||
hPutStrLn h wat
|
||||
else
|
||||
inspectWasm wat
|
||||
|
||||
parse_e2e :: FilePath -> IO Scm.Program
|
||||
parse_e2e = runEff . runFileSystem . readScm
|
||||
|
||||
@@ -19,6 +19,7 @@ data Options = MkOptions
|
||||
-- , dumpQBE :: Maybe FilePath
|
||||
dumpCPS :: Bool
|
||||
, dumpParsed :: Bool
|
||||
, inspectWasm :: Bool
|
||||
, output :: FilePath
|
||||
, sourceFile :: FilePath
|
||||
}
|
||||
@@ -49,10 +50,12 @@ parseOutput = strOption
|
||||
|
||||
parseDumpCPS = switch (long "dump-cps")
|
||||
parseDumpParsed = switch (long "dump-parsed")
|
||||
parseInspectWasm = switch $ long "inspect-wasm" <> short 'p'
|
||||
|
||||
parser :: Parser Options
|
||||
parser = MkOptions
|
||||
<$> parseDumpCPS
|
||||
<*> parseDumpParsed
|
||||
<*> parseInspectWasm
|
||||
<*> parseOutput
|
||||
<*> argument str (metavar "FILE")
|
||||
|
||||
@@ -52,7 +52,7 @@ import Language.Haskell.TH.Quote (QuasiQuoter)
|
||||
|
||||
|
||||
newtype Name = MkName { inner :: Text }
|
||||
deriving newtype (Show, Eq, IsString, Gen, Hashable)
|
||||
deriving newtype (Show, Eq, Ord, IsString, Gen, Hashable)
|
||||
deriving stock (Generic, Data)
|
||||
|
||||
getName :: Name -> Text
|
||||
|
||||
Reference in New Issue
Block a user