Befunge是由Chris Pressey在1993年创造,其显著特点是二维代码以及自我修改代码。其设计初衷是创造一种难以被编译的语言,但发布不久之后出现了各式各样的编译器。出于兴趣并查阅了大量资料,我顺利通过python语言将这一语言展现出来。
一.Befunge基本语法
Befunge的语法较为简单,具体见下:
# 0-9 将这个数字压入堆栈。
# +添加:弹出a和b,然后压入a+b。
# -减法:弹出a和b,然后压入b-a。
# *乘法:弹出a和b,然后压入a*b。
# /整数除法:弹出a和b,然后 压入 b/a,向下取整。
# %取模:弹出a和b,然后按下b%a。如果a为零,则按零。
# ! 逻辑非:弹出一个值。如果值为零,则按下1;否则,按零。
# `(反引号)大于:弹出a和b,然后推送1if b>a,否则推送零。
# > 开始向右移动。
# < 开始向左移动。
# ^ 开始向上移动。
# v 开始向下移动。
# ? 开始向随机的主要方向移动。
# _弹出一个值;如果 向右移动value = 0,否则向左移动。
# |弹出一个值;向下移动value = 0,否则向上移动。
# "开始字符串模式:将每个字符的 ASCII 值压入栈一直推到下一个".
# :堆栈顶部的重复值。如果堆栈顶部没有任何内容,则将0压入.
# \交换堆栈顶部的两个值。如果只有一个值,假设0堆栈底部有一个额外的值。
# $ 从堆栈中弹出值并丢弃它。
# . 弹出值并输出为整数。
# , 弹出值并输出存储在值中的整数代码表示的ASCII字符。
# # 蹦床:跳过下一个单元格。
# p“放置”调用(一种存储值以供以后使用的方法)。流行y,x并且v,把第x行第Y列的字符换成v。
# g“get”调用(一种在存储中检索数据的方法)。弹出y和x,然后在程序中压入第x行第y列字符的 ASCII 值。
# @ 结束程序。
# & 读取一个整数并压入栈
# ~ 读取一个字符并压入栈
#(即一个空格)无操作。什么也没做。
所有代码(含注释)为:
import random
import time
import tkinter
import sys
import tkinter.simpledialog # 导入对话框相关包
# 在python中二维代码以二维数组的形式存在
# 数组理论上无限大小,这里假设为N*N大小
# 相关全局变量
mode = 1 # 窗口模式,初始为1
codes = '' # 代码文本
stack = [] # 堆栈
direction = 'right' # 指针运行方向
text1 = [] # 辅助元组1
code = [] # 辅助元组2
x = y = 0 # 指针坐标
point = None # 指针位置
jump = False # 是否跳过
char_mode = False # 是否处于字符串模式
output_text = '' # 输出的文字
first_time = True # 第一次建立窗口
write_mode = False # 是否处于输入模式
wait = False # 程序等待
write_kind = 0
a = ''
# 相关子函数
def run(): # 程序开始运行
global mode, codes
codes = input_text.get('1.0', 'end')
input_window.destroy() # 关闭输入窗口
mode = 2
def end(): # 结束程序
sys.exit()
def pos(m, n): # 返回指定位置的字符
return code[int(n)][int(m)]
def replace(o, p, q): # 将(x,y处的字符换成v
code[int(p)][int(o)] = q
def input_int(): # 定义整数输入框
result = tkinter.simpledialog.askinteger(title='获取整数', prompt='请输入一个整数:')
return result
def input_str(): # 定义字符输入框
result = tkinter.simpledialog.askstring(title='获取字符', prompt='请输入一个字符:')
return result
def next_one(): # 指针指向下一个
global x, y
if direction == 'up':
x = x - 1
elif direction == 'down':
x = x + 1
elif direction == 'left':
y = y - 1
elif direction == 'right':
y = y + 1
def refresh():
global show_box, screenWidth, screenHeight, output_window, width, height, left, top, end_button, write_box
global write_button
screenWidth = output_window.winfo_screenwidth()
screenHeight = output_window.winfo_screenheight()
width = 600
height = 400
left = (screenWidth - width) / 2
top = (screenHeight - height) / 2
# 输出窗口设置
show_box = tkinter.Text(output_window, height=27, width=75) # 输出文本框
show_box.place(x=10, y=10)
show_box.delete('1.0', 'end')
for group in code:
for word in group:
show_box.insert('insert', word)
show_box.insert('insert', '\n')
show_box.tag_add('the_tag', str(str(x + 1) + '.' + str(y))) # 高亮文本显示
show_box.tag_config('the_tag', background='yellow')
show_box.update()
end_button = tkinter.Button(output_window, text='结束程序', command=end) # 结束程序按钮
end_button.place(x=100, y=370)
end_button.update()
stack_box.delete(0, 'end') # 更新栈窗口
for item in stack:
stack_box.insert(0, item)
stack_box.update()
if len(output_text) != 0:
out = tkinter.StringVar()
out.set(output_text)
show_ans = tkinter.Label(output_window, textvariable=out)
show_ans.place(x=10, y=300-int(len(output_text) / 5))
show_ans.update()
while True:
if mode == 1:
input_window = tkinter.Tk() # 创建输入窗口
input_window.title('Befunge编译器') # 设置窗口名称
# 设置窗口大小以及位置
screenWidth = input_window.winfo_screenwidth()
screenHeight = input_window.winfo_screenheight()
width = 600
height = 400
left = (screenWidth - width) / 2
top = (screenHeight - height) / 2
input_window.geometry("%dx%d+%d+%d" % (width, height, left, top)) # 设置窗口大小
input_text = tkinter.Text(input_window) # 定义命令文本输入框
input_text.pack()
button = tkinter.Button(input_window, text='运行', command=run) # 定义按钮
button.pack()
end_button = tkinter.Button(input_window, text='结束程序', command=end) # 结束程序按钮
end_button.place(x=100, y=370)
input_window.mainloop()
elif mode == 2:
if first_time:
output_window = tkinter.Tk() # 创建输出窗口
output_window.title('Befunge编译器')
screenWidth = output_window.winfo_screenwidth()
screenHeight = output_window.winfo_screenheight()
width = 600
height = 400
left = (screenWidth - width) / 2
top = (screenHeight - height) / 2
output_window.geometry("%dx%d+%d+%d" % (width, height, left, top)) # 设置窗口大小
show_box = tkinter.Text(output_window, height=27, width=75) # 输出文本框
show_box.place(x=10, y=10)
show_box.insert('insert', codes)
end_button = tkinter.Button(output_window, text='结束程序', command=end) # 结束程序按钮
end_button.place(x=100, y=370)
stack_box = tkinter.Listbox(output_window, width=5) # 堆栈
stack_box.place(x=550, y=10)
show_box.tag_add('the_tag', str(str(x + 1) + '.' + str(y))) # 高亮文本定位
show_box.tag_config('the_tag', background='yellow')
# 运算程序
# 二维代码数组化
if first_time:
code = []
for x0 in codes:
if x0 != '\n':
text1.append(x0)
else:
code.append(text1)
text1 = []
first_time = False # 并非第一次刷新
# 依次读取字符并进行操作
if point != '@': # 程序终止条件:读取到字符@
point = code[x][y] # 指针刷新
time.sleep(0.1)
# 更新相关组件
refresh()
# 下面依次对不同字符给予操作
if not char_mode: # 字符串模式关闭情况下才能实现字符功能
if not jump:
if isinstance(point, int): # 如果输入0~9,则将数字压入栈
stack.append(int(point))
elif point == '+':
a = stack.pop()
b = stack.pop()
c = int(a) + int(b)
stack.append(c)
elif point == '-':
a = stack.pop()
b = stack.pop()
c = int(b) - int(a)
stack.append(c)
elif point == '*':
a = stack.pop()
b = stack.pop()
c = int(a) * int(b)
stack.append(c)
elif point == '/':
a = stack.pop()
b = stack.pop()
c = int(int(b) / int(a))
stack.append(c)
elif point == '%': # 四则运算符
a = stack.pop()
b = stack.pop()
c = int(b) % int(a)
stack.append(c)
elif point == '"': # 开启字符串模式,直到下一个"为止遇到的所有字符全部压入栈
char_mode = True
elif point == '&': # 输入一个整数并压入栈
a = input_int()
stack.append(a)
elif point == '~': # 输入一个字符,
a = input_str()
stack.append(a)
elif point == '.': # 弹出一个元素并作为整数输出
a = stack.pop()
if isinstance(a, int):
output_text = output_text + str(a)
else:
output_text = output_text + str(ord(str(a)))
elif point == ',': # 弹出一个元素并作为字符输出
a = stack.pop()
if not isinstance(a, int):
output_text = output_text + str(a)
else:
output_text = output_text + chr(a)
elif point == '_': # 弹出一个元素,若为0 向右走否则向左走
a = stack.pop()
if a == 0:
direction = 'right'
else:
direction = 'left'
elif point == '|': # 弹出一个元素,若为0向下走否则向上走
a = stack.pop()
if a == 0 or len(stack) == 0 or a == '0':
direction = 'down'
else:
direction = 'up'
elif point == ':': # 复制栈顶的元素
if len(stack) == 0:
stack.append(0)
else:
a = stack.pop()
stack.append(a)
stack.append(a)
elif point == '\\': # 交换栈顶两元素
a = stack.pop()
b = stack.pop()
stack.append(a)
stack.append(b)
elif point == '#': # 跳过下一个指令
next_one()
elif point == '!': # 弹出一个值,如果是0压入1,否则压入0
a = stack.pop()
if a == 0:
stack.append(1)
else:
stack.append(0)
elif point == '`': # 弹出a和b,如果b>a 压入1,否则压入0
a = stack.pop()
b = stack.pop()
if b > a:
stack.append(1)
else:
stack.append(0)
elif point == 'g': # 弹出y和x,然后压入(x,y)处的字符的ASCLL值
y2 = stack.pop()
x2 = stack.pop()
stack.append(ord(pos(x2, y2)))
elif point == 'p': # 弹出y,x,v,然后将(x,y)处替换为v
y2 = stack.pop()
x2 = stack.pop()
v = stack.pop()
replace(x2, y2, v)
elif point == '?': # 随机选择方向
dirctions = ['left', 'right', 'up', 'down']
direction = dirctions[random.randrange(0, 4)]
elif point == '$': # 丢掉栈顶
a = stack.pop()
elif point == ' ': # 空格跳过
pass
elif point == '>': # 四个方向
direction = 'right'
elif point == '<':
direction = 'left'
elif point == '^':
direction = 'up'
elif point == 'v':
direction = 'down'
else:
jump = False
elif point == '"' and char_mode: # 读取对应",关闭字符串模式
char_mode = False
else:
stack.append(point) # 字符串,模式中所有字符都会压入栈
# 指针坐标变动
next_one()
else:
print(output_text) # 输出所有
output_window.mainloop()
能满足基本的代码需求,至于美观性就有时间再完善吧( •̀ ω •́ )y,以下为代码运行示例: