编号:
实习 | 一 | 二 | 三 | 四 | 五 | 六 | 七 | 八 | 九 | 十 | 总评 | 教师签名 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
成绩 |
Mini语言包含程序设计所需要的最基本语言成分,包括
程序定义语句 :以 program 开头 end 结尾,中间需要有 main()函数
赋值语句 :var 标识符 = 表达式;
算数表达式:包含 + - * / ()运算
逻辑表达式:包含 II && ! ()运算
if-else 语句:if (逻辑表达式或变量)begin 程序体 end else begin 程序体 end
while 循环语句:while(逻辑表达式或变量)begin 循环体 end
break 语句:break;
简化后的单词编码表为
单词符号 | 编码 | 单词符号 | 编码 |
---|---|---|---|
main | 1 | + | 15 |
if | 2 | - | 16 |
else | 3 | * | 17 |
while | 4 | / | 18 |
program | 5 | = | 19 |
begin | 6 | && | 20 |
end | 7 | || | 21 |
var | 8 | ! | 22 |
break | 9 | == | 23 |
true | 10 | 整数常量表 | 24 |
false | 11 | 浮点数常量表 | 25 |
( | 12 | 标识符表 | 26 |
) | 13 | ||
; | 14 |
先有词法分析器将程序扫描一遍,得到Token串,例如main转为<1,->,if转为<2,->...!转为<22,->,特别的标识符,整数常量,浮点数常量有两个表分别转换为<23,i>.<24,i>,<25,i>.
但是在语法分析的文法中,为了方便理解,这里依然用main等字符串,而不是使用<1,->进行分析,其中标识符用id表示,整数常量用wholenum表示,浮点数用floatnum表示。
得到如下的上下文无关文法G(S),(小写表示终结符号,大写表示非终结符号)
S ->PROGRAM //PROGRAM为整个语法程序
PROGRAM -> program PROBODY end
//PROBODY为程序体
PROBODY -> main begin PROSTATEMENT end
//PROSTATEMENT为程序语句
PROSTATEMENT -> ASSIGNMENT PROSTATEMENT | CONDITION PROSTATEMENT | LOOP PROSTATEMENT | BREAK PROSTATEMENT | ε //ASSIGNMENT为赋值语句 COUNT 为算数表达式 LOGIC 为逻辑表达式 CONDITION 为if else 分支语句 LOOP 为while循环语句 BREAK 为break语句,用于跳出循环//
ASSIGNMENT -> var id = VALUE ;
//VAlUE 为赋值语句可以赋的值,id为变量,属于词法分析器中单词编码表中的标识符
VALUE -> id | COUNT | LOGIC
COUNT -> TF F -> +T | -T | ε T -> GH H -> *G | /G | ε G -> (COUNT) | wholenum | floatunm | id
//wholenum 为词法分析器中单词编码表中的整数,floatnum为词法分析器中词法编码表中的浮点数,整个算术运算符表达,每个算数单元可以是数字也可以是变量
LOGIC -> IJ J -> ||I | ==I | ε I -> KL L -> &&K |ε K -> !M | M M -> (LOGIC) | true | false | id
//这里是逻辑表达式,每个逻辑单元可以是true false 也可以是变量
CONDITION -> if (LOGIC) begin PROSTATEMENT end ELSE
//ELSE是else语句
ELSE -> begin PROSTATEMENT end | ε
//if else语句中,先进行逻辑语句的判读,然后进入程序语句,else是可空的
LOOP -> while (LOGIC) begin PROSTATEMENT end
//循环语句
BREAK -> break ;
//break跳出语句
使用递归下降算法,结合词法分析器,用get_nexttoken();
函数表示取到下一个token串,用w.nexttoken();表示w后面的token串,用Token数据结构表示token串,所有的终结符号都使用Token进行定义,由于这些字符原本就是c语言的关键字,所以为了区分,将所有Mini文法中的关键字在程序表达中都加上s,边界符使用S+所对应的数字表示,如‘(’用S12表示。
Token mains = new Token(1);
Token ifs = new Token(1);
Token elses = new Token(1);
Token whiles = new Token(1);
.
.
.
Token S12 = new Token(12);
Token S13 = new Token(13);
Token S14 = new Token(14);
Token S15 = new Token(15);
.
.
.
Token id = new Token(26);
Token wholenum = new Token(24);
Token floatnum = new Token(25);
get_nexttoken(w);从词法分析器中取下一个token串,存放在全局变量w中
每个非终结符号都是一个函数,如下所示
void PROGRAM (){
if(w==programs)
{
get_nexttoken(w);
PROBODY();
if(w==end){
print("程序正确!");
}
else{
error(0);
}
}
else error(0);
}
void PROBODY (){
if(w==mains){
get_nexttoken(w);
if(w==begins){
get_nexttoken(w);
PROSTATEMENT();
if(w==ends){
get_nexttoken(w);
}
else error(1);
}
else error(1);
}
else error(1);
}
void PROSTATEMENT (){
if(w==vars) {
ASSIGNMEN();
PROSTATEMENT ();
}
else if(w==ifs) {
CONDITION();
PROSTATEMENT ();
}
else if(w==whiles) {
LOOP();
PROSTATEMENT ();
}
else if(w==breaks) {
BREAK();
PROSTATEMENT ();
}
else if(w==ends);
else error(2);
}
void ASSIGNMENT (){
if(w==var){
get_nexttoken(w);
if(w==id){
get_nexttoken(w);
if(w==S19){
get_nexttoken(w);
VALUE();
if(w==S14)
get_nexttoken(w);
else error(3);
}
else error(3);
}
else error(3);
}
else error(3);
}
void VALUE (){
if(w==wholenum) COUNT();
else if(w==floatnum) COUNT();
else if(w==trues) LOGIC();
else if(w==falses) gLOGIC();
else if(w==id&&w.nexttoken==S14) get_nexttoken(w);
else if (!(w==id&&w.nexttoken==S14)||w==S12||w==id){
if(w==S12&&w.nexttoken==id){
if(w.nexttoken.nexttoken==(S15||S16||S17||S18){
COUNT();
}
else if(w.nexttoken.nexttoken==(S20||S21||S23){
LOGIC();
}
else error(4);
}
else if(w==S12&&(w.nexttoken==(wholenum||floatnum))){
COUNT();
}
else if (w==S12&&(w.nexttoken==(true||false))){
LOGIC();
}
else if(w==id&&(w.nexttoken==(S15||S16||S17||S18)){
COUNT();
}
else if(w==id&&(w.nexttoken==S20||S21||S23)){
LOGIC();
}
else error(4);
}
else error(4);
}
void COUNT(){
T();
F();
}
void F(){
if(w==S15||w==S16){
get_nexttoken(w);
T();
}
}
void T(){
G();
H();
}
void H(){
if(w==S17||w==S18){
get_nettoken(w);
G();
}
}
void G(){
if(w==S12){
get_nexttoken(w);
COUNT();
if(w==S13){
get_nexttoken(w);
}
else error(5);
}
else if(w==(wholenum||floatnum||id)){
get_nexttoken(w);
}
else error(5);
}
void LOGIC(){
I();
J();
}
void J(){
if(w==(S21||23)){
get_nexttoken(w);
I();
}
}
void I(){
K();
L();
}
void L(){
if(w==S20)
{
get_nexttoken(w);
K();
}
}
void K(){
if(w==!){
get_nexttoken(w);
M();
}
else{
M();
}
}
void M(){
if(w==S11){
get_nexttoken(w);
LOGIC();
if(w==S12){
get_nexttoken(w);
}
else error(6);
}
else if(w==(trues||falses||id)){
get_nexttoken(w);
}
else error(6);
}
void CONDITION(){
if(w==ifs){
get_nexttoken(w);
if(w==S12){
get_nexttoken(w);
LOGIC();
if(w==S13){
get_nexttoken(w);
if(w==begins){
get_nexttoken(w);
PROSTATEMNT();
if(w==ends){
get_nexttoken(w);
ELSE();
}
else error(7);
}
else error(7);
}
else error(7);
}
else error(7);
}
else error(7);
}
void ELSE(){
if(w==begins){
get_nexttoken(w);
PROSTATEMENT();
if(w==ends){
get_nexttoken(w);
}
else{
error(7);
}
}
}
void LOOP(){
if(w==whiles&&(w.nexttoken==S12)){
get_nexttoken(w);
get_nexttoken(w);
LOGIC();
if(w==begins){
get_nexttoken(w);
PROSTATEMENT();
if(w==ends){
get_nexttoken(w);
}
else error(8);
}
else error(8);
}
else error(8);
}
void BREAK(){
if(w==breaks&&(w.nexttoken==S14)){
get_nexttoken(w);
get_nexttoken(w);
}
else{
error(9);
}
}
程序出错时,其实w记录了出错所在的token串,只需用另一个变量记录token串所在整个程序的位置,在使用get_nettoken()
取到下一个token串时,变量加一,就可以判断程序出错的位置。
错误函数 | 错误产生式 | 错误类型 |
---|---|---|
error(0) | PROGRAM -> program PROBODY end | 程序定义错误 |
error(1) | PROBODY -> main begin PROSTATEMENT end | 程序体定义错误 |
error(2) | PROSTATEMENT -> ASSIGNMENT | CONDITION | LOOP | BREAK | ε | 语句定义不合法 |
error(3) | ASSIGNMENT -> var id = VALUE ; | 赋值语句格式错误 |
error(4) | VALUE -> id | COUNT | LOGIC | 赋值语句右值不正确 |
error(5) | G -> (COUNT) | wholenum | floatunm | id | 算数表达式格式错误 |
error(6) | M -> (LOGIC) | true | false | id | 逻辑表达式格式错误 |
error(7) | CONDITION -> if (LOGIC) begin PROSTATEMENT end ELSE | 条件语句错误 |
error(8) | LOOP -> while (LOGIC) begin PROSTATEMENT end | 循环语句错误 |
error(9) | BREAK -> break ; | 跳出语句错误 |
测试用例中所包括的语法单位应该尽可能覆盖Mini语言的文法中的所有非终结符,包括程序定义、变量声明、if-else语句、赋值语句、条件语句、循环语句、跳出语句、算数表达式和逻辑表达式。
测试用例如下
program
main begin
var logic=true;
var i=0;
while(i<10)
begin
if(logic&&(!(!true)))
begin
i=i+1;
logic=!logic;
end
else
begin
logic=!logic;
end
end
end
end
测试结果,无错误!