-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
305 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
start = Word+ { | ||
start = Word+ | ||
{ | ||
while (!EMPTY()) { | ||
id word = POP_STR(); | ||
LOG(word); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#import <PEGKit/PKParser.h> | ||
|
||
enum { | ||
TABLEINDEX_TOKEN_KIND_BY = 14, | ||
TABLEINDEX_TOKEN_KIND_INDEXED, | ||
TABLEINDEX_TOKEN_KIND_NOT_UPPER, | ||
TABLEINDEX_TOKEN_KIND_DOT, | ||
}; | ||
|
||
@interface CurlyActionParser : PKParser | ||
|
||
@end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
#import "CurlyActionParser.h" | ||
#import <PEGKit/PEGKit.h> | ||
|
||
#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 | ||
// [<Word «mydb»>, <Word «mytable»>] | ||
// 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` | ||
// […, <Quoted String «"foo"»>] | ||
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |