|
|
|
@@ -24,6 +24,10 @@ import Effectful.State.Static.Local (runState, evalState, get)
|
|
|
|
|
import Data.Traversable
|
|
|
|
|
import Control.Applicative (Alternative(..))
|
|
|
|
|
import Gyehoek.Sexp.Print (htmlData)
|
|
|
|
|
import Control.DeepSeq (deepseq, ($!!))
|
|
|
|
|
import Gyehoek.Sexp.Print (htmlData, htmlDatum)
|
|
|
|
|
import Control.DeepSeq (deepseq, ($!!))
|
|
|
|
|
import Data.String (fromString)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- | inessential information maintained only to aide in debugging.
|
|
|
|
@@ -48,19 +52,23 @@ data Env = MkEnv
|
|
|
|
|
}
|
|
|
|
|
deriving (Show, Generic)
|
|
|
|
|
|
|
|
|
|
step :: Env -> VM -> VM
|
|
|
|
|
step :: Jalmot :> es => Env -> VM -> Eff es VM
|
|
|
|
|
step g vm = case vm ^. #code of
|
|
|
|
|
c:cs -> stepI g (vm & #code .~ cs) c
|
|
|
|
|
[] -> stepT g vm vm.tail
|
|
|
|
|
|
|
|
|
|
stepI :: Env -> VM -> Instr -> VM
|
|
|
|
|
vmerror :: (HasCallStack, Jalmot :> es) => Text -> Eff es a
|
|
|
|
|
vmerror = throwError . VMError
|
|
|
|
|
|
|
|
|
|
stepI e vm (Push v) = vm & #stack %~ (evalVal e vm v :)
|
|
|
|
|
stepI :: Jalmot :> es => Env -> VM -> Instr -> Eff es VM
|
|
|
|
|
|
|
|
|
|
stepI e vm (Prim r p) = case evalVal e vm <$> p of
|
|
|
|
|
stepI e vm (Push v) = traverseOf #stack push vm
|
|
|
|
|
where push xs = (:) <$> evalVal e vm v <*> pure xs
|
|
|
|
|
|
|
|
|
|
stepI e vm (Prim r p) = traverse (evalVal e vm) p >>= \case
|
|
|
|
|
PrimZeroP x -> case x of
|
|
|
|
|
ObjImm (ImmInt n) -> ret . ObjImm . ImmBool $ n == 0
|
|
|
|
|
_ -> error [i|bad arg to zero?: #{x}|]
|
|
|
|
|
_ -> vmerror [i|bad arg to zero?: #{x}|]
|
|
|
|
|
PrimAdd x y -> arith_binop (+) x y
|
|
|
|
|
PrimMul x y -> arith_binop (*) x y
|
|
|
|
|
PrimSub x y -> arith_binop (-) x y
|
|
|
|
@@ -68,68 +76,69 @@ stepI e vm (Prim r p) = case evalVal e vm <$> p of
|
|
|
|
|
PrimMakeClosure f env ->
|
|
|
|
|
case f of
|
|
|
|
|
ObjImm (ImmLabel l) -> ret . ObjHob $ HobClosure l env
|
|
|
|
|
_ -> error [i|expected label, got #{f}|]
|
|
|
|
|
_ -> vmerror [i|expected label, got #{f}|]
|
|
|
|
|
PrimEnvCode env ->
|
|
|
|
|
case env of
|
|
|
|
|
ObjHob (HobClosure l _) -> ret . ObjImm . ImmLabel $ l
|
|
|
|
|
_ -> error [i|expected closure, got #{env}|]
|
|
|
|
|
_ -> vmerror [i|expected closure, got #{env}|]
|
|
|
|
|
PrimEnvRef env n ->
|
|
|
|
|
case env of
|
|
|
|
|
ObjHob (HobClosure _ xs) -> ret $ xs ^?! ix n
|
|
|
|
|
_ -> error [i|expected closure, got #{env}|]
|
|
|
|
|
_ -> vmerror [i|expected closure, got #{env}|]
|
|
|
|
|
PrimCons x y -> ret $ ObjHob $ HobPair x y
|
|
|
|
|
PrimCar x -> case x of
|
|
|
|
|
ObjHob (HobPair car _) -> ret car
|
|
|
|
|
_ -> error [i|expected pair, got ${x}|]
|
|
|
|
|
_ -> vmerror [i|expected pair, got ${x}|]
|
|
|
|
|
PrimCdr x -> case x of
|
|
|
|
|
ObjHob (HobPair _ cdr) -> ret cdr
|
|
|
|
|
_ -> error [i|expected pair, got ${x}|]
|
|
|
|
|
x -> error [i|unimplemented prim: #{p}|]
|
|
|
|
|
_ -> vmerror [i|expected pair, got ${x}|]
|
|
|
|
|
x -> vmerror [i|unimplemented prim: #{p}|]
|
|
|
|
|
where
|
|
|
|
|
ret v = vm & #registers . at r ?~ v
|
|
|
|
|
ret v = pure $ vm & #registers . at r ?~ v
|
|
|
|
|
arith_binop op (ObjImm (ImmInt x)) (ObjImm (ImmInt y)) =
|
|
|
|
|
ret $ ObjImm (ImmInt (op x y))
|
|
|
|
|
arith_binop _ x y = error [i|bad arith: #{x}, #{y}|]
|
|
|
|
|
arith_binop _ x y = vmerror [i|bad arith: #{x}, #{y}|]
|
|
|
|
|
|
|
|
|
|
stepI e vm (Pop r) = case vm ^. #stack of
|
|
|
|
|
[] -> error "empty stack"
|
|
|
|
|
(x:xs) -> vm & #registers . at r ?~ x
|
|
|
|
|
[] -> vmerror "empty stack"
|
|
|
|
|
(x:xs) -> pure $ vm & #registers . at r ?~ x
|
|
|
|
|
& #stack .~ xs
|
|
|
|
|
|
|
|
|
|
stepI e vm ins = error [i|unimplemented instruction: #{ins}|]
|
|
|
|
|
stepI e vm ins = vmerror [i|unimplemented instruction: #{ins}|]
|
|
|
|
|
|
|
|
|
|
stepT :: Env -> VM -> Tail -> VM
|
|
|
|
|
stepT :: Jalmot :> es => Env -> VM -> Tail -> Eff es VM
|
|
|
|
|
|
|
|
|
|
stepT g vm (TailCall f xs) =
|
|
|
|
|
case evalToLabel g vm f of
|
|
|
|
|
"halt" -> vm & #result ?~ fmap (evalVal g vm) xs
|
|
|
|
|
l -> vm & #code .~ rt.start.code
|
|
|
|
|
stepT g vm (TailCall f xs) = do
|
|
|
|
|
xs' <- traverse (evalVal g vm) xs
|
|
|
|
|
evalToLabel g vm f >>= \case
|
|
|
|
|
"halt" -> pure $ vm & #result ?~ xs'
|
|
|
|
|
l -> do
|
|
|
|
|
rt <- case g ^. #labels . at l of
|
|
|
|
|
Nothing -> vmerror [i|undefined label: #{l}|]
|
|
|
|
|
Just x -> pure x
|
|
|
|
|
pure $ vm & #code .~ rt.start.code
|
|
|
|
|
& #tail .~ rt.start.tail
|
|
|
|
|
& #registers .~
|
|
|
|
|
fmap (evalVal g vm) (H.fromList $ rt.params `zip` xs)
|
|
|
|
|
& #registers .~ 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 (If c t f) = vm & #code .~ branch.code & #tail .~ branch.tail
|
|
|
|
|
where
|
|
|
|
|
branch = case evalVal g vm c of
|
|
|
|
|
stepT g vm (If c t f) = do
|
|
|
|
|
branch <- evalVal g vm c <&> \case
|
|
|
|
|
ObjImm (ImmBool False) -> f
|
|
|
|
|
_ -> t
|
|
|
|
|
pure $ vm & #code .~ branch.code & #tail .~ branch.tail
|
|
|
|
|
|
|
|
|
|
evalToLabel :: Jalmot :> es => Env -> VM -> Val -> Eff es Name
|
|
|
|
|
evalToLabel e vm v =
|
|
|
|
|
case evalVal e vm v of
|
|
|
|
|
ObjImm (ImmLabel x) -> x
|
|
|
|
|
x -> error [i|not a label: #{x}|]
|
|
|
|
|
evalVal e vm v >>= \case
|
|
|
|
|
ObjImm (ImmLabel x) -> pure x
|
|
|
|
|
x -> vmerror [i|not a label: #{x}|]
|
|
|
|
|
|
|
|
|
|
evalVal :: Env -> VM -> Val -> Obj
|
|
|
|
|
evalVal :: Jalmot :> es => Env -> VM -> Val -> Eff es Obj
|
|
|
|
|
evalVal e vm = \case
|
|
|
|
|
ValImm imm -> ObjImm imm
|
|
|
|
|
ValImm imm -> pure $ ObjImm imm
|
|
|
|
|
ValReg r -> case vm ^. #registers . at r of
|
|
|
|
|
Just x -> x
|
|
|
|
|
Nothing -> error [i|undefined register: #{r}|]
|
|
|
|
|
Just x -> pure x
|
|
|
|
|
Nothing -> vmerror [i|undefined register: #{r}|]
|
|
|
|
|
|
|
|
|
|
initialVM :: VM
|
|
|
|
|
initialVM = MkVM
|
|
|
|
@@ -154,33 +163,54 @@ loop f a = case f a of
|
|
|
|
|
Right a' -> loop f a'
|
|
|
|
|
Left b -> b
|
|
|
|
|
|
|
|
|
|
eval :: Program -> List Obj
|
|
|
|
|
eval p = initialVM & loop \vm -> case vm ^. #result of
|
|
|
|
|
Nothing -> Right $ step (initialEnv p) vm
|
|
|
|
|
Just rs -> Left rs
|
|
|
|
|
loopM :: Monad m => (a -> m (Either b a)) -> a -> m b
|
|
|
|
|
loopM f a = f a >>= \case
|
|
|
|
|
Right a' -> loopM f a'
|
|
|
|
|
Left b -> pure b
|
|
|
|
|
|
|
|
|
|
trace :: Program -> NonEmpty VM
|
|
|
|
|
trace p = initialVM & NE.unfoldr \vm ->
|
|
|
|
|
case vm.result of
|
|
|
|
|
Just _ -> (vm, Nothing)
|
|
|
|
|
Nothing -> (vm, Just $ step e vm)
|
|
|
|
|
where e = initialEnv p
|
|
|
|
|
eval :: Jalmot :> es => Program -> Eff es (List Obj)
|
|
|
|
|
eval p = initialVM & loopM \vm -> case vm ^. #result of
|
|
|
|
|
Nothing -> Right <$> step (initialEnv p) vm
|
|
|
|
|
Just rs -> pure . Left $ rs
|
|
|
|
|
|
|
|
|
|
data Trace
|
|
|
|
|
= Step { vm :: VM, next :: Trace }
|
|
|
|
|
| StepToSuccess { vm :: VM, result :: List Obj }
|
|
|
|
|
| StepToFailure { vm :: VM, err :: AJalmotCS }
|
|
|
|
|
deriving (Show)
|
|
|
|
|
|
|
|
|
|
trace :: Program -> Trace
|
|
|
|
|
trace p = go (initialEnv p) initialVM
|
|
|
|
|
where
|
|
|
|
|
go g vm =
|
|
|
|
|
case vm.result of
|
|
|
|
|
Just rs -> StepToSuccess vm rs
|
|
|
|
|
Nothing ->
|
|
|
|
|
case runPureEff . runJalmotCS $ step g vm of
|
|
|
|
|
Left err -> StepToFailure vm err
|
|
|
|
|
Right vm' -> Step vm (go g vm')
|
|
|
|
|
|
|
|
|
|
writeObj :: Obj -> Text
|
|
|
|
|
writeObj = runJalmotUnsafe . S.encodeWith' S.datumIso
|
|
|
|
|
|
|
|
|
|
traceEval :: IOE :> es => Program -> Eff es (List Obj)
|
|
|
|
|
traceEval :: IOE :> es => Program -> Eff es ()
|
|
|
|
|
traceEval p = do
|
|
|
|
|
let vms = trace p
|
|
|
|
|
liftIO . renderToFile "trace.html" . ppVMs p $ vms
|
|
|
|
|
pure $ NE.last vms ^?! #result . _Just
|
|
|
|
|
let t = trace p
|
|
|
|
|
liftIO . renderToFile "trace.html" . ppDoc p $ t
|
|
|
|
|
|
|
|
|
|
ppVMs :: Foldable f => Program -> f VM -> Html ()
|
|
|
|
|
ppVMs p vms =
|
|
|
|
|
ppDoc :: Program -> Trace -> Html ()
|
|
|
|
|
ppDoc p t =
|
|
|
|
|
html_ do
|
|
|
|
|
head_ do
|
|
|
|
|
title_ "stackify trace"
|
|
|
|
|
style_ """
|
|
|
|
|
pre {
|
|
|
|
|
max-width: 95vw;
|
|
|
|
|
overflow: scroll;
|
|
|
|
|
}
|
|
|
|
|
table {
|
|
|
|
|
max-width: 95vw;
|
|
|
|
|
}
|
|
|
|
|
tbody > tr:nth-of-type(even) {
|
|
|
|
|
background-color: rgb(237 238 242);
|
|
|
|
|
}
|
|
|
|
@@ -209,15 +239,36 @@ ppVMs p vms =
|
|
|
|
|
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
|
|
|
|
|
ppTrace t
|
|
|
|
|
|
|
|
|
|
ppTrace :: Trace -> Html ()
|
|
|
|
|
ppTrace trace =
|
|
|
|
|
table_ do
|
|
|
|
|
thead_ $ tr_ do
|
|
|
|
|
traverse_ (th_ [scope_ "col"])
|
|
|
|
|
["location","instruction","stack"]
|
|
|
|
|
tbody_ do
|
|
|
|
|
go trace
|
|
|
|
|
where
|
|
|
|
|
go :: Trace -> Html ()
|
|
|
|
|
go = \case
|
|
|
|
|
Step vm next -> ppVM vm >> go next
|
|
|
|
|
StepToSuccess vm rs -> do
|
|
|
|
|
ppVM vm
|
|
|
|
|
tr_ [colspan_ "3",class_ "trace-result"] do
|
|
|
|
|
sequence_ . intersperse " | " $ code_ . ppDatum <$> rs
|
|
|
|
|
StepToFailure vm err -> do
|
|
|
|
|
ppVM vm
|
|
|
|
|
tr_ [class_ "trace-failure"] do
|
|
|
|
|
td_ [colspan_ "3"] do
|
|
|
|
|
details_ do
|
|
|
|
|
summary_ "error"
|
|
|
|
|
pre_ do
|
|
|
|
|
samp_ do
|
|
|
|
|
fromString $ displayException err
|
|
|
|
|
|
|
|
|
|
ppVM :: VM -> Html ()
|
|
|
|
|
ppVM vm =
|
|
|
|
|
ppVM vm = do
|
|
|
|
|
tr_ do
|
|
|
|
|
td_ do
|
|
|
|
|
details_ do
|
|
|
|
@@ -227,50 +278,12 @@ ppVM vm =
|
|
|
|
|
pre_ do
|
|
|
|
|
code_ . toHtml . pShowNoColor $ vm
|
|
|
|
|
td_ do
|
|
|
|
|
code_ . toHtml $ curi
|
|
|
|
|
code_ curi
|
|
|
|
|
td_ do
|
|
|
|
|
let xs = code_ . toHtml . ppSexp <$> (vm ^. #stack)
|
|
|
|
|
let xs = code_ . ppDatum <$> (vm ^. #stack)
|
|
|
|
|
sequence_ $ intersperse " | " xs
|
|
|
|
|
where
|
|
|
|
|
curi = vm ^?! failing (#code . _head . to ppSexp) (#tail . to ppSexp)
|
|
|
|
|
curi = vm ^?! failing (#code . _head . to ppDatum) (#tail . to ppDatum)
|
|
|
|
|
|
|
|
|
|
ppSexp :: S.DatumIso a => a -> Text
|
|
|
|
|
ppSexp = runJalmotUnsafe . S.encodeWith' S.datumIso
|
|
|
|
|
|
|
|
|
|
blah = [stkP|
|
|
|
|
|
(define ($r7 %x8)
|
|
|
|
|
(pop! %lambda-tail2)
|
|
|
|
|
(tail-call %lambda-tail2 #f))
|
|
|
|
|
|
|
|
|
|
(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 %r10))
|
|
|
|
|
|
|
|
|
|
(define ($r5 %x6)
|
|
|
|
|
(pop! %lambda-tail4)
|
|
|
|
|
(tail-call %lambda-tail4 %x6))
|
|
|
|
|
|
|
|
|
|
(define ($cc-ish11-code17 %_ %cc-ish11 %x12)
|
|
|
|
|
(prim %cc9 (env-ref %cc-ish11 0))
|
|
|
|
|
(tail-call %cc9 %x12))
|
|
|
|
|
|
|
|
|
|
(define ($start %start-ktail0)
|
|
|
|
|
(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 %code16 $cc9 %lambda-body1 %cc-ish11))
|
|
|
|
|
|]
|
|
|
|
|
ppDatum :: S.DatumIso a => a -> Html ()
|
|
|
|
|
ppDatum = htmlDatum . runJalmotUnsafe . S.toDatum S.datumIso
|
|
|
|
|