以 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数组

在哪里jsonWhitspacejsonValue已经声明。

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) }
)

GitHub

查看 Github