• 编译原理递归下降分析法


    所谓递归下降法 (recursive descent method),是指对文法的每一非终结符号,都根据相应产生式各候选式的结构,为其编写一个子程序 (或函数),用来识别该非终结符号所表示的语法范畴。例如,对于产生式E′→+TE′,可写出相应的子程序如下:

    exprprime( )
    {
    if (match (PLUS))
    {
    advance( );
    term( );
    exprprime( );
    }
    }

         其中: 函数match()的功能是,以其实参与当前正扫视的符号 (单词)进行匹配,若成功则回送true,否则回送false;函数advance()是一个读单词子程序,其功能是从输入单词串中读取下一个单词,并将它赋给变量Lookahead;term则是与非终结符号T相对应的子程序。诸如上述这类子程序的全体,便组成了所需的自顶向下的语法分析程序。
         应当指出,由于一个语言的各个语法范畴 (非终结符号)常常是按某种递归方式来定义的,此种特点也就决定了这组子程序必然以相互递归的方式进行调用,因此,在实现递归下降分析法时,应使用支持递归调用的语言来编写程序。所以,通常也将上述方法称为递归子程序法。
        
    例42对于如下的文法G[statements]:
    statements→expression; statements |ε
    expression→term expression′
    expression′→+term expression′ |ε
    term→factor term′
    term′→*factor term′ |ε
    factor→numorid | (expression)
    通过对其中各非终结符号求出相应的FIRST集和FOLLOW集 (计算FIRST集和FOLLOW集的方法后面再做介绍),可以验证,此文法为一LL(1)文法,故可写出递归下降语法分析程序如程序41所示(其中,在文件lex.h里,将分号、加号、乘号、左括号、右括号、输入结束符及运算对象分别命名为SEMI,PLUS,TIMES,LP,RP,EOI及NUMORID,并指定了它们的内部码;此外,还对外部变量yytext,yyleng及yylineno进行了说明)。

    程序 G\[statements\]的递归下降语法分析程序
    1/* Basic parser, shows the structure but there's no code generation */
    2
    3#include <stdio.h>
    4#include "lex.h"
    5
    6statements ( )
    7{
    8/* statements → expression SEMI
    9 *| expression SEMI statements
    10*/
    11
    12expression( );
    13
    14if (match (SEMI))
    15advance( );
    16else
    17fprintf (stderr, "%d: Inserting missing semicolon\n", yylineno);
    18
    19if (!match (EOI))
    20statements ( );/* Do another statement. */
    21}
    22
    23expression( )
    24{
    25/* expression → term expression′ */
    26
    27term( );
    28exprprime( );
    29}
    30
    31exprprime( )
    32{
    33/* expression′ → PLUS term expression′
    34 *| epsilon
    35 */
    36
    37if (match (PLUS))
    38{
    39advance ( );
    40term( );
    41exprprime( );
    42}
    43}
    44
    45term( )
    46{
    47/* term → factor term′ */
    48
    49factor( );
    50termprime( );
    51}
    52
    53termprime( )
    54{
    55/* term′→TIMES factor term′
    56 *| epsilon
    57 */
    58
    59if (match (TIMES))
    60{
    61advance( );
    62factor( );
    63termprime( );
    64}
    65}
    66
    67factor( )
    68{
    69/* factor → NUMORID
    70 *| LP expression RP
    71 */
    72
    73if (match (NUMORID))
    74advance( );
    75
    76else if (match (LP))
    77{
    78advance( );
    79expression( );
    80if (match(RP))
    81advance( );
    82else
    83fprintf (stderr, "%d: Mismatched parenthesis\n", yylineno);
    84}
    85else
    86fprintf (stderr, "%d: Number or identifier expected\n", yylineno);
    87}
    利用程序41进行语法分析时,我们约定,如果当前的输入符号不是应出现的符号 (例如,对于第37行,当前的输入符号不是加号),则用相应的ε产生式进行推导。另外,上述程序有两个比较明显的缺点: 一是频繁的递归调用将使工作效率大为降低;二是缺乏较完善的语法检查和出错处理。这可通过适当改写文法和扩充程序的功能来改善。例如,可将原文法改写为如下的文法G′[statements]:
    statements→{expression;}
    expression→term{+term}
    term→factor{*factor}
    factor→numorid | (expression)
    此时,对一些子程序的递归调用就可用一段代码的重复执行来代替。于是,对于文法G′[statements],可写出改进的递归下降分析程序如程序42所示。
    程序 42改进的递归下降语法分析程序
    1/* Revised parser */
    2
    3#include <stdio.h>
    4#include "lex.h"
    5
    6voidfactor(void);
    7voidterm(void);
    8voidexpression(void);
    9
    10statements( )
    11{
    12/* statements → expression SEMI | expression SEMI statements */
    13
    14while (!match (EOI))
    15{
    16expression( );
    17
    18if (match (SEMI))
    19advance( );
    20else
    21fprintf (stderr, "%d: Inserting missing semicolon\n", yylineno);
    22}
    23}
    24
    25voidexpression( )
    26{
    27/* expression → term expression′
    28 * expression′ → PLUS term expression′ | epsilon
    29 */
    30
    31if (!legallookahead (NUMORID, LP,0))
    32return;
    33
    34term( );
    35while (match (PLUS))
    36{
    37advance( );
    38term( );
    39}
    40}
    41
    42voidterm( )
    43{
    44if (!legallookahead (NUMORID, LP,0))
    45return;
    46
    47factor( );
    48while (match (TIMES))
    49{
    50advance( );
    51factor( );
    52}
    53}
    54
    55voidfactor( )
    56{
    57if (!legallookahead (NUMORID, LP,0))
    58return;
    59
    60if (match (NUMORID))
    61advance( );
    62
    63else if (match (LP))
    64{
    65advance( );
    66expression( );
    67if (match (RP))
    68advance( );
    69else
    70fprintf (stderr, "%d: Mismatched parenthesis\n", yylineno);
    71}
    72else
    73fprintf (stderr, "%d: Number or identifier expected\n", yylineno);
    74}
    在程序42的expression( )、term( )及factor( )等三个函数中,都调用了一个名为legallookahead的函数。此函数的实参是相应非终结符FIRST集合中的各个元素,并且用一个0作为最后一个实参,其功能是: 检查当前的输入符号是否属于相应非终结符的FIRST集,若是其中的元素,则继续进行语法分析;否则,除报错外,还逐个删除输入串中的符号,直到出现属于该FIRST集中的某个符号为止。
         下面,我们列出函数legallookahead的代码如程序43所示。
         程序 43函数legallookahead
    75#include <stdarg.h>
    76
    77#defineMAXFIRST16
    78#defineSYNCHSEMI
    79
    80intlegallookahead (firstarg)
    81intfirstarg;
    82{
    83/* Simple error detection and recovery. Arguments are a 0terminated list of
    84 * those tokens that can legitimately come next in the input. If the list is
    85 * empty, the end of file must come next. Print an error message if
    86 * necessary. Error recovery is performed by discarding all input symbols
    87 * until one that's in the input list is found
    88 *
    89 * Return true if there's no error or if we recovered from the error,
    90 * false if we can't recover.
    91 */
    92
    93valistargs;
    94inttok;
    95intlookaheads [MAXFIRST], *p=lookaheads, *current;
    96interrorprinted=0;
    97intrval=0;
    98
    99vastart (args, firstarg);
    100
    101if (!firstarg)
    102{
    103if(match (EOI))
    104rval =1;
    105}
    106else
    107{
    108*p++=firstarg;
    109while ((tok=vaarg(args, int)) && p<&lookaheads[MAXFIRST])
    110*++p=tok;
    111
    112while (!match (SYNCH))
    113{
    114for (current = lookaheads; current < p; ++currrent)
    115if (match (*current))
    116{
    117rval = 1;
    118goto exit;
    119}
    120
    121if (!errorprinted)
    122{
    123fprintf (stderr, "Line %d: Syntax error\n", yylineno);
    124errorprinted = 1;
    125}
    126
    127advance( );
    128}
    129}
    130
    131exit;
    132vaend (args)
    133return rval;
    134}

    ---恢复内容结束---

  • 相关阅读:
    js debounce防抖技术
    我在项目中es6中数组的常用方法
    windows 部署Nginx转发http2.0协议
    AES加密,C#和java相同
    asp:Button js弹出提示框信息
    服务器不重启安装Asp.net Core 程序包
    C# string.Join的用法
    IIS部署asp.net core webapi
    ASP.net 加载不了字体Failed to load resource: the server responded with a status of 404 (Not Found)
    Windows Redis 取消保护模式C#进行访问
  • 原文地址:https://www.cnblogs.com/youxin/p/2965723.html
Copyright © 2020-2023  润新知