用Python编写C++词法分析器时的代码拆分问题求助
问题描述
当前实现的Python版C++词法分析器存在两个核心问题:
- 用
split(" ")按空格拆分代码时,x=2、function()这类无空格分隔的代码无法拆分成独立Token,必须手动添加空格(比如改成x = 2)才能识别 - 无法识别每行开头的Token,只有手动在Token之间和行首加空格时代码才能正常运行
试过两种方案都没解决:先按行拆分再按空格拆分,操作复杂且没解决无空格Token的识别;想过按运算符拆分,但没法同时把运算符本身作为Token识别,方案不可行。
尝试过的代码
第一种直接按空格拆分:
f=open("code.txt") input=f.read() input=input.split(" ")
第二种先按行拆分再按空格拆分:
f=open("code.txt") input=f.read() input1=input.split("\n") for var in input1: var=var.split(" ")
解决方案:用正则表达式匹配Token
词法分析不能靠简单的空格拆分,得按Token的语法规则去匹配。用正则表达式可以一次性识别所有符合C++语法的Token,完美解决无空格分隔和行首Token的问题。
步骤1:定义Token的正则规则
先把C++常见的Token类型对应的正则模式写出来,包括标识符、数字、运算符、标点和关键字:
import re # 定义C++各类Token的正则模式,带注释方便维护 token_pattern = r""" (?P<IDENTIFIER>[a-zA-Z_][a-zA-Z0-9_]*)| # 变量/函数名:开头是字母或下划线,后面跟字母/数字/下划线 (?P<NUMBER>\d+(\.\d+)?)| # 数字:整数或带小数点的浮点数 (?P<OPERATOR>=|\+|-|\*|/|==|!=|<|>|<=|>=)| # 常用运算符:单字符或双字符的 (?P<PUNCTUATION>\(|\)|\{|\}|\;|\,)| # 括号和标点:(){};, (?P<KEYWORD>int|void|return|if|else) # 关键字:可根据需求扩展更多 """ # 编译正则,忽略注释和空白符,关键字不区分大小写 regex = re.compile(token_pattern, re.VERBOSE | re.IGNORECASE)
步骤2:读取代码并提取所有Token
用re.findall()全局匹配所有符合规则的Token,不管有没有空格分隔:
# 用with语句自动管理文件,比直接open更安全 with open("code.txt", "r") as f: code = f.read() # 提取所有匹配的Token tokens = regex.findall(code) # 整理结果:过滤空字符串,只保留有效Token processed_tokens = [] for token_group in tokens: # 找到每组里非空的那个值(每个组对应一种Token类型) token = next(item for item in token_group if item) processed_tokens.append(token) # 打印结果 print(processed_tokens)
比如输入代码x=2; function(),会输出['x', '=', '2', ';', 'function', '(', ')'],完全符合预期。
扩展:区分Token类型
如果需要知道每个Token的类型(比如是标识符还是运算符),可以修改处理逻辑,把类型和值一起保存:
processed_tokens_with_type = [] for token_group in tokens: # 找到第一个非空的匹配项,同时获取对应的类型名 token_info = next((name, value) for name, value in zip(regex.groupindex.keys(), token_group) if value) processed_tokens_with_type.append(token_info) print(processed_tokens_with_type)
输出会是[('IDENTIFIER', 'x'), ('OPERATOR', '='), ('NUMBER', '2'), ('PUNCTUATION', ';'), ('IDENTIFIER', 'function'), ('PUNCTUATION', '('), ('PUNCTUATION', ')')],方便后续做语法分析。
为什么这个方案可行?
正则表达式是按语法规则匹配,而不是按空格拆分,所以不管Token之间有没有空格,只要符合C++的语法规则,就能被正确识别。而且全局匹配会忽略行首、行尾的空白,自然解决了行首Token无法识别的问题。
内容的提问来源于stack exchange,提问作者B Khorram

