PLP: 4.2/4.3 Attribute Gramma阅读笔记3

构造AST在compiler中是否是一个必不可少的步骤? 由于attribute grammar和one-pass compiler或者说语法制导翻译(syntax-directed translation,SDT)联系很紧密,所以attribute grammar和这个问题有存在间接的的关系。

答案是构造AST并不是一个必要的步骤(可以思考构造AST的目的是什么),甚至在计算机资源极其有限的几十年前,one-pass compiler还是一个比较普遍的做法。

什么是Attribute Grammars

Attribute Grammars were invented by Donald Knuth as a way to unify all of the stages of compiling into one. They give a formal way to pass semantic information (types, values, etc.) around a parse tree.

注:再次跪拜Don Knuth大神

首先attribute grammars被提出来用于将多个编译步骤整合成一个,例如你要编译数学表达式(3+4)3(3 + 4) * 3,此时就没有必要生成AST并为其生成binary了。“attribute grammar + 语法制导”就直接将该表达式的值evaluate出来了。针对应用场景使用合适的方法才是王道,否则就可能像见识少的我一样,把特例当做通用解法使用

Attribute Grammar为何能够做到将不同的编译阶段整合成一个呢?Attribute Grammar就是在Grammar上扩充了Attriute的文法,核心就在于这些Attribute。Attribute可以是类型,可以中间代码,也可以是表达式的值。我们就可以根据这些attributed的文法,将类型检查,中间代码生成以及表达式求值融合到parser中。甚至于一些constant folding,available expressioin也可以融合进去。

Attribute Grammar的核心在于这些Attribute,而关于Attribute如何定义以及如何evaluate则视问题不同而不同

下面我摘PLP中的一个AG的示例,简单的复述一下整个过程。
EE+TE \rightarrow E+T
EETE \rightarrow E-T
ETE \rightarrow T
TTFT \rightarrow T*F
TT÷FT \rightarrow T\div F
TFT \rightarrow F
FFF \rightarrow -F
F(E)F \rightarrow (E)
FdigitF \rightarrow digit

上面的文法用于生成一个数学运算式,我们可以在其上附着一些attribute,如下所示:
1. E1E2+T1.\ E_1 \rightarrow E_2 + T
Attribute: E1.val:=sum(E2.val,T.val)\rhd E_1.val := sum(E_2.val, T.val)

2. E1E2T2.\ E_1 \rightarrow E_2 - T
Attribute: E1.val:=difference(E2.val,T.val)\rhd E_1.val := difference(E_2.val, T.val)

3. ET3.\ E \rightarrow T
Attribute: E.val:=T.val\rhd E.val := T.val

4. T1T2F4.\ T_1 \rightarrow T_2 * F
Attribute: T1.val:=product(T2.val,F.val)\rhd T_1.val := product(T_2.val,F.val)

5. T1T2÷T5.\ T_1 \rightarrow T_2 \div T
Attribute: T1.val:=quotient(T2.val,F.val)\rhd T_1.val := quotient(T_2.val,F.val)

6. TF6.\ T \rightarrow F
Attribute: T.val:=F.val\rhd T.val := F.val

7. F1F27.\ F_1 \rightarrow -F_2
Attribute: F1.val:=additiveinverse(F2.val)\rhd F_1.val := additive_inverse(F_2.val)

8. F(E)8.\ F \rightarrow (E)
Attribute: F.val:=E.val\rhd F.val := E.val

9. Fdigit9.\ F \rightarrow digit
Attribute: F.val:=digit.val\rhd F.val := digit.val

使用\rhd开头的可以称为Semantic Functions(与PLP稍有不同),针对上述AG,这些semantic functions只是简单的数学运算,它们的参数都来源于当前文法内部。

至此我们就可以知道Attriubte Grammar的作用是将***文法和一些有意义的sematic function粘和起来***。这些semantic function都是人为赋予的,我们同样可以在代码生成阶段,将sematic function替换为一段段machine code。

Evaluate Attributes

PLP将evaluate attribute称作Parse Tree的annotation(或decoration),一个简单的示例如下图所示:
示例
注:图片摘自PLP 4.3

从上图中我们可以看到整个evaluate的过程就是后序递归下降,最终我们得到结果8,这个Parse Tree不是必须的,并且运算符号的优先级已经在parse的时候隐含的处理了。

Synthesized Attributes

对于数学运算式对应的Attribute Grammar,我们可以称之为Synthesized Attributes

Syntathesized Attributes: their values are calculated (synthesized) only in productions in which their symbol appears on the left-hand side. This means that the attribute flow - the pattern in which information moves from node to node - is entirely bottom-up.

Synthesized Attributes又称之为S-attribute,龙书翻译成综合属性和S属性,注意终结符的属性值来源于scanner。

Inherited Attributes

与Synthesized Attributes对应的另外一种属性是Inherited Attributes,又称之为L属性(L意思是Left-To-Right)。

In general, we can imagine attributes whose values are calculated when their symbol is on the right-hand side of the current production. Such attributes are said to be inherited.

Inherited Attributes能够将context information从上至下,从左至右进行传递,一个比较典型的Inherited Attributes应用是Symbol Table和Type check。

下面给出一个既含有S属性又包含L属性的例子,这个例子主要用于automatic type evaluation,stst表示symbol table。

1. S::=DEC1.\ S::=DEC
Attribute:  S.st=DEC.st\rhd\ S.st=DEC.st

2. S::=S1 DEC2.\ S::=S_1\ DEC
Attribute:  S.st=S1.st+DEC.st\rhd\ S.st=S_1.st + DEC.st

3. DEC::=T L3.\ DEC::=T\ L
Attribute:  L.type=T.type;DEC.st=L.st\rhd\ L.type=T.type;DEC.st=L.st

4. T::=int4.\ T::=int
Attribute:  T.type=int\rhd\ T.type=int

5. T::=string5.\ T::=string
Attribute:  T.type=string\rhd\ T.type=string

6. L::=id6.\ L::=id
Attribute:  L.st=(id.name,L.type)\rhd\ L.st=(id.name, L.type)

7. L::=L1,id7.\ L::=L_1, id
Attribute:  L1.type=L.type;L.st=L1.st+(id.name,L.type)\rhd\ L_1.type=L.type;L.st=L_1.st + (id.name, L.type)

其中属性ststS属性,而属性typetypeL属性。对于下面的两行代码,对应的decorated tree如下所示。

int a, b, c;
string s;

在这里插入图片描述
从上图中我们可以清楚的看到,L属性typetype沿着topbottomtop\rightarrow bottomleftrightleft\rightarrow right传递,S属性stst沿着bottomupbottom\rightarrow up的方式传递。

在parse的时候这里有一个准则,
When we get to a node during parsing,

  • we must have all of the information we need to evaluate its inherited attributes.
  • Before we leave the node we must have all of the information we need to evaluate its synthesized attributes.

读书的重点在于思考和理解,否则无疑是向脑子里倾倒垃圾

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章