簡體   English   中英

Lark 解析器以錯誤的順序解析輸入

[英]Lark parser parsing input in wrong order

所以我正在使用 lark 在 python 中制作一種編程語言,當我解析輸入時,例如

print("HI");
x = input("Number: ");

它在print語句之前解析input語句。 下面是我的代碼的樣子。

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)

這有點是我的代碼的要點,我不知道為什么我的解析器沒有按順序解析。 幫助將不勝感激,謝謝!

您看到這種行為的原因是因為您的變壓器。

您似乎在評估 Transformer 中的樹執行時(就像對 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

如果你想增強所以評估都是在之后完成的,那么你需要一個類來處理expraddmul等每一件事,以及一種區分 NUMBER 和變量等的方法。你將成為什么做的是將算術表達式轉換為一組指令,這些指令將返回其結果,希望也能處理除以 0 之類的事情。

這實際上應該在您的 Transformer 中相對容易構建 - 通過從例如 add 返回正確的內容 - 必須給定對 NUMBER/STRING/variable/class 實例的 val1 和 val2 引用並返回此 Add(thing1,thing2)。

我首先查看 lark calc.py 示例,因為它已經處理了括號表達式 - https://github.com/lark-parser/lark/blob/master/examples/calc.py - 並基於此而不是立即執行每個計算,您需要返回類實例,以便在整個表達式Expr()的類實例上對.eval()一次調用評估所有包含的類實例以返回單個值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM