可配置语法分析器开发纪事(一) 构造语法树

就像之前的博客文章所说的,(主要还是)因为GacUI的原因,我决定开发一个更好的可配置轻量级语法分析器来代替之前的落后的版本。在说这个文章之前,我还是想在此向大家推荐一本《编程语言实现模式》,这的确是一本好书,让我相见恨晚。

其实说到开发语法分析器,我从2007年就已经开始在思考类似的问题了。当时C++还处于用的不太熟练的时候,难免会做出一些傻逼的事情,不过总的来说当年的idea还是能用的。从那时候开始,我为了锻炼自己,一直在实现各种不同的语言。所以给自己开发一个可配置语法分析器也是在所难免的事情了。于是就有:    

第一版:http://hi.baidu.com/geniusvczh/archive/tag/syngram%E6%97%A5%E5%BF%97

第二版:http://www.cppblog.com/vczh/archive/2009/04/06/79122.html

第三版:http://www.cppblog.com/vczh/archive/2009/12/13/103101.html

还有第三版的教程:http://www.cppblog.com/vczh/archive/2010/04/28/113836.html

上面的所有分析器都致力于在C++里面可以通过直接描述文法和一些语义行为来让系统可以迅速构造出一个针对特定目的的用起来方便的语法分析器,而“第三版”就是到目前为止还在用的一个版本。至于为什么我要做一个新的——也就是第四版——之前的文章已经说了。

而今天,第四版的开发已经开始了有好几天。如果大家关心进度的话,可以去GacUI的Codeplex页面下载代码,然后阅读Common\Source\Parsing下面的源文件。对应的单元测试可以在Common\UnitTest\UnitTest\TestParsing.cpp里找到。

于是今天就说说关于构造语法树的事情。

用C++写过parser的人都知道,构造语法树以及语义分析用的符号表是一件极其繁琐,而且一不小心就容易写出翔的事情。但是根据我写过无穷多棵语法树以及构造过无穷多个符号表以及附带的副作用,翔,啊不,经验,做这个事情还是有一些方法的。

在介绍这个方法之前,首先要说一句,人肉做完下面的所有事情是肯定要疯掉的,所以这一次的可配置语法分析器我已经决定了一定要TMD写出一个生成语法树的C++代码的工具。

一颗语法树,其实就是一大堆互相继承的类。一切成熟的语法树结构所具有的共同特征,不是他的成员怎么安排,而是他一定会附带一个visitor模式的机制。至于什么是visitor模式,大家请自行参考设计模式,我就不多说废话了。这一次的可配置语法分析器是带有一个描述性语法的。也就是说,跟Antlr或者Yacc一样,首先在一个文本文件里面准备好语法树结构和文法规则,然后我的工具会帮你生成一个内存中的语法分析器,以及用C++描述的语法树的声明和实现文件。这个描述性语法就类似下面的这个大家熟悉到不能再熟悉的带函数的四则运算表达式结构:

class Expression
{
}

class NumberExpression : Expression
{
    token value;
}

class BinaryExpression : Expression
{
    enum BinaryOperator
    {
        Add,
        Sub,
        Mul,
        Div,
    }

    Expression firstOperand;
    Expression secondOperand;
    BinaryOperator binaryOperator;
}

class FunctionExpression : Expression
{
    token functionName;
    Expression[] arguments;
}

token NAME = "[a-zA-Z_]/w*";
token NUMBER = "/d+(./d+)";
token ADD = "/+";
token SUB = "-";
token MUL = "/*";
token DIV = "//";
token LEFT = "/(";
token RIGHT = "/)";
token COMMA = ",";

rule NumberExpression Number
        = NUMBER : value;

rule FunctionExpression Call
        = NAME : functionName "(" [ Exp : arguments { "," Exp : arguments } ] ")";

rule Expression Factor
        = !Number | !Call;

rule Expression Term
        = !Factor;
        = Term : firstOperand "*" Factory : secondOperand as BinaryExpression with { binaryOperator = "Mul" };
        = Term : firstOperand "/" Factory : secondOperand as BinaryExpression with { binaryOperator = "Div" };

rule Expression Exp
        = !Term;
        = Exp : firstOperand "+" Term : secondOperand as BinaryExpression with { binaryOperator = "Add" };
        = Exp : firstOperand "-" Term : secondOperand as BinaryExpression with { binaryOperator = "Sub" };

本栏目更多精彩内容:http://www.bianceng.cn/Programming/cplus/

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, token
, 语法
, 语法分析
, 文本 编辑器 语义 c++
, expression
, 一个
配置语法
语法树构造、编译原理构造语法树、lex yacc 构造语法树、语法树、抽象语法树,以便于您获取更多的相关知识。

时间: 2024-10-26 03:02:09

可配置语法分析器开发纪事(一) 构造语法树的相关文章

可配置语法分析器开发纪事(四) 构造一个真正能用的状态机(上)

本来说这一篇文章要把构造确定性状态机和look ahead讲完的,当我真正要写的时候发现东西太多,只好分成两篇了.上一篇文章说道一个基本的状态机是如何构造出来的,但是根据第一篇文章的说法,这一次设计的文法是为了直接构造出语法树服务的,所以必然在执行状态机的时候就要获得构造语法树的一切信息.如果自己开发过类似的东西就会知道,类似LALR这种东西,你可以很容易的把整个字符串分析完判断他是不是属于这个LALR状态机描述的这个集合,但是你却不能拿到语法分析所走的路径,也就是说你很难直接拿到那颗分析树.没

可配置语法分析器开发纪事(二) 构造符号表

上一篇博客讲到了构造语法树的问题.有朋友在留言问我,为什么一定要让语法分析器产生语法树,而不是让用户自己决定要怎么办呢?在这里我先解答这个问题. 1.大部分情况下都是真的需要有语法树 2.如果要直接返回计算结果之类的事情的话,只需要写一个visitor运行一下语法树就好了,除去自动生成的代码以外(反正这不用人写,不计入代价),代码量基本上没什么区别 3.加入语法树可以让文法本身描述起来更简单,如果要让程序员把文法单独放在一边,然后自己写完整的语义函数来让他生成语法树的话,会让大部分情况(需要语法

可配置语法分析器开发纪事(六) 构造一个真正能用的状态机(下)

上一篇文章对大部分文法都构造出了一个使用的状态机了,这次主要来讲右递归的情况.右递归不像左递归那么麻烦,因为大部分右递归写成循环也不会过分的让语法树变得难以操作,不过仍然有少数情况是我们仍然希望保留递归的语法树形状,譬如C++的连等操作,因此这里就来讲一下这个问题. 右递归是怎么形成的呢?在这里我们先不想这个问题,我们来看一个普通的文法.在上一篇文章我们已经说过了,如果一条文法有一个非终结符引用了另一条文法,那么就要做一条shift和reduce来从这个状态机穿插到那个状态机上: 开发纪事(六)

可配置语法分析器开发纪事(五) 构造一个真正能用的状态机(中)

上一篇博客写到了如何给一个非终结符的文法规则构造出一个压缩过的下推状态机,那么今天说的就是如何把所有的文法都连接起来.其实主要的idea在(三)和他的勘误(三点五)里面已经说得差不多了.但是今天我们要处理的是带信息的transition,所以还有一些地方要注意. 所以在这里我们先把几条文法的最后的状态机都列出来(大图): 开发纪事(五) 构造一个真正能用的状态机(中)-语法树构造"> 本栏目更多精彩内容:http://www.bianceng.cn/Programming/cplus/

可配置语法分析器开发纪事(三) 生成下推自动机

上一篇博客讲到了构造符号表的事情.构造完符号表之后,就要进入语义分析的后一个阶段了:构造状态机.跟我以前写的如何实现正则表达式引擎的两篇文章讲的一样,自动机先从Epsilon Nondeterministic Automaton开始,然后一步一步构造成Deterministic Automaton.但是语法分析和正则表达式有很大不同,那么这个自动机是什么样子的呢? (对学术感兴趣的人可以去wiki一下"下推自动机") 下推自动机和有限自动机的区别是,下推自动机扩展成普通的自动机的时候,

可配置语法分析器开发纪事(三点五) 生成下推自动机的具体步骤

刚刚发了上一篇文章之后就发现状态机画错了.虽然LiveWriter有打开博客并修改文章的功能,不过为了让我留下一个教训,我还是决定发一篇勘误.这个教训就是,作分析的时候不要随便"跳步",该一步一步来就一步一步来.其实人呢,就是很容易忘掉以前的教训的了.第一个告诉我不能这么干的人其实是小学三年级的数学老师.当时我因为懒得写字,所以计算应用题的时候省了几步,被批评了. 故事就从状态机开始.文法我就不重复了,见上一篇文章.现在我们从状态机开始.第一个状态机是直接从文法变过来的: 开发纪事(三

WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发

今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们来详细学习WCF服务元数据交换的相关内容.WCF服务元数据究竟是什么?为什么WCF服务要暴露元数据交换节点?这些和以前的Web Service有什么关系?WCF服务元数据交换的方式有那些?我们如何实现WCF服务元数据交换,本节我们会详细讲解.全文结构如下:[1]WCF服务元数据的基本概念.[2]WC

《编译与反编译技术》—第3章3.5节语法分析器的生成器

3.5 语法分析器的生成器 本节介绍一个语法分析器的自动产生工具YACC(Yet Another Compiler-Complier),YACC通过输入用户提供的语言的语法描述规格说明,基于LALR(1)语法分析的原理,自动构造一个该语言的语法分析器.YACC源程序又称YACC规格说明,同LEX源程序类似,也由说明部分.翻译规则和辅助过程三部分组成,形式如下: [说明部分] %% 翻译规则 [%% 辅助过程] 其中,用方括号括起来的部分可以省略,但是翻译规则部分不能省略.下面通过一个例子来说明Y

《ANTLR 4权威指南》——2.2 实现一个语法分析器

2.2 实现一个语法分析器 ANTLR工具依据类似于我们之前看到的assign的语法规则,产生一个递归下降的语法分析器(recursive-descent parser).递归下降的语法分析器实际上是若干递归方法的集合,每个方法对应一条规则.下降的过程就是从语法分析树的根节点开始,朝着叶节点(词法符号)进行解析的过程.首先调用的规则,即语义符号的起始点,就会成为语法分析树的根节点.在前一节的例子中,就是调用stat()方法作为起始点的.这种解析过程的更广为人知的名字是"自上而下的解析"