簡體   English   中英

YACC語法用於算術表達式,不帶括號

[英]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 ,通過擴展exprtermfactor'(' expr ')'

這是解決這種歧義的三種解決方案:

1.明確消除歧義

消除歧義是乏味的,但並不是特別困難; 我們僅在每個優先級上定義兩種子表達式,一種可以用括號括起來,另一種絕對不能用括號包圍。 我們從括號的表達式的簡寫開始:

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_listexpr之間進行選擇。 它只是減少了 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

沒有沖突。

2.使用GLR解析器

盡管可以明確地消除歧義,但是生成的語法是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如果兩個生產都可能,則設置。 (它們在只能進行一次生產的情況下不起作用。)

3.修正語言

盡管可以按照指定的方式解析語言,但我們可能沒有對任何人做任何幫助。 甚至會有人因改變而感到驚訝

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.

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