Unefunge-98: 14 13 22 chars
&:7p&:' \/*-.@
Unefunge is the 1-dimensional instance of Funge-98: http://quadium.net/funge/spec98.html
Explanation (Command <- Explaination [Stack]):
& <- Get integer input of value A and store on stack.
[A]
: <- Duplicate top of stack.
[A A]
7 <- Push 7 on stack. Used for the `p` command.
[A A 7]
p <- Pop top two values (7 then A). Place the character whose ASCII value
is A at position 7 in the code (where the space is).
[A]
& <- Get integer input of value B and store on stack.
[A B]
: <- Duplicate top of stack.
[A B B]
' <- Jump over next character and grap the ASCII value of the jumped character.
[A B B A]
<- Because of the `p` command, this is actually the character whose ASCII
value is A at this point in the code. This was jumped over by the
previous instruction.
\ <- Swap top two values of stack.
[A B A B]
/ <- Pop top two values (B then A). Push (A/B) (integer division) onto stack.
[A B (A/B)]
* <- Pop top two values ((A/B) then B). Push (B*(A/B)) onto stack.
[A (B*(A/B))]
- <- Pop top two values ((B*(A/B)) then A). Push (A-(B*(A/B))) onto stack.
[(A-(B*(A/B)))]
. <- Pop top value and print it as an integer.
[]
@ <- Exit program.
Code tested is this incomplete (but complete enough) Unefunge-98 interpreter I wrote to test the code:
module Unefunge where
import Prelude hiding (subtract)
import qualified Data.Map as Map
import Control.Exception (handle)
import Control.Monad
import Data.Char (chr, ord)
import Data.Map (Map)
import System.Environment (getArgs)
import System.Exit (exitSuccess, exitFailure, ExitCode (..))
import System.IO (hSetBuffering, BufferMode (..), stdin, stdout)
-----------------------------------------------------------
iterateM :: (Monad m) => (a -> m a) -> m a -> m b
iterateM f m = m >>= iterateM f . f
-----------------------------------------------------------
data Cell = Integer Integer | Char Char
-----------------------------------------------------------
newtype Stack = Stack [Integer]
mkStack = Stack []
push :: Integer -> Stack -> Stack
push x (Stack xs) = Stack (x : xs)
pop :: Stack -> Stack
pop (Stack xs) = case xs of
[] -> Stack []
_:ys -> Stack ys
top :: Stack -> Integer
top (Stack xs) = case xs of
[] -> 0
y:_ -> y
-----------------------------------------------------------
data Env = Env {
cells :: Map Integer Cell
, position :: Integer
, stack :: Stack
}
withStack :: (Stack -> Stack) -> Env -> Env
withStack f env = env { stack = f $ stack env }
pushStack :: Integer -> Env -> Env
pushStack x = withStack $ push x
popStack :: Env -> Env
popStack = withStack pop
topStack :: Env -> Integer
topStack = top . stack
-----------------------------------------------------------
type Instruction = Env -> IO Env
cellAt :: Integer -> Env -> Cell
cellAt n = Map.findWithDefault (Char ' ') n . cells
currentCell :: Env -> Cell
currentCell env = cellAt (position env) env
lookupInstruction :: Cell -> Instruction
lookupInstruction cell = case cell of
Integer n -> pushInteger n
Char c -> case c of
'\''-> fetch
'\\'-> swap
'0' -> pushInteger 0
'1' -> pushInteger 1
'2' -> pushInteger 2
'3' -> pushInteger 3
'4' -> pushInteger 4
'5' -> pushInteger 5
'6' -> pushInteger 6
'7' -> pushInteger 7
'8' -> pushInteger 8
'9' -> pushInteger 9
' ' -> nop
'+' -> add
'-' -> subtract
'*' -> multiply
'/' -> divide
'#' -> trampoline
'&' -> inputDecimal
'.' -> outputDecimal
':' -> duplicate
'p' -> put
'@' -> stop
instructionAt :: Integer -> Env -> Instruction
instructionAt n = lookupInstruction . cellAt n
currentInstruction :: Env -> Instruction
currentInstruction = lookupInstruction . currentCell
runCurrentInstruction :: Instruction
runCurrentInstruction env = currentInstruction env env
nop :: Instruction
nop = return
swap :: Instruction
swap env = return $ pushStack a $ pushStack b $ popStack $ popStack env
where
b = topStack env
a = topStack $ popStack env
inputDecimal :: Instruction
inputDecimal env = readLn >>= return . flip pushStack env
outputDecimal :: Instruction
outputDecimal env = putStr (show n ++ " ") >> return (popStack env)
where
n = topStack env
duplicate :: Instruction
duplicate env = return $ pushStack (topStack env) env
pushInteger :: Integer -> Instruction
pushInteger n = return . pushStack n
put :: Instruction
put env = return env' { cells = Map.insert loc c $ cells env'}
where
loc = topStack env
n = topStack $ popStack env
env' = popStack $ popStack env
c = Char . chr . fromIntegral $ n
trampoline :: Instruction
trampoline env = return env { position = position env + 1 }
fetch :: Instruction
fetch = trampoline >=> \env -> let
cell = currentCell env
val = case cell of
Char c -> fromIntegral $ ord c
Integer n -> n
in pushInteger val env
binOp :: (Integer -> Integer -> Integer) -> Instruction
binOp op env = return $ pushStack (a `op` b) $ popStack $ popStack env
where
b = topStack env
a = topStack $ popStack env
add :: Instruction
add = binOp (+)
subtract :: Instruction
subtract = binOp (-)
multiply :: Instruction
multiply = binOp (*)
divide :: Instruction
divide = binOp div
stop :: Instruction
stop = const exitSuccess
tick :: Instruction
tick = trampoline
-----------------------------------------------------------
buildCells :: String -> Map Integer Cell
buildCells = Map.fromList . zip [0..] . map Char . concat . eols
eols :: String -> [String]
eols "" = []
eols str = left : case right of
"" -> []
'\r':'\n':rest -> eols rest
_:rest -> eols rest
where
(left, right) = break (`elem` "\r\n") str
data Args = Args { sourceFileName :: String }
processArgs :: IO Args
processArgs = do
args <- getArgs
case args of
[] -> do
putStrLn "No source file! Exiting."
exitFailure
fileName:_ -> return $ Args { sourceFileName = fileName }
runUnefunge :: Env -> IO ExitCode
runUnefunge = iterateM round . return
where
round = runCurrentInstruction >=> tick
main :: IO ()
main = do
args <- processArgs
contents <- readFile $ sourceFileName args
let env = Env {
cells = buildCells contents
, position = 0
, stack = mkStack
}
mapM_ (`hSetBuffering` NoBuffering) [stdin, stdout]
handle return $ runUnefunge env
return ()