• 简单编译器之语法分析


    OK,书接上文,今次这篇博客是准备说语法分析。

    其实词法分析和语法分析可以说是一体的。词法分析用于分析输入的单词,将其一一分门别类。语法分析分析已经分门别类好的单词,看其组成的句子是否符合语言的文法。

    首先,先确定一个简单的文法:

    S -> if ( M ) { N }
    S -> N
    M - > P == P
    p -> id | digit
    N -> int id ; | int id = num ; | Q;
    Q - > id = E
    E - > E + T | T
    T - > id | digit| ( E )

    这是一个非常简单的java风格文法,大家一眼就能看出来它的意思了。这段文法会匹配如下的句子:

    if(1 == 1)
    {
        int p = 1;
    }

    就如从正则表达式可以构造出词法分析器一样,也存在语法分析器的生成器之类的工具,可以用来构造预测分析器。相关的算法有LL(1)、LR(1)等。本文涉及的文法比较简单,符合LL(1)文法,不需要用到LR(1)这样强大而复杂的推理。因而本文准备用递归下降算法来构造预测分析器。

    LL(1)文法代表从左到右分析、最左推导和超前查看一个符号。为了确认一个文法是否是LL(1)文法需要构造LL(1)分析表,而为了构造LL(1)分析表又需要计算文法的FIRST和FOLLOW集合(详细的算法可以自行百度,这里就不列出)。

    记得第一次看LL(1)描述时看的头晕眼花,云里雾里的。后来自己干脆动手写程序时,就突然一下子明白了,为了要有这些步骤,这些步骤到底用来做什么的?

    直接用例子来说明,以S -> if ( M ) { N }、S -> N 来说,我们要如何写匹配这段文法的程序了?

    1.获取下一个字符t;

    2.根据t,判断是走if ( M ) { N }这条路了,还是走N这条路;

    3.如果t == 'if',而N的首字符没有'if'的情况,那就妥妥的走if ( M ) { N },反之也一样;

    4.如果N -> if blabla,那么我就傻眼了,到底应该怎么判断了?

    如果真出现4中的情况,那么这个文法就不是LL(1)文法。因为从左到右只超前读一个字符无法推导出一个唯一的结果。

    事实上,无论是消除左递归、计算FIRST集合、计算FOLLOW集合都是为了确认当前文法在出现分岔路时,每个分支的首个终结字符不相同

    本文的文法比较简单,直接消除左递归后得到满足LL(1)的文法:

    S -> if (M) {N}
    S -> N
    M -> P == P
    P -> id | digit
    N -> N1 N11 | E
    N1 -> int id
    N11 -> = num | null
    E -> TE1
    E1 -> + T | T
    T -> id | digit | (E)

    然后直接根据这个就可以得到下面的程序啦:

    def S():
        #下一个字符,用于预测
        token = seeToPretict();
        if(token == 'if'):
            eat('if');
            eat('(');
            M();
            eat(')');
            eat('{');
            N();
            eat('}');
            pass
        elif(token == 'id' or token == 'digit'):
            N();
        else :
            #报错
            Syntaxerror();
        print 'complete';
        pass
            
    def M():
        P();
        eat('==');
        P();
        pass
    
    def P():
        #下一个字符
        token = getToken();
        if(token != 'id' and token != 'digit'):
            syntaxerror();
        pass
    
    def N():
        token = seeToPretict();
        if(token == 'int'):
            N1();
            N11();
            eat(';');
        elif(token == 'id' or token == 'digit'):
            E();
            eat(';');
        else:
            syntaxerror();
        pass
        
    def N1():
        eat('int');
        eat('id');
    
    def N11():
        token = seeToPretict();
        if(token == '='):
            eat('=');
            eat('digit');
        pass
        
    def E():
        T();
        E1();
        
    def E1():
        token = seeToPretict();
        if(token == '+'):
            eat('+');
            T();
        elif(token == 'id' or token == 'digit'):
            T();
        else:
            syntaxerror();
    
    def T():
        token = getToken();
        if(token != 'id' and token != 'digit'):
            syntaxerror();
  • 相关阅读:
    如何获得Spring容器里管理的Bean,。不论是Service层,还是实体Dao层
    解析PHP中的file_get_contents获取远程页面乱码的问题【转】
    CSS中应用position的absolute和relative的属性制作浮动层
    css position 绝对定位和相对定位
    html bootstrap 表头固定在顶部,表列 可以自由滚动的效果
    php工具 phpstorm 的快捷键 的使用(待添加
    关于PHP HTML <input type="file" name="img"/>上传图片,图片大小,宽高,后缀名。
    Thinkphp 3.2 添加 验证码 如何添加。
    网页自适应@media
    如何让div上下左右都居中
  • 原文地址:https://www.cnblogs.com/sorheart/p/3250944.html
Copyright © 2020-2023  润新知