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
  • 相关阅读:
    剑指Offer-11.二进制中1的个数(C++/Java)
    剑指Offer-10.矩形覆盖(C++/Java)
    剑指Offer-9.变态跳台阶(C++/Java)
    UVA 1608 Non-boring sequence 不无聊的序列(分治,中途相遇)
    UVA1607 Gates 与非门电路 (二分)
    UVA 1451 Average平均值 (数形结合,斜率优化)
    UVA 1471 Defense Lines 防线 (LIS变形)
    UVA 1606 Amphiphilic Carbon Molecules 两亲性分子 (极角排序或叉积,扫描法)
    UVA 11134 FabledRooks 传说中的车 (问题分解)
    UVA 1152 4 Values Whose Sum is Zero 和为0的4个值 (中途相遇)
  • 原文地址:https://www.cnblogs.com/jourluohua/p/14512229.html
Copyright © 2011-2022 走看看