FootlessParser 2.0.6

FootlessParser 2.0.6

kareman 维护。



  • 作者
  • Kare Morstol

Carthage compatible

Swift 5, 4.2 和 4.1 | Swift 2.2+

FootlessParser

FootlessParser 是 Swift 中解析器组合器的一个简单且直观的实现。它支持无限前瞻,带有错误报告的明确解析。

有一系列关于其开发的博客文章(链接)以及从源代码中的文档

简介

简而言之,FootlessParser 允许您像这样定义解析器

let parser = function1 <^> parser1 <*> parser2 <|> parser3

function1parser3 返回相同类型。

parser 将将输入传递给 parser1,然后是 parser2,将它们的结果传递给 function1 并返回其结果。如果那失败了,它将原始输入传递给 parser3 并返回其结果。

定义

解析器

一个函数,它接受一些输入(一系列标记)并返回输出及其余未解析的输入部分或错误描述,如果失败。

令牌

输入中的单个项目。例如,字符串中的一个字符、数组中的一个元素或命令行参数列表中的一个字符串。

解析器输入

通常是文本,但也可能是一个数组或任何符合CollectionType的集合。

解析器

通用思路是将非常简单的解析器组合成更复杂的解析器。所以 char("a") 创建的解析器会检查下一个输入令牌是否为 "a"。如果是,则返回那个 "a",否则返回错误。然后您可以使用操作符和函数,如 zeroOrMoreoptional 来创建更复杂的解析器。更多函数请参阅 函数完整列表

操作符

<^> (映射)

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,返回其输出或错误。

示例

实际应用

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 来安装它。