基于rply开发自定义语言时如何通过Parser实现字符串打印
核心报错原因
代码无法运行、不能正确输出字符串的问题来自4处:
- Parser中
statement语法规则仅添加了装饰器,没有编写对应处理函数,语法定义不完整 - 现有
statement规则仅支持匹配单个STRINGtoken,无法处理引号内被空格拆分为多个STRING的连续单词 - AST层缺少字符串字面量节点,
Print节点调用value.eval()时没有对应实现 - 主入口提前遍历了lexer返回的迭代器对象,迭代器遍历一次后内容为空,后续解析器拿不到任何token直接抛出异常
具体修改方案
1. 补全AST节点定义(AST.py)
新增字符串字面量节点,实现eval方法返回字符串内容:
class Print(): def __init__(self, value): self.value = value def eval(self): print(self.value.eval()) # 新增字符串节点 class String(): def __init__(self, content): self.value = content def eval(self): return self.value
2. 完善Parser语法规则(Parser.py)
导入新增的String节点,补全statement规则,支持多个连续STRING token自动拼接为完整字符串:
from rply import ParserGenerator from ast import Print, String class Parser(): def __init__(self): self.pg = ParserGenerator( ['PRINT', 'QUOTE', 'STRING', 'ENDLN'] ) def parse(self): @self.pg.production('program : PRINT QUOTE statement QUOTE ENDLN') def program(p): # p[2]对应语法规则里的statement部分,也就是拼接完成的字符串节点 return Print(p[2]) # 匹配单个字符串token @self.pg.production('statement : STRING') def single_string(p): return String(p[0].getstr()) # 匹配多个连续字符串token,递归拼接,单词之间补空格 @self.pg.production('statement : statement STRING') def concat_string(p): prev_content = p[0].eval() new_word = p[1].getstr() return String(f"{prev_content} {new_word}") def get_parser(self): return self.pg.build()
3. 修复主入口迭代器耗尽问题
rply的lexer返回的是生成器对象,提前遍历会导致后续解析无内容,如需调试打印token要先转成列表存储:
from lexer import Lexer from parser import Parser lexer = Lexer().get_lexer() tokens = lexer.lex(input()) # 调试打印token时请打开以下注释,直接遍历原生成器会导致后续解析失败 # token_list = list(tokens) # for token in token_list: # print(token) pg = Parser() pg.parse() parser = pg.get_parser() # 调试时传入token_list,正常运行直接传入tokens即可 parser.parse(tokens).eval()
修改完成后输入outln "I love SO";即可正常输出I love SO。
可选优化:如果想简化Parser逻辑,可以直接修改Lexer规则,一次性匹配双引号包裹的完整字符串,不需要拆分后再拼接,对应添加规则:
self.lexer.add('STRING', r'"[^"]*"'),解析时去掉首尾引号即可,这种实现方式更符合常规词法分析器的设计逻辑。
内容的提问来源于stack exchange,提问作者gamer merch
相关产品推荐
相关产品推荐

