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:pos–1] if (pos–1) > 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:pos–1] if (pos–1) > 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_num–1, 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)


