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

上一篇博客讲到了构造语法树的问题。有朋友在留言问我,为什么一定要让语法分析器产生语法树,而不是让用户自己决定要怎么办呢?在这里我先解答这个问题。

1、大部分情况下都是真的需要有语法树

2、如果要直接返回计算结果之类的事情的话,只需要写一个visitor运行一下语法树就好了,除去自动生成的代码以外(反正这不用人写,不计入代价),代码量基本上没什么区别

3、加入语法树可以让文法本身描述起来更简单,如果要让程序员把文法单独放在一边,然后自己写完整的语义函数来让他生成语法树的话,会让大部分情况(需要语法树)变得特别复杂,而少数情况(不需要语法树)又没有获得什么好处。

尽管类似yacc这样的东西的确是不包含语法树的内容而要你自己写的,但是用起来难道不是很难受吗?

现在转入正题。这一篇文章讲的主要是构造符号表的问题。想要把符号表构造的好是一件很麻烦的问题。我曾经尝试过很多种方法,包括强类型的符号表,弱类型的符号表,基于map的符号表等等,最后还是挑选了跟Visual Studio自带的用来读pdb文件的DIA类其中的IDIASymbol(http://msdn.microsoft.com/en-us/library/w0edf0x4.aspx)基本上一样的结构:所有的符号都只有这么一个symbol类,然后包罗万象,什么都有。为什么最后选择这么做呢?因为在做语义分析的时候,其实做的最多的事情不是构造符号表,而是查询符号表。如果符号表是强类型的画,譬如说类型要一个类,变量要一个类,函数要一个类之类的,总是需要到处cast来cast去,也找不到什么好方法来在完成相同事情的情况下,保留强类型而不在代码里面出现cast。为什么语法树就要用visitor来解决这个问题,而符号表就不行呢?因为通常我们在处理语法树的时候都是递归的形式,而符号表并不是。在一个上下文里面,实际上我们是知道那个symbol对象究竟是什么东西的(譬如说我们查询了一个变量的type,那这返回值肯定只能是type了)。这个时候我们要cast才能用,本身也只是浪费表情而已。这个时候,visitor模式就不是和面对这种情况了。如果硬要用visitor模式来写,会导致语义分析的代码分散得过于离谱导致可读性几乎就丧失了。这是一个辩证的问题,大家可以好好体会体会。

说了这么一大段,实际上就是怎么样呢?让我们来看“文法规则”本身的符号表吧。既然这个新的可配置语法分析器也是通过parse一个文本形式的文法规则来生成parser,那实际上就跟编译器一样要经历那么多阶段,其中肯定有符号表:

class ParsingSymbol : public Object
{
public:
    enum SymbolType
    {
        Global,
        EnumType,
        ClassType,        // descriptor == base type
        ArrayType,        // descriptor == element type
        TokenType,
        EnumItem,        // descriptor == parent
        ClassField,        // descriptor == field type
        TokenDef,        // descriptor == token type
        RuleDef,        // descriptor == rule type
    };
public:
    ~ParsingSymbol();

    ParsingSymbolManager*            GetManager();
    SymbolType                        GetType();
    const WString&                    GetName();
    vint                            GetSubSymbolCount();
    ParsingSymbol*                    GetSubSymbol(vint index);
    ParsingSymbol*                    GetSubSymbolByName(const WString& name);
    ParsingSymbol*                    GetDescriptorSymbol();
    ParsingSymbol*                    GetParentSymbol();
    bool                            IsType();
    ParsingSymbol*                    SearchClassSubSymbol(const WString& name);
    ParsingSymbol*                    SearchCommonBaseClass(ParsingSymbol* classType);
};

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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索问题
, 符号
, 语法
, type
, 符号表
, descriptor
, 一个
, 强符号
, 配置语法
, wstring
isType
语法树构造、编译原理构造语法树、lex yacc 构造语法树、构造函数语法缺少形参、语法符号,以便于您获取更多的相关知识。

时间: 2024-09-20 18:43:04

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

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

就像之前的博客文章所说的,(主要还是)因为GacUI的原因,我决定开发一个更好的可配置轻量级语法分析器来代替之前的落后的版本.在说这个文章之前,我还是想在此向大家推荐一本<编程语言实现模式>,这的确是一本好书,让我相见恨晚. 其实说到开发语法分析器,我从2007年就已经开始在思考类似的问题了.当时C++还处于用的不太熟练的时候,难免会做出一些傻逼的事情,不过总的来说当年的idea还是能用的.从那时候开始,我为了锻炼自己,一直在实现各种不同的语言.所以给自己开发一个可配置语法分析器也是在所难免的

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

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

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

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

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

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

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

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

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

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

利用Stripes、Apache Derby和Eclipse进行无配置的J2EE开发(二)

Stripes 注释和单元测试 发现 Stripes 注释的套件,并在您的应用程序上构造单元测试. Stripes 注释 Stripes 带有以下注释: 分派注释: @UrlBinding:该注释允许 ActionBean 类生成定制 UrlBinding URL 路径.在带注释 的 UrlBinding 路径被请求时,会调用 ActionBean 类. @HandlesEvent:该绑定允许通过指定的名称调用 actionBean() 方法.默认情况下, Stripes 试图将事件名解析成 a

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

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

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

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