China-pub.com 下载 附录A编译器设计方案 本章要点 ·C一惯用的词法 ·C一语言的Tiny Machine运行时环境 ·C一的语法和语义 ·使用C一和TM的编程设计 ·C一的程序例子 这里定义了一个编程语言称作C一Mius(或简称为C一),这是一种适合编译器设计方案的 语言,它比TNV语言更复杂,句括承数和数组。本质上它是C的一个子集,但省夫了一些重要 的部分,因此得名。这个附录由5小节组成。首先,我们列出了语言惯用的词法,包括语言标 记的描述。其次,给出了每个语言构造的BNF描述,同时还有相关语义的英语描述。在A3节, 给出了C一的两个示例程序。再者,描述了C一的一个Tiny Machine运行时环境。最后一节描述 了一些使用C一和TM的编程设计方案,适合于一个编译器教程。 A.1C-惯用的词法 1.下面是语言的关键字: else if int return void while 所有的关键字都是保留字,并且必须是小写。 2.下面是专用符号: +-*/<<=>>====;,()【】【*/ 3.其他标记是ID和NUM,通过下列正则表达式定义: ID letter lettert NUM digit digit+ 1 etter=a1.1zla1…1z dig1t01.9 小写和大写字母是有区别的。 4.空格由空白、换行符和制表符组成。空格通常被忽略,除了它必须分开ID、NUM关 键字。 5.注释用通常的C语言符号/*,·.*/围起来。注释可以放在任何空白出现的位置(即注释 不能放在标记内)上,且可以超过一行。注释不能嵌套。 A.2C-的语法和语义 C一的BNF语法如下: 1.program-declaration-list 2.declaration-listdeclaration-list declaration declaration 3.declaration-var-declaration fun-declaration
下载 附录A 编译器设计方案 本章要点 • C-惯用的词法 • C-语言的Tiny Machine运行时环境 • C-的语法和语义 • 使用C-和T M的编程设计 • C-的程序例子 这里定义了一个编程语言称作C-M i n u s (或简称为C-),这是一种适合编译器设计方案的 语言,它比T I N Y语言更复杂,包括函数和数组。本质上它是C的一个子集,但省去了一些重要 的部分,因此得名。这个附录由 5小节组成。首先,我们列出了语言惯用的词法,包括语言标 记的描述。其次,给出了每个语言构造的 B N F描述,同时还有相关语义的英语描述。在 A . 3节, 给出了C-的两个示例程序。再者,描述了C-的一个Tiny Machine运行时环境。最后一节描述 了一些使用C-和T M的编程设计方案,适合于一个编译器教程。 A.1 C-惯用的词法 1. 下面是语言的关键字: else if int return void while 所有的关键字都是保留字,并且必须是小写。 2. 下面是专用符号: + - * / < <= > >= == != = ; , ( ) [ ] { } /* */ 3. 其他标记是I D和N U M,通过下列正则表达式定义: ID = letter letter* NUM = digit digit* letter = a|..|z|A|..|Z digit = 0|..|9 小写和大写字母是有区别的。 4. 空格由空白、换行符和制表符组成。空格通常被忽略,除了它必须分开 I D、N U M关 键字。 5. 注释用通常的C语言符号/ * . . . * /围起来。注释可以放在任何空白出现的位置 (即注释 不能放在标记内)上,且可以超过一行。注释不能嵌套。 A.2 C-的语法和语义 C-的B N F语法如下: 1. p ro g r a m → d e c l a r a t i o n - l i s t 2. d e c l a r a t i o n - l i s t → d e c l a r a t i o n - l i s t d e c l a r a t i o n | d e c l a r a t i o n 3. d e c l a r a t i o n → v a r- d e c l a r a t i o n | f u n - d e c l a r a t i o n
374 翁译原理及实践 China-pub.com 下载 4.rar-declaration一pe-specifier ID:lppe-specifier ID【NuM】; 5.type-specifier-int lvoid 6.fun-declaration-type-specifier ID params compound-stmt 7.params-params-list void 8.param-list-param-list,param param 9.param→pe-specifier ID type-specifier ID[】 10.compound-stmt local-declarations statement-list) 11.local-declarations local-declarations var-declaration empty 12.statement-list-statement-list statement empty 13.statementexpression-stmt compound-stmt selection-simt iteration-stmt return-stmt 14.expression-simt-expression 15.selection-stmt -if expression statement if expression statement else statement 16.iteration-stmt-while expression statement 17.return-simt-return return expression; 18.expressionvar=expression simple-expression 19.var -IDID expression 20.simple-expression-additive-expression relop additive-expression additive-expression 21.elop→<=|<|>|>=|=|1= 22.additive-expression-additive-expression addop term term 23.addop→+1- 24.term-term mulop factor factor 25.mlop→*/ 26.factor→(expression)var|call1uw 27.call→tD(amgs) 28.args-arg-list empty 29.arg-list-arg-list,expression expression 对以上每条文法规则,给出了相关语义的简短解释 1.program declaration-list 2.declaration-list-declaration-list declarationdeclaration 3.declarationvar-declaration fun-declaration 程序由声明的列表(或序列)组成,声明可以是函数或变量声明,顺序是任意的。至少必须有一个 声明。接下来是语义限制(这些在C中不会出现)。所有的变量和函数在使用前必须声明(这避免了 向后backpatching引用)。程序中最后的声明必须是一个函数声明,名字为main。注意,C一缺乏 原型,因此声明和定义之间没有区别(像C一样)。 4.var-declaration一ype-specifier ID;lpe-specifier ID【wM】: 5.type-specifier-int void 变量声明或者声明了简单的整数类型变量,或者是基类型为整数的数组变量,索引范围从0到 N0M-1。注意,在C一中仅有的基本类型是整型和空类型。在一个变量声明中,只能使用类型
4. v a r- d e c l a r a t i o n → t y p e - s p e c i f i e r I D ; | t y p e - s p e c i f i e r I D [ N U M ] ; 5. t y p e - s p e c i f i e r → i n t | v o i d 6. f u n - d e c l a r a t i o n → t y p e - s p e c i f i e r I D ( p a r a m s ) | c o m p o u n d - s t m t 7. p a r a m s → p a r a m s-l i s t | v o i d 8. p a r a m - l i s t → p a r a m - l i s t , p a r a m | p a r a m 9. p a r a m → t y p e - s p e c i f i e r I D | t y p e - s p e c i f i e r I D [ ] 10. c o m p o u n d - s t m t → { l o c a l-d e c l a r a t i o ns s t a t e m e n t-l i s t } 11. l o c a l-d e c l a r a t i o ns → l o c a l-d e c l a r a t i o ns v a r- d e c l a r a t i o n | e m p t y 12. s t a t e m e n t-l i s t → s t a t e m e n t-l i s t s t a t e m e n t | e m p t y 13. s t a t e m e n t → e x p re s s i o n-s t m t | c o m p o u n d - s t m t | s e l e c t i o n - s t m t | i t e r a t i o n-s t m t | re t u r n-s t m t 14. e x p re s s i o n-s t m t → e x p re s s i o n ; | ; 15. s e l e c t i o n - s t m t → i f ( e x p re s s i o n ) s t a t e m e n t | i f ( e x p re s s i o n ) s t a t e m e n t e l s e s t a t e m e n t 16. i t e r a t i o n -s t m t → w h i l e ( e x p re s s i o n ) s t a t e m e n t 17. re t u r n -s t m t → return ;| r e t u r n e x p re s s i o n; 18. e x p re s s i o n → v a r = e x p re s s i o n | s i m p l e-e x p re s s i o n 19. v a r → I D | I D [ e x p re s s i o n ] 20. s i m p l e-e x p re s s i o n → a d d i t i v e-e x p re s s i o n re l o p a d d i t i v e-e x p re s s i o n | a d d i t i v e -e x p re s s i o n 21. re l o p →< = | < | > | > = | = = | ! = 22. a d d i t i v e-e x p re s s i o n → a d d i t i v e-e x p re s s i o n a d d o p t e r m | t e r m 23. a d d o p →+ | - 24. t e r m → t e r m m u l o p f a c t o r | f a c t o r 25. m u l o p →* | / 26. f a c t o r → ( e x p re s s i o n ) | v a r | c a l l | N U M 27. c a l l → I D ( a rg s ) 28. a rg s → a rg - l i s t | e m p t y 29. a rg-l i s t → a rg-list , e x p re s s i o n | e x p re s s i o n 对以上每条文法规则,给出了相关语义的简短解释。 1. p ro g r a m → d e c l a r a t i o n - l i s t 2. d e c l a r a t i o n - l i s t → d e c l a r a t i o n - l i s t d e c l a r a t i o n | d e c l a r a t i o n 3. d e c l a r a t i o n → v a r- d e c l a r a t i o n | f u n - d e c l a r a t i o n 程序由声明的列表(或序列)组成,声明可以是函数或变量声明,顺序是任意的。至少必须有一个 声明。接下来是语义限制(这些在C中不会出现)。所有的变量和函数在使用前必须声明(这避免了 向后b a c k p a t c h i n g引用)。程序中最后的声明必须是一个函数声明,名字为m a i n。注意,C-缺乏 原型,因此声明和定义之间没有区别(像C一样)。 4. v a r- d e c l a r a t i o n → t y p e - s p e c i f i e r I D ; | t y p e - s p e c i f i e r I D [ NUM ] ; 5. t y p e - s p e c i f i e r → i n t | v o i d 变量声明或者声明了简单的整数类型变量,或者是基类型为整数的数组变量,索引范围从 0到 N UM -1。注意,在C-中仅有的基本类型是整型和空类型。在一个变量声明中,只能使用类型 3 7 4 编译原理及实践 下载
China-pub.com 附录A编译器设计方案 375 下载 指示符int。void用于函数声明(参见下面)。也要注意,每个声明只能声明一个变量 6.fun-declaration-type-specifier ID params compound-stmt 7.params-param-list void 8.param-list-param-list,param param 9.param-type-specifier ID type-specifier ID[ 函数声明由返回类型指示符、标识符以及在圆括号内的用逗号分开的参数列表组成,后面 跟着一个复合语句,是函数的代码。如果函数的返回类型是vo1,那么函数不返回任何值(即 是一个过程)。函数的参数可以是v0i(即没有参数),或者一列描述函数的参数。参数后面跟 着方括号是数组参数,其大小是可变的。简单的整型参数由值传递。数组参数由引用来传递 (也就是指针),在调用时必须通过数组变量来匹配。注意,类型“函数”没有参数。一个函数 参数的作用域等于函数声明的复合语句,函数的每次请求都有一个独立的参数集。函数可以是 递归的(对于使用声明允许的范围)。 10.compound-stmt-{local-declarations statement-list 复合语句由用花括号围起来的一组声明和语句组成。复合语句通过用给定的顺序执行语句 序列来执行。局部声明的作用域等于复合语句的语句列表,并代替任何全局声明。 11.local-declarationslocal-declarations var-declaration empty 2.statement-liststatement-list statement empty 注意声明和语句列表都可以是空的(非终结符mp表示空字符串,有时写作e。) 13.statement expression-stmi compound-stmt selection-stmt iteration-stmi return-stmt 14.expression-stmt-expression; 表达式语句有一个可选的且后面跟着分号的表达式。这样的表达式通常求出它们一方的结 果。因此,这个语句用于赋值和函数调用。 15.selection-stmtf (expression)statement if (expression)statement else statement i语句有通常的语义:表达式进行计算:非0值引起第一条语句的执行:0值引起第二条语 句的执行,如果它存在的话。这个规则导致了典型的悬挂else二义性,可以用一种标准的方法 解决:els部分通常作为当前if的一个子结构立即分析(“最近嵌套”非二义性规则)。 16.iteration-stmt-while (expression)statement whil语句是C一中唯一的垂复语句。它垂复执行表达式,并且如果表达式的求值为非0, 则执行语句,当表达式的值为0时结束。 17.return-stmt-return return expression 返回语句可以返回一个值也可无值返回。函数没有说明为voi就必须返回一个值。函数 声明为void就没有返回值。return引起控制返回调用者(如果它在main中,则程序结束) 18.expression-var expression simple-expression 19.var→ID|ID【expression] 表达式是一个变量引用,后面跟着赋值符号(等号)和一个表达式,或者就是一个简单的表 达式。赋值有通常的存储语义:找到由"ar表示的变量的地址,然后由赋值符右边的子表达式
指示符i n t。v o i d用于函数声明(参见下面)。也要注意,每个声明只能声明一个变量。 6. f u n - d e c l a r a t i o n → t y p e - s p e c i f i e r I D ( p a r a m s )c o m p o u n d - s t m t 7. p a r a m s → p a r a m - l i s t | v o i d 8. p a r a m-l i s t → p a r a m - l i s t , p a r a m | p a r a m 9. p a r a m → t y p e - s p e c i f i e r I D | t y p e - s p e c i f i e r I D [ ] 函数声明由返回类型指示符、标识符以及在圆括号内的用逗号分开的参数列表组成,后面 跟着一个复合语句,是函数的代码。如果函数的返回类型是 v o i d,那么函数不返回任何值(即 是一个过程)。函数的参数可以是v o i d (即没有参数),或者一列描述函数的参数。参数后面跟 着方括号是数组参数,其大小是可变的。简单的整型参数由值传递。数组参数由引用来传递 (也就是指针),在调用时必须通过数组变量来匹配。注意,类型“函数”没有参数。一个函数 参数的作用域等于函数声明的复合语句,函数的每次请求都有一个独立的参数集。函数可以是 递归的(对于使用声明允许的范围)。 10. c o m p o u n d - s t m t → { l o c a l-d e c l a r a t i o ns s t a t e m e n t-l i s t } 复合语句由用花括号围起来的一组声明和语句组成。复合语句通过用给定的顺序执行语句 序列来执行。局部声明的作用域等于复合语句的语句列表,并代替任何全局声明。 11. l o c a l-d e c l a r a t i o ns → l o c a l-d e c l a r a t i o ns v a r- d e c l a r a t i o n | e m p t y 12. s t a t e m e n t-l i s t → s t a t e m e n t-l i s t s t a t e m e n t | e m p t y 注意声明和语句列表都可以是空的(非终结符e m p t y表示空字符串,有时写作 。) 13. s t a t e m e n t → e x p re s s i o n-s t m t | c o m p o u n d - s t m t | s e l e c t i o n - s t m t | i t e r a t i o n-s t m t | re t u r n-s t m t 14. e x p re s s i o n-s t m t → e x p re s s i o n; |; 表达式语句有一个可选的且后面跟着分号的表达式。这样的表达式通常求出它们一方的结 果。因此,这个语句用于赋值和函数调用。 15. s e l e c t i o n - s t m t → i f (e x p re s s i o n) s t a t e m e n t | i f (e x p re s s i o n) s t a t e m e n t e l s e s t a t e m e n t i f语句有通常的语义:表达式进行计算;非 0值引起第一条语句的执行; 0值引起第二条语 句的执行,如果它存在的话。这个规则导致了典型的悬挂 e l s e二义性,可以用一种标准的方法 解决:e l s e部分通常作为当前i f的一个子结构立即分析(“最近嵌套”非二义性规则)。 16. i t e r a t i o n-s t m t → w h i l e (e x p re s s i o n) s t a t e m e n t w h i l e语句是C-中唯一的重复语句。它重复执行表达式,并且如果表达式的求值为非 0, 则执行语句,当表达式的值为0时结束。 17. re t u r n -s t m t → return ;| r e t u r n e x p re s s i o n; 返回语句可以返回一个值也可无值返回。函数没有说明为 v o i d就必须返回一个值。函数 声明为v o i d就没有返回值。r e t u r n引起控制返回调用者(如果它在m a i n中,则程序结束)。 18. e x p re s s i o n → v a r = e x p re s s i o n | s i m p l e-e x p re s s i o n 19. v a r → I D | I D [e x p re s s i o n] 表达式是一个变量引用,后面跟着赋值符号 (等号)和一个表达式,或者就是一个简单的表 达式。赋值有通常的存储语义:找到由 v a r表示的变量的地址,然后由赋值符右边的子表达式 附录A 编译器设计方案 3 7 5 下载
376 翁译原理及实践 China-pub.com 下载 进行求值,子表达式的值存储到给定的地址。这个值也作为整个表达式的值返回。vr是简单 的(整型)变量或下标数组变量。负的下标将引起程序停止(与C不同)。然而,不进行下标越界 检查。 var表示C一比C的进一步限制。在C中赋值的目标必须是左值(value,左值是可以由许多 操作获得的地址。在C一中唯一的左值是由r语法给定的,因此这个种类按照句法进行检查 代替像C中那样的类型检查。故在C一中指针运算是禁止的。 20.simple-expressionadditive-expression relop additive-expression additive -expression 21.relop+<=|<|>|>=l==1!= 简单表达式由无结合的关系操作符组成(即无括号的表达式仅有一个关系操作符)。简单表 达式在它不包含关系操作符时,其值是加法表达式的值,或者如果关系算式求值为tur,其值 为l,求值为false时值为0。 22.additive-expression-additive-expression addop term term 23.addop→+1- 24.termterm mulop factor factor 25.mulop* 加法表达式和项表示了算术操作符的结合性和优先级。符号表示整数除:即任何余数都被 截去。 26 factor-(expression)I var call NUM 因子是围在括号内的表达式:或一个变量,求出其变量的值:或者一个函数调用,求出函 数的返回值:或者一个心M,其值由扫描器计算。数组变量必须是下标变量,除非表达式由单 个D组成,并且以数组为参数在函数调用中使用(如下所示)。 27.cal→rD(amgs) 28.argsarg-list empty 29.arg-list arg-list,expression expres 函数调用的组成是一个ID(函数名),后面是用括号围起来的参数。参数或者为空,或者由 逗号分割的表达式列表组成,表示在一次调用期间分配的参数的值。函数在调用之前必须声明, 声明中参数的数目必须等于调用中参数的数目。函数声明中的数组参数必须和一个表达式匹配 这个表达式由一个标识符组成表示一个数组变量。 最后,上面的规则没有给出输入和输出语句。在C一的定义中必须包含这样的函数,因为 与C不同,C一没有独立的编译和链接工具:因此,考虑两个在全局环境中预定义的函数,好 像它们已进行了声明: int input(void)(...) void output(int x)(...) inputi函数没有参数,从标准输入设备(通常是键盘)返回一个整数值。output函数接受 一个整型参数,其值和一个换行符一起打印到标准输出设备(通常是屏幕)。 A.3C一的程序例子 下面的程序输入两个整数,计算并打印出它们的最大公因子
进行求值,子表达式的值存储到给定的地址。这个值也作为整个表达式的值返回。 v a r是简单 的(整型)变量或下标数组变量。负的下标将引起程序停止 (与C不同)。然而,不进行下标越界 检查。 v a r表示C-比C的进一步限制。在C中赋值的目标必须是左值(l - v a l u e),左值是可以由许多 操作获得的地址。在C-中唯一的左值是由v a r语法给定的,因此这个种类按照句法进行检查, 代替像C中那样的类型检查。故在C-中指针运算是禁止的。 20. s i m p l e-e x p re s s i o n → a d d i t i v e-e x p re s s i o n re l o p a d d i t i v e-e x p re s s i o n | a d d i t i v e -e x p re s s i o n 21. re l o p → < = | < | > | > = | = = |! = 简单表达式由无结合的关系操作符组成 (即无括号的表达式仅有一个关系操作符 )。简单表 达式在它不包含关系操作符时,其值是加法表达式的值,或者如果关系算式求值为 t u r e,其值 为1,求值为f a l s e时值为0。 22. a d d i t i v e-e x p re s s i o n → a d d i t i v e-e x p re s s i o n a d d o p t e r m | t e r m 23. a d d o p → + | - 24. t e r m → t e r m m u l o p f a c t o r | f a c t o r 25. m u l o p → * | / 加法表达式和项表示了算术操作符的结合性和优先级。符号表示整数除;即任何余数都被 截去。 26. f a c t o r → (e x p re s s i o n) | v a r | c a l l | N U M 因子是围在括号内的表达式;或一个变量,求出其变量的值;或者一个函数调用,求出函 数的返回值;或者一个N U M,其值由扫描器计算。数组变量必须是下标变量,除非表达式由单 个ID 组成,并且以数组为参数在函数调用中使用(如下所示)。 27. c a l l → I D ( a rg s ) 28. a rg s → a rg - l i s t | e m p t y 29. a rg - l i s t → a rg-list , e x p re s s i o n | e x p re s s i o n 函数调用的组成是一个I D (函数名),后面是用括号围起来的参数。参数或者为空,或者由 逗号分割的表达式列表组成,表示在一次调用期间分配的参数的值。函数在调用之前必须声明, 声明中参数的数目必须等于调用中参数的数目。函数声明中的数组参数必须和一个表达式匹配, 这个表达式由一个标识符组成表示一个数组变量。 最后,上面的规则没有给出输入和输出语句。在 C-的定义中必须包含这样的函数,因为 与C不同,C-没有独立的编译和链接工具;因此,考虑两个在全局环境中预定义的函数,好 像它们已进行了声明: int input(void) {...} void output(int x) {...} i n p u t函数没有参数,从标准输入设备 (通常是键盘)返回一个整数值。o u t p u t函数接受 一个整型参数,其值和一个换行符一起打印到标准输出设备 (通常是屏幕)。 A.3 C-的程序例子 下面的程序输入两个整数,计算并打印出它们的最大公因子。 /* A program to perform Euclid's Algorithm to compute gcd. */ 3 7 6 编译原理及实践 下载
China-pub.com 附录A编译器设计方案 377 下载 1 nt ged(intu,intv) {ie(y=o)return u; else return ged(v,u-u/vtv); /u-u/vtv =u mod vt/ void main(void) 【1ntx;inty: x input();y input(); output (ged(x,y)): 下面的程序输入10个整数的列表,对它们进行选择排序,然后再输出: /t A program to perform selection sort on a 10 element array. 1ntx【10】: int minloc int a[],int low,int high (1nt且:1nt;1ntk: k low: x=a【1ow】: 1=10w+1: while (i high) 1£《a【11<x) 这=a【1】 k=1:】 1m1+1: return k: int k: 1 while (<hgh-1) int t inloc (a,i,high) t=a【k =1+1: void main (void) int i; 1■0: wh11。(1<10) (x【i】=input; 1=1+1:
int gcd (int u, int v) { if (v == 0) return u ; else return gcd(v,u-u/v*v); /* u-u/v*v == u mod v */ } void main(void) { int x; int y; x = input(); y = input(); o u t p u t ( g c d ( x , y ) ) ; } 下面的程序输入1 0个整数的列表,对它们进行选择排序,然后再输出: /* A program to perform selection sort on a 10 element array. */ int x[10]; int minloc ( int a[], int low, int high ) { int i; int x; int k; k = low; x = a[low]; i = low + 1; while (i < high) { if (a[i] < x) { x = a[i]; k = i; } i = i + 1; } return k; } void sort ( int a[], int low, int high ) { int i; int k; i = low; while (i < high-1) { int t; k = minloc (a,i,high); t =a[k]; a[k] = a[i]; a[i] = t; i = i + 1; } } void main (void) { int i; i = 0; while (i < 10) { x[i] = input; i = i + 1; 附录A 编译器设计方案 3 7 7 下载