This commit is contained in:
2026-08-27 01:02:16 -06:00
parent 009a154a6e
commit 196dd0d1b3
9 changed files with 209 additions and 92 deletions
+1 -2
View File
@@ -1,5 +1,4 @@
(call/cc
(λ (k)
(begin ((λ (x) (k x))
#t)
(begin ((λ () (k #t)))
#f)))
+2
View File
@@ -116,6 +116,8 @@ library
, typed-process
, unordered-containers
, vector
, lucid
, prettyprinter-lucid
hs-source-dirs: src
default-language: GHC2024
+2 -1
View File
@@ -4,6 +4,7 @@ module Gyehoek.CPS.Close
) where
import Gyehoek.CPS.Syntax
import Data.List (nub)
import Gyehoek.GenSym
import Gyehoek.Prelude
@@ -15,7 +16,7 @@ close = transformM \case
-- it would probably be most sane to generate a symbol for `env`,
-- but we're reusing the lambda binding so we don't have to
-- explicitly substitute recursive calls.
let frees = freeWithBound' [f] lam
let frees = nub $ freeWithBound' [f] lam
let m' = ifoldr
(\n x q -> [cps|(prim (env-ref #{f} #{n})
(κ (#{x}) #{q}))|])
+30 -12
View File
@@ -11,11 +11,12 @@ import Data.Maybe (fromMaybe)
import Text.Show.Functions ()
import qualified Data.HashMap.Strict as H
import Gyehoek.Prelude
import Debug.Pretty.Simple
data Env = MkEnv
{ vars :: HashMap Name Obj
, labels :: HashMap Name (Env, Abs)
, labels :: HashMap Name Abs
}
deriving (Show, Generic)
@@ -23,27 +24,46 @@ eval :: Env -> Exp -> List Obj
eval g (Halt xs) = evalVal g <$> xs
eval g (ExpContinue ((^?! #ValVar) -> k) xs) =
case g ^. #labels . at k of
eval g (ExpContinue k xs) =
case g ^. #labels . at k' of
Just (h, AbsKappa' bs m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
where
h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
_ -> error [i|not a kappa: #{k}|]
where
k' = case evalVal g k of
ObjImm (ImmLabel x) -> x
x -> error [i|expected label, got #{x}|]
eval g (ExpApply ((^?! #ValVar) -> f) xs ktail) =
case g ^?! #labels . at f of
eval g (ExpApply f xs ktail) =
case g ^?! #labels . at f' of
Just (h,AbsLambda' bs kb m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
& #labels . at kb .~ (g ^. #labels . at ktail)
Nothing -> error [i|undefined label: #{f}|]
where
f' = case evalVal g f of
ObjImm (ImmLabel x) -> x
x -> error [i|expected label, got #{x}|]
eval g (ExpLetRec [(b, ab)] e) = eval g' e
where g' = g & #labels . at b ?~ (g,ab)
where g' = g & #labels . at b ?~ ab
eval g (ExpPrim p (MkKappa bs e)) = case evalVal g <$> p of
PrimAdd x y -> arithBinop (+) x y
PrimMul x y -> arithBinop (*) x y
PrimSub x y -> arithBinop (-) x y
PrimDiv x y -> arithBinop div x y
PrimMakeClosure x env -> ret [ObjHob (HobClosure lbl env) ]
where
lbl = case x of
ObjImm (ImmLabel l) -> l
_ -> error [i|expected label, got #{x}|]
PrimEnvCode x -> ret . (:[]) . ObjImm . ImmLabel $ code
where
code = case x of
ObjHob (HobClosure lbl _) -> lbl
_ -> error [i|expected closure, got #{x}|]
_ -> error [i|unhandled prim: #{p}|]
where
ret rs = eval
@@ -70,10 +90,8 @@ emptyEnv = MkEnv
-- special case of `eval` responsible for `halt` only covers terms
-- of the form `(continue $halt xs …)`; other terms such as
-- `($some-fn xs $halt)` just see an undefined label `$halt`.
, labels = H.singleton "halt"
( emptyEnv
, AbsKappa' ["h0"] $ Halt [ValVar "h0"]
)
, labels = H.singleton "halt" $
AbsKappa' ["h0"] $ Halt [ValVar "h0"]
}
evalExp :: Exp -> List Obj
@@ -82,5 +100,5 @@ evalExp = eval emptyEnv
evalProgram :: Program -> List Obj
evalProgram (MkProgram lam) = eval emptyEnv [cps|
(letrec ((start #{lam}))
(apply start halt))
(start halt))
|]
+5 -1
View File
@@ -26,7 +26,7 @@ import System.Environment.Blank (getEnvDefault)
import qualified Data.Text.IO as TIO
import qualified Data.ByteString.Lazy as BS
import Gyehoek.CPS.Stackify (stackifyProgram)
import Gyehoek.Stack.VM (eval, writeObj, Obj)
import Gyehoek.Stack.VM (eval, writeObj, Obj, traceEval)
import qualified Data.Text as T
import Gyehoek.Stack.Syntax qualified as Stk
import Gyehoek.CPS.Close (closeProgram)
@@ -139,6 +139,10 @@ driver opts = do
-- (lowerProgram cps)
-- inspectWasm
-- (\wat -> withFile opts.output FS.WriteMode \h -> hPutStrLn h wat)
when opts.traceStackified $ do
stackifyProgram closedCps
>>= traceEval
>>= (hPutStrLn FS.stdout . T.unwords . fmap writeObj)
parse_e2e :: FilePath -> IO Scm.Program
parse_e2e = runJalmotIO . runFileSystem . readScm
+2
View File
@@ -29,6 +29,7 @@ data Options = MkOptions
, dumpCPS :: Bool
, dumpParsed :: Bool
, dumpStackified :: Bool
, traceStackified :: Bool
, runtime :: Maybe Runtime
, inspectWasm :: Bool
, output :: FilePath
@@ -60,6 +61,7 @@ parser = do
dumpCPS <- switch (long "dump-cps")
dumpStackified <- switch (long "dump-stackified")
dumpParsed <- switch (long "dump-parsed")
traceStackified <- switch (long "trace-stackified")
inspectWasm <- switch $ long "inspect-wasm" <> short 'p'
runtime <- option runtimeReader . fold $
[ long "runtime"
+48 -5
View File
@@ -4,10 +4,12 @@ module Gyehoek.Sexp.Print
, printDatum'
, printData
, printData'
, htmlDatum
, htmlData
) where
import Gyehoek.Sexp.Syntax
import Data.Text.Prettyprint.Doc
import Prettyprinter
import Data.Functor.Foldable
import qualified Control.Comonad.Trans.Cofree as F
import Prettyprinter.Util
@@ -19,6 +21,9 @@ import Prettyprinter.Render.Terminal (Color(..), color, italicized, AnsiStyle, b
import Prettyprinter.Render.Text (renderStrict)
import qualified Data.Scientific as Sci
import Data.List (intersperse)
import Lucid
import Prettyprinter.Render.Util.SimpleDocTree (treeForm)
import Prettyprinter.Lucid (renderHtml)
printDatum' :: Datum -> Text
@@ -31,6 +36,31 @@ printDatum' =
{ layoutPageWidth = AvailablePerLine 80 1.0
}
htmlDatum :: Datum -> Html ()
htmlDatum =
prettyDatum 0
>>> layoutPretty opts
>>> treeForm
>>> fmap highlightHtml
>>> renderHtml
where
opts = LayoutOptions
{ layoutPageWidth = AvailablePerLine 80 1.0
}
htmlData :: Foldable f => f Datum -> Html ()
htmlData =
foldr f mempty
>>> layoutPretty opts
>>> treeForm
>>> fmap highlightHtml
>>> renderHtml
where
f x y = prettyDatum 0 x <> hardline <> hardline <> y
opts = LayoutOptions
{ layoutPageWidth = AvailablePerLine 80 1.0
}
printDatum :: Datum -> Text
printDatum = printDatumW 80
@@ -44,7 +74,7 @@ printDatumW :: Int -> Datum -> Text
printDatumW w =
prettyDatum 0
>>> layoutSmart opts
>>> reAnnotateS highlight
>>> reAnnotateS highlightAnsi
>>> ANSI.renderStrict
where
opts = LayoutOptions
@@ -91,10 +121,10 @@ prettySimple depth = \case
putDoc :: Doc Syn -> IO ()
putDoc = ANSI.renderIO stdout
. reAnnotateS highlight . layoutSmart defaultLayoutOptions . (<>"\n")
. reAnnotateS highlightAnsi . layoutSmart defaultLayoutOptions . (<>"\n")
highlight :: Syn -> AnsiStyle
highlight = \case
highlightAnsi :: Syn -> AnsiStyle
highlightAnsi = \case
(SynBuiltin; SynMacro) -> color Magenta <> italicized <> bold
SynProcedure -> color Blue
SynConstant -> color Yellow
@@ -102,3 +132,16 @@ highlight = \case
_ -> mempty
where
rainbow = cycle [Red,Yellow,Green,Blue,Magenta,Cyan]
highlightHtml :: Syn -> Html () -> Html ()
highlightHtml syn = span_ [class_ synClass]
where
synClass = case syn of
SynBuiltin -> "syn-builtin"
SynMacro -> "syn-macro"
SynConstant -> "syn-constant"
SynString -> "syn-string"
SynProcedure -> "syn-procedure"
SynVariable -> "syn-variable"
SynNone -> "syn-none"
SynParen n -> [i|syn-paren-#{mod n 5}|]
-2
View File
@@ -60,7 +60,6 @@ data Block = MkBlock
data Tail
= TailCall Val (List Val)
| PushCall Val Val (List Val)
| If Val Block Block
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
@@ -105,7 +104,6 @@ instance S.DataIso Block where
instance S.DatumIso Tail where
datumIso = S.match
$ S.With (S.headTagged1' "tail-call" S.datumIso S.datumIso >>>)
$ S.With (S.headTagged2' "push-call" S.datumIso S.datumIso S.datumIso >>>)
$ S.With (if_ >>>)
$ S.End
where
+119 -69
View File
@@ -1,4 +1,4 @@
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ViewPatterns, MultilineStrings #-}
module Gyehoek.Stack.VM
( VM(..)
, Env(..)
@@ -6,16 +6,32 @@ module Gyehoek.Stack.VM
, trace
, module Gyehoek.Stack.Syntax
, writeObj
, traceEval
) where
import Gyehoek.Stack.Syntax
import Control.Lens
import qualified Data.HashMap.Strict as H
import Data.List (unfoldr)
import Data.List (unfoldr, intersperse)
import Gyehoek.Prelude
import qualified Data.List.NonEmpty as NE
import Lucid
import Data.Foldable (traverse_)
import qualified Gyehoek.Sexp as S
import Gyehoek.Jalmot
import Text.Pretty.Simple (pStringNoColor, pShowNoColor)
import Effectful.State.Static.Local (runState, evalState, get)
import Data.Traversable
import Control.Applicative (Alternative(..))
import Gyehoek.Sexp.Print (htmlData)
-- | inessential information maintained only to aide in debugging.
data DebugVM = MkDebugVM
{ currentRoutine :: Name
}
deriving (Show, Generic)
data VM = MkVM
{ stack :: List Obj
, code :: List Instr
@@ -23,6 +39,7 @@ data VM = MkVM
, registers :: HashMap Name Obj
, stdout :: Text
, result :: Maybe (List Obj)
, debug :: DebugVM
}
deriving (Show, Generic)
@@ -83,14 +100,12 @@ stepT g vm (TailCall f xs) =
& #tail .~ rt.start.tail
& #registers .~
fmap (evalVal g vm) (H.fromList $ rt.params `zip` xs)
& #debug . #currentRoutine .~ rt.label
where
rt = case g ^. #labels . at l of
Nothing -> error [i|undefined label: #{l}|]
Just x -> x
stepT g vm (PushCall k f xs) =
_
stepT g vm (If c t f) = vm & #code .~ branch.code & #tail .~ branch.tail
where
branch = case evalVal g vm c of
@@ -117,6 +132,9 @@ initialVM = MkVM
, registers = mempty
, stdout = ""
, result = Nothing
, debug = MkDebugVM
{ currentRoutine = "<nowhere>"
}
}
initialEnv :: Program -> Env
@@ -150,76 +168,108 @@ writeObj (ObjImm im) = case im of
writeObj (ObjHob h) = case h of
HobClosure code env -> "#<procedure>"
traceEval :: IOE :> es => Program -> Eff es (List Obj)
traceEval p = do
let vms = trace p
liftIO . renderToFile "trace.html" . ppVMs p $ vms
pure $ NE.last vms ^?! #result . _Just
ppVMs :: Foldable f => Program -> f VM -> Html ()
ppVMs p vms =
html_ do
head_ do
title_ "stackify trace"
style_ """
tbody > tr:nth-of-type(even) {
background-color: rgb(237 238 242);
}
.loc {
font-size: 0.8rem;
}
.syn-builtin, .syn-macro {
color: purple;
font-style: italic;
font-weight: bold;
}
.syn-constant {
color: olive;
}
.syn-procedure {
color: teal;
}
.syn-paren-0 { color: maroon; }
.syn-paren-1 { color: olive; }
.syn-paren-2 { color: green; }
.syn-paren-3 { color: navy; }
.syn-paren-4 { color: purple; }
"""
body_ do
details_ do
summary_ "stack code"
pre_ $ code_ do
htmlData . runJalmotUnsafe . S.toData S.dataIso $ p
table_ do
thead_ $ tr_ do
traverse (th_ [scope_ "col"])
["location","instruction","stack"]
tbody_ do
traverse_ ppVM vms
ppVM :: VM -> Html ()
ppVM vm =
tr_ do
td_ do
details_ do
summary_ do
var_ [class_ "loc"] . toHtml $ vm ^. #debug . #currentRoutine
. re (_Unwrapped' . prefixed "$")
pre_ do
code_ . toHtml . pShowNoColor $ vm
td_ do
code_ . toHtml $ curi
td_ do
let xs = code_ . toHtml . ppSexp <$> (vm ^. #stack)
sequence_ $ intersperse " | " xs
where
curi = vm ^?! failing (#code . _head . to ppSexp) (#tail . to ppSexp)
ppSexp :: S.DatumIso a => a -> Text
ppSexp = runJalmotUnsafe . S.encodeWith' S.datumIso
blah = [stkP|
(define ($r3 %x4)
(pop! %f)
(pop! %iter)
(pop! %lambda-tail1)
(pop! %n)
(prim %r5 (- %n 1))
(prim %code22 (env-code %iter))
(push! %lambda-tail1)
(tail-call %code22 $r6 %iter %r5 %f))
(define ($r7 %x8)
(pop! %lambda-tail2)
(tail-call %lambda-tail2 #f))
(define ($cc18 %r19)
(define ($lambda-body3-code15 %lambda-tail4 %lambda-body3 %x)
(prim %k (env-ref %lambda-body3 0))
(prim %k (env-ref %lambda-body3 1))
(prim %code13 (env-code %k))
(push! %lambda-tail4)
(tail-call %code13 $r5 %k %x))
(define ($lambda-body1-code18 %lambda-tail2 %lambda-body1 %k)
(prim %lambda-body3 (make-closure $lambda-body3-code15 %k %k))
(prim %code14 (env-code %lambda-body3))
(push! %lambda-tail2)
(tail-call %code14 $r7 %lambda-body3 #t))
(define ($cc9 %r10)
(pop! %start-ktail0)
(tail-call %start-ktail0 %r19))
(tail-call %start-ktail0 %r10))
(define ($lambda-body10-code26 %lambda-tail11 %lambda-body10 %n)
(prim %k (env-ref %lambda-body10 0))
(prim %k (env-ref %lambda-body10 1))
(prim %r12 (- %n 5))
(prim %r13 (zero? %r12))
(if %r13
(then
(prim %code24 (env-code %k))
(push! %lambda-tail11)
(tail-call %code24 $r14 %k #t))
(else
(tail-call %lambda-tail11 #f))))
(define ($r5 %x6)
(pop! %lambda-tail4)
(tail-call %lambda-tail4 %x6))
(define ($r6 %x7)
(pop! %lambda-tail1)
(tail-call %lambda-tail1 %x7))
(define ($r14 %x15)
(pop! %lambda-tail11)
(tail-call %lambda-tail11 %x15))
(define ($cc-ish20-code28 %_ %cc-ish20 %x21)
(prim %cc18 (env-ref %cc-ish20 0))
(tail-call %cc18 %x21))
(define ($r16 %x17)
(pop! %lambda-tail9)
(tail-call %lambda-tail9 %x17))
(define ($iter-code30 %lambda-tail1 %iter %n %f)
(prim %r2 (zero? %n))
(if %r2
(then
(tail-call %lambda-tail1 #f))
(else
(prim %code23 (env-code %f))
(push! %n)
(push! %lambda-tail1)
(push! %iter)
(push! %f)
(tail-call %code23 $r3 %f %n))))
(define ($lambda-body8-code29 %lambda-tail9 %lambda-body8 %k)
(prim %iter (env-ref %lambda-body8 0))
(prim %iter (env-ref %lambda-body8 1))
(prim %lambda-body10 (make-closure $lambda-body10-code26 %k %k))
(prim %code25 (env-code %iter))
(push! %lambda-tail9)
(tail-call %code25 $r16 %iter 5 %lambda-body10))
(define ($cc-ish11-code17 %_ %cc-ish11 %x12)
(prim %cc9 (env-ref %cc-ish11 0))
(tail-call %cc9 %x12))
(define ($start %start-ktail0)
(prim %iter (make-closure $iter-code30))
(prim %lambda-body8 (make-closure $lambda-body8-code29 %iter %iter))
(prim %cc-ish20 (make-closure $cc-ish20-code28 $cc18))
(prim %code27 (env-code %lambda-body8))
(prim %lambda-body1 (make-closure $lambda-body1-code18))
(prim %cc-ish11 (make-closure $cc-ish11-code17 $cc9))
(prim %code16 (env-code %lambda-body1))
(push! %start-ktail0)
(tail-call %code27 $cc18 %lambda-body8 %cc-ish20))
(tail-call %code16 $cc9 %lambda-body1 %cc-ish11))
|]