自制动态语言Medusa的解释器

今天整理了一下学习编译原理时,实现语言Medusa时写的笔记,搞出了一篇类似说明文档的东西,介绍下Medusa


语法部分

Medusa是一门动态脚本语言,语法和代码格式借鉴Python,运行代码在顶层书写。

Medusa不识别':' ';'等符号,但识别white spcae和换行符:
1 + -9:执行结果是-8
1 + - 9:语法分析错误

语句Statement暂分func defination、if、while、return、expression几类。
若干statements组成block,由'{'、'}'分隔,block是def、if、while句式 的主体结构
if expression { statements } else { statements }
while expression { statements }
def name( paras ) { statements }

顶层代码定义的变量将放入global  environment 中,block的执行将有自己的local environment,并且local env带有指向父环境的指针

函数定义以'def'关键词起始,不支持在block中定义函数,所有的函数由func_list列表保存

Medusa带有简单的报错处理,错误信息有行号、字符位置、出错记号流以及提示,水平有限,不支持错误校正、预测处理,报错后即终止程序。


内部设计和结构

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 -> returnexp 
               | exp
              | ifexp {statements} else {statements}
              | whileexp {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*[] );
env维护的是一个 “pair<string(变量名),  MdsObject* >” 的数组;
表达式“c = a+b”的执行如下:在local env中寻找关键字“a”、“b”,提取变量值,即 MdsObject的指针,计算两个对象的相加值,并利用结果的hash值在 int_map、string_map、list_map寻找对象,没有则新建并插入,最后将env中关键字“c”项的内容写为结果对象的地址。

gc:
Medusa支持对象的垃圾回收,mark&sweep的起始点为全局环境、函数局部环境链表、用于运算过程的stack容器中的对象(保存临时变量)。gc触发时机是每10次内存申请操作时。




你可能感兴趣的:(C++,动态类型,编译原理,medusa)