[英]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
如果你想增強所以評估都是在之后完成的,那么你需要一個類來處理expr
、 add
、 mul
等每一件事,以及一種區分 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.