我正在為 python 的一個子集撰寫一個決議器(使用 PLY)。它包括:int、name、add、unarysub、assignments和function_calls。
到目前為止,我有以下語法:
# Grammar for P0:
# statements := statement
# statement := simple_statement
# simple_statement := assignment | d_expression
# assignment := NAME ['=' expression]
# d_expression := expression
# expression := sum
# sum := sum ' ' term | term
# term := factor
# factor := ' ' factor | '-' factor | primary
# primary: primary '(' [arguments] ') | atom
# atom := INT | NAME
# arguments := args | empty
# args := arg | arg ',' args
# arg := expression
它幾乎適用于我需要的所有情況。但是,我在使用d_expression上面的丟棄運算式()時遇到了麻煩。Python 支持丟棄運算式。例如,python 將x = 1 2和都1 2視為有效程式。前者在 python AST 中得到一個 Assign 節點,而后者得到一個 Expr 節點(它被丟棄,因此名稱為丟棄運算式)。
我試圖在我的決議器中模擬這種行為,但是在實作此功能時,我的決議器將1 - 2其視為兩個 Expr 節點 ( Expr(Constant(value=1), Expr(UnaryOp(op=USub(), operand=Constant(value=2)) 的串列。這是不正確的行為,它應該引發語法錯誤,因為我的語法中沒有 a expression MINUS expression。我不確定我哪里出錯了,感謝任何幫助。
下面是我的決議器的代碼實作:
class MyParser:
tokens = MyLexer.tokens
precedence = (
('left', 'PLUS', 'MINUS'),
('right', 'UMINUS'),
)
def __init__(self):
self.lexer = P0Lexer()
self.parser = yacc.yacc(module=self)
def p_module(self, p):
'''
module : statements
'''
p[0] = Module(body=p[1])
logging.info(ast.dump(p[0]))
def p_statements(self, p):
'''
statements : statement
| statement statements
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = [p[1]] p[2]
for i in p[0]:
logging.info(ast.dump(i))
def p_statement(self, p):
'''
statement : simple_statement
'''
p[0] = p[1]
logging.info(ast.dump(p[0]))
def p_simple_statement(self, p):
'''
simple_statement : assignment
'''
p[0] = p[1]
logging.info(ast.dump(p[0]))
def p_assignment(self, p):
'''
assignment : NAME EQUALS expression
'''
p[0] = Assign(targets=[Name(id=p[1], ctx=Store())], value=p[3])
logging.info(ast.dump(p[0]))
# def p_d_expression(self, p):
# '''
# d_expression : expression
# '''
# p[0] = Expr(value=p[1])
# logging.info(ast.dump(p[0]))
def p_expression(self, p):
'''
expression : sum
'''
p[0] = p[1]
logging.info(ast.dump(p[0]))
def p_sum(self, p):
'''
sum : sum PLUS term
| term
'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = BinOp(left=p[1], op=Add(), right=p[3])
logging.info(ast.dump(p[0]))
def p_term(self, p):
'''
term : factor
'''
p[0] = p[1]
logging.info(ast.dump(p[0]))
def p_factor(self, p):
'''
factor : MINUS factor %prec UMINUS
| primary
'''
if len(p) == 2:
p[0] = p[1]
else:
if p[1] == ' ':
p[0] = UnaryOp(op=UAdd(), operand=p[2])
else:
p[0] = UnaryOp(op=USub(), operand=p[2])
logging.info(ast.dump(p[0]))
def p_primary(self, p):
'''
primary : primary LPAREN arguments RPAREN
| atom
'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = Call(func=p[1], args=p[3])
logging.info(ast.dump(p[0]))
def p_arguments(self, p):
'''
arguments : args
| empty
'''
if len(p) == 2:
p[0] = p[1] if p[1] is not None else []
else:
p[0] = []
for i in p[0]:
logging.info(ast.dump(i))
def p_args(self, p):
'''
args : arg
| arg COMMA args
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = [p[1]] p[3]
for i in p[0]:
logging.info(ast.dump(i))
def p_arg(self, p):
'''
arg : expression
'''
p[0] = p[1]
logging.info(ast.dump(p[0]))
def p_atom(self, p):
'''
atom : INT
| NAME
'''
if isinstance(p[1], int):
p[0] = Constant(value=p[1])
else:
p[0] = Name(id=p[1], ctx=Load())
logging.info(ast.dump(p[0]))
def p_empty(self, p):
'''
empty :
'''
pass
def p_error(self, p):
if p:
logging.error("Syntax error at '%s'" % p.value)
else:
logging.error("Syntax error at EOF")
exit(1)
uj5u.com熱心網友回復:
我懷疑這里的問題是
statements := statement
是你的罪魁禍首。這允許您statement并排放置任何兩個 s 并將其視為一個statements. 如果是這種情況,那么1 - 2確實是一個有效的statements,因為正如您所指出的,這可以分解為陳述句1和-2,每個陳述句都是一個丟棄運算式。
以其他編程語言為例來了解如何解決這個問題可能會有所幫助。在 C、C 或 Java 之類的語言中,運算式可以構成陳述句,因為有一個語法規則
statement := expression ;
與;作為分隔符。這意味著如果你要找到類似的東西
1 2 2 3;
在 C/C /Java 程式中,這將是一個語法錯誤,因為 while1 2和2 3是運算式,這些運算式不是后面沒有分號的陳述句。另一方面,用這些語言寫是合法的
1 2; 2 3;
在一行上,構成這兩個陳述句的兩個運算式之間有一個明確的分隔符。
現在讓我們看看 Python。Python 不允許你寫
1 2 2 3
并將其算作兩個運算式。為什么不?好吧,如果您查看Python 語法,您可以看到它將一系列簡單陳述句決議為simple_stmts運算式的方式。(Python 使用決議運算式語法而不是 CFG,因此語法有點不同。)
simple_stmts:
| simple_stmt !';' NEWLINE
| ';'.simple_stmt [';'] NEWLINE
換句話說,asimple_stmts要么
- A
simple_stmt后跟換行符,或 - 一系列
simple_stmts,用分號分隔,后跟換行符。
這些規則中的每一個都在運算式之間給出了明確的分隔符,換行符或分號(或兩者)。
所以從這個意義上說,我懷疑問題根本上不是關于 drop 運算式的問題,而是關于如何允許多個陳述句彼此分離的問題。在用作陳述句的每個運算式之后需要某種分隔符 - 例如換行符或分號 - 應該可以解決此問題。
uj5u.com熱心網友回復:
感謝@templatetypdef。我允許兩個沒有分隔符的運算式。
我通過介紹這個產品解決了這個問題:
simple_stmts:
| simple_stmt !';' NEWLINE # Not needed, there for speedup
| ';'.simple_stmt [';'] NEWLINE
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/484535.html
