Parcoa 是一个 Objective-C 解析器和解析器组合器的集合,由 Haskell 的 Parsec 软件包和 Python 的 Parcon 库启发而来。它遵循 MIT 许可协议。
ParcoaParser *
是围绕一个函数的轻量级包装,该函数block
接受一个NSString *
作为参数,尝试从该字符串解析一些值,然后返回一个OK
或Fail
结果。成功时,解析器block
返回解析的值,未消耗的残留输入以及指示允许解析器消耗更多信息输入的消息。失败时,解析器block
返回一个字符串描述,说明它在输入中期望找到什么。
考虑一个非常简单的解析器,它期望 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解析器。目前它仅支持整数文字。
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仍在积极开发中。一些当前的问题点
您可以从它的Git仓库克隆Parcoa或使用CocoaPods安装它。
我建议使用CocoaPods,因为所有您的链接、构建设置和头文件包含都将自动设置。如果您从GitHub克隆,您将不得不手动配置这些设置。
安装并配置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
。
Parcoa构建一个静态库libParcoa.a
。库目标在Parcoa.xcodeproj
中。要将Parcoa链接到您的目标
Parcoa
GitHub 仓库git clone https://github.com/brotchie/Parcoa.git
。Parcoa.xcodeproj
拖放到您的XCode项目中。libParcoa.a
(在OSX上为Parcoa OS X.dylib
)添加到您的目标的链接二进制与库
中的构建阶段
选项卡。Parcoa (Parcoa)
(在OSX上为Parcoa OS X (Parcoa)
)位于您的目标的构建阶段
选项卡中的目标依赖
中。用户头文件搜索路径
,并在您的目标的构建设置
选项卡上将始终搜索用户路径
设置为YES
。构建设置
选项卡上添加-ObjC
到其他链接器标志
中。从项目的根目录开始,将Parcoa
仓库克隆到Git子模块中
mkdir -p Submodules
git submodule add https://github.com/brotchie/Parcoa.git Submodules/Parcoa
git submodule update --init
然后按照上面Git部分下的步骤进行。