LL(1)语法分析器:从语法规则到语法树

发布时间:2023-05-24

在编译原理中,语法分析是编译器的一个重要阶段。语法分析器的作用是将代码转换成语法树,以便后续阶段进行处理。LL(1)语法分析器是语法分析器的一种,它采用的是自顶向下的分析方法,可以通过一个预测表来进行分析。下面将从几个方面对LL(1)语法分析器进行详细的阐述。

一、LL(1)语法分析器的概念

LL(1)语法分析器是一种自顶向下的语法分析器,它可以通过一个预测表来进行分析。LL(1)语法分析器的“LL”代表的是“左侧扫描,左推导(Derivation),1个向前查看符号(Lookahead)”。 LL(1)语法分析器的输入是一个代码串,输出是一个语法树。它首先需要根据代码所遵循的语法规则,建立文法。然后再通过文法,生成一个可以预测的分析表。最后,使用该预测表对代码串进行分析,生成语法树。

二、LL(1)语法分析器的文法设计

在LL(1)语法分析器的文法设计中,需要考虑以下几个问题: 1、如何表示终结符和非终结符? 在文法设计中,终结符是一种不可再分的符号。例如,加号“+”就是一个终结符。而非终结符则表示一个语法结构,例如,表达式就是一个非终结符。终结符一般用小写字母表示,非终结符一般用大写字母表示。 2、如何设计产生式? 产生式是一条用于生成语法结构的规则。它的格式通常是非终结符 -> 符号串,其中符号串可以由终结符和非终结符组成。 3、如何进行语法规则的归并与提取? 在设计文法时,需要将一些重复的产生式进行合并,以便简化文法。同时,需要进行一些产生式的提取,以便后续的语法分析。

三、LL(1)语法分析器的预测表生成

LL(1)语法分析器的预测表是一个二维的表格,它的行表示文法的非终结符,列表示文法的终结符和一个特殊的“$”符号(代表输入串结束)。表格中的每个元素代表了采取相应非终结符和终结符时的产生式编号。预测表的生成就是根据文法的有效产生式生成。 在预测表生成过程中,需要考虑以下几个问题: 1、如何消除文法的左递归? 左递归是文法的一种特殊形式,其在预测表生成中会带来一些问题。需要将左递归进行消除,以便生成预测表。 2、如何进行FIRST、FOLLOW集的计算? FIRST集表示文法每个符号可能的最左推导串集合,FOLLOW集表示在每个非终结符后面可能跟随的符号集合。它们是预测表生成的基础。 3、如何进行预测表的生成? 预测表可以通过计算FIRST、FOLLOW集来进行生成。如果对于某个非终结符A,它的FIRST集中包括了某个终结符a,那么就将文法中A -> α产生式中,将FIRST(α)中的所有符号都映射到预测表中,行为A,列为a。如果同时存在多个产生式需要映射到同一行上,则说明文法不是LL(1)文法。

四、LL(1)语法分析器的语法分析

LL(1)语法分析器在进行语法分析时,需要使用预测表来匹配输入符号,进行分析。匹配成功后,就可以根据预测表中对应的产生式,将输入符号替换成文法中的非终结符或空串(ε)。直到匹配到输入串的结束符号“$”为止,最后生成语法树。 在语法分析过程中,可能会出现一些错误。例如,输入串无法匹配预测表中的任何产生式,或者预测表中的不同产生式映射到同一行上。这些错误需要进行处理,以便生成正确的语法树。

五、LL(1)语法分析器的代码示例

#include <iostream>
#include <stack>
#include <vector>
using namespace std;
class LR_Syntax_Analyzer
{
public:
    LR_Syntax_Analyzer()
    {
        init_sta_table();
    }
    bool analyze(vector<string> inputs)
    {
        // 将输入符号表插入一个结束标识符 $
        vector<string> input_symbols = inputs;
        input_symbols.push_back("$");
        // 初始化状态栈和符号栈
        int n = input_symbols.size();
        sta_stack.push(0);
        sym_stack.push("$");
        int i = 0;
        while(i < n)
        {
            string a = input_symbols[i];
            int s = sta_stack.top();
            int t = get_sta_table_index(s, a);
            if(t == -1)
            {
                // 找不到与输入符号匹配的产生式
                return false;
            }
            else if(t > 0)
            {
                // 移进操作
                sta_stack.push(t);
                sym_stack.push(a);
                i++;
            }
            else if(t < 0)
            {
                // 规约操作
                int p = -t;
                p = p - 1;
                // 弹出产生式右部的符号
                for(int j = 0; j < prod_len[p]; j++)
                {
                    sta_stack.pop();
                    sym_stack.pop();
                }
                // 获取新的状态
                int s_new = sta_stack.top();
                string x = prod_left[p];
                // 在表中查找 goto(s_new, x)
                int q = get_sta_table_index(s_new, x);
                // 更新状态栈和符号栈
                sta_stack.push(q);
                sym_stack.push(x);
            }
            else
            {
                // 找到空产生式
                i++;
            }
        }
        return true;
    }
private:
    stack<int> sta_stack; // 状态栈
    stack<string> sym_stack; // 符号栈
    // 文法的产生式
    vector<string> prod_left = {"S", "S", "L", "L", "L", "E", "E"};
    vector<vector<string>> prod_right = {
            {"L", "=", "R"},
            {"R"},
            {"L", "+", "R"},
            {"L", "-", "R"},
            {"E"},
            {},
            {"(", "L", ")"}
    };
    vector<int> prod_len = {3, 1, 3, 3, 1, 0, 3};
    // 状态转换表
    vector<vector<int>> sta_table = {
            {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0},
            {3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 5, 6, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 8, 9, 0, 0, 0, 0, -2, -2, 0},
            {3, 4, 0, 0, 10, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 0},
            {0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0}
    };
    void init_sta_table()
    {
        sta_table.resize(8, vector<int>(11, 0));
        // 初始化表中的移进和规约操作
        sta_table[0][8] = 1;
        sta_table[0][9] = 2;
        sta_table[2][1] = 3;
        sta_table[2][2] = 4;
        sta_table[2][8] = -5;
        sta_table[2][9] = -5;
        sta_table[2][10] = 3;
        sta_table[3][1] = 6;
        sta_table[3][2] = 7;
        sta_table[4][1] = -1;
        sta_table[4][2] = -1;
        sta_table[4][8] = -1;
        sta_table[4][9] = -1;
        sta_table[5][4] = 11;
        sta_table[5][5] = 12;
        sta_table[5][8] = -3;
        sta_table[5][9] = -3;
        sta_table[5][10] = 11;
        sta_table[6][0] = 13;
        sta_table[7][1] = 6;
        sta_table[7][2] = 7;
        sta_table[7][9] = 14;
    }
    int get_sta_table_index(int s, string a)
    {
        int i = -1;
        if(a == "=") i = 0;
        else if(a == "+") i = 1;
        else if(a == "-") i = 2;
        else if(a == "*") i = 3;
        else if(a == "/") i = 4;
        else if(a == "(") i = 5;
        else if(a == ")") i = 6;
        else if(a == "id") i = 7;
        else if(a == "$") i = 8;
        if(i != -1)
        {
            return sta_table[s][i];
        }
        else
        {
            return -1;
        }
    }
};