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 |