zoukankan      html  css  js  c++  java
  • 递归下降文法

    什么是递归下降分析法

    递归下降(Recursive Descent)分析法是确定的自上而下分析法,这种分析法要求文法是LL(1)文法。

    • 为每个非终结符编制一个递归下降分析procedure,每个函数名是相应的非终结符,函数体则是根据规则右部符号串的结构和顺序编写。
    • procedure相互递归调用。

    递归下降文法的详细说明见龙书或者http://www.cs.binghamton.edu/~zdu/parsdemo/recintro.html

    详细描述了递归下降和如何消除左递归。

    对于递归下降文法的使用,一般软件有Yacc和Parse::RecDescent 等。

    1. Parse::RecDescent

    Parse::RecDescent是一个轻量级的解析器,不仅轻量(只需要一个.pm文件),而且非常容易可视化和方便BNF的查看,同时在调试时,可以很明显的看到在给定输出下如何解析每个规则。文档可见:https://metacpan.org/pod/Parse::RecDescent

     

    1. Yacc

    Yacc(Yet Another Compiler Compiler)是一个经典的生成词法分析器的工具。Yacc生成用C语言写成的词法解释器,需要与词法解析器Lex一起使用,然后把两部分分别生成的C程序一起编译。

    从逻辑上是Lex->Yacc->BackendCompiler-linker的顺序。

    在目前使用的linux系统上,Lex/Yacc实际上是使用的Flex&Bison工具,至于为什么Linux上的是Flex&Bison,历史问题可以自行探究,实际上Lex的作者包括Google的前CEO。对应关系是flex-lex,可以认为flex=Fast Lex,Bison=Yacc++。

    网络上有很多Lex/Yacc的教程,说是需要使用Lex/Yacc来进行文本处理和程序的解析,很多的说法都来自于《flex&Bison》一书。以个人的经验来看,这本书的内容实在太过陈旧,目前简单的文本处理使用Python即可,复杂到需要一些编译的东西,首先Python自己本身就是个解析器,使用Python开发的各个语言的解析器也不少,使用很方便,如果需要对语言的特性有非常好的支持,使用Python调用clang/llvm的东西也非常方便,在今天,flex&Bison的主要用处是一个轻量级的编译器入门教程,相比Clang/llvm的庞大代码量和复杂的结构和API问题,flex&Bison作为教学使用还是不错的。

    Flex&Bison安装问题

    虽然有人说现在Linux相关公开发行版本中,默认安装Flex&Bison,但是经个人测试AWS提供的Ubuntu16.04系统中并没有提供相关的环境。需要使用来进行安装。

    sudo apt-get install flex

    sudo apt-get install bison

    我本人在说Lex/Yacc或者Flex&Bison喜欢将其放在一起进行,实际上,他们都是独立的工具,可以单独使用。

    Flex&Bison与正则表达式(Regular Expression

    对于Flex&Bison的用户来说,必须要掌握词法和语法的规则,这个规则是由正则表达式和特定规则构造的。正则表达式是使用一些特殊字符描述的字符串,用来表示一种匹配的模式(pattern)。下面给出一些简单的元字符说明。具体来源不列,source中除了这部分以外,给的说法存在太多错误。

    元字符

    匹配内容

    .

    除了换行符之外的任意字符

    换行符

    *

    0次或者多次匹配

    +

    1次或者多次匹配

    ?

    0次或者1次匹配

    ^

    行首

    $

    行尾

    a|b

    a或者b

    (ab)+

    ab的一次或者多次匹配

    “a+b”

    a+b(字面意思)

    []

    分组

    Flex实现的wc使用说明

    下面将首先介绍使用仅Flex来实现linux中的wc功能。(代码来源于《Flex&Bison》)

    编写这样的一个文件,并将其命名为wc.l。

    /*   wc.l   */
    %{
    int chars = 0;
    int words = 0;
    int lines = 0;
    %}
    
    %%
    
    [a-zA-Z]+ {chars+=strlen(yytext); words+=1;}
    
            {lines+=1; chars+=1;}
    .         {chars+=1;}
    
    %%
    
    main(int argc, char** argv)
    {
        yylex();
        printf("%8d %8d %8d
    ", chars, words, lines);
    }

    flex wc.l     //---generate lex.yy.c然后使用flex进行编译处理

    gcc lex.yy.c -lfl -o a.out

    首先说明编译和使用过程,再对flex&Bison的语法进行说明。

    wc.l是lex(flex)的程序文件,如果用到了yacc(Bison)的话,一般以.y结尾。

    lex程序文件经flex编译后,会生成lex.yy.c;yacc程序文件经Bison后,生成了yacc.tab.c(可能还会有yacc.tab.h文件),经过分别编译后,最后可以统一链接称为最终的目标文件。

    因此一个典型的flex&Bison project的Makefile可以写成这样的:

    LEX=flex
    YACC=bison
    CC=gcc
    OBJECT=main.out   #object file
    
    $(OBJECT): lex.yy.o yacc.tab.o
        $(CC) lex.yy.o yacc.tab.o -o $(OBJECT)
    
    lex.yy.o: lex.yy.c yacc.tab.h
        $(CC) -c lex.yy.c
    
    yacc.tab.o: yacc.tab.c
        $(CC) -c yacc.tab.c
    
    yacc.tab.c yacc.tab.h: yacc.y
        $(YACC) -d yacc.y
    
    lex.yy.c: wc.l
        $(LEX) wc.l
    
    clean:
        @rm -f $(OBJECT) *.o
    View Code


     如果实在看不懂Makefile的,请参照《跟我一起写Makefile》https://seisman.github.io/how-to-write-makefile/

    Lex&Yacc文件结构说明

    在Yacc中,这个说明文件可以分为三部分,以符号%%分开:

     [第一部分:声明定义段]

     %%

     第二部分:规则段

     %%

     第三部分:辅助函数段

       其中,第一部分及第三部分和第三部分之上的%%都可以省略(即上述方括号括起的部分可以省略)。以%开头的符号和关键字,或者是规则段的各个规则一般顶着行首来写,前面没有空格。

    一般来说,声明定义段用%{和}%来进行包裹,里边是C语言写的一些定义和声明,包括文件包含,宏定义,全局变量,函数声明什么的。

    规则段用%%和%%来包裹,主要是你的规则部分,在wc.l中,是识别[a-zA-Z]这样的word等,当然这里还包括状态等,不细节进行描述,本文档只负责进行介绍。

    最后的辅助函数段,需要实现前边声明的函数还有最重要的main函数。

    参考文档:

    1. 《flex&Bison》
    2. http://dinosaur.compilertools.net/
    3. https://www.geeksforgeeks.org/introduction-to-yacc/
    4. Yacc:Yet Another Compiler-Compiler http://dinosaur.compilertools.net/yacc/
    5. https://blog.csdn.net/wp1603710463/article/details/50365640
    6. https://www.jianshu.com/p/1fe5a61fd9dc
    7. https://blog.csdn.net/pandaxcl/category_188988.html?spm=1001.2014.3001.5482
    8. https://blog.csdn.net/huyansoft/article/details/8860224
    9. https://blog.csdn.net/Augusdi/article/details/64127840
  • 相关阅读:
    数据公钥加密和认证中的私钥公钥
    hibernate数据的三种状态
    寄存器和立即数和内存单元
    三条总线作用
    liunx安装redis和gcc
    Python遍历列表
    String.split()分割字符串方法
    Jmeter教程 录制脚本
    Jemeter压力测试
    Jmeter性能测试
  • 原文地址:https://www.cnblogs.com/jourluohua/p/14512229.html
Copyright © 2011-2022 走看看