Hello World! in Brainfuck:
function bf(s) m, l, i, j, o = zeros(UInt8,2^15), Int[], 1, 1, UInt8(1)
while i<=length(s) c=s[i]
c=='>' ? j+=1 : c=='<' ? j-=1 : c=='+' ? m[j]+=o : c=='-' ? m[j]-=o :
c=='.' ? print(Char(m[j])) : c==',' ? m[j]=read(stdin,UInt8) :
c=='[' ? push!(l,i+1) : c==']' ? (m[j]!=0 ? (i=last(l);continue) : pop!(l)) : 0
i+=1 end end
bf("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.")
The implementation is non-standard because when it encounters the character '['
it unconditionally executes the block at least once instead of skipping to the matching ']'
if the current byte is zero. In terms of the translation table described here, it is as if '['
were translated to do {...} while (*ptr)
instead of while (*ptr) {...}
.
Edit 1
Here is a version with the correct implementation for '['
. The function jmp
is used to jump to the instruction after the matching ']'
.
jmp(s,i) = (n=1; i+=1; while n>0 s[i]=='[' ? n+=1 : s[i]==']' ? n-=1 : 0; i+=1 end; i)
function bf(s) m=zeros(UInt8,2^15); l=Int[]; i=j=1; o=UInt8(1)
while i<=length(s) c=s[i]; c=='>' ? j+=1 : c=='<' ? j-=1 : c=='+' ? m[j]+=o :
c=='-' ? m[j]-=o : c=='.' ? print(Char(m[j])) : c==',' ? m[j]=read(stdin,UInt8) :
c=='[' ? (m[j]==0 ? (i=jmp(s,i);continue) : push!(l,i+1)) :
c==']' ? (m[j]!=0 ? (i=l[end];continue) : pop!(l)) : 0; i+=1 end end
bf(">>++++++[>++++++++<-]+[[>.[>]+<<[->-<<<]>[>+<<]>]>++<++]")
The Brainfuck code at the end generates the Thue–Morse sequence (I hope: I haven’t checked) and is taken from here. The resulting program is non-terminating.
Some comments about the code
s
is the Brainfuck source code, m
is the memory tape, i
is the current instruction index, c
is the current instruction code character, j
is the index of the current memory byte, l
is the loop stack, o
is one as UInt8
used to increment/decrement the memory.
Edit 2
Here is a version that transpiles Brainfuck to Julia code using metaprogramming.
const t = Dict('>'=>:(j+=1), '<'=>:(j-=1), '+'=>:(m[j]+=UInt8(1)), '-'=>:(m[j]-=UInt8(1)), '.'=>:(print(Char(m[j]))), ','=>:(m[j]=read(stdin,UInt8)))
le(s) = (n=1; i=2; while n>0 s[i]=='[' ? n+=1 : s[i]==']' ? n-=1 : 0; i+=1 end; i-1)
function bf(s) if isempty(s) nothing
elseif s[1]=='[' l=le(s); n=l+1; :( while m[j]!=0; $(bf(s[2:l-1])) end; $(bf(s[l+1:end])) )
else :($(get(t,s[1],nothing)); $(bf(s[2:end]))) end end
macro bf(s) :(()->(m=zeros(UInt8,2^15); j=1; $(bf(s)))) end
@bf(">>++++++[>++++++++<-]+[[>.[>]+<<[->-<<<]>[>+<<]>]>++<++]")()
The function le
is used to find the loop end (that is, the index of the matching ]
). The function bf
transforms a Brainfuck string to a Julia expression. The macro @bf
returns an anonymous function that initializes the memory, the memory index and executes the code generated by bf
.
The code generated by bf
is not particularly nice to read because it is deeply nested. There are better ways to generate the code but they require more lines (at least, I haven’t found a smart way to do it). You can inspect the code of the function generated by the macro with
@code_warntype @bf(">>++++++[>++++++++<-]+[[>.[>]+<<[->-<<<]>[>+<<]>]>++<++]")()