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 (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的示例,简单的复述一下整个过程。
E → E + T E \rightarrow E+T E→E+T
E → E − T E \rightarrow E-T E→E−T
E → T E \rightarrow T E→T
T → T ∗ F T \rightarrow T*F T→T∗F
T → T ÷ F T \rightarrow T\div F T→T÷F
T → F T \rightarrow F T→F
F → − F F \rightarrow -F F→−F
F → ( E ) F \rightarrow (E) F→(E)
F → d i g i t F \rightarrow digit F→digit
上面的文法用于生成一个数学运算式,我们可以在其上附着一些attribute,如下所示:
1. E 1 → E 2 + T 1.\ E_1 \rightarrow E_2 + T 1. E1→E2+T
Attribute: ⊳ E 1 . v a l : = s u m ( E 2 . v a l , T . v a l ) \rhd E_1.val := sum(E_2.val, T.val) ⊳E1.val:=sum(E2.val,T.val)
2. E 1 → E 2 − T 2.\ E_1 \rightarrow E_2 - T 2. E1→E2−T
Attribute: ⊳ E 1 . v a l : = d i f f e r e n c e ( E 2 . v a l , T . v a l ) \rhd E_1.val := difference(E_2.val, T.val) ⊳E1.val:=difference(E2.val,T.val)
3. E → T 3.\ E \rightarrow T 3. E→T
Attribute: ⊳ E . v a l : = T . v a l \rhd E.val := T.val ⊳E.val:=T.val
4. T 1 → T 2 ∗ F 4.\ T_1 \rightarrow T_2 * F 4. T1→T2∗F
Attribute: ⊳ T 1 . v a l : = p r o d u c t ( T 2 . v a l , F . v a l ) \rhd T_1.val := product(T_2.val,F.val) ⊳T1.val:=product(T2.val,F.val)
5. T 1 → T 2 ÷ T 5.\ T_1 \rightarrow T_2 \div T 5. T1→T2÷T
Attribute: ⊳ T 1 . v a l : = q u o t i e n t ( T 2 . v a l , F . v a l ) \rhd T_1.val := quotient(T_2.val,F.val) ⊳T1.val:=quotient(T2.val,F.val)
6. T → F 6.\ T \rightarrow F 6. T→F
Attribute: ⊳ T . v a l : = F . v a l \rhd T.val := F.val ⊳T.val:=F.val
7. F 1 → − F 2 7.\ F_1 \rightarrow -F_2 7. F1→−F2
Attribute: ⊳ F 1 . v a l : = a d d i t i v e i n v e r s e ( F 2 . v a l ) \rhd F_1.val := additive_inverse(F_2.val) ⊳F1.val:=additiveinverse(F2.val)
8. F → ( E ) 8.\ F \rightarrow (E) 8. F→(E)
Attribute: ⊳ F . v a l : = E . v a l \rhd F.val := E.val ⊳F.val:=E.val
9. F → d i g i t 9.\ F \rightarrow digit 9. F→digit
Attribute: ⊳ F . v a l : = d i g i t . v a l \rhd F.val := digit.val ⊳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, s t st st表示symbol table。
1. S : : = D E C 1.\ S::=DEC 1. S::=DEC
Attribute: ⊳ S . s t = D E C . s t \rhd\ S.st=DEC.st ⊳ S.st=DEC.st
2. S : : = S 1 D E C 2.\ S::=S_1\ DEC 2. S::=S1 DEC
Attribute: ⊳ S . s t = S 1 . s t + D E C . s t \rhd\ S.st=S_1.st + DEC.st ⊳ S.st=S1.st+DEC.st
3. D E C : : = T L 3.\ DEC::=T\ L 3. DEC::=T L
Attribute: ⊳ L . t y p e = T . t y p e ; D E C . s t = L . s t \rhd\ L.type=T.type;DEC.st=L.st ⊳ L.type=T.type;DEC.st=L.st
4. T : : = i n t 4.\ T::=int 4. T::=int
Attribute: ⊳ T . t y p e = i n t \rhd\ T.type=int ⊳ T.type=int
5. T : : = s t r i n g 5.\ T::=string 5. T::=string
Attribute: ⊳ T . t y p e = s t r i n g \rhd\ T.type=string ⊳ T.type=string
6. L : : = i d 6.\ L::=id 6. L::=id
Attribute: ⊳ L . s t = ( i d . n a m e , L . t y p e ) \rhd\ L.st=(id.name, L.type) ⊳ L.st=(id.name,L.type)
7. L : : = L 1 , i d 7.\ L::=L_1, id 7. L::=L1,id
Attribute: ⊳ L 1 . t y p e = L . t y p e ; L . s t = L 1 . s t + ( i d . n a m e , L . t y p e ) \rhd\ L_1.type=L.type;L.st=L_1.st + (id.name, L.type) ⊳ L1.type=L.type;L.st=L1.st+(id.name,L.type)
其中属性 s t st st是S属性,而属性 t y p e type type是L属性。对于下面的两行代码,对应的decorated tree如下所示。
int a, b, c;
string s;
从上图中我们可以清楚的看到,L属性 t y p e type type沿着 t o p → b o t t o m top\rightarrow bottom top→bottom和 l e f t → r i g h t left\rightarrow right left→right传递,S属性 s t st st沿着 b o t t o m → u p bottom\rightarrow up bottom→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.
读书的重点在于思考和理解,否则无疑是向脑子里倾倒垃圾。
PLP: 4.2/4.3 Attribute Gramma阅读笔记3相关推荐
- 【论文阅读笔记】Learning To Detect Unseen Object Classes by Between-Class Attribute Transfer
摘要: 本文主要研究训练和测试类别不相交时(即没有目标类别的训练示例)的对象分类问题.在此之前并没有对于毫无关联的训练集和测试集进行对象检测的工作,只是对训练集所包含的样本进行分类.实验表明,通过使用 ...
- Visual Attribute Transfer through Deep Image Analogy论文阅读笔记
Visual Attribute Transfer through Deep Image Analogy论文阅读笔记 介绍 论文提出了一种新的两张图片直接进行视觉属性迁移的方法.该方法针对的是两张具有 ...
- 西瓜书第一章阅读笔记
西瓜书第一章阅读笔记 第一章 绪论 1.机器学习基本术语 2.归纳偏好 3.所有学习算法一样优秀? 4.补充资料 第一章 绪论 1.机器学习基本术语 记录:对一个事件或对象的描述,也称为"示 ...
- syzkaller 源码阅读笔记1(syz-extract syz-sysgen)
文章目录 1. syz-extract 1-0 总结 1-1. `main()` 1-2 `archList()` - `1-1 (3)` 获取架构 name list 1-3 `createArch ...
- A Practical Approach to Constructing a Knowledge Graph for Cybersecurity 阅读笔记
A Practical Approach to Constructing a Knowledge Graph for Cybersecurity 阅读笔记 Article Background Pur ...
- 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析
为什么80%的码农都做不了架构师?>>> 刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部 ...
- 双稳态环PUF:一种强物理不可封闭功能的新架构阅读笔记
双稳态环PUF:一种强物理不可克隆函数的新架构阅读笔记 原文:<The Bistable Ring PUF: A New Architecture for Strong Physical Unc ...
- trainer setup_Detectron2源码阅读笔记-(一)Configamp;Trainer
一.代码结构概览 1.核心部分 configs:储存各种网络的yaml配置文件 datasets:存放数据集的地方 detectron2:运行代码的核心组件 tools:提供了运行代码的入口以及一切可 ...
- VoxelNet阅读笔记
作者:Tom Hardy Date:2020-02-11 来源:VoxelNet阅读笔记
最新文章
- 将您的基于 Accelerator 的 SAP Commerce Cloud Storefront 迁移到 Spartacus Storefront
- 【No.7 C++对象的构造与析构时间】
- 动态生成li 根据后台返回个数动态生成li
- linux格式化外接硬盘命令,linux格式化硬盘命令
- XTU 二分图和网络流 练习题 C. 方格取数(1)
- CentOS 6.8 Bonding技术实现和网卡功能配置基础
- 微星GE60有线网卡Qualcomm Atheros Bigfoot Killer E2200 ethernet card在ubuntu下无法找到驱动的解决办法...
- 短网址生成-nodejs实现
- erlang之ets总结
- bootstrap的图标新手使用教程
- Dot.js中添加函数用法
- Python开发技术—面向对象程序设计2
- 【稳定性day14】支付宝技术风险体系TRaaS——把风险去服务化、产品化
- matlab计算器设计流程图_matlab计算器的设计.doc
- 2021---长安“战疫”网络安全卫士守护赛 Writeup
- 自媒体5大免费网站,帮助你高效运营,快收藏起来
- 高清电脑壁纸桌面图片|到高图随心换高清图
- 考研概率论与数理统计(知识点梳理)
- Chromedriver安装教程(简洁版)
- 关于比尔·乔伊(Bill Joy),有哪些有趣的故事?
热门文章
- 关键词:MAU,DAU,DAU/MAU
- Python进程池multiprocessing.Pool的用法
- 转自51cto(http://smailes.blog.51cto.com/rss.php?uid=28248)
- C# FileInfo(System.IO)
- Android USB编程
- 数据结构——栈的应用
- C语言的快乐-表白代码
- 辗转相除法的原理,一看就懂,一学就会
- C 中用语言描述出下述方法的功能,文献检索复习题A
- 哪里可以下载Holer软件包