如何为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
相关产品推荐
相关产品推荐

