Brainfuck 语法介绍
有一种很神奇的编程语言叫做brainfuck. 很多人都学过C++, Java, Python等主流的语言,总是会有一种“天哪,语法好复杂”的感觉,brainfuck的语法则超级简单:
字符命令 | 含义 |
---|---|
> | 指针右移一个单位 |
< | 指针左移一个单位 |
+ | 当前数据加一 |
- | 当前数据减一 |
. | 输出当前数据 |
, | 读入一个字节并赋给当前数据 |
[ | 如果当前数据是0,则跳到匹配的]指令后面;否则执行下一条指令 |
] | 如果当前的数据不是0,就跳到匹配的[ 的下一条指令;否则执行下一条指令 |
用C语言来翻译一下,就是:
字符命令 | 等价的C语言 |
---|---|
> | ++ptr |
< | --ptr |
+ | ++(*ptr) |
- | --(*ptr) |
. | putchar(*ptr) |
, | *ptr = getchar() |
[ | while (*ptr) { |
] | } |
除了上面所列的8个字符之外的所有字符,都被视为程序的注释。
虽然brainfuck的语法如此之简单,但这种语言也满足图灵完全(Turing Completeness) 的性质,简单来说,所有可以用图灵机计算求解的问题都可以用brainfuck来求解。当然,用这种语言来编程必然是非常痛苦的。。
Brainfuck解释器
写一个brainfuck解释器,只需要分别实现8种操作命令就可以了,下面给出用ruby实现操作命令的几个例子。
比如,要实现>
操作命令,自增指针就可以了:
def forward()
@mem_ptr += 1
if @mem_ptr >= @mem_limit
print "Access Violation\n"
exit 1
end
next_pc
end
next_pc
函数是取下一条指令,pc
是程序计数器(Program Counter)
的缩写。
要实现+
操作命令,自增当前指针的数据就可以了:
def increment()
@memory[@mem_ptr] = (@memory[@mem_ptr] + 1) % 256
next_pc
end
下面给出完整可运行的代码实现:
class BrainfuckInt
attr_accessor :code, :mem_limit, :memory, :buffer, :mem_ptr, :pc
def initialize(path)
@code = File.read(path).split('').select{|c| valid?(c)}
if !bracket_match?
print "Error: Unmatched brackets\n"
exit 1
end
@mem_limit = 32 * 1024 # default memory size is 32KB
@memory = Array.new(@mem_limit, 0)
@pc = 0
@mem_ptr = 0
@buffer = ''
end
def parse()
while @pc < @code.length
if @code[@pc] == '+'
increment
elsif @code[@pc] == '-'
decrement
elsif @code[@pc] == '<'
backward
elsif @code[@pc] == '>'
forward
elsif @code[@pc] == '.'
display
elsif @code[@pc] == ','
read
elsif @code[@pc] == '['
enter_loop
elsif @code[@pc] == ']'
exit_loop
else
print "Invalid character: " + @code[@pc] + "\n"
exit 1
end
end
end
private
def increment()
@memory[@mem_ptr] = (@memory[@mem_ptr] + 1) % 256
next_pc
end
def decrement()
@memory[@mem_ptr] = (@memory[@mem_ptr] - 1 + 256) % 256
next_pc
end
def read()
@memory[@mem_ptr] = get_char().ord
next_pc
end
def display()
print @memory[@mem_ptr].chr
next_pc
end
def forward()
@mem_ptr += 1
if @mem_ptr >= @mem_limit
print "Access Violation\n"
exit 1
end
next_pc
end
def backward()
@mem_ptr -= 1
if @mem_ptr < 0
print "Access Violation\n"
exit 1
end
next_pc
end
def enter_loop()
if @memory[@mem_ptr] == 0
cnt = 1
next_pc
while cnt > 0
if @code[@pc] == '['
cnt += 1
elsif @code[@pc] == ']'
cnt -= 1
if cnt == 0
break
end
end
next_pc
end
end
next_pc
end
def exit_loop()
if @memory[@mem_ptr] != 0
cnt = 1
@pc -= 1
while cnt > 0
if @code[@pc] == ']'
cnt += 1
elsif @code[@pc] == '['
cnt -= 1
if cnt == 0
break
end
end
@pc -= 1
end
end
next_pc
end
def next_pc()
@pc += 1
end
def valid?(ch)
['+', '-', '<', '>', '.', ',', '[', ']'].index(ch) != nil
end
def bracket_match?()
cnt, i = 0, 0
while i < @code.length
if @code[i] == '['
cnt += 1
elsif @code[i] == ']'
cnt -= 1
end
if cnt < 0
return false
end
i += 1
end
true
end
def get_char()
while @buffer == nil or @buffer.length == 0
@buffer = STDIN.gets
end
next_char = @buffer[0]
@buffer = @buffer[1, @buffer.length]
next_char
end
end
def help()
print "Usage: \n\truby ./bf.rb example.bf\n"
end
if __FILE__ == $0
if ARGV.length != 1
help
exit 1
end
path = ARGV[0]
if not File.file?(path)
print "Invalid path: " + path + "\n"
exit 1
end
interpreter = BrainfuckInt.new(path)
interpreter.parse
end
代码比较短,不用加注释也可以很容易读懂。
运行示例
运行命令很简单,比如有一个helloworld.bf
的源文件:
ruby ./bf.rb helloworld.bf
就可以了
下面是一个可能版本的helloworld.bf
实现:
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
在终端运行: