Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
parse.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11
12/*
13 * Top-level parsing of gpr code into blocks
14 *
15 */
16
17#include <ast/ast.h>
18#include <ast/error.h>
19#include <cgraph/agxbuf.h>
20#include <cgraph/alloc.h>
21#include <cgraph/gv_ctype.h>
22#include <cgraph/unreachable.h>
23#include <gvpr/parse.h>
24#include <stdbool.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28
29static int lineno = 1; /* current line number */
30static int col0 = 1; /* true if char ptr is at column 0 */
31static int startLine = 1; /* set to start line of bracketd content */
32static int kwLine = 1; /* set to line of keyword */
33
34static char *case_str[] = {
35 "BEGIN",
36 "END",
37 "BEG_G",
38 "END_G",
39 "N",
40 "E",
41 "EOF",
42 "ERROR",
43};
44
45/* caseStr:
46 * Convert case_t to string.
47 */
48static char *caseStr(case_t cs)
49{
50 return case_str[(int) cs];
51}
52
53/* eol:
54 * Eat characters until eol.
55 */
56static int eol(FILE *str) {
57 int c;
58 while ((c = getc(str)) != '\n') {
59 if (c < 0)
60 return c;
61 }
62 lineno++;
63 col0 = 1;
64 return c;
65}
66
67/* readc:
68 * return character from input stream
69 * while keeping track of line number.
70 * Strip out comments, and return space or newline.
71 * If a newline is seen in comment and ostr
72 * is non-null, add newline to ostr.
73 */
74static int readc(FILE *str, agxbuf *ostr) {
75 int c;
76 int cc;
77
78 switch (c = getc(str)) {
79 case '\n':
80 lineno++;
81 col0 = 1;
82 break;
83 case '#':
84 if (col0) { /* shell comment */
85 c = eol (str);
86 }
87 else col0 = 0;
88 break;
89 case '/':
90 cc = getc(str);
91 switch (cc) {
92 case '*': /* in C comment */
93 while (1) {
94 switch (c = getc(str)) {
95 case '\n':
96 lineno++;
97 if (ostr)
98 agxbputc(ostr, (char)c);
99 break;
100 case '*':
101 switch (cc = getc(str)) {
102 case -1:
103 return cc;
104 break;
105 case '\n':
106 lineno++;
107 if (ostr)
108 agxbputc(ostr, (char)cc);
109 break;
110 case '*':
111 ungetc(cc, str);
112 break;
113 case '/':
114 col0 = 0;
115 return ' ';
116 break;
117 }
118 }
119 }
120 break;
121 case '/': /* in C++ comment */
122 c = eol (str);
123 break;
124 default: /* not a comment */
125 if (cc >= '\0')
126 ungetc(cc, str);
127 break;
128 }
129 break;
130 default:
131 col0 = 0;
132 break;
133 }
134 return c;
135}
136
137/* unreadc;
138 * push character back onto stream;
139 * if newline, reduce lineno.
140 */
141static void unreadc(FILE *str, int c) {
142 ungetc(c, str);
143 if (c == '\n')
144 lineno--;
145}
146
147/* skipWS:
148 */
149static int skipWS(FILE *str) {
150 int c;
151
152 while (true) {
153 c = readc(str, 0);
154 if (!gv_isspace(c)) {
155 return c;
156 }
157 }
158}
159
160/* parseID:
161 * Put initial alpha in buffer;
162 * add additional alphas, up to buffer size.
163 */
164static void parseID(FILE *str, int c, char *buf, size_t bsize) {
165 char *ptr = buf;
166 char *eptr = buf + (bsize - 1);
167
168 *ptr++ = (char)c;
169 while (true) {
170 c = readc(str, 0);
171 if (c < 0)
172 break;
173 if (gv_isalpha(c) || c == '_') {
174 if (ptr == eptr)
175 break;
176 *ptr++ = (char)c;
177 } else {
178 unreadc(str, c);
179 break;
180 }
181 }
182 *ptr = '\0';
183
184}
185
186#define BSIZE 8
187
188/* parseKind:
189 * Look for keywords: BEGIN, END, BEG_G, END_G, N, E
190 * As side-effect, sets kwLine to line of keyword.
191 */
192static case_t parseKind(FILE *str) {
193 int c;
194 char buf[BSIZE];
195 case_t cs = Error;
196
197 c = skipWS(str);
198 if (c < 0)
199 return Eof;
200 if (!gv_isalpha(c)) {
202 "expected keyword BEGIN/END/N/E...; found '%c', line %d", c,
203 lineno);
204 return Error;
205 }
206
207 kwLine = lineno;
208 parseID(str, c, buf, BSIZE);
209 if (strcmp(buf, "BEGIN") == 0) {
210 cs = Begin;
211 } else if (strcmp(buf, "BEG_G") == 0) {
212 cs = BeginG;
213 } else if (strcmp(buf, "E") == 0) {
214 cs = Edge;
215 } else if (strcmp(buf, "END") == 0) {
216 cs = End;
217 } else if (strcmp(buf, "END_G") == 0) {
218 cs = EndG;
219 } else if (strcmp(buf, "N") == 0) {
220 cs = Node;
221 }
222 if (cs == Error)
223 error(ERROR_ERROR, "unexpected keyword \"%s\", line %d", buf, kwLine);
224 return cs;
225}
226
227/* endString:
228 * eat characters from ins, putting them into outs,
229 * up to and including a terminating character ec
230 * that is not escaped with a back quote.
231 */
232static int endString(FILE *ins, agxbuf *outs, char ec) {
233 int sline = lineno;
234 int c;
235
236 while ((c = getc(ins)) != ec) {
237 if (c == '\\') {
238 agxbputc(outs, (char)c);
239 c = getc(ins);
240 }
241 if (c < 0) {
242 error(ERROR_ERROR, "unclosed string, start line %d", sline);
243 return c;
244 }
245 if (c == '\n')
246 lineno++;
247 agxbputc(outs, (char) c);
248 }
249 agxbputc(outs, (char)c);
250 return 0;
251}
252
253/* endBracket:
254 * eat characters from ins, putting them into outs,
255 * up to a terminating character ec.
256 * Strings are treated as atomic units: any ec in them
257 * is ignored. Since matching bc-ec pairs might nest,
258 * the function is called recursively.
259 */
260static int endBracket(FILE *ins, agxbuf *outs, char bc, char ec) {
261 int c;
262
263 while (true) {
264 c = readc(ins, outs);
265 if (c < 0 || c == ec)
266 return c;
267 else if (c == bc) {
268 agxbputc(outs, (char) c);
269 c = endBracket(ins, outs, bc, ec);
270 if (c < 0)
271 return c;
272 else
273 agxbputc(outs, (char) c);
274 } else if (c == '\'' || c == '"') {
275 agxbputc(outs, (char) c);
276 if (endString(ins, outs, (char)c)) return -1;
277 } else
278 agxbputc(outs, (char) c);
279 }
280}
281
282/* parseBracket:
283 * parse paired expression : bc <string> ec
284 * returning <string>
285 * As a side-effect, set startLine to beginning of content.
286 */
287static char *parseBracket(FILE *str, agxbuf *buf, int bc, int ec) {
288 int c;
289
290 c = skipWS(str);
291 if (c < 0)
292 return 0;
293 if (c != bc) {
294 unreadc(str, c);
295 return 0;
296 }
298 c = endBracket(str, buf, bc, (char)ec);
299 if (c < 0) {
300 if (!getErrorErrors())
302 "unclosed bracket %c%c expression, start line %d", bc, ec,
303 startLine);
304 return 0;
305 }
306 else
307 return agxbdisown(buf);
308}
309
310/* parseAction:
311 */
312static char *parseAction(FILE *str, agxbuf *buf) {
313 return parseBracket(str, buf, '{', '}');
314}
315
316/* parseGuard:
317 */
318static char *parseGuard(FILE *str, agxbuf *buf) {
319 return parseBracket(str, buf, '[', ']');
320}
321
322/* parseCase:
323 * Recognize
324 * BEGIN <optional action>
325 * END <optional action>
326 * BEG_G <optional action>
327 * END_G <optional action>
328 * N <optional guard> <optional action>
329 * E <optional guard> <optional action>
330 * where
331 * guard = '[' <expr> ']'
332 * action = '{' <expr> '}'
333 */
334static case_t parseCase(FILE *str, char **guard, int *gline, char **action,
335 int *aline)
336{
337 case_t kind;
338
339 agxbuf buf = {0};
340
341 kind = parseKind(str);
342 switch (kind) {
343 case Begin:
344 case BeginG:
345 case End:
346 case EndG:
347 *action = parseAction(str, &buf);
348 *aline = startLine;
349 if (getErrorErrors ())
350 kind = Error;
351 break;
352 case Edge:
353 case Node:
354 *guard = parseGuard(str, &buf);
355 *gline = startLine;
356 if (!getErrorErrors ()) {
357 *action = parseAction(str, &buf);
358 *aline = startLine;
359 }
360 if (getErrorErrors ())
361 kind = Error;
362 break;
363 case Eof:
364 case Error: /* to silence warnings */
365 break;
366 default:
367 UNREACHABLE();
368 }
369
370 agxbfree(&buf);
371 return kind;
372}
373
374/* addBlock:
375 * create new block and append to list;
376 * return new item as tail
377 */
378static parse_block *addBlock(parse_block *last, char *stmt, int line,
379 size_t n_nstmts, case_info *nodelist,
380 size_t n_estmts, case_info *edgelist) {
382
383 item->l_beging = line;
384 item->begg_stmt = stmt;
385 item->n_nstmts = n_nstmts;
386 item->n_estmts = n_estmts;
387 item->node_stmts = nodelist;
388 item->edge_stmts = edgelist;
389 if (last)
390 last->next = item;
391
392 return item;
393}
394
395/* addCase:
396 * create new case_info and append to list;
397 * return new item as tail
398 */
399static case_info *addCase(case_info * last, char *guard, int gline,
400 char *action, int line, size_t *cnt) {
401 if (!guard && !action) {
403 "Case with neither guard nor action, line %d - ignored", kwLine);
404 return last;
405 }
406
407 ++(*cnt);
408 case_info *item = gv_alloc(sizeof(case_info));
409 item->guard = guard;
410 item->action = action;
411 item->next = NULL;
412 if (guard)
413 item->gstart = gline;
414 if (action)
415 item->astart = line;
416
417 if (last)
418 last->next = item;
419
420 return item;
421}
422
423/* bindAction:
424 *
425 */
426static void
427bindAction(case_t cs, char *action, int aline, char **ap, int *lp)
428{
429 if (!action)
430 error(ERROR_WARNING, "%s with no action, line %d - ignored",
431 caseStr(cs), kwLine);
432 else if (*ap)
433 error(ERROR_ERROR, "additional %s section, line %d", caseStr(cs),
434 kwLine);
435 else {
436 *ap = action;
437 *lp = aline;
438 }
439}
440
441/* parseProg:
442 * Parses input into gpr sections.
443 */
444parse_prog *parseProg(char *input, int isFile)
445{
446 FILE *str;
447 char *guard = NULL;
448 char *action = NULL;
449 bool more;
450 parse_block *blocklist = NULL;
452 case_info *nodelist = NULL;
453 parse_block *blockl = NULL;
454 case_info *edgel = NULL;
455 case_info *nodel = NULL;
456 size_t n_blocks = 0;
457 size_t n_nstmts = 0;
458 size_t n_estmts = 0;
459 int line = 0, gline = 0;
460 int l_beging = 0;
461 char *begg_stmt;
462
463
464 lineno = col0 = startLine = kwLine = 1;
465 parse_prog *prog = calloc(1, sizeof(parse_prog));
466 if (!prog) {
467 error(ERROR_ERROR, "parseProg: out of memory");
468 return NULL;
469 }
470
471 if (isFile) {
472 str = fopen(input, "r");
473 prog->source = input;
474
475 } else {
476 str = tmpfile();
477 if (str != NULL) {
478 fputs(input, str);
479 rewind(str);
480 }
481 prog->source = NULL; /* command line */
482 }
483
484 if (!str) {
485 if (isFile)
486 error(ERROR_ERROR, "could not open %s for reading", input);
487 else
488 error(ERROR_ERROR, "parseProg : unable to create sfio stream");
489 free (prog);
490 return NULL;
491 }
492
493 begg_stmt = NULL;
494 more = true;
495 while (more) {
496 switch (parseCase(str, &guard, &gline, &action, &line)) {
497 case Begin:
498 bindAction(Begin, action, line, &prog->begin_stmt, &prog->l_begin);
499 break;
500 case BeginG:
501 if (action && (begg_stmt || nodelist || edgelist)) { /* non-empty block */
502 blockl = addBlock(blockl, begg_stmt, l_beging,
503 n_nstmts, nodelist, n_estmts, edgelist);
504 if (!blocklist)
505 blocklist = blockl;
506 n_blocks++;
507
508 /* reset values */
509 n_nstmts = n_estmts = 0;
510 edgel = nodel = edgelist = nodelist = NULL;
511 begg_stmt = NULL;
512 }
513 bindAction(BeginG, action, line, &begg_stmt, &l_beging);
514 break;
515 case End:
516 bindAction(End, action, line, &prog->end_stmt, &prog->l_end);
517 break;
518 case EndG:
519 bindAction(EndG, action, line, &prog->endg_stmt, &prog->l_endg);
520 break;
521 case Eof:
522 more = false;
523 break;
524 case Node:
525 nodel = addCase(nodel, guard, gline, action, line, &n_nstmts);
526 if (!nodelist)
527 nodelist = nodel;
528 break;
529 case Edge:
530 edgel = addCase(edgel, guard, gline, action, line, &n_estmts);
531 if (!edgelist)
532 edgelist = edgel;
533 break;
534 case Error: /* to silence warnings */
535 more = false;
536 break;
537 default:
538 UNREACHABLE();
539 }
540 }
541
542 if (begg_stmt || nodelist || edgelist) { /* non-empty block */
543 blockl = addBlock(blockl, begg_stmt, l_beging,
544 n_nstmts, nodelist, n_estmts, edgelist);
545 if (!blocklist)
546 blocklist = blockl;
547 n_blocks++;
548 }
549
550 prog->n_blocks = n_blocks;
551 prog->blocks = blocklist;
552
553 fclose(str);
554
555 if (getErrorErrors ()) {
556 freeParseProg (prog);
557 prog = NULL;
558 }
559
560 return prog;
561}
562
563static void
565{
566 case_info* nxt;
567 while (ip) {
568 nxt = ip->next;
569 free (ip->guard);
570 free (ip->action);
571 free (ip);
572 ip = nxt;
573 }
574}
575
576static void
578{
579 parse_block* nxt;
580 while (ip) {
581 nxt = ip->next;
582 free (ip->begg_stmt);
585 ip = nxt;
586 }
587}
588
589void
591{
592 if (!prog) return;
593 free (prog->begin_stmt);
594 freeBlocks (prog->blocks);
595 free (prog->endg_stmt);
596 free (prog->end_stmt);
597 free (prog);
598}
599
static agxbuf last
last message
Definition agerror.c:29
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
Memory allocation wrappers that exit on failure.
static void * gv_alloc(size_t size)
Definition alloc.h:47
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:149
Dt_t edgelist
Definition edgelist.h:24
void error(int level, const char *s,...)
Definition error.c:83
int getErrorErrors(void)
Definition error.c:32
#define ERROR_WARNING
Definition error.h:35
#define ERROR_ERROR
Definition error.h:36
static snode guard
Definition fPQ.c:21
void free(void *)
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
replacements for ctype.h functions
static bool gv_isalpha(int c)
Definition gv_ctype.h:29
static bool gv_isspace(int c)
Definition gv_ctype.h:55
static Agdesc_t kind
Definition gvpack.cpp:88
agxbuf * str
Definition htmlparse.c:97
static char * parseGuard(FILE *str, agxbuf *buf)
Definition parse.c:318
void freeParseProg(parse_prog *prog)
Definition parse.c:590
static int col0
Definition parse.c:30
static char * parseBracket(FILE *str, agxbuf *buf, int bc, int ec)
Definition parse.c:287
static void unreadc(FILE *str, int c)
Definition parse.c:141
#define BSIZE
Definition parse.c:186
static case_t parseKind(FILE *str)
Definition parse.c:192
static int lineno
Definition parse.c:29
static int readc(FILE *str, agxbuf *ostr)
Definition parse.c:74
static void freeCaseList(case_info *ip)
Definition parse.c:564
static void parseID(FILE *str, int c, char *buf, size_t bsize)
Definition parse.c:164
static parse_block * addBlock(parse_block *last, char *stmt, int line, size_t n_nstmts, case_info *nodelist, size_t n_estmts, case_info *edgelist)
Definition parse.c:378
static int endString(FILE *ins, agxbuf *outs, char ec)
Definition parse.c:232
static char * parseAction(FILE *str, agxbuf *buf)
Definition parse.c:312
static char * case_str[]
Definition parse.c:34
static case_t parseCase(FILE *str, char **guard, int *gline, char **action, int *aline)
Definition parse.c:334
static int eol(FILE *str)
Definition parse.c:56
static void freeBlocks(parse_block *ip)
Definition parse.c:577
static int startLine
Definition parse.c:31
static char * caseStr(case_t cs)
Definition parse.c:48
static case_info * addCase(case_info *last, char *guard, int gline, char *action, int line, size_t *cnt)
Definition parse.c:399
static int endBracket(FILE *ins, agxbuf *outs, char bc, char ec)
Definition parse.c:260
parse_prog * parseProg(char *input, int isFile)
Definition parse.c:444
static void bindAction(case_t cs, char *action, int aline, char **ap, int *lp)
Definition parse.c:427
static int kwLine
Definition parse.c:32
static int skipWS(FILE *str)
Definition parse.c:149
case_t
Definition parse.h:19
@ Error
Definition parse.h:20
@ End
Definition parse.h:20
@ Eof
Definition parse.h:20
@ Begin
Definition parse.h:19
@ BeginG
Definition parse.h:20
@ EndG
Definition parse.h:20
Definition edges.h:19
Definition node.h:24
char * guard
Definition parse.h:24
char * action
Definition parse.h:26
struct _case_info * next
Definition parse.h:27
Definition cdt.h:104
struct _parse_block * next
Definition parse.h:37
case_info * edge_stmts
Definition parse.h:36
case_info * node_stmts
Definition parse.h:35
char * begg_stmt
Definition parse.h:32
Definition utils.c:748
int l_end
Definition parse.h:42
int l_begin
Definition parse.h:42
int l_endg
Definition parse.h:42
size_t n_blocks
Definition parse.h:44
char * end_stmt
Definition parse.h:47
char * endg_stmt
Definition parse.h:46
parse_block * blocks
Definition parse.h:45
char * begin_stmt
Definition parse.h:43
char * source
Definition parse.h:41
#define UNREACHABLE()
Definition unreachable.h:30