一直对技术有很强的兴趣,终于,决定要写自己的语言(m语言)。那就先从最简单的开始:解释执行器。

一套完整的语言包含的肯定不止解释执行器了,还要有编译器和IDE,也就还要有语法高亮、智能提示等,不过还没学会那些,先搞个最基本的解释执行器。

思路如下:

  1. 定义好希望的语法(基本语句有:顺序执行、if语句、for语句、while语句、系统自有函数定义、用户函数定义、函数调用)
  2. 找一款词法语法解析器工具,让字符串流变成语法书(AST)
  3. 编写解释执行器
    1. 元数据收集
    2. 变量作用域定义、查找
    3. 解释执行

先设想我们的m语言语法要怎么牛b啊,比如下面这段demo语法代码:

go 计算标准体重(年龄)
{
体重:年龄*;
体重;
} 体重:;
a:;
a:输出(体重);
b:;
a:++b;
输出(a);
(a==)->
{
a:a+a+a;
输出(a);
}
else
{
输出(b);
};
a:;
while a< ->{
a:a+;
输出(a);
}; 输出("WHILE OK"); repeat i from to step ->{
输出(i);
}
init->{
输出("FOR INIT");
}
onerror->{
输出("FOR ERROR");
}
finally->{
输出("FOR FINALLY");
};
输出('FOR OK'); a:;
输出(计算标准体重(a));

很显然,第一个语句块是用户函数的定义方式,以"go"字符串为函数定义的开始,接着是常规的函数名称、参数、函数方法块。

剩下的大致上就是顺序执行了,其中穿插着一些循环语句等,repeat循环自定义的比较厉害,好叼。。。感觉。。真的好叼。。。。

每个语句以封号后缀结束、赋值以冒号来标识。

接着来看看基于ANTLR的词法定义:

m.g4:

grammar m;

import basic,function,assignStmt,ifStmt,forStmt,whileStmt;

nomalStmt
:assignStmt
|ifStmt
|forStmt
|whileStmt
;
declarationStmt
:functionDeclare
;
stmt
:nomalStmt LS
|declarationStmt
; program
: stmt+
;  

由于词法语法定义较多,不贴代码了,可以下载代码看全部的(基于ideas/需要安装antlr4插件)

接下来是时候让我们load进demo代码解析成AST树啦:

String code=Utils.readTxtFile("F:\\BaiduYunDownload\\mLanguage(4)\\m_code2.m");//这个是放demo代码的文件
code=code.substring(1);//去掉第一个特殊字符 CharStream is = CharStreams.fromString(code);                 //antlr对象,读入字符串
mLexer lexer = new mLexer(is);                         //mLexer是antlr自动生成的一个词法类
CommonTokenStream tokens = new CommonTokenStream(lexer);         //antlr对象
mParser parser = new mParser(tokens);                     //mParser是antlr自动生成的一个此法解析类 mParser.ProgramContext tree=parser.program();                //program是入口规则,根规则 program program= NodeParser.parseProgram(tree);               //自己写的NodeParser类,需要一堆自定义的节点类型配合解析整棵AST树 mRuntime runtime=new mRuntime(program); runtime.plainInterpreter();                           //解释器执行 System.out.println("");

AST节点的定义:

  

demo代码构建成AST树的效果图(antlr插件中能看):

转换成为AST树后,剩下的就是编写解释执行器,其实相当于前端编译器。

主要步骤是3步:

  1. 收集元数据
  2. 定义变量作用域
  3. 语句块的解释执行
public void execute(program program) {
//1. 先扫描函数定义,收集元数据
collectMetaData(program); //2. 变量作用域
walkAST4Variables(program); //3. 解释执行代码
runCode(program);
}

1. 收集元数据,其实就是对自定义函数的收集,统一放到一个Dictionary里,以便到时候引用到了执行语句块(和参数的传递)

private void collectMetaData(program program) {
for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
if(stmt.declarationStmt!=null)
this.userDefinedFunctionSymbols.defineMethod(stmt.declarationStmt.functionDeclare.functionIdentifier.getIdentifier(), stmt.declarationStmt.functionDeclare);
} public class UserDefinedFunctionSymbols {
private Dictionary<String, functionDeclare> methods=new Hashtable<>();
public functionDeclare getMethod(String identifier) {
return methods.get(identifier);
} public void defineMethod(String identifier, functionDeclare ast) {
methods.put(identifier, ast);
}
}

functionDeclare是具体的node,属于AST中众多节点类型中的一种,代表函数声明节点。

2. 定义变量作用域,由于存在函数(自定义函数、系统自带函数),因此需要有变量Scope的概念,存在局部变量覆盖全局变量现象

private void walkAST4Variables(program program)
{
program.VariableSymbols=globalVariableSymbol;
for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
{
stmt.VariableSymbols=program.VariableSymbols;
if(stmt.declarationStmt!=null)
{
stmt.declarationStmt.VariableSymbols=stmt.VariableSymbols;
VarWalker.walk(stmt.declarationStmt);
} if(stmt.nomalStmt!=null)
{
stmt.nomalStmt.VariableSymbols=stmt.VariableSymbols;
VarWalker.walk(stmt.nomalStmt);
}
}
} public class VariableSymbol {
private Dictionary<String, Variable> variables=new Hashtable<>();
private VariableSymbol parentVariableSymbol; public void setParentVariableSymbol(VariableSymbol parentVariableSymbol)
{
this.parentVariableSymbol=parentVariableSymbol;
} public void defineVariable(String name, Variable variable) {
variables.put(name, variable);
} public void setValue(String name, Object value)
{
Variable variable=getVariable(name);
variable.Value=value;
}
public Object getValue(String name)
{
Variable variable=getVariable(name);
return variable.Value;
} private Variable getVariable(String name) {
List<String> keys=Collections.list(variables.keys());
if(keys.contains(name))
return this.variables.get(name); if(this.parentVariableSymbol!=null)
return this.parentVariableSymbol.getVariable(name); throw new RuntimeException("变量未定义");
}
}  

当局部变量中没有找到本地变量定义时,会根据parent关联向上找变量,直到为null。

3. 语句块的解释执行,这个可以说是最容易理解的地方了

private void runCode(program program) {
StmtExecutor executor=new StmtExecutor(this); for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
if(stmt.nomalStmt!=null)
executor.execute(stmt.nomalStmt);
}

StmtExecutor.execute(nomalStmt)会调用一系列子语句,如下图就一图就懂:

如上图中,针对expression是调用calc的,一堆calc,expression中套expression。

system built-in函数的定义,是通过NativeMethodNode.setCode来标识的,比如当前实现的code为OUTPUT,功能如下:System.out.print/Console.Write()

第一个红框是native node中判断code是哪个system built-in函数的编码代号

第二个红框是对应built-in函数的java语句执行。

demo m代码对应的解释执行输出:

10
135
405
3
5
7
9
11
WHILE OK
FOR INIT
0
10
20
30
40
50
60
70
80
90
100
FOR FINALLY
FOR OK
30
ok  

代码下载(基于java)

自己写编程语言-m语言的更多相关文章

  1. atitit.编程语言 程序语言 的 工具性 和 材料性 双重性 and 语言无关性 本质

    atitit.编程语言 程序语言 的 工具性 和 材料性 双重性 and 语言无关性 本质 #---语言的 工具和材料双重性 有的人说语言是个工具,有的人说语言是个材料..实际上语言同时属于两个属性. ...

  2. 中文编程语言Z语言开源正式开源!!!

    (Z语言基于.NET环境,源码中有很多高技术的代码,让更多的人知道对大家有会有很好的帮助,请管理员一点要批准放在首页) 本人实现的中文编程语言Z语言现在正式开源,采用LGPL协议. 编译器核心的网址为 ...

  3. 如何写好 C语言 main 函数!你准备好编写 C 程序了吗?

    学习如何构造一个 C 文件并编写一个 C main 函数来成功地处理命令行参数.   我知道,现在孩子们用 Python 和 JavaScript 编写他们的疯狂"应用程序".但是 ...

  4. 苹果新的编程语言 Swift 语言进阶(六)--函数和闭包

    一 .函数 1.1. 函数的定义和调用 函数的定义以funckeyword作为前缀,接着是函数名字,接着跟着一个能够带有參数.也能够不带參数的圆括号.接着用-> 指示函数的返回类型. 函数运行体 ...

  5. 写给C语言新手的话

    首先声明啊,写这个是因为一些加我QQ的朋友问我学习经验,我才写的. 另外,如果是二级党,那么请用谭浩强老师的书.然后你就可以不用看了.倒不是有偏见,而是我写的这个东西,根本不是为了考试,而是为了和新手 ...

  6. 苹果新的编程语言 Swift 语言进阶(十一)--实例的初始化与类的析构

    一 .实例的初始化          实例的初始化是准备一个类.结构或枚举的实例以便使用的过程.初始化包括设置一个实例的每一个存储属性为一个初始值,以及执行任何其它新的实例能够使用之前需要的设置或初始 ...

  7. 苹果新的编程语言 Swift 语言进阶(八)--属性

    属性是特定类.结构或枚举的相关值,属性根据作用域不同分为实例属性与类型属性,还可以根据是否存储分为存储属性和计算属性. 1.1 实例属性 为一个类.结构或枚举定义的属性默认属于实例属性,即该属性属于为 ...

  8. 写Python脚本语言时,“SyntaxError: Non-ASCII character '\xe6' in file”错误的处理

    如图是一段简单的文件操作代码,以为打开文件"test1221_1.py",若不存在此文件,则创建此文件.接下来并输出相关信息. 当少写了黄色方框圈起来的一行看似不经意的带有编码格式 ...

  9. 苹果新的编程语言 Swift 语言进阶(五)--控制流

    Swift 语言支持C语言全部的控制语句.包含for  和while循环语句,if和switch条件语句,以及break和continue控制语句等. Swift 语言除了支持以上语句,还添加了一个f ...

随机推荐

  1. Dubbo 新编程模型之外部化配置

    外部化配置(External Configuration) 在Dubbo 注解驱动例子中,无论是服务提供方,还是服务消费方,均需要转配相关配置Bean: @Bean public Applicatio ...

  2. 【Luogu1373】小a和uim之大逃离(动态规划)

    [Luogu1373]小a和uim之大逃离(动态规划) 题面 题目背景 小a和uim来到雨林中探险.突然一阵北风吹来,一片乌云从北部天边急涌过来,还伴着一道道闪电,一阵阵雷声.刹那间,狂风大作,乌云布 ...

  3. mysql5.6版本备份报错

    MySQL5.6版本备份报错,密码不安全 [root@centos199 mysql]# mysqldump -uroot -ppassword cz-office > mysql38.sqlW ...

  4. 论文学习:Fully Convolutional Networks for Semantic Segmentation

    发表于2015年这篇<Fully Convolutional Networks for Semantic Segmentation>在图像语义分割领域举足轻重. 1 CNN 与 FCN 通 ...

  5. javascript 特殊的面向对象以及继承详解(入门篇)

    学习Javascript人,大多听说一句话叫js里面一切都是对象.我刚开始接触javascript面向对象编程时候,挺乱的,我当时习惯性的把PHP的面像对象思想套用在js上面,其实js的面向对象与传统 ...

  6. win8快捷键

    win+Q/S搜索所有位置 win+W搜索设置 win+E文件资源管理器 win+R运行 win+T选中第一个应用程序(不确定) win+U轻松使用设置中心 win+I设置 win+P投影 win+D ...

  7. python 全栈开发,Day2(正式)

    一.in的使用 in 操作符用于判断关键字是否存在于变量中 a = '男孩wusir' print('男孩' in a) 执行输出: True in是整体匹配,不会拆分匹配. a = '男孩wusir ...

  8. FTP站点设置

    0x00前言: 应老师今天教的和题目所需 有了今天的博文 0x01准备: windows server 2008 FTP服务 0x02正文: 1.先安装FTP服务 先打开--服务器管理 点击--添加角 ...

  9. conda下载速度慢——添加源

    清华提供的anaconda镜像,使用以后真的很快!尤其在学校龟速的网络环境里提速非常明显. https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ TU ...

  10. Ubuntu下sudo命令出现无法解析主机名

    替换hosts文件后sudo命令提示 无法解析主机名 把hosts文件中127.0.0.1后的名字改为主机名,即 /etc/hostname 中的名字