diff options
author | Alex Merry <dev@randomguy3.me.uk> | 2013-03-23 15:49:16 +0000 |
---|---|---|
committer | Alex Merry <dev@randomguy3.me.uk> | 2013-03-23 15:49:42 +0000 |
commit | 194806b17d3309202ddaf7a981ec02581984f033 (patch) | |
tree | 084fd3843f51364352112021453ee9450f977260 /tikzit/src/common/TikzGraphAssembler.m | |
parent | 8cde489ab6c4169fb03d810447c18eea0d0eaa14 (diff) |
Bring back parser/lexer error reporting
Even better than before!
Diffstat (limited to 'tikzit/src/common/TikzGraphAssembler.m')
-rw-r--r-- | tikzit/src/common/TikzGraphAssembler.m | 233 |
1 files changed, 129 insertions, 104 deletions
diff --git a/tikzit/src/common/TikzGraphAssembler.m b/tikzit/src/common/TikzGraphAssembler.m index 5a01036..a6f0e3d 100644 --- a/tikzit/src/common/TikzGraphAssembler.m +++ b/tikzit/src/common/TikzGraphAssembler.m @@ -22,15 +22,11 @@ // #import "TikzGraphAssembler.h" -#import "TikzGraphAssembler+Parser.h" #import "tikzparser.h" +#import "TikzGraphAssembler+Parser.h" #import "tikzlexer.h" #import "NSError+Tikzit.h" -void yyerror(TikzGraphAssembler *assembler, const char *str) { - [assembler invalidateWithError:str]; -} - @implementation TikzGraphAssembler - (id)init { @@ -57,6 +53,37 @@ void yyerror(TikzGraphAssembler *assembler, const char *str) { [super dealloc]; } +- (BOOL) parseTikz:(NSString*)t error:(NSError**)error { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + tikzStr = [t UTF8String]; + yy_scan_string(tikzStr, scanner); + int result = yyparse(self); + tikzStr = NULL; + + [pool drain]; + + if (result == 0) { + return YES; + } else { + if (error) { + if (lastError) { + *error = [[lastError retain] autorelease]; + } else if (result == 1) { + *error = [NSError errorWithMessage:@"Syntax error" + code:TZ_ERR_PARSE]; + } else if (result == 2) { + *error = [NSError errorWithMessage:@"Insufficient memory" + code:TZ_ERR_PARSE]; + } else { + *error = [NSError errorWithMessage:@"Unknown error" + code:TZ_ERR_PARSE]; + } + } + return NO; + } +} + + (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr { return [self parseTikz:tikz forGraph:gr error:NULL]; } @@ -74,60 +101,15 @@ void yyerror(TikzGraphAssembler *assembler, const char *str) { } + (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr error:(NSError**)error { - if([tikz length] == 0) { // empty string -> empty graph return YES; } - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; TikzGraphAssembler *assembler = [[self alloc] initWithGraph:gr]; - [assembler autorelease]; - - /* - lineno = 1; - tokenpos = 0; - NSRange range = [tikz rangeOfString:@"\n"]; - NSString *firstLine; - if (range.length == 0) { - firstLine = tikz; - } else { - firstLine = [tikz substringToIndex:range.location]; - } - if (![firstLine getCString:linebuff - maxLength:500 - encoding:NSUTF8StringEncoding]) { - // first line too long; just terminate it at the end of the buffer - linebuff[499] = 0; - } - */ - - yy_scan_string([tikz UTF8String], [assembler scanner]); - int result = yyparse(assembler); - - [pool drain]; - if (result == 0) { - return YES; - } else { - if (error) { - /* - if (lastError) { - *error = [[lastError retain] autorelease]; - } else - */ - if (result == 1) { - *error = [NSError errorWithMessage:@"Syntax error" - code:TZ_ERR_PARSE]; - } else if (result == 2) { - *error = [NSError errorWithMessage:@"Insufficient memory" - code:TZ_ERR_PARSE]; - } else { - *error = [NSError errorWithMessage:@"Unknown error" - code:TZ_ERR_PARSE]; - } - } - return NO; - } + BOOL success = [assembler parseTikz:tikz error:error]; + [assembler release]; + return success; } + (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz { @@ -140,7 +122,8 @@ void yyerror(TikzGraphAssembler *assembler, const char *str) { yyset_extra(nil, scanner); yy_scan_string([testTikz UTF8String], scanner); YYSTYPE lval; - yylex(&lval, scanner); + YYLTYPE lloc; + yylex(&lval, &lloc, scanner); r = !(yyget_leng(scanner) < [testTikz length]); yylex_destroy(scanner); [testTikz autorelease]; @@ -148,70 +131,112 @@ void yyerror(TikzGraphAssembler *assembler, const char *str) { return r; } -- (void)invalidate { - [graph release]; - graph = nil; - lastError = nil; -} - @end @implementation TikzGraphAssembler (Parser) - (Graph*)graph { return graph; } + - (void)addNodeToMap:(Node*)n { [nodeMap setObject:n forKey:[n name]]; } + - (Node*)nodeWithName:(NSString*)name { return [nodeMap objectForKey:name]; } -- (void) newLineStarted:(char *)text { - /* - strncpy(linebuff, yytext+1, 500); - linebuff[499] = 0; // ensure null-terminated - lineno++; - tokenpos = 0; - */ -} -- (void) incrementPosBy:(size_t)amount { - //tokenpos += amount; + +- (void) setLastError:(NSError*)error { + [error retain]; + [lastError release]; + lastError = error; } -- (void) invalidateWithError:(const char *)message { - /* - // if the error is on the first line, treat specially - if([assembler lineNumber] == 1){ - //strcpy(linebuff, yytext+1); - NSLog(@"Problem ahoy!"); - } +- (void) reportError:(const char *)message atLocation:(YYLTYPE*)yylloc { + NSString *nsmsg = [NSString stringWithUTF8String:message]; - NSString *pointerStrPad = [@"" stringByPaddingToLength:(tokenpos-yyleng) - withString:@" " - startingAtIndex:0]; - NSString *pointerStr = [@"" stringByPaddingToLength:yyleng - withString:@"^" - startingAtIndex:0]; - NSLog(@"Parse error on line %i: %s\n%s\n%@\n", lineno, str, linebuff, - [pointerStrPad stringByAppendingString:pointerStr]); - NSDictionary *userInfo = - [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String:str], - NSLocalizedDescriptionKey, - [NSNumber numberWithInt:lineno], - @"lineNumber", - [NSString stringWithUTF8String:linebuff], - @"syntaxString", - [NSNumber numberWithInt:tokenpos], - @"tokenStart", - [NSNumber numberWithInt:yyleng], - @"tokenLength"]; - NSError *error = - [NSError errorWithDomain:@"net.sourceforge.tikzit" - code:TZ_ERR_PARSE - userInfo:userInfo]; + const char *first_line_start = find_start_of_nth_line ( + tikzStr, yylloc->first_line - 1); + const char *last_line_start = find_start_of_nth_line ( + first_line_start, yylloc->last_line - yylloc->first_line); + const char *last_line_end = last_line_start; + while (*last_line_end && *last_line_end != '\n') { + // points to just after end of last line + ++last_line_end; + } + const char *error_start = first_line_start + (yylloc->first_column - 1); + const char *error_end = last_line_start + (yylloc->last_column - 1); - lastError = [error retain]; - */ - [self invalidate]; + if (error_start > error_end || error_end > last_line_end) { + // error position state is corrupted + NSLog(@"Got bad error state for error \"%s\": start(%i,%i), end(%i,%i)", + message, + yylloc->first_line, + yylloc->first_column, + yylloc->last_line, + yylloc->last_column); + [self setLastError:[NSError errorWithMessage:nsmsg + code:TZ_ERR_PARSE]]; + } else { + // +1 for null terminator + size_t error_text_len = last_line_end - first_line_start; + char *error_text = malloc (error_text_len + 1); + strncpy (error_text, first_line_start, error_text_len); + *(error_text + error_text_len) = '\0'; + + int error_start_pos = error_start - first_line_start; + int error_end_pos = error_end - first_line_start; + + NSDictionary *userInfo = + [NSDictionary dictionaryWithObjectsAndKeys: + nsmsg, + NSLocalizedDescriptionKey, + [NSNumber numberWithInt:yylloc->first_line], + @"startLine", + [NSNumber numberWithInt:yylloc->first_column], + @"startColumn", + [NSNumber numberWithInt:yylloc->last_line], + @"endLine", + [NSNumber numberWithInt:yylloc->last_column], + @"endColumn", + [NSString stringWithUTF8String:error_text], + @"syntaxString", + [NSNumber numberWithInt:error_start_pos], + @"tokenStart", + [NSNumber numberWithInt:error_end_pos], + @"tokenLength", + nil]; + [self setLastError: + [NSError errorWithDomain:TZErrorDomain + code:TZ_ERR_PARSE + userInfo:userInfo]]; + + // we can now freely edit error_text + // we only bother printing out the first line + if (yylloc->last_line > yylloc->first_line) { + char *nlp = strchr(error_text, '\n'); + if (nlp) { + *nlp = '\0'; + error_text_len = nlp - error_text; + if (error_end_pos > error_text_len) + error_end_pos = error_text_len; + } + } + NSString *pointerLinePadding = + [@"" stringByPaddingToLength:error_start_pos + withString:@" " + startingAtIndex:0]; + NSString *pointerLineCarets = + [@"" stringByPaddingToLength:(error_end_pos - error_start_pos) + withString:@"^" + startingAtIndex:0]; + NSLog(@"Parse error on line %i, starting at %i: %s\n%s\n%@%@", + yylloc->first_line, + yylloc->first_column, + message, + error_text, + pointerLinePadding, + pointerLineCarets); + free (error_text); + } } - (void*) scanner { return scanner; } @end |