前言
之前的文章对于词法分析和语法分析规则进行了讲解,我们的目标 是解析代码并生成语法树,因此必须在识别出语句或表达式时添加生成语法树的代码.
为了达到上述目的,可以借助 action
功能,当 token
序列和语法规则匹配时就能够执行任意代码.
本文将对 action
的使用方法进行说明
action
可以通过在符号串内部添加{ }
代码块,执行我们想要添加的一些特点操作,这些代码块被称为action,举例
void defstruct(): {}
{
<STRUCT> name()
{
System.out.println(" 发现了结构体!"); }
member_list()
{
System.out.println(" 发现了成员列表!"); }
";"
{
System.out.println(" 发现了分号!"); }
}
非终端符号的规则定义
action的执行时机
终端符号被执行时,才会触发action,扫描终端符号不会触发action
需要注意“解析终端符号”和“扫描终端符号”是不同的.解析器会对 token
进行超前扫描,因此即便是写在 action
之后的 token
,也完全有可能已经被扫描器读取进来了
例如下面的规则,在 action 执行时 <X> 和 <Y> 就已经被扫描进来了
LOOKAHEAD(2) { System.out.println("action executed"); } <X> <Y>
但解析器一定会在执行 action 之后再对 <X> 和 <Y> 进行解析
token类的属性
token类的属性
只需写“变量名 = 非终端符号”,就能获取终端符号和非终端符号的语义值
语义值
我们可以设置返回语义值的action
String name():
{
Token tok;
}
{
tok=<IDENTIFIER> { return tok.image; }
}
action的执行次数
选择和action
: 如果在所有选项的最后执行共通的action
{
Token x, y;
}
{
(x=<X> | y=<Y>)
{
if (x != null) {
System.out.println("X found. image=" + x.image);
}
else {
System.out.println("Y found. image=" + y.image);
}
}
}
重复和action
每当发现 <X> 和 <Y>时都会执行
void iteration():
{
Token x, y;
}
{
(x=<X> y=<Y>
{
System.out.println("x=" + x.image + "; y=" + y.image);
} )*
}
只会执行一次
void iteration2():
{
Token x, y;
}
{
(x=<X> y=<Y>)*
{
System.out.println("x=" + x.image + "; y=" + y.image);
}
}
总结
上面介绍了action
主要功能和获取语义值,本文的总结如下
- 使用 action 能够获取终端符号 / 非终端符号的语义值,还能够给非终端符号赋予语义值
- 终端符号的语义值为 Token 类的实例。从 Token 类的属性中可以取得 token 的字面量及其在源文件中的位置等信息
- 非终端符号的语义值取决于 action。通过在规则的开头添加语义值的类型,并从 action 返回值,就可以设置语义值
- 当解析到规则中写有 action 之处时,action 才会被执行。若在符号串的最后写有 action,那么在该符号串全部被发现后 action 才会被执行
- 组合使用选项和 action,能够编写只有在发现特定的选项时才被执行的 action
- 组合使用重复和 action,能够编写在每次重复时都会被执行的 action