实现自己的脚本语言ngscript之二:语法分析
ngscript的语法分析使用的是我自己的语法分析工具parseroid。与常用cc工具(yacc、bison、javacc、antlr、etc…)不同的是,parseroid生成的不是语法分析器的源程序,而是一个parser对象,直接可以用来执行parsing。也就是说,可以由BNF在执行阶段动态生成parser。
生成parser的action table运算量还是有点大,所以在新版的parseroid里面table改成了serializable的,可以缓存下来免去生成table的过程。
实现parseroid
文法描述
parseroid使用LALR(1)文法。
parseroid使用的文法描述文件是这个样子
//starter symbol
%start <program>;
%array <statements> <param_list> <params>;
%equiv <expr> <expr1> <expr2> <expr3> <expr4> <expr5> <expr6> <expr7> <expr8> <nullable_expr>;
%filter semicolon comma;
<program> ::= <statements>;
//----------------------------------------------------------------------------
//statements is a collection of statement;
<statements> ::= NULL;
<statements> ::= <statement> <statements>;
//省略一些
<throw_exception> ::= throw <expr> semicolon => <expr>;
//再省略一些
%开头的是注记,有这几种
%start 指定一个起始符
%equiv 标记等价类,在生成语法树之后的reduce过程中,会把等价类合并
%filter 也是在reduce过程中,会把这些节点去掉
=> 标记的用处是在规约生成语法树的时候调整AST,如这句
<throw_exception> ::= throw <expr> semicolon => <expr>;
这句的意思是用throw <expr> semicolon的结构规约到<throw_exception>,但是只保存<expr>这个元素,主要是为了后面处理语法树方便和看起来更加顺眼……
实现
LALR(1)的具体细节书本上都有,就不再阐述了。
错误恢复
parseroid使用error符号错误恢复。
具体做法是写这样的产生式
<statements> ::= error semicolon <statements>;
效果是,在遇到statements中的错误之后,会把错误部分parsing为一个error节点,然后同步到semicolon的位置继续parsing。
具体过程抄一段书
1.不断的弹出栈顶,直到一个特定的状态,在该状态中error符号的动作为移进。
2.移进error符号
3.不断丢弃输入符号,直到一个特定的向前查看符号,它在当前状态,对应一个非出错的动作。
4.重新开始正常分析
语法树简化
- 合并等价类
- 删除无用的嵌套
- 删除不必要的节点
合并等价类是因为在parseroid的文法描述中,没有显式指定算符的优先级,需要使用产生式的层次来表现。
于是就有很多<expr1> <expr2>这种东西,但是他们实质上都可以当成expr来处理。
无用的嵌套是因为在<expr1>这种化为<expr>之后,可能出现如<expr> -> <expr> -> number 这种,也是可以简化的。
不必要的节点,就是左括号右括号这种。
parseroid源代码在这里
https://github.com/wssccc/parseroid.git
后面我会用lexeroid和parseroid实现ngscript的解析器。
一些分析方法的介绍
语法分析的方法有很多,简单介绍一下。
SLR(1)
编译原理的大作业就是实现SLR(1),可以处理这样的文法
S-># Ee #
Ee->Ee + Ti|Ee - Ti|Ti
Ti->Ti * Ff|Ti / Ff|Ff
Ff->( Ee )|d
Ff->sin Ff
Ff->cos Ff
当时觉得只要文法改改就能用,后来真正用的时候才发现是个坑……
书上是把它当过渡方法来讲的
Parser组合子
最开始知道这个是在
我也用Java实现了一个,因为Java没有lambda,硬是用匿名类整了出来。也是CPS的,带错误恢复。
运行起来大概是这样

不过解析器组合子的缺点也很明显,就是太难调试了。而且文法是用代码的形式在程序中描述的,太复杂之后我就被绕晕了 = =
LL(1)
LL(1)应该算是比较容易实现的,不过需要对文法做一些调整,消除左递归,提取左公因式什么的,后来写到二元运算符的文法我还是觉得LL(1)不能忍。
LALR(1)
书上介绍分析方法的顺序LALR(1)一般是排在最后的,按这种设定应该是这个最靠谱了。
附一张图:

实现自己的脚本语言ngscript之二:语法分析的更多相关文章
- 实现自己的脚本语言ngscript之零
正式开始介绍前先扯点没用的. 从小玩basic长大的小朋友大多有一个梦想,就是自己实现一个basic解释器. 不过这里我实现的不是basic,而是一个语法和功能类似javascript的东西. 暂且称 ...
- 实现自己的脚本语言ngscript之三:语法设计
这是第四篇了,之所以隔了这么久才写,一方面是因为最近开始实习了,另一方面是因为设计语法真是要考虑很多东西. 于是我去读了这本书,里面实现了两种语言,一种跟js差不多语法,用ast解释执行:另一种语法类 ...
- JavaScript脚本语言基础(二)
导读: JavaScript条件语句 JavaScript循环语句 JavaScript网页中错误捕获 JavaScript的Break和Continue命令 JavaScript的转义字符 1.Ja ...
- 实现自己的脚本语言ngscript之四:代码生成
最近的进度 ngscript测试代码 function c1(a, b, c, d) { this.a = 1; this.b = new array(); this.b[0] = 1; this.b ...
- 实现自己的脚本语言ngscript之一:词法分析
正则表达式的理论基础可以参考装配脑袋的 这个 自己动手开发编译器(二)正则语言和正则表达式 这个 自己动手开发编译器(三)有穷自动机 还有这个 自己动手开发编译器(四)利用DFA转换表建立扫描器 如果 ...
- 【程序员技术练级】学习一门脚本语言 python(二)遍历本地文件系统
这篇将讲述怎么使用python来遍历本地文件系统,并把文件按文件大小从小到大排序的一个小例子 在这个例子中,主要会用到python内置的和OS模块的几个函数: os.walk() : 该方法用来遍历指 ...
- 脚本语言:Xmas(二)
本篇,来谈谈类型系统,以及部分与垃圾收集器相关的内容. 一.基本类型 Xmas的基本类型:Null.Boolean.Label.String.Ref.Function.Integer.Float.De ...
- 全栈工程师之路(二)—— JavaScript(网页前端脚本语言)
javascript 是可以运行在网页前端的脚本语言,可以基于 html 之上实现更丰富的交互(网页内容的交互显示).异步回调.多线程.定时器.动画等. hello_world.html <ht ...
- InstallShield 脚本语言学习笔记
InstallShield脚本语言是类似C语言,利用InstallShield的向导或模板都可以生成基本的脚本程序框架,可以在此基础上按自己的意愿进行修改和添加. 一.基本语法规则 ...
随机推荐
- oracle redo日志维护
环境 OS:Red Hat Linux As 5 DB:10.2.0.1 1.添加日志组 alter database add logfile group 4 ('/u01/app/oracle/or ...
- Angularjs总结(七) 路由及请求服务等
define(['angular'], function (ng) { 'use strict'; var app = ng.module('index-module', ['ngCookies', ...
- jQuery无刷新上传学习心得
记得刚离开大学,进入目前这家公司不到一个月时,有一位前辈给我们当时的新人讲了下JS无刷新上传的相关知识. 在此之前,一直都是在使用C#提供的服务器上传控件FileUpload,但是每次使用时,都会刷新 ...
- 查看当前linux系统位数
linux系统也有位数之分,所以在linux上安装一些软件,比如jdk之类的就需要注意下版本. 查看linux系统位数最简单的命令(这里以redhat为例,不同版本linux命令也许不同) 命令1:g ...
- 【转】NHibernate入门教程
开源框架完美组合之Spring.NET + NHibernate + ASP.NET MVC + jQuery + easyUI 中英文双语言小型企业网站Demo 摘要: 热衷于开源框架探索的我发现A ...
- ubuntu14.04 开启root登陆
想要在登录界面使用root身份登录,可编辑/usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf文件, sudo gedit /usr/share/light ...
- sublime text 3 安装中文
本经验目前在Ubuntu14.04环境下,已有搜狗输入法 for Linux和Sublime Text 3的情况下安装成功. END 解决方法步骤2 1 保存下面的代码到文件sublime_imf ...
- __set($key,$values) 和__get($varName) 魔术方法设置读取私有属性
__set($key,$val) 对类内私有属性赋值 作用:对私有属性的处理 当在类外对类内的私有属性赋值时会自动调用此函数 __get($varName) 读取类内私有属性 作用:虽然可以外部访问, ...
- CSS2简写代码(优化)
[1]如果CSS属性值为0,那么你不必为其添加单位(如:px/em): 下面是你可能的写法: padding: 10px 5px 0px 0px; 但是你可能这样写: padding: 10px 5p ...
- C语言存储类型及各存储类型作用域和生存域比较
c语言中的存储类型有`auto`, `extern`, `register`,`static` 这四种,存储类型说明了该变量要在进程的哪一个段中分配内存空间,可以为变量分配内存存储空间的有数据区.BB ...