C++实现一个简易版的ParseC

ParseC 最初出现在 Haskell 社区,是一个很方便的实现 Parser 的工具,它定义了一个很简洁的 Parser 模型,并围绕这个模型,结合 Haskell 本身的语法特性,构建了一个强力的工具库。在 C++中我们依然可以吸取它的思想,利用 C++的语法特性实现一个类似的工具。

基础模型

整个模型可以被概括为两个基本的理念:(1)复杂的 Parser 可以通过一系列简单的 Parser 组合得到;(2)Parser 可以定义为一个将字符串变换为解析结果和剩余字符串的映射。借助代码一个 Parser 可以抽象为这样一个映射:

rust 复制代码
type Parser a = String -> Maybe (a, String)

那么在 C++中,可以定义如下对等的产物

arduino 复制代码
#include <optional>
#include <string_view>
#include <tuple>

template <typename T>
using ParserRet = std::optional<std::tuple<T, std::string_view>>;

template <typename T>
using Parser = std::function<ParserRet<T>(std::string_view)>;

一些基本的 Parser

在实现复杂的 Parser 之前,我们需要一些基础的 Parser 来提供最基础的单元,这些基础的 Parser 具备一定的普适性。

char parser

首先来实现一个最简单的 Parser,这个 Parser 仅仅解析出一个字符'h'

arduino 复制代码
ParserRet<char> h_parser(std::string_view s) {
  if(s.empty() || s.at(0) != 'h') {
    return std::nullopt;
  }
  
  return std::make_tuple('h', s.substr(1));
}

观察可以发现,上面函数里的模式是具有普遍性的,可以进行第一步推广,推广为解析各个指定的字符

arduino 复制代码
ParserRet<char> char_parser(char c, std::string_view s) {
  if (s.empty() || s.at(0) != c) {
    return std::nullopt;
  }
  return std:make_tuple(c, s.substr(1)); 
}

前面的h_parser 就可以改写为

rust 复制代码
Parser<char> h_parser = std::bind(char_parser, 'h', std::placeholder::_1); 

或者写成lambda表达式的形式

rust 复制代码
Parser<char> h_parser = [](std::string_view s) -> ParserRet<char> {
  return char_parser('h', s); 
};

更近一步,如果想要能解析满足更灵活诉求的字符,可以进一步推广上面的Parser为

c 复制代码
Parser<char> satisify_parser(std::function<bool(char)> f, std::string_view s) {
  if (s.empty() || !f(s.at(0))) {
    return std::nullopt;
  }
  return std::make_tuple(s.at(0), s.substr(1)); 
}

上面的h_parser 可以改写为

rust 复制代码
Parser<char> char_parser = std::bind(
    satisify_parser,
    [](char c) -> bool {
      return c == 'h';
    },
    std::placeholder::_1);

string parser

类似的,可以实现解析特定字符串的Parser

c 复制代码
ParserRet<std::string> string_parser(std::string target, std::string_view s) {
  if (s.empty() || s.substr(0, target.size()) != target) {
    return std::nullopt;
  }
  return std::make_tuple(target, s.substr(target.size()));
}

基于此,采用类似的思路,可以实现类似解析数字,整数,小数,括号,标点等常见的元素。

相关推荐
初圣魔门首席弟子6 分钟前
C++ STL string(字符串)学习笔记
c++·笔记·学习
AA陈超29 分钟前
虚幻引擎5 GAS开发俯视角RPG游戏 P04-12 可缩放浮点数的曲线表
c++·游戏·ue5·游戏引擎·虚幻
旭意38 分钟前
C++微基础备战蓝桥杯之数组篇10.1
开发语言·c++·蓝桥杯
青草地溪水旁2 小时前
VSCode C/C++ 构建任务配置文件 `tasks.json` 全字段深度解析
c语言·c++·vscode
爱和冰阔落4 小时前
C++模板进阶 非类型模板参数 模板的特化 分离编译的深入探索
c++·面试·编译原理·模板
charlie11451419110 小时前
精读C++20设计模式:行为型设计模式:中介者模式
c++·学习·设计模式·c++20·中介者模式
楼田莉子10 小时前
Qt开发学习——QtCreator深度介绍/程序运行/开发规范/对象树
开发语言·前端·c++·qt·学习
oioihoii10 小时前
超越 std::unique_ptr:探讨自定义删除器的真正力量
c++
Gohldg11 小时前
C++算法·贪心例题讲解
c++·数学·算法·贪心算法
天若有情67311 小时前
C++空值初始化利器:empty.h使用指南
开发语言·c++