以 Schwifty 方式进行的类似 Haskell 的解析器组合器
蛇
/sɑːrp/
以 Schwifty 方式进行的类似 Haskell 的解析器组合器
玩具项目 如果您正在寻找更适合生产的东西,我会推荐:davedufresne/SwiftParsec
大多数理论来自 Scott Wlaschin 在 F# 解析器组合器方面的工作。我真的很推荐他的演讲“理解解析器组合器:深入研究”。这是我对组合器的第一次介绍,也是我兴趣的顶峰。
什么是解析器组合器
解析器组合器是在语义上与其他解析器组合的解析器。例如:
- 优先顺序:
parser1.preceded(by: parser2)
- 重复:
parser1.repeat(4...)
- 分枝:
either(parser1, parser2)
解析器组合器也是单子,这意味着我们可以将它们绑定到一个返回解析器的函数,并将其应用于结果。
- 地图:
parseDigits.map { Int($0) }
- 选修的:
parser.optional()
- 应用:
parser1.apply(parser2)
最重要的解析器是satisfy
,它接受一个元素并.success
在它与谓词函数匹配时返回。
示例:(satsify {"a"..."z" ~= $0}
如果是小写字母则取一个字符)
bind
整个解析器库可以从和构建satisfy
,但支持回溯预防、限制和出于优化原因,有一些自定义函数。
组合器是非常模块化的,因此可以实现这些部分并将其组合成更大的解析器。
特征
- Swifty:专注于创建具有 Swift 表现力的解析器,而不是拥有庞大的 API 界面
- 没有运算符重载:许多单子解析器库使用
<&>
, '>>=,
<|>` 等来组合解析器。我更喜欢文字。 - 很少的原语:使优化变得容易
- Generic : 不限于字符串
- 面向价值:解析器是价值,它们是不可变的。
- 回溯预防:
- 缓冲区限制意识:
例子
猫或狗
enum Animal {
case cat
case dog
}
let parser = either(
literal("cat").to(Animal.cat),
literal("dog").to(Animal.dog)
)
assert(parser.parse("cat") == .success(.cat, ""))
Bind
例子
enum Number: Equatable {
case signed(Int)
case unsigned(UInt)
}
let number = char("-").optional().bind { minus in
let unsignedNumber = satisfy { "0" ... "9" ~= $0 }
.repeat(0...)
.map { String($0) }
if let minus {
return unsignedNumber
.map { -Int($0)! }
.map { Number.signed($0) }
} else {
return unsignedNumber
.map { UInt($0)! }
.map { Number.unsigned($0) }
}
}
assert(number.parse("3") == .limit(.unsigned(3), ""))
assert(number.parse("-0345somethingElse") == .success(.signed(-345), "somethingElse"))
JSON数组
在哪里jsonWhitspace
和jsonValue
已经声明。
let jsonArray = either(
jsonWhitespace
.preceded(by: char("["))
.terminated(by: char("]"))
.to(JSON.array([])),
serial(
jsonValue(),
jsonValue()
.preceded(by: char(","))
.repeat(0...)
).map { first, rest in
[first] + rest
}
.preceded(by: char("["))
.terminated(by: char("]"))
.map { JSON.array($0) }
)