本篇笔记是我对龙书第2.3-2.5节内容的理解,主要介绍编译器前端关于语法分析的众多基础概念。下篇笔记将根据本篇笔记的主要内容,实现一个针对简单表达式的后缀式语法翻译器Demo(原书中是java实例,我给出的将是逻辑一致的Python版本的实现)。
1. 语法分析(Syntax Analysis)
简单来说,语法分析的任务是分析输入的符号字符串(string of symbols, 通常是词法分析产生的tokens)是否遵循某种语言在其上下文无关文法(context-free grammar)中给出的规则,其中,规则由一系列产生式(production)给出。
如果分析成功,则它会构造出程序的抽象语法树(Abstract syntax tree http://en.wikipedia.org/wiki/Abstract_syntax_tree);否则它会抛出类似于"syntax error"的错误提示。
完成语法分析的程序在术语上被称为Parser,其任务是判决输入的token streams是否可以由该语言的文法的开始符号推导出来以及如何推导。
原则上parser必须具备为输入串构造出语法分析树的能力(虽然事实上,编译器在实现语法分析时是不会真正去构造完整的语法分析树的,它会构造更简洁的抽象语法树)。
Paser有两类常见实现方式:
1) top-down parsing
自顶向下类型的parser从根节点开始构造语法分析树,逐步向叶子节点方向进行。这种方法可以较容易地手工构造出高效的语法分析器。
与此类parser相关的常见术语有inherited attributes和LL parser。
2) bottom-up parsing
自底向上的方法从叶子节点开始,逐步构造出根节点。这种方法可以处理更多种文法和翻译计划,故直接从文法生成语法分析器的软件工具通常采用bottom-up的方法。
与此类parser相关的常见术语有synthesized attributes和LR parser。
关于目前业界常见的parsers(如LL-parser/LR-parser)的总结,可以参考wikipedia的parser条目。
2. 语法制导翻译(Syntax-Directed Translation, SDT)
语法制导翻译指的是一个转换过程,其目的是将输入的字符串翻译成一系列具有语义的动作,这是通过向文法产生式附加规则或程序片段来实现的。SDT为向语法附加语义动作(semantic actions)提供了一个简单的方法。
SDT有两个重要的相关概念:语法制导定义(Syntax-Directed Definition, SDD)和语法制导翻译计划(Syntax-Directed Translation Scheme, SDTS)。
2.1 语法制导定义(SDD)
SDD是一种特殊的context-free grammar,其特殊之处在于它为每个文法符号(grammar symbol)附加了属性(如符号的数据类型、指令数目、指令位置等),并且为这些文法符号构成的产生式附加了语义规则(semantic rules),其中语义规则定义了产生式中的符号的关联属性值的求值规则。下面是语法制导定义的示例,它为左侧的产生式附加了右侧的语义规则。
2.2 语法制导翻译计划(SDTS)
SDTS也是一种特殊的context-free grammar,其特殊之处在于它在产生式体中嵌入了被称为语义动作(semantic actions)的程序片段。语法制导翻译计划与语法制导定义相似,只是显式指定了语义规则的计算顺序。下面是语法制导翻译计划的示例,它在产生式中嵌入了语义动作。
2.3 SDD和SDTS的区别与联系
Stanford某CS课程关于龙书的Lecture Notes对这两个概念的区别描述是:
SDDs are useful for specifying translations.
SDTSs are useful for implementing translations.
即语法制导定义(SSDs)指定附加在产生式上的语义规则;而语法制导翻译计划(SDTSs)在合适的时机(通常是根据产生式推导输入的token流期间)实现了这些语义动作。
根据wikipedia关于Syntax-directed translation的介绍,当采用bottom-up parsing的方法且SDD采用S-attributed文法时,SDD实现起来最简单。此时,可以通过在产生式体最右边嵌入语义动作的方式来构造语法制导翻译计划,这些动作在产生式被推导时执行。这种把所有的语义动作嵌入在产生式最右的SDT被称为后缀(postfix)翻译计划。
3. 具体语法树(Concrete Syntax Tree, CST)和抽象语法树(Abstract Syntax Tree, AST)
具体语法树CST其实就是语法分析树(常简称为parse tree),如下图是龙书第2.2.3节给出的关于表达式"9-5+2"在产生式为"list -> list + digit"时的语法分析树。
在具体语法树中,树的内部节点表示非终结符,叶子节点全部为终结符,这些终结符构成了可以由对应的产生式推导出来的输入串。
抽象语法树AST是一种数据结构,在表达式的AST中,每个内部节点代表一个操作符,而节点的子节点代表该操作符的操作数。下图是龙书第2.5.1节给出的关于表达式"9-5+2"在产生式为"list -> list + digit"时的抽象语法树。
对比AST和CST可知,AST省略了很多出现在CST中的辅助符号,这使得AST显得很简洁,在编译器的实现语法分析时,处理AST显然更高效。
值得注意的是,CST只是个概念上的语法树,它在原则上保证了编译器对源码文件的无歧义语法分析,不过编译器实现语法分析时,CST显得过于冗余,所以,CST的简化版本AST才是大部分编译器在语法分析时真正构建的数据结构。
关于CST和AST的更多讨论,可以参考StackOverflow的这篇帖子What is the difference between an Abstract Syntax Tree and a Concrete
Syntax Tree?或参考这篇文章Abstract vs. Concrete Syntax Trees。
【参考资料】
1. 龙书第2.3-2.5节
2. Stanford Lecture Notes for DragonBook:
Syntax-Directed Definitions
3. wikipedia: Types of parsers
4. wikipedia: Attribute grammar (Inherited-Attributes && Synthesized-Attributes)
5. wikipedia: Syntax-directed translation
6. StackOverflow: What is the difference between
an Abstract Syntax Tree and a Concrete Syntax Tree?
7. Abstract vs. Concrete Syntax Trees
========================== EOF =========================