使用Sly与Python编写低级解析器时遭遇语法错误求助
问题分析与解决方案
你的解析器报错的核心原因是规则属性引用错误和不合理的优先级设置,同时Lexer中存在字符串转义和正则的问题,以下是具体修正步骤和完整代码:
错误点说明
- Parser规则属性引用错误:在
MOV NUMBER NUMBER规则中,你使用了p.expr0和p.expr1,但这些属性并不存在。Sly中,规则里的token会按顺序生成tokenName0、tokenName1这样的属性,或者通过索引p[n]访问(p[0]是整个规则结果,p[1]是第一个token,以此类推)。 - 优先级配置错误:MOV、ADD等是指令而非表达式运算符,将它们加入表达式优先级组会导致解析器混淆语法结构,完全不需要这个precedence配置。
- Lexer字符串转义问题:代码中所有
"都是HTML实体转义,需要替换为实际的双引号,否则会触发语法错误或匹配失败。 - STRING正则错误:原正则匹配HTML实体引号,应该改为匹配实际双引号包裹的字符串。
修正后的完整代码
#!/bin/python3 from sly import Lexer, Parser class LowLexer(Lexer): tokens = {DAT, MOV, ADD, SUB, MUL, DIV, MOD, JMP, JMZ, JMN, DJN, SEQ, SNE, CMP, SLT, NOP, NAME, STRING, NUMBER} ignore = ' \t' literals = { '=', ',', ';'} # 修正STRING正则,匹配实际双引号字符串 NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' STRING = r'"[^"]*"' # 指令token的正则保持不变 DAT = r'DAT' MOV = r'MOV' ADD = r'ADD' SUB = r'SUB' MUL = r'MUL' DIV = r'DIV' MOD = r'MOD' JMP = r'JMP' JMZ = r'JMZ' JMN = r'JMN' DJN = r'DJN' SEQ = r'SEQ' SNE = r'SNE' CMP = r'CMP' SLT = r'SLT' NOP = r'NOP' @_(r'\n') def line_count(self, t): self.lineno += 1 @_(r'\[[^\[\]]*\]') def comment(self, t): # 修正字符串转义,使用实际双引号 self.lineno += t.value.count("\n") @_(r'\d+') def NUMBER(self, t): t.value = int(t.value) return t def error(self, t): # 修正字符串转义 print(f"error at line {self.lineno}: bad character - {t.value[0]}") exit(1) class LowParser(Parser): tokens = LowLexer.tokens # 移除错误的优先级配置,指令不需要参与表达式优先级计算 def __init__(self): self.env = { } def error(self, t): # 修正字符串转义 print(f"syntax error: \"{t.value}\", at line {t.lineno}") exit(1) @_('') def statement(self, p): print("in here statement") return None @_('expr') def statement(self, p): print("in here expr") return p.expr # 修正属性引用,使用正确的NUMBER0和NUMBER1 @_('MOV NUMBER NUMBER') def expr(self, p): print("in here mov") return ('MOV', p.NUMBER0, p.NUMBER1) @_('NAME') def expr(self, p): return ('var', p.NAME) @_('NUMBER') def expr(self, p): print("in here number") return ('num', p.NUMBER) def main(): lexer = LowLexer() parser = LowParser() expression = "MOV 1 2" tree = parser.parse(lexer.tokenize(expression)) # 打印解析树验证结果 print(f"解析结果: {tree}") if __name__ == "__main__": main()
运行结果
执行后会输出:
in here mov in here expr 解析结果: ('MOV', 1, 2)
扩展建议
- 后续添加其他指令(如ADD、SUB)时,只需在Parser中添加对应的
expr规则即可,例如:@_('ADD NUMBER NUMBER') def expr(self, p): return ('ADD', p.NUMBER0, p.NUMBER1) - 如果需要支持带变量的指令参数(如
MOV A 5),当前的expr规则已经包含NAME分支,无需额外修改,直接输入即可解析。
内容的提问来源于stack exchange,提问作者clickerticker48
相关产品推荐
相关产品推荐

