文法产生式

语法和语义的区别

  • 语法:描述该语言的程序的正确形式
  • 语义:定义了程序的含义,即每个程序在运行时做什么

抽象语法树和三地址指令

三地址指令可以理解为只有3个成分的指令:2个操作数和一个操作符,最多执行一个操作。恰好对应一颗二叉树的2个子节点和其父亲节点。

抽象语法树如下:

2.1

笔记后续更新,可以关注github https://github.com/dslu7733/Compilers

2.1

对于抽象语法树的“翻译”,是从叶子节点开始向上翻译的。


文法产生式

->表示推导,可由前者推导出后者,也可以理解为某种“等价替换”

  • 加减运算的文法产生式 (且该文法是左递归的,且仅针对个位数加减)
//list叫开始符,总是从它开始
list -> list + digit
list -> list - digit
list -> digit
digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

左递归指,该文法可以一直向左边推导,等价成一颗不断向左子树延申的抽象语法树。

  • 二义性

同样,对于上面的例子,可能有人会想到下面的文法生成式

string -> string + string
string -> string - string
string -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

这种文法的缺点在于它对应的语法树是二义性的,即存在2颗不同的语法树对应着同一句话,在某些情况下,这是含糊的。

2.1

2.2.png

但是细心可以发现这2颗树还是有所不同的,前一颗是左递归,后一颗是右递归。可以人为的赋予一层解释给它们,子树的运算级别更高,即添加了一层“隐性的括号”。

于是对于前一颗语法树,它将被翻译为 (9-5)+2 = 6
后一颗语法树被翻译为 9-(5+2) = 2

  • 左结合与右结合

看了上面对于左递归和右递归的解释,人们会想第一个例子能不能写成右递归呢?答案是可以的

list -> digit suffix | digit suffix
suffix -> - digit suffix | + digit suffix | epsilon
digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    
//epsilon表示空字符串, ’|‘表示‘或’

那它们有什么区别吗?就像上面那个string的例子,解释不同,对于右递归的文法,是右结合的。

例如加减运算是左结合的,所以3+2-1=(3+2)-1; 而等号是右结合的,所以a=b=2等同于a=(b=2)

  • 函数调用中参数列表的文法产生式
call -> id (optparams)
optparams -> epsilon | params
params -> params , param | param

上面的参数列表文法产生式中用id表示标识符,epsilon表示空字符串,param表示形参。
这个文法产生式某种意义上来说是”不完整“的, 因为它没有将id与params推导到字符级别,但有时候为了方便理解,就这么写了。

除此之外,文法产生式是很严谨的,连参数之间的逗号和函数调用的括号都要考虑在内,因为param仅代表一个字符串,不包含逗号在内。

  • 运算符的优先级

到现在可以看到文法生成式算得上是一种严谨的”数学推导“了,而且它还能表示运算符号的优先级。

考虑
+-:左结合
*/:左结合
优先级 */ > +-

expr -> expr - term | expr + term | term
term -> term * factor | term / factor | factor
factor -> digit | (expr)

语法树的翻译是从叶子节点开始的,因此可以区分出优先级,子树的运算优先级比其父节点高。

factor是最小的运算单位,表示不能被其它任何运算符分开的表达式。

上面这个文法可以实现*/优先级比+-高,但是他也能表示2 *(1+3)这类语句。

  • 后缀表达式

上面的文法都是中缀形式,后缀可以写成E -> E E op | digit


C语句的子集的文法

说了这么多,来看一个涉及C语言部分语句的文法

stmt -> id = expression;
	  | if(expression) stmt
      | if(expression) stmt else stmt
      | do stmt while (expression);
      | while(expression) stmt
      | {stmts}
stmts -> stmts stmt | epsilon

上面用id表示标识符,expression表示实际程序语句。注意这个文法中的分号的使用,非常严谨,不多不少。

原文地址:https://www.cnblogs.com/friedCoder/p/12762089.html