YACC文件格式
yacc文件分为三部分:
... definitions ...(%{}%)
%%
... rules ...
%%
... subroutines ...
 
定义部分
第一部分包括标志(token)定义和C代码(用“%{”和“%}”括起来)。
如在定义部分定义标志:
%token INTEGER
当运行yacc后,会产生头文件,里面包含该标志的预定义,如:
#ifndef YYSTYPE 
#define YYSTYPE int 
#endif 
#define INTEGER 258 
extern YYSTYPE yylval;
lex使用该头文件中的标志定义。Yacc调用lex的yylex()来获得标志(token),与标志对应的值由lex放在变量yylval中。yylval的类型由YYSTYPE决定,YYSTYPE缺省类型是int。如:
[0-9]+ { 
yylval = atoi(yytext); 
return INTEGER; 
}
标志0-255被保留作为字符值,一般产生的token标志从258开始。如:
[-+] return *yytext; /* return operator */
返回加号或减号。注意要把减号放在前面,避免被认作是范围符号。
对于操作符,可以定义%left和%right:%left表示左相关(left-associative),%right表示右相关(right-associative)。可以定义多组%left或%right,在后面定义的组有更高的优先级。如:
%left ‘+’ ‘-‘
%left ‘*’ ‘/’
上面定义的乘法和除法比加法和减法有更高的优先级。
改变YYSTYPE的类型。如这样定义TTSTYPE:
%union

     int iValue; /* integer value */ 
     char sIndex; /* symbol table index */ 
     nodeType *nPtr; /* node pointer */ 
};
则生成的头文件中的内容是:
typedef union

     int iValue;      /* integer value */ 
     char sIndex;    /* symbol table index */ 
     nodeType *nPtr; /* node pointer */ 
} YYSTYPE; 
extern YYSTYPE yylval;
可以把标志(token)绑定到YYSTYPE的某个域。如:
%token <iValue> INTEGER 
%type <nPtr> expr
把expr绑定到nPtr,把INTEGER绑定到iValue。yacc处理时会做转换。如:
expr: INTEGER { $$ = con($1); }
转换结果为:
yylval.nPtr = con(yyvsp[0].iValue);
其中yyvsp[0]是值栈(value stack)当前的头部。
 
定义一元减号符有更高的优先级的方法:
%left GE LE EQ NE '>' '<' 
%left '+' '-' 
%left '*' 
%nonassoc UMINUS
%nonassoc的含义是没有结合性。它一般与%prec结合使用表示该操作有同样的优先级。如:
expr: '-' expr %prec UMINUS { $$ = node(UMINUS, 1, $2); }
表示该操作的优先级与UMINUS相同,在上面的定义中,UMINUS的优先级高于其他操作符,所以该操作的优先级也高于其他操作符计算。
 
规则部分
规则部分很象BNF语法。
规则中目标或非终端符放在左边,后跟一个冒号(:),然后是产生式的右边,之后是对应的动作(用{}包含)。如:
%token INTEGER
%%
program: program expr '\n' { printf("%d\n", $2); }

;
expr: INTEGER {

= $1; }  
     | expr '+' expr {

= $1 + $3; } 
     | expr '-' expr { $$ = $1 - $3; } 
;
%%
int yyerror(char *s) 

     fprintf(stderr, "%s\n", s); 
     return 0; 
}

其中,$1表示右边的第一个标记的值,$2表示右边的第二个标记的值,依次类推。$$表示规约后的值。
第三部分
该部分是函数部分。当yacc解析出错时,会调用函数yyerror(),用户可自定义函数的实现。
main函数是调用yacc解析入口函数yyparse()。如:

int main(void) 

     yyparse(); 
     return 0; 
}
递归的处理
递归处理有左递归和右递归。
左递归形式:
list: item 
    | list ',' item;
右递归形式:
list: item 
     | item ',' list
使用右递归时,所有的项都压入堆栈里,才开始规约;而使用左递归的话,同一时刻不会有超过三个项在堆栈里。
 
 
If-Else的冲突
当有两个IF一个ELSE时,该ELSE和哪个IF匹配是一个问题。有两种匹配方法:与第一个匹配和与第二匹配。现代程序语言都让ELSE与最近的IF匹配,这也是yacc的缺省行为。
虽然yacc行为正确,但为避免警告,可以给IF-ELSE语句比IF语句更高的优先级:
%nonassoc IFX 
%nonassoc ELSE
stmt: IF expr stmt %prec IFX 
       | IF expr stmt ELSE stmt
 
 
出错处理
当yacc解析出错时,缺省的行为是调用函数yyerror(),然后从yylex返回一个值。一个更友好的方法是忽略一段错误输入流,继续开始扫描。这里要涉及到YACC中错误保留字error的应用。
Yacc源程序的风格
建议按照如下风格来写:
(1)终端符名全部用大写字母,非终端符全部用小写字母;
(2)把语法规则和语义动作放在不同的行;
(3)把左部相同的规则写在一起,左部只写一次,而后面所有规则都写在竖线“|”之后;
(4)把分号“;”放在规则最后,独占一行;
(5)用制表符来对齐规则和动作。
语法分析中的错误处理
当进行语法分析时发现输入串有语法错误,最好能在报告出错信息以后继续进行语法分析,以便发现更多的错误。
Yacc处理错误的方法是:当发现语法错误时,yacc丢掉那些导致错误的符号适当调整状态栈。然后从出错处的后一个符号处或跳过若干符号直到遇到用户指定的某个符号时开始继续分析。Yacc内部有一个保留的终结符error,把它写在某个产生式的右部,则Yacc就认为这个地方可能发生错误,当语法分析的确在这里发生错误时,Yacc就用上面介绍的方法处理,如果没有用到 error的产生式,则 Yacc打印出“Syntax error”,就终止语法分析。
下面看两个使用error的简单例子:
1.下面的产生式
stat: error
;
使yacc在分析stat推导出的句型时,遇到语法错误时跳过出错的部分,继续分析(也会打印语法错误信息)

2.下面的产生式
stat: error ';'
;
使yacc碰到语法错时,跳过输入串直到碰到下一个分号才继续开始语法分析。
 
 
嵌入式动作
对于语法分析程序中的每一个语法规则,都有相应的C/C++语句来做一些额外的处理,这个额外的处理就是语法动作。不过语法动作和词法动作的不同之处在于,语法动作允许嵌入式的语法动作,而词法动作不行。
尽管yacc的语法分析技术只允许动作在规则的末端,但yacc可以自动模拟嵌入在规则内部的动作。如果在规则内部写入一个动作,yacc就会创造一个右侧为空并且左边是自动生成的名字规则,使得嵌入的动作进高规则的动作里去,用自动成成的名字代替最初的规则内的动作。
例如: 下面的句子是等价的
thing : A {printf("I am A") ;} B
thing : A fakename B;
fakename : {printf("I am A");}
这种方式将A植作为$1,  规则末端的动作可将嵌入式动作的值作为$2,B的值为$3.
 Example:
//L文件:
%{
#include "FIRST_TA.H"
#include <stdio.h>
#include <stdlib.h>
%}
%%
a {return A_STATE;}
b {return B_STATE;}
c {return C_STATE;}
not {return NOT;}
%% //Y文件:
%{
#include <stdio.h>
#include <stdlib.h>
%}
%token A_STATE B_STATE C_STATE NOT
%% program :
A_STATE B_STATE {
int c, d;
c = ;
d = ;
}
c_state_not {
int e,f;
e = ;
f = ;
}
|
A_STATE B_STATE {
int a, b;
a = ;
b = ;
}
c_state_not : C_STATE NOT{}
%% 输入文件的字符:a, b, c, f, c, not

YACC基本用法的更多相关文章

  1. 【译】Python Lex Yacc手册

    本文是PLY (Python Lex-Yacc)的中文翻译版.转载请注明出处.这里有更好的阅读体验. 如果你从事编译器或解析器的开发工作,你可能对lex和yacc不会陌生,PLY是David Beaz ...

  2. qmake理解(还可以加入Lex Yacc文件)

    关于qmake,好一段时间令我一头雾水,不知道用来干嘛的,只知道怎么用,而且也只懂那么一两个命令,详细看过资料以后整理如下: 1.首先,感性的认识是,qmake可以利用源文件(包括头文件h,实现文件c ...

  3. Lex Yacc手册

    Python Lex Yacc手册 本文是PLY (Python Lex-Yacc)的中文翻译版.转载请注明出处.这里有更好的阅读体验. 如果你从事编译器或解析器的开发工作,你可能对lex和yacc不 ...

  4. 【转】BNF和EBNF的含义与用法

    [转]BNF和EBNF的含义与用法   BNF 和EBNF的含义与用法 1简介       关于本文       什么是BNF?工作原理       基本原理       一个实例 EBNF及其用途  ...

  5. VIM+ctags+cscope用法

    使用vim + cscope/ctags,就能够实现Source Insight的功能,可以很方便地查看分析源代码.   关键词: vim, cscope, ctags, tags   1. 查看vi ...

  6. EditText 基本用法

    title: EditText 基本用法 tags: EditText,编辑框,输入框 --- EditText介绍: EditText 在开发中也是经常用到的控件,也是一个比较必要的组件,可以说它是 ...

  7. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  8. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  9. [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法

    一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...

随机推荐

  1. directive和controller如何通信

    1.AngularJS是何方神圣 Angular JS (Angular.JS) 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持整个开发进程,提供web应用的架构,无需进行手工 ...

  2. windows 数据类型转换为 dotnet 数据类型

    Windows Data Type .NET Data Type BOOL, BOOLEAN Boolean or Int32 BSTR String BYTE Byte CHAR Char DOUB ...

  3. Arcgis Engine 添加一个Symbol符号样式步骤

    public static void DrawPictureMarkerSymbol(IGlobe globe, String layerName) { //添加一个图层 ESRI.ArcGIS.Ca ...

  4. 自动化高效css开发,畅谈less的灵活变化

    css是一种让html与样式分离解析而出现的代码,它的出现大大提高了程序员的工作效率,和后期进行维护的效率.但是发展至今,由于起死板单调的写法,越来越不能满足程序员们灵活的思维,很多时候是种恨铁不成钢 ...

  5. 初学swift笔记 结构体(八)

    import Foundation /* 和类很相似 结构体 -> 封装 属性.方法 结构体是值类型 */ //定义一个LSQ类型的结构体 关键字struct struct LSQ { var ...

  6. 水池(DFS)

    水池数目 点我 描述 南阳理工学院校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地图上仅标识了此处是否是水池,现在,你的任务来了,请用计算机算出该地图中 ...

  7. acdream 1154 Lowbit Sum

    先贴代码,以后再写题解... 首先,直接枚举肯定是会超时的,毕竟n就有10^9那么多... 对于每个数,我们先把它转化为二进制:例:21-->10101: 对于00001~10101,可以分为几 ...

  8. JS 禁止浏览器右键菜单和刷新

    <script language="javascript"> //禁止按键F5 document.onkeydown = function(e){ e = window ...

  9. MVC 视频笔记

    1.关闭Jquery的浏览器缓存 $.ajaxSetup({cache:fasle});

  10. 用 for/in 在 Java 5.0 中增强循环

    这个方便的构造提供了什么?什么时候适用于代码? Brett McLaughlin (brett@newInstance.com), 作者/编辑, O'Reilly Media, Inc. 简介: fo ...