语法分析
引子:
语法分析器检查词法分析器输出的记号流是否符合源语言的语法规则;并依据这些规则体现出的程序的各种语法结构的层次,为记号流构建用Eclipse AST表示的抽象语法树AST。
通过些实践,了解并掌握语法分析器的多种构造方法,如手工编写、用语法分析的生成工具CUP等来构造语法分析器等等;同时也将学会如何恢复和处理语法分析中遇到的错误。
一、SkipOOMiniJOOL语言的语法
1、类型、常量和变量
(1)类型
int 、boolean、String 和三种类型的一维数组类型。
EBNF文法定义:
type = primitive_type|”String”|array_type
primitive_type = “int”|”boolean”
array_type = (primitive_type|”String”) “[“ [ constant_expression]” ]”
(2)字面常量
int : -2147483648~2147483647
boolean: true、false
(3)int 值上的运算
比较运算其结果为boolean值
数值比较运算符:<、<=、>、>=
数值相等运算符:==和!=;
算术运算,其结果为int型值
一元的正、负运算符:+和-
乘除运算符:*、/、%
加减运算:+、-
(4)布尔值上的运算
关系运算符:==和!=
逻辑运算符:!(非)、&&、||
(5)、变量(域变量field_declaration、局部变量local_variable_declaration_statement、形参变量formal_parameter)
a、变量的声明(文法):
field_declaration = [“static”][“final”] type variable_declarators”;”
local_variable_declaration_statement = type variable_declarators”;”
formal_parameter = type variable_declarator_id
variable_declarators = variable_declarator{“,”varible_declarator}
variable_declarator = variable_declarator_id [“=”variable_initializer] //或初始化表达式
variable_declarator_id = IDENTIFIER
variable_initializer = expression|array_initializer
array_initializer = “{“expression{“,”expression}”}”
b、变量的类型与存储特点
由变理声明的类型,可将变量分成整型、布尔型、字符串型和一维数组类型。当一个变量被创建时,意味着这个变量被绑定到了一个存储单元,在程序中通过变量可以访问这个存储单元。变量所绑定的存储单元的地址称为变量的左值,而存储单元的内容称为变量的右值,简称值。
c、变量的初值
全局变量或数组元素在创建时按缺省值初始化。int型变量的缺省值是0,boolean型变量缺省值是false,String型变量的缺泔省值是编译器内置的null,表示空引用。
形参变量被初始化为由调用者提供的对应实参的值。
局部变量必须在使用前被显式赋值。
全局变量在声明时可以带初始化表达 如:
static int a = 4;
static int [2] b = {a,2};
static String[] ss = {“year”,”month”,”day”};
补:数组声明中的初始化表达式
数组初始化表达式包含在花括号内、由逗号分隔的表达式序列,但是它不允许以逗号结尾;当数组变量在声明时含有初始化表达式时,可以同时指定数组的长度,并且要求指定长度与初始化表达式中的表达式个数一样。如“int[2] b = {1,2};” “ int[] b = {1,2};”正确,”int [3] b = {1,2};” 、int[2] b = {1,2,};”将产生编译时错误。
d、变量使用特征
int型:只能被赋int型值。赋值后可参加比较运算和算术运算,也可作为实参传给int型形参。
boolean型:只能被赋boolean类型的值。赋值后可参加关系运算逻辑运算,也可作为实参传递给boolean型形参。
String型:只能被赋字符串常量或已被告赋值的String型变量。
数组:数组变量只能作为实参传递给元素类型相同的数组类型的形参。数组类型的形参可以接收不同长度、但元素类型相同的数组类型的实参,可以通过“<数组变量>.length”获得数组变量的长度。数组变量不能被赋值给另一个数组变量,否则产生编译错误。
在任何使用数组的地方,编译运行系统需要对数组元素的下标做是否越界的检查。
2、语句
输入输出语句外,还有空语句、if语句、while语句、break语句、continue语句、return语句及语句块(亦称复合语句)。print语句以支持对int、boolean、或String型表达式的打印输出,提供read语句以支持对具有左值的int型表达式的值输入。
(1)、语句块
语句块(block)是包含在一对花括号中的由若干条语句(statement)和局部变量声明语句(local_variable_declaration_statement)组成的语句序列,EBNF定义:
block = “{“[block_statements]”}”
block_statements = block_statement{block_statement}
block_statement = local_variable_declaration_statement|statement
(2)、局部变量声明语句(见上)
(3)、语句
除局部变量声明语句之外的其他语句,包反括语句块,都统称为statement,EBNF:
statement = “;”|statement_expression”;”
|”break” “;” |”continue” “;”
|” return”[expression] “;” | block
|”if” “(“ expression “)” statement
|”if” “(“ expression “)” statement “else” statement
|”while” “(“ expression “)”statement
|”print” “(“ expression “)” “;”
|”read” “(“ lvalue “)” “;”
存在问题:“悬空else问题”
(4)、表达式语句
表达式语句由statement_expression后跟分号组成。可以组成语句的表达式有赋值表达式和方法调用。
statement_expression = assignment_exprssion|method_invocation
3、表达式
(1)、基本表达式
它是构成其他表达式的基本元素,包括字面常量(literal)、由一对圆括号括起的表达式、方法调用表达式、和数组访问表达式。
primary = literal | “(“ expression “)”|method_invocation|array_access
method_invocation = IDENTIFIER “(“[argument_list]”)”
argument_list = expression{ “,”expression }
array_access = IDENTIFIER “[“ expression “]”
(2)、具有左值的表达式
所有的表达式都具有右值,只有变量、数组访问表达式具有左值。由一对圆括号括起的左值表达式也视为左值表达式。
lvaue = IDENTIFIER|array_access| “(“ lvalue ”)”
(3)、一般的表达式
一般表达式由变量、基本表达式、一元运算表达式、二元运算表达式和赋值表达式等组成;还包括取数组长度的表达式,如a.length.赋值表达式要求赋值运算符左边的运算对象必须具有左值。EBNF:
expression = IDENTIFIER|IDENTIFIER “.” “length” |primary
|unary_operator expression
|expression binary_operator expression
|assignment_expression
unary_operator = “+”|”-“|”!”
binary_operator = “*”|”/”|”%”|”+”|”-“
|”==”|”!=”|”<”|”<=”|”>”|”>=”|”||”|”&&”
assignment_expression = lvalue assignment_operator assignment_expression
assignment_operator = “=”|”*=”|”/=”|”%=”|”+=”|”-=”
补:逻辑与(&&)和逻辑或(||)运算的求值过程是不完全计算的。即当计算表达式a和b的逻辑或(||)运算,如a为true则无需计算b;当计逻辑与时,如a为false,则无需计算b。
4、该语言程序的总体结构
了解语言所支持的类型、常量、变量、表达式和语句后,给出程序的总体结构EBNF:
program = class_declaration
class_declaration = “class”IDENTIFIER class_body
class_body = “{“ class_body_declaration { class_body_declaration } “}”
class_body_declaration = field_declaration | method_declaration
该语言程序只包含一个类声明,类名是任意的标识符。类体(class_body)可以包含若干域声明(见上)和方法声明。
方法声明EBNF:
method_declaration = method_header method_body //方法头声明了该方法的形参及其类型、以及返回类型。方法体是一个语句块。
method_header = [“static”](type|”void”) method_declarator
method_declarator = IDENTIFIER “(“[formal_parameter_list]”)”
formal_parameter_list = formal_parameter{ “,” formal_parameter }
method_body = block
block = “{“ [block_statements “]” “}”