/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: <http://www.research.att.com/sw/tools/graphviz/license/source.html> If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ /* requires flex (i.e. not lex) */ %{ #pragma prototyped #include <grammar.h> #include <aghdr.h> #define GRAPH_EOF_TOKEN '@' /* lex class must be defined below */ /* this is a workaround for linux flex */ static int line_num = 1; static int html_nest = 0; /* nesting level for html strings */ static char* InputFile; static Agdisc_t *Disc; static void *Ifile; /* Reset line number */ void agreadline(int n) { line_num = n; } /* (Re)set file: */ void agsetfile(char* f) { InputFile = f; line_num = 1; } /* There is a hole here, because switching channels * requires pushing back whatever was previously read. * There probably is a right way of doing this. */ void aglexinit(Agdisc_t *disc, void *ifile) { Disc = disc; Ifile = ifile;} #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ((result = Disc->io->afread(Ifile, buf, max_size)) < 0) \ YY_FATAL_ERROR( "input in flex scanner failed" ) #endif /* buffer for arbitrary length strings (longer than BUFSIZ) */ static char *Sbuf,*Sptr,*Send; static void beginstr(void) { if (Sbuf == NIL(char*)) { Sbuf = malloc(BUFSIZ); Send = Sbuf + BUFSIZ; } Sptr = Sbuf; *Sptr = 0; } static void addstr(char *src) { char c; if (Sptr > Sbuf) Sptr--; do { do {c = *Sptr++ = *src++;} while (c && (Sptr < Send)); if (c) { long sz = Send - Sbuf; long off = Sptr - Sbuf; sz *= 2; Sbuf = (char*)realloc(Sbuf,sz); Send = Sbuf + sz; Sptr = Sbuf + off; } } while (c); } static void endstr(void) { yylval.str = (char*)agstrdup(Ag_G_global,Sbuf); } /* chkNum: * The regexp for NUMBER allows a terminating letter. * This way we can catch a number immediately followed by a name * and report this to the user. */ static int chkNum(void) { char c = yytext[yyleng-1]; /* last character */ if (!isdigit(c) && (c != '.')) { /* c is letter */ char buf[BUFSIZ]; sprintf(buf,"badly formed number '%s' in line %d\n",yytext,line_num); strcat (buf, "Splits into two name tokens"); agerror(AGERROR_SYNTAX,buf); return 1; } else return 0; } /* The LETTER class below consists of ascii letters, underscore, all non-ascii * characters. This allows identifiers to have characters from any * character set independent of locale. The downside is that, for certain * character sets, non-letter and, in fact, undefined characters will be * accepted. This is not likely and, from dot's stand, shouldn't do any * harm. (Presumably undefined characters will be ignored in display.) And, * it allows a greater wealth of names. */ %} GRAPH_EOF_TOKEN [@] LETTER [A-Za-z_\200-\377] DIGIT [0-9] NAME {LETTER}({LETTER}|{DIGIT})* NUMBER [-]?(({DIGIT}+(\.{DIGIT}*)?)|(\.{DIGIT}+)){LETTER}? ID ({NAME}|{NUMBER}) %x comment %x qstring %x hstring %% {GRAPH_EOF_TOKEN} return(EOF); <INITIAL,comment,qstring,hstring>\n line_num++; "/*" BEGIN(comment); <comment>[^*\n]* /* eat anything not a '*' */ <comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ <comment>"*"+"/" BEGIN(INITIAL); "//".* /* ignore C++-style comments */ "#".* /* ignore shell-like comments */ [ \t\r] /* ignore whitespace */ "node" return(T_node); /* see tokens in agcanonstr */ "edge" return(T_edge); "graph" return(T_graph); "digraph" return(T_digraph); "strict" return(T_strict); "subgraph" return(T_subgraph); "->"|"--" return(T_edgeop); {NAME} { yylval.str = (char*)agstrdup(Ag_G_global,yytext); return(T_atom); } {NUMBER} { if (chkNum()) yyless(yyleng-1); yylval.str = (char*)agstrdup(Ag_G_global,yytext); return(T_atom); } ["] BEGIN(qstring); beginstr(); <qstring>["] BEGIN(INITIAL); endstr(); return (T_qatom); <qstring>[\\][\n] line_num++; /* ignore escaped newlines */ <qstring>([^"\\]*|[\\].) addstr(yytext); [<] BEGIN(hstring); html_nest = 1; beginstr(); <hstring>[>] html_nest--; if (html_nest) addstr(yytext); else {BEGIN(INITIAL); endstr(); return (T_qatom);} <hstring>[<] html_nest++; addstr(yytext); <hstring>[\\][<] addstr("<"); <hstring>[\\][>] addstr(">"); <hstring>[\\][\n] line_num++; /* ignore escaped newlines */ <hstring>([^><\\]*|[\\].) addstr(yytext); . return (yytext[0]); %% void yyerror(char *str) { char buf[BUFSIZ]; if (InputFile) sprintf(buf,"%s:%d: %s in line %d near '%s'",InputFile, line_num, str,line_num,yytext); else sprintf(buf," %s in line %d near '%s'", str,line_num,yytext); agerror(AGERROR_SYNTAX,buf); } /* must be here to see flex's macro defns */ void aglexeof() { unput(GRAPH_EOF_TOKEN); } #ifndef YY_CALL_ONLY_ARG # define YY_CALL_ONLY_ARG void #endif int yywrap(YY_CALL_ONLY_ARG) { return 1; }