bision教程

lex与yacc比较

  1. lex 不能用于识别像括号这样的外壳结构。
    识别外壳结构需要使用一个混合堆栈。 当我们遇到“(” 时, 就把它压入栈中。当遇到“)时,我们就在栈的顶部匹配它, 并且弹出栈。 lex 只有状态和状态转换能力。 由于它没有堆栈,它不适合用于剖析外壳结构。
  2. yacc 给 FSA 增加了一个堆栈,并且能够轻易处理像括号这样的结构。

bison语法(分为三部分)

三部分通过%%分割

%{
第一部分yacc代码:定义
%}
%%
第二部分yacc代码:规则
%%
第三部分yacc代码:子程序

第一部分:声明和选项设置(编译后复制到生成的yac.tab.c中)

%{

  1. c变量为要复制到lex.yy.c中的c语言变量,动作中可以使用这些变量
  2. yacc变量编译后生成剖析器复制到yac.tab.c中,同时产生一个y.tab.h文件
  3. 定义非终结符和终结符(记号)

%}

第二部分:文法匹配后进行某种动作(生成yylex()函数到lex.yy.c中)

基础语法

文法表达式1 动作1
文法表达式2 动作2
文法表达式3 动作3

  1. 文法表达式要按照文法表达式语法写,yacc会编译为语法分析代码。文法匹配是基于类别的(基于token),但是每个类别(token)对应一些具体值,出站时对这些具体值计算。
  2. 动作是c语言语句,动作c语言语句直接复制到yacc.yy.c中
  3. 两者合起来即是yyparse()函数,yyparse()函数可以在yacc中使用

文法表达式

bision一条规则有两类符号构成: 非终结符和终结符

非终结符:非终结符 终结符类别tokenK(一个整数值)

  1. 文法在编写时基于token整数值得
  2. 文法左侧的符号是非终结符,左侧不能出现非终结符
  3. 每一条规则被归约的时候,信息在分析树中上移。这就是从根开始,自底向上一步步归约,最后归约到树的顶点,即最初的非终结符。
记号
  1. 文字记号(yacc内置记号)

    bison把单引号引起来的字符看做一个记号。例如:
    expr : ‘(’ expr ‘)’; //左圆括号和右圆括号都是文字记号。
    文字记号的记号编号就是它们本地字符集(通常是ASCII)中对应的整数值。
    但是为了移植性,尽量不要这样用。

  2. 自定义用户记号

    1. 第一步: 定义记号

      1. 定义记号并与该记号的值得数据类型进行关联(推荐)

        这里写图片描述

      2. 直接定义

        %token UP DOWN LEFT RIGHT//只定义记号
        %left UP //定义记号的同时定义记号的左结合性
        %right UP //定义记号的同时定义记号的右结合性
        %nonassoc //定义记号的同时定义记号的无结合性

    2. 第二步: 定义不同记号关联的值的不同数据类型

这里写图片描述
每个在动作代码中需要使用记号的值的记号,必须声明它的类型。如果不声明,默认所有记号的值得数据类型都是int。

    在%union声明中列出所有可能的类型。  
    bison会把他们转化为联合类型的typedef,宏YYSTYPE。  
    通过$$、%1等等来使用记号值时,bison自动使用联合类型中的恰当域。  

3.  第三步: 词法分析器返回记号的值

    1.  第一步: 编译时加-d选项,生成记号头文件。词法分析器引入头文件

        引入 y.tab.h头文件,可以使用yacc代码中定义的字符串符号类别 token和符号值  

这里写图片描述

    2.  第二步: 词法分析器动作中返回记号的值

这里写图片描述

        1.  返回文法中终结符字符串符号类别 token  
            动作中使用c语句return token,yacc在语法分析的时候使用该值。该值在yacc代码中定义。  
            用于匹配文法,对应yacc中分析栈。  
            当然了,该值也可以直接返回匹配到的字符,比如计算器例子中返回文法表达式中的'+'和'-'
        2.  返回文法中终结符对应的符号值  
            lex将值写入yacc内置变量 yylval中,yacc在语法分析的时候使用该值。yylval 的类型由内置变量YYSTYPE决定,在yacc代码中定义,用户可以重写。  
            用于文法匹配成功后,归约出栈时对符号文法元素对应值的求值,用于yacc中内容栈。

4.  第四步: yacc中使用记号的值

    词法分析器返回的记号值保存在变量yylval中。  
    exp: REAL RRELOP REAL if($1 == $2) \[=1;else \];  
    $$表示非终结符exp的值,$1表示记号REAL的值,$2表示记号REAL的值  
  1. 给记号取别名

    %token NE “!=” //这里给定义了记号NE,别名!=,这样在写文法的时候可以直接用更加形象的符号!=。

    文法:
    exp: exp “!=” exp; //用这种方式更加形象
    exp: exp NE exp;两个文法是等价的

文法中的非终结符

文法规则中的非终结符也是有值得:
exp: REAL RRELOP REAL if( 1== 2) [=1;else ];
当文法匹配成功后,归约出栈的时候,内容栈进行运算后非终结符号的数值。
$$表示非终结符exp的值,$1表示记号REAL的值,$2表示记号REAL的值
%type 来声明非终结符
%type

文法编写注意事项
  1. 注意不要产生无限递归

    xlist:xlist X;

  2. 使用左递归,左递归比右递归效率高

文法冲突的解决
  1. 方法一:修改文法表达式

  2. 方法二: 利用bison遇到冲突时的默认行为解决冲突

  3. 方法三: 设置规则的优先级。遇到冲突时,规则的优先级可以和终结符的优先级进行比较

    文法中规则的优先级由该规则中的%prec子句来声明,如果没有%prec子句,该规则的优先级由规则中最右边的记号的优先级决定。

  4. 方法四: 设置终结符的优先级

    在第一部分定义代码中加入如下代码
    %left ‘+’ ‘-’ //这两个符号具有左结合性
    %right ‘*’ ‘/’ //这两个符号具有右结合性
    %nonassoc ‘|’ UMIUS //这两个符号没有结合性
    代码前面的为低优先级,后面为高优先级

  5. 方法五: 设置终结符的结合性

    在第一部分定义代码中加入如下代码
    %left ‘+’ ‘-’ //这两个符号具有左结合性
    %right ‘*’ ‘/’ //这两个符号具有右结合性
    %nonassoc ‘|’ UMIUS //这两个符号没有结合性
    代码前面的为低优先级,后面为高优先级

匹配文法后的动作

语法

{一条或者多条c语言语句}

动作中使用的变量
  1. 第一部分声明中定义的变量
  2. lex默认定义的内部变量

    变量名作用
    yytext指向本次匹配的输入文本
      
      
  3. 引入的yacc中定义的变量

如果不写,默认动作

$$=$1

打印匹配的数据

{ECHO;}
ECHO是一个词
\#define ECHO fwrite(yytext, yyleng, 1, yyout)

对非终结符字符串的值进行操作

expr:
INTEGER { $$ = $1; }

expr ‘+’ expr { $$ = $1 + $3; }
expr ‘­’ expr { $$ = $1 ­ $3; }

;
:左侧非终结符的值$$被设置为相应的f($1,$3)

代码注释符号

%%
文法
TAB字符/*
要注释的代码
TAB字符*/
%%
注意必须输入TAB字符

第三部分:直接复制到生成的y.tab.c中

int main()
{
yylex();

}

lex和bison例子

计算器

Bison 1.25 - Examples

计算器

lex文件

%{
\#include

yacc文件

//要复制到yacc中的c代码
%{
int yylex(void);
void yyerror(char *);
%}

//符号声明
%token INTEGER

%%
program:
program expr ‘\n’ { printf(“%d\n”, $2); }

;
expr:
INTEGER { $$ = $1; }

expr ‘+’ expr { $$ = $1 + $3; }
expr ‘­’ expr { $$ = $1 ­ $3; }

;
%%
void yyerror(char *s) {
fprintf(stderr, “%s\n”, s);
return 0;
}
int main(void) {
yyparse();
return 0;
}

其他例子

lex与yacc入门示例 – 积木村の研究所
一个Lex/Yacc完整的示例(可使用C++) - huyansoft的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/huyansoft/article/details/8860224
Yacc 与 Lex 快速入门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值