YACC解析器首行语法错误,空测试程序亦报错求助
问题描述
我正在完成一项基于Lex和YACC的语言设计作业,任务是搭建一个简单的YACC解析器。但遇到两个问题:
- 传入测试文件后,解析器总是返回“首行存在语法错误”的提示,且似乎未执行到示例的第一条语句;
- 即使测试程序为空,也会返回错误信息。
Lex代码
%{ #include <stdio.h> #include "y.tab.h" void yyerror(char *); %} lowerLetter [a-z] letter [a-zA-Z] digit [0-9] signs [+-] integer {signs}?{digit}+ double {signs}?{digit}*(\.)?{digit}+ word {lowerLetter}+({letter}*{digit}*)* string \"[^\"]*\" day ("Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday") month ("January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December") time {day}(\,)(\ )[0-3][1-9](\ ){month}(\ ){digit}*(\ )[0-2][0-9](\:)[0-5][0-9](\:)[0-5][0-9]((\ )("GMT")(((\+)|(\-)){integer}(\:){integer})?)? sensor (\$)("s"){digit}* switch (\$)("sw"){digit} url ("http")("s")?("://")("www.")?[a-zA-Z0-9]*(\.)(.)* type ("integer"|"double"|"string"|"sensor"|"switch"|"url"|"boolean"|"time"|"letter") boolean ("true")|("false")|([a-zA-Z0-9]+((\ )*)?(((\=|\<|\>|\!)(\=))|(\>|\<))+((\ )*)?[a-zA-Z0-9]+)) identifier ({letter}*|{digit}*(\_)*)* %% while return WHILE; for return FOR; return return RETURN; sysin return SYSIN; sysout return SYSOUT; if return IF; main return MAIN; end return END; else return ELSE; fun return FUNCTION_IDENTIFIER; cons return CONS; isURL return IS_URL; connect return CONNECT; send return SEND; receive return RECEIVE; \\\n return NL; \. return DOT; \, return COMMA; \: return COLON; \; return SEMICOLON; \+ return PLUS_OP; \- return MINUS_OP; \* return MULTIPLY_OP; \/ return DIVIDE_OP; \% return MOD_OP; \# return HASHTAG; \$ return SENSOR_IDENTIFIER; \^ return POWER_OP; \_ return UNDER_SCORE; \? return QUESTION; \! return NOT; \( return LP; \) return RP; \{ return LCB; \} return RCB; \[ return LSB; \] return RSB; \= return ASSN_OP; \> return GT; \< return LT; \=\= return EQ; \>\= return GEQ; \<\= return LEQ; \!\= return NE; \&\& return AND; \|\| return OR; \/\/ return DS; {day} return DAY; {month} return MONTH; {time} return TIME; {type} return TYPE; {lowerLetter} return LOWER_LETTER; {letter} return LETTER; {integer} return INTEGER; {string} return STRING; {sensor} return SENSOR; {switch} return SWITCH; {double} return DOUBLE; {boolean} return BOOLEAN; {url} return URL; {word} return WORD; {identifier} return IDENTIFIER; [ \t] ; %% int yywrap(void) { return 1; }
YACC代码
%{ #include "stdio.h" #include <stdlib.h> void yyerror(char *); extern int yylineno; #include "y.tab.h" int yylex(void); %} %token MAIN %token DOT COMMA COLON SEMICOLON UNDER_SCORE QUESTION LP RP LCB RCB LSB RSB DS NL %token WHILE FOR RETURN SYSIN SYSOUT IF END ELSE FUNCTION_IDENTIFIER NOT CONS PLUS_OP MINUS_OP MULTIPLY_OP DIVIDE_OP MOD_OP HASHTAG SENSOR_IDENTIFIER POWER_OP %token ASSN_OP GT LT EQ GEQ LEQ NE AND OR DAY MONTH TIME TYPE IS_URL CONNECT SEND RECEIVE %token LOWER_LETTER LETTER WORD STRING INTEGER DOUBLE BOOLEAN SENSOR SWITCH URL IDENTIFIER %nonassoc ELSE %left PLUS_OP MINUS_OP %left MULTIPLY_OP DIVIDE_OP %left POWER_OP MOD_OP %start program %% program: stmts {printf("\rProgram is valid.\n");}; stmts: stmt | stmts stmt; stmt: if_stmt | non_if_stmt ; if_stmt: IF LP logical_expr RP LCB stmts RCB | IF LP logical_expr RP LCB stmts RCB ELSE LCB stmts RCB; non_if_stmt: loops | aritmetic_op | func_call | func_dec | initialize | decl | decl_ini | input_stmt | output_stmt //| comment | url_checker | send | receive | connect; loops: while_loop | for_loop; while_loop: WHILE LP logical_expr RP LCB stmts RCB; for_loop: FOR LP decl_ini SEMICOLON logical_expr SEMICOLON aritmetic_op RP LCB stmts RCB; initialize: IDENTIFIER ASSN_OP value SEMICOLON; logical_expr: logical_term logical_op logical_term | logical_expr logical_connector logical_term | boolean_stmt; boolean_stmt: BOOLEAN | NOT boolean_stmt; logical_term: term; //| logical_term AND term; logical_connector: AND | OR; term: IDENTIFIER | constant; constant: CONS IDENTIFIER; logical_op: EQ | NE | LT | GT | LEQ | GEQ; decl: TYPE term SEMICOLON; decl_ini: TYPE term ASSN_OP value SEMICOLON; value: number | STRING | SENSOR | SWITCH | URL | BOOLEAN | LETTER | TIME; number: DOUBLE | INTEGER; /* value_list: value | value_list COMMA value; array_decl: TYPE term LSB RSB SEMICOLON; array_ini: TYPE term LSB RSB ASSN_OP LSB value_list RSB SEMICOLON | term ASSN_OP LSB value_list RSB SEMICOLON; get_array_val: term LSB INTEGER RSB; */ func_dec: FUNCTION_IDENTIFIER TYPE IDENTIFIER LP arguments RP LCB function_block RCB; func_call: IDENTIFIER LP arguments RP SEMICOLON; function_block: stmts | stmts RETURN value | stmts RETURN term; arguments: TYPE term | arguments COMMA TYPE term; aritmetic_op: addition | subtraction | multiplication | division | modulo | power; addition: aritmetic_op PLUS_OP term | term PLUS_OP term | term PLUS_OP aritmetic_op; subtraction: aritmetic_op MINUS_OP term | term MINUS_OP term | term MINUS_OP aritmetic_op; multiplication: aritmetic_op MULTIPLY_OP term | term MULTIPLY_OP term | term MULTIPLY_OP aritmetic_op; division: aritmetic_op DIVIDE_OP term | term DIVIDE_OP term | term DIVIDE_OP aritmetic_op; modulo: aritmetic_op MOD_OP term | term MOD_OP term | term MOD_OP aritmetic_op; power: aritmetic_op POWER_OP term | term POWER_OP term | term POWER_OP aritmetic_op; /* comment: | DS sentence NL; sentence: | IDENTIFIER | DOT | COMMA | COLON | SEMICOLON | PLUS_OP | MINUS_OP | MULTIPLY_OP | DIVIDE_OP | MOD_OP | HASHTAG | SENSOR_IDENTIFIER | POWER_OP | UNDER_SCORE | QUESTION | NOT | LP | RP | LCB | RCB | ASSN_OP | GT | LT | sentence sentence; */ input_stmt: TYPE term ASSN_OP SYSIN LP RP SEMICOLON | SYSIN LP RP SEMICOLON; output_stmt: SYSOUT LP output RP SEMICOLON; output: term | value | aritmetic_op | output COMMA term; url_checker: IS_URL LP STRING RP | IS_URL LP term RP; connect: CONNECT LP URL RP; send: SEND LP number COMMA URL RP; receive: RECEIVE LP URL RP; %% void yyerror(char *s) { fprintf(stderr, "syntax error at line: %d %s\n", yylineno, s); } int main(void){ yyparse(); if(yynerrs < 1) printf("there are no syntax errors!!\n"); }
简单测试程序
integer a = 5; double e = 5.5; double f = 3.0; sadasd
扩展测试程序
sensor a = $s1; switch b = $sw1; integer c = -5; integer d = 75; double e = 5.5; double f = 3.0; c = c + d; f = f + e; sysout (e + f); integer g = sysin(); url k = https://www.cs.bilkent.edu.tr/~guvenir/courses/CS315/Pr1.htm; url l = https://docs.oracle.com/cd/E19504-01/802-5880/lex-6/index.html; if(isURL(k)){connect(k);} //Connecting to URL k after checking if its a URL send(g, k); //Sending the integer g to the URL k integer h = receive(l); //Receiving the integer h from URL l sysout (h); //Printing the integer h which we took from the URL l if (e > f) { e = f + e; }else{ e = f - e; } time t = Friday, 14 October 2022 18:05:52 time t = Monday, 17 October 2022 12:05:52 GMT time t = Saturday, 22 October 2022 20:05:52 GMT+03:00 //Defining time in different ways while (e > f) { e = e - f; } for (integer i = 0, e <= f, i = i + 1) { e = e + i; } fun boolean isGreater(integer x, integer y){ boolean z = (x > y); return z; } boolean g = isGreater(e, f); sysout (k); sysout (l);
Makefile
LEX = lex YACC = yacc -d CC = gcc all: parser clean parser: y.tab.o lex.yy.o $(CC) -o parser y.tab.o lex.yy.o ./parser < test.txt lex.yy.o: lex.yy.c y.tab.h lex.yy.o y.tab.o: y.tab.c y.tab.c y.tab.h: y.y $(YACC) -v y.y lex.yy.c: lex.l $(LEX) lex.l clean: -rm -f *.o lex.yy.c *.tab.* parser *.output
问题分析与修复方案
1. 空输入报错的解决
YACC的program规则强制要求至少有一条语句,空输入无法匹配。修改规则允许空程序:
program: stmts {printf("\rProgram is valid.\n");} | {printf("\rEmpty program is valid.\n");};
2. 首行语法错误的核心修复
(1)Lex未处理换行符
当前Lex仅忽略空格和制表符,换行符会被识别为未知字符,触发语法错误。添加规则忽略换行:
[ \t\n] ; /* 忽略空格、制表符、换行符 */
(2)Boolean规则冲突
原boolean规则会匹配a > b这类表达式,导致Lex将整个表达式作为单个BOOLEANtoken返回,与YACC的语法规则冲突。修改规则仅匹配true/false:
boolean ("true")|("false")
(3)URL规则匹配范围错误
原URL规则会把后面的分号也包含进URLtoken,导致YACC找不到语句结束的分号。修改规则限制匹配范围:
url ("http")("s")?("://")("www.")?[a-zA-Z0-9_\-\.~\/]*
(4)For循环语法不匹配
YACC的for_loop规则要求用分号分隔循环三要素,但测试程序用了逗号。修改测试程序的for语句:
for (integer i = 0; e <= f; i = i + 1) { e = e + i; }
(5)Time语句缺少分号
扩展测试程序中的time赋值语句未加结束分号,添加分号:
time t = Friday, 14 October 2022 18:05:52; time t = Monday, 17 October 2022 12:05:52 GMT; time t = Saturday, 22 October 2022 20:05:52 GMT+03:00;
3. 其他细节修复
- Lex中
sysin规则的多余空格:改为sysin return SYSIN;避免匹配异常; - 在Lex中添加
int yylineno = 1;,确保行号统计正确; - 测试程序中的
sadasd属于无意义标识符,若要允许此类语句,需在YACC的non_if_stmt中添加IDENTIFIER SEMICOLON规则。
内容的提问来源于stack exchange,提问作者asimtot
相关产品推荐
相关产品推荐

