01
项目简介
1、轻量化与高效
02
解析器分类
// 使用一个解析器Char('a')来检查输入字符串"a"是否可以被解析为单个字符'a'。
// 如果可以,Assert.AreEqual将验证解析结果是否为'a'。
Parser<char, char> parser = Char('a');
Assert.AreEqual('a', parser.ParseOrThrow("a"));
// 使用Digit解析器来检查输入字符串"3"是否可以被解析为一个数字字符,并期望得到整数形式的'3'(尽管这里实际上得到的是字符'3')。
// 注意:如果目标是得到整数类型的3,而不是字符'3',则示例可能需要调整或明确说明转换。
// 但按照代码字面意思,Assert.AreEqual验证的是字符'3'。
Assert.AreEqual('3', Digit.ParseOrThrow("3"));
// 创建一个解析器String("foo"),该解析器尝试从输入字符串中提取子字符串"foo"。
// 如果输入字符串确实是"foo",则Assert.AreEqual将验证解析结果是否为字符串"foo"。
Parser<char, string> parser = String("foo");
Assert.AreEqual("foo", parser.ParseOrThrow("foo"));
// 使用Return(3)创建一个解析器,该解析器不依赖于输入字符串,而是直接返回整数3。
// 这意味着无论输入字符串是什么,解析结果都将是3。
// 因此,即使输入是"foo",Assert.AreEqual仍然验证解析结果是否为整数3。
Parser<char, int> parser = Return(3);
Assert.AreEqual(3, parser.ParseOrThrow("foo"));
排序解析器
Then:顺序执行两个解析器,仅保留第二个的结果。
Before:顺序执行两个解析器,但仅保留第一个的结果。
Map:对解析器结果进行转换,不改变解析流程。
Bind:基于第一个解析器的结果选择并执行第二个解析器。
// 示例1: 使用Then(假设它保留第二个解析器的结果)
Parser<char, string> parser1 = String("foo");
Parser<char, string> parser2 = String("bar");
// 注意:这里可能需要一个特殊的Then实现,因为标准的Then可能不直接丢弃第一个结果
// 假设有一个名为ThenThatOnlyKeepsSecond的扩展方法
Parser<char, string> sequencedParser = parser1.ThenThatOnlyKeepsSecond(parser2); // 假设的方法名
Assert.AreEqual("bar", sequencedParser.ParseOrThrow("foobar"));
// 示例2: 使用Before(假设它存在且保留第一个解析器的结果)
// 注意:这通常不是标准库中的操作,但我们可以假设它存在
Parser<char, string> beforeParser = parser1.Before(parser2); // 假设的Before实现
Assert.AreEqual("foo", beforeParser.ParseOrThrow("foobar"));
// 示例3: 使用Map来合并两个解析器的结果
// 注意:这里我们假设有一个能够处理两个解析器的Map实现
Parser<char, string> mappedParser = Map((foo, bar) => bar + foo, parser1, parser2); // 假设的Map实现
Assert.AreEqual("barfoo", mappedParser.ParseOrThrow("foobar"));
// 示例4: Bind的使用(但这里给出的例子并不典型)
// 假设我们想要根据输入的第一个字符来构造一个解析器,但这里我们简化它
Parser<char, char> anyCharParser = AnyChar(); // 假设的AnyChar解析器
Parser<char, char> specificCharParser = anyCharParser.Bind(c => Char(c));
// 但为了符合您的示例,我们假设它工作如下(实际上这并不实用)
Assert.AreEqual('a', specificCharParser.ParseOrThrow("a")); // 这将总是返回输入的第一个字符
3、选择替代方案
Or:表示可以解析两个备选方案之一的解析器。
OneOf:等同于Or,只不过它接受可变数量的参数。
// 定义一个能够解析字符串 "foo" 或 "bar" 的解析器
// 使用 Or 方法组合两个 String 解析器
Parser<char, string> parser = String("foo").Or(String("bar"));
// 测试解析器是否能正确解析 "foo"
Assert.AreEqual("foo", parser.ParseOrThrow("foo")); // 应该成功解析并返回 "foo"
// 测试解析器是否能正确解析 "bar"
Assert.AreEqual("bar", parser.ParseOrThrow("bar")); // 应该成功解析并返回 "bar"
// 用于创建一个能够解析多个选项中任意一个的解析器
Parser<char, string> parser = OneOf(String("foo"), String("bar"));
// 测试解析器是否能正确解析 "foo"
Assert.AreEqual("foo", parser.ParseOrThrow("foo")); // 应该成功解析并返回 "foo"
// 测试解析器是否能正确解析 "bar"
Assert.AreEqual("bar", parser.ParseOrThrow("bar")); // 应该成功解析并返回 "bar"
4、LINQ语法
Parser<char, char> parser =
from c in Any
from c2 in Char(c)
select c2
5、递归语法
Parser<char, char> expr = null;
Parser<char, char> parenthesised = Char('(')
.Then(Rec(() => expr))
.Before(Char(')'));
expr = Digit.Or(parenthesised);
Assert.AreEqual('1', expr.ParseOrThrow("1"));
Assert.AreEqual('1', expr.ParseOrThrow("(1)"));
Assert.AreEqual('1', expr.ParseOrThrow("(((1)))"));
03
与解析工具对比
Pidgin vs Sprache
Sprache 是另一个 C# 解析组合器库,但与 Pidgin 相比,Sprache 存在一些限制:
Pidgin vs FParsec
FParsec 是一个为 F# 设计的解析组合器库,与 Pidgin 相比,它有以下不同:
04
项目地址
https://github.com/benjamin-hodgson/Pidgin
推荐阅读
回复“888”,免费领取.NetCore视频教程