Skip to content

Commit

Permalink
Added JSON parser
Browse files Browse the repository at this point in the history
  • Loading branch information
beelsebob committed Mar 31, 2011
1 parent 9ae4c17 commit 24cb9a1
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 27 deletions.
16 changes: 16 additions & 0 deletions CoreParse.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
1F38820A1323F760000C8876 /* CPItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F3882081323F75F000C8876 /* CPItem.m */; };
1F38820D132432B2000C8876 /* NSSetFunctional.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F38820B132432A1000C8876 /* NSSetFunctional.h */; };
1F38820E132432B2000C8876 /* NSSetFunctional.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F38820C132432AA000C8876 /* NSSetFunctional.m */; };
1F45A2E413422E1300092D78 /* CPJSONParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F45A2E213422E1300092D78 /* CPJSONParser.h */; };
1F45A2E513422E1300092D78 /* CPJSONParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F45A2E313422E1300092D78 /* CPJSONParser.m */; };
1F4683891306AA8500407491 /* CPKeywordRecogniser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F4683861306AA8500407491 /* CPKeywordRecogniser.h */; settings = {ATTRIBUTES = (Public, ); }; };
1F46838A1306AA8500407491 /* CPKeywordRecogniser.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F4683871306AA8500407491 /* CPKeywordRecogniser.m */; };
1F46838B1306AA8500407491 /* CPTokenRecogniser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F4683881306AA8500407491 /* CPTokenRecogniser.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -140,6 +142,8 @@
1F3882081323F75F000C8876 /* CPItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPItem.m; sourceTree = "<group>"; };
1F38820B132432A1000C8876 /* NSSetFunctional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSSetFunctional.h; sourceTree = "<group>"; };
1F38820C132432AA000C8876 /* NSSetFunctional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSSetFunctional.m; sourceTree = "<group>"; };
1F45A2E213422E1300092D78 /* CPJSONParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPJSONParser.h; sourceTree = "<group>"; };
1F45A2E313422E1300092D78 /* CPJSONParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPJSONParser.m; sourceTree = "<group>"; };
1F4683861306AA8500407491 /* CPKeywordRecogniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CPKeywordRecogniser.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
1F4683871306AA8500407491 /* CPKeywordRecogniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CPKeywordRecogniser.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
1F4683881306AA8500407491 /* CPTokenRecogniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CPTokenRecogniser.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
Expand Down Expand Up @@ -244,6 +248,7 @@
1F50A93B1308600800C50D7D /* Grammar */,
1F530B841321690C00F52EB5 /* Syntax Tree */,
1F530B7B132155BB00F52EB5 /* Parsers */,
1F45A2E113422DD800092D78 /* Built In Parsers */,
1F0E8900130462F300537D04 /* Supporting Files */,
1F38820B132432A1000C8876 /* NSSetFunctional.h */,
1F38820C132432AA000C8876 /* NSSetFunctional.m */,
Expand Down Expand Up @@ -326,6 +331,15 @@
path = CPShiftReduceParsers;
sourceTree = "<group>";
};
1F45A2E113422DD800092D78 /* Built In Parsers */ = {
isa = PBXGroup;
children = (
1F45A2E213422E1300092D78 /* CPJSONParser.h */,
1F45A2E313422E1300092D78 /* CPJSONParser.m */,
);
path = "Built In Parsers";
sourceTree = "<group>";
};
1F46837A1306A56100407491 /* Token Types */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -439,6 +453,7 @@
1FB3EB30132BB31700ACC453 /* CPLR1Item.h in Headers */,
1FB81322132FEDAF0095982D /* CPTestWhiteSpaceIgnoringDelegate.h in Headers */,
1FB81326132FF16E0095982D /* CPTestMapCSSTokenisingDelegate.h in Headers */,
1F45A2E413422E1300092D78 /* CPJSONParser.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -581,6 +596,7 @@
1FB3EB3B132D096900ACC453 /* CPGrammarSymbol.m in Sources */,
1FB81323132FEDAF0095982D /* CPTestWhiteSpaceIgnoringDelegate.m in Sources */,
1FB81327132FF16E0095982D /* CPTestMapCSSTokenisingDelegate.m in Sources */,
1F45A2E513422E1300092D78 /* CPJSONParser.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
17 changes: 17 additions & 0 deletions CoreParse/Built In Parsers/CPJSONParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// CPJSONParser.h
// CoreParse
//
// Created by Tom Davie on 29/03/2011.
// Copyright 2011 In The Beginning... All rights reserved.
//

#import <Foundation/Foundation.h>


@interface CPJSONParser : NSObject
{}

- (id<NSObject>)parse:(NSString *)json;

@end
194 changes: 194 additions & 0 deletions CoreParse/Built In Parsers/CPJSONParser.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
//
// CPJSONParser.m
// CoreParse
//
// Created by Tom Davie on 29/03/2011.
// Copyright 2011 In The Beginning... All rights reserved.
//

#import "CPJSONParser.h"

#import "CPTokeniser.h"
#import "CPSLRParser.h"
#import "CPKeywordRecogniser.h"
#import "CPNumberRecogniser.h"
#import "CPWhiteSpaceRecogniser.h"
#import "CPQuotedRecogniser.h"

#import "CPKeywordToken.h"
#import "CPNumberToken.h"
#import "CPQuotedToken.h"
#import "CPWhiteSpaceToken.h"

@interface CPJSONParser () <CPParserDelegate, CPTokeniserDelegate>
@end

@implementation CPJSONParser
{
CPTokeniser *jsonTokeniser;
CPParser *jsonParser;
}

- (id)init
{
self = [super init];

if (nil != self)
{
jsonTokeniser = [[CPTokeniser alloc] init];
CPQuotedRecogniser *stringRecogniser = [CPQuotedRecogniser quotedRecogniserWithStartQuote:@"\"" endQuote:@"\"" escapeSequence:@"\\" name:@"String"];
[stringRecogniser setEscapeReplacer:^ NSString * (NSString *str, NSUInteger *loc)
{
if ([str length] > *loc)
{
switch ([str characterAtIndex:*loc])
{
case 'b':
*loc = *loc + 1;
return @"\b";
case 'f':
*loc = *loc + 1;
return @"\f";
case 'n':
*loc = *loc + 1;
return @"\n";
case 'r':
*loc = *loc + 1;
return @"\r";
case 't':
*loc = *loc + 1;
return @"\t";
default:
break;
}
}
return nil;
}];

[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"{"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"}"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"["]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"]"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@":"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@","]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"true"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"false"]];
[jsonTokeniser addTokenRecogniser:[CPKeywordRecogniser recogniserForKeyword:@"null"]];
[jsonTokeniser addTokenRecogniser:[CPNumberRecogniser numberRecogniser]];
[jsonTokeniser addTokenRecogniser:stringRecogniser];
[jsonTokeniser addTokenRecogniser:[CPWhiteSpaceRecogniser whiteSpaceRecogniser]];
[jsonTokeniser setDelegate:self];

CPGrammar *jsonGrammar = [CPGrammar grammarWithStart:@"value"
backusNaurForm:
@"0 value ::= \"String\";"
@"1 value ::= \"Number\";"
@"2 value ::= <object>;"
@"3 value ::= <array>;"
@"4 value ::= <boolean>;"
@"5 value ::= \"null\";"
@"6 object ::= \"{\" \"}\";"
@"7 object ::= \"{\" <members> \"}\";"
@"8 members ::= <pair>;"
@"9 members ::= <pair> \",\" <members>;"
@"10 pair ::= \"String\" \":\" <value>;"
@"11 array ::= \"[\" \"]\";"
@"12 array ::= \"[\" <elements> \"]\";"
@"13 elements ::= <value>;"
@"14 elements ::= <value> \",\" <elements>;"
@"15 boolean ::= \"true\";"
@"16 boolean ::= \"false\";"];
jsonParser = [[CPSLRParser parserWithGrammar:jsonGrammar] retain];
[jsonParser setDelegate:self];
}

return self;
}

- (void)dealloc
{
[jsonTokeniser release];
[jsonParser release];

[super dealloc];
}

- (id<NSObject>)parse:(NSString *)json
{
CPTokenStream *tokenStream = [jsonTokeniser tokenise:json];
return [jsonParser parse:tokenStream];
}

- (BOOL)tokeniser:(CPTokeniser *)tokeniser shouldConsumeToken:(CPToken *)token
{
return YES;
}

- (NSArray *)tokeniser:(CPTokeniser *)tokeniser willProduceToken:(CPToken *)token
{
if ([token isKindOfClass:[CPWhiteSpaceToken class]])
{
return [NSArray array];
}
return [NSArray arrayWithObject:token];
}

- (id)parser:(CPParser *)parser didProduceSyntaxTree:(CPSyntaxTree *)syntaxTree
{
NSArray *children = [syntaxTree children];
switch ([[syntaxTree rule] tag])
{
case 0:
return [(CPQuotedToken *)[children objectAtIndex:0] content];
case 1:
return [(CPNumberToken *)[children objectAtIndex:0] number];
case 2:
case 3:
case 4:
return [children objectAtIndex:0];
case 5:
return [NSNull null];
case 6:
return [NSDictionary dictionary];
case 7:
return [children objectAtIndex:1];
case 8:
{
NSDictionary *p = [children objectAtIndex:0];
return [NSMutableDictionary dictionaryWithObject:[p objectForKey:@"v"] forKey:[p objectForKey:@"k"]];
}
case 9:
{
NSDictionary *p = [children objectAtIndex:0];
NSMutableDictionary *ms = [children objectAtIndex:2];
[ms setObject:[p objectForKey:@"v"] forKey:[p objectForKey:@"k"]];
return ms;
}
case 10:
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[(CPQuotedToken *)[children objectAtIndex:0] content], @"k",
[children objectAtIndex:2], @"v",
nil];
}
case 11:
return [NSArray array];
case 12:
return [children objectAtIndex:1];
case 13:
return [NSMutableArray arrayWithObject:[children objectAtIndex:0]];
case 14:
{
NSMutableArray *es = [children objectAtIndex:2];
[es insertObject:[children objectAtIndex:0] atIndex:0];
return es;
}
case 15:
return [NSNumber numberWithBool:YES];
case 16:
return [NSNumber numberWithBool:NO];
}
return nil;
}

@end
2 changes: 2 additions & 0 deletions CoreParse/CoreParse.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@
#import "CPParser.h"
#import "CPSLRParser.h"
#import "CPLR1Parser.h"

#import "CPJSONParser.h"
76 changes: 49 additions & 27 deletions CoreParseTests/CoreParseTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -271,33 +271,33 @@ - (void)testQuotedTokeniser

tokeniser = [[[CPTokeniser alloc] init] autorelease];
CPQuotedRecogniser *rec = [CPQuotedRecogniser quotedRecogniserWithStartQuote:@"\"" endQuote:@"\"" escapeSequence:@"\\" name:@"String"];
rec.escapeReplacer = ^ NSString * (NSString *str, NSUInteger *loc)
{
if ([str length] > *loc)
{
switch ([str characterAtIndex:*loc])
{
case 'b':
*loc = *loc + 1;
return @"\b";
case 'f':
*loc = *loc + 1;
return @"\f";
case 'n':
*loc = *loc + 1;
return @"\n";
case 'r':
*loc = *loc + 1;
return @"\r";
case 't':
*loc = *loc + 1;
return @"\t";
default:
break;
}
}
return nil;
};
[rec setEscapeReplacer:^ NSString * (NSString *str, NSUInteger *loc)
{
if ([str length] > *loc)
{
switch ([str characterAtIndex:*loc])
{
case 'b':
*loc = *loc + 1;
return @"\b";
case 'f':
*loc = *loc + 1;
return @"\f";
case 'n':
*loc = *loc + 1;
return @"\n";
case 'r':
*loc = *loc + 1;
return @"\r";
case 't':
*loc = *loc + 1;
return @"\t";
default:
break;
}
}
return nil;
}];
[tokeniser addTokenRecogniser:rec];
tokenStream = [tokeniser tokenise:@"\"\\n\\r\\f\""];
if (![tokenStream isEqual:[CPTokenStream tokenStreamWithTokens:[NSArray arrayWithObjects:[CPQuotedToken content:@"\n\r\f" quotedWith:@"\"" name:@"String"], [CPEOFToken eof], nil]]])
Expand Down Expand Up @@ -489,5 +489,27 @@ - (void)testMapCSSParsing
STFail(@"Failed to parse MapCSS", nil);
}
}

- (void)testJSONParsing
{
CPJSONParser *jsonParser = [[[CPJSONParser alloc] init] autorelease];
id<NSObject> result = [jsonParser parse:@"{\"a\":\"b\", \"c\":true, \"d\":5.93, \"e\":[1,2,3], \"f\":null}"];

NSDictionary *expectedResult = [NSDictionary dictionaryWithObjectsAndKeys:
@"b" , @"a",
[NSNumber numberWithBool:YES] , @"c",
[NSNumber numberWithDouble:5.93] , @"d",
[NSArray arrayWithObjects:
[NSNumber numberWithDouble:1],
[NSNumber numberWithDouble:2],
[NSNumber numberWithDouble:3],
nil] , @"e",
[NSNull null] , @"f",
nil];
if (![result isEqual:expectedResult])
{
STFail(@"Failed to parse JSON", nil);
}
}

@end

0 comments on commit 24cb9a1

Please sign in to comment.