Skip to content

Instantly share code, notes, and snippets.

@astynax
Last active October 13, 2019 13:44
Show Gist options
  • Save astynax/c1c9f47b940532e6a49808d866eef1b4 to your computer and use it in GitHub Desktop.
Save astynax/c1c9f47b940532e6a49808d866eef1b4 to your computer and use it in GitHub Desktop.
Simple stack based machine with two stacks.

This machine uses two stacks: for data and for commands.

Each command can extend any of stacks.

Also the language provides a fold function that uses command stack manipulations for recursion.

{-# LANGUAGE LambdaCase #-}
module Main where
main :: IO ()
main = do
-- nil 2 +
try [nil, n 2, add]
-- 1 2 ' * call
try [n 1, n 2, q, add, call]
-- nil 2 3 4 ' * fold
try [nil, n 2, n 3, n 4, q, mul, fold]
-- nil 4 nil 2 3 ' + fold ' * fold
try [nil, n 4, nil, n 2, n 3, q, add, fold, q, mul, fold]
--
print $ populate $ State []
$ nil : map n [1..1000000] ++ [q, add, fold]
where
add = Op $ Action "+" $ binaryOp (+)
mul = Op $ Action "*" $ binaryOp (*)
try s = do
print s
putStrLn "---"
print $ populate $ State [] s
putStrLn ""
data StackItem
= Number Double
| Op Op
type Stack = [StackItem]
data State = State
{ dataStack :: Stack
, opStack :: Stack
} deriving (Show)
type Action = State -> State
data Op
= Nil
| Quote
| Halt String
| Action String Action
type Result = Either (String, State) Stack
instance Show StackItem where
show (Number x) = show x
show (Op Nil) = "Nil"
show (Op Quote) = "'"
show (Op (Halt s)) = "!" ++ show s
show (Op (Action s _)) = s
populate :: State -> Result
populate (State ds []) = Right ds
populate (State ds (x@(Number _) : os)) = populate (State (x : ds) os)
populate (State ds (Op (Halt m) : os)) = Left (m, State ds os)
populate (State ds (Op Nil : os)) = populate (State (Op Nil : ds) os)
populate (State ds (Op (Action _ f) : os)) = populate $ f $ State ds os
populate s@(State ds (Op Quote : os)) =
case os of
(Op x : xs) -> populate $ State (Op x : ds) xs
_ -> Left ("Bad quotation!", s)
haltWith :: String -> Action
haltWith msg (State ds os) = State ds $ Op (Halt msg) : os
dsOnly :: (Stack -> Stack) -> Action
dsOnly f (State ds os) = State (f ds) os
osOnly :: (Stack -> Stack) -> Action
osOnly f (State ds os) = State ds (f os)
popDS :: (StackItem -> Action) -> Action
popDS _ s@(State [] _) = haltWith "DS underflow!" s
popDS f (State (x:xs) os) = f x (State xs os)
pushDS :: StackItem -> Action
pushDS x (State ds os) = State (x:ds) os
pushOS :: StackItem -> Action
pushOS x (State ds os) = State ds (x:os)
pushEachOS :: Stack -> Action
pushEachOS = flip (foldr pushOS)
dupDS :: Action
dupDS = popDS $ \x -> pushDS x . pushDS x
dropDS :: Action
dropDS = popDS $ const id
peekDS :: (StackItem -> Action) -> Action
peekDS f = popDS f . dupDS
popNumber :: (Double -> Action) -> Action
popNumber f = peekDS $ \case
Number x -> f x . dropDS
_ -> haltWith "Non-number!"
popOp :: (Op -> Action) -> Action
popOp f = peekDS $ \case
Op x -> f x . dropDS
_ -> haltWith "Non-op!"
unaryOp :: (Double -> Double) -> Action
unaryOp f = popNumber $ pushDS . Number . f
binaryOp :: (Double -> Double -> Double) -> Action
binaryOp f = popNumber $ \x -> popNumber $ \y -> pushDS (Number $ f x y)
callOp :: Action
callOp = popOp $ pushOS . Op
foldOp :: Action
foldOp = popOp $ \op -> popNumber $ \x -> peekDS $ \case
Number y ->
pushEachOS [n y, n x, Op op, q, Op op, fold] . dropDS
Op Nil ->
pushDS (Number x) . dropDS
_ -> haltWith "Bad operand for folding!"
-- shortcuts
n = Number
nil = Op Nil
q = Op Quote
call = Op $ Action "call" callOp
fold = Op $ Action "fold" foldOp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment