0

所以我正在使用 lark 在 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, val1, val2):
        return int(val1) - int(val2)

    def mul(self, val1, val2):
        return int(val1) * int(val2)

    def div(self, val1, 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, name, 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)

这有点像我的代码的要点,我不知道为什么我的解析器没有按顺序解析。帮助将不胜感激,谢谢!

4

1 回答 1

0

你看到这种行为的原因是你的变压器。

您似乎处于在执行变压器时评估树的中途(就像您对 var_input_statement 所做的那样,这就是它立即请求输入的原因)和最后产生输出(就​​像您对 print_statement 所做的那样,这就是它们出现的原因输入后)。你必须选择其中之一。

一种替代方法是忽略您的 Input 和 Print 类,并立即生成输出/请求输入/进行计算 - 然后您将按照您期望的顺序获得它们,即按照您正在解析的程序代码。

另一种选择是将所有内容延迟到.eval()调用方法之前,在这种情况下,您的转换器必须发出一系列不直接评估任何内容的类实例 - 好吧,您可以立即将两个文字数字相加,但为了简单性我建议您在未优化之前忽略优化。这种替代方法需要对您的代码进行更复杂的更改,因此它不会评估例如直接添加 - 您必须及时维护变量.eval(),然后也进行expr计算,并使用结果更新变量。但是,您还没有获得表达式所需的类,因此您可以调用eval()它们,它们将使用变量。

这是您的 mre 中采用第一种方法的一些非常温和的修改代码 - 您必须自己解决第二种方法的复杂性。

就我个人而言,我会在变压器中保留一些打印语句,以便我可以跟踪正在发生的事情,直到它工作且稳定为止。从长远来看,您可能会将这些打印语句转换为日志调用。

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, val1, val2):
        return int(val1) - int(val2)

    def mul(self, val1, val2):
        return int(val1) * int(val2)

    def div(self, val1, 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, name, 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等,以及区分 NUMBER 和变量等的方法。您要做的是转向将算术表达式转换为一组指令,这些指令将返回其结果,希望还能处理除以 0 等事情。

这实际上应该相对容易在您的 Transformer 中构建 - 通过从例如 add 返回正确的内容 - 必须将 val1 和 val2 引用提供给 NUMBER/STRING/variable/class 实例并返回此 Add(thing1,thing2)。

我将从查看 lark calc.py 示例开始,因为它已经处理了括号表达式 - https://github.com/lark-parser/lark/blob/master/examples/calc.py - 并且基于此而不是立即执行每个计算,您需要返回类实例,以便.eval()对整个表达式的类实例的一次调用Expr()评估所有包含的类实例以返回单个值。

于 2020-11-09T19:37:07.917 回答