From 8cde489ab6c4169fb03d810447c18eea0d0eaa14 Mon Sep 17 00:00:00 2001 From: Alex Merry Date: Sat, 23 Mar 2013 03:30:19 +0000 Subject: Make the parser/lexer reentrant No more locking! Also, the interface for TikzGraphAssembler is much simpler. Changes to OSX code are completely untested. --- tikzit/src/common/TikzGraphAssembler+Parser.h | 14 +- tikzit/src/common/TikzGraphAssembler.h | 23 +-- tikzit/src/common/TikzGraphAssembler.m | 243 ++++++++++++++------------ tikzit/src/common/TikzShape.m | 6 +- tikzit/src/common/tikzlexer.lm | 114 ++++++------ tikzit/src/common/tikzparser.ym | 30 ++-- 6 files changed, 226 insertions(+), 204 deletions(-) (limited to 'tikzit/src/common') diff --git a/tikzit/src/common/TikzGraphAssembler+Parser.h b/tikzit/src/common/TikzGraphAssembler+Parser.h index 683e3e2..efceae9 100644 --- a/tikzit/src/common/TikzGraphAssembler+Parser.h +++ b/tikzit/src/common/TikzGraphAssembler+Parser.h @@ -25,9 +25,19 @@ #import "TikzGraphAssembler.h" @interface TikzGraphAssembler (Parser) +- (Graph*) graph; /** Store a node so that it can be looked up by name later */ -- (void)addNodeToMap:(Node*)n; -- (Node*)nodeWithName:(NSString*)name; +- (void) addNodeToMap:(Node*)n; +/** Get a previously-stored node by name */ +- (Node*) nodeWithName:(NSString*)name; +- (void) newLineStarted:(char *)text; +- (void) incrementPosBy:(size_t)amount; +- (void) invalidateWithError:(const char *)message; +- (void*) scanner; @end +#define YY_EXTRA_TYPE TikzGraphAssembler * +#define YYLEX_PARAM [assembler scanner] +void yyerror(TikzGraphAssembler *assembler, const char *str); + // vi:ft=objc:noet:ts=4:sts=4:sw=4 diff --git a/tikzit/src/common/TikzGraphAssembler.h b/tikzit/src/common/TikzGraphAssembler.h index 3fc4e9e..16fdf7f 100644 --- a/tikzit/src/common/TikzGraphAssembler.h +++ b/tikzit/src/common/TikzGraphAssembler.h @@ -26,24 +26,19 @@ @interface TikzGraphAssembler : NSObject { Graph *graph; + void *scanner; NSMutableDictionary *nodeMap; NSError *lastError; + char linebuff[500]; + int lineno; + size_t tokenpos; } -@property (readonly) Graph *graph; -@property (readonly) NSError *lastError; - -- (BOOL)parseTikz:(NSString*)tikz; -- (BOOL)parseTikz:(NSString*)tikz forGraph:(Graph*)gr; - -- (BOOL)testTikz:(NSString*)tikz; - -- (void)invalidate; -- (void)invalidateWithError:(NSError*)error; - -+ (void)setup; -+ (TikzGraphAssembler*)currentAssembler; -+ (TikzGraphAssembler*)assembler; ++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr error:(NSError**)e; ++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr; ++ (Graph*) parseTikz:(NSString*)tikz error:(NSError**)e; ++ (Graph*) parseTikz:(NSString*)tikz; ++ (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz; @end diff --git a/tikzit/src/common/TikzGraphAssembler.m b/tikzit/src/common/TikzGraphAssembler.m index 26bf515..5a01036 100644 --- a/tikzit/src/common/TikzGraphAssembler.m +++ b/tikzit/src/common/TikzGraphAssembler.m @@ -22,84 +22,72 @@ // #import "TikzGraphAssembler.h" +#import "TikzGraphAssembler+Parser.h" +#import "tikzparser.h" +#import "tikzlexer.h" #import "NSError+Tikzit.h" -extern int yyparse(void); -extern int yylex(void); -extern int yy_scan_string(const char* yy_str); -extern void yy_delete_buffer(int b); -extern int yylex_destroy(void); - - -static NSLock *parseLock = nil; -static id currentAssembler = nil; - -extern int yylineno; -extern int yyleng; -int lineno; -int tokenpos; -extern char *yystr; -char linebuff[500]; - - -void yyerror(const char *str) { - // if the error is on the first line, treat specially - if(lineno == 1){ -// strcpy(linebuff, yytext+1); - NSLog(@"Problem ahoy!"); - } - - NSLog(@"Parse error on line %i: %s\n%s\n%@\n", lineno, str, linebuff, [[@"" stringByPaddingToLength:(tokenpos-yyleng) withString: @" " startingAtIndex:0] stringByAppendingString:[@"" stringByPaddingToLength:yyleng withString: @"^" startingAtIndex:0]]); - if (currentAssembler != nil) { - NSError *error = [NSError errorWithDomain:@"net.sourceforge.tikzit" - code:TZ_ERR_PARSE - userInfo:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithCString:str encoding:NSUTF8StringEncoding], - [NSNumber numberWithInt:lineno], - [NSString stringWithCString:linebuff encoding:NSUTF8StringEncoding], - [NSNumber numberWithInt:tokenpos], - [NSNumber numberWithInt:yyleng], - nil] - forKeys: [NSArray arrayWithObjects:NSLocalizedDescriptionKey, - @"lineNumber", - @"syntaxString", - @"tokenStart", - @"tokenLength", - nil]]]; - - [currentAssembler invalidateWithError:error]; - } -} - -int yywrap() { - return 1; +void yyerror(TikzGraphAssembler *assembler, const char *str) { + [assembler invalidateWithError:str]; } @implementation TikzGraphAssembler - (id)init { + self = nil; + return nil; +} + +- (id)initWithGraph:(Graph*)g { self = [super init]; + if (self) { + graph = [g retain]; + nodeMap = [[NSMutableDictionary alloc] init]; + yylex_init (&scanner); + yyset_extra(self, scanner); + } return self; } - (void)dealloc { [graph release]; + [nodeMap release]; [lastError release]; + yylex_destroy (scanner); [super dealloc]; } -- (Graph*)graph { return graph; } -- (NSError*)lastError { return lastError; } - -- (BOOL)parseTikz:(NSString *)tikz { - return [self parseTikz:tikz forGraph:[Graph graph]]; ++ (BOOL) parseTikz:(NSString*)tikz forGraph:(Graph*)gr { + return [self parseTikz:tikz forGraph:gr error:NULL]; +} ++ (Graph*) parseTikz:(NSString*)tikz error:(NSError**)e { + Graph *gr = [[Graph alloc] init]; + if ([self parseTikz:tikz forGraph:gr error:e]) { + return [gr autorelease]; + } else { + [gr release]; + return nil; + } +} ++ (Graph*) parseTikz:(NSString*)tikz { + return [self parseTikz:tikz error:NULL]; } -- (BOOL)parseTikz:(NSString*)tikz forGraph:(Graph*)gr { - [parseLock lock]; - - lineno = 1; - tokenpos = 0; - NSRange range = [tikz rangeOfString:@"\n"]; ++ (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; @@ -112,49 +100,50 @@ int yywrap() { // first line too long; just terminate it at the end of the buffer linebuff[499] = 0; } + */ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - currentAssembler = self; - - // set the current graph - if (graph != gr) { - [graph release]; - graph = [gr retain]; - } - - // the node map keeps track of the mapping of names to nodes - nodeMap = [[NSMutableDictionary alloc] init]; - - // do the parsing if actual input - if([tikz length] > 0){ - yy_scan_string([tikz UTF8String]); - yyparse(); - yylex_destroy(); - } - - [nodeMap release]; - nodeMap = nil; + yy_scan_string([tikz UTF8String], [assembler scanner]); + int result = yyparse(assembler); - currentAssembler = nil; [pool drain]; - - [parseLock unlock]; - - return (graph != nil); + 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)testTikz:(NSString *)tikz{ ++ (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz { BOOL r; NSString * testTikz = [NSString stringWithFormat: @"{%@}", tikz]; - yy_scan_string([testTikz UTF8String]); - yylex(); - - r = !(yyleng < [testTikz length]); - + void *scanner; + yylex_init (&scanner); + yyset_extra(nil, scanner); + yy_scan_string([testTikz UTF8String], scanner); + YYSTYPE lval; + yylex(&lval, scanner); + r = !(yyget_leng(scanner) < [testTikz length]); + yylex_destroy(scanner); [testTikz autorelease]; - yylex_destroy(); return r; } @@ -165,32 +154,66 @@ int yywrap() { lastError = nil; } -- (void)invalidateWithError:(NSError*)error { - [self invalidate]; - lastError = [error retain]; -} - -+ (void)setup { - parseLock = [[NSLock alloc] init]; -} - -+ (TikzGraphAssembler*)currentAssembler { - return currentAssembler; -} - -+ (TikzGraphAssembler*)assembler { - return [[[TikzGraphAssembler alloc] init] autorelease]; -} - @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]; + 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) 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!"); + } + + 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]; + + lastError = [error retain]; + */ + [self invalidate]; } +- (void*) scanner { return scanner; } @end // vi:ft=objc:ts=4:noet:sts=4:sw=4 diff --git a/tikzit/src/common/TikzShape.m b/tikzit/src/common/TikzShape.m index b5b23c3..722275c 100644 --- a/tikzit/src/common/TikzShape.m +++ b/tikzit/src/common/TikzShape.m @@ -35,11 +35,7 @@ error:NULL]; if (tikz == nil) return nil; - TikzGraphAssembler *ass = [[TikzGraphAssembler alloc] init]; - [ass parseTikz:tikz]; - - Graph *graph = [ass graph]; - [ass release]; + Graph *graph = [TikzGraphAssembler parseTikz:tikz]; if (graph == nil) return nil; NSRect graphBounds = ([graph hasBoundingBox]) ? [graph boundingBox] : [graph bounds]; diff --git a/tikzit/src/common/tikzlexer.lm b/tikzit/src/common/tikzlexer.lm index ad99445..3e2e0ed 100644 --- a/tikzit/src/common/tikzlexer.lm +++ b/tikzit/src/common/tikzlexer.lm @@ -1,11 +1,3 @@ -%option nounput -%option yylineno -%s props -%s xcoord -%s ycoord -%s noderef -FLOAT \-?[0-9]*(\.[0-9]+)? - %{ /* * Copyright 2010 Chris Heunen @@ -28,96 +20,100 @@ FLOAT \-?[0-9]*(\.[0-9]+)? */ #import -#ifdef __APPLE__ -#include "y.tab.h" -#else -#include "tikzparser.h" -#endif - -extern char linebuff[500]; -extern int lineno; -extern int tokenpos; +#import "tikzparser.h" %} + +%option reentrant bison-bridge 8bit +%option nounput +%option yylineno +%option noyywrap +%option header-file="common/tikzlexer.h" + +%s props +%s xcoord +%s ycoord +%s noderef + +FLOAT \-?[0-9]*(\.[0-9]+)? + %% -%\n /* ignore end of line */; -\n.* { strncpy(linebuff, yytext+1, 500); - linebuff[499] = 0; // ensure null-terminated - lineno++; - tokenpos = 0; - yyless(1); - } -[ ]+ { tokenpos += yyleng; } /* ignore whitespace */; -[\t]+ { tokenpos += 8*yyleng; } /* ignore whitespace */; -\\begin\{tikzpicture\} { tokenpos += yyleng; return BEGIN_TIKZPICTURE_CMD; } -\\end\{tikzpicture\} { tokenpos += yyleng; return END_TIKZPICTURE_CMD; } -\\begin\{pgfonlayer\} { tokenpos += yyleng; return BEGIN_PGFONLAYER_CMD; } -\\end\{pgfonlayer\} { tokenpos += yyleng; return END_PGFONLAYER_CMD; } -\\draw { tokenpos += yyleng; return DRAW_CMD; } -\\node { tokenpos += yyleng; return NODE_CMD; } -\\path { tokenpos += yyleng; return PATH_CMD; } -rectangle { tokenpos += yyleng; return RECTANGLE; } -node { tokenpos += yyleng; return NODE; } -at { tokenpos += yyleng; return AT; } -to { tokenpos += yyleng; return TO; } -; { tokenpos += yyleng; return SEMICOLON; } + +\n.* { + [yyextra newLineStarted:yytext+1]; + yyless(1); +} +[ ]+ { [yyextra incrementPosBy:yyleng]; } /* ignore whitespace */; +[\t]+ { [yyextra incrementPosBy:8*yyleng]; } /* ignore whitespace */; +\\begin\{tikzpicture\} { [yyextra incrementPosBy:yyleng]; return BEGIN_TIKZPICTURE_CMD; } +\\end\{tikzpicture\} { [yyextra incrementPosBy:yyleng]; return END_TIKZPICTURE_CMD; } +\\begin\{pgfonlayer\} { [yyextra incrementPosBy:yyleng]; return BEGIN_PGFONLAYER_CMD; } +\\end\{pgfonlayer\} { [yyextra incrementPosBy:yyleng]; return END_PGFONLAYER_CMD; } +\\draw { [yyextra incrementPosBy:yyleng]; return DRAW_CMD; } +\\node { [yyextra incrementPosBy:yyleng]; return NODE_CMD; } +\\path { [yyextra incrementPosBy:yyleng]; return PATH_CMD; } +rectangle { [yyextra incrementPosBy:yyleng]; return RECTANGLE; } +node { [yyextra incrementPosBy:yyleng]; return NODE; } +at { [yyextra incrementPosBy:yyleng]; return AT; } +to { [yyextra incrementPosBy:yyleng]; return TO; } +; { [yyextra incrementPosBy:yyleng]; return SEMICOLON; } \([ ]*{FLOAT}[ ]*,[ ]*{FLOAT}[ ]*\) { - tokenpos += 1; + [yyextra incrementPosBy:1]; yyless(1); BEGIN(xcoord); } {FLOAT} { - tokenpos += yyleng; - yylval.pt.x=(float)strtod(yytext,NULL); + [yyextra incrementPosBy:yyleng]; + yylval->pt.x=(float)strtod(yytext,NULL); BEGIN(ycoord); } -, { tokenpos += yyleng; } +, { [yyextra incrementPosBy:yyleng]; } {FLOAT} { - tokenpos += yyleng; - yylval.pt.y=(float)strtod(yytext,NULL); + [yyextra incrementPosBy:yyleng]; + yylval->pt.y=(float)strtod(yytext,NULL); } \) { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; BEGIN(INITIAL); return COORD; } /* when we see "[", change parsing mode */ \[ /*syntaxhlfix]*/ { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; BEGIN(props); return LEFTBRACKET; } -= { tokenpos += yyleng; return EQUALS; } -, { tokenpos += yyleng; return COMMA; } += { [yyextra incrementPosBy:yyleng]; return EQUALS; } +, { [yyextra incrementPosBy:yyleng]; return COMMA; } [^=,\{\] \t]([^=,\{\]]*[^=,\{\] \t])? { - tokenpos += yyleng; - yylval.nsstr=[NSString stringWithUTF8String:yytext]; + [yyextra incrementPosBy:yyleng]; + yylval->nsstr=[NSString stringWithUTF8String:yytext]; return PROPSTRING; } \] { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; BEGIN(INITIAL); return RIGHTBRACKET; } \( { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; BEGIN(noderef); return LEFTPARENTHESIS; } \. { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; return FULLSTOP; } [^\.\{\)]+ { - tokenpos += yyleng; - yylval.nsstr=[NSString stringWithUTF8String:yytext]; + [yyextra incrementPosBy:yyleng]; + yylval->nsstr=[NSString stringWithUTF8String:yytext]; return REFSTRING; } \) { - tokenpos += yyleng; + [yyextra incrementPosBy:yyleng]; BEGIN(INITIAL); return RIGHTPARENTHESIS; } @@ -127,7 +123,7 @@ to { tokenpos += yyleng; return TO; } unsigned int brace_depth = 1; unsigned int escape = 0; while (1) { - char c = input(); + char c = input(yyscanner); // eof reached before closing brace if (c == '\0' || c == EOF) yyterminate(); @@ -147,8 +143,8 @@ to { tokenpos += yyleng; return TO; } NSString *s = [buf copy]; yyleng += 1 + [buf length]; [s autorelease]; - yylval.nsstr = s; - tokenpos += yyleng; + yylval->nsstr = s; + [yyextra incrementPosBy:yyleng]; return DELIMITEDSTRING; } diff --git a/tikzit/src/common/tikzparser.ym b/tikzit/src/common/tikzparser.ym index 074ec2d..1183f12 100644 --- a/tikzit/src/common/tikzparser.ym +++ b/tikzit/src/common/tikzparser.ym @@ -1,5 +1,3 @@ -%error-verbose - %{ /* * Copyright 2010 Chris Heunen @@ -21,18 +19,12 @@ * along with this program. If not, see . */ -#import "TikzGraphAssembler+Parser.h" #import "Edge.h" -extern char* yystr; -extern int yylineno; -extern int tokenpos; -extern int yylex(void); -extern void yyerror(const char *str); - %} %code requires { +#import "TikzGraphAssembler+Parser.h" #import "GraphElementData.h" #import "GraphElementProperty.h" #import "Node.h" @@ -42,6 +34,12 @@ struct noderef { }; } +%defines "common/tikzparser.h" +%pure-parser +%parse-param {TikzGraphAssembler *assembler} +%error-verbose + + %union { NSPoint pt; NSString *nsstr; @@ -51,6 +49,10 @@ struct noderef { struct noderef noderef; }; +%{ +#import "tikzlexer.h" +%} + %token BEGIN_TIKZPICTURE_CMD "\\begin{tikzpicture}" %token END_TIKZPICTURE_CMD "\\end{tikzpicture}" @@ -127,14 +129,14 @@ node: "\\node" optproperties nodename "at" COORD DELIMITEDSTRING ";" [node setName:$3]; [node setPoint:$5]; [node setLabel:$6]; - [[TikzGraphAssembler currentAssembler] addNodeToMap:node]; - [[[TikzGraphAssembler currentAssembler] graph] addNode:node]; + [assembler addNodeToMap:node]; + [[assembler graph] addNode:node]; }; optanchor: { $$ = nil; } | "." REFSTRING { $$ = $2; }; noderef: "(" REFSTRING optanchor ")" { - $$.node = [[TikzGraphAssembler currentAssembler] nodeWithName:$2]; + $$.node = [assembler nodeWithName:$2]; $$.anchor = $3; }; optnoderef: @@ -163,7 +165,7 @@ edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";" [edge setTargetAnchor:$3.anchor]; } [edge setAttributesFromData]; - [[[TikzGraphAssembler currentAssembler] graph] addEdge:edge]; + [[assembler graph] addEdge:edge]; }; ignoreprop: val | val "=" val; @@ -172,7 +174,7 @@ optignoreprops: "[" ignoreprops "]"; boundingbox: "\\path" optignoreprops COORD "rectangle" COORD ";" { - [[[TikzGraphAssembler currentAssembler] graph] setBoundingBox:NSRectAroundPoints($3, $5)]; + [[assembler graph] setBoundingBox:NSRectAroundPoints($3, $5)]; }; /* vi:ft=yacc:noet:ts=4:sts=4:sw=4 -- cgit v1.2.3