PLY:错过了 "if" 声明
PLY : missed "if" statement
这是我第一次尝试使用 PLY 或任何 lexer/parser 工具,所以我不确定哪里出了问题。
我正在尝试基于 Python 语法松散地实现一种小型汇编语言,尤其是对于带有缩进块的 if 语句。这是一个例子:
if d0 == 0x42:
a1 = d2
d0 = 0
我编写了一个解析器来处理 INDENT
和 DEDENT
,它生成了这个标记列表:
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)
这似乎还可以(INDENT
和DEDENT
value
、lineno
和lexpos
是错误的,但我没有使用它们)
我的解析器是:
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
,因此您的语法描述的输入由一个语句组成,可以是赋值语句,也可以是条件语句。因此,单一语句之后的标记必须是输入的结尾。但事实并非如此。这是 REGISTER
d0
,因为您的输入包含两个语句。
LALR(1) 解析器,这是 Ply 生成的,可以使用下一个标记来验证可能的缩减操作。 [注意 1] 如果下一个标记不能跟在简化的非终结符后面,那么将发出错误信号。此错误是在缩减之前还是之后发出信号取决于解析器生成器的特定性质。一些解析器生成器,如 Bison,积极优化前瞻,如果不是绝对必要,它们的解析器甚至不会读取前瞻标记。但是大多数解析器生成器,包括 Ply,生成的解析器总是在决定下一步操作之前读取先行标记。 [注2]
如果您希望解析器处理一系列语句,您将需要一个开始符号来扩展为一系列语句,例如 program: | program statement
。您可能还希望允许 if 语句的主体包含一系列语句,而不仅仅是当前语法允许的单个语句。
备注
“归约动作”是解析器在到达非终结符末尾时执行的操作,并将对应于该非终结符的序列“归约”为单个非终结符。作为归约动作的一部分,解析器执行归约动作;在 Ply 的情况下,这意味着调用与正在减少的生产相关联的 p_
函数。
仅仅因为解析器已经读取了先行标记并不意味着它在进行归约之前一定会参考它。许多解析器使用压缩表,这些表可能会将错误操作与默认减少操作结合起来。
这是我第一次尝试使用 PLY 或任何 lexer/parser 工具,所以我不确定哪里出了问题。
我正在尝试基于 Python 语法松散地实现一种小型汇编语言,尤其是对于带有缩进块的 if 语句。这是一个例子:
if d0 == 0x42:
a1 = d2
d0 = 0
我编写了一个解析器来处理 INDENT
和 DEDENT
,它生成了这个标记列表:
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)
这似乎还可以(INDENT
和DEDENT
value
、lineno
和lexpos
是错误的,但我没有使用它们)
我的解析器是:
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
,因此您的语法描述的输入由一个语句组成,可以是赋值语句,也可以是条件语句。因此,单一语句之后的标记必须是输入的结尾。但事实并非如此。这是 REGISTER
d0
,因为您的输入包含两个语句。
LALR(1) 解析器,这是 Ply 生成的,可以使用下一个标记来验证可能的缩减操作。 [注意 1] 如果下一个标记不能跟在简化的非终结符后面,那么将发出错误信号。此错误是在缩减之前还是之后发出信号取决于解析器生成器的特定性质。一些解析器生成器,如 Bison,积极优化前瞻,如果不是绝对必要,它们的解析器甚至不会读取前瞻标记。但是大多数解析器生成器,包括 Ply,生成的解析器总是在决定下一步操作之前读取先行标记。 [注2]
如果您希望解析器处理一系列语句,您将需要一个开始符号来扩展为一系列语句,例如 program: | program statement
。您可能还希望允许 if 语句的主体包含一系列语句,而不仅仅是当前语法允许的单个语句。
备注
“归约动作”是解析器在到达非终结符末尾时执行的操作,并将对应于该非终结符的序列“归约”为单个非终结符。作为归约动作的一部分,解析器执行归约动作;在 Ply 的情况下,这意味着调用与正在减少的生产相关联的
p_
函数。仅仅因为解析器已经读取了先行标记并不意味着它在进行归约之前一定会参考它。许多解析器使用压缩表,这些表可能会将错误操作与默认减少操作结合起来。