欢迎光临
我们一直在努力

词法分析【实验二 词法分析错误处理】

2. 词法分析【实验二 词法分析错误处理】

词法分析实验 编程题

词法分析【实验二】

  • 2. 词法分析【实验二 词法分析错误处理】
    • 【问题描述】
    • 【输入形式】
    • 【输出形式】
    • 【样例输入】
    • 【样例输出】
    • 【样例说明】
    • 【评分标准】
    • 评测结果(满分10分)
    • 解题思路
      • 总结
    • 代码内容(python语言)
    • 参考代码(python语言)

【问题描述】

编写词法分析程序,在实验一的基础之上添加错误识别功能,基本要求如下:

(1)输入的被编译源文件统一命名为

1.

t

x

t

1.txt

1.txt

(2)输出结果中包含如下两种信息:

错误所在的行号 错误类型编码 (行号与错误类型编码之间只有一个空格)

其中,错误类型编码按下表中的定义输出,行号从1开始计数: 在这里插入图片描述

101 非法字符 如:#、@、¥等

102 不符合构词规则 包括:非法标识符;小数点后面没有数字等

103 注释未闭合 缺少闭合的*/

104 字符类型缺少配对的’

105 字符类型缺少配对的“

【输入形式】

程序代码,请在代码中读取

1.

t

x

t

1.txt

1.txt 文件

【输出形式】

行号 错误码 按如上要求将错误信息打印输出,也可以自行保存到错误信息文件中

e

r

r

o

r

.

t

x

t

error.txt

error.txt

【样例输入】

float b_2 = 20. ;
int a = 12 ;

【样例输出】

1 102

【样例说明】

每个测试用例的错误类型说明:

  • 用例1:浮点数构词错误;非法字符

  • 用例2:八进制数构词错误;字符常量错误情况1;字符常量错误情况2

  • 用例3:非法字符;浮点数构词错误;字符串错误

  • 用例4:字符常量错误;注释错误

  • 用例5:标识符构词错误;十六进制数构词错误;非法字符

【评分标准】

按正确样例数目进行评分

(1)本次实验的每个测试用例各包含1-3个错误,均来自上表,识别出一个测试用例中的全部错误后得2分;

(2)上表中未包含的错误,可以自行设计,本次实验不考核;

(3)完成本次实验时,不需要输出词法分析实验一要求输出的内容。

评测结果(满分10分)

在这里插入图片描述

解题思路

本题核心是在实验一词法解析基础上,新增五类词法错误的检测与记录:扩展分析器类,新增错误列表存储(行号,错误码);逐行解析时,针对不同错误类型设计检测逻辑——未知字符标记

101

101

101 非法字符,浮点数小数点后无数字标记

102

102

102 构词错误,多行注释未闭合标记

103

103

103 ,字符常量缺单引号标记

104

104

104 ,字符串常量缺双引号标记

105

105

105 ;解析过程中实时检测并记录错误,解析完成后按行号排序输出错误信息;同时保留实验一的合法单词解析功能,确保错误检测不影响原有词法分析流程。该方法通过针对性的错误校验逻辑,覆盖题目要求的所有错误类型,精准记录错误行号和编码,适配自动评测的输出格式要求。

总结

  • 核心逻辑:在实验一词法解析基础上,新增五类错误的检测逻辑,实时记录错误行号和编码,最后按行号排序输出。
  • 关键操作:检测非法字符、浮点数构词错误、未闭合注释、缺配对单/双引号,错误信息按行号升序输出。
  • 效率保障:线性遍历源代码,错误检测嵌入原有解析流程,无额外冗余计算,同时保留合法单词解析与文件输出功能。
  • 代码内容(python语言)

    import sys

    class Token:
    def __init__(self, lexeme, token_type, line):
    self.lexeme = lexeme
    self.token_type = token_type
    self.line = line

    def __str__(self):
    return f"{self.lexeme:<15} {self.token_type:<5} {self.line}"

    class LexicalAnalyzer:
    def __init__(self, source_code):
    self.source_code = source_code
    self.lines = source_code.splitlines()

    # 关键字-类别码映射
    self.keywords = {
    'char': 101, 'int': 102, 'float': 103, 'if': 111,
    'return': 106, 'do': 109, 'while': 110, 'void': 107,
    'const': 105, 'else': 112
    }

    # 运算符-类别码映射(与C版本完全一致)
    self.operators = {
    '=': 219, '+': 209, '>': 213, '-': 210,
    '*': 206, '/': 204, '<': 211, '>=': 207,
    '<=': 208, '==': 215, '!=': 216, '&&': 217,
    '||': 218, '!': 205
    }

    # 分隔符-类别码映射(与C版本完全一致)
    self.delimiters = {
    '{': 301, '}': 302, ';': 303, '(': 201,
    ')': 202, ',': 304, '[': 203, ']': 204,
    '.': 309 # 添加点号,C中为309
    }

    self.ID_CODE = 700
    self.INT_CODE = 400
    self.FLOAT_CODE = 800
    self.CHAR_CODE = 500
    self.STR_CODE = 600

    self.errors = [] # (行号, 错误编码)

    # 仅识别ASCII字母和下划线
    def is_ascii_letter(self, c):
    return ('a' <= c <= 'z') or ('A' <= c <= 'Z') or c == '_'

    # 仅识别ASCII数字
    def is_ascii_digit(self, c):
    return '0' <= c <= '9'

    # 仅识别ASCII十六进制字符
    def is_ascii_hex_digit(self, c):
    return ('0' <= c <= '9') or ('a' <= c <= 'f') or ('A' <= c <= 'F')

    # 跳过空白字符
    def skip_whitespace(self, line, pos):
    while pos < len(line) and line[pos].isspace():
    pos += 1
    return pos

    # 分析标识符或关键字
    def analyze_identifier_or_keyword(self, line, pos, line_num):
    start = pos
    while pos < len(line) and (self.is_ascii_letter(line[pos]) or self.is_ascii_digit(line[pos])):
    pos += 1
    lexeme = line[start:pos]
    token_type = self.keywords.get(lexeme, self.ID_CODE)

    # 检查标识符后是否紧跟非法字符(非ASCII字母/数字/下划线/空白/运算符/分隔符)
    if pos < len(line):
    ch = line[pos]
    if not (self.is_ascii_letter(ch) or self.is_ascii_digit(ch) or ch == '_' or ch.isspace() or
    ch in self.operators or ch in self.delimiters):
    self.errors.append((line_num, 101))
    return None, len(line) # 跳过整行
    return (lexeme, token_type, line_num), pos

    # 分析数字
    def analyze_number(self, line, pos, line_num):
    start = pos
    n = len(line)
    illegal = False

    # 十六进制 0x…
    if line[pos] == '0' and pos + 1 < n and line[pos+1] in ('x', 'X'):
    pos += 2
    if pos >= n or not self.is_ascii_hex_digit(line[pos]):
    self.errors.append((line_num, 102))
    return None, n # 跳过整行
    while pos < n and self.is_ascii_hex_digit(line[pos]):
    pos += 1
    # 检查后跟字母/下划线(仅ASCII字母)
    if pos < n and (self.is_ascii_letter(line[pos]) or line[pos] == '_'):
    self.errors.append((line_num, 102))
    return None, n # 跳过整行
    lexeme = line[start:pos]
    return (lexeme, self.INT_CODE, line_num), pos

    # 八进制 0… (非十六进制的0开头数字)
    if line[pos] == '0' and pos + 1 < n and line[pos+1] not in ('x', 'X'):
    pos += 1
    # 检查后续字符是否为八进制数字(0-7)
    while pos < n:
    if line[pos] in '01234567':
    pos += 1
    elif line[pos].isdigit(): # 8或9,非法
    illegal = True
    self.errors.append((line_num, 102))
    # 跳过整行
    return None, n
    else:
    break
    # 检查后跟字母/下划线
    if pos < n and (self.is_ascii_letter(line[pos]) or line[pos] == '_'):
    self.errors.append((line_num, 102))
    return None, n # 跳过整行
    if illegal:
    return None, n
    lexeme = line[start:pos]
    return (lexeme, self.INT_CODE, line_num), pos

    # 十进制整数或浮点数
    has_dot = False
    while pos < n:
    c = line[pos]
    if self.is_ascii_digit(c):
    pos += 1
    elif c == '.' and not has_dot:
    has_dot = True
    pos += 1
    if pos >= n or not self.is_ascii_digit(line[pos]):
    self.errors.append((line_num, 102))
    return None, n # 跳过整行
    elif c == '.' and has_dot:
    self.errors.append((line_num, 102))
    return None, n # 跳过整行
    else:
    break

    # 检查后跟字母/下划线
    if pos < n and (self.is_ascii_letter(line[pos]) or line[pos] == '_'):
    self.errors.append((line_num, 102))
    return None, n # 跳过整行

    lexeme = line[start:pos]
    token_type = self.FLOAT_CODE if has_dot else self.INT_CODE
    return (lexeme, token_type, line_num), pos

    # 分析字符常量
    def analyze_char_literal(self, line, pos, line_num):
    start = pos
    pos += 1
    char_count = 0
    while pos < len(line) and line[pos] != "'":
    if line[pos] == '\\\\':
    pos += 1
    if pos < len(line):
    pos += 1
    char_count += 1
    else:
    break
    else:
    pos += 1
    char_count += 1
    if pos >= len(line) or line[pos] != "'":
    self.errors.append((line_num, 104))
    return None, len(line) # 跳过整行
    pos += 1
    if char_count != 1:
    self.errors.append((line_num, 102))
    lexeme = line[start+1:pos1] if (pos1) > start else ""
    return (lexeme, self.CHAR_CODE, line_num), pos

    # 分析字符串常量
    def analyze_string_literal(self, line, pos, line_num):
    start = pos
    pos += 1
    while pos < len(line) and line[pos] != '"':
    if line[pos] == '\\\\':
    pos += 1
    pos += 1
    if pos >= len(line) or line[pos] != '"':
    self.errors.append((line_num, 105))
    return None, len(line) # 跳过整行
    pos += 1
    lexeme = line[start+1:pos1] if (pos1) > start else ""
    return (lexeme, self.STR_CODE, line_num), pos

    # 分析运算符
    def analyze_operator(self, line, pos, line_num):
    if pos + 1 < len(line):
    two_char = line[pos:pos+2]
    if two_char in self.operators:
    return (two_char, self.operators[two_char], line_num), pos + 2
    one_char = line[pos]
    if one_char in self.operators:
    return (one_char, self.operators[one_char], line_num), pos + 1
    self.errors.append((line_num, 101))
    return None, len(line) # 跳过整行

    # 分析分隔符
    def analyze_delimiter(self, line, pos, line_num):
    c = line[pos]
    if c in self.delimiters:
    return (c, self.delimiters[c], line_num), pos + 1
    self.errors.append((line_num, 101))
    return None, len(line) # 跳过整行

    # 主分析函数
    def analyze(self):
    line_num = 1
    total_lines = len(self.lines)

    while line_num <= total_lines:
    line = self.lines[line_num 1]
    pos = 0
    line_length = len(line)

    while pos < line_length:
    pos = self.skip_whitespace(line, pos)
    if pos >= line_length:
    break

    current_char = line[pos]

    # 单行注释
    if current_char == '/' and pos + 1 < line_length and line[pos+1] == '/':
    pos = line_length # 跳过整行
    continue

    # 多行注释(逐字符扫描,模拟C行为)
    if current_char == '/' and pos + 1 < line_length and line[pos+1] == '*':
    pos += 2
    comment_start_line = line_num
    found_end = False
    while True:
    # 在当前行内逐字符查找 */
    while pos < line_length:
    if line[pos] == '*' and pos + 1 < line_length and line[pos+1] == '/':
    pos += 2
    found_end = True
    break
    pos += 1
    if found_end:
    break
    # 未找到,继续下一行
    line_num += 1
    if line_num > total_lines:
    # 注释未闭合,错误记录在最后一行
    self.errors.append((total_lines, 103))
    # 设置 line_num 为 total_lines,pos 为行尾,并标记结束
    line_num = total_lines
    pos = line_length
    found_end = True
    break
    line = self.lines[line_num 1]
    line_length = len(line)
    pos = 0
    continue

    # 标识符/关键字
    if self.is_ascii_letter(current_char):
    _, pos = self.analyze_identifier_or_keyword(line, pos, line_num)
    continue

    # 数字
    if self.is_ascii_digit(current_char):
    _, pos = self.analyze_number(line, pos, line_num)
    continue

    # 字符常量
    if current_char == "'":
    _, pos = self.analyze_char_literal(line, pos, line_num)
    continue

    # 字符串常量
    if current_char == '"':
    _, pos = self.analyze_string_literal(line, pos, line_num)
    continue

    # 运算符
    if current_char in self.operators or (pos+1 < line_length and line[pos:pos+2] in self.operators):
    _, pos = self.analyze_operator(line, pos, line_num)
    continue

    # 分隔符
    if current_char in self.delimiters:
    _, pos = self.analyze_delimiter(line, pos, line_num)
    continue

    # 其他非法字符
    self.errors.append((line_num, 101))
    pos = line_length # 跳过整行

    line_num += 1

    def output_errors(self):
    self.errors.sort(key=lambda x: x[0])
    for line, code in self.errors:
    print(f"{line} {code}")

    if __name__ == '__main__':
    try:
    with open('1.txt', 'r', encoding="utf-8") as f:
    test_code = f.read()
    except FileNotFoundError:
    print("错误:未找到1.txt文件,请确保文件存在!")
    sys.exit(1)

    analyzer = LexicalAnalyzer(test_code)
    analyzer.analyze()
    analyzer.output_errors()

    参考代码(python语言)

    # 定义单词-编码映射表(整合所有类别,方便查询)
    TOKEN_MAP = {
    # 关键字
    'char': 101, 'int': 102, 'float': 103, 'break': 104,
    'const': 105, 'return': 106, 'void': 107, 'continue': 108,
    'do': 109, 'while': 110, 'if': 111, 'else': 112, 'for': 113,
    # 界符
    '{': 301, '}': 302, ';': 303, ',': 304,
    # 运算符
    '(': 201, ')': 202, '[': 203, ']': 204, '!': 205, '*': 206,
    '/': 207, '%': 208, '+': 209, '-': 210, '<': 211, '<=': 212,
    '>': 213, '>=': 214, '==': 215, '!=': 216, '&&': 217, '||': 218,
    '=': 219, '.': 220,
    # 类别标识(非直接匹配,用于动态识别)
    '整数': 400, '字符': 500, '字符串': 600, '标识符': 700, '实数(float)': 800
    }

    # 关键字集合(快速判断)
    KEYWORDS = {'char', 'int', 'float', 'break', 'const', 'return', 'void',
    'continue', 'do', 'while', 'if', 'else', 'for'}

    # 单字符运算符/界符集合
    SINGLE_OPERATORS = {'(', ')', '[', ']', '!', '*', '/', '%', '+', '-',
    '<', '>', '=', '.', '{', '}', ';', ','}

    # 双字符运算符前缀(需要检查下一个字符)
    DOUBLE_OP_PREFIX = {'<', '>', '!', '&', '|', '='}
    DOUBLE_OP_MAP = {
    '<': '<=', '>': '>=', '!': '!=',
    '&': '&&', '|': '||', '=': '=='
    }

    # 合法字符集合(用于判断错误码101)
    LEGAL_CHARS = {
    # 字母(大小写)、数字、下划线
    *[chr(c) for c in range(65, 91)], *[chr(c) for c in range(97, 123)],
    *[chr(c) for c in range(48, 58)], '_',
    # 空白符
    ' ', '\\t', '\\n',
    # 特殊符号
    '+', '-', '*', '/', '%', '=', '<', '>', '!', '&', '|', '(', ')', '{', '}',
    '[', ']', ',', '.', ';', "'", '"', '\\\\', '$' # $作为结束符
    }

    class LexicalAnalyzer:
    def __init__(self, code):
    # 预处理:保留换行(注释识别需要),添加结束符
    self.code = code.strip() + '$'
    self.pos = 0 # 当前字符位置
    self.current_char = self.code[self.pos] # 当前字符
    self.errors = [] # 存储错误信息:(错误码, 描述, 位置)
    self.row = 1 # 当前行号
    self.col = 0 # 当前列号
    self.next = False

    # 读取下一个字符
    def next_char(self):
    self.pos += 1
    self.current_char = self.code[self.pos] if self.pos < len(self.code) else '$'
    self.col += 1
    if self.next:
    self.row += 1
    self.col = 1
    self.next = False

    if self.current_char == '\\n':
    self.next = True

    # 跳过空白字符(空格、制表符)
    def skip_whitespace(self):
    while self.current_char in (' ', '\\t', '\\n'):
    self.next_char()

    # 判断是否为字母/下划线
    def is_letter_or_underline(self, char):
    return char.isalpha() or char == '_'

    # 判断是否为数字
    def is_digit(self, char):
    return char.isdigit()

    # 检查字符是否合法(错误码101判断)
    def is_legal_char(self, char):
    return char in LEGAL_CHARS

    # 识别单行注释(//)和多行注释(/* */)
    def skip_comment(self):
    # 情况1:单行注释 //
    if self.current_char == '/' and self.code[self.pos + 1] == '/':
    self.next_char()
    self.next_char()
    # 跳过直到换行或结束符
    while self.current_char not in ('\\n', '$'):
    self.next_char()
    return True
    # 情况2:多行注释 /*
    elif self.current_char == '/' and self.code[self.pos + 1] == '*':
    self.next_char()
    self.next_char()
    # 状态:寻找 */ 闭合
    while self.current_char != '$':
    if self.current_char == '*' and self.code[self.pos + 1] == '/':
    self.next_char()
    self.next_char()
    return True
    self.next_char()
    # 多行注释未闭合
    self.errors.append((103, "注释未闭合", self.row, self.col)) # 自定义-2标识注释未闭合,最终输出对应描述
    return True
    return False

    # 识别标识符/关键字(含构词规则检查:错误码102)
    def identify_identifier_or_keyword(self):
    token = ''
    # 构词规则:标识符必须以字母/下划线开头
    if not self.is_letter_or_underline(self.current_char):
    self.errors.append((102, f"不符合构词规则:标识符不能以{self.current_char}开头", self.row, self.col))
    self.next_char()
    return None
    # 状态S1:收集字母/数字/下划线
    while self.is_letter_or_underline(self.current_char) or self.is_digit(self.current_char):
    token += self.current_char
    self.next_char()
    # 判断是否为关键字
    if token in KEYWORDS:
    return (TOKEN_MAP[token], token, self.row)
    else:
    return (TOKEN_MAP['标识符'], token, self.row)

    # 识别数字(整数/实数,含构词规则检查:错误码102)
    def identify_number(self):
    token = ''
    # 状态S2:收集整数部分
    while self.is_digit(self.current_char):
    token += self.current_char
    self.next_char()
    # 检查是否为实数(状态S3)
    if self.current_char == '.':
    token += self.current_char
    self.next_char()
    # 收集小数部分
    if self.is_digit(self.current_char):
    while self.is_digit(self.current_char):
    token += self.current_char
    self.next_char()
    return (TOKEN_MAP['实数(float)'], token, self.row)
    else:
    # 构词错误:小数点后无数字
    self.errors.append((102, f"不符合构词规则:实数{token}小数点后无数字", self.row, self.col))
    # 退回字符,仅保留整数部分
    self.pos -= 1
    self.current_char = self.code[self.pos]
    return (TOKEN_MAP['整数'], token[:1], self.row) if token[:1] else None
    # 纯整数
    return (TOKEN_MAP['整数'], token, self.row) if token else None

    # 识别运算符/界符
    def identify_operator_or_delimiter(self):
    current = self.current_char
    # 状态S4:检查双字符运算符
    if current in DOUBLE_OP_PREFIX:
    next_c = self.code[self.pos + 1] if self.pos + 1 < len(self.code) else '$'
    double_op = current + next_c
    if double_op in DOUBLE_OP_MAP.values():
    self.next_char() # 跳过第二个字符
    self.next_char()
    return (TOKEN_MAP[double_op], double_op, self.row)
    # 状态S5:单字符运算符/界符
    self.next_char()
    return (TOKEN_MAP[current], current, self.row) if current in TOKEN_MAP else None

    def identify_string_literal(self):
    if self.current_char == '"':
    current = ''
    self.next_char()
    while self.current_char != '"':
    current += self.current_char
    if self.current_char == '$' or self.current_char == '\\n':
    self.errors.append((105, f"字符串未闭合", self.row, self.col))
    return (TOKEN_MAP['字符'], current, self.row)
    self.next_char()
    if self.current_char == '"':
    self.next_char()
    return (TOKEN_MAP['字符串'], current, self.row)
    elif self.current_char == "'":
    self.next_char()
    current = ''
    if self.current_char != "'":
    current += self.current_char
    self.next_char()
    if self.current_char != "'":
    self.errors.append((104, f"字符字面量只能包含一个字符", self.row, self.col))
    # while self.current_char != "'" and self.current_char != '$':
    # self.next_char()
    return (TOKEN_MAP['字符'], current, self.row)
    else:
    self.next_char()
    return (TOKEN_MAP['字符'], current, self.row)
    else:
    return (TOKEN_MAP['字符'], current, self.row)

    # 核心分析方法
    def analyze(self):
    tokens = []
    while self.current_char != '$':
    self.skip_whitespace()
    if self.current_char == '$':
    break

    # 第一步:检查是否为注释
    if self.skip_comment():
    continue

    # 第二步:检查字符是否合法(错误码101)
    if not self.is_legal_char(self.current_char):
    self.errors.append((101, f"错误字符:{self.current_char}", self.row, self.col))
    self.next_char()
    continue

    # 第三步:分支识别
    if self.is_letter_or_underline(self.current_char):
    # 标识符/关键字分支
    token = self.identify_identifier_or_keyword()
    if token:
    tokens.append(token)
    elif self.is_digit(self.current_char):
    # 数字分支
    token = self.identify_number()
    if token:
    tokens.append(token)
    elif self.current_char in SINGLE_OPERATORS or self.current_char in DOUBLE_OP_PREFIX:
    # 运算符/界符分支
    token = self.identify_operator_or_delimiter()
    if token:
    tokens.append(token)
    elif self.current_char == '"' or self.current_char == "'":
    # 字符串字面量分支
    token = self.identify_string_literal()
    if token:
    tokens.append(token)
    else:
    # 其他合法字符但未识别(理论上不会走到这里,兜底)
    self.errors.append((102, f"不符合构词规则:{self.current_char}", self.row, self.col))
    self.next_char()

    # 输出错误信息
    for err_code, _, row, _ in self.errors:
    print(f"{row} {err_code}")
    return tokens

    class Token:
    def __init__(self, lexeme, token_type, line):
    self.lexeme = lexeme
    self.token_type = token_type
    self.line = line

    def __str__(self):
    return f"{self.lexeme:<15} {self.token_type:<7} {self.line}"

    # 测试示例
    if __name__ == '__main__':
    # 测试代码(包含注释、错误字符、构词错误、未闭合注释)
    with open('1.txt', 'r', encoding="utf-8") as f:
    test_code = f.read()
    # 创建分析器并执行分析
    analyzer = LexicalAnalyzer(test_code)
    result = analyzer.analyze()
    # 输出有效token
    # for code, content, row in result:
    # t = Token(content, code, row)
    # print(t)

    赞(0)
    未经允许不得转载:171主机测评 » 词法分析【实验二 词法分析错误处理】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址