背景
最近又玩了遍《人力资源机器》和《70亿人》(都是编程游戏,用一种类似汇编的简陋语言解决谜题,后者相当于前者的多线程版本),考虑自己设计开发一个编程游戏。游戏玩法大概是玩家管理一个机器人编队(包括近战型、远程型和支援型)与敌方机器人编队作战,每台机器人包括一个可编程CPU和一些可选模块(比如移动、撞击、射击、炮火打击和雷达模块等等),核心玩法为机器人的改装(模块装配)、编队(队伍构成、站位和对地形的利用)和编程(编写机器人CPU运行的核心代码,从模块中获取战场信息并控制模块行动)。最后打算以Unity开发,Unity学习中…
还是先用Python+pygame写个demo…
简介
考虑到游戏内给机器人编程所使用的编程语言应该简单有效,核心操作在于CPU与模块的交互,所以刚好用上了之前设计的Y语言,不过之前的Y语言设计得非常简陋,不支持真正的函数,即使是实现一个简单的功能也需要大量语句,因此为了适应游戏内容做了一些改进些,这是前身:Y语言1.0,相比Y1.0版本主要做了以下改动:
- 真正的函数:新版解释器加入了调用栈,现在的Y语言函数支持了传参和返回值功能,同时可以进行复杂的嵌套调用和递归调用了,函数仍然支持嵌套定义;
- 变量域:新增了变量域的概念,现在变量区分局部变量和全局变量了,在使用CALL语句调用函数和RET语句返回时会有局部变量域的切换发生;
- 特殊变量@:新增了一个特殊的全局变量@,绝大部分运算指令和函数返回值都会在运行结束后存入该变量;
- 更自然的列表:删除了v1.0中反人类的idx、set和len等列表操作指令,现在可以用中括号[]来索引列表中的元素了,创建列表也可以采用[1,2,3]这种方式了,同时还支持了列表嵌套索引、嵌套定义和多维列表;
- 更简洁的if语句:现在if语句支持if 1 < 2这种书写方式了,此外还加入了else语句;
- 循环语句:新增了loop语句,类似高级语言中while的用法,写循环不再需要危险的at和go了;
- 移除字符串:因为编程游戏中不需要字符串的处理工作,所以移除了字符串的支持和相关操作;
一段代码片段示例:
Python:
# 函数flatten可以将一个多层嵌套列表list压平成一维列表
def flatten(lst):
tmp = []
for item in lst:
if isinstance(item, list):
for it in flatten(item):
tmp.append(it)
else:
tmp.append(item)
return tmp
x = [1, [2, [3, [4, [5], 6, [[7, 8], 9]]]], 10, 11, 12]
print(x, len(x))
y = flatten(x)
print(y, len(y))
翻译成Y语言:
// 函数flatten可以将一个多层嵌套列表list压平成一维列表
def flatten list
mov tmp []
mov i 0
// list[] 返回list的长度,对于非列表则返回-1
loop i < list[]
if list[i][] == -1
push tmp list[i]
else
// 激动人心的递归调用
call flatten list[i]
mov res
mov j 0
loop j < res[]
push tmp res[j]
inc j
elop
eif
inc i
elop
ret tmp
edef
// 定义复杂的嵌套列表x
mov x [1,[2,[3,[4,[5],6,[[7,8],9]]]],10,11,12]
out x x[]
call flatten x
// 返回值放入特殊变量@,此时@ = [1,2,3,4,5,6,7,8,9,10,11,12]
out @ @[]
详细介绍
1.空格、换行、注释以及缩进
空格是Y语言语句中唯一的分隔符,指令与变量与常量之间均以空格分隔。
语句与语句之间用换行分隔,一行只能有一条语句。
注释以//打头,运行时跳过,空行同理。
缩进对代码运行无影响。
2.变量
变量名为非数字打头的下划线数字字母串,变量自身无类型,可存储Y语言支持的三种数据类型:整数,浮点数,列表。
变量分为全局变量和局部变量,取变量时优先搜索局部变量域,即定义局部变量会屏蔽同名全局变量。存变量时除非使用glb指令,否则都存入局部变量域(局部变量不存在时会被创建)。调用函数和函数返回时会发生局部变量域切换。全局变量域始终可见。
另外@为一个特殊的全局变量,用于存储一些指令的结果。
数据类型支持整数、浮点数、列表三种
数字皆以自然方式定义,如:1、0、1.5、-1.5、+6皆为有效定义;
列表以[]括起来的一组元素定义,元素间以逗号分隔,支持嵌套定义列表和包含变量,如:[1,2,3,[1,2,3],var1,var2]。空[]表示空列表。
列表的索引也采用[],list[x]表示列表list的x项,若list是多维列表也支持list[x][y]来多维索引,list[x[y]]的嵌套索引也支持。此外变量名后接空括号[]表示求长度,如array = [1,2,3] 则array[]为3,若array为非列表则array[] = -1。
3.行号和指令
Y语言的代码从头开始逐行运行,每行代码都对应一个行号,行号从0开始。每行语句的第一个单词即指令,其后都为指令需要的参数,指令指示解释器的行为,具体见下文。
指令和语法
指令可以分为控制指令、操作指令、逻辑指令和外部指令四类,所有指令运行时均不分大小写。其中外部指令由解释器外挂了哪些模块决定,并非通用的指令。
1.控制指令
- at:定位指令,不推荐使用,接受一个变量名,将当前行号存入该变量。如 at var。
- go:跳转指令,不推荐使用,接受一个参数,可以是整数常量或变量,将当前运行行跳转到数字对应行的下一行。如 go target。
- mov:赋值指令,接受一个变量名和一个可选参数,将第二个常量或变量的值存入第一个变量,第一个变量(除@外)在局部变量域不存在时会被创建,若无第二个参数则默认为@。如 mov list [1,2,3]或 mov @ 1.234,此外mov x 等效于 mov x @。
- glb:全局赋值指令,用法与mov相同,但对第一个参数的操作变量域为全局变量域。
- if:条件指令,接受0、1或3个参数,接受1个参数时,若该参数为0则跳转到匹配的else(如果有)或eif,否则继续运行,如:if flag。不接受参数时 if 相当于 if @。接受三个参数时需要满足if a op b 的形式,其中a、b皆为数字量,op为<、<=、>、>=、==、!=中的一种,a op b表达成立时继续运行否则跳转,如 if var <= 999。
- else:否则条件指令,不接受参数,与if配对使用,当if条件不满足时会跳转到该指令(如果有),若if条件满足遇到该指令则会跳转到匹配的eif。
- eif:结束条件指令,不接受参数,与if配对使用,用于if和else的跳转定位,eif会匹配之前最近一个未被匹配的if。
- def:函数定义指令,接受一个变量名和若干可选变量名,将当前行号存入该变量(定义于最外层的函数会被存入全局变量),可选变量名将作为函数调用时的局部变量被传入参数创建,随后跳转到对应edef语句,用于定义函数。如 def fun a b。
- edef:结束定义指令,不接受参数,与def配对使用,用于结束def的定义范围。
- call:调用指令,接受一个变量名和该变量对应函数对应的参数个数,储存当前位置和局部变量进入调用栈,以传入参数创建新的局部变量域并跳转至函数定义行的下一行,如 call fun 1 2。
- ret:返回指令,接受一个可选参数,仅能在调用函数内使用,从调用栈中弹出上一层的call的调用位置和局部变量域并跳转,如果接受了参数还会将该参数值存入变量@,如 ret a。
- loop:循环指令,接受参数与if相同,与elop配对使用,满足条件时继续运行,否则跳转到对应elop指令的下一行,如 loop i < array[]。
- elop:循环结束指令,不接受参数,与loop配对使用,跳转回对应的loop。
- brk:跳出指令,不接受参数,用于循环内部,跳转到对应elop指令的下一行。
- ctn:继续指令,不接受参数,用于循环内部,跳转到对应loop指令。
2.操作指令
- push:压入指令,接受一个列表和一个可选参数,将第二个参数追加到列表末尾,如 push list var。无可选参数时 push list 相当于 push list @。
- pop:弹出指令,接受一个列表和一个可选参数,将列表末尾元素弹出放入第二个参数,如 pop list var。无可选参数时 pop list 相当于 pop list @。
- int:取整指令,接受一个数字变量,将其值的小数部分舍去,如 int num。
- inc:自增指令,接受一个数字变量,将其值增加1,如 inc x。
- dec:自减指令,接受一个数字变量,将其值减少1,如 dec x。
- add:加法指令,接受两个数字量,求和存入@,如 add n 1。
- sub:减法指令,接受两个数字量,求差存入@,如 sub n 2。
- mul:乘法指令,接受两个数字量,求积存入@,如 mul n 3。
- div:除法指令,接受两个数字量,求商存入@,如 div n 4。
- mod:模指令,接受两个数字量,求模存入@,如 mod n 5。
- pow:幂指令,接受两个数字量,求幂存入@,如 pow n 0.5。
3.逻辑指令
- eq:相等指令,接受两个参数,两个参数相等时将1存入@,否则将0存入@,如 eq i [1,2,3]。
- neq:不等指令,接受两个参数,两个参数相等时将0存入@,否则将1存入@,如 neq i 22。
- gt:大于指令,接受两个数字量,满足前者大于后者时将1存入@,否则将0存入@,如 gt x 0。
- ls:小于指令,接受两个数字量,满足前者小于后者时将1存入@,否则将0存入@,如 ls x 0。
- ge:大于等于指令,接受两个数字量,满足前者大于等于后者时将1存入@,否则将0存入@,如 ge x 0。
- le:小于等于指令,接受两个数字量,满足前者小于等于后者时将1存入@,否则将0存入@,如 le x 0。
- and:逻辑与指令,接受两个参数,两个参数都不为0时将1存入@,否则将0存入@,如 and a b。
- or:逻辑或指令,接受两个参数,两个参数任意一个不为0时将1存入@,否则将0存入@,如 or a b。
- not:逻辑与指令,接受一个参数,参数为0时将1存入@,否则将0存入@,如 not a。
4.外部指令
- out:输出指令,来自模块Outputer,接受任意个参数,将其依次输出到屏幕,如 out 1 x y。
- in:输入指令,来自模块Inputer,不接受参数,从键盘输入一个数字存入@。
……
Python实现的解释器
认真写了个解释器,应该没有没处理到的异常和BUG。
1.解释器文件interpreter.py
class InterpreterError(Exception):
""" 解释器内部异常 """
pass
class Interpreter:
""" Y语言解释器 """
def __init__(self, codes: list, mod_cmds: dict):
self.codes = codes # Y语言代码,字符串列表
self.ans = 0 # 特殊变量@
self.call_stack = [] # 解释器栈
self.global_vars = {} # 全局变量
self.local_vars = {} # 局部变量
self.pointer = 0 # 代码指针
self.prime_commands = {name[4:]: self.__getattribute__(name) for name in self.__dir__()
if name.startswith('cmd_')} # Y语言原生指令集
self.module_commands = mod_cmds # 模块指令集
def run(self, cpt: int):
""" 执行cpt条指令,顺利执行完毕返回True,已无指令可执行或执行出错返回False """
count = 0
while count < cpt:
if self.pointer >= len(self.codes) or self.pointer < 0:
return False
code = self.codes[self.pointer].strip()
try:
count += self.exec(code)
except InterpreterError as e:
print('(line {}: {})Cpu Error: '.format(self.pointer, code) + str(e))
return False
self.pointer += 1
return True
def exec(self, code: str):
""" 执行一条指令,执行成功返回1,否则返回0"""
cmd, *args = code.split(' ')
cmd = cmd.lower()
if not cmd or cmd.startswith('//'):
return 0
try:
self.prime_commands[cmd](args)
except KeyError:
try:
val = self.module_commands[cmd]([self.get_value(arg) for arg in args])
if val is not None:
self.set_value('@', val)
except KeyError:
raise InterpreterError('Unknow command ' + cmd)
return 1
def get_matched_end(self, ptr: int, start: str, end: str, step=1):
""" 获取ptr行strat匹配的end所在行 """
count = 1
ptr += step
while 0 <= ptr < len(self.codes):
code = self.codes[ptr].upper().strip()
if code.startswith(start):
count += 1
elif code.startswith(end):
count -= 1
if count == 0:
return ptr
ptr += step
raise InterpreterError(end + ' not found')
def get_matched_else_eif(self, ptr: int, elsed=False):
""" 获取ptr行if或else匹配的else或eif所在行 """
count = 1
ptr += 1
while ptr < len(self.codes):
code = self.codes[ptr].upper().strip()
if code.startswith('IF'):
count += 1
elif count == 1 and code.startswith('ELSE'):
if elsed:
self.pointer = ptr
raise InterpreterError('ELSE is mismatched')
return ptr
elif code.startswith('EIF'):
count -= 1
if count == 0:
return ptr
ptr += 1
raise InterpreterError('EIF not found')
def get_array_index(self, var: str):
""" 将var拆分为列表名和索引 """
left = self.left_bracket_index(var)
index = self.get_value(var[left + 1: -1])
var = self.get_value(var[: left])
if not isinstance(index, int):
raise InterpreterError(str(index) + ' is not a int')
if not isinstance(var, list):
raise InterpreterError(str(var) + ' is not a list')
return var, index
def get_operands(self, args: list):
""" 获取操作数 """
oper1 = self.get_value(args[0])
oper2 = self.get_value(args[1])
if not (isinstance(oper1, int) or isinstance(oper1, float)):
raise InterpreterError(str(oper1) + ' is not a number')
if not (isinstance(oper2, int) or isinstance(oper2, float)):
raise InterpreterError(str(oper2) + ' is not a number')
return oper1, oper2
@staticmethod
def left_bracket_index(var: str):
""" var为末尾为]的表达式,返回和末尾]匹配的[ """
left = len(var) - 2
count = 1
while left != -1:
if var[left] == '[':
count -= 1
elif var[left] == ']':
count += 1
if count == 0:
break
left -= 1
else:
raise InterpreterError(var + ' missing [')
return left
@staticmethod
def parse_list(lst: str):
""" 分析列表字符串,返回元素列表 """
items = []
count = 0
tmp = ''
for c in lst[1:-1]:
if c == '[':
count += 1
elif c == ']':
count -= 1
elif count == 0 and c == ',':
items.append(tmp)
tmp = ''
continue
tmp += c
if tmp:
items.append(tmp)
return items
@staticmethod
def return_number(var: str):
""" var为数字字符串,返回var表示的数字 """
try:
return int(var)
except ValueError:
try:
return float(var)
except ValueError:
raise InterpreterError(var + ' is not a number or identifier')
def return_var(self, var: str):
""" var为变量名,返回var的值,优先返回局部变量 """
try:
return self.local_vars[var]
except KeyError:
try:
return self.global_vars[var]
except KeyError:
raise InterpreterError(var + ' is not defined')
def get_value(self, var: str):
""" var为变量名、立即数或者数组嵌套表达式字符串,获取数组嵌套表达式的值或者变量的值或者立即数本身 """
if not var:
raise InterpreterError('invalid syntax')
if var.endswith(']'):
if var.startswith('['):
return [self.get_value(item) for item in self.parse_list(var)]
if var.endswith('[]'):
item = self.get_value(var[:-2])
if isinstance(item, list):
return len(item)
else:
return -1
var, index = self.get_array_index(var)
try:
return var[index]
except IndexError:
raise InterpreterError(str(index) + ' out of range')
if var == '@':
return self.ans
if var.isidentifier():
return self.return_var(var)
return self.return_number(var)
def set_value(self, var: str, val, global_var=False):
""" var为变量名或者数组嵌套表达式字符串,设置数组嵌套表达式的值或者变量的值为val """
if var.endswith(']'):
if var.startswith('['):
raise InterpreterError(var + ' is not a variable')
if var.endswith('[]'):
raise InterpreterError(var + ' is not a variable')
var, index = self.get_array_index(var)
try:
var[index] = val
except IndexError:
raise InterpreterError(str(index) + ' out of range')
return
if var == '@':
self.ans = val
return
if var.isidentifier():
if global_var:
self.global_vars[var] = val
else:
self.local_vars[var] = val
return
raise InterpreterError(var + ' is not a variable')
def cmp_expr(self, args: list):
""" 计算比较表达式 """
oper1 = self.get_value(args[0])
op = args[1]
oper2 = self.get_value(args[2])
if op not in {'==', '!=', '<', '<=', '>', '>='}:
raise InterpreterError(op + ' is not a cmp operator')
if not (isinstance(oper1, int) or isinstance(oper1, float)):
raise InterpreterError(str(oper1) + ' is not a number')
if not (isinstance(oper2, int) or isinstance(oper2, float)):
raise InterpreterError(str(oper2) + ' is not a number')
return eval('oper1 {} oper2'.format(op))
def cmd_at(self, args: list):
if len(args) != 1:
raise InterpreterError('AT takes 1 argument but {} were given'.format(len(args)))
self.set_value(args[0], self.pointer)
def cmd_go(self, args: list):
if len(args) != 1:
raise InterpreterError('GO takes 1 argument but {} were given'.format(len(args)))
ptr = self.get_value(args[0])
if not isinstance(ptr, int):
raise InterpreterError(str(ptr) + ' is not a int')
self.pointer = ptr
def cmd_mov(self, args: list):
if not 1 <= len(args) <= 2:
raise InterpreterError('MOV takes 1 or 2 argument(s) but {} were given'.format(len(args)))
self.set_value(args[0], self.get_value(args[1] if len(args) == 2 else '@'))
def cmd_glb(self, args: list):
if not 1 <= len(args) <= 2:
raise InterpreterError('GLB takes 1 or 2 argument(s) but {} were given'.format(len(args)))
self.set_value(args[0], self.get_value(args[1] if len(args) == 2 else '@'), global_var=True)
def cmd_if(self, args: list):
if len(args) > 1 and len(args) != 3:
raise InterpreterError('IF takes 0 or 1 or 3 argument(s) but {} were given'.format(len(args)))
if (len(args) != 3 and self.get_value(args[0] if args else '@') == 0) \
or (len(args) == 3 and not self.cmp_expr(args)):
self.pointer = self.get_matched_else_eif(self.pointer)
def cmd_else(self, args: list):
if len(args) != 0:
raise InterpreterError('ELSE takes no argument but {} were given'.format(len(args)))
self.pointer = self.get_matched_else_eif(self.pointer, elsed=True)
@staticmethod
def cmd_eif(args: list):
if len(args) != 0:
raise InterpreterError('EIF takes no argument but {} were given'.format(len(args)))
def cmd_def(self, args: list):
if len(args) < 1:
raise InterpreterError('DEF takes at least 1 argument but {} were given'.format(len(args)))
self.set_value(args[0], self.pointer, global_var=False if self.call_stack else True)
self.pointer = self.get_matched_end(self.pointer, 'DEF', 'EDEF')
def cmd_edef(self, args: list):
if len(args) != 0:
raise InterpreterError('EDEF takes no argument but {} were given'.format(len(args)))
if not self.call_stack:
raise InterpreterError('EDEF is mismatched')
self.pointer, self.local_vars = self.call_stack.pop()
def cmd_ret(self, args: list):
if len(args) > 1:
raise InterpreterError('RET takes 0 or 1 argument but {} were given'.format(len(args)))
if not self.call_stack:
raise InterpreterError('RET outside function')
if len(args) == 1:
self.set_value('@', self.get_value(args[0]))
self.pointer, self.local_vars = self.call_stack.pop()
def cmd_call(self, args: list):
if len(args) < 1:
raise InterpreterError('CALL takes at least 1 argument but {} were given'.format(len(args)))
ptr = self.get_value(args[0])
if not isinstance(ptr, int):
raise InterpreterError(args[0] + ' is not a function')
cmd, *fun_args = self.codes[ptr].strip().split(' ')
if cmd.lower() != 'def':
raise InterpreterError(args[0] + ' is not a function')
if len(args) != len(fun_args):
raise InterpreterError(
fun_args[0] + ' takes {} argument(s) but {} were given'.format(len(fun_args) - 1, len(args) - 1))
for arg in fun_args:
if not arg.isidentifier():
raise InterpreterError(arg + ' is not a variable')
self.call_stack.append((self.pointer, self.local_vars))
self.local_vars = {var: self.get_value(args[i]) for i, var in enumerate(fun_args)}
self.pointer = ptr
def cmd_loop(self, args: list):
if len(args) > 1 and len(args) != 3:
raise InterpreterError('LOOP takes 0 or 1 or 3 argument(s) but {} were given'.format(len(args)))
if (len(args) != 3 and self.get_value(args[0] if args else '@') == 0) \
or (len(args) == 3 and not self.cmp_expr(args)):
self.pointer = self.get_matched_end(self.pointer, 'LOOP', 'ELOP')
def cmd_elop(self, args: list):
if len(args) != 0:
raise InterpreterError('ELOP takes no argument but {} were given'.format(len(args)))
self.pointer = self.get_matched_end(self.pointer, 'ELOP', 'LOOP', -1) - 1
def cmd_brk(self, args: list):
if len(args) != 0:
raise InterpreterError('BRK takes no argument but {} were given'.format(len(args)))
self.pointer = self.get_matched_end(self.pointer, 'LOOP', 'ELOP')
def cmd_ctn(self, args: list):
if len(args) != 0:
raise InterpreterError('CTN takes no argument but {} were given'.format(len(args)))
self.pointer = self.get_matched_end(self.pointer, 'ELOP', 'LOOP', -1) - 1
def cmd_push(self, args: list):
if not 1 <= len(args) <= 2:
raise InterpreterError('PUSH takes 1 or 2 argument(s) but {} were given'.format(len(args)))
lst = self.get_value(args[0])
if not isinstance(lst, list):
raise InterpreterError(args[0] + ' is not a list')
lst.append(self.get_value(args[1] if len(args) == 2 else '@'))
def cmd_pop(self, args: list):
if not 1 <= len(args) <= 2:
raise InterpreterError('POP takes 1 or 2 argument(s) but {} were given'.format(len(args)))
lst = self.get_value(args[0])
if not isinstance(lst, list):
raise InterpreterError(args[0] + ' is not a list')
if len(lst) == 0:
raise InterpreterError(args[0] + ' is empty')
self.set_value(args[1] if len(args) == 2 else '@', lst.pop())
def cmd_int(self, args: list):
if len(args) != 1:
raise InterpreterError('INT takes 1 argument but {} were given'.format(len(args)))
oper = self.get_value(args[0])
if not (isinstance(oper, int) or isinstance(oper, float)):
raise InterpreterError(str(oper) + ' is not a number')
self.set_value(args[0], int(oper))
def cmd_inc(self, args: list):
if len(args) != 1:
raise InterpreterError('INC takes 1 argument but {} were given'.format(len(args)))
oper = self.get_value(args[0])
if not (isinstance(oper, int) or isinstance(oper, float)):
raise InterpreterError(str(oper) + ' is not a number')
self.set_value(args[0], oper + 1)
def cmd_dec(self, args: list):
if len(args) != 1:
raise InterpreterError('DEC takes 1 argument but {} were given'.format(len(args)))
oper = self.get_value(args[0])
if not (isinstance(oper, int) or isinstance(oper, float)):
raise InterpreterError(str(oper) + ' is not a number')
self.set_value(args[0], oper - 1)
def cmd_add(self, args: list):
if len(args) != 2:
raise InterpreterError('ADD takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', oper1 + oper2)
def cmd_sub(self, args: list):
if len(args) != 2:
raise InterpreterError('SUB takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', oper1 - oper2)
def cmd_mul(self, args: list):
if len(args) != 2:
raise InterpreterError('MUL takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', oper1 * oper2)
def cmd_div(self, args: list):
if len(args) != 2:
raise InterpreterError('DIV takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
if oper2 == 0:
raise InterpreterError('division by zero')
self.set_value('@', oper1 / oper2)
def cmd_mod(self, args: list):
if len(args) != 2:
raise InterpreterError('MOD takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
if oper2 == 0:
raise InterpreterError('modulo by zero')
self.set_value('@', oper1 % oper2)
def cmd_pow(self, args: list):
if len(args) != 2:
raise InterpreterError('POW takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', oper1 ** oper2)
def cmd_eq(self, args: list):
if len(args) != 2:
raise InterpreterError('EQ takes 2 arguments but {} were given'.format(len(args)))
oper1 = self.get_value(args[0])
oper2 = self.get_value(args[1])
self.set_value('@', 1 if oper1 == oper2 else 0)
def cmd_neq(self, args: list):
if len(args) != 2:
raise InterpreterError('NEQ takes 2 arguments but {} were given'.format(len(args)))
oper1 = self.get_value(args[0])
oper2 = self.get_value(args[1])
self.set_value('@', 1 if oper1 != oper2 else 0)
def cmd_gt(self, args: list):
if len(args) != 2:
raise InterpreterError('GT takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', 1 if oper1 > oper2 else 0)
def cmd_ls(self, args: list):
if len(args) != 2:
raise InterpreterError('LS takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', 1 if oper1 < oper2 else 0)
def cmd_ge(self, args: list):
if len(args) != 2:
raise InterpreterError('GE takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', 1 if oper1 >= oper2 else 0)
def cmd_le(self, args: list):
if len(args) != 2:
raise InterpreterError('LE takes 2 arguments but {} were given'.format(len(args)))
oper1, oper2 = self.get_operands(args)
self.set_value('@', 1 if oper1 <= oper2 else 0)
def cmd_and(self, args: list):
if len(args) != 2:
raise InterpreterError('AND takes 2 arguments but {} were given'.format(len(args)))
oper1 = self.get_value(args[0])
oper2 = self.get_value(args[1])
self.set_value('@', 1 if oper1 != 0 and oper2 != 0 else 0)
def cmd_or(self, args: list):
if len(args) != 2:
raise InterpreterError('OR takes 2 arguments but {} were given'.format(len(args)))
oper1 = self.get_value(args[0])
oper2 = self.get_value(args[1])
self.set_value('@', 1 if oper1 != 0 or oper2 != 0 else 0)
def cmd_not(self, args: list):
if len(args) != 1:
raise InterpreterError('NOT takes 1 argument but {} were given'.format(len(args)))
oper = self.get_value(args[0])
self.set_value('@', 0 if oper != 0 else 1)
2.外部模块文件moudle.py
class MoudleError(Exception):
""" 外部模块异常 """
pass
class BaseMoudle:
def __init__(self, command: str, level=1):
self.command = command
self.level = level
def run(self, args: list):
pass
class Inputer(BaseMoudle):
def __init__(self):
BaseMoudle.__init__(self, 'in')
def run(self, args: list):
if len(args) > 0:
raise MoudleError('IN takes no argument but {} were given'.format(len(args)))
ret = input()
try:
return int(ret)
except ValueError:
try:
return float(ret)
except ValueError:
raise MoudleError(ret + ' is not a number')
class Outputer(BaseMoudle):
def __init__(self):
BaseMoudle.__init__(self, 'out')
def run(self, args: list):
print(*args)
3.CPU文件cpu.py
from moudle import BaseMoudle
from interpreter import Interpreter
class Cpu:
def __init__(self, command_per_turn: int):
self.cpt = command_per_turn
self.bus = Bus()
self.interpreter = None
def boot(self, codestr: str):
codes = codestr.split('\n')
mod_cmds = {mod.command: mod.run for mod in self.bus.modules.values()}
self.interpreter = Interpreter(codes, mod_cmds)
def run(self):
return self.interpreter.run(self.cpt)
class Bus:
def __init__(self):
self.modules = {}
def install(self, moudle: BaseMoudle):
if moudle.command in self.modules.keys():
raise RuntimeError('Moudle Access Conflict: ' + moudle.command)
self.modules[moudle.command] = moudle
def uninstall(self, moudle: BaseMoudle):
if moudle.command in self.modules.keys():
del self.modules[moudle.command]
raise RuntimeError('Moudle Access Not Existent: ' + moudle.command)
4.测试文件
from cpu import Cpu
from moudle import Inputer, Outputer
code = '''
// 函数flatten可以将一个多层嵌套列表list压平成一维列表
def flatten list
mov tmp []
mov i 0
// list[] 返回list的长度,对于非列表则返回-1
loop i < list[]
if list[i][] == -1
push tmp list[i]
else
// 激动人心的递归调用
call flatten list[i]
mov res
mov j 0
loop j < res[]
push tmp res[j]
inc j
elop
eif
inc i
elop
ret tmp
edef
// 定义复杂的嵌套列表x
mov x [1,[2,[3,[4,[5],6,[[7,8],9]]]],10,11,12]
out x x[]
call flatten x
// 返回值放入特殊变量@,此时@ = [1,2,3,4,5,6,7,8,9,10,11,12]
out @ @[]
'''
cpu = Cpu(1)
cpu.bus.install(Inputer())
cpu.bus.install(Outputer())
cpu.boot(code)
count = 1
while cpu.run():
count += 1
print('\ncount: {}'.format(count))
案例代码和运行效果
1、质数输出
def isprime n
pow n 0.5
mov t
mov i 2
loop i <= t
mod n i
if @ == 0
ret 0
eif
inc i
elop
ret 1
edef
def print_prime n
mov primes []
mov i 2
loop i <= n
call isprime i
if
push primes i
eif
inc i
elop
ret primes
edef
call print_prime 1000
out @
输出:[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
2、快速排序
def Qsort array
sub array[] 1
call quick_sort array 0 @
edef
def quick_sort array low high
loop low < high
mov i low
mov j high
mov key array[low]
loop i < j
loop key <= array[j]
if i >= j
brk
eif
dec j
elop
mov array[i] array[j]
loop key >= array[i]
if i >= j
brk
eif
inc i
elop
mov array[j] array[i]
elop
mov array[i] key
sub i 1
call quick_sort array low @
add i 1
mov low
elop
edef
mov array [3,5,6,8,3,1,2,5,7,9,5,6,8,6,5]
call Qsort array
out array
输出:[1, 2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 7, 8, 8, 9]