]>
Commit | Line | Data |
---|---|---|
3eb9473e | 1 | /*\r |
2 | * astlib.c\r | |
3 | *\r | |
4 | * SOFTWARE RIGHTS\r | |
5 | *\r | |
6 | * We reserve no LEGAL rights to SORCERER -- SORCERER is in the public\r | |
7 | * domain. An individual or company may do whatever they wish with\r | |
8 | * source code distributed with SORCERER or the code generated by\r | |
9 | * SORCERER, including the incorporation of SORCERER, or its output, into\r | |
10 | * commerical software.\r | |
11 | *\r | |
12 | * We encourage users to develop software with SORCERER. However, we do\r | |
13 | * ask that credit is given to us for developing SORCERER. By "credit",\r | |
14 | * we mean that if you incorporate our source code into one of your\r | |
15 | * programs (commercial product, research project, or otherwise) that you\r | |
16 | * acknowledge this fact somewhere in the documentation, research report,\r | |
17 | * etc... If you like SORCERER and have developed a nice tool with the\r | |
18 | * output, please mention that you developed it using SORCERER. In\r | |
19 | * addition, we ask that this header remain intact in our source code.\r | |
20 | * As long as these guidelines are kept, we expect to continue enhancing\r | |
21 | * this system and expect to make other tools available as they are\r | |
22 | * completed.\r | |
23 | *\r | |
24 | * SORCERER 1.00B\r | |
25 | * Terence Parr\r | |
26 | * AHPCRC, University of Minnesota\r | |
27 | * 1992-1994\r | |
28 | */\r | |
29 | \r | |
30 | #include <stdio.h>\r | |
31 | #include "pcctscfg.h"\r | |
32 | #include <ctype.h>\r | |
33 | \r | |
34 | #define SORCERER_TRANSFORM\r | |
35 | \r | |
36 | #include "CASTBase.h"\r | |
37 | #include "astlib.h"\r | |
38 | \r | |
39 | #ifdef PCCTS_USE_STDARG\r | |
40 | #include <stdarg.h>\r | |
41 | #else\r | |
42 | #include <varargs.h>\r | |
43 | #endif\r | |
44 | \r | |
45 | /* String Scanning/Parsing Stuff */\r | |
46 | \r | |
47 | #define StringScanMaxText 50\r | |
48 | \r | |
49 | typedef struct stringlexer {\r | |
50 | #ifdef __USE_PROTOS\r | |
51 | signed int c;\r | |
52 | #else\r | |
53 | int c;\r | |
54 | #endif\r | |
55 | char *input;\r | |
56 | char *p;\r | |
57 | char text[StringScanMaxText];\r | |
58 | } StringLexer;\r | |
59 | \r | |
60 | #define LPAREN 1\r | |
61 | #define RPAREN 2\r | |
62 | #define PERCENT 3\r | |
63 | #define INT 4\r | |
64 | #define COLON 5\r | |
65 | #define POUND 6\r | |
66 | #define PERIOD 7\r | |
67 | #define StringScanEOF -1\r | |
68 | #define VALID_SCAN_TOKEN(t) (t>=LPAREN && t<=PERIOD)\r | |
69 | \r | |
70 | static char *scan_token_tbl[] = {\r | |
71 | "invalid", /* 0 */\r | |
72 | "LPAREN", /* 1 */\r | |
73 | "RPAREN", /* 2 */\r | |
74 | "PERCENT", /* 3 */\r | |
75 | "INT", /* 4 */\r | |
76 | "COLON", /* 5 */\r | |
77 | "POUND", /* 6 */\r | |
78 | "PERIOD", /* 7 */\r | |
79 | };\r | |
80 | \r | |
81 | char *\r | |
82 | #ifdef __USE_PROTOS\r | |
83 | scan_token_str(int t)\r | |
84 | #else\r | |
85 | scan_token_str(t)\r | |
86 | int t;\r | |
87 | #endif\r | |
88 | {\r | |
89 | if ( VALID_SCAN_TOKEN(t) ) return scan_token_tbl[t];\r | |
90 | else if ( t==StringScanEOF ) return "<end-of-string>";\r | |
91 | else return "<invalid-token>";\r | |
92 | }\r | |
93 | \r | |
94 | typedef struct stringparser {\r | |
95 | int token;\r | |
96 | StringLexer *lexer;\r | |
97 | int num_labels;\r | |
98 | } StringParser;\r | |
99 | \r | |
100 | /* This type ONLY USED by ast_scan() */\r | |
101 | \r | |
102 | typedef struct _scanast {\r | |
103 | struct _scanast *right, *down;\r | |
104 | int token;\r | |
105 | int label_num;\r | |
106 | } ScanAST;\r | |
107 | \r | |
108 | #ifdef __USE_PROTOS\r | |
109 | static void stringlexer_init(StringLexer *scanner, char *input);\r | |
110 | static void stringparser_init(StringParser *, StringLexer *);\r | |
111 | static ScanAST *stringparser_parse_scanast(char *templ, int *n);\r | |
112 | static ScanAST *stringparser_parse_tree(StringParser *parser);\r | |
113 | static ScanAST *stringparser_parse_element(StringParser *parser);\r | |
114 | static void stringscan_advance(StringLexer *scanner);\r | |
115 | static int stringscan_gettok(StringLexer *scanner);\r | |
116 | #else\r | |
117 | static void stringlexer_init();\r | |
118 | static void stringparser_init();\r | |
119 | static ScanAST *stringparser_parse_scanast();\r | |
120 | static ScanAST *stringparser_parse_tree();\r | |
121 | static ScanAST *stringparser_parse_element();\r | |
122 | static void stringscan_advance();\r | |
123 | static int stringscan_gettok();\r | |
124 | #endif\r | |
125 | \r | |
126 | /* build a tree (root child1 child2 ... NULL)\r | |
127 | * If root is NULL, simply make the children siblings and return ptr\r | |
128 | * to 1st sibling (child1). If root is not single node, return NULL.\r | |
129 | *\r | |
130 | * Siblings that are actually sibling lists themselves are handled\r | |
131 | * correctly. For example #( NULL, #( NULL, A, B, C), D) results\r | |
132 | * in the tree ( NULL A B C D ).\r | |
133 | *\r | |
134 | * Requires at least two parameters with the last one being NULL. If\r | |
135 | * both are NULL, return NULL.\r | |
136 | *\r | |
137 | * The ast_down and ast_right down/right pointers are used to make the tree.\r | |
138 | */\r | |
139 | SORAST *\r | |
140 | #ifdef PCCTS_USE_STDARG\r | |
141 | ast_make(SORAST *rt, ...)\r | |
142 | #else\r | |
143 | ast_make(va_alist)\r | |
144 | va_dcl\r | |
145 | #endif\r | |
146 | {\r | |
147 | va_list ap;\r | |
148 | register SORAST *child, *sibling=NULL, *tail = NULL, *w;\r | |
149 | SORAST *root;\r | |
150 | \r | |
151 | #ifdef PCCTS_USE_STDARG\r | |
152 | va_start(ap, rt);\r | |
153 | root = rt;\r | |
154 | #else\r | |
155 | va_start(ap);\r | |
156 | root = va_arg(ap, SORAST *);\r | |
157 | #endif\r | |
158 | \r | |
159 | if ( root != NULL )\r | |
160 | if ( root->ast_down != NULL ) return NULL;\r | |
161 | child = va_arg(ap, SORAST *);\r | |
162 | while ( child != NULL )\r | |
163 | {\r | |
164 | /* find end of child */\r | |
165 | for (w=child; w->ast_right!=NULL; w=w->ast_right) {;}\r | |
166 | if ( sibling == NULL ) {sibling = child; tail = w;}\r | |
167 | else {tail->ast_right = child; tail = w;}\r | |
168 | child = va_arg(ap, SORAST *);\r | |
169 | }\r | |
170 | if ( root==NULL ) root = sibling;\r | |
171 | else root->ast_down = sibling;\r | |
172 | va_end(ap);\r | |
173 | return root;\r | |
174 | }\r | |
175 | \r | |
176 | /* The following push and pop routines are only used by ast_find_all() */\r | |
177 | \r | |
178 | static void\r | |
179 | #ifdef __USE_PROTOS\r | |
180 | _push(SORAST **st, int *sp, SORAST *e)\r | |
181 | #else\r | |
182 | _push(st, sp, e)\r | |
183 | SORAST **st;\r | |
184 | int *sp;\r | |
185 | SORAST *e;\r | |
186 | #endif\r | |
187 | {\r | |
188 | (*sp)--;\r | |
189 | require((*sp)>=0, "stack overflow");\r | |
190 | st[(*sp)] = e;\r | |
191 | }\r | |
192 | \r | |
193 | static SORAST *\r | |
194 | #ifdef __USE_PROTOS\r | |
195 | _pop(SORAST **st, int *sp)\r | |
196 | #else\r | |
197 | _pop(st, sp)\r | |
198 | SORAST **st;\r | |
199 | int *sp;\r | |
200 | #endif\r | |
201 | {\r | |
202 | SORAST *e = st[*sp];\r | |
203 | (*sp)++;\r | |
204 | require((*sp)<=MaxTreeStackDepth, "stack underflow");\r | |
205 | return e;\r | |
206 | }\r | |
207 | \r | |
208 | /* Is 'u' a subtree of 't' beginning at the root? */\r | |
209 | int\r | |
210 | #ifdef __USE_PROTOS\r | |
211 | ast_match_partial(SORAST *t, SORAST *u)\r | |
212 | #else\r | |
213 | ast_match_partial(t, u)\r | |
214 | SORAST *t, *u;\r | |
215 | #endif\r | |
216 | {\r | |
217 | SORAST *sib;\r | |
218 | \r | |
219 | if ( u==NULL ) return 1;\r | |
220 | if ( t==NULL ) if ( u!=NULL ) return 0; else return 1;\r | |
221 | \r | |
222 | for (sib=t; sib!=NULL&&u!=NULL; sib=sib->ast_right, u=u->ast_right)\r | |
223 | {\r | |
224 | if ( sib->token != u->token ) return 0;\r | |
225 | if ( sib->ast_down!=NULL )\r | |
226 | if ( !ast_match_partial(sib->ast_down, u->ast_down) ) return 0;\r | |
227 | }\r | |
228 | return 1;\r | |
229 | }\r | |
230 | \r | |
231 | /* Find all occurrences of u in t.\r | |
232 | * 'cursor' must be initialized to 't'. It eventually\r | |
233 | * returns NULL when no more occurrences of 'u' are found.\r | |
234 | */\r | |
235 | SORAST *\r | |
236 | #ifdef __USE_PROTOS\r | |
237 | ast_find_all(SORAST *t, SORAST *u, SORAST **cursor)\r | |
238 | #else\r | |
239 | ast_find_all(t, u, cursor)\r | |
240 | SORAST *t, *u, **cursor;\r | |
241 | #endif\r | |
242 | {\r | |
243 | SORAST *sib;\r | |
244 | static SORAST *template_stack[MaxTreeStackDepth];\r | |
245 | static int tsp = MaxTreeStackDepth;\r | |
246 | \r | |
247 | if ( *cursor == NULL ) return NULL;\r | |
248 | if ( *cursor!=t ) sib = *cursor;\r | |
249 | else {\r | |
250 | /* else, first time--start at top of template 't' */\r | |
251 | tsp = MaxTreeStackDepth;\r | |
252 | sib = t;\r | |
253 | /* bottom of stack is always a NULL--"cookie" indicates "done" */\r | |
254 | _push(template_stack, &tsp, NULL);\r | |
255 | }\r | |
256 | \r | |
257 | keep_looking:\r | |
258 | if ( sib==NULL ) /* hit end of sibling list */\r | |
259 | {\r | |
260 | sib = _pop(template_stack, &tsp);\r | |
261 | if ( sib == NULL ) { *cursor = NULL; return NULL; }\r | |
262 | }\r | |
263 | \r | |
264 | if ( sib->token != u->token )\r | |
265 | {\r | |
266 | /* look for another match */\r | |
267 | if ( sib->ast_down!=NULL )\r | |
268 | {\r | |
269 | if ( sib->ast_right!=NULL ) _push(template_stack, &tsp, sib->ast_right);\r | |
270 | sib=sib->ast_down;\r | |
271 | goto keep_looking;\r | |
272 | }\r | |
273 | /* nothing below to try, try next sibling */\r | |
274 | sib=sib->ast_right;\r | |
275 | goto keep_looking;\r | |
276 | }\r | |
277 | \r | |
278 | /* found a matching root node, try to match what's below */\r | |
279 | if ( ast_match_partial(sib, u) )\r | |
280 | {\r | |
281 | /* record sibling cursor so we can pick up next from there */\r | |
282 | if ( sib->ast_down!=NULL )\r | |
283 | {\r | |
284 | if ( sib->ast_right!=NULL ) _push(template_stack, &tsp, sib->ast_right);\r | |
285 | *cursor = sib->ast_down;\r | |
286 | }\r | |
287 | else if ( sib->ast_right!=NULL ) *cursor = sib->ast_right;\r | |
288 | else *cursor = _pop(template_stack, &tsp);\r | |
289 | return sib;\r | |
290 | }\r | |
291 | \r | |
292 | /* no match, keep searching */\r | |
293 | if ( sib->ast_down!=NULL )\r | |
294 | {\r | |
295 | if ( sib->ast_right!=NULL ) _push(template_stack, &tsp, sib->ast_right);\r | |
296 | sib=sib->ast_down;\r | |
297 | }\r | |
298 | else sib = sib->ast_right; /* else, try to right if zip below */\r | |
299 | goto keep_looking;\r | |
300 | }\r | |
301 | \r | |
302 | /* are two trees exactly alike? */\r | |
303 | int\r | |
304 | #ifdef __USE_PROTOS\r | |
305 | ast_match(SORAST *t, SORAST *u)\r | |
306 | #else\r | |
307 | ast_match(t, u)\r | |
308 | SORAST *t, *u;\r | |
309 | #endif\r | |
310 | {\r | |
311 | SORAST *sib;\r | |
312 | \r | |
313 | if ( t==NULL ) if ( u!=NULL ) return 0; else return 1;\r | |
314 | if ( u==NULL ) return 0;\r | |
315 | \r | |
316 | for (sib=t; sib!=NULL&&u!=NULL; sib=sib->ast_right, u=u->ast_right)\r | |
317 | {\r | |
318 | if ( sib->token != u->token ) return 0;\r | |
319 | if ( sib->ast_down!=NULL )\r | |
320 | if ( !ast_match(sib->ast_down, u->ast_down) ) return 0;\r | |
321 | }\r | |
322 | return 1;\r | |
323 | }\r | |
324 | \r | |
325 | static int\r | |
326 | #ifdef __USE_PROTOS\r | |
327 | ast_scanmatch(ScanAST *t, SORAST *u, SORAST **labels[], int *n)\r | |
328 | #else\r | |
329 | ast_scanmatch(t, u, labels, n)\r | |
330 | ScanAST *t;\r | |
331 | SORAST *u;\r | |
332 | SORAST **labels[];\r | |
333 | int *n;\r | |
334 | #endif\r | |
335 | {\r | |
336 | ScanAST *sib;\r | |
337 | \r | |
338 | if ( t==NULL ) if ( u!=NULL ) return 0; else return 1;\r | |
339 | if ( u==NULL ) return 0;\r | |
340 | \r | |
341 | for (sib=t; sib!=NULL&&u!=NULL; sib=sib->right, u=u->ast_right)\r | |
342 | {\r | |
343 | /* make sure tokens match; token of '0' means wildcard match */\r | |
344 | if ( sib->token != u->token && sib->token!=0 ) return 0;\r | |
345 | /* we have a matched token here; set label pointers if exists */\r | |
346 | if ( sib->label_num>0 )\r | |
347 | {\r | |
348 | require(labels!=NULL, "label found in template, but no array of labels");\r | |
349 | (*n)++;\r | |
350 | *(labels[sib->label_num-1]) = u;\r | |
351 | }\r | |
352 | /* match what's below if something there and current node is not wildcard */\r | |
353 | if ( sib->down!=NULL && sib->token!=0 )\r | |
354 | if ( !ast_scanmatch(sib->down, u->ast_down, labels, n) ) return 0;\r | |
355 | }\r | |
356 | return 1;\r | |
357 | }\r | |
358 | \r | |
359 | void\r | |
360 | #ifdef __USE_PROTOS\r | |
361 | ast_insert_after(SORAST *a, SORAST *b)\r | |
362 | #else\r | |
363 | ast_insert_after(a, b)\r | |
364 | SORAST *a,*b;\r | |
365 | #endif\r | |
366 | {\r | |
367 | SORAST *end;\r | |
368 | require(a!=NULL, "ast_insert_after: NULL input tree");\r | |
369 | if ( b==NULL ) return;\r | |
370 | /* find end of b's child list */\r | |
371 | for (end=b; end->ast_right!=NULL; end=end->ast_right) {;}\r | |
372 | end->ast_right = a->ast_right;\r | |
373 | a->ast_right = b;\r | |
374 | }\r | |
375 | \r | |
376 | void\r | |
377 | #ifdef __USE_PROTOS\r | |
378 | ast_append(SORAST *a, SORAST *b)\r | |
379 | #else\r | |
380 | ast_append(a, b)\r | |
381 | SORAST *a,*b;\r | |
382 | #endif\r | |
383 | {\r | |
384 | SORAST *end;\r | |
385 | require(a!=NULL&&b!=NULL, "ast_append: NULL input tree");\r | |
386 | /* find end of child list */\r | |
387 | for (end=a; end->ast_right!=NULL; end=end->ast_right) {;}\r | |
388 | end->ast_right = b;\r | |
389 | }\r | |
390 | \r | |
391 | SORAST *\r | |
392 | #ifdef __USE_PROTOS\r | |
393 | ast_tail(SORAST *a)\r | |
394 | #else\r | |
395 | ast_tail(a)\r | |
396 | SORAST *a;\r | |
397 | #endif\r | |
398 | {\r | |
399 | SORAST *end;\r | |
400 | require(a!=NULL, "ast_tail: NULL input tree");\r | |
401 | /* find end of child list */\r | |
402 | for (end=a; end->ast_right!=NULL; end=end->ast_right) {;}\r | |
403 | return end;\r | |
404 | }\r | |
405 | \r | |
406 | SORAST *\r | |
407 | #ifdef __USE_PROTOS\r | |
408 | ast_bottom(SORAST *a)\r | |
409 | #else\r | |
410 | ast_bottom(a)\r | |
411 | SORAST *a;\r | |
412 | #endif\r | |
413 | {\r | |
414 | SORAST *end;\r | |
415 | require(a!=NULL, "ast_bottom: NULL input tree");\r | |
416 | /* find end of child list */\r | |
417 | for (end=a; end->ast_down!=NULL; end=end->ast_down) {;}\r | |
418 | return end;\r | |
419 | }\r | |
420 | \r | |
421 | SORAST *\r | |
422 | #ifdef __USE_PROTOS\r | |
423 | ast_cut_between(SORAST *a, SORAST *b)\r | |
424 | #else\r | |
425 | ast_cut_between(a, b)\r | |
426 | SORAST *a,*b;\r | |
427 | #endif\r | |
428 | {\r | |
429 | SORAST *end, *ret;\r | |
430 | require(a!=NULL&&b!=NULL, "ast_cut_between: NULL input tree");\r | |
431 | /* find node pointing to b */\r | |
432 | for (end=a; end->ast_right!=NULL&&end->ast_right!=b; end=end->ast_right)\r | |
433 | {;}\r | |
434 | require(end->ast_right!=NULL, "ast_cut_between: a,b not connected");\r | |
435 | end->ast_right = NULL; /* don't want it point to 'b' anymore */\r | |
436 | ret = a->ast_right;\r | |
437 | a->ast_right = b;\r | |
438 | return ret;\r | |
439 | }\r | |
440 | \r | |
441 | SList *\r | |
442 | #ifdef __USE_PROTOS\r | |
443 | ast_to_slist(SORAST *t)\r | |
444 | #else\r | |
445 | ast_to_slist(t)\r | |
446 | SORAST *t;\r | |
447 | #endif\r | |
448 | {\r | |
449 | SList *list=NULL;\r | |
450 | SORAST *p;\r | |
451 | \r | |
452 | for (p=t; p!=NULL; p=p->ast_right)\r | |
453 | {\r | |
454 | slist_add(&list, p);\r | |
455 | }\r | |
456 | return list;\r | |
457 | }\r | |
458 | \r | |
459 | SORAST *\r | |
460 | #ifdef __USE_PROTOS\r | |
461 | slist_to_ast(SList *list)\r | |
462 | #else\r | |
463 | slist_to_ast(list)\r | |
464 | SList *list;\r | |
465 | #endif\r | |
466 | {\r | |
467 | SORAST *t=NULL, *last=NULL;\r | |
468 | SList *p;\r | |
469 | \r | |
470 | for (p = list->next; p!=NULL; p=p->next)\r | |
471 | {\r | |
472 | SORAST *u = (SORAST *)p->elem;\r | |
473 | if ( last==NULL ) last = t = u;\r | |
474 | else { last->ast_right = u; last = u; }\r | |
475 | }\r | |
476 | return t;\r | |
477 | }\r | |
478 | \r | |
479 | void\r | |
480 | #ifdef __USE_PROTOS\r | |
481 | ast_free(SORAST *t)\r | |
482 | #else\r | |
483 | ast_free(t)\r | |
484 | SORAST *t;\r | |
485 | #endif\r | |
486 | {\r | |
487 | if ( t == NULL ) return;\r | |
488 | ast_free( t->ast_down );\r | |
489 | ast_free( t->ast_right );\r | |
490 | free( t );\r | |
491 | }\r | |
492 | \r | |
493 | int\r | |
494 | #ifdef __USE_PROTOS\r | |
495 | ast_nsiblings(SORAST *t)\r | |
496 | #else\r | |
497 | ast_nsiblings(t)\r | |
498 | SORAST *t;\r | |
499 | #endif\r | |
500 | {\r | |
501 | int n=0;\r | |
502 | \r | |
503 | while ( t!=NULL )\r | |
504 | {\r | |
505 | n++;\r | |
506 | t = t->ast_right;\r | |
507 | }\r | |
508 | return n;\r | |
509 | }\r | |
510 | \r | |
511 | SORAST *\r | |
512 | #ifdef __USE_PROTOS\r | |
513 | ast_sibling_index(SORAST *t, int i)\r | |
514 | #else\r | |
515 | ast_sibling_index(t,i)\r | |
516 | SORAST *t;\r | |
517 | int i;\r | |
518 | #endif\r | |
519 | {\r | |
520 | int j=1;\r | |
521 | require(i>0, "ast_sibling_index: i<=0");\r | |
522 | \r | |
523 | while ( t!=NULL )\r | |
524 | {\r | |
525 | if ( j==i ) return t;\r | |
526 | j++;\r | |
527 | t = t->ast_right;\r | |
528 | }\r | |
529 | return NULL;\r | |
530 | }\r | |
531 | \r | |
532 | static void\r | |
533 | #ifdef __USE_PROTOS\r | |
534 | scanast_free(ScanAST *t)\r | |
535 | #else\r | |
536 | scanast_free(t)\r | |
537 | ScanAST *t;\r | |
538 | #endif\r | |
539 | {\r | |
540 | if ( t == NULL ) return;\r | |
541 | scanast_free( t->down );\r | |
542 | scanast_free( t->right );\r | |
543 | free( t );\r | |
544 | }\r | |
545 | \r | |
546 | /*\r | |
547 | * ast_scan\r | |
548 | *\r | |
549 | * This function is like scanf(): it attempts to match a template\r | |
550 | * against an input tree. A variable number of tree pointers\r | |
551 | * may be set according to the '%i' labels in the template string.\r | |
552 | * For example:\r | |
553 | *\r | |
554 | * ast_scan("#( 6 #(5 %1:4 %2:3) #(1 %3:3 %4:3) )",\r | |
555 | * t, &w, &x, &y, &z);\r | |
556 | *\r | |
557 | * Naturally, you'd want this converted from\r | |
558 | *\r | |
559 | * ast_scan("#( RangeOp #(Minus %1:IConst %2:Var) #(Plus %3:Var %4Var) )",\r | |
560 | * t, &w, &x, &y, &z);\r | |
561 | *\r | |
562 | * by SORCERER.\r | |
563 | *\r | |
564 | * This function call must be done withing a SORCERER file because SORCERER\r | |
565 | * must convert the token references to the associated token number.\r | |
566 | *\r | |
567 | * This functions parses the template and creates trees which are then\r | |
568 | * matched against the input tree. The labels are set as they are\r | |
569 | * encountered; hence, partial matches may leave some pointers set\r | |
570 | * and some NULL. This routines initializes all argument pointers to NULL\r | |
571 | * at the beginning.\r | |
572 | *\r | |
573 | * This function returns the number of labels matched.\r | |
574 | */\r | |
575 | int\r | |
576 | #ifdef PCCTS_USE_STDARG\r | |
577 | ast_scan(char *templ, SORAST *tree, ...)\r | |
578 | #else\r | |
579 | ast_scan(va_alist)\r | |
580 | va_dcl\r | |
581 | #endif\r | |
582 | {\r | |
583 | va_list ap;\r | |
584 | ScanAST *t;\r | |
585 | int n, i, found=0;\r | |
586 | SORAST ***label_ptrs=NULL;\r | |
587 | \r | |
588 | #ifdef PCCTS_USE_STDARG\r | |
589 | va_start(ap, tree);\r | |
590 | #else\r | |
591 | char *templ;\r | |
592 | SORAST *tree;\r | |
593 | \r | |
594 | va_start(ap);\r | |
595 | templ = va_arg(ap, char *);\r | |
596 | tree = va_arg(ap, SORAST *);\r | |
597 | #endif\r | |
598 | \r | |
599 | /* make a ScanAST tree out of the template */\r | |
600 | t = stringparser_parse_scanast(templ, &n);\r | |
601 | \r | |
602 | /* make an array out of the labels */\r | |
603 | if ( n>0 )\r | |
604 | {\r | |
605 | label_ptrs = (SORAST ***) calloc(n, sizeof(SORAST **));\r | |
606 | require(label_ptrs!=NULL, "ast_scan: out of memory");\r | |
607 | for (i=1; i<=n; i++)\r | |
608 | {\r | |
609 | label_ptrs[i-1] = va_arg(ap, SORAST **);\r | |
610 | *(label_ptrs[i-1]) = NULL;\r | |
611 | }\r | |
612 | }\r | |
613 | \r | |
614 | /* match the input tree against the template */\r | |
615 | ast_scanmatch(t, tree, label_ptrs, &found);\r | |
616 | \r | |
617 | scanast_free(t);\r | |
618 | free(label_ptrs);\r | |
619 | \r | |
620 | return found;\r | |
621 | }\r | |
622 | \r | |
623 | static ScanAST *\r | |
624 | #ifdef __USE_PROTOS\r | |
625 | new_scanast(int tok)\r | |
626 | #else\r | |
627 | new_scanast(tok)\r | |
628 | int tok;\r | |
629 | #endif\r | |
630 | {\r | |
631 | ScanAST *p = (ScanAST *) calloc(1, sizeof(ScanAST));\r | |
632 | if ( p == NULL ) {fprintf(stderr, "out of mem\n"); exit(-1);}\r | |
633 | p->token = tok;\r | |
634 | return p;\r | |
635 | }\r | |
636 | \r | |
637 | static ScanAST *\r | |
638 | #ifdef __USE_PROTOS\r | |
639 | stringparser_parse_scanast(char *templ, int *num_labels)\r | |
640 | #else\r | |
641 | stringparser_parse_scanast(templ, num_labels)\r | |
642 | char *templ;\r | |
643 | int *num_labels;\r | |
644 | #endif\r | |
645 | {\r | |
646 | StringLexer lex;\r | |
647 | StringParser parser;\r | |
648 | ScanAST *t;\r | |
649 | \r | |
650 | stringlexer_init(&lex, templ);\r | |
651 | stringparser_init(&parser, &lex);\r | |
652 | t = stringparser_parse_tree(&parser);\r | |
653 | *num_labels = parser.num_labels;\r | |
654 | return t;\r | |
655 | }\r | |
656 | \r | |
657 | static void\r | |
658 | #ifdef __USE_PROTOS\r | |
659 | stringparser_match(StringParser *parser, int token)\r | |
660 | #else\r | |
661 | stringparser_match(parser, token)\r | |
662 | StringParser *parser;\r | |
663 | int token;\r | |
664 | #endif\r | |
665 | {\r | |
666 | if ( parser->token != token ) sorcerer_panic("bad tree in ast_scan()");\r | |
667 | }\r | |
668 | \r | |
669 | /*\r | |
670 | * Match a tree of the form:\r | |
671 | * (root child1 child2 ... childn)\r | |
672 | * or,\r | |
673 | * node\r | |
674 | *\r | |
675 | * where the elements are integers or labeled integers.\r | |
676 | */\r | |
677 | static ScanAST *\r | |
678 | #ifdef __USE_PROTOS\r | |
679 | stringparser_parse_tree(StringParser *parser)\r | |
680 | #else\r | |
681 | stringparser_parse_tree(parser)\r | |
682 | StringParser *parser;\r | |
683 | #endif\r | |
684 | {\r | |
685 | ScanAST *t=NULL, *root, *child, *last = NULL;\r | |
686 | \r | |
687 | if ( parser->token != POUND )\r | |
688 | {\r | |
689 | return stringparser_parse_element(parser);\r | |
690 | }\r | |
691 | stringparser_match(parser,POUND);\r | |
692 | parser->token = stringscan_gettok(parser->lexer);\r | |
693 | stringparser_match(parser,LPAREN);\r | |
694 | parser->token = stringscan_gettok(parser->lexer);\r | |
695 | root = stringparser_parse_element(parser);\r | |
696 | while ( parser->token != RPAREN )\r | |
697 | {\r | |
698 | child = stringparser_parse_element(parser);\r | |
699 | if ( t==NULL ) { t = child; last = t; }\r | |
700 | else { last->right = child; last = child; }\r | |
701 | }\r | |
702 | stringparser_match(parser,RPAREN);\r | |
703 | parser->token = stringscan_gettok(parser->lexer);\r | |
704 | root->down = t;\r | |
705 | return root;\r | |
706 | }\r | |
707 | \r | |
708 | static ScanAST *\r | |
709 | #ifdef __USE_PROTOS\r | |
710 | stringparser_parse_element(StringParser *parser)\r | |
711 | #else\r | |
712 | stringparser_parse_element(parser)\r | |
713 | StringParser *parser;\r | |
714 | #endif\r | |
715 | {\r | |
716 | static char ebuf[100];\r | |
717 | int label = 0;\r | |
718 | \r | |
719 | if ( parser->token == POUND )\r | |
720 | {\r | |
721 | return stringparser_parse_tree(parser);\r | |
722 | }\r | |
723 | if ( parser->token == PERCENT )\r | |
724 | {\r | |
725 | parser->token = stringscan_gettok(parser->lexer);\r | |
726 | stringparser_match(parser,INT);\r | |
727 | label = atoi(parser->lexer->text);\r | |
728 | parser->num_labels++;\r | |
729 | if ( label==0 ) sorcerer_panic("%%0 is an invalid label");\r | |
730 | parser->token = stringscan_gettok(parser->lexer);\r | |
731 | stringparser_match(parser,COLON);\r | |
732 | parser->token = stringscan_gettok(parser->lexer);\r | |
733 | /* can label tokens and wildcards */\r | |
734 | if ( parser->token != INT && parser->token != PERIOD )\r | |
735 | sorcerer_panic("can only label tokens");\r | |
736 | }\r | |
737 | if ( parser->token == INT )\r | |
738 | {\r | |
739 | ScanAST *p = new_scanast(atoi(parser->lexer->text));\r | |
740 | parser->token = stringscan_gettok(parser->lexer);\r | |
741 | p->label_num = label;\r | |
742 | return p;\r | |
743 | }\r | |
744 | if ( parser->token == PERIOD )\r | |
745 | {\r | |
746 | ScanAST *p = new_scanast(0); /* token of 0 is wildcard */\r | |
747 | parser->token = stringscan_gettok(parser->lexer);\r | |
748 | p->label_num = label;\r | |
749 | return p;\r | |
750 | }\r | |
751 | sprintf(ebuf, "mismatch token in ast_scan(): %s", scan_token_str(parser->token));\r | |
752 | sorcerer_panic(ebuf);\r | |
753 | return NULL; /* MR20 make -Wall happy */\r | |
754 | }\r | |
755 | \r | |
756 | static void\r | |
757 | #ifdef __USE_PROTOS\r | |
758 | stringparser_init(StringParser *parser, StringLexer *input)\r | |
759 | #else\r | |
760 | stringparser_init(parser, input)\r | |
761 | StringParser *parser;\r | |
762 | StringLexer *input;\r | |
763 | #endif\r | |
764 | {\r | |
765 | parser->lexer = input;\r | |
766 | parser->token = stringscan_gettok(parser->lexer);\r | |
767 | parser->num_labels = 0;\r | |
768 | }\r | |
769 | \r | |
770 | static void\r | |
771 | #ifdef __USE_PROTOS\r | |
772 | stringlexer_init(StringLexer *scanner, char *input)\r | |
773 | #else\r | |
774 | stringlexer_init(scanner, input)\r | |
775 | StringLexer *scanner;\r | |
776 | char *input;\r | |
777 | #endif\r | |
778 | {\r | |
779 | scanner->text[0]='\0';\r | |
780 | scanner->input = input;\r | |
781 | scanner->p = input;\r | |
782 | stringscan_advance(scanner);\r | |
783 | }\r | |
784 | \r | |
785 | static void\r | |
786 | #ifdef __USE_PROTOS\r | |
787 | stringscan_advance(StringLexer *scanner)\r | |
788 | #else\r | |
789 | stringscan_advance(scanner)\r | |
790 | StringLexer *scanner;\r | |
791 | #endif\r | |
792 | {\r | |
793 | if ( *(scanner->p) == '\0' ) scanner->c = StringScanEOF;\r | |
794 | scanner->c = *(scanner->p)++;\r | |
795 | }\r | |
796 | \r | |
797 | static int\r | |
798 | #ifdef __USE_PROTOS\r | |
799 | stringscan_gettok(StringLexer *scanner)\r | |
800 | #else\r | |
801 | stringscan_gettok(scanner)\r | |
802 | StringLexer *scanner;\r | |
803 | #endif\r | |
804 | {\r | |
805 | char *index = &scanner->text[0];\r | |
806 | static char ebuf[100];\r | |
807 | \r | |
808 | while ( isspace(scanner->c) ) { stringscan_advance(scanner); }\r | |
809 | if ( isdigit(scanner->c) )\r | |
810 | {\r | |
811 | int tok = INT;\r | |
812 | while ( isdigit(scanner->c) ) {\r | |
813 | *index++ = scanner->c;\r | |
814 | stringscan_advance(scanner);\r | |
815 | }\r | |
816 | *index = '\0';\r | |
817 | return tok;\r | |
818 | }\r | |
819 | switch ( scanner->c )\r | |
820 | {\r | |
821 | case '#' : stringscan_advance(scanner); return POUND;\r | |
822 | case '(' : stringscan_advance(scanner); return LPAREN;\r | |
823 | case ')' : stringscan_advance(scanner); return RPAREN;\r | |
824 | case '%' : stringscan_advance(scanner); return PERCENT;\r | |
825 | case ':' : stringscan_advance(scanner); return COLON;\r | |
826 | case '.' : stringscan_advance(scanner); return PERIOD;\r | |
827 | case '\0' : return StringScanEOF;\r | |
828 | case StringScanEOF : return StringScanEOF;\r | |
829 | default :\r | |
830 | sprintf(ebuf, "invalid char in ast_scan: '%c'", scanner->c);\r | |
831 | sorcerer_panic(ebuf);\r | |
832 | return 0; /* MR20 Make -Wall happy */\r | |
833 | }\r | |
834 | }\r |