如何解决云雀解析器以错误的顺序解析输入
所以我正在使用百灵鸟在python中编写一种编程语言,当我解析输入时,例如
print("HI");
x = input("Number: ");
它在input
语句之前解析print
语句。下面是我的代码的样子。
from lark import Lark,Transformer,v_args
from lark.indenter import Indenter
grammar = '''
?start: expr
| statement* -> statement
?statement: "print" "(" expr ")" ";" -> print_statement
| "input" "(" expr ")" ";" -> input_statement
| NAME "=" "input" "(" expr ")" ";" -> var_input_statement
| NAME "=" expr ";" -> assign_var
?expr: STRING -> string
| NUMBER -> number
| NUMBER "+" NUMBER -> add
| NUMBER "-" NUMBER -> sub
| NUMBER "*" NUMBER -> mul
| NUMBER "/" NUMBER -> div
| STRING "+" STRING -> str_add
| NAME -> get_var
%import common.ESCAPED_STRING -> STRING
%import common.NUMBER
%import common.CNAME -> NAME
%declare _INDENT _DEDENT
%import common.WS_INLINE
%ignore WS_INLINE
%import common.NEWLINE -> _NL
%ignore _NL
'''
class Print():
def __init__(self,value):
self.value = value
def eval(self):
return print(self.value)
class input():
def __init__(self,value):
self.value = value
def eval(self):
return input(self.value)
@v_args(inline=True)
class MainTransformer(Transformer):
number = int
string = str
def __init__(self):
self.vars = {}
def add(self,val1,val2):
return int(val1) + int(val2)
def sub(self,val2):
return int(val1) - int(val2)
def mul(self,val2):
return int(val1) * int(val2)
def div(self,val2):
return int(val1) / int(val2)
def get_var(self,name):
try:
return self.vars[name]
except KeyError:
raise Exception(f"Variable {name} not found")
def assign_var(self,name,value):
if name == "print":
pass
if type(value) == str:
value = value.strip('"')
self.vars[name] = value
def var_input_statement(self,value):
data = self.input_statement(value,store_data=True)
self.assign_var(name,data)
def print_statement(self,value=" "):
if type(value) == str:
value = value.strip('"')
return Print(value)
def input_statement(self,value,store_data=False):
if type(value) == str:
value = value.strip('"')
if store_data == True:
data = input(value)
return data
else:
return Input(value)
def statement(self,*values):
for value in values:
if value == None:
pass
else:
value.eval()
class MainIndenter(Indenter):
NL_type = '_NL'
OPEN_PAREN_types = ['LPAR','LBRACE']
CLOSE_PAREN_types = ['RPAR','RBRACE']
INDENT_TYPE = '_INDENT'
DEDENT_type = '_DEDENT'
tab_len = 8
parser = Lark(grammar,parser='lalr',transformer=MainTransformer(),postlex=MainIndenter())
main_parser = parser.parse
data_input = '''
print("HI");
x = input("Number: ");
'''
if __name__ == '__main__':
main_parser(data_input)
这在某种程度上是我代码的要点,我不知道为什么解析器没有按顺序解析。帮助将不胜感激,谢谢!
解决方法
看到此行为的原因是由于使用了变压器。
您似乎在执行Transformer中的树(执行var_input_statement时,这就是为什么它立即请求输入)的评估树与最后生成输出(就像执行print_statement那样)之间的过程为什么它们出现在输入之后)。您必须选择一个。
一种选择是忽略您的Input和Print类,而只是立即产生输出/请求输入/做计算-然后您将按期望的顺序获得它们,即根据要解析的程序代码。
另一种替代方法是将所有内容延迟到调用.eval()
方法之前,在这种情况下,转换器必须发出一系列不直接求值的类实例-可以立即添加两个文字数字一起使用,但是为了简单起见,我建议您不要进行优化,直到未进行优化为止。这种替代方案需要对您的代码进行更复杂的更改,因此不会评估例如直接添加-您必须在.eval()
时维护变量,然后也进行expr
计算,并用结果更新变量。但是,您还没有获得表达式所需的类,因此您可以仅对它们调用eval()
,它们将使用变量。
以下是您的残骸中经过非常轻微修改的代码,它们采用第一种方法-您必须自己解决第二种方法的复杂性。
就我个人而言,我会在转换器中保留一些打印语句,以便我可以跟踪正在发生的事情,直到它正常工作并稳定为止。从长远来看,您可能会将这些打印语句转换为记录调用。
from lark import Lark,Transformer,v_args
from lark.indenter import Indenter
grammar = '''
?start: expr
| statement* -> statement
?statement: "print" "(" expr ")" ";" -> print_statement
| "input" "(" expr ")" ";" -> input_statement
| NAME "=" "input" "(" expr ")" ";" -> var_input_statement
| NAME "=" expr ";" -> assign_var
?expr: STRING -> string
| NUMBER -> number
| NUMBER "+" NUMBER -> add
| NUMBER "-" NUMBER -> sub
| NUMBER "*" NUMBER -> mul
| NUMBER "/" NUMBER -> div
| STRING "+" STRING -> str_add
| NAME -> get_var
%import common.ESCAPED_STRING -> STRING
%import common.NUMBER
%import common.CNAME -> NAME
%declare _INDENT _DEDENT
%import common.WS_INLINE
%ignore WS_INLINE
%import common.NEWLINE -> _NL
%ignore _NL
'''
class Print():
def __init__(self,value):
self.value = value
def eval(self):
return print(self.value)
class Input():
def __init__(self,value):
self.value = value
def eval(self):
return input(self.value)
@v_args(inline=True)
class MainTransformer(Transformer):
number = int
string = str
def __init__(self):
self.vars = {}
def add(self,val1,val2):
return int(val1) + int(val2)
def sub(self,val2):
return int(val1) - int(val2)
def mul(self,val2):
return int(val1) * int(val2)
def div(self,val2):
return int(val1) / int(val2)
def get_var(self,name):
try:
return self.vars[name]
except KeyError:
raise Exception(f"Variable {name} not found")
def assign_var(self,name,value):
if name == "print":
pass
if type(value) == str:
value = value.strip('"')
self.vars[name] = value
def var_input_statement(self,value):
data = self.input_statement(value,store_data=True)
self.assign_var(name,data)
def print_statement(self,value=" "):
if type(value) == str:
value = value.strip('"')
print( "PRINTING",value ) # ADDED
return Print(value)
def input_statement(self,value,store_data=False):
if type(value) == str:
value = value.strip('"')
if store_data == True:
data = input(value)
return data
else:
return Input(value) # UNUSED
def statement(self,*values):
for value in values:
if value == None:
pass
else:
# value.eval() # COMMENTED
pass # ADDED
class MainIndenter(Indenter):
NL_type = '_NL'
OPEN_PAREN_types = ['LPAR','LBRACE']
CLOSE_PAREN_types = ['RPAR','RBRACE']
INDENT_TYPE = '_INDENT'
DEDENT_type = '_DEDENT'
tab_len = 8
parser = Lark(grammar,parser='lalr',transformer=MainTransformer(),postlex=MainIndenter())
main_parser = parser.parse
data_input = '''
print("HI");
x = input("Number: ");
'''
if __name__ == '__main__':
main_parser(data_input)
输出:
PRINTING HI
Number: qwe
如果您要增强功能,以便随后进行评估,那么您需要为expr
,add
,mul
等每件事都需要一个类,并需要一种区分方法一个数字和一个变量等。您要做的是将算术表达式转换为一组指令,这些指令将返回其结果,希望还可以处理除以0等操作。
这实际上应该相对容易地在您的Transformer中构建-通过从例如add返回正确的东西-必须将val1和val2引用赋予NUMBER / STRING / variable / class实例,然后返回此Add(thing1,的东西)。
我将首先查看百灵鸟calc.py示例,因为它已经可以处理方括号中的表达式-https://github.com/lark-parser/lark/blob/master/examples/calc.py-并且基于此,而不是立即执行每个计算,您需要返回类实例,这样一个实例在整个表达式.eval()
的类实例上调用Expr()
会计算所有包含的类实例以返回单个值。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。