分类: C/C++
2008-06-04 13:22:51
名称
yacc -根据指定的语法规则产生 LR 分析程序的程序。
用法
yacc [-vltds] [-b prefix] [-N number] [-p symbol_prefix] [-P pathname]
grammar
标准
本文中的接口遵循下列工业标准:
yacc: XPG4, XPG4-UNIX
选项
-b prefix
定义所有输出文件名的前缀(prefix.tab.c, prefix.tab.h, 和 prefix.output)。
-d
产生 y.tab.h 文件,它包含yacc赋予的标记值的预定义。这可以使其它的源文件
通过包含该头文件来访问代码。
-l
在 y.tab.c 文件中不包含#line语句。仅在语法和相关动作被彻底调试后使用。
-N number
[Compaq] 提供给yacc额外的存储以创建它的先行LR(1)文法分析程序分析表(LALR),
这在编译非常大的语法时很重要。使用此选项时 number 值应大于40000。
-p symbol_prefix
允许多个yacc分析程序连接在一起。使用symbol_prefix前缀来代替 yy 表示全局符号。
-P pathname
指定可选的分析程序(替代/usr/ccs/lib/yaccpar)。
-s
[Compaq] 将yyparse()函数分解为几个小的函数,因为它的尺寸有些与语法成比例,这可能导致
yyparse()函数太大而不能被编译、优化或有效地执行。
-t
编译运行时调试代码。缺省情况下,当y.tab.c 被编译时不包含该代码。如果YYDEBUG具有非零值,
C编译器将会包含调试代码,而不管 –t 选项是否使用。不包含调试代码时,函数yyparse()将执行
的更快。
-v
生成y.output文件,它包含有关分析表的可读的描述以及由语法含糊导致的冲突的报导。
操作数
grammar
包含输入指令的文件。
描述
yacc的语法可以是不明确的;指定的优先规则用于打破这种不明确性。
你必须用C语言编译器编译输出文件y.tab.c 以产生yyparse() 函数。该函数必须与yylex
词法分析函数一起被调用,同样的还有main() 和 yyerror(),后者为错误处理函数(你必须
提供这些函数。当创建yacc可用的词法分析器时命令lex很有用。
yacc程序从文件/usr/ccs/lib/yaccpar中读取分析框架。使用环境变量 YACCPAR 可以指定其它
的分析框架。如果使用该环境变量,-P 选项将被忽略。
输入文件的语法
此段将对yacc输入文件(一般具有 .y 后缀)进行描述,同时提供一系列yacc所认识的
特殊值,宏和函数。
yacc输入文件的一般格式:
[ definitions ]
%%
[ rules ]
[ %%
[ user functions ]]
其中
definitions
此区域定义在以后的语法(如规则区域)中使用的变量,以及包含文件和处理条件。
此区域是可选的。
rules
定义语法分析的规则。此项必选。
user functions
定义可被规则区域使用的用户自定义函数。此项为可选项。
空字符(NULL)不能被用于语法规则,也不能在字面上使用。定义区域中的每一行可为:
%{
%}
所包含的 C 代码将输出为输出文件中的全局定义。此域通常包含预处理指令及外部
变量和函数的声明。
%token [type] token [number] [name [number]...
列出输入文件其它部分使用的标记和结束符号。对那些没在其它 % 中定义的标记此行
是必须的。如果type已经存在,此行定义的所有标记的 C 类型被声明为由type参考
的类型。如果标记后跟一个实际的整数值,该值将赋给标记。
%left [
指出每一个标记都是操作符,且此定义中的所有标记具有相同的优先级,求值顺序为
从左到右。
%right [
指出每一个标记都是操作符,且此定义中的所有标记具有相同的优先级,求值顺序为
从右到左。
%nonassoc [
指出每一个标记都是操作符,且此定义中的标记不能连在一起使用,也就是这些操作符
不能结合使用。
%start symbol
指定最高级别的归约规则,也就是使分析器认为归约已经完成的规约。如果没有这项定义,
分析器将使用第一条规则。symbol必须为非结束符(不是一个标记)。
%type < type > symbol [ symbol ... ]
定义每个 symbol 为 type 类型,以消除歧义。如果此项存在,yacc将执行类型检查,否则
假定其后所有的符号为整数类型。
%union union-def
定义yyval全局变量为联合体,union-def 为以下标准C定义格式:
{ type member ; [type member ; ... ] }
至少有一个成员为int型。任何合法的C数据类型可被定义,包括结构。当运行带有-d
选项的yacc命令时,关于yyval的定义将被放在 y.tab.h 文件中,并能被lex输入文
件所引用。
每个标记(非结束符)必须处于一个带有前导%的定义中。多个标记可被空格或逗号分割。
所有在%left,%right, 和 %nonassoc中定义中的标记都被赋予一个优先级,后定义的标记
比先定义的标记具有更高的优先级。
除了作为符号,标记可作为字面字符,此时需要单引号(多字节字符可被词法分析器识别并
作为标记返回)。下列特殊字符可以使用,如同在C程序中一样:
\a Alert
\n Newline
\t Tab
\v Vertical tab
\r Carriage Return
\b Backspace
\f Form Feed
\\ Backslash
\' Single Quote
\? Question mark
\n One or more octal digits specifying the integer value of the character
规则区域由一系列待分析器解释的规则组成,每条规则的格式如下:
symbol : symbol-sequence [ action ] [ | symbol-sequence [ action ] ... ] ;
其中每个 symbol-sequence 由零个或多个由空格分割的符号组成。第一个符号必须为该行的首位字符,
但换行和空白符可出现在规则中任何其它的地方。所有的结束符号必须在定义%token中声明。
Each symbol-sequence represents an alternative way of reducing the rule. A
symbol can appear recursively in its own rule. Always use left-recursion
(where the recursive symbol appears before the terminating case in symbol-
sequence).
每个符号序列表示一种可选的归约方法。一个符号可在自己的规则中递归出现。此时总是使用左递归(递归
符号出现在导致结束的符号列表前)。
特殊的序列:
%prec token
表示该符号序列在定义区域中赋给标记的优先级别上具有最高的优先级。
特殊指定的标记 error 匹配任何不能识别的输入序列。这个标记促使分析器调用yyerror函数。
缺省时,分析器试图与输入同步,它读入并抛弃所有的输入直至error后的符号,然后继续执行。
(你可以通过 yyerrok 函数重载此项行为)。如果yacc输入文件中没有error标记,分析器将
退出,并返回不可识别的输入的错误消息。
当遇到动作前的符号时,分析器将执行该动作。因此,动作可出现在符号列表的中间,每个
符号列表之后,或者多个符号列表之后。在最后一种情况下,动作在分析器能匹配其中任何
一个符号列表时执行。
动作由大括号内的标准C代码组成,并且可采用下列数值,变量,和关键字。
yylval
如果由函数返回的标记关联一个重要的数值,yylex应该将该值放在这个全局变量中。
缺省情况下,yyval为 long 型。定义区域可使用 %union 定义来使用其它的数据类型,
包括结构。如果使用 -d 选项运行yacc,全部的yylval定义被写入可被lex访问的
文件
yyerrok
导致分析器立即在不明确的序列后开始分析标记,而不是执行缺省地同步动作。
yyerrok动作应该立即出现在error标记后。
$ [
参考符号n(从规则开始计算的标记索引),$1表示冒号后的第一个符号。type 是在
指令 %union 中定义的联合中某个成员的名字。type 语法允许数值被转化为特殊的
数据类型。但注意应尽可能的少用 type语法。
$ [
参考由匹配的符号序列所返回的值,它在解释其它的规则中出现的该匹配符号时使用。
一般地,符号序列赋值于 $$。type 是在指令 %union 中定义的联合中某个成员的名字。
用户函数区域包含用户提供的程序。如果你提供一个词法分析器给语法分析器,它必须包
含在用户函数区域内。
下列包含在用户函数区域中的函数被由yacc所产生的yyparse函数调用。
yylex()
由函数yyparse 调用的词法分析器,它用来识别输入信息中的标记。此函数通常由lex
创建,它识别输入中的表达式,并返回一个标记类型值。此函数返回值为整型。返回值为零
表示输入结束。
如果语法分析器与函数yylex不能就标记值达成一致,它们也无法进行协商。对于字面上的
单字符标记,它为当前所处字符集中字符的值。其它类型标记的值由 yacc 或用户决定。
在任何一种情况下,都可用C语言中的预定义语句使函数yylex()返回表示这些值的符号。
预定义语句放在代码文件中,如果需要也可放在头文件中。yacc所允许的字符集比C语言中
的字符集要大。包含这些超出C语言字符集的字符的标记名将不能放在预定义语句中。
如果由yacc选择标记的值,除字面上的单字符标记外,其余的标记将被赋予大于256的值,
且没有任何隐含的顺序。一个标记可在定义区域中首次出现时通过后跟一个数值来显式的赋值。
所有已赋值的标记应是唯一的,且与字面上的单字符标记的值不同。在生成分析器时,重复的
标记值将促使yacc报告错误,而其它情况下则可能接受该标记,或是报告错误。
输入的结尾由称为结束符的特殊标记标识,该标识值为零或负数。所有的词法分析器在输入结束时
返回零或负数作为标记的值。
yyerror(string)
当发生输入错误时分析器所调用的函数。缺省的函数定义在库liby.a中,它仅仅打印
字符串到标准错误,用户可重定义此函数。函数的返回值为空。
库liby.a中包含缺省的main() 和 yyerror()函数,它们大致如下:
main()
{
setlocale(LC_ALL, );
(void) yyparse();
return(0);
}
int yyerror(s);
char *s;
{
fprintf(stderr,"%s\n",s);
return (0);
}
注释可以可以出现在用户函数或定义区域中的任何地方,在规则区域,注释可出现在符号
所允许的地方。空行和仅由空格组成的行可出现在任何位置,它们被忽略。
注意
变量LANG 和 LC_*影响yacc命令的执行,由yacc定义的 main() 函数调用:
setlocale(LC_ALL, "")
因此,由yacc命令产生的程序在运行时将受到这些变量的影响。
返回状态
返回值:
0 成功
>0 错误
示例
此段描述lex和yacc的一个示例程序,它们创建一个简单的桌面计算器,该计算器能执行
加、减、乘、除运算,并能允许赋值给变量(由单个小写 ASCII 字符表示),且变量能够
参与运算。程序包含以下的文件:
calc.l
定义词法分析规则的lex文件。
calc.y
yacc语法文件。它定义语法规则并调用由lex创建的yylex()函数提供输入。
以下的描述期望当前目录包含lex 和 yacc示例文件。
编译示例程序
使用lex和yacc执行以下的步骤来创建示例程序:
1. 选项 -d 让 yacc创建一个定义它所使用的标记的单独的文件。
yacc -d calc.y
下列文件被创建:
y.tab.c
yacc创建的供语法分析器使用的C语言源程序。
分析器使用的包含标记的 #define 语句的头文件。
2. 处理lex文件:
lex calc.l
下列文件被创建:
lex.yy.c
由lex为词法分析器创建的C语言源文件。
3. 编译和链接两个C语言源文件:
cc -o calc y.tab.c lex.yy.c
下列文件被创建:
y.tab.o
lex.yy.o
calc 可执行文件
现在可以运行程序:
calc
然后键入数字和操作符,然后键入回车键,程序将显示计算结果。如果你赋值给一个
变量,光标将移到下一行:
m=4
_
然后你可用该变量来进行计算:
m+5
9
源代码
calc.y文件:
%{
#include
int regs[26];
int base;
%}
%start list
%token DIGIT LETTER
%left '|'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%left UMINUS /*supplies precedence for unary minus */
%% /*beginning of rules section */
list : /*empty */
| list stat '\n'
| list error '\n'
{ yyerrok; }
;
stat : expr
{ printf("%d\n",$1); }
| LETTER '=' expr
{ regs[$1] = $3; }
;
expr : '(' expr ')'
{ $$ = $2; }
| expr '*' expr
{ $$ = $1 * $3; }
| expr '/' expr
{ $$ = $1 / $3; }
| expr '%' expr
{ $$ = $1 % $3; }
| expr '+' expr
{ $$ = $1 + $3; }
| expr '-' expr
{ $$ = $1 - $3; }
| expr '&' expr
{ $$ = $1 & $3; }
| expr '|' expr
{ $$ = $1 | $3; }
| '-' expr %prec UMINUS
{ $$ = -$2; }
| LETTER
{ $$ = regs[$1]; }
| number
;
number : DIGIT
{ $$ = $1; base = ($1==0) ? 8:10; }
| number DIGIT
{ $$ = base * $1 + $2; }
;
%%
main()
{
return(yyparse());
}
yyerror(s)
char *s;
{
fprintf(stderr,"%s\n",s);
}
yywrap()
{
return(1);
}
定义区域
定义区域包含以下内容:
+ 包含标准 I/O 头文件
+ 定义全局变量
+ 定义开始处理的规则列表。
+ 定义分析器使用的标记
+ 定义操作符及其优先级
规则区域
规则区域定义分析输入流的规则。
程序区域
程序区域包含下列函数。因为这些函数包含在此文件中,因此不需要yacc库。
main()
必须的主程序,它调用yyparse()函数,开始程序的执行。
yyerror(s)
错误处理函数,它仅打印错误消息。
yywrap()
清除函数,在输入结束时调用,它返回值1。
词法分析源程序
calc.l文件:
%{
#include
#include "y.tab.h"
int c;
#if !defined (YYSTYPE)
#define YYSTYPE long
#endif
extern YYSTYPE yylval;
%}
%%
" " ;
[a-z] {
c = yytext[0];
yylval = c - 'a';
return(LETTER);
}
[0-9] {
c = yytext[0];
yylval = c - '0';
return(DIGIT);
}
[^a-z 0-9] {
c = yytext[0];
return(c);
}
环境变量
下列的环境变量影响yacc的执行:
LANG
提供未设置或空的国际化变量的缺省值。如果任何国际化变量包含非法的设置,则
该变量就如同没有被定义。
LC_ALL
如果设置为非空的字符串值,将覆盖所有其它的国际化变量。
LC_CTYPE
定义如何解释字符文本数据中字节的序列(例如,在参数和输入文件中,单字节字符
与多字节字符不同)
LC_MESSAGES
定义写入到标准错误中的诊断消息的内容和格式表示方法。
NLSPATH
定义处理LC_MESSAGES变量的消息目录的位置。
文件
y.output
分析表和关于由语法含糊产生的冲突报导的描述信息。
y.tab.c
输出文件
定义标记名
yacc.tmp
临时文件
yacc.debug
临时文件
yacc.acts
临时文件
/usr/ccs/lib/yaccpar
缺省的C程序分析框架。
/usr/ccs/lib/liby.a
yacc 库.