diff --git a/PEGKit.xcodeproj/project.pbxproj b/PEGKit.xcodeproj/project.pbxproj index d286c76..b7b78ad 100644 --- a/PEGKit.xcodeproj/project.pbxproj +++ b/PEGKit.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 3D0466A918E1D9770022A1BC /* OCMock.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D37214CA18DF3B0100525058 /* OCMock.framework */; }; + 40CA4045198C3E8B0094DF1F /* PGCLI.m in Sources */ = {isa = PBXBuildFile; fileRef = 40CA4044198C3E8B0094DF1F /* PGCLI.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; + 40CA4048198C54AB0094DF1F /* PGGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40CA4047198C54AB0094DF1F /* PGGenerator.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; D306298218E1ED5D00EF745E /* TDTestScaffold.m in Sources */ = {isa = PBXBuildFile; fileRef = D306298118E1ED5D00EF745E /* TDTestScaffold.m */; }; D3083AB61705F05C00DA6F95 /* elementsAssign.grammar in Resources */ = {isa = PBXBuildFile; fileRef = D3083AB51705F05C00DA6F95 /* elementsAssign.grammar */; }; D3083AB91705F09B00DA6F95 /* ElementAssignParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D3083AB81705F09B00DA6F95 /* ElementAssignParserTest.m */; }; @@ -478,6 +480,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 40CA4044198C3E8B0094DF1F /* PGCLI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGCLI.m; sourceTree = ""; }; + 40CA4046198C3F4C0094DF1F /* PGCLI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PGCLI.h; sourceTree = ""; }; + 40CA4047198C54AB0094DF1F /* PGGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGGenerator.m; sourceTree = ""; }; + 40CA4049198C55100094DF1F /* PGGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PGGenerator.h; sourceTree = ""; }; D302272A17020F9400594F16 /* PGClassImplementationTemplate.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = PGClassImplementationTemplate.txt; path = res/PGClassImplementationTemplate.txt; sourceTree = ""; }; D306298118E1ED5D00EF745E /* TDTestScaffold.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDTestScaffold.m; path = test/TDTestScaffold.m; sourceTree = ""; }; D3083AB51705F05C00DA6F95 /* elementsAssign.grammar */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = elementsAssign.grammar; path = res/elementsAssign.grammar; sourceTree = ""; }; @@ -1132,9 +1138,13 @@ isa = PBXGroup; children = ( D3383049171C923700CCE513 /* PGMainMenu.xib */, + 40CA4046198C3F4C0094DF1F /* PGCLI.h */, + 40CA4044198C3E8B0094DF1F /* PGCLI.m */, D3383043171C923700CCE513 /* PGDocument.h */, D3383044171C923700CCE513 /* PGDocument.m */, D3383046171C923700CCE513 /* PGDocument.xib */, + 40CA4049198C55100094DF1F /* PGGenerator.h */, + 40CA4047198C54AB0094DF1F /* PGGenerator.m */, D3B22A571703D03F00446945 /* PGTemplates */, D3A1492A16F8C79600770DEE /* visitor */, D325FFBC161E4E3200D4EBCC /* ast */, @@ -1899,6 +1909,7 @@ files = ( D376F6E318D0B5090064C888 /* PEGKitParser.m in Sources */, D376F6D018D0B3990064C888 /* PGDelimitedNode.m in Sources */, + 40CA4045198C3E8B0094DF1F /* PGCLI.m in Sources */, D338303E171C923700CCE513 /* main.m in Sources */, D376F6D418D0B3990064C888 /* PGMultipleNode.m in Sources */, D376F6CC18D0B3990064C888 /* PGConstantNode.m in Sources */, @@ -1912,6 +1923,7 @@ D376F6DF18D0B5020064C888 /* PGParserFactory.m in Sources */, D376F6DA18D0B3990064C888 /* PGReferenceNode.m in Sources */, D366C1AD1A5310F200D69669 /* PGNegationNode.m in Sources */, + 40CA4048198C54AB0094DF1F /* PGGenerator.m in Sources */, D376F6EE18D0B5190064C888 /* PGBaseVisitor.m in Sources */, D376F6DC18D0B3990064C888 /* PGRootNode.m in Sources */, D376F6C818D0B3990064C888 /* PGCollectionNode.m in Sources */, diff --git a/ParserGenApp/PGCLI.h b/ParserGenApp/PGCLI.h new file mode 100644 index 0000000..1393a65 --- /dev/null +++ b/ParserGenApp/PGCLI.h @@ -0,0 +1,24 @@ +// +// PGCLI.h +// PEGKit +// +// Created by Ewan Mellor on 7/29/14. +// +// + +#import + + +@interface PGCLI : NSObject + +/** + * @return true if there are command-line arguments that this class can handle. + */ +-(BOOL)willHandleCommandLine; + +/** + * @return The value that should be returned from main. + */ +-(int)handleCommandLine; + +@end diff --git a/ParserGenApp/PGCLI.m b/ParserGenApp/PGCLI.m new file mode 100644 index 0000000..408cf54 --- /dev/null +++ b/ParserGenApp/PGCLI.m @@ -0,0 +1,117 @@ +// +// PGCLI.m +// PEGKit +// +// Created by Ewan Mellor on 7/29/14. +// +// + +#import "PGGenerator.h" + +#import "PGCLI.h" + + +#define nsfprintf(__fp, ...) \ + fprintf(__fp, "%s\n", [[NSString stringWithFormat:__VA_ARGS__] UTF8String]) + + +@interface PGCLI () + +@property (nonatomic) PGGenerator * generator; +@property (nonatomic) NSString * grammarFile; + +@end + + +@implementation PGCLI + + +-(instancetype)init { + self = [super init]; + if (self) { + _generator = [[PGGenerator alloc] init]; + } + return self; +} + + +-(BOOL)willHandleCommandLine { + [self parseArgs]; + + return (self.grammarFile != nil); +} + + +-(int)handleCommandLine { + if (self.generator.destinationPath.length == 0 || self.grammarFile.length == 0) { + nsfprintf(stderr, + @"\n" + @"Usage:\n" + @"\n" + @" ParserGenApp -grammar -destPath \n" + @"\n" + @"Options are any combination of:\n" + @" -parserName \n" + @" -enableARC 1\n" + @" -enableAutomaticErrorRecovery 1\n" + @" -enableHybridDFA 1\n" + @" -enableMemoization 1\n"); + return 1; + } + + BOOL ok = [self loadGrammar]; + if (!ok) { + return 1; + } + + ok = [self generate]; + if (!ok) { + return 1; + } + + return 0; +} + + +-(void)parseArgs { + NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; + + self.grammarFile = [ud stringForKey:@"grammar"]; + + self.generator.destinationPath = [ud stringForKey:@"destPath"]; + if (self.generator.destinationPath == nil) { + self.generator.destinationPath = [ud stringForKey:@"destinationPath"]; + } + self.generator.enableARC = [ud boolForKey:@"enableARC"]; + self.generator.enableAutomaticErrorRecovery = [ud boolForKey:@"enableAutomaticErrorRecovery"]; + self.generator.enableHybridDFA = [ud boolForKey:@"enableHybridDFA"]; + self.generator.enableMemoization = [ud boolForKey:@"enableMemoization"]; + self.generator.parserName = [ud stringForKey:@"parserName"]; +} + + +-(BOOL)loadGrammar { + NSString * path = [self.grammarFile stringByExpandingTildeInPath]; + NSError * err = nil; + NSString * grammar = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err]; + if (grammar == nil) { + nsfprintf(stderr, NSLocalizedString(@"Failed to load grammar: %@", nil), [err localizedDescription]); + return NO; + } + else { + self.generator.grammar = grammar; + return YES; + } +} + + +-(BOOL)generate { + bool ok = [self.generator generate]; + if (!ok) { + nsfprintf(stderr, NSLocalizedString(@"Failed to generate: %@", nil), [self.generator.error localizedDescription]); + } + return ok; +} + + +@end diff --git a/ParserGenApp/PGDocument.m b/ParserGenApp/PGDocument.m index 4f1a243..2d1acab 100644 --- a/ParserGenApp/PGDocument.m +++ b/ParserGenApp/PGDocument.m @@ -23,21 +23,13 @@ #import "PGDocument.h" //#import #import "PGParserGenVisitor.h" - -@interface PGDocument () -@property (nonatomic, retain) PGParserFactory *factory; -@property (nonatomic, retain) PGRootNode *root; -@property (nonatomic, retain) PGParserGenVisitor *visitor; -@end +#import "PGGenerator.h" @implementation PGDocument - (instancetype)init { self = [super init]; if (self) { - self.factory = [PGParserFactory factory]; - _factory.collectTokenKinds = YES; - self.enableARC = YES; self.enableHybridDFA = YES; self.enableMemoization = YES; @@ -63,9 +55,6 @@ - (void)dealloc { self.textView = nil; - self.factory = nil; - self.root = nil; - self.visitor = nil; [super dealloc]; } @@ -154,7 +143,18 @@ - (IBAction)generate:(id)sender { self.error = nil; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self generateWithDestinationPath:destPath parserName:parserName grammar:grammar]; + PGGenerator * generator = [[PGGenerator alloc] init]; + generator.destinationPath = destPath; + generator.parserName = parserName; + generator.grammar = grammar; + generator.enableARC = self.enableARC; + generator.enableAutomaticErrorRecovery = self.enableAutomaticErrorRecovery; + generator.enableHybridDFA = self.enableHybridDFA; + generator.enableMemoization = self.enableMemoization; + [generator generate]; + dispatch_async(dispatch_get_main_queue(), ^(void){ + [self done]; + }); }); } @@ -223,70 +223,6 @@ - (IBAction)reveal:(id)sender { #pragma mark Private -- (void)generateWithDestinationPath:(NSString *)destPath parserName:(NSString *)parserName grammar:(NSString *)grammar { - NSError *err = nil; - self.root = (id)[_factory ASTFromGrammar:_grammar error:&err]; - if (err) { - self.error = err; - goto done; - } - - NSAssert([_root.startMethodName length], @""); - NSString *className = self.parserName; - NSAssert([className length], @""); - if (![className hasSuffix:@"Parser"]) { - className = [NSString stringWithFormat:@"%@Parser", className]; - } - - _root.grammarName = self.parserName; - - self.visitor = [[[PGParserGenVisitor alloc] init] autorelease]; - _visitor.enableARC = _enableARC; - _visitor.enableHybridDFA = _enableHybridDFA; //NSAssert(_enableHybridDFA, @""); - _visitor.enableMemoization = _enableMemoization; - _visitor.enableAutomaticErrorRecovery = _enableAutomaticErrorRecovery; - _visitor.delegatePreMatchCallbacksOn = _delegatePreMatchCallbacksOn; - _visitor.delegatePostMatchCallbacksOn = _delegatePostMatchCallbacksOn; - - @try { - [_root visit:_visitor]; - } - @catch (NSException *ex) { - id userInfo = @{NSLocalizedFailureReasonErrorKey: [ex reason]}; - NSError *err = [NSError errorWithDomain:[ex name] code:0 userInfo:userInfo]; - self.error = err; - goto done; - } - - NSString *path = [[NSString stringWithFormat:@"%@/%@.h", destPath, className] stringByExpandingTildeInPath]; - err = nil; - if (![_visitor.interfaceOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) { - NSMutableString *str = [NSMutableString stringWithString:[err localizedFailureReason]]; - [str appendFormat:@"\n\n%@", [path stringByDeletingLastPathComponent]]; - id dict = [NSMutableDictionary dictionaryWithDictionary:[err userInfo]]; - dict[NSLocalizedFailureReasonErrorKey] = str; - self.error = [NSError errorWithDomain:[err domain] code:[err code] userInfo:dict]; - goto done; - } - - path = [[NSString stringWithFormat:@"%@/%@.m", destPath, className] stringByExpandingTildeInPath]; - err = nil; - if (![_visitor.implementationOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) { - NSMutableString *str = [NSMutableString stringWithString:[err localizedFailureReason]]; - [str appendFormat:@"\n\n%@", [path stringByDeletingLastPathComponent]]; - id dict = [NSMutableDictionary dictionaryWithDictionary:[err userInfo]]; - dict[NSLocalizedFailureReasonErrorKey] = str; - self.error = [NSError errorWithDomain:[err domain] code:[err code] userInfo:dict]; - goto done; - } - -done: - dispatch_async(dispatch_get_main_queue(), ^(void){ - [self done]; - }); -} - - - (void)done { if (_error) { [[NSSound soundNamed:@"Basso"] play]; diff --git a/ParserGenApp/PGGenerator.h b/ParserGenApp/PGGenerator.h new file mode 100644 index 0000000..640c21e --- /dev/null +++ b/ParserGenApp/PGGenerator.h @@ -0,0 +1,30 @@ +// +// PGGenerator.h +// PEGKit +// +// Created by Ewan Mellor on 8/1/14. +// +// + +#import + +#import "PGParserFactory.h" + + +@interface PGGenerator : NSObject + +@property (nonatomic, strong) NSString * destinationPath; +@property (nonatomic, assign) BOOL enableARC; +@property (nonatomic, assign) BOOL enableAutomaticErrorRecovery; +@property (nonatomic, assign) BOOL enableHybridDFA; +@property (nonatomic, assign) BOOL enableMemoization; +@property (nonatomic, strong) NSString * parserName; +@property (nonatomic, strong) NSString * grammar; +@property (nonatomic, assign) PGParserFactoryDelegateCallbacksOn delegatePreMatchCallbacksOn; +@property (nonatomic, assign) PGParserFactoryDelegateCallbacksOn delegatePostMatchCallbacksOn; + +@property (nonatomic, strong) NSError * error; + +-(BOOL)generate; + +@end diff --git a/ParserGenApp/PGGenerator.m b/ParserGenApp/PGGenerator.m new file mode 100644 index 0000000..4684498 --- /dev/null +++ b/ParserGenApp/PGGenerator.m @@ -0,0 +1,151 @@ +// +// PGGenerator.m +// PEGKit +// +// Created by Ewan Mellor on 7/29/14. +// +// + +#import "PGParserGenVisitor.h" +#import "PGRootNode.h" + +#import "PGGenerator.h" + + +@interface PGGenerator () + +@property (nonatomic) PGParserFactory * factory; +@property (nonatomic) PGRootNode * root; +@property (nonatomic) PGParserGenVisitor * visitor; + +@end + + +@implementation PGGenerator + + +-(instancetype)init { + self = [super init]; + if (self) { + _factory = [PGParserFactory factory]; + _factory.collectTokenKinds = YES; + + _visitor = [[PGParserGenVisitor alloc] init]; + } + return self; +} + + +-(PGParserFactoryDelegateCallbacksOn)delegatePreMatchCallbacksOn { + return self.visitor.delegatePreMatchCallbacksOn; +} + +-(void)setDelegatePreMatchCallbacksOn:(PGParserFactoryDelegateCallbacksOn)delegatePreMatchCallbacksOn { + self.visitor.delegatePreMatchCallbacksOn = delegatePreMatchCallbacksOn; +} + +-(PGParserFactoryDelegateCallbacksOn)delegatePostMatchCallbacksOn { + return self.visitor.delegatePostMatchCallbacksOn; +} + +-(void)setDelegatePostMatchCallbacksOn:(PGParserFactoryDelegateCallbacksOn)delegatePostMatchCallbacksOn { + self.visitor.delegatePostMatchCallbacksOn = delegatePostMatchCallbacksOn; +} + + +-(BOOL)enableARC { + return self.visitor.enableARC; +} + +-(void)setEnableARC:(BOOL)enableARC { + self.visitor.enableARC = enableARC; +} + +-(BOOL)enableAutomaticErrorRecovery { + return self.visitor.enableAutomaticErrorRecovery; +} + +-(void)setEnableAutomaticErrorRecovery:(BOOL)enableAutomaticErrorRecovery { + self.visitor.enableAutomaticErrorRecovery = enableAutomaticErrorRecovery; +} + +-(BOOL)enableHybridDFA { + return self.visitor.enableHybridDFA; +} + +-(void)setEnableHybridDFA:(BOOL)enableHybridDFA { + self.visitor.enableHybridDFA = enableHybridDFA; +} + +-(BOOL)enableMemoization { + return self.visitor.enableMemoization; +} + +-(void)setEnableMemoization:(BOOL)enableMemoization { + self.visitor.enableMemoization = enableMemoization; +} + + +-(BOOL)generate { + assert(self.parserName.length > 0); + assert(self.destinationPath.length > 0); + + NSString *className = self.parserName; + if (![className hasSuffix:@"Parser"]) { + className = [NSString stringWithFormat:@"%@Parser", className]; + } + + NSError *err = nil; + self.root = (id)[self.factory ASTFromGrammar:self.grammar error:&err]; + if (err) { + self.error = err; + return NO; + } + + if (self.root.startMethodName.length == 0) { + NSString * msg = NSLocalizedString(@"Failed to find start method", nil); + self.error = [NSError errorWithDomain:@"PEGKit" code:0 userInfo:@{NSLocalizedFailureReasonErrorKey: msg}]; + return NO; + } + + self.root.grammarName = self.parserName; + + @try { + [self.root visit:self.visitor]; + } + @catch (NSException *ex) { + NSDictionary * userInfo = @{NSLocalizedFailureReasonErrorKey: ex.reason}; + self.error = [NSError errorWithDomain:ex.name code:0 userInfo:userInfo]; + return NO; + } + + NSString *path = [[NSString stringWithFormat:@"%@/%@.h", self.destinationPath, className] stringByExpandingTildeInPath]; + err = nil; + BOOL ok = [self.visitor.interfaceOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]; + if (!ok) { + NSString *str = [err.localizedFailureReason stringByAppendingFormat:@"\n\n%@", [path stringByDeletingLastPathComponent]]; + self.error = errorWithReason(err, str); + return NO; + } + + path = [[NSString stringWithFormat:@"%@/%@.m", self.destinationPath, className] stringByExpandingTildeInPath]; + err = nil; + ok = [self.visitor.implementationOutputString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]; + if (!ok) { + NSString *str = [err.localizedFailureReason stringByAppendingFormat:@"\n\n%@", [path stringByDeletingLastPathComponent]]; + self.error = errorWithReason(err, str); + return NO; + } + + return YES; +} + + +static NSError * errorWithReason(NSError * err, NSString * reason) { + NSMutableDictionary * userInfo = [NSMutableDictionary dictionaryWithDictionary:err.userInfo]; + userInfo[NSLocalizedFailureReasonErrorKey] = reason; + return [NSError errorWithDomain:err.domain code:err.code userInfo:userInfo]; +} + + +@end diff --git a/ParserGenApp/main.m b/ParserGenApp/main.m index 97fd09d..5b4f015 100644 --- a/ParserGenApp/main.m +++ b/ParserGenApp/main.m @@ -22,6 +22,15 @@ #import +#import "PGCLI.h" + int main(int argc, char *argv[]) { + @autoreleasepool { + PGCLI * cli = [[PGCLI alloc] init]; + if (cli.willHandleCommandLine) { + return [cli handleCommandLine]; + } + } + return NSApplicationMain(argc, (const char **)argv); }