的常用方法是通過拆分元運算符成一個獨立的生產:
%left '+' '-'
%left '*'
%nonassoc UNARY
%token INTEGER
%%
start: expr {printf("Result: %d\n",$1);}
expr : primary
| '-' primary %prec UNARY {$$ = -$2;}
| expr '+' expr {$$ = $1 + $3;}
| expr '-' expr {$$ = $1 - $3;}
| expr '*' expr {$$ = $1 * $3;}
;
primary : INTEGER
| '(' expr ')' {$$ = $2;}
;
這是(部分)轉換的優先級規則爲「正常」的作品,然後調整他們得到你想要的效果。
Alterantely,如果你希望禁止所有連續的運營商,而不僅僅是同一個運營商重複,你可以因此它不能任何其他操作後發生分解出的前綴規則:
%left '+' '-'
%left '*'
%nonassoc UNARY
%token INTEGER
%%
start: expr {printf("Result: %d\n",$1);}
expr : INTEGER
| '-' non_unary_expr %prec UNARY {$$ = -$2;}
| expr '+' non_unary_expr {$$ = $1 + $3;}
| expr '-' non_unary_expr {$$ = $1 - $3;}
| expr '*' non_unary_expr {$$ = $1 * $3;}
| '(' expr ')' {$$ = $2;}
;
non_unary_expr : INTEGER
| non_unary_expr '*' non_unary_expr {$$ = $1 * $3;}
| '(' expr ')' {$$ = $2;}
;
你可能要好奇爲什麼二進制文件+
和-
從最後一條規則中缺失 - 事實證明,它們因優先級而不需要(如果添加它們,會因爲衝突而得到野牛警告,說它們無用)。一般來說,當像這樣重構時,我寧願擺脫所有的優先級定義,而是對優先級使用單獨的規則,因爲這會使得事情更清晰:
%token INTEGER
%%
start : expr { printf("Result: %d\n",$1); }
expr : term
| expr '+' non_unary_term { $$ = $1 + $2; }
| expr '-' non_unary_term { $$ = $1 - $2; }
;
term : factor
| term '*' non_unary_factor { $$ = $1 * $3; }
;
non_unary_term : non_unary_factor
| non_unary_term '*' non_unary_factor { $$ = $1 * $3; }
;
factor : INTEGER
| '-' non_unary_factor { $$ = -$2; }
| '(' expr ')' { $$ = $2; }
;
non_unary_factor : INTEGER
| '(' expr ')' { $$ = $2; }
;
我不確定這是一個嚴重的建議,但是如果'--'是一個由詞法分析器識別但不能被語法識別的令牌,那麼當用戶鍵入'--'時,它們將會出現語法錯誤。我想,你可以用'+ -'來做同樣的事情。我不確定你是否想取締'2/-3'和'2 * -3'。顯然,你會允許'-2 + 1',並且加法是交換的,所以不應該允許使用'1 + -2'。它真的會傷害你和你的語法嗎?沒有小的限制,你的系統會更系統化(更容易預測,使用,記錄)。 –
是的,我想禁止所有連續的操作員。無論如何,除非我讓詞法分析器變得更加複雜,否則,當它們被空格分隔時,您的建議將允許它們:「1-2-2」! – Tsf
我懷疑你的需求變成語義規則,由語法的動作中的代碼強制執行,而不是由語法強制執行。你大概不會把括號作爲操作符。另一個選擇,我想是讓詞法分析器記住它最後返回的內容,並使它引發某種錯誤(返回一個無法識別的標記類型,例如'CONSECUTIVE_OPERATORS',如果它有兩個連續的操作符,但我不相信這是一個很好的限制,放在代碼上,正交性和所有...... –