Parcoa 0.0.1

Parcoa 0.0.1

测试已测试
Lang语言 MUMPSMUMPS
许可证 MIT
发布最后发布2014年12月

未申报维护。



Parcoa 0.0.1

  • 作者:
  • James Brotchie

Parcoa:Objective-C 解析器组合器

Parcoa 是一个 Objective-C 解析器和解析器组合器的集合,由 Haskell 的 Parsec 软件包和 Python 的 Parcon 库启发而来。它遵循 MIT 许可协议。

纯 Parcoa 解析器

ParcoaParser * 是围绕一个函数的轻量级包装,该函数block接受一个NSString *作为参数,尝试从该字符串解析一些值,然后返回一个OKFail结果。成功时,解析器block返回解析的,未消耗的残留输入以及指示允许解析器消耗更多信息输入的消息。失败时,解析器block返回一个字符串描述,说明它在输入中期望找到什么。

Parser Diagram

考虑一个非常简单的解析器,它期望 unicode 字符 'a'

ParcoaParser *simpleA = [Parcoa unichar:'a'];

如果我们传递给它字符串 "abcd",它会解析第一个字符并返回一个 OK 结果

ParcoaResult *result = [simpleA parse:@"abcd"];

result.isOK == TRUE
result.value == @"a"

如果没有前面的 'a',则解析失败

ParcoaResult *result = [simpleA parse:@"bcd"];

result.isFail == TRUE

什么是解析器组合器?

尽管简单且独立,但是基本的 Parcoa 解析器单独使用是无用的。一个parser combinator 是一个函数,它接收一个或多个解析器,创建具有附加功能的解析器。组合解析器可以进一步组合,允许通过组合解析原始代码来实现复杂的解析行为。

例如,而不是单个 'a' 字符,我们可以匹配任何连续序列的 a

ParcoaParser *manyA = [Parcoa many:simpleA];

ParcoaResult *result = [manyA parse:@"aaaabcd"];

result.isOK == TRUE
result.value == @[@"a", @"a", @"a", @"a"]]

也许我们想匹配连续序列的 a 或字符串"hello"

ParcoaParser *hello = [Parcoa string:@"hello"];
ParcoaParser *manyAConcat = [manyA concat];
ParcoaParser *thisorthat = [manyAConcat or: hello];

ParcoaResult *result = [thisorthat parse:@"helloworld"];

result.isOK == TRUE
result.value == @"hello"

result = [thisorthat parse:@"aaaaaworld"];

result.isOK == TRUE
result.value = @"aaaaa"

一个简单的JSON解析器

以下是简单的JSON解析器。目前它仅支持整数文字。

ParcoaParser *colon        = [[Parcoa unichar:':'] skipSurroundingSpaces];
ParcoaParser *comma        = [[Parcoa unichar:','] skipSurroundingSpaces];
ParcoaParser *openBrace    = [[Parcoa unichar:'{'] skipSurroundingSpaces];
ParcoaParser *closeBrace   = [[Parcoa unichar:'}'] skipSurroundingSpaces];
ParcoaParser *openBracket  = [[Parcoa unichar:'['] skipSurroundingSpaces];
ParcoaParser *closeBracket = [[Parcoa unichar:']'] skipSurroundingSpaces];

ParcoaParser *quote         = [Parcoa unichar:'"'];
ParcoaParser *notQuote      = [Parcoa noneOf:@"\""];
ParcoaParser *escapedQuote  = [Parcoa string:@"\\\""];
ParcoaParser *stringContent = [Parcoa concatMany:[escapedQuote or: notQuote]];

ParcoaParserForward *json = [ParcoaParserForward forwardWithName:@"json"
                                                         summary:@"json forward declaration"];

ParcoaParser *string  = [stringContent between:quote and: quote];
ParcoaParser *null    = [Parcoa string:@"null"];
ParcoaParser *boolean = [Parcoa bool];
ParcoaParser *integer = [Parcoa integer];
ParcoaParser *pair    = [[string keepLeft: colon] then: json];
ParcoaParser *object  = [[[pair sepBy:comma] between:openBrace   and: closeBrace] dictionary];
ParcoaParser *list    =  [[json sepBy:comma] between:openBracket and: closeBracket];

[json setImplementation:[Parcoa choice:@[object, list, string, integer, boolean, null]]];

如果我们对这个解析器运行某些输入

ParcoaResult *result = [json parse:@"[{\"name\" : \"James\", \"age\" : 28, \"active\" : true}]"];
NSLog(@"%@", result.value);

我们得到本机Objective-C对象作为输出

2013-01-03 11:16:46.666 ParcoaJSONExample[20822:c07] (
        {
        Active = 1;
        Age = 28;
        Name = James;
    }
)

Paroca解析器在输入无效时会生成堆栈跟踪

NSString *input = @"[{\"name\" : \"James\", \"age\" : 28 \"active\" : true}]";
ParcoaResult *result = [json parse:input];
NSLog(@"%@", [result traceback:input]);
2013-01-04 00:20:55.691 ParcoaJSONExample[26758:c07] Line 1 Column 1: Expected one or more matches.
  Line 1 Column 1: Expected all parsers in sequence to match.
    Line 1 Column 2: Expected optional child parser matched.
      Line 1 Column 2: Expected one or more separated items.
        Line 1 Column 2: Expected all parsers in sequence to match.
          Line 1 Column 2: Expected one or more matches.
            Line 1 Column 2: Expected all parsers in sequence to match.
              Line 1 Column 31: Expected more matches of delimiter and child parser.
                Line 1 Column 31: Expected more matches of child parser.
                  Line 1 Column 31: Expected all parsers in sequence to match.
                    Line 1 Column 31: Expected all parsers in sequence to match.
                      Line 1 Column 32: Expected Character matching predicate inCharacterSet(whitespace).
                      Line 1 Column 32: Expected Character matching predicate isUnichar(',').
              Line 1 Column 31: Expected all parsers in sequence to match.
                Line 1 Column 32: Expected Character matching predicate inCharacterSet(whitespace).
                Line 1 Column 32: Expected Character matching predicate isUnichar('}').

解析器在第1行第32列遇到了无效输入,并期望一个空格、逗号,或关闭花括号}

贡献

Parcoa仍在积极开发中。一些当前的问题点

  • 有一些单元测试覆盖率。理想情况下所有原始解析器和组合器都将完全单元测试。
  • 尚未进行性能基准测试。
  • 大多数Parsec组合器已实现。如果在您的解析之旅中遇到重复出现的原始模式或组合器,请随时发送拉取请求。
  • 错误报告和堆栈跟踪目前对程序员而非最终用户友好。

安装

您可以从它的Git仓库克隆Parcoa或使用CocoaPods安装它。

我建议使用CocoaPods,因为所有您的链接、构建设置和头文件包含都将自动设置。如果您从GitHub克隆,您将不得不手动配置这些设置。

CocoaPod

安装并配置CocoaPods

在项目的根目录中创建一个名为Podfile的文件,包含以下内容

platform :ios
xcodeproj '<# YOUR PROJECT #>.xcodeproj'
pod 'Parcoa', :git => "https://github.com/brotchie/Parcoa.git"

然后运行

pod install

注意:在运行pod install之后,在Xcode中打开<# YOUR PROJECT #>.xcworkspace而不是<# YOUR PROJECT #>.xcodeproj

Git

Parcoa构建一个静态库libParcoa.a。库目标在Parcoa.xcodeproj中。要将Parcoa链接到您的目标

  1. 克隆Parcoa GitHub 仓库git clone https://github.com/brotchie/Parcoa.git
  2. Parcoa.xcodeproj拖放到您的XCode项目中。
  3. libParcoa.a(在OSX上为Parcoa OS X.dylib)添加到您的目标的链接二进制与库中的构建阶段选项卡。
  4. 确保Parcoa (Parcoa)(在OSX上为Parcoa OS X (Parcoa))位于您的目标的构建阶段选项卡中的目标依赖中。
  5. 将Parcoa目录添加为递归路径到用户头文件搜索路径,并在您的目标的构建设置选项卡上将始终搜索用户路径设置为YES
  6. 在您的目标的构建设置选项卡上添加-ObjC其他链接器标志中。

Git子模块

从项目的根目录开始,将Parcoa仓库克隆到Git子模块中

mkdir -p Submodules
git submodule add https://github.com/brotchie/Parcoa.git Submodules/Parcoa
git submodule update --init

然后按照上面Git部分下的步骤进行。