Y语言v2.0、Python——设计一门更复杂的解释型编程语言并实现解释器

背景

最近又玩了遍《人力资源机器》和《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配对使用,用于ifelse的跳转定位,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]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值