2. 语法分析错误处理
语法分析错误处理 编程题
语法分析【实验二 语法分析错误处理】
- 2. 语法分析错误处理
-
- 【问题描述】
- 【输入形式】
- 【输出形式】
- 【样例输入】
- 【样例输出】
- 【样例说明】
- 【评分标准】
- 【特别提醒】
- 评测结果(满分10分)
- 解题思路
-
- 总结
- 代码内容(python语言)
- 参考代码(python语言)
【问题描述】
请根据给定的文法设计并实现错误处理程序,能诊察出常见的语法错误,进行错误局部化处理,并输出错误信息。为了方便自动评测,输入输出及处理要求如下:
(1)输入的被编译源文件统一命名为
i
n
p
u
t
.
t
x
t
input.txt
input.txt ;错误信息输出到命名为
o
u
t
p
u
t
.
t
x
t
output.txt
output.txt 的结果文件中;
(2)结果文件中包含如下两种信息:错误所在的行号 错误的类别码 (行号与类别码之间只有一个空格)。其中错误类别码按下表中的定义输出,行号从
1
1
1 开始计数,报错行号为前一个非终结符所在行号。
| 声明语句中缺少标识符 | 201 | 声明语句中缺少标识符,例如 int ; 或 const int ;。 |
| 缺少分号 | 202 | 语句末尾缺少分号,例如 int a 后直接跟另一个声明或语句。 |
| 出现多余的右花括号 | 203 | 出现多余的右花括号,例如 } 没有对应的左括号。 |
| 复合语句开始缺少左花括号 | 204 | 复合语句开始缺少左花括号,例如函数体 int main() return 0;。 |
| 复合语句结束缺少右花括号 | 205 | 复合语句结束缺少右花括号,例如 int main() { return 0;。 |
| 出现多余的右括号 | 206 | 出现多余的右括号,例如 )没有对应的左括号。 |
| 需要左括号的地方缺失 | 207 | 需要左括号的地方缺失,例如 if a > 0) 或 while a < 10)。 |
| 右括号缺失 | 208 | 右括号缺失,例如 if (a > 0 或 while (a < 10。 |
| 赋值表达式左边必须是变量 | 210 | 赋值表达式左边必须是变量,如 5=x |
| 二元操作符缺少操作数 | 211 | 二元运算符缺少左操作数或右操作数,例如 a + 或 * b。 |
| do- while语句中缺少while | 212 | do 后的语句体结束后缺少 while 关键字,例如 do { a++; } (a<10);。 |
(3)所有错误都不会出现恶意换行的情况,包括字符、字符串中的换行符、函数调用等等。
【输入形式】
i
n
p
u
t
.
t
x
t
input.txt
input.txt 中的存在语法错误的
t
o
k
e
n
token
token 序列。
【输出形式】
按如上要求将错误处理结果输出至
o
u
t
p
u
t
.
t
x
t
output.txt
output.txt 中。
【样例输入】
int 102 2
main 700 2
( 201 2
) 202 2
{ 301 2
int 102 3
a 700 3
, 304 3
b 700 3
const 105 4
int 102 4
c 700 4
= 219 4
5 400 4
; 303 4
if 111 5
a 700 5
) 202 5
a 700 6
= 219 6
1 400 6
; 303 6
int 102 7
d 700 7
= 219 7
0 400 7
; 303 7
return 106 8
0 400 8
; 303 8
【样例输出】
3 202
5 207
8 205
【样例说明】
【评分标准】
【特别提醒】
(1)上表中只列举了部分错误类型和报告该错误类型的情况,未包含的错误类型或错误情况,需要自行设计,本次作业考核不涉及;
(2)完成本次作业时,请勿输出词法分析和语法分析作业要求输出的内容;
(3)本次考核之外,发现错误时最好直接输出描述信息,而不是仅给出错误类别码,有助于完善编译器的设计、开发与调试。
(4)每一行中最多只有一个错误。
评测结果(满分10分)

解题思路
本题核心是递归下降语法分析 + 定向错误检测,基于输入的 Token 序列校验 C 类语言语法,识别指定类型错误。按照文法规则依次解析常量 / 变量声明、函数定义、复合语句、条件 / 循环语句、表达式,遍历过程中实时检测:声明缺标识符、语句缺分号、括号 / 花括号不匹配、条件语句缺左括号等错误。严格遵循要求,错误行号记录前一个非终结符所在行,且每行仅保留一个错误。最终将检测到的错误按行号排序后输出,算法线性遍历 Token 流,时间复杂度O(n),精准匹配题目输入输出要求。
总结
核心逻辑:递归下降解析 Token 序列,按文法规则校验语法,定向检测题目指定的错误类型。
关键操作:栈结构匹配括号 / 花括号,逐语句校验标识符、分号、括号完整性,严格记录错误行号。
效率保障:线性遍历 Token 流,无冗余计算,完美适配样例输入并输出正确结果。
代码内容(python语言)
import sys
import os
# 常量定义
TOK_ID = 700
TOK_CONST = 400
TOK_STR = 500
TOK_INT = 102
TOK_CONST_TYPE = 105
TOK_IF = 111
TOK_ELSE = 112
TOK_WHILE = 110
TOK_RET = 106
TOK_FOR = 113
TOK_DO = 109
TOK_BREAK = 104
TOK_CONT = 108
TOK_VOID = 101
TOK_FLOAT = 103
TOK_CHAR = 107
TOK_LPAREN = 201
TOK_RPAREN = 202
TOK_LBRACE = 301
TOK_RBRACE = 302
TOK_SEMI = 303
TOK_COMMA = 304
TOK_ASSIGN = 219
TOK_PLUS = 209
TOK_MINUS = 210
TOK_MUL = 206
TOK_DIV = 207
TOK_GT = 213
TOK_LT = 211
TOK_GE = 214
TOK_LE = 212
TOK_EQ = 215
TOK_NE = 216
TOK_AND = 217
TOK_OR = 218
TOK_NOT = 205
# 类型映射
type_map = {
'int': TOK_INT,
'float': TOK_FLOAT,
'char': TOK_CHAR,
'void': TOK_VOID,
}
class TokenUnit:
"""词法单元"""
def __init__(self, text, kind, lineno):
self.text = text
self.kind = int(kind)
self.lineno = int(lineno)
class SyntaxChecker:
"""语法分析器"""
def __init__(self, units):
# 转换为内部使用的属性名
self.token_list = []
for u in units:
tmp = type('', (), {})()
tmp.value = u.text
tmp.type_code = u.kind
tmp.line = u.lineno
self.token_list.append(tmp)
self.pos = 0 # 当前索引
self.error_list = [] # (行号, 错误码)
self.keywords = ['int', 'float', 'char', 'void', 'double']
self.stmt_starts = ['if', 'while', 'for', 'do', 'return', 'break', 'continue', '{'] + self.keywords
def look_ahead(self, offset=0):
"""向前查看token"""
idx = self.pos + offset
if idx < len(self.token_list):
return self.token_list[idx]
return None
def prev_line_num(self):
"""获取前一个token的行号"""
if self.pos > 0:
return self.token_list[self.pos – 1].line
return 1
def record_error(self, code, line=None):
"""记录错误(每行最多一个)"""
line = line if line is not None else self.prev_line_num()
if not any(e[0] == line for e in self.error_list):
self.error_list.append((line, code))
def match_token(self, val, code):
"""匹配指定词法值"""
t = self.look_ahead()
if t and t.value == val:
self.pos += 1
return True
else:
self.record_error(code)
return False
def run(self):
"""启动语法分析"""
self.check_program()
self.error_list.sort()
return self.error_list
def check_program(self):
"""program → { const_decl | var_decl | function | stmt }"""
while self.look_ahead():
t = self.look_ahead()
if t.value == 'const':
self.check_const_declaration()
elif t.value in self.keywords:
look2 = self.look_ahead(2)
if look2 and look2.value == '(':
self.check_function_definition()
else:
self.check_var_declaration()
elif t.value == '}':
self.record_error(203, t.line)
self.pos += 1
else:
self.check_statement()
def check_const_declaration(self):
"""const_decl → 'const' type id [ '=' expr ] { ',' id [ '=' expr ] } ';'"""
self.pos += 1 # const
self.pos += 1 # type
while True:
t = self.look_ahead()
if t and t.type_code == TOK_ID:
self.pos += 1
if self.look_ahead() and self.look_ahead().value == '=':
self.pos += 1
self.check_expression()
else:
self.record_error(201)
if self.look_ahead() and self.look_ahead().value == ',':
self.pos += 1
else:
break
self.match_token(';', 202)
def check_var_declaration(self):
"""var_decl → type id [ '=' expr ] { ',' id [ '=' expr ] } ';'"""
self.pos += 1 # type
while True:
t = self.look_ahead()
if t and t.type_code == TOK_ID:
self.pos += 1
if self.look_ahead() and self.look_ahead().value == '=':
self.pos += 1
self.check_expression()
else:
self.record_error(201)
if self.look_ahead() and self.look_ahead().value == ',':
self.pos += 1
else:
break
self.match_token(';', 202)
def check_function_definition(self):
"""function → type id '(' [ type id { ',' type id } ] ')' ( ';' | compound )"""
self.pos += 2 # type id
self.match_token('(', 207)
while self.look_ahead() and self.look_ahead().value != ')':
if self.look_ahead().value in self.keywords:
self.pos += 1
if not (self.look_ahead() and self.look_ahead().type_code == TOK_ID):
self.record_error(201)
else:
self.pos += 1
if self.look_ahead() and self.look_ahead().value == ',':
self.pos += 1
else:
break
self.match_token(')', 208)
if self.look_ahead() and self.look_ahead().value == ';':
self.pos += 1
else:
self.check_compound_statement()
def check_compound_statement(self):
"""compound → '{' { const_decl | var_decl | stmt } '}'"""
self.match_token('{', 204)
while self.look_ahead() and self.look_ahead().value != '}':
t = self.look_ahead()
if t.value in self.keywords:
self.check_var_declaration()
elif t.value == 'const':
self.check_const_declaration()
else:
self.check_statement()
self.match_token('}', 205)
def check_statement(self):
"""stmt → if | while | do | return | break | continue | compound | ';' | expr_stmt"""
t = self.look_ahead()
if not t:
return
if t.value == 'if':
self.pos += 1
self.match_token('(', 207)
self.check_expression()
self.match_token(')', 208)
self.check_statement()
if self.look_ahead() and self.look_ahead().value == 'else':
self.pos += 1
self.check_statement()
elif t.value == 'while':
self.pos += 1
self.match_token('(', 207)
self.check_expression()
self.match_token(')', 208)
self.check_statement()
elif t.value == 'do':
self.pos += 1
self.check_statement()
self.match_token('while', 212)
self.match_token('(', 207)
self.check_expression()
self.match_token(')', 208)
self.match_token(';', 202)
elif t.value == 'return':
self.pos += 1
if self.look_ahead() and self.look_ahead().value != ';':
self.check_expression()
self.match_token(';', 202)
elif t.value == '{':
self.check_compound_statement()
elif t.value == ';':
self.pos += 1
elif t.value == ')':
self.record_error(206, t.line)
self.pos += 1
else:
self.check_expression_statement()
def check_expression_statement(self):
"""expr_stmt → expr [ '=' expr ] ';'"""
is_id = self.look_ahead() and self.look_ahead().type_code == TOK_ID
self.check_expression()
if self.look_ahead() and self.look_ahead().value == '=':
if not is_id:
self.record_error(210)
self.pos += 1
self.check_expression()
self.match_token(';', 202)
def check_expression(self):
"""expr → primary { op primary }"""
self.check_binary_operation(self.check_primary, ['||', '&&', '==', '!=', '>', '<', '>=', '<=', '+', '-', '*', '/'])
def check_binary_operation(self, sub_func, operators):
"""处理二元运算"""
sub_func()
while self.look_ahead() and self.look_ahead().value in operators:
self.pos += 1
if not self.check_primary():
self.record_error(211)
def check_primary(self):
"""primary → '(' expr ')' | id | const | string | '!' primary | id '(' [ expr { ',' expr } ] ')'"""
t = self.look_ahead()
if not t:
return False
if t.value == '(':
self.pos += 1
self.check_expression()
self.match_token(')', 208)
return True
elif t.type_code in [TOK_ID, TOK_CONST, TOK_STR]:
self.pos += 1
if self.look_ahead() and self.look_ahead().value == '(': # 函数调用
self.pos += 1
while self.look_ahead() and self.look_ahead().value != ')':
self.check_expression()
if self.look_ahead().value == ',':
self.pos += 1
else:
break
self.match_token(')', 208)
return True
elif t.value == '!':
self.pos += 1
return self.check_primary()
return False
def main():
base = os.path.dirname(os.path.abspath(__file__))
in_file = os.path.join(base, 'input.txt')
out_file = os.path.join(base, 'output.txt')
units = []
try:
with open(in_file, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split()
if len(parts) == 3:
units.append(TokenUnit(parts[0], parts[1], parts[2]))
except FileNotFoundError:
print(f"错误:找不到文件 {in_file}")
sys.exit(1)
checker = SyntaxChecker(units)
errors = checker.run()
with open(out_file, 'w', encoding='utf-8') as f:
for line_num, err_code in errors:
f.write(f"{line_num} {err_code}\\n")
if __name__ == '__main__':
main()




