顶层代码定义的变量将放入global environment中,block的执行将有自己的local environment,并且local env带有指向父环境的指针
函数定义以‘def‘关键词起始,目前不支持在block中定义函数,所有的函数由func_list列表保存,支持内置函数定义,目前print是唯一的内置函数。
medusa带有简单的报错处理,错误信息有行号、字符位置、出错记号流以及prompt,水平有限,不支持错误校正、预测处理,报错后即终止程序。
内部设计和结构
interpreter:
解释器类,包含解释过程中需要的绝大数的数据结构,如记号流、函数表、全局环境、stack等等。init过程完成初始化工作,并向函数表中添加内置函数,目前内置函数仅有print函数
词法分析:
使用自己写的正则表达式引擎(支持基本的正则符号:‘*‘ ‘+‘ ‘|‘ ‘(‘和‘)‘),构建状态机输出,状态转移矩阵。词法scaner对源程序文本做检查,输出记号流,遇到不符合正则规则的字符串,报错并exit。
构造状态机的算法:首先将正则的中缀形式转换成后缀形式,例如a|b|c|d变为a b c d | | |,使用栈完成不确定有限状态自动机的构造,再利用闭包算法将NFA转换为DFA。
语法分析:
过程没有使用yacc、bison等自动生成器,使用递归下降算法手写而成,程序的上下文无关语法表示如下:
proc -> def id(id...){ statements }
|statement
statement -> return
exp
| exp
| if
exp {statements} else {statements}
| while
exp {statements}
表达式exp使用算术运算符、比较运算符、赋值运算符连接callfunc(函数调用)、variable(变量)、literal(字面常量)等节点,在语法分析模块中的表示形式是AST。Medusa 没有bool型变量,不同于C、c++中可将数值型转换为bool型,if、while句子只判别exp结果非0和0。语法分析过程还将向函数表添加自定义函数
语义分析:
由于Medusa是一门动态类型的语言,变量不需要声明,直接定义并使用,所以传统语义分析中的类型检查部分实现较难,解释器将此功能化为执行期执行,而变量符号表也随之移到执行期间建立
执行:
execute.cc为执行过程代码,执行statements,最重要的exp执行过程是后序遍历AST树,递归调用求解表达式的值。
执行过程解释器还将完成垃圾回收工作,gc算法是mark&sweep,
符号表以 pair<string,MdsObject*>形式表示了程序上下文, 多个env对象可区分程序的作用域,每个env对象都有parent域指向父环境,最顶层的env就是global env,当前作用域没有变量声明时,搜索上层环境;
stack用于保存exp计算时的临时值,这些值没有变量名。
内存分配:
采用两级分配器模型,最底层是封装了malloc、free函数的内存池,用于向内核申请大块内存;次级分配器是对象分配器,维护了多条空闲区域链表,链表放置了大小8、16、24...126字节的空闲区域,解释器空间申请将从这里得到满足,空闲区域不足时将向内存池申请新的空间。分配的内存块前置一个BlockMeta类,放置mark标志位和大小信息。
(内存模块是为了个人理解内存模型和分配策略而造的轮子,实际上malloc、free即可,这里说明一下)
变量模型:
Medusa的类型舍弃了C、JAVA中的原始类型设计,变量的值不在放在变量内,而是参考了python,采用引用模型。Medusa包含三个内置类型,int、string、list,为了减少工作量,省去了float、bool、long等类型。三种内置类型分别对应MdsIntObject、MdsStringObject、MdsListObject三个类,继承自MdsObject,利用多态实现不同的调用以及提供统一的上层接口。
所有的值对象在内存中都只有唯一的拷贝,并且放入int_map、string_map、list_map三张表中,表的索引分别是对象值的hash值(int、char*、void*[])。