From e3e666cf4ffdb8d9005bad6f3a19cb616c25479f Mon Sep 17 00:00:00 2001 From: Todd Ditchendorf Date: Fri, 28 Mar 2014 09:37:13 -0500 Subject: [PATCH] * curly action test --- PEGKit.xcodeproj/project.pbxproj | 10 ++ res/curly_action.grammar | 3 +- test/CurlyActionParser.h | 13 +++ test/CurlyActionParser.m | 193 +++++++++++++++++++++++++++++++ test/CurlyActionParserTest.m | 87 ++++++++++++++ 5 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 test/CurlyActionParser.h create mode 100644 test/CurlyActionParser.m create mode 100644 test/CurlyActionParserTest.m diff --git a/PEGKit.xcodeproj/project.pbxproj b/PEGKit.xcodeproj/project.pbxproj index 591efd5..15095d9 100644 --- a/PEGKit.xcodeproj/project.pbxproj +++ b/PEGKit.xcodeproj/project.pbxproj @@ -364,6 +364,8 @@ D3D48D8218E5B484001B54E9 /* TableIndexSpecParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D48D8118E5B484001B54E9 /* TableIndexSpecParserTest.m */; }; D3D48D8518E5B500001B54E9 /* TableIndexSpecParser.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D48D8418E5B500001B54E9 /* TableIndexSpecParser.m */; }; D3D48D8718E5BCFC001B54E9 /* curly_action.grammar in Resources */ = {isa = PBXBuildFile; fileRef = D3D48D8618E5BCFC001B54E9 /* curly_action.grammar */; }; + D3D48D8A18E5BFD3001B54E9 /* CurlyActionParser.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D48D8918E5BFD3001B54E9 /* CurlyActionParser.m */; }; + D3D48D8C18E5BFFF001B54E9 /* CurlyActionParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D48D8B18E5BFFF001B54E9 /* CurlyActionParserTest.m */; }; D3E9B2CD1891714100AC0974 /* PKAST.m in Sources */ = {isa = PBXBuildFile; fileRef = D375DAC2173C645C00A5E050 /* PKAST.m */; }; D3E9B2CE1891714400AC0974 /* PKAST.h in Headers */ = {isa = PBXBuildFile; fileRef = D3A1492816F8C6BD00770DEE /* PKAST.h */; settings = {ATTRIBUTES = (Public, ); }; }; D3F8A40E175817DF00056188 /* NSArray+PEGKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D34BAFD30FF9E95500D7773A /* NSArray+PEGKitAdditions.h */; }; @@ -788,6 +790,9 @@ D3D48D8318E5B500001B54E9 /* TableIndexSpecParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TableIndexSpecParser.h; path = test/TableIndexSpecParser.h; sourceTree = ""; }; D3D48D8418E5B500001B54E9 /* TableIndexSpecParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TableIndexSpecParser.m; path = test/TableIndexSpecParser.m; sourceTree = ""; }; D3D48D8618E5BCFC001B54E9 /* curly_action.grammar */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = curly_action.grammar; path = res/curly_action.grammar; sourceTree = ""; }; + D3D48D8818E5BFD3001B54E9 /* CurlyActionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CurlyActionParser.h; path = test/CurlyActionParser.h; sourceTree = ""; }; + D3D48D8918E5BFD3001B54E9 /* CurlyActionParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CurlyActionParser.m; path = test/CurlyActionParser.m; sourceTree = ""; }; + D3D48D8B18E5BFFF001B54E9 /* CurlyActionParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CurlyActionParserTest.m; path = test/CurlyActionParserTest.m; sourceTree = ""; }; D3F0E2470FFE8EB900C9DF74 /* PKQuoteState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PKQuoteState.h; path = include/PEGKit/PKQuoteState.h; sourceTree = SOURCE_ROOT; }; D3F8A48D175817DF00056188 /* PEGKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PEGKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D3F8A4921758187300056188 /* PEGKit-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "PEGKit-Info.plist"; path = "res/PEGKit-Info.plist"; sourceTree = ""; }; @@ -978,6 +983,7 @@ D31BBCC618E4F1A50003FFA3 /* Tests */ = { isa = PBXGroup; children = ( + D3D48D8B18E5BFFF001B54E9 /* CurlyActionParserTest.m */, D31BBCBC18E4AAE20003FFA3 /* TableIndexParserTest.m */, D3D48D8118E5B484001B54E9 /* TableIndexSpecParserTest.m */, D31BBCAF18E4722F0003FFA3 /* CreateTableStmtParserTest.m */, @@ -1309,6 +1315,8 @@ D3B22A581703D07000446945 /* Parsers */ = { isa = PBXGroup; children = ( + D3D48D8818E5BFD3001B54E9 /* CurlyActionParser.h */, + D3D48D8918E5BFD3001B54E9 /* CurlyActionParser.m */, D31BBCBE18E4AAFC0003FFA3 /* TableIndexParser.h */, D31BBCBF18E4AAFC0003FFA3 /* TableIndexParser.m */, D3D48D8318E5B500001B54E9 /* TableIndexSpecParser.h */, @@ -1954,6 +1962,7 @@ D30B8F4A170CFAAD001796F4 /* NegationParserTest.m in Sources */, D30B8F53170CFF2B001796F4 /* PEGKitParserTest.m in Sources */, D30B8F59170CFFFE001796F4 /* DelimitedParserTest.m in Sources */, + D3D48D8C18E5BFFF001B54E9 /* CurlyActionParserTest.m in Sources */, D30B8F5E170D001E001796F4 /* DelimitedParser.m in Sources */, D30B8F65170D0EB0001796F4 /* PatternParserTest.m in Sources */, D30B8F6A170D0EEB001796F4 /* PatternParser.m in Sources */, @@ -1997,6 +2006,7 @@ D376F6D918D0B3990064C888 /* PGReferenceNode.m in Sources */, D31BBCF218E4F1EF0003FFA3 /* TDTokenizerTest.m in Sources */, D30DA1771724AD0100A1A3EC /* JSONParser.m in Sources */, + D3D48D8A18E5BFD3001B54E9 /* CurlyActionParser.m in Sources */, D30DA17A1724ADE700A1A3EC /* JSONParserTest.m in Sources */, D30DA18A1725B6A800A1A3EC /* RecoverySingleTokenDeletionTest.m in Sources */, D30DA18D1725BB5700A1A3EC /* RecoverySingleTokenInsertionTest.m in Sources */, diff --git a/res/curly_action.grammar b/res/curly_action.grammar index 802b08e..8ce29ea 100644 --- a/res/curly_action.grammar +++ b/res/curly_action.grammar @@ -1,4 +1,5 @@ -start = Word+ { +start = Word+ +{ while (!EMPTY()) { id word = POP_STR(); LOG(word); diff --git a/test/CurlyActionParser.h b/test/CurlyActionParser.h new file mode 100644 index 0000000..599588c --- /dev/null +++ b/test/CurlyActionParser.h @@ -0,0 +1,13 @@ +#import + +enum { + TABLEINDEX_TOKEN_KIND_BY = 14, + TABLEINDEX_TOKEN_KIND_INDEXED, + TABLEINDEX_TOKEN_KIND_NOT_UPPER, + TABLEINDEX_TOKEN_KIND_DOT, +}; + +@interface CurlyActionParser : PKParser + +@end + diff --git a/test/CurlyActionParser.m b/test/CurlyActionParser.m new file mode 100644 index 0000000..4df504c --- /dev/null +++ b/test/CurlyActionParser.m @@ -0,0 +1,193 @@ +#import "CurlyActionParser.h" +#import + +#define LT(i) [self LT:(i)] +#define LA(i) [self LA:(i)] +#define LS(i) [self LS:(i)] +#define LF(i) [self LD:(i)] + +#define POP() [self.assembly pop] +#define POP_STR() [self popString] +#define POP_TOK() [self popToken] +#define POP_BOOL() [self popBool] +#define POP_INT() [self popInteger] +#define POP_UINT() [self popUnsignedInteger] +#define POP_FLOAT() [self popFloat] +#define POP_DOUBLE() [self popDouble] + +#define PUSH(obj) [self.assembly push:(id)(obj)] +#define PUSH_BOOL(yn) [self pushBool:(BOOL)(yn)] +#define PUSH_INT(i) [self pushInteger:(NSInteger)(i)] +#define PUSH_UINT(u) [self pushUnsignedInteger:(NSUInteger)(u)] +#define PUSH_FLOAT(f) [self pushFloat:(float)(f)] +#define PUSH_DOUBLE(d) [self pushDouble:(double)(d)] + +#define EQ(a, b) [(a) isEqual:(b)] +#define NE(a, b) (![(a) isEqual:(b)]) +#define EQ_IGNORE_CASE(a, b) (NSOrderedSame == [(a) compare:(b)]) + +#define MATCHES(pattern, str) ([[NSRegularExpression regularExpressionWithPattern:(pattern) options:0 error:nil] numberOfMatchesInString:(str) options:0 range:NSMakeRange(0, [(str) length])] > 0) +#define MATCHES_IGNORE_CASE(pattern, str) ([[NSRegularExpression regularExpressionWithPattern:(pattern) options:NSRegularExpressionCaseInsensitive error:nil] numberOfMatchesInString:(str) options:0 range:NSMakeRange(0, [(str) length])] > 0) + +#define ABOVE(fence) [self.assembly objectsAbove:(fence)] +#define EMPTY() [self.assembly isStackEmpty] + +#define LOG(obj) do { NSLog(@"%@", (obj)); } while (0); +#define PRINT(str) do { printf("%s\n", (str)); } while (0); + +@interface PKParser () +@property (nonatomic, retain) NSMutableDictionary *tokenKindTab; +@property (nonatomic, retain) NSMutableArray *tokenKindNameTab; +@property (nonatomic, retain) NSString *startRuleName; +@property (nonatomic, retain) NSString *statementTerminator; +@property (nonatomic, retain) NSString *singleLineCommentMarker; +@property (nonatomic, retain) NSString *blockStartMarker; +@property (nonatomic, retain) NSString *blockEndMarker; +@property (nonatomic, retain) NSString *braces; + +- (BOOL)popBool; +- (NSInteger)popInteger; +- (double)popDouble; +- (PKToken *)popToken; +- (NSString *)popString; + +- (void)pushBool:(BOOL)yn; +- (void)pushInteger:(NSInteger)i; +- (void)pushDouble:(double)d; +@end + +@interface CurlyActionParser () +@end + +@implementation CurlyActionParser + +- (id)initWithDelegate:(id)d { + self = [super initWithDelegate:d]; + if (self) { + self.startRuleName = @"qualifiedTableName"; + self.tokenKindTab[@"BY"] = @(TABLEINDEX_TOKEN_KIND_BY); + self.tokenKindTab[@"INDEXED"] = @(TABLEINDEX_TOKEN_KIND_INDEXED); + self.tokenKindTab[@"NOT"] = @(TABLEINDEX_TOKEN_KIND_NOT_UPPER); + self.tokenKindTab[@"."] = @(TABLEINDEX_TOKEN_KIND_DOT); + + self.tokenKindNameTab[TABLEINDEX_TOKEN_KIND_BY] = @"BY"; + self.tokenKindNameTab[TABLEINDEX_TOKEN_KIND_INDEXED] = @"INDEXED"; + self.tokenKindNameTab[TABLEINDEX_TOKEN_KIND_NOT_UPPER] = @"NOT"; + self.tokenKindNameTab[TABLEINDEX_TOKEN_KIND_DOT] = @"."; + + } + return self; +} + +- (void)start { + [self qualifiedTableName_]; + [self matchEOF:YES]; +} + +- (void)qualifiedTableName_ { + + [self name_]; + [self indexOpt_]; + [self execute:(id)^{ + + // now stack contains 3 `NSString`s. + // ["mydb", "mytable", "foo"] + // NSString *indexName = POP(); + // NSString *tableName = POP(); + // NSString *dbName = POP(); + // do stuff here + + }]; + + [self fireDelegateSelector:@selector(parser:didMatchQualifiedTableName:)]; +} + +- (void)databaseName_ { + + [self matchWord:NO]; + + [self fireDelegateSelector:@selector(parser:didMatchDatabaseName:)]; +} + +- (void)tableName_ { + + [self matchWord:NO]; + + [self fireDelegateSelector:@selector(parser:didMatchTableName:)]; +} + +- (void)indexName_ { + + [self matchQuotedString:NO]; + + [self fireDelegateSelector:@selector(parser:didMatchIndexName:)]; +} + +- (void)name_ { + + if ([self speculate:^{ [self databaseName_]; [self match:TABLEINDEX_TOKEN_KIND_DOT discard:YES]; }]) { + [self databaseName_]; + [self match:TABLEINDEX_TOKEN_KIND_DOT discard:YES]; + } + [self tableName_]; + [self execute:(id)^{ + + // now stack contains 2 `PKToken`s of type Word + // [, ] + // pop their string values + NSString *tableName = POP_STR(); + NSString *dbName = POP_STR(); + PUSH(dbName); + PUSH(tableName); + + }]; + + [self fireDelegateSelector:@selector(parser:didMatchName:)]; +} + +- (void)indexOpt_ { + + if ([self predicts:TABLEINDEX_TOKEN_KIND_INDEXED, TABLEINDEX_TOKEN_KIND_NOT_UPPER, 0]) { + [self index_]; + } else { + [self matchEmpty:NO]; + [self execute:(id)^{ + PUSH(@""); + }]; + } + + [self fireDelegateSelector:@selector(parser:didMatchIndexOpt:)]; +} + +- (void)index_ { + + if ([self predicts:TABLEINDEX_TOKEN_KIND_INDEXED, 0]) { + [self match:TABLEINDEX_TOKEN_KIND_INDEXED discard:YES]; + [self match:TABLEINDEX_TOKEN_KIND_BY discard:YES]; + [self indexName_]; + [self execute:(id)^{ + + // now top of stack will be a Quoted String `PKToken` + // […, ] + // pop its string value + NSString *indexName = POP_STR(); + // trim quotes + indexName = [indexName substringWithRange:NSMakeRange(1, [indexName length]-2)]; + // leave it on the stack for later + PUSH(indexName); + + }]; + } else if ([self predicts:TABLEINDEX_TOKEN_KIND_NOT_UPPER, 0]) { + [self match:TABLEINDEX_TOKEN_KIND_NOT_UPPER discard:YES]; + [self match:TABLEINDEX_TOKEN_KIND_INDEXED discard:YES]; + [self execute:(id)^{ + PUSH(@""); + }]; + } else { + [self raise:@"No viable alternative found in rule 'index'."]; + } + + [self fireDelegateSelector:@selector(parser:didMatchIndex:)]; +} + +@end \ No newline at end of file diff --git a/test/CurlyActionParserTest.m b/test/CurlyActionParserTest.m new file mode 100644 index 0000000..27c7ec2 --- /dev/null +++ b/test/CurlyActionParserTest.m @@ -0,0 +1,87 @@ +#import "TDTestScaffold.h" +#import "PGParserFactory.h" +#import "PGParserGenVisitor.h" +#import "PGRootNode.h" +#import "CurlyActionParser.h" + +@interface CurlyActionParserTest : XCTestCase +@property (nonatomic, retain) PGParserFactory *factory; +@property (nonatomic, retain) PGRootNode *root; +@property (nonatomic, retain) PGParserGenVisitor *visitor; +@property (nonatomic, retain) CurlyActionParser *parser; +@property (nonatomic, retain) id mock; +@end + +@implementation CurlyActionParserTest + +- (void)parser:(PKParser *)p didFailToMatch:(PKAssembly *)a {} + +- (void)parser:(PKParser *)p didMatchLcurly:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchRcurly:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchName:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchColon:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchValue:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchComma:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchStructure:(PKAssembly *)a {} +- (void)parser:(PKParser *)p didMatchStructs:(PKAssembly *)a {} + +- (void)dealloc { + self.factory = nil; + self.root = nil; + self.visitor = nil; + self.parser = nil; + self.mock = nil; + [super dealloc]; +} + + +- (void)setUp { + self.factory = [PGParserFactory factory]; + _factory.collectTokenKinds = YES; + + NSError *err = nil; + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"curly_action" ofType:@"grammar"]; + NSString *g = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err]; + + err = nil; + self.root = (id)[_factory ASTFromGrammar:g error:&err]; + _root.grammarName = @"CurlyAction"; + + self.visitor = [[[PGParserGenVisitor alloc] init] autorelease]; + _visitor.enableMemoization = NO; + + [_root visit:_visitor]; + +#if TD_EMIT + path = [[NSString stringWithFormat:@"%s/test/CurlyActionParser.h", getenv("PWD")] stringByExpandingTildeInPath]; + err = nil; + if (![_visitor.interfaceOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) { + NSLog(@"%@", err); + } + + path = [[NSString stringWithFormat:@"%s/test/CurlyActionParser.m", getenv("PWD")] stringByExpandingTildeInPath]; + err = nil; + if (![_visitor.implementationOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) { + NSLog(@"%@", err); + } +#endif + + self.parser = [[[CurlyActionParser alloc] initWithDelegate:_mock] autorelease]; +} + +- (void)tearDown { + self.factory = nil; +} + + +- (void)testFooBarBaz { + NSString *s = @"foo bar baz"; + + NSError *err = nil; + PKAssembly *res = [_parser parseString:s error:&err]; + TDNil(err); + + TDEqualObjects(TDAssembly(@"[foo, bar, baz]foo/bar/baz^"), [res description]); +} + +@end