You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为PEGTL解析器接入自定义词法分析器的Token输入?

使用自定义Token向量作为PEGTL解析器输入的解决方案

我明白你已经有了自己的词法分析器生成的Token向量,想把它替换标准输入作为PEGTL的输入源。确实,PEGTL默认是针对字符流设计的,所以咱们需要做两件核心事:实现一个适配Token向量的输入适配器,以及调整语法规则来匹配你的Token类型。下面结合你的求和示例一步步来修改:

1. 实现Token输入适配器

首先要写一个符合PEGTL ParseInput 概念的类,用来包装你的std::vector<Token>。这个类需要提供PEGTL解析时需要的核心方法:获取当前Token、移动到下一个Token,以及一些必要的类型定义。

#include <vector>
#include <tao/pegtl.hpp>
using namespace TAO_PEGTL_NAMESPACE;

// 你的Token定义保持不变
struct Token {
    typedef enum { COMMA, NUM, END_OF_FILE } Type;
    Token(Type type, int num = 0) : type(type), num(num) {}
    Type type;
    int num;
};

// 自定义Token输入适配器
class token_input {
public:
    // PEGTL要求的类型别名
    using iterator_t = std::vector<Token>::const_iterator;
    using char_t = Token; // 把Token作为输入的"字符"类型
    using input_t = token_input;

    // 构造函数:传入Token向量的引用
    explicit token_input(const std::vector<Token>& tokens)
        : m_tokens(tokens), m_current(m_tokens.begin()) {}

    // 获取当前Token,到达末尾时返回EOF Token
    const Token& current() const noexcept {
        if (m_current != m_tokens.end()) {
            return *m_current;
        }
        static const Token eof_token(Token::END_OF_FILE);
        return eof_token;
    }

    // 移动到下一个Token,返回是否成功(未到末尾则成功)
    bool next() noexcept {
        if (m_current != m_tokens.end()) {
            ++m_current;
            return true;
        }
        return false;
    }

    // PEGTL要求的其他辅助方法(按需实现)
    std::size_t size() const noexcept { return m_tokens.size(); }
    bool empty() const noexcept { return m_tokens.empty(); }
    iterator_t begin() const noexcept { return m_tokens.begin(); }
    iterator_t end() const noexcept { return m_tokens.end(); }
    std::string source() const noexcept { return "token_vector"; }
    position position() const noexcept { return { source(), 0, 0 }; }

private:
    const std::vector<Token>& m_tokens;
    iterator_t m_current;
};

2. 调整语法规则适配Token类型

原来的语法是匹配字符的,现在要改成匹配Token的类型。PEGTL提供了pegtl::match<T>规则,用于匹配自定义输入元素的值(这里就是Token的Type枚举)。

namespace sum {
    // 匹配NUM类型的Token
    struct num : pegtl::match<Token::NUM> {};
    // 匹配COMMA类型的Token
    struct comma : pegtl::match<Token::COMMA> {};
    // 用comma分隔的num列表
    struct int_list : list<num, comma> {};
    // 完整语法:int_list 后接END_OF_FILE
    struct grammar : seq<int_list, pegtl::match<Token::END_OF_FILE>> {};

    // 调整动作类:直接从Token中取数值,无需字符串转换
    template<typename Rule>
    struct action {};

    template<>
    struct action<num> {
        template<typename ActionInput>
        static void apply(const ActionInput& in, int& sum) {
            // 直接访问当前Token的num属性
            sum += in.current().num;
        }
    };
} // namespace sum

3. 在主函数中使用Token向量解析

现在可以直接把你的Token向量传给PEGTL的parse函数,替换原来的istream_input:

int main() {
    // 构建你的Token向量(保留你原来的逻辑)
    std::vector<Token> tokens;
    tokens.emplace_back(Token::NUM, 1);
    tokens.emplace_back(Token::COMMA);
    tokens.emplace_back(Token::NUM, 2);
    tokens.emplace_back(Token::COMMA);
    tokens.emplace_back(Token::NUM, 3);
    tokens.emplace_back(Token::END_OF_FILE);

    // 用自定义输入适配器解析Token向量
    int sum = 0;
    if (parse<sum::grammar, sum::action>(token_input(tokens), sum)) {
        std::cout << "Token parsing OK; sum = " << sum << std::endl;
    } else {
        std::cout << "Token parsing failed" << std::endl;
    }

    return 0;
}

4. 编译运行

编译命令和之前一致,确保PEGTL的include路径正确:

g++ -std=c++17 -I PEGTL/include parser.cc

运行后会输出:Token parsing OK; sum = 6,说明解析成功。

关键要点说明

  • 输入适配器的char_t类型设为Token,告诉PEGTL我们的输入元素是Token,而不是单个字符。
  • pegtl::match<T>规则会检查当前输入元素(即Token的type)是否等于T的值,完美适配自定义Token类型的匹配需求。
  • 动作类中不再需要字符串转整数,直接从Token中取num属性,效率更高也更简洁。

内容的提问来源于stack exchange,提问作者Frank Buss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:35:08