[C++] 无聊的人写的LL分析和LR分析

本文介绍了如何使用C++实现LL(1)和LR(1)语法分析方法,包括模型抽取、递归调用分析、求First集和Follow集的算法,以及LL(1)和LR(1)分析表的构造和通用分析器的设计。通过测试结果展示了这两种分析法在简化推导过程上的效果。
摘要由CSDN通过智能技术生成

模型抽取

一个模型Model<term, uterm>拥有两个元类型Term终结符类型和Uterm非终结符类型,对应于文法中的概念 G ( N , T , P , S ) G(N,T,P,S) G(N,T,P,S)如下:

template<typename term_t = int32_t, typename uterm_t = int32_t>
class Model {
   
    friend class Processor;
public:
    using string = std::string;
    using strvec = std::vector<string>;
    using symbol_t = Symbol<term_t, uterm_t>;
    using model = Model<term_t, uterm_t>;
    std::map<string, symbol_t> sym_table;
    std::vector<Production<symbol_t>> prods;
    symbol_t begin_symbol;
}

其中symtable对应 N ∪ T N\cup T NT,prods对应 P P P, begin symbol对应 S S S
这个模型类只负责将文件内的模型读入到内存中,不负责具体的语法构建。

递归调用分析

递归调用分析写出的代码只针对具体一种语言,因此实用性不强。代码如下:

template<typename token_t, class Source, class TokenTable>
class RecursiveAParser {
   
    using istream = Source;
    using result_t = Result<token_t, UTerm>;
    using node_t = ASTNode<token_t, UTerm>;
    istream &ref;
    token_t token;

    result_t *result;
public:
    RecursiveAParser(istream &ref): ref(ref) {
   }
    result_t* parse() {
   
        result = new result_t();
        result->code = ResultCode::Ok;
        auto hdl = result;
        read();
        parseE(result->rt = result->alloc(UTerm::E, true));
        result = nullptr;
        return hdl;
    }
private:
    void read() {
   
        ref >> token;
    }
    void error() {
   
        result->code = ResultCode::Error;
        std::cout << "error " << token << std::endl;
    }
    void parseE(node_t* &rt) {
   
        parseT(rt->insert(result->alloc(UTerm::T, true)));
        if (token != TokenTable::eof) {
   
            parseED(rt->insert(result->alloc(UTerm::ED, true)));
        }
    }
    void parseED(node_t* &rt) {
   
        if (token == TokenTable::add || token == TokenTable::sub) {
   
            rt->insert(result->alloc(token));
            read();
            parseT(rt->insert(result->alloc(UTerm::T, true)));
            parseED(rt->insert(result->alloc(UTerm::ED, true)));
        }
    }
    void parseT(node_t* &rt) {
   
        parseF(rt->insert(result->alloc(UTerm::F, true)));
        if (token != TokenTable::eof) {
   
            parseTD(rt->insert(result->alloc(UTerm::TD, true)));
        }
    }
    void parseTD(node_t* &rt) {
   
        if (token == TokenTable::mul || token == TokenTable::div) {
   
            rt->insert(result->alloc(token));
            read();
            parseF(rt->insert(result->alloc(UTerm::F, true)));
            parseTD(rt->insert(result->alloc(UTerm::TD, true)));
        }
    }
    void parseF(node_t* &rt) {
   
        if (token == TokenTable::lbr) {
   
            rt->insert(result->alloc(token));
            read();
            parseE(rt->insert(result->alloc(UTerm::E, true)));
            if (token == TokenTable::rbr) {
   
                rt->insert(result->alloc(token));
                read();
            } else {
   
                error();
            }
        } else if (token == TokenTable::num) {
   
            rt->insert(result->alloc(token));
            read();
        } else {
   
            error();
        }
    }
};

istreamlexer的输出流,经过递归函数组织以后生成语法树。生成结果与树节点数据结构如下:

template<typename term_t, typename uterm_t>
struct Result {
   
    using node_t = ASTNode<term_t, uterm_t>;
    node_t *rt;
    ResultCode code;
}

template<typename term_t, typename uterm_t>
struct ASTNode {
   
    using symbol_t = Symbol<term_t, uterm_t>;
    symbol_t symbol;
    std::vector<ASTNode*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值