欢迎光临
我们一直在努力

词法分析【实验一 词法分析-Sample语言常见单词】

1.词法分析【实验一 词法分析-Sample语言常见单词】

词法分析实验 编程题

词法分析【实验一】

  • 1.词法分析【实验一 词法分析-Sample语言常见单词】
    • 【问题描述】
    • 【输入形式】
    • 【输出形式】
    • 【样例输入】
    • 【样例输出】
    • 【样例说明】
    • 【评分标准】
    • 评测结果(满分10分)
    • 解题思路
      • 总结
    • 代码内容(python语言)
    • 参考代码(python语言)

【问题描述】

利用状态转换图识别单词(包括标识符、整数、浮点数、关键字等),加深对词法分析原理和状态转换图的理解

请根据给定的

S

a

m

p

l

e

Sample

Sample 语言的构词规则设计并实现词法分析程序,从源程序中识别出单词,记录其单词类别、单词值和行号,输入输出及处理要求如下:

  • (1)数据结构和与语法分析程序的接口请自行定义;

  • (2)为了方便进行自动评测,输入的被编译源文件统一命名为

    1.

    t

    x

    t

    1.txt

    1.txt ;结果文件中每行包含以下三部分:

单词类别码 单词的字符/字符串形式 行号

(3)种别码按教材表

2.1

2.1

2.1 统一定义,如果从外部读入种别码表的话,文件名为:

t

o

k

e

n

_

c

o

d

e

s

.

t

x

t

token\\_codes.txt

token_codes.txt ,输出内容直接打印到控制台,并同时输出到外部文件存储,作为下一个分析阶段的输入备用。

【输入形式】

S

a

m

p

l

e

Sample

Sample 语言程序,请在程序中读取

1.

t

x

t

1.txt

1.txt 作为测试代码。

【输出形式】

输入输出示例代码(

p

y

t

h

o

n

python

python ):

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}"

# 测试示例

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)

【样例输入】

int main{

int a = 1;

}

【样例输出】

int 102 1

main 700 1

{ 301 1

int 102 3

a 700 3

= 219 3

1 400 3

; 303 3

} 302 5

【样例说明】

词法编码参考课本表2.1

【评分标准】

按测试样例成功数目评分

评测结果(满分10分)

在这里插入图片描述

解题思路

本题核心是通过状态转换思想+逐行逐字符解析实现

S

a

m

p

l

e

Sample

Sample 语言的词法分析:先定义关键字、运算符、分隔符的类别码映射,将源代码按行分割后逐行遍历;跳过空白符和注释(单行

/

/

//

// 、多行

/

/

/* */

// ),按字符类型分流处理——字母/下划线开头识别为标识符/关键字,数字开头识别为整数/浮点数,单/双引号开头识别为字符/字符串常量,运算符/分隔符匹配预定义映射;过程中记录每个单词的内容、类别码和行号,最终输出格式化结果并保存到文件。该方法通过分阶段状态处理(如注释跳过、多字符运算符优先匹配),覆盖

S

a

m

p

l

e

Sample

Sample 语言所有合法单词类型,适配教材规定的类别码规范,精准完成词法解析。

总结

  • 核心逻辑:基于状态转换思想,按字符类型分流解析,匹配预定义的关键字/运算符/分隔符映射,识别各类单词。
  • 关键操作:跳过空白和注释,优先匹配多字符运算符,区分标识符与关键字、整数与浮点数,记录行号和类别码。
  • 效率保障:逐行逐字符线性遍历,无冗余计算,适配常规源代码的解析需求,同时输出格式化结果并保存文件。
  • 代码内容(python语言)

    class Token:
    # 单词类:存储单词内容、类别码、行号
    def __init__(self, lexeme, token_type, line):
    self.lexeme = lexeme
    self.token_type = token_type
    self.line = line

    def __str__(self):
    # 格式化输出:单词(左对齐15字符) 类别码(左对齐5字符) 行号
    return f"{self.lexeme:<15} {self.token_type:<5} {self.line}"

    class LexicalAnalyzer:
    # 词法分析器类:解析Sample语言源代码,生成单词序列
    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
    }

    # 运算符-类别码映射(根据期望输出修正)
    self.operators = {
    '=': 219,
    '+': 209,
    '>': 213,
    '-': 210,
    '*': 206,
    '/': 204,
    '<': 211,
    '>=': 207,
    '<=': 208,
    '==': 215,
    '!=': 216,
    '&&': 217,
    '||': 218,
    '!': 205
    }

    # 分隔符-类别码映射
    self.delimiters = {
    '{': 301, '}': 302, ';': 303,
    '(': 201,
    ')': 202,
    ',': 304,
    '[': 203,
    ']': 204
    }

    # 预定义类别码
    self.ID_CODE = 700 # 标识符
    self.INT_CODE = 400 # 整数
    self.FLOAT_CODE = 800 # 浮点数
    self.CHAR_CODE = 500 # 字符常量
    self.STR_CODE = 600 # 字符串常量

    self.errors = [] # 用于记录错误(本次修改新增,但未在最终输出中使用)

    def is_letter(self, c):
    return c.isalpha() or c == '_'

    def is_digit(self, c):
    return c.isdigit()

    def is_hex_digit(self, c):
    return c.isdigit() 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 skip_single_line_comment(self, line, pos):
    pos += 2
    return len(line)

    def skip_multi_line_comment(self, lines, line_num, pos):
    start_line = line_num + 1
    pos += 2
    current_line = line_num
    found_end = False

    while current_line < len(lines):
    line = lines[current_line]
    while pos < len(line):
    if line[pos] == '*' and pos + 1 < len(line) and line[pos+1] == '/':
    pos += 2
    found_end = True
    break
    pos += 1
    if found_end:
    break
    current_line += 1
    pos = 0

    if not found_end:
    print(f"警告:第{start_line}行的多行注释未找到结束符 */")
    return current_line, pos

    def analyze_identifier_or_keyword(self, line, pos, line_num):
    start = pos
    while pos < len(line) and (self.is_letter(line[pos]) or self.is_digit(line[pos])):
    pos += 1
    lexeme = line[start:pos]
    token_type = self.keywords.get(lexeme, self.ID_CODE)
    return (lexeme, token_type, line_num), pos

    def analyze_number(self, line, pos, line_num):
    start = pos

    # 十六进制整数 0x…
    if line[pos] == '0' and pos + 1 < len(line) and line[pos+1] in ('x', 'X'):
    pos += 2
    if pos >= len(line) or not self.is_hex_digit(line[pos]):
    # 0x后没有数字,不生成token
    return None, pos
    while pos < len(line) and self.is_hex_digit(line[pos]):
    pos += 1
    lexeme = line[start:pos]
    return (lexeme, self.INT_CODE, line_num), pos

    # 八进制整数 0…
    if line[pos] == '0' and pos + 1 < len(line) and line[pos+1] in '01234567':
    pos += 1
    while pos < len(line) and line[pos] in '01234567':
    pos += 1
    lexeme = line[start:pos]
    return (lexeme, self.INT_CODE, line_num), pos

    # 十进制整数或浮点数
    has_dot = False
    while pos < len(line):
    c = line[pos]
    if self.is_digit(c):
    pos += 1
    elif c == '.' and not has_dot:
    has_dot = True
    pos += 1
    if pos >= len(line) or not self.is_digit(line[pos]):
    # 小数点后无数字,不生成token
    return None, pos
    else:
    break
    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 # 跳过单引号
    # 处理转义字符
    if pos < len(line) and line[pos] == '\\\\':
    pos += 1
    # 跳过字符内容(至少一个字符)
    if pos < len(line):
    pos += 1
    # 跳过结束单引号(如果存在)
    if pos < len(line) and line[pos] == "'":
    pos += 1
    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) and line[pos] == '"':
    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
    return None, pos + 1

    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
    return None, pos + 1

    def analyze(self):
    result = []
    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 = self.skip_single_line_comment(line, pos)
    continue

    # 处理多行注释 /* */
    if current_char == '/' and pos + 1 < line_length and line[pos+1] == '*':
    new_line_idx, new_pos = self.skip_multi_line_comment(self.lines, line_num1, pos)
    line_num = new_line_idx + 1
    if line_num > total_lines:
    break
    line = self.lines[line_num 1] if line_num <= total_lines else ""
    line_length = len(line)
    pos = new_pos
    continue

    # 标识符/关键字
    if self.is_letter(current_char):
    token, pos = self.analyze_identifier_or_keyword(line, pos, line_num)
    result.append(token)
    # 数字(整数/浮点数)
    elif self.is_digit(current_char):
    token, pos = self.analyze_number(line, pos, line_num)
    if token:
    result.append(token)
    # 字符常量 'xxx'
    elif current_char == "'":
    token, pos = self.analyze_char_literal(line, pos, line_num)
    result.append(token)
    # 字符串常量 "xxx"
    elif current_char == '"':
    token, pos = self.analyze_string_literal(line, pos, line_num)
    result.append(token)
    # 运算符
    elif current_char in self.operators or (pos+1 < line_length and line[pos:pos+2] in self.operators):
    token, pos = self.analyze_operator(line, pos, line_num)
    if token:
    result.append(token)
    # 分隔符
    elif current_char in self.delimiters:
    token, pos = self.analyze_delimiter(line, pos, line_num)
    if token:
    result.append(token)
    # 未知字符:跳过
    else:
    pos += 1

    line_num += 1

    return result

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

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

    output_lines = []
    for content, code, row in result:
    t = Token(content, code, row)
    print(t)
    output_lines.append(f"{content:<15} {code:<5} {row}")

    with open('result.txt', 'w', encoding="utf-8") as f:
    f.write('\\n'.join(output_lines))

    参考代码(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.next = False
    self.row += 1
    self.col = 1
    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:收集整数部分
    if self.current_char == '0':
    if self.code[self.pos + 1] in ('x', 'X'):
    # 16进制数
    token += self.current_char
    self.next_char()
    token += self.current_char
    self.next_char()
    while self.is_digit(self.current_char) or (self.current_char.lower() in 'abcdefABCDEF'):
    token += self.current_char
    self.next_char()
    return (TOKEN_MAP['整数'], token, self.row)
    elif self.code[self.pos + 1] in ('b', 'B'):
    # 2进制数
    token += self.current_char
    self.next_char()
    token += self.current_char
    self.next_char()
    while self.current_char in '01':
    token += self.current_char
    self.next_char()
    return (TOKEN_MAP['整数'], token, self.row)
    elif self.code[self.pos + 1] in '01234567':
    # 8进制数
    token += self.current_char
    self.next_char()
    token += self.current_char
    self.next_char()
    while self.current_char in '01234567':
    token += self.current_char
    self.next_char()
    return (TOKEN_MAP['整数'], token, self.row)

    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):
    current = ''
    if self.current_char == '"':
    self.next_char()
    while self.current_char != '"':
    current += self.current_char
    self.next_char()
    if self.current_char == '"':
    self.next_char()
    return (TOKEN_MAP['字符串'], current, self.row)
    else:
    self.errors.append((105, f"字符串未闭合", self.row, self.col))
    return (TOKEN_MAP['字符串'], current, self.row)
    elif self.current_char == "'":
    self.next_char()
    current = ''
    if self.current_char != "'":
    current += self.current_char
    if current != '\\\\':
    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:
    self.next_char()
    if self.current_char in ['n', 't', '\\\\', "'", '"']:
    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:
    self.errors.append((104, f"无效的转义字符:\\\\{self.current_char}", self.row, self.col))
    while self.current_char != "'" and self.current_char != '$':
    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主机测评 » 词法分析【实验一 词法分析-Sample语言常见单词】
    分享到: 更多 (0)

    评论 抢沙发

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