Swift 5, 4.2 和 4.1 | Swift 2.2+
FootlessParser
FootlessParser 是 Swift 中解析器组合器的一个简单且直观的实现。它支持无限前瞻,带有错误报告的明确解析。
简介
简而言之,FootlessParser 允许您像这样定义解析器
let parser = function1 <^> parser1 <*> parser2 <|> parser3
function1
和 parser3
返回相同类型。
parser
将将输入传递给 parser1
,然后是 parser2
,将它们的结果传递给 function1
并返回其结果。如果那失败了,它将原始输入传递给 parser3
并返回其结果。
定义
解析器
一个函数,它接受一些输入(一系列标记)并返回输出及其余未解析的输入部分或错误描述,如果失败。
令牌
输入中的单个项目。例如,字符串中的一个字符、数组中的一个元素或命令行参数列表中的一个字符串。
解析器输入
通常是文本,但也可能是一个数组或任何符合CollectionType的集合。
解析器
通用思路是将非常简单的解析器组合成更复杂的解析器。所以 char("a")
创建的解析器会检查下一个输入令牌是否为 "a"。如果是,则返回那个 "a",否则返回错误。然后您可以使用操作符和函数,如 zeroOrMore
和 optional
来创建更复杂的解析器。更多函数请参阅 函数完整列表。
操作符
<^> (映射)
function <^> parser1
创建一个新解析器,它会运行解析器1。如果成功,则将输出传递给 function
并返回结果。
<*> (应用)
function <^> parser1 <*> parser2
创建一个新的解析器,它首先运行parser1。如果它成功,将运行parser2。如果这也成功,它将两个输出传递给function
并返回结果。
<*> 操作符要求其左侧解析器返回一个函数,通常与 <^> 一起使用。function
必须接受两个正确类型的参数,并且必须是柯里化(curried)的,如下所示
func function (a: A) -> (B) -> C
这是因为 <*> 返回两个解析器的输出,并且它不知道如何处理它们。如果您希望在元组、数组中进行返回或者将其添加在一起,您可以在 <^> 之前在函数中进行操作。
如果有3个解析器并且有2个 <*>,函数必须接受3个参数,依此类推。
<*
<*> 与上方的相同,但它丢弃右侧解析器的结果。因为它只返回一个输出,所以不需要与 <^> 一起使用。但如果您希望将输出转换为其他什么,当然可以。
*>
<* , 与 <* 类似,但它丢弃左侧解析器的结果。
<|> (选择)
parser1 <|> parser2 <|> parser3
该操作符按顺序尝试所有解析器并返回第一个成功的解析器的结果。
>>- (平坦映射)
parser1 >>- ( o -> parser2 )
这与Swift标准库中的flatMap函数的作用相同。它创建一个新的解析器,该解析器首先尝试parser1。如果失败,返回错误;如果成功,将输出传递给函数,该函数使用它来创建parser2。然后运行parser2,返回其输出或错误。
示例
实际应用
- oleander/BitBarParser - 允许您将任何脚本/程序在 Mac OS X 菜单栏中输出的内容。
- banjun/NorthLayout - 在代码中自动布局视图。
CSV 解析器
let delimiter = "," as Character
let quote = "\"" as Character
let newline = "\n" as Character
let cell = char(quote) *> zeroOrMore(not(quote)) <* char(quote)
<|> zeroOrMore(noneOf([delimiter, newline]))
let row = extend <^> cell <*> zeroOrMore(char(delimiter) *> cell) <* char(newline)
let csvparser = zeroOrMore(row)
这里一个单元格(或字段)可以是
- 以一个 " 字符开始,然后包含任何内容,包括逗号、制表符和换行符,并以 " 字符结束(两个引号都被丢弃)
- 或者未加引号且只包含逗号或换行符以外的任何内容。
然后每一行由一个或多个单元格组成,由逗号分隔,以换行符结束。extend
函数将单元格组合成数组。最后,csvparser
将零个或多个行收集成一个数组。
执行实际解析
do {
let output = try parse(csvparser, csvtext)
// output is an array of all the rows,
// where each row is an array of all its cells.
} catch {
}
递归表达式
func add(a: Int) -> (Int) -> Int { return { b in a + b } }
func multiply(a: Int) -> (Int) -> Int { return { b in a + b } }
let nr = { Int($0)! } <^> oneOrMore(oneOf("0123456789"))
var expression: Parser<Character, Int>!
let factor = nr <|> lazy (char("(") *> expression <* char(")"))
var term: Parser<Character, Int>!
term = lazy (multiply <^> factor <* char("*") <*> term <|> factor)
expression = lazy (add <^> term <* char("+") <*> expression <|> term)
do {
let result = try parse(expression, "(1+(2+4))+3")
} catch {
}
expression
解析 "12"
、"1+2+3"
、"(1+2)"
、"12*3+1"
和 "12*(3+1)"
等输入,并将结果返回为 Int。
所有直接或间接引用自身的解析器都必须预声明为隐式解包的可选变量(var expression: Parser<Character, Int>!
)。为了避免无限递归,定义必须使用 lazy
函数。
安装
Carthage
使用github "kareman/FootlessParser"
然后运行 carthage update
。
然后按照 Carthage 的 README 中的安装说明操作。
CocoaPods
使用将 FootlessParser
添加到您的 Podfile
文件中。
pod 'FootlessParser', '~> 0.5.2'
然后运行 pod install
来安装它。