关于Bison的使用细节可以查看https://www.gnu.org/software/bison/manual/bison.html,是Bison的英文手册。
如果你有哪个部分不清晰,可以点击上面的Bison链接,通过”Ctrl+F“搜索你不清楚的关键字来查询文档。(不过文档很生涩难读,读不懂的部分可以粘贴给语言大模型翻译。
目录

11.“%precedence”"%left""right""%nonassoc"
一、什么是Bison
1.基本介绍
Bison 是一个广泛使用的语法分析工具,它是 GNU 项目的一部分,旨在为编程语言的解析和编译提供支持。Bison 允许用户定义语言的语法,并生成一个可用于解析该语言的解析器。
Bison 是 Yacc 的 GNU 实现,功能和语法上相似,但 Bison 提供了更多的特性,比如更好的错误处理和可重入解析器。
(扩展)GNU(GNU's Not Unix)是一个自由软件项目,旨在创建一个完全自由的Unix-like操作系统。
Bison还涉及到两个概念:
- 上下文无关文法(CFG):Bison 使用上下文无关文法来描述编程语言的语法。用户可以定义语法规则和语义动作。
- 产生式(Production Rules): 语法规则被定义为产生式,由一个非终结符和它可以替代的终结符或其他非终结符构成。
2.基本组成部分
1)声明部分:
- 包含 `%token`、`%type`、`%start` 等指令,用于声明终结符、非终结符和起始符号。
- 可以包含 C 语言的头文件。
2)语法规则部分:
- 定义语法规则,描述如何从终结符和非终结符生成语言构造。
- 语法规则通常还包括语义动作,即在解析时执行的代码。
3. 辅助程序代码部:
- 包含额外的 C 代码,可用于定义辅助函数或数据结构。
下面bison代码可以很好地展现代码组成部分,每个部分通过“%%”分隔开。
%{
#include <stdio.h>
#include <stdlib.h>
%}
%token NUMBER
%%
expression:
NUMBER
| expression '+' NUMBER { printf("%d\n", $1 + $3); }
| expression '-' NUMBER { printf("%d\n", $1 - $3); }
;
%%
int main(void) {
return yyparse();
}
void yyerror(const char *s) {
fprintf(stderr, "Error: %s\n", s);
}
二、Bison的基本符号
1.“:”
功能:`:` 符号用于分隔规则的左侧(产生式的名称)和右侧(产生式的定义)部分。
示例:
//产生式:statement->expression
statement : expression
{
// 语义动作
};
2.“|”
功能:`|` 符号用于定义一个语法规则的多个选择(备选项)。
示例:
//相当于产生式中的“|”符号
//expression->term| expression+term |expression-term
expression : term
| expression '+' term
| expression '-' term;
3.";"
功能:`;` 符号用于结束一个语法规则的定义。每个产生式的定义都必须以 `;` 结束。
示例:
expression : term '+' term
| term '-' term;
4.“{}”
功能:`{}` 符号用于包围语义动作的代码块,这些动作在解析完规则后执行,通常用于构造抽象语法树(AST)或其他数据结构。
`{}`内的内容可以是C++代码,比如”$$=new int(3)“.与一般C++程序不同点就在于,增加了"$$""$n"等符号来指代产生式的左部符号、右部的第n个符号。
示例:
//在解析完expression->term后,会执行语义动作“ printf("1")”,打印出1来
expression : term
{
printf("1"); // 语义动作
};
5.“%%”
功能:`%%` 符号用于分隔语法规则部分和其他部分(如声明部分或 C 代码部分)。
示例:
%{
// C 代码
%}
%%
// 语法规则定义
```
6.“%token”"%type""%start"
功能:“%token”"%type""%start"分别表示终结符、非终结符、开始符号
示例:
%token NUM // 声明一个 token,表示数字
%token PLUS // 声明一个 token,表示加号
%token TIMES // 声明一个 token,表示乘号
%token LPAREN // 声明一个 token,表示左括号
%token RPAREN // 声明一个 token,表示右括号
%type <int> expr // 声明一个非终结符expr,类型为 int
%type <int> term
%type <int> factor
%start expr // 声明起始符号为 expr
7."<>"
功能:`<>`符号括起来的部分通常用于表示类型或模板参数。可以是基本数据类型(比如int,float),也可以是自定义的数据结构(比如<program><block>)
示例:
%union{
Symbol symbol_token;
Program program;
}
%token <symbol_token> STR_CONST IDENT
%type <program> Program
%type <int> expr term
8." ' ' "
功能:单引号用于表示终结符(token),即解析器在输入字符串中需要匹配的字符或标记。例如,`'{'` 和 `'}'` 表示匹配大括号。
示例:
expression : term
| expression '+' term
| expression '-' term;
9."$$"和"$n"
功能:"$$"符号表示当前规则的值。通常用于在规则的动作部分生成计算结果或其他值。
"$n"符号表示当前规则中第 `n` 个符号的值。第一个符号用 `$1` 表示,第二个用 `$2` 表示,以此类推。
示例:
%{
#include <stdio.h>
#include <stdlib.h>
%}
%token NUM
%token PLUS MINUS
%type <int> expr term
%%
// 语法规则
expr:
expr PLUS term { $$ = $1 + $3; } // 表达式加法,expr的结果为expr+term
| expr MINUS term { $$ = $1 - $3; } // 表达式减法
| term { $$ = $1; } // 基础表达式
;
term:
NUM { $$ = $1; } // 数字,term的结果为NUM
;
%%
10."@$""@n"
功能:`@$` 和 `@n` 用于访问在 Bison 中对应的符号或规则组件的位置信息。而”$$“"$n"都是用来处理语义值的,而不是位置信息。
示例:
expr
:term
{
@$=@1;//将 `term` 的位置信息作为 `expr` 的位置信息。这种处理主要用于在需要位置跟踪的上下文中,例如错误信息的输出或是警告信息的提示。
}
11.“%precedence”"%left""right""%nonassoc"
功能:`"%left"表示左结合,"%right"表示右结合,"%nonassoc"表示非结合。“%precedence”用于定义优先级但没有结合性的运算符(在 Bison 中,有些运算符不需要结合性,比如一元运算符(如负号 `-`),“%precedence”只是简单地告诉解析器运算符的优先级。)
什么是左结合、右结合、非结合?
那算式"a+b+c"而言,左结合是"(a+b)+c",右结合是"a+(b+c)",非结合是不允许"a+b+c"这样加号连续出现的式子。
也就是对于"a+b+c"中的"b",左边有个”+“,右边也有个"+".左结合就是b先与左边的符号结合,右结合就是b先与右边的符号结合。而非结合时,b左边右边都存在非结合的符号就是非法的。
示例1(ps:同样结合性的,后面行声明的优先级更高):
//a+b-c*d/e=(a+b)-(c*d/e)
//对于c,碰见‘-’‘*’时,‘*’的优先级高,优先与*结合;b碰见”+“和"-",优先级相同,是左结合,先与左边的"+"结合
%left '+' '-' // 左结合,优先级低于乘法和除法
%left '*' '/' // 左结合,优先级高于加法和减法
示例2:
//a^b^c=a^(b^c)
//-a^c=(-a)^c
%right '^' // 右结合,用于幂运算
%left '+' '-'
%left '*' '/'
%precedence UMINUS // 单目负号优先级高于乘除
示例3:
//a>b合法,a>b>c不合法(不能连续使用)
%nonassoc '<' '>' // 非结合,用于比较运算符