PLY : 错过"if"声明



这是我第一次尝试使用PLY或任何词法分析器/解析器工具,所以我不确定什么是错的。

我正试图实现一个小型的基于Python语法松散的汇编语言,特别是对于if语句与缩进块。下面是一个例子:

if d0 == 0x42:
a1 = d2
d0 = 0

我写了一个解析器来处理INDENTDEDENT,它产生了这个标记列表:

LexToken(IF,'if',1,0)
LexToken(REGISTER,'d0',1,3)
LexToken(IS_EQUAL,'==',1,6)
LexToken(NUMBER,'0x42',1,9)
LexToken(COLON,':',1,13)
LexToken(INDENT,'t',2,15)
LexToken(REGISTER,'a1',2,16)
LexToken(EQUAL,'=',2,19)
LexToken(REGISTER,'d2',2,21)
LexToken(DEDENT,'d0',3,24)
LexToken(REGISTER,'d0',3,24)
LexToken(EQUAL,'=',3,27)
LexToken(NUMBER,'0',3,29)

这似乎OK (INDENTDEDENTvalue,linenolexpos是错误的,但我不使用它们)

解析器是:

import ply.yacc as yacc
# Get the token map from the lexer.  This is required.
from asmlexer import tokens, MyLexer
from instr import *

def p_statement_assignment(p):
'statement : assignment'
print('statmt1 :', [x for x in p])
p[0] = p[1]

def p_statement_ifstatmt(p):
'statement : ifstatmt'
print('statmt2 :', [x for x in p])
p[0] = p[1]

def p_assignment(p):
'assignment : value EQUAL value'
print("assignment :", [x for x in p])
p[0] = Move(p[3], p[1])
def p_ifstatmt(p):
'ifstatmt : IF condition COLON INDENT statement DEDENT'
print("IF :", [x for x in p])
p[0] = If(p[2], body = p[5])
def p_condition_equal(p):
'condition : value IS_EQUAL value'
print("condition ==", [x for x in p])
p[0] = "%s == %s" % (p[1], p[3])

def p_value_register(p):
'value : REGISTER'
print('register :', [x for x in p])
p[0] = Register(p[1])
def p_value_number(p):
'value : NUMBER'
print('value:', [x for x in p])
p[0] = Value(p[1])
# Error rule for syntax errors
def p_error(p):
print(p)
print("Syntax error in input!")
# Build the parser
class MyParser(object):
def __init__(self):
lexer = MyLexer()
self.lexer = lexer
self.parser = yacc.yacc()

def parse(self, code):
self.lexer.input(code)
result = self.parser.parse(lexer = self.lexer)
return result
if True:
with open("test.psm") as f:
data = f.read()
parser = MyParser()
result = parser.parse(data)
print(result)
print(result.get_code())

似乎缺少IF标记:

register : [None, 'd0']
value: [None, '0x42']
condition == [None, <instr.Register object at 0x000002429DA5E340>, '==', <instr.Value object at 0x000002429DA5E250>]
register : [None, 'a1']
register : [None, 'd2']
assignment : [None, <instr.Register object at 0x000002429DA2C970>, '=', <instr.Register object at 0x000002429DA5E5E0>]
statmt1 : [None, <instr.Move object at 0x000002429DA5E370>]
LexToken(REGISTER,'d0',3,24)
Syntax error in input!
value: [None, '0']
None
Traceback (most recent call last):
File ".asmparser.py", line 137, in <module>
print(result.get_code())
AttributeError: 'NoneType' object has no attribute 'get_code'

我不明白为什么…

语法的开始符号是statement,因此语法描述的输入由一条语句组成,要么是赋值语句,要么是条件语句。因此,紧跟在该单一语句后面的令牌必须是输入的结束。但事实并非如此。它是REGISTERd0,因为您的输入包含两个语句。

LALR(1)解析器(由Ply生成)可以使用下一个令牌来验证可能的约简操作。[注1]如果下一个令牌不能跟随缩减后的非终结符,则会发出错误信号。此错误是在缩减之前还是之后发出信号,取决于解析器生成器的特定性质。一些解析器生成器,如Bison,积极地优化了前瞻性,如果不是绝对必要的,它们的解析器甚至不会读取前瞻性标记。但是大多数解析器生成器(包括Ply)生成的解析器总是在决定下一步操作之前读取前瞻令牌。[注2]

如果你想让你的解析器处理一个语句序列,你需要一个开始符号展开成一个语句序列,比如program: | program statement。您可能还希望允许if语句体包含一系列语句,而不仅仅是当前语法允许的单个语句。

指出

  1. 减排行动;是解析器在到达非终结符的末尾时所做的事情,并且"该非末端与单个非末端相对应的序列。作为缩减操作的一部分,解析器执行缩减操作;对于Ply,这意味着调用与产量减少相关的p_函数。

  2. 解析器读取了前瞻标记并不意味着它必须在执行缩减之前咨询它。许多解析器使用压缩表,可以将错误操作与默认的缩减操作结合起来。

最新更新