[英]YACC grammar for arithmetic expressions, with no surrounding parentheses
我想在YACC中編寫算術表達式的規則; 其中定義了以下操作:
+ - * / ()
但是,我不希望該語句帶有括號。 也就是說, a+(b*c)
應該具有匹配規則,但(a+(b*c))
不應該具有匹配規則。
我該如何實現?
動機:
在我的語法中,我定義了一個像這樣的集合: (1,2,3,4)
並且我想將(5)
視為1元素集合。 模糊性導致減少/減少沖突。
這是一個非常小的算術語法。 它處理您提到的四個運算符和賦值語句:
stmt: ID '=' expr ';'
expr: term | expr '-' term | expr '+' term
term: factor | term '*' factor | term '/' factor
factor: ID | NUMBER | '(' expr ')' | '-' factor
定義“ set”文字很容易:
set: '(' ')' | '(' expr_list ')'
expr_list: expr | expr_list ',' expr
如果我們假設集合文字只能作為賦值語句中的值出現,而不能作為算術運算符的操作數出現,那么我們將為“表達式或集合文字”添加語法:
value: expr | set
並修改賦值語句的語法以使用該語法:
stmt: ID '=' value ';'
但這會導致您提到的減少/減少沖突,因為(5)
可能是一個expr
,通過擴展expr
→ term
→ factor
→ '(' expr ')'
。
這是解決這種歧義的三種解決方案:
消除歧義是乏味的,但並不是特別困難; 我們僅在每個優先級上定義兩種子表達式,一種可以用括號括起來,另一種絕對不能用括號包圍。 我們從括號的表達式的簡寫開始:
paren: '(' expr ')'
然后為每個子表達式類型X
添加一個生產pp_X
:
pp_term: term | paren
並通過允許帶括號的子表達式作為操作數來修改現有的生產形式:
term: factor | pp_term '*' pp_factor | pp_term '/' pp_factor
不幸的是,由於定義了expr_list
的方式,我們仍然會遇到移位/減少沖突。 面對賦值語句的開頭:
a = ( 5 )
已經用5
結束,所以)
是先行標記,解析器不知道(5)
是set
(在這種情況下下一個標記將是; )還是paren
(僅paren
一個標記有效)令牌是一個操作數)。 這並不是模棱兩可的-可以使用LR(2)解析表輕松解析該解析-但是沒有多少工具可以生成LR(2)解析器。 因此,我們堅持認為expr_list
必須具有兩個表達式,並在set
的生產中添加paren
來回避問題:
set: '(' ')' | paren | '(' expr_list ')'
expr_list: expr ',' expr | expr_list ',' expr
現在,解析器無需在賦值語句中在expr_list
和expr
之間進行選擇。 它只是減少了( 5 )的paren
並等待下一個標記澄清解析。
最終結果是:
stmt: ID '=' value ';'
value: expr | set
set: '(' ')' | paren | '(' expr_list ')'
expr_list: expr ',' expr | expr_list ',' expr
paren: '(' expr ')'
pp_expr: expr | paren
expr: term | pp_expr '-' pp_term | pp_expr '+' pp_term
pp_term: term | paren
term: factor | pp_term '*' pp_factor | pp_term '/' pp_factor
pp_factor: factor | paren
factor: ID | NUMBER | '-' pp_factor
沒有沖突。
盡管可以明確地消除歧義,但是生成的語法是is腫的,而且不是很清楚,這是不幸的。
Bison可以生成GLR解析器,這將使語法更加簡單。 實際上,原始語法幾乎無需修改即可運行。 我們只需要使用Bison %dprec
動態優先級聲明來指示如何消除歧義:
%glr-parser
%%
stmt: ID '=' value ';'
value: expr %dprec 1
| set %dprec 2
expr: term | expr '-' term | expr '+' term
term: factor | term '*' factor | term '/' factor
factor: ID | NUMBER | '(' expr ')' | '-' factor
set: '(' ')' | '(' expr_list ')'
expr_list: expr | expr_list ',' expr
value
的兩個生產中的%dprec
聲明告訴解析器偏愛value: set
如果兩個生產都可能,則設置。 (它們在只能進行一次生產的情況下不起作用。)
盡管可以按照指定的方式解析語言,但我們可能沒有對任何人做任何幫助。 甚至會有人因改變而感到驚訝
a = ( some complicated expression ) * 2
至
a = ( some complicated expression )
突然, a
變成了集合,而不是標量。
通常情況下,語法不明顯的語言也很難被人類解析。 (例如,請參見C ++的“最令人煩惱的解析”)。
使用( expression list )
創建元組文字的Python采用一種非常簡單的方法:( ( expression )
始終是一個表達式,因此元組需要為空或至少包含一個逗號。 為了使后者成為可能,Python允許用尾隨逗號編寫元組文字。 除非元組包含單個元素,否則尾隨逗號是可選的。 因此(5)
是一個表達式,而()
, (5,)
, (5,6)
和(5,6,)
都是元組(后兩個在語義上是相同的)。
Python列表寫在方括號之間; 在此,再次允許使用逗號作為結尾,但由於[5]
不太明確,因此不需要使用逗號。 所以[]
, [5]
, [5,]
, [5,6]
和[5,6,]
都是列表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.