《语法分析.doc》由会员分享,可在线阅读,更多相关《语法分析.doc(69页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第4章 语法分析第4章 语法分析在本章中,读者将学习为SkipOOMiniJOOL语言实现语法分析器。语法分析器检查词法分析器输出的记号流是否符合源语言的语法规则;并依据这些规则体现出的程序中各种语法结构的层次性,为记号流构建用Eclipse AST表示的抽象语法树AST。通过本章的课程实践,读者将了解并掌握语法分析器的多种构造方法,如手工编写一个语法分析器,用语法分析器的生成工具CUP或者JavaCC来构造语法分析器,等等;还将学会如何恢复和处理语法分析中遇到的错误。读者可以根据实际情况,选做本章安排的部分或全部的课程设计。为了指导读者开展本章的课程设计,引入小型的无类型语言SimpleBl
2、ock(见4.3.1节)以及强类型语言Block(见4.4.3节),并给出其语法分析器的多种构造方法以及错误恢复和处理方法示例。SimpleBlock和Block语言的程序结构都是以赋值语句组成的语句块为基础,前者的变量均视为整型且无须声明即可使用,后者则引入布尔类型和整型并且变量须先声明再使用。在以下各节中,首先介绍SkipOOMiniJOOL语言的语法,其中穿插说明了部分语义特征,以帮助读者了解语言的特征;然后概述本章的各个课程设计;接着围绕各个课程设计给出一些指导。4.1 SkipOOMiniJOOL语言的语法SkipOOMiniJOOL语言是MiniJOOL语言的非面向对象子集,它在S
3、impleMiniJOOL语言的基础上扩展增加了对多函数(方法)、多类型的支持。与SimpleMiniJOOL语言类似,SkipOOMiniJOOL语言同样以类声明的形式来表示程序,并且不支持public、protected及private访问控制修饰符。一个SkipOOMiniJOOL程序由一个类声明组成,类名可以是任意标识符,类体中允许有多个域声明和方法声明,其中至少有一个是类型为static void、名为main的无参方法声明,main( )方法是整个程序的入口方法。在以下各小节中,将首先介绍SkipOOMiniJOOL语言所支持的类型和字面常量、变量、语句和表达式,然后给出SkipO
4、OMiniJOOL程序的总体结构及程序示例。我们将结合EBNF文法来描述SkipOOMiniJOOL语言的语法。4.1.1 类型、常量和变量1. 类型和字面常量(1) 类型SkipOOMiniJOOL语言支持32位的整型(int)、布尔型(boolean)、字符串型(String),以及元素类型为int、boolean或String的一维数组类型,其中数组的长度是能静态(编译时)定值为正整数的常量表达式(constant_expression)。下面是类型有关的EBNF文法定义:type=primitive_type | “String” | array_typeprimitive_type=
5、“int” | “boolean”array_type=( primitive_type | “String”) “” constant_expression “”需要注意的是,array_type产生式右部(right hand side,RHS)中的constant_expression是可选的,其省略情况只有以下两种:当声明一个数组类型的形参时,不必为该形参指定数组的长度,这时形参数组的长度由运行时传入的实参数组的长度来决定。当声明的数组变量包含有初始化表达式(见后面关于变量的声明的内容)时,可以在声明语句的类型中不指定数组的长度。例如,语句“int a=1, 2, b=1, 2, 3;
6、”声明了两个一维整型数组a和b,其长度分别由初始化表达式“1, 2”和“1, 2, 3”推出,即长度分别为2和3。 (2) 字面常量与上述类型相对应,SkipOOMiniJOOL语言包含整型、布尔型和字符串型3类字面常量(参见3.2节)。literal=INTEGER_LITERAL | BOOLEAN_LITERAL| STRING_LITERAL字面常量的取值范围和在内存中的表示定义如下。整型常量:在内存中占4个字节,是有符号整型,其取值范围在-2147483648到2147483647之间。布尔型常量:有两种可能的值:true和false。boolean型值在内存中的表示由编译器决定。字
7、符串型常量:是由一对双引号括起来的零个或多个字符组成的字符序列。一个长度为n(n0)的字符串常量在内存中将占n+1个字节,前面n个字节依次存放字符串常量中各字符的ASCII码值,最后一个字节存放的是0,表示字符串结束。例如,保存长度为3的字符串常量abc将需要4个字节的内存空间。(3) 整型值上的运算SkipOOMiniJOOL语言提供以下在int型值上的运算,这些运算所完成的功能与Java语言中的完全相同。比较运算,其结果为boolean型值。数值比较运算符:、=;数值相等运算符:=和!=。算术运算,其结果为int型值。一元的正、负运算符:+和-;乘除运算符:*(乘)、/(整除)和%(求余)
8、;加减运算符:+和-。附录2列出了各种运算符的优先级和结合性。(4) 布尔值上的运算SkipOOMiniJOOL语言提供以下在boolean型值上的运算。关系运算符:=和!=;逻辑运算符:!(逻辑非)、&(逻辑与)和|(逻辑或)。(5) 字符串值上的运算SkipOOMiniJOOL语言不提供任何在字符串值上的运算。2. 变量(1) 变量的声明在SkipOOMiniJOOL语言以及后面的MiniJOOL语言中,除数组元素外,程序中的所有变量都必须显式声明。这些显式声明的变量根据其被声明的位置和方法,可以分为域变量、局部变量和形参变量3类。如果一个变量声明为数组类型,则将自动产生一组无名变量,即数
9、组元素,这些数组元素在程序中可以通过数组引用表达式(类型为数组类型)后跟下标表达式来访问,例如“ai%2”。对于一个长度为N(N取值为正整数)的数组来说,其元素的下标取值范围为0N-1的整数。此外,在程序中可以通过“数组名.length”获得指定数组的长度。下面给出SkipOOMiniJOOL和MiniJOOL语言中的变量声明文法,域、局部变量和形参分别通过域声明field_declaration、局部变量声明语句local_variable_declaration_statement和形参声明formal_parameter来声明。 示例代码4-1 变量声明对应的文法field_declar
10、ation=“static” “final” type variable_declarators “;”local_variable_declaration_statement=type variable_declarators “;”formal_parameter=type variable_declarator_idvariable_declarators=variable_declarator “,” variable_declarator variable_declarator=variable_declarator_id “=” variable_initializer varia
11、ble_declarator_id=IDENTIFIERvariable_initializer=expression | array_initializerarray_initializer=“” expression “,” expression “”从上面的文法中可以看出,一条域声明或局部变量声明语句中可以同时声明多个变量,各个变量声明符(variable_declarator)之间通过逗号分隔;此外,变量在声明时还可以指定初始化表达式(variable_initializer)。下面分别介绍各类变量的作用域和生存期。域(也称为成员变量)。在MiniJOOL语言中,域进一步分为类变量和实
12、例变量两大类:声明为static的域称为类变量,它是类的数据成员,在类初始化时会为之创建一个变量;未声明为static的域称为实例变量,它是类实例(也称为对象)的数据成员,在创建类的每一个新实例时都会为之创建相应的一个新变量。由于SkipOOMiniJOOL语言是非面向对象的,程序中只有一个类且不存在类实例的概念,因此程序中所有的域都是类变量,可以把它们看成是SkipOOMiniJOOL程序中的全局变量。类变量的作用域是整个程序,其生存期从程序运行时开始直到程序运行结束为止。实例变量的作用域是其所属类声明的类体,除此之外,其作用域由类实例的作用域决定,在类实例的作用域内可以通过受限名“.”来访
13、问相应的实例变量。实例变量的生存期随类实例的创建而开始,随类实例的消亡而结束。局部变量。每条局部变量声明语句只从属于一个语句块。局部变量的作用域是从其声明处开始,到拥有该声明语句的语句块结束时为止。当程序执行进入到某个语句块时,将为该语句块中声明的每个局部变量创建一个新变量;当退出该语句块时,所创建的局部变量将不再存在。形参。形参的作用域是其所属方法的方法体;它在方法被调用时创建,而在方法体执行结束时不再存在。需要强调的是,虽然SkipOOMiniJOOL语言中的域都应该声明为static,但是为了方便SkipOOMiniJOOL程序员,这里允许在声明域时省略static。为此,就要求Skip
14、OOMiniJOOL编译器必须能识别出程序中存在的这种省略情况(示例代码4-2),给出警告信息,并将这个域默认为static类别。示例代码4-2 SkipOOMiniJOOL程序示例:全局变量声明缺少staticclass Program int i; / 警告:缺少static static void main( ) 在上述各类变量中,只有域可以声明为final类别。声明为final的变量必须且只能被显式赋值一次。如果某final变量的显式赋值是静态完成的(即编译时完成),那么该final变量就相当于程序中的常量。如果final变量在程序中被赋值多次,将产生编译时错误。在SkipOOMini
15、JOOL程序中,final类别的域必须在声明时带初始化表达式,即所有的final变量都相当于程序中的常量。例如static final int n = 5; static final int a = 1, 2;否则,将产生编译时错误。(2) 变量的类型与存储特点根据变量所声明的类型,可以将变量分成整型、布尔型、字符串型和一维数组类型4类。当一个变量被创建时,意味着这个变量被绑定到了一个存储单元,在程序中通过变量可以访问这个存储单元。变量所绑定的存储单元的地址称为变量的左值,而存储单元中的内容称为变量的右值,或简称值。接下来简述各类变量所对应的存储单元特点。整型变量:其对应的存储单元占4个字节,
16、存储单元中的内容表示-2147483648到2147483647之间的一个值。布尔型变量:其对应的存储单元中存放的是表示true或false的一个值,值的具体表示形式以及所占空间大小由编译器决定。字符串型变量:其对应的存储单元存放的是一个引用值,即对某字符串常量的引用,引用值的具体表示形式和存储结构由编译器决定。数组变量:其对应的存储空间至少包含一个用于顺序存放数组元素值的连续空间,具体的存储结构由编译器决定。数组变量的值是一个引用值,表示对该数组存储空间的基址的引用。对于数组类型的域变量或局部变量来说,其值在该变量声明之后一直保持不变;而对于数组类型的形参变量来说,其值是可变的,用来引用相应
17、的实参数组。(3) 变量的初值在SkipOOMiniJOOL程序中,每个变量在使用前必须有值。全局变量或数组元素(不论数组变量是局部的还是全局的)在创建时按默认值初始化。int型变量的默认值是0,boolean型变量的默认值是false,String型变量的默认值是编译器内置的null,表示空引用。形参变量被初始化为由调用者提供的对应实参的值。局部变量必须在使用前被显式赋值,但数组元素除外。全局变量在声明时可以带初始化表达式,如 static int a = 4; static int2 b = a, 2; static String ss=“year”, “month”, “day”;全局变
18、量的初始化表达式在执行main( )前被求值并被赋给相应的全局变量。局部变量在声明时也可以带初始化表达式,它在程序运行到这个声明语句时被求值并赋给相应的局部变量。针对数组变量在声明时的初始化,SkipOOMiniJOOL语言中的规定与Java语言不太一样。在Java语言中,当数组变量在声明时如果含有初始化表达式(即包含在花括号内、由逗号分隔的表达式序列)时,则不允许指定数组的长度,数组变量的长度由初始化表达式中的表达式个数决定;此外,花括号中的表达式序列允许以逗号结尾。例如,“int2 b=1, 2;”将产生编译错误,而可以改为“int b=1, 2;”或“int b=1, 2,;”。在Ski
19、pOOMiniJOOL语言(MiniJOOL语言也是类似的)中,数组的初始化表达式同样是包含在花括号内、由逗号分隔的表达式序列,但是它不允许以逗号结尾;当数组变量在声明时含有初始化表达式时,可以同时指定数组的长度,并且要求指定的长度必须与初始化表达式中的表达式个数一样。例如“int2 b=1, 2;”和“int b=1, 2;”是正确的,但是“int3 b=1, 2;”和“int2 b=1, 2,;”将产生编译时错误。需要特别注意的是,在数组变量声明的初始化表达式中,不能认为该数组变量的任何一个元素已经被赋值。即形如“int2 b=1,b0;”的数组变量声明语句是错误的。因为在数组变量初始化表
20、达式中,数组元素b0还没有被赋值。(4) 变量的使用特征在SkipOOMiniJOOL语言中,全局变量、局部变量、形参变量都可以声明为int、boolean、String或数组类型。但是,当全局变量和局部变量声明为数组类型时,必须显式指定数组的长度或者是通过初始化表达式来隐式指定数组的长度;而当形参变量声明为数组类型时,则不必指定数组的长度,这时形参数组的长度将由传入的实参数组的长度决定。如果数组类型的形参变量在声明时指定了长度,则编译器应给出警告信息,指出这个长度是无效的,其长度由实参数组的长度决定。下面简述各种类型变量的使用特征。int型变量只能被赋予int型值。一旦它被赋值,则可以参加比
21、较运算和算术运算,也可以作为实参传递给int型形参。boolean型变量只能被赋予boolean类型的值。一旦它被赋值,则可以参加关系运算和逻辑运算,也可以作为实参传递给boolean型形参。String型变量只能被赋予字符串常量或已被赋值的String型变量。如前所述,String型变量保存的是对字符串常量的引用,这样,一个字符串常量就可以被多个String型变量所引用。在SkipOOMiniJOOL语言中,由于空引用null是编译器内置的,程序中不能直接使用null,因此,SkipOOMiniJOOL程序中的String型变量必须在显式被赋值为一个实际字符串常量的引用之后才可以被使用,它可
22、以作为实参传递给String型形参。数组变量只能作为实参传递给元素类型相同的数组类型的形参,这时形参将引用实参数组。数组类型的形参可以接收不同长度、但元素类型相同的数组类型的实参。在程序中,可以通过“.length”获得数组变量的长度。数组变量不能被赋值给另一个数组变量,否则产生编译时错误。在任何使用数组元素的地方,编译运行系统需要对数组元素的下标做是否越界的检查,这种越界检查要求尽量在编译时完成,对于编译时无法判断的,则应在运行时检查。当数组元素越界时,应产生编译时或运行时错误。除此之外,数组元素在使用上与类型和它相同的变量没有差异。4.1.2 语句程序的执行顺序由语句控制。除输入、输出之外
23、,SkipOOMiniJOOL语言的语句种类是C语言或Java语言中语句种类的子集,它含有空语句、表达式语句、if语句、while语句、break语句、continue语句、return语句以及语句块(又称复合语句)。此外,SkipOOMiniJOOL语言还提供print语句以支持对int、boolean或String型表达式的打印输出,提供read语句以支持对具有左值的int型表达式的值输入。1. 语句块语句块(block)是包含在一对花括号中的由若干条语句(statement)和局部变量声明语句(local_variable_declaration_statement)组成的语句序列,其E
24、BNF文法定义如下:block=“” block_statements “”block_statements=block_statement block_statement block_statement=local_variable_declaration_statement | statement2. 局部变量声明语句局部变量声明语句用来声明一个或多个局部变量,其EBNF文法如示例代码4-1所示。每条局部变量声明语句只从属于一个语句块;在一个语句块中,局部变量声明语句与其他语句混杂在一起。局部变量声明语句是可执行语句,当它执行时,自左至右依次处理其所包含的各个变量声明符(variable_
25、declarators)。如果一个变量声明符中含有初始化表达式,则对该初始化表达式求值并将值赋给相应的变量;如果没有初始化表达式,则编译器必须检查该变量在每次使用前是否被赋值。所有的局部变量(注意:数组元素除外)在使用前都必须被显式赋值,且必须先声明再使用。3. 语句除局部变量声明语句之外的其他语句,包括语句块,都统称为statement,其EBNF文法描述如下:statement=“;” | statement_expression “;” | “break” “;” | “continue” “;”| “return” expression “;” | block | “if” “(” e
26、xpression “)” statement| “if” “(” expression “)” statement “else” statement| “while” “(” expression “)” statement| “print” “(” expression “)” “;”| “read” “(” lvalue “)” “;”上述文法会引起if语句的“悬空else问题”(dangling else problem)。针对这种问题, MiniJOOL语言及其子集规定每个else语句都与其上最近的if语句相匹配。图41展示了if-else的就近匹配原则,其中相互匹配的if-else
27、对使用折线连接在一块。图41 MiniJOOL程序示例:if-else的就近匹配为了解决“悬空else问题”,可以通过改写文法来保证按if-else的就近匹配原则来进行语法分析,下面的EBNF文法就体现了这种匹配原则。 示例代码4-3 解决了“悬空else问题”的EBNF文法statement=stmt_no_trailing_substmt| if_then_stmt | if_then_else_stmt | while_stmtstmt_no_trailing_substmt=block | “;” | statement_expression “;”| “break” “;” | “c
28、ontinue” “;” | “return” expression “;”| “print” “(” expression “)” “;” | “read” “(” lvalue “)” “;”if_then_stmt=“if” “(” expression “)” statementif_then_else_stmt=“if” “(” expression “)” stmt_no_short_if “else” statementif_then_else_stmt_no_short_if=“if” “(” expression “)” stmt_no_short_if “else” stm
29、t_no_short_ifstmt_no_short_if=stmt_no_trailing_substmt| if_then_else_stmt_no_short_if| while_stmt_no_short_ifwhile_stmt=“while” “(” expression “)” statementwhile_stmt_no_short_if=“while” “(” expression “)” stmt_no_short_if4. 表达式语句表达式语句由statement_expression后跟分号组成。在SkipOOMiniJOOL语言中,可以组成语句的表达式有赋值表达式和方
30、法调用,关于这两类表达式的特点将在4.1.3节中介绍。statement_expression=assignment_expression | method_invocation4.1.3 表达式以下按基本表达式、左值表达式和一般的表达式分类进行说明。1. 基本表达式基本表达式是构成其他表达式的基本元素,包括字面常量(literal,见4.1.1节)、由一对圆括号括起的表达式、方法调用表达式和数组访问表达式。primary=literal | “(” expression “)” | method_invocation | array_accessmethod_invocation=IDENT
31、IFIER “(” argument_list “)”argument_list=expression “,” expression array_access=IDENTIFIER “” expression “”方法调用表达式用来调用方法。在编译时,对方法调用表达式的处理主要包括:查找要调用的方法声明,检查方法调用表达式中的实参数目是否与该方法声明中的形参数目相等,检查各实参类型是否与对应的形参类型相兼容,再检查对方法调用结果的使用是否满足该方法声明的返回类型。如果这期间发生了错误,则产生编译时错误。在运行时,需要依次计算方法调用表达式中各实参表达式的值,再按照值传递方式进行虚实结合。数组访
32、问表达式将引用一个数组元素,它由数组引用表达式和下标表达式组成。数组引用表达式的类型必须是一个数组类型,下标表达式的类型必须是int型,否则产生编译时错误。此外,需要尽可能地在编译时对下标进行越界检查,如果编译时无法判断,则应在运行时进行越界检查。如果发生越界,则产生编译时或运行时错误。2. 具有左值的表达式所有的表达式都具有右值,只有变量、数组访问表达式具有左值。由一对圆括号括起的左值表达式也视为左值表达式。lvalue=IDENTIFIER | array_access | “(” lvalue “)”3. 一般的表达式一般的表达式由变量、基本表达式、一元运算表达式、二元运算表达式和赋值表
33、达式等组成;此外,还包括取数组长度的表达式,如a.length。赋值表达式要求赋值运算符左边的运算对象必须具有左值。下面给出了一般表达式的EBNF文法。expression=IDENTIFIER | IDENTIFIER “.” “length” | primary| unary_operator expression| expression binary_operator expression| assignment_expressionunary_operator=“+” | “-” | “!”binary_operator=“*” | “/” | “%” | “+” | “-”| “=”
34、 | “!=” | “” | “” | “=” | “|” | “&”assignment_expression=lvalue assignment_operator assignment_expressionassignment_operator=“=” | “*=” | “/=” | “%=” | “+=” | “-=”附录2列出了各种运算符的优先级和结合性。它们的含义与Java中的一样,这里不再赘述。需要强调的是,逻辑与(&)和逻辑或(|)运算的求值过程是不完全计算(也称为短路计算)。即,当计算两个子表达式a和b的逻辑与时,如果a为false,则无需计算b,即可得到整个表达式的值为fal
35、se;当计算两个子表达式a和b的逻辑或时,如果a为true,则无需计算b,即可得到整个表达式的值为true。上面的表达式文法会引起许多表达式求值的二义性。为解决这个问题,可以根据算符的优先级和结合性,利用编译原理教材中介绍的方法,改写文法来消除歧义;也可以在CUP或JavaCC支持的文法描述中利用优先级声明语句描述相关终结符的优先级和结合性,从而达到消除歧义的目的。4.1.4 SkipOOMiniJOOL程序的总体结构在了解了SkipOOMiniJOOL语言所支持的类型、常量、变量、表达式和语句之后,给出如下的SkipOOMiniJOOL程序的总体结构的EBNF文法:program=class
36、_declarationclass_declaration=“class” IDENTIFIER class_bodyclass_body=“” class_body_declaration class_body_declaration “”class_body_declaration=field_declaration | method_declaration一个SkipOOMiniJOOL程序只包含一个类声明,类名是任意的标识符。类体(class_body)可以包含若干个域声明和方法声明,域声明和方法声明混杂在一起。关于域声明的文法见示例代码4-1,这里简述方法声明的文法。 示例代码4-4
37、 方法声明的相关文法 method_declaration=method_header method_bodymethod_header= “static” ( type | “void” ) method_declaratormethod_declarator=IDENTIFIER “(” formal_parameter_list “)”formal_parameter_list=formal_parameter “,” formal_parameter method_body=blockblock=“” block_statements “”方法用来声明可以被调用执行的代码。一个方法由方
38、法头(method_header)和方法体(method_body)组成。方法头声明了该方法的形参及其类型、以及返回类型,方法体是一个语句块(见4.1.2节)。在SkipOOMiniJOOL语言中,方法的形参类型可以是int、boolean、String或一维数组类型(元素类型为int、boolean、String之一),返回类型可以是int、boolean或者无类型,即void。如果一个方法的返回类型声明为void,则方法体中不能含有任何带表达式的return语句。如果一个方法的返回类型不是void,则方法体中的每一个出口都必须有带表达式的return语句且表达式的类型必须与返回类型兼容。与
39、域声明类似,MiniJOOL语言中的方法也可以声明为static或非static,声明为static的方法称为类方法(class method),在类方法的方法体中,不能引用这个类或其超类中的非static成员,否则导致编译时错误;而未声明为static的方法称为实例方法(instance method),实例方法在调用时总是与一个类实例相关联,在实例方法的方法体中可以引用这个类或其超类中的所有成员。对于SkipOOMiniJOOL语言来说,其中的方法都应该声明为static,即为类方法。但是,同样为了方便SkipOOMiniJOOL程序员,这里允许在声明方法时省略static。为此,就要求S
40、kipOOMiniJOOL编译器必须能识别出程序中存在的这种省略情况,给出警告信息,并将这个方法默认为static类别。4.1.5 一个SkipOOMiniJOOL程序示例示例代码4-5给出了一个完整的SkipOOMiniJOOL程序,它是快速排序算法的一个实现。这个程序包含一个名为QuickSort的类声明,类体中声明了一个全局变量data,它是一个长度为10的int型数组,用于保存排序前后的10个整数。类体中还声明有方法main( )、qsort( )、printArray( )、partition( )、readArray( )。main( )方法的方法体中使用了全局变量data,并且调
41、用了qsort( )、readArray( )和printArray( )方法;而data( )、qsort( )、readArray( )、printArray( )都在main( )方法之后声明。这种“后声明先使用”的情况在SkipOOMiniJOOL语言中只适用于域和方法,其余的变量都要求先声明再使用。示例代码4-5 SkipOOMiniJOOL程序示例:快速排序class QuickSort static int i = 1; static void main( ) int flag; while (true) if (i1 ) print(nContinue? 1-yes, 0-no
42、: ); read(flag); if (flag = 0 ) break; else if (flag!=1) continue; readArray( ); print(nExecuting No.); print(i); print( quicksort:n Before sorting:); printArray(data); qsort(data, 0, data.length - 1); print(n After sorting:); printArray(data); i = i+1; static int 10 data = 123,52,8,74,62,74,55,44,7
43、4,80; static void qsort(int a, int low, int high) if (low = high) return; int p = partition(a, low, high); qsort(a, low, p-1); qsort(a, p + 1, high); static void printArray(int a) int i = 0; String str = n Member ; while (i a.length) print(str); print(i); print( is ); print( ai); i=i+1; static int p
44、artition(int a, int low, int high) int pivot = alow, i = low, j = high; while (i j) while (i= pivot) j=j-1; ai = aj; while (ij & ai = pivot) i=i+1; aj = ai; ai = pivot; return i; / end of partition static void readArray( ) int i = 0; print(Please input ); print(data.length); print( integers to be so
45、rted.n); while (i data.length) read(datai); i=i+1; 需要指出的是,如果域声明中含有初始化表达式,则初始化表达式中出现的变量要求先声明再使用。例如,以下域声明序列是合法的:static int a = 2;static int b = a;而以下域声明序列是错误的,因为域a的初始化表达式引用了在其后声明的域b:static int a = b;static int b = 2;4.2 本章课程设计概述本章安排如下5个课程设计。课程设计3-1 手工编写一个分析合法SimpleBlock程序的语法分析器。注意:对程序而言,其“合法”性可以指符合语言的语法,也可以指不仅符合语言的语法还符合语言的语义。本章主要讨论语言的语法,而语法是通过文法来表示的,故本章所述的“合法”程序是指符合语言文法的程序。在课程设计3-1中,将采用有回溯的递归下降分析法构造SimpleBlock语言的语法分析器,它能分析一个合法的SimpleBlock程序,并生成对应的AST。读者需要知道递归下降方法所适用的文法范围,以及如何将一个不适用的文法改写为一个适用的文法。在现代编译技术中,
限制150内