云雀解析器以错误的顺序解析输入

问题描述

所以我正在使用百灵鸟在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

如果您要增强功能,以便随后进行评估,那么您需要为expraddmul等每件事都需要一个类,并需要一种区分方法一个数字和一个变量等。您要做的是将算术表达式转换为一组指令,这些指令将返回其结果,希望还可以处理除以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()会计算所有包含的类实例以返回单个值。