Brainfsck in Haskell

Recently, I had to sit an exam which involved questions on/about the functional language Haskell (it was an Oxford start of term exam, also known as a collection, if you're interested). To revise the feel and syntax of Haskell, I decided to write a minimal interpreter for my favourite esoteric language, Brainfsck, in Haskell.

Writing such an interpreter in C/C++ is fairly trivial, with the only slight difficulty being providing an array which is infinitely long in both directions. As Haskell is a functional language (function in the mathematical sense, rather than the sub-procedure sense), providing an infinitely long array is extremely simple, and it is everything else which is non-trivial. The code ends up making heavy use of monadic I/O (ironically, monadic I/O isn't really covered in the course syllabus beyond "you can construct a string and then pass it to putStr to write stuff"), and looks very different to how you'd implement it in C/C++:

code = "++++++++++[>+++++++>+++++++++++>+++>++++++++++<<<<-]>+.>+.>++.<<++++.>.+++.>>+.<+.<<<."

data Token = C Char | L [Token]
join a (b, c) = (a:b, c)
parse ('[':xs) = join (L inner) (parse etc) where (inner, etc) = parse xs
parse (']':xs) = ([], xs)
parse (x:xs) = if elem x "<>+-.," then join (C x) (parse xs) else parse xs
parse [] = ([], [])
evalWith l c r = eval (return (l, c, r))
evalRaw (C '<') (l:ls) c r = evalWith ls l (c:r)
evalRaw (C '>') l c (r:rs) = evalWith (c:l) r rs
evalRaw (C '+') l c r = evalWith l (if c == 255 then 0 else (c + 1)) r
evalRaw (C '-') l c r = evalWith l (if c == 0 then 255 else (c - 1)) r
getCharNoR = getChar >>= \c -> if c == '\r' then getCharNoR else (return c)
eval s ((L  cs):xs) = s >>= (\(l, c, r) -> if c == 0 then (eval s xs) else (eval (eval s cs) ((L cs):xs)))
eval s ((C '.'):xs) = s >>= (\(l, c, r) -> (putStr [toEnum c]) >> (eval s xs))
eval s ((C ','):xs) = s >>= (\(l, _, r) -> getCharNoR >>= \c -> evalWith l (fromEnum c) r xs)
eval s (x:xs) = s >>= (\(l, c, r) -> evalRaw x l c r xs)
eval s [] = s

result = evalWith (repeat 0) 0 (repeat 0) (fst (parse code)) >>= (\_ -> return ())

The parse function (along with its helper, join) is used to perform a first-pass over the Brainfsck code. This removes non-code characters, and performs bracket matching to simplify the evaluation stage. The output of the parse function is a list of tokens, where each token is either a code character, or a loop (which is just another list of tokens). To simplify the implementation of parse, it also returns the tail of the code which it could not understand as a second return value.

The eval function (along with helpers evalWith, evalRaw, and getCharNoR) handles the transformation of the tokenised Brainfsck code into a monadic I/O object representing the evaluation of the Brainfsck code.

Finally, the result function ties everything together. Note the incredibly simple creation of the infinite array - it is just (repeat 0) 0 (repeat 0), which also describes the infinite array fairly well.

CorsixTH - Playable Beta 1

Today marks the release of the first playable beta of CorsixTH, an open source Theme Hospital clone which I started a few months ago.

Relevant links: CorsixTH Homepage | Downloads | Release Annoucement.

Theme Hospital Update

It's been a while since since my first blog post on Theme Hospital, and there has been a reasonable amount of development since then, so I thought I'd post a video showing some of the new stuff:

As before, if you're interested in grabbing a copy, or contributing, then see the Google Code project.

Lua can be ASCII Art

Observe my wonderful Lua ASCII art creation:

                                                                _={_=_G
                                                              }for--[[--]]__
                                                            in(next),_["_"]do
                                                           (_)[_]=(__)_[#_[_]]
                                                          =_[_]_[_]="sub"end(_)
                              [_]=_[_]                    [_[_]]_[_._]=#"_._"_[
                       _[_._]]=_[_](_[_[_._]*_             [_._]],_[_._],_[_._
                    ]).._[_](_[(_[_._]+_[_._]/_[_._         ])*_[_._]],_[_._]/
                _[_._]+_[_._]/_[_._],_[_._]).._[_](_[_       [_._]*_[_._]],_
             [_._]+_[_._]-_[_._]/_[_._],_[_._]+_[_._]-_[       _._]/_[_._]
           ).._[_](_[_[_._]*_[_._]],_[_._],_[_._]).._[_](_[         _
         [_._]*_[_._]],_[_._]/_[_._]-_[_._]           ,_[_._
        ]/_[_._]-_[_._])_[_[_._]*_[_._]+_               [_._]-
      _[_._]/_[_._]]=(_)_[_[_._]*_[_._]+                  _[_._
     ]+_[_._]/_[_._]]=(_)_[#_+_[_._]/_[                    _._]]
    =_._[_[#_-_[_._]-#_/#_]]_[_[#_]]=_[                    #_](_[
   _[_._]].."(".._[#_-#_/_[_._]].."('"                     .._[_[_
  ._]]..[[("\\'..(...+#_*(_[_._]+_[_._]                   ))..'")')
  )()]])_[_[#_]]=_[_[_._]][_[_[#_]](_[_.                 _]*_[_._])
 .._[_[#_]](#_-_[_._]/_[_._]).._[_[#_]](_               [_._]+_[_._]
 +_[_._]/_[_._]).._[_[#_]](_[_._]^_[_._]-_[_         ._])]_[_[_[#_]](
 #_)]=#_*#_/_[_._]_[_[_[#_]](#_)]=_[_[_[#_]](#_)]+_[_[_[#_]](#_)]/_[(
 _._)]_._[_[    _[#_]](_[_[_[#_]](#_)]+#_-_[_._],_[_[_[#_]](#_)]+#_-
 #_/#_,_[_[_[   #_]](#_)]+#_/_[_._],_[_[_[#_]](#_)]+#_-#_/_[_._], _[
 _[_[#_]](#_)   ]+#_+#_/#_)](_[_[#_+_[_._]-_[_._]]](#_*#_/_[_._]-_[_.
 _],_[_[_[#_]   ](#_)]+_[_._]    /_[_.   _],_[  _    [_[#_]](#_)]+#_
 /_[_._]+_[_.   _],_[_[_[#_]](   #_)]+   #_/   _[_.   _]+_[_._],_[_[
  _[#_]](#_)]   +#_-_[_._]-_[_   ._]/_   [_._  ],#_   +#_+_[_._]-_[
  _._]/_[_._]   ,#_*(_[_._]+_[   _._])   -_[_.    _   ],_[_[_[#_]](
   #_)]+#_-_[   _._]-_[_._ ]/_   [_._]   ,_[_   [_[   #_]](#_)]+#_
    -#_/#_,_[   _[_[#_]](  #_)   ]+#_/   _[_   ._]+   _[_._],_[_[
     _[#_]](     #_)],#   _+#_    +#       _    /# _  + #_/#_,_[_
     [_[#_               ]](#_)     ]+_   [_._    ]-_  [_._]/_[_
      ._],_[_[_[#_]](#_)]+#_-_[_._]/_[_._],(_[_._])^_[_._]*(_[
        _._]+_[_._]/_[_._])+_[_._],_[_[_[#_]](#_)]+_[_._]*_[_
         ._],#_+#_+#_/#_+#_/#_,#_*#_/_[_._]+_[_[_[#_]](#_)]/
            _[_[_[#_]](#_)],_[_[_[#_]](#_)]+#_+_[_[_[#_]](
              #_)]/_[_[_[#_]](#_)]+_[_[_[#_]](#_)]/_[_[
                 _[#_]](#_)],_[_[_[#_]](#_)]-_[_._]))
                    _._=_[_[_._]][_[_[_[#_]](#_)]
                         ]_[(#_)^#_-_[_._]]=
                                 _._

As an improvement on my prior post, this valid Lua program has a more interesting whitespace arrangement, has less string literals, and less alphanumeric characters (no "byte" in this code, whereas the previous post does).

The worst Lua "Hello World" you'll ever see

As a continuation of my Lua abomination series, the below code is surely one of the most indecipherable versions of print 'Hello World' that you're likely to see.

_={_=_G}for--[[]]__--[[]]in(next),_["_"]do(_)[_]=(__)_[#_[_]],_[_[_]:byte(-#"#"
)+#_[_]-(#{}+#"(#''"*#"*#*#*"*#"_[_[]]")]=_[_],_[_]end(_)[_]=_._[_[#""]]{[_._[_
[#""]]]=_}_[""]=_._[_._[_[#[=[=#=]=]*-((#[=[#[=]#]=]))]](_._[_[-#[[_[-#[#_[_]]]
](_))]_[";"]=_._[_[#"#"+(#")#^")^#"#^"]]_["'"]=[[sub]]_['"']=_[""][_["'"]]_["/"
]=[[/_)=.,[#"('*:^;+]]_["'"]=_[""][_['"'](_[-#[[=[=]=]]],-#",_",-#"..").._["'"]
]_["["]=_['"'](_[-#"#-]_"],#",",#{_}).._['"'](_[-#"-"],#",",#"#").._['"'](_[-(#
"^#^")^#"^#"],#"-",#"(").._['"'](_[#_[-#"#"]*-#"[#"],#_[-#"#"],#_[-#"#"]).._[''
..'"'](_[-#[[=[]=]]],#_["/"]/#_["/"],#"/").._['"'](_[-(#"#)-")^#[[""]]],-#"-,",
-#[=[[]]=])_["]"]=_['"'](_[-#_[-#"-"]],#",",#"#").._[";"](_["["]..[=[('\]=]..(#
'#).'*#',..]]'*#'",#"#",'-#'(').."')")().._['"'](_[-#_[-#"-"]],-#_[-#"-"]-#"-",
-#_[-#"-"])_['_']=_[";"](_["["]..'(_[""].'.._[";"](_["["]..[[('\]]..((#_["/"]+#
"'")*#"#*("*#"..").."')")().._['"'](_[#_[-#"_"]*#"[_"],-#"#-,",-#"(,").._['"'](
_[-#_["/"]],-#",",-#"(")..'(_["/"],...,#"#","")-#"#")')_[";"](_["'"](_["'"]([[]
#/#)[([;#.))."[,[:[:[+)/,#[+#)[:[.)))^)^#/#)[([;#.))."[,[:[:[+)/,#[+#)[:[.)))^]
]],"[^".._["/"].."]",""),"(.)(.)",_[";"]("_['.'],_['#']=...".._["["].."(_['']."
.._["]"].."(_['_'](...)*#_['/']+_['_'](_['#'])))")))(...)_={#{...},#{#{}},#"#"}

(If you don't believe me, it does print Hello World).

The aim was to use as few alphanumeric characters as possible, and I think I did fairly well - you've only got "G", "for", "in", "next", "do", "byte", "end", and "sub". For a language which typically is full of alphanumeric characters, I consider it quite impressive. As an added bonus, there is also no whitespace.

Only about half a line of the code is an encoding of "print'Hello World'" - the other lines just perform generic decoding. That's right; not only this is an obscene obfuscation, it's also an easily reusable obfuscation.

page: 16 17 18 19 20