编译原理分析模块的组成
Ⅰ 想学《编译原理》请各位推荐些书
我们学校用的是《编译原理》与《编译原理与实践》这两本书,这两本书都是国外的教材。我觉得《编译原理与实践》这本书不错,自学应该能看懂,而且代码比较多,书最后还有整个小型编译器的源代码。
编译不好学,你就慢慢学吧。
下面的资料请作参考:
当代编译技术三大圣经级别的教材
1.龙书(Dragon book)
书名是Compilers: Principles,Techniques,and Tools
作者是:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman
内容简介
《编译原理》作者Alfred V.Aho、Ravi Sethi和Jeffrey D.Ullman是世界着名的计算机 科学家,他们在计算机科学理论、数据库等很多领域都做出了杰出贡献。《编译原理》 是编译领域无可替代的经典着作,被广大计算机专业人士誉为“龙书”。《编译原理》一 直被世界各地的着名高等院校和科研机构(如贝尔实验室、哥伦比亚大学、普 林斯顿大学和斯坦福大学等)广泛用作本科生和研究生编译原理与技术课程的 教材,《编译原理》对我国计算机教育界也具有重大影响。 书中深入讨论了编译器设计的重要主题,包括词法分析、语法分析、语法制 导分析、类型检查、运行环境、中间代码生成、代饥码茄码生成、代码优化等,并在 最后两章中讨论了实现编译器的一些编程问题和几个编译器实例,而且每章都 提供了大量的练习和参考文献。
与上一版相比,《编译原理》第二版进行了全面的修订,涵盖了编译器开发方面的最新进展。每章中都提供了大量的系统及参考文献。《编译原理》是编译原理课程方面的经典教材,内容丰富,适合作为高等院校计算机及相关专业本科生及研究生的编译原理课程的教材,也是广大技术人员的极佳参考读物。
作者烂察简介
Alfred V.Aho,美国歌伦比亚大学教授,美国国家工程院院士,ACM和IEEE会士,曾获得IEEE的冯·诺伊曼奖。着有多部算法、数据结构、编译器、数据库系统及计算机科学基础方面的着作。
Monica S.Lam,斯坦福大学计算机科学系教授,曾任Tensilica的首席科学家,也是Moka5的首任CEO。曾经主持SUIF项目,该项目产生了最流行的研究用编译器之一。
Ravi Sethi,Avaya实验室总裁,曾任贝尔实验室高级副总裁TLucent Technologies通信软件的CTO。他曾在宾夕法尼亚州立大学、亚利桑那州立大学和普林斯顿大学任教,是ACM会士。
Jeffrey D.Ullman斯坦福大学计算机科学系教授和Gradiance CEO,他的研究兴趣包括数据库理论、数据库集成、数据挖掘和利用信息基础设施教学等。他是美国国家工程院院士、IEEE会士,获得过ACM的KarIstrom杰出教育家奖和Knuth奖。
第一版中文版
第二版中文版
2.鲸书(Whale book)
书名是:Advanced Compiler Design and Implementation
作者是:Steven S.Muchnick
内容简介
本书迎接现代语言和体系结构的挑战,帮助读者作好准备,去应对将来要遇到的编译器设计的问题。
本书涵盖现代微处理器编译器的设计和实现方面的所有高级主题。本书从编译设计基础领域中的高级问题开始,广泛而深入地阐述各种重要的代码优化技术,分析各种优化之间的相对重模侍要关系,以及实现这些优化的最有效方法。
本书特点
●为理解高级编译器设计的主要问题奠定了基础
●深入阐述优化问题
●用Sun的SPARC、IBM的POWER和PowerPC、DEC的Alpha以及Intel的Pentium和相关商业编译 器作为案例,说明编译器结构、中间代码设计和各种优化方法
●给出大量定义清晰的关于代码生成、优化和其他问题的算法
●介绍由作者设计的以清晰、简洁的方式描述算法的语言ICAN (非形式编译算法表示)。
本书是经典的编译器着作,与“龙书”齐名,称为鲸书。书中针对现代语言和体系结构全面介绍了编译器设计与实现的高级论题,从编译器的基础领域中的高级问题开始,然后深入讨论了各种重要的代码优化。本书专为编译器专业人士和计算机专业本科生,研究生编写,在设计和实现高度优化的编译器以及确定优化的重要性和实现优化的最有效的方法等方面,为读者提供了非常有价值的指导。
作者简介
Steven S.Muchnick,曾是计算机科学教授,后作为惠普的PA-RISC和SUN的SPARC两种计算机体系结构的核心开发成员,将自己的知识和经验应用于编译器设计,并担任这些系统的高级编译器设计与实现小组的领导人。他在研究和开发方面的双重经验,对于指导读者作出编译器设计决策极具价值。
3.虎书(Tiger book)
书名是:Modern Compiler Implementation in C /java /ML,Second Edition
作者是:Andrew W.Appel,with Jens Palsberg
内容简介
《现代编译原理——c语言描述(英文版)/图灵原版计算机科学系列》全面讲述了现代编译器的各个组成部分,包括:词法分析、语法分析、抽象语法、语义检查、中间代码表示、指令选择、数据流分析、寄存器分配以及运行时系统等。与大多数编译原理的教材不同,《现代编译原理——C语言描述(英文版)/图灵原版计算机科学系列》采用了函数语言和面向对象语言来描述代码生成和寄存器分配,对于编译器中各个模块之间的接口都给出了实际的 C 语言头文件。 全书分成两部分,第一部分是编译的基础知识,适用于第一门编译原理课程(一个学期);第二部分是高级主题,包括面向对象语言和函数语言、垃圾收集、循环优化、 SSA(静态单赋值)形式、循环调度、存储结构优化等。
本书是一本着名的编译原理课程的教材。国际上众多名校均采用本书作为编译原理课程的教材,包括美国麻省理工学院、加州大学伯克利分校、普林斯顿大学和英国剑桥大学等。本书在国外享有“虎书”的称号,与有“龙书”之称的《编译原理》(Alfred Aho 等编着)齐名。与编译原理方面的其他名着相比,本书出版时间晚,内容新。 书中专门为学生提供了一个用 C 语言编写的实习项目,包括前端和后端设计,学生可以在一学期内创建一个功能完整的编译器。
作者简介
Andrew W.Appel,美国普林斯顿大学计算机科学系教授,第26届ACM SIGPLAN-SIGACT程序设计原理年会大会执行主席,1998-1999年在贝尔实验室做研究工作。主要研究方向是计算机安全、编译器设计、程序设计语言等。
Ⅱ 请问PLC中的梯形图逻辑的编译原理
PLC系统组成及各部分的功能
一.系统组成。
二.各部分的作用。
1. CPU运算和控制中心
起“心脏”作用。
纵:当从编程器输入的程序存入到用户程序存储器中,然后CPU根据系统所赋予的功能(系统程序存储器的解释编译程序),把用户程序翻译成PLC内部所认可的用户编译程序。
横:输入状态和输入信息从输入接口输进,CPU将之存入工作数据存储器中或输入映象寄存器。然后由CPU把数据和程序有机地结合在一起。把结果存入输出映象寄存器或工作数据存储器中,然后输出到输出接口、控制外部驱动器。
组成:CPU由控制器、运算器和寄存器组成。这些电路集成在一个芯片上。CPU通过地址总线、数据总线与I/O接口电路相连接。
2. 存储器
具有记忆功能的半导体电路。
分为系统程序存储器和用户存储器。
系统程序存储器用以存放系统程序,包括管理程序,监控程序以及对用户程序做编译处理的解释编译程序。由只读存储器、ROM组成。厂家使用的,内容不可更改,断电不消失。
用户存储器:分为用户程序存储区和工作数据存储区。由随机存取存储器(RAM)组成。用户使用的。断电内容消失。常用高效的锂电池作为后备电源,寿命一般为3~5年。
3.输入/输出接口
(1)输入接口:
光电耦合器由两个发光二极度管和光电三极管组成。
发光二级管:在光电耦合器的输入端加上变化的电信号,发光二极管就产生与输入信号变化规律相同的光信号。
光电三级管:在光信号的照射下导通,导通程度与光信号的强弱有关。在光电耦合器的线性工作区内,输出信号与输入信号有线性关系。
输入接口电路工作过程:当开关合上,二极管发光,然后三极管在光的照射下导通,向内部电路输入信号。当开关断开,二极管不发光,三极管不导通。向内部电路输入信号。也就是通过输入接口电路把外部的开关信号转化成PLC内部所能接受的数字信号。
(2)输出接口
PLC的继电器输出接口电路
工作过程:当内部电路输出数字信号1,有电流流过,继电器线圈有电流,然后常开触点闭合,提供负载导通的电流和电压。当内部电路输出数字信号0,则没有电流流过,继电器线圈没有电流,然后常开触点断开,断开负载的电流或电压。也就是通过输出接口电路把内部的数字电路化成一种信号使负载动作或不动作。
三种类型:
继电器输出:有触点、寿命短、频率低、交直流负载
晶体管输出:无触点、寿命长、直流负载
晶闸管输出:无触点、寿命长、交流负载
4.编程器
编程器分为两种,一种是手持编程器,方便。我们实验室使用的就是手持编程器。二种是通过PLC的RS232口。与计算机相连。然后敲击键盘。通过NSTP-GR软件(或WINDOWS下软件)向PLC内部输入程序。
第二节 PLC的基本工作原理
一.PLC采用“顺序扫描,不断循环”的工作方式
1.每次扫描过程。集中对输入信号进行采样。集中对输出信号进行刷新。
2.输入刷新过程。当输入端口关闭时,程序在进行执行阶段时,输入端有新状态,新状态不能被读入。只有程序进行下一次扫描时,新状态才被读入。
3.一个扫描周期分为输入采样,程序执行,输出刷新。
4.元件映象寄存器的内容是随着程序的执行变化而变化的。
5.扫描周期的长短由三条决定。(1)CPU执行指令的速度(2)指令本身占有的时间(3)指令条数
6.由于采用集中采样。集中输出的方式。存在输入/输出滞后的现象,即输入/输出响应延迟。
二.PLC与继电器控制系统、微机区别
1.PLC与继电器控制系统区别
前者工作方式是“串行”,后者工作方式是“并行”。
前者用“软件”,后者用“硬件”。
2.PLC与微机区别
前者工作方式是“循环扫描”。后者工作方式是“待命或中断”
PLC 编程方式
PLC最突出的优点采用“软继电器”代替“硬继电器”。用“软件编程逻辑”代替“硬件布线逻辑”。
PLC编程语言有梯形图、布尔助记符语言,等等。尤其前两者为常用。
梯形图语言特点:
1.每个梯形图由多个梯级组成。
2.梯形图中左右两边的竖线表示假想的逻辑电源。当某一梯级的逻辑运算结果为“1”时,有假想的电流通过。
3.继电器线圈只能出现一次,而它的常开、常闭触点可以出现无数次。
4.每一梯级的运算结果,立即被后面的梯级所利用。
5.输入继电器受外部信号控制。只出现触点,不出现线圈。
第四节 主要技术性能
用户程序存储容量:是衡量可存储用户应用程序多少的指标。通常以字或K字为单位。16位二进制数为一个字,每1024个字为1K字。PLC以字为单位存储指令和数据。一般的逻辑操作指令每条占1个字。定时/计数,移位指令占2个字。数据操作指令占2~4个字。
每五节 PLC的分类
按结构分类:
1. 整体式:是把PLC各组成部分安装在一起或少数几块印刷电路板上,并连同电源一起装在机壳内形成一个单一的整体,称之为主机或基本单元、小型、超小型PLC采用这种结构。
模块式:是把PLC各基本组成做成独立的模块。中型、大型PLC采用这种方式。便于维修。
Ⅲ 编译和解释的区别是什么
1.定义区别
①编译原理旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。
②汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
2.处理方式区别
①编译过程与解释挺像,区别就在于编译是将所有的源代码指令一次性成翻目标代码并执行。
②汇编过程就是把汇编指令一对一地翻译成01机器码的过程。而采用这种处理方式的语言只有一类:汇编语言。
3.特点区别
①编译语言的特点就是不需要解释器的参与,所以运行比较快,但是编译好的程序只能在当前平台运行,是个局限性。
②汇编语言是当今世界上历史最早,应用最广,功能最强大,运行速度最快的编程语言。但是汇编语言开发工期长,可读性差,并且不能跨平台编程。
Ⅳ 编译程序包括哪几个主要组成部分
编译过程分为分析和综合两个部分,并进一步划分为词法分析、语法分析、语义分析、代码优化、存储分配和代码生成等六个相继的逻辑步骤。这六个步骤只表示编译程序各部分之间的逻辑联系,而不是时间关系。
编译过程既可以按照这六个逻辑步骤顺序地执行,也可以按照平行互锁方式去执行。在确定编译程序的具体结构时,常常分若干遍实现。对于源程序或中间语言程序,从头到尾扫视一次并实现所规定的工作称作一遍。每一遍可以完成一个或相连几个逻辑步骤的工作。
(4)编译原理分析模块的组成扩展阅读:
对于c编译程序来说,其语言的特点如下:
1、c语言是一种结构化语言。它层次清晰,便于按模块化方式组织程序,易于调试和维护,而且表现能力和处理能力极强。
2、c语言具有丰富的运算符和数据类型,便于实现各类复杂的数据结构。它还可以直接访问内存的物理地址,进行位(bit)一级的操作。
3、由于c语言实现了对硬件的编程操作,因此集高级语言和低级语言的功能于一体。它既可用于系统软件的开发,也适合于应用软件的开发。
4、此外,c语言还具有效率高、可移植性强等特点。因此它广泛地移植到了各类各型计算机上,从而形成了多种版本。
Ⅳ GCC 的整体架构
在探索GCC的架构时,我们可以从构建GCC和编译原理两大方面入手,以理解GCC作为一个强大且复杂的编译器系统是如何工作的。
构建GCC时,构建系统起到了关键作用。以GNU Make为代表,它能基于构建脚本实现正确的增量编译方案。然而,构建脚本的生成并非孤立的过程。现代实践中,CMake因其广泛的采用,如在C++项目中,通过分析输入关系生成构建脚本,使得构建过程更为自动化和高效。GCC采用的是Autoconf输出Makefile,这与更现代的项目如LLVM使用CMake输出Ninja构建脚本形成了对比。
编译原理方面,编译过程通常分为五个阶段:预处理、词法分析、语法和语义分析、中间代码生成和目标代码生成。预处理和词法分析阶段主要将源代码转换为token,而语法和语义分析则解析出抽象语法树(AST)。对于C++这样复杂的语言,递归下降解析器因其灵活性和可定制性而被广泛应用。GCC内部的tree结构则支持C++相关语法。中间代码生成阶段,GCC将AST转换为GIMPLE,此阶段进行优化以提高代码性能。后端阶段负责从GIMPLE生成最终的汇编代码或机器代码。GCC代码结构紧密耦合,但每个开发者专注于特定模块的实现,提高了开发效率。
GCC的目录结构包括核心文件、运行时库、语言标准库等,具体语言的前端代码位于gcc/目录下,而编译器中端和后端也在此目录中实现。GCC代码虽庞大,但使用C++进行重构,继承了复杂垃圾回收系统,这在现代编译器设计中并不常见,如LLVM更倾向于使用C++的模板和RAII管理内存。
GCC的编译过程主要由gcc/gcc.cc中的driver::main函数驱动。根据文件后缀名,compiler driver选择不同的编译和链接过程。对于C++文件,若使用gcc编译,可能会导致错误,而g++则能正确处理C++文件的编译和链接。
在预处理和词法分析阶段,GCC支持C系列语言的共同特性,并通过CPP和特定源代码文件实现。语法和语义分析阶段使用递归下降解析器,GCC中端将AST转换为GIMPLE进行优化。最后,编译器后端将GIMPLE转换为目标代码。
随着行业的成熟,编译器前端技术已发展得相当成熟,但仍然需要维护和优化以适应新语言的特性。编译器中端对于CPU架构的优化已相对成熟,但对于异构计算,如GPU优化和计算图优化,仍有巨大的研究空间。编译器后端则在新兴芯片和架构设计中持续吸引着大量的研究和开发。
在探索GCC架构时,我们不仅了解到其构建过程的复杂性,也洞察到编译器领域未来的挑战与机遇。随着技术的不断进步,编译器将向着更高效、更灵活和更适应异构计算环境的方向发展。
Ⅵ 编译原理有有符号un-1.u=un吗
编译程序把源程序翻译为目标程序。根据源程序的语言种类,翻译程序可以分为汇编程序与编译程序。与之相对,解释程序是对源程序进行解释执行的程序。相应的可以将高级语言分为
编译型 C/C++, Swift, etc.
解释型 Python, javascript, etc.
混合型 Java, etc.
本文重点放在编译程序的设计上。典型的编译程序具有 7 77 个逻辑部分
对源程序扫描一次被称为一遍 (pass)。典型的一遍扫描编译程序有如下形式
通常将中间代码生成前的分析部分称为编译器的前端,其后的综合部分则被称为后端。这样就把一个编译程序分为了与源语言相关和与目标机有关的两个独立的部分,降低了程序的耦合。假设 llvm 编译器 支持 M MM 种源语言到 N NN 种目标语言的编译
传统的编译器如 gcc 可能需要开发 M × N M \times NM×N 个不同的子模块。而 llvm 使用统一的中间语言 llvm Intermediate Representation 只需要 M MM 个前端与 N NN 个后端,大大降低了开发成本。
文法
设非空有穷集合 Σ \SigmaΣ 为一字母表,则其上的符号串为 ∀ s ∈ Σ ∗ \forall s \in \Sigma^*∀s∈Σ
∗
,其中 ∗ *∗ 表示集合的闭包。特别的记 Σ 0 = ε \Sigma^0 = {\varepsilon}Σ
0
=ε 为空串组成的集合。规则通常写作
U : : = x or U → x , ∣ U ∣ = 1 , ∣ x ∣ ≥ 0 U ::= x\text{ or }U\rightarrow x,\quad |U| = 1, |x| \ge 0U::=x or U→x,∣U∣=1,∣x∣≥0
其中左部 U UU 是符号,右部 x xx 是有穷符号串。规则的集合 P PP 即可确定一个文法 G GG
<程序> ::= <常量说明><变量说明><函数说明>
<常量说明> ::= {const<常量定义>;}
<常量定义> ::= int<标识符>=<整数>{,<标识符>=<整数>}|char<标识符>=<字符>{,<标识符>=<字符>}
<变量说明> ::= {<类型标识符><变量定义>;}
<变量定义> ::= <标识符>[<下标>]{,<标识符>[<下标>]}
<下标> ::= '['<无符号整数>']' // <无符号整数>表示数组元素的个数,其值需大于0
<函数说明> ::= {(<类型标识符>|void)<函数定义>}void<主函数>
<函数定义> ::= <标识符>'('<参数表>')'<复合语句>
<参数表> ::= [<类型标识符><标识符>{,<类型标识符><标识符>}]
<主函数> ::= main'('')'<复合语句>
<复合语句> ::= '{'<常量说明><变量说明>{<语句>}'}'
<语句> ::= <条件语句>|'{'{<语句>}'}'|<函数调用语句>;|<赋值语句>;|<读语句>;|<写语句>;|<返回语句>;|;
<条件语句> ::= <if语句>|<while语句>|<do语句>|<for语句>
<if语句> ::= if'('<条件>')'<语句>[else<语句>]
<while语句> ::= while'('<条件>')'<语句>
<do语句> ::= do<语句>while'('<条件>')'
<for语句> ::= for'('<标识符>=<表达式>;<条件>;<标识符>=<标识符><加法运算符><无符号整数>')'<语句>
<条件> ::= <表达式>[<关系运算符><表达式>] // 表达式为0条件为假,否则为真
<函数调用语句> ::= <标识符>'('[<表达式>{,<表达式>}]')'
<赋值语句> ::= <标识符>['['<表达式>']']=<表达式>
<读语句> ::= scanf'('<标识符>{,<标识符>}')'
<写语句> ::= printf'('<字符串>[,<表达式>]')'|printf'('<表达式>')'
<返回语句> ::= return['('<表达式>')']
<表达式> ::= [<加法运算符>]<项>{<加法运算符><项>} // [+|-]只作用于第一个<项>
<项> ::= <因子>{<乘法运算符><因子>}
<因子> ::= <标识符>['['<表达式>']']|'('<表达式>')'|<整数>|<字符>|<函数调用语句>
<整数> ::= [<加法运算符>]<无符号整数>
<标识符> ::= <字母>{<字母>|<数字>}
<无符号整数> ::= <非零数字>{<数字>}|0
<数字> ::= 0|<非零数字>
<非零数字> ::= 1|...|9
<字符> ::= '<加法运算符>'|'<乘法运算符>'|'<字母>'|'<数字>'
<字符串> ::= "{十进制编码为32,33,35-126的ASCII字符}"
<类型标识符> ::= int|char
<加法运算符> ::= +|-
<乘法运算符> ::= *|/
<关系运算符> ::= <|<=|>|>=|!=|==
<字母> ::= _|a|...|z|A|...|Z
复制
上述文法使用扩充的 BNF 表示法进行描述
符号 定义 说明
∣ \vert∣ 或 作用域由括号限定
{ t } n m \{t\}^m_n{t}
n
m
将 t tt 重复连接 n ∼ m n \sim mn∼m 次 缺省时 m = ∞ , n = 0 m = \infin,\ n = 0m=∞, n=0
[ t ] [t][t] 符号串 t tt 可有可无 等价于 { t } 1 \{t\}^1{t}
1
( t ) (t)(t) 局部作用域 主要用于限定 ∣ \vert∣ 范围
相关概念有
概念 符号 定义 示例
识别符号 Z ZZ 文法中第一条规则的左部符号 <程序>
字汇表 V VV 文法中出现的全部符号 { <程序>, <常量说明>, …, 0, 1, … }
非终结符号集 V n V_nV
n
全部规则的左部组成的集合 { <程序>, <常量说明>, <变量说明>, … }
终结符号集 V t V_tV
t
V − V n V - V_nV−V
n
{ 0, 1, …, _, a, b, … }
设 U : : = u ∈ P U ::= u \in PU::=u∈P 则对于 ∀ x , y ∈ V ∗ \forall x, y \in V^*∀x,y∈V
∗
有直接推导 x U y ⇒ x u y xUy \Rightarrow xuyxUy⇒xuy 。如果 y ∈ V t ∗ y \in V_t^*y∈V
t
∗
则 x U y ⤃ x u y xUy\ ⤃\ xuyxUy ⤃ xuy 称为规范推导。直接推导序列 u 0 ⇒ u 1 ⇒ ⋯ ⇒ u n u_0 \Rightarrow u_1 \Rightarrow \cdots \Rightarrow u_nu
0
⇒u
1
⇒⋯⇒u
n
可简记为
{ u 0 ⇒ + u n n > 0 u 0 ⇒ ∗ u n n ≥ 0 \begin{cases} u_0 \mathop\Rightarrow\limits^+ u_n & n > 0\\ u_0 \mathop\Rightarrow\limits^* u_n & n \ge 0\\ \end{cases}{
u
0
⇒
+
u
n
u
0
⇒
∗
u
n
n
>
0
n
≥
0
进一步定义
句型 V ∗ ∋ x ⇐ ∗ Z V^* \ni x \mathop\Leftarrow\limits^* ZV
∗
∋x
⇐
∗
Z
句子 V t ∗ ∋ x ⇐ + Z V_t^* \ni x \mathop\Leftarrow\limits^+ ZV
t
∗
∋x
⇐
+
Z
语言 L ( G ) = { x ∣ x is sentence } L(G) = \{ x| x\text{ is sentence} \}L(G)={x∣x is sentence}
如果文法 G GG 和 G ′ G'G
′
有 L ( G ) = L ( G ′ ) L(G) = L(G')L(G)=L(G
′
) ,则称这两个文法等价。设 w = x u y w=xuyw=xuy 为一句型,称 u uu 为一个相对于 U ∈ V n U \in V_nU∈V
n
的
w ww 的短语 如果 Z ⇒ ∗ x U y ∧ U ⇒ + u Z \mathop\Rightarrow\limits^* xUy \land U \mathop\Rightarrow\limits^+ uZ
⇒
∗
xUy∧U
⇒
+
u
w ww 的简单短语 如果 u uu 是短语且 U ⇒ u U \mathop\Rightarrow\limits uU⇒u
句型的最左简单短语称为句柄。
二义性
文法 G GG 是二义性的,如果 ∃ x ∈ L ( G ) \exist x \in L(G)∃x∈L(G) 使下列条件之一成立
x xx 可以对应两颗不同的语法树
x xx 有两个不同的规范推导