1 /*
2 * $Id: codger.c,v 1.3 2007-03-29 23:56:19 dhmunro Exp $
3 *
4 * codger
5 * automatic CODe GEneratoR for adding compiled functions to yorick
6 *
7 * compiled extensions to yorick come in two forms:
8 * 1. archive libraries of object code, linked into yorick when
9 * yorick itself is loaded
10 * 2. dynamic libraries of object code, linked into yorick at runtime
11 * by means of the interpreted plug_in function (which invokes the
12 * system dynamic loader)
13 *
14 * the compiled code for a yorick extension is the same in either case
15 * (although the object modules must be built with different compiler
16 * flags in order to become part of a dynamic library on some platforms)
17 *
18 * Every yorick extension library (whether a static archive or a dynamic
19 * library) must contain a ywrap.o module, which must define a single
20 * function (with y_pkg_t prototype), called yk_pkgname where "pkgname"
21 * is the name of the package. Yorick calls this function to return
22 * the names and addresses of the built-in functions and data defined by
23 * the package. The static archive would be libpkgname.a, and the
24 * dynamic library would be pkgname.so or pkgname.sl or pkgname.dll
25 * depending on the platform.
26 *
27 * yk_pkgname returns pointers to the following objects:
28 * 1. the list of .i file names which must be included in order to
29 * load the package, typically installed in the Y_HOME/i0/ directory
30 * - yorick includes these at startup in the static load case,
31 * and at plug_in in the dynamic load case
32 * 2. the list of pointers to the builtin functions defined by pkgname
33 * (declared by extern statements in the .i files)
34 * 3. the list of pointers to any compiled data defined by pkgname
35 * (declared by extern statments followed by reshape statements
36 * or EXTERN comments in the .i files)
37 * 4. the list of names by which the interpreter can reference (2) and (3)
38 *
39 * ywrap.o optionally contains automatically generated wrappers for
40 * the builtin list (2), which reference C functions defined in other
41 * modules of the package (in the case that the other modules do not
42 * directly provide builtin functions that communicate with the yorick
43 * interpreter for their arguments and results)
44 * - these wrappers are specified by PROTOTYPE comments in the .i files
45 *
46 * codger generates the ywrap.c source code, given the .i files for
47 * the package
48 *
49 * Additionally, codger generates the yinit.c source code, containing the
50 * on_launch callback that passes the function addresses of yk_yor and
51 * any statically loaded yk_pkgname functions to yorick at startup,
52 * plus the Y_HOME and Y_SITE directory names.
53 */
54 /* Copyright (c) 2005, The Regents of the University of California.
55 * All rights reserved.
56 * This file is part of yorick (http://yorick.sourceforge.net).
57 * Read the accompanying LICENSE file for details.
58 */
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63
64 static int usage(void);
65 static int geninit(int argc, char *argv[]);
66 static int genwrap(int argc, char *argv[]);
67 static FILE *out_open(char *name);
68 static FILE *i_open(char *name, char **dirs, int ndirs);
69 static int i_process(FILE *out, FILE *in, int flink, char *inname);
70 static char *deescape(char *path);
71
72 #define E_(msg) fputs(msg "\n", stderr)
73 #define O_(msg) fputs(msg "\n", out)
74
75 static int
usage(void)76 usage(void)
77 {
78 E_("codger usage:");
79 E_(" codger w pkgname pkga.i [pkgb.i ...]");
80 E_(" generate ywrap.c for pkgname from pkga.i, pkgb.i, ...");
81 E_(" options (after pkgname):");
82 E_(" -o alt.c (before any .i) output to alt.c instead of ywrap.c");
83 E_(" -Idir add dir to search path for .i files");
84 E_(" -Ldir -lname record dependencies required to link package");
85 E_(" -f_ -f -F_ -F select fortran linkage convention (default f_)");
86 E_(" codger i Y_HOME Y_SITE pkgname1 [pkgname2 ...]");
87 E_(" generate yinit.c for pkgname1, pkgname2, ...");
88 E_(" options (after Y_SITE):");
89 E_(" -o alt.c output to alt.c instead of yinit.c");
90 return 1;
91 }
92
93 int
main(int argc,char * argv[])94 main(int argc, char *argv[])
95 {
96 if (argc>=4 && !argv[1][1]) {
97 if (argv[1][0]=='i')
98 return geninit(argc-2, argv+2);
99 else if (argv[1][0]=='w')
100 return genwrap(argc-2, argv+2);
101 }
102 return usage();
103 }
104
105 static int
geninit(int argc,char * argv[])106 geninit(int argc, char *argv[])
107 {
108 FILE *out;
109 char *outname = 0;
110 char *home = argv[0];
111 char *site = argv[1];
112 char **pkgs = malloc(sizeof(char *) * argc);
113 int npkgs = 0;
114 long i, n;
115
116 for (argc-=2,argv+=2 ; argc ; argc--,argv++) {
117 if (argv[0][0] == '-') {
118 if (argv[0][1]=='o' && !argv[0][2]) {
119 argc--,argv++;
120 if (!argc || outname)
121 return usage();
122 outname = argv[0];
123 } else {
124 return usage();
125 }
126 } else {
127 n = strlen(argv[0]);
128 if (n > 100) {
129 E_("codger: pkgname has over 100 chars");
130 return 2;
131 }
132 for (i=0 ; i<n ; i++) {
133 if ((argv[0][i]>='a' && argv[0][i]<='z') ||
134 (argv[0][i]>='A' && argv[0][i]<='Z') ||
135 (argv[0][i]>='0' && argv[0][i]<='9') ||
136 argv[0][i]=='_') continue;
137 E_("codger: pkgname has illegal character, not [a-z][A-Z][0-9]_");
138 return 2;
139 }
140 if (strcmp(argv[0], "yor")) { /* skip repeats and yor */
141 for (i=0 ; i<npkgs ; i++) if (!strcmp(argv[0], pkgs[i])) break;
142 if (i>=npkgs)
143 pkgs[npkgs++] = argv[0];
144 }
145 }
146 }
147
148 out = fopen(outname? outname : "yinit.c", "w");
149 if (!out) {
150 fprintf(stderr, "codger: unable to create %s\n",
151 outname? outname : "yinit.c");
152 return 3;
153 }
154
155 O_("/* codger-generated yorick initialization file */");
156 O_("#include \"play.h\"");
157 O_("#include \"ydata.h\"");
158 O_("");
159 fprintf(out, "static char *yhome = \"%s\";\n", deescape(home));
160 fprintf(out, "static char *ysite = \"%s\";\n", deescape(site));
161 O_("");
162 O_("extern y_pkg_t yk_yor;");
163 for (i=0 ; i<npkgs ; i++)
164 fprintf(out, "extern y_pkg_t yk_%s;\n", pkgs[i]);
165 O_("");
166 O_("static y_pkg_t *ypkgs[] = {");
167 O_(" &yk_yor,");
168 for (i=0 ; i<npkgs ; i++)
169 fprintf(out, " &yk_%s,\n", pkgs[i]);
170 O_(" 0");
171 O_("};");
172 O_("");
173 O_("int");
174 O_("on_launch(int argc, char *argv[])");
175 O_("{");
176 O_(" return y_launch(argc, argv, yhome, ysite, ypkgs);");
177 O_("}");
178
179 fclose(out);
180 return 0;
181 }
182
183 static char *
deescape(char * path)184 deescape(char *path)
185 {
186 /* try to cope with escaped blanks in Y_HOME, Y_SITE */
187 char *p, *q = path;
188 for (p=q ; q[0] ; p++,q++) {
189 if (q[0]=='\\' && q[1]==' ') p[0]=' ', q++;
190 else if (p!=q) p[0] = q[0];
191 }
192 if (p!=q) p[0] = '\0';
193 return path;
194 }
195
196 #if defined(f_linkage)
197 # define F_LINK 1
198 #elif defined(F_LINKAGE_)
199 # define F_LINK 2
200 #elif defined(F_LINKAGE)
201 # define F_LINK 3
202 #else
203 # define F_LINK 0
204 #endif
205
206 /* ------------------------------------------------------------------------ */
207 /* red-black tree is simplest balanced binary tree
208 * - use it here to store list of extern variable names
209 * - idea is that this is minimal code to support
210 * efficient name collision detection
211 */
212
213 typedef struct rbtree rbtree;
214 extern void rbinit(rbtree *node, rbtree *up);
215 extern rbtree *rblookup(rbtree *root, void *data, long ldata);
216 extern rbtree *rbinsert(rbtree **proot, void *data, long ldata);
217 /* rbremove not necessary here */
218
219 /* --------- application-specific routines and struct */
220
221 extern rbtree *rballoc(rbtree *up, void *data, long ldata);
222 extern int rbcmp(rbtree *node, void *data, long ldata);
223
224 rbtree *var_table = 0;
225 rbtree *var_first = 0;
226 rbtree *var_last = 0;
227
228 struct rbtree {
229 /* generic rbtree links */
230 rbtree *left, *right, *up;
231 int red;
232
233 /* application-specific data */
234 rbtree *next;
235 char *cname;
236 int type;
237 char name[4];
238 };
239
240 #define VAR_UNKNOWN 0
241 #define VAR_CODE 1
242 #define VAR_DATA 2
243 #define VAR_FORT 4
244 #define VAR_RESHAPE 8
245
246 rbtree *
rballoc(rbtree * up,void * data,long ldata)247 rballoc(rbtree *up, void *data, long ldata)
248 {
249 char *name = data;
250 rbtree *node = malloc(sizeof(rbtree)+ldata);
251 if (node) {
252 rbinit(node, up);
253 if (!var_first) var_first = node;
254 else var_last->next = node;
255 var_last = node;
256 node->next = 0;
257 node->cname = 0;
258 node->type = 0;
259 strncpy(node->name, name, ldata);
260 node->name[ldata] = '\0';
261 }
262 return node;
263 }
264
265 int
rbcmp(rbtree * node,void * data,long ldata)266 rbcmp(rbtree *node, void *data, long ldata)
267 {
268 int cmp = strncmp(node->name, data, ldata);
269 if (!cmp && node->name[ldata]) cmp = 1;
270 return -cmp;
271 }
272
273 /* --------- generic rbtree routines */
274
275 void
rbinit(rbtree * node,rbtree * up)276 rbinit(rbtree *node, rbtree *up)
277 {
278 node->left = node->right = 0;
279 node->up = up;
280 node->red = (up != 0);
281 }
282
283 rbtree *
rblookup(rbtree * root,void * data,long ldata)284 rblookup(rbtree *root, void *data, long ldata)
285 {
286 int cmp;
287 while (root) {
288 cmp = rbcmp(root, data, ldata);
289 if (!cmp) return root;
290 root = (cmp<0)? root->left : root->right;
291 }
292 return 0;
293 }
294
295 rbtree *
rbinsert(rbtree ** proot,void * data,long ldata)296 rbinsert(rbtree **proot, void *data, long ldata)
297 {
298 int lchild, lparent;
299 rbtree *node, *child, *gramps, *uncle, *parent = *proot;
300
301 if (!parent)
302 return *proot = rballoc(0, data, ldata);
303
304 for (;;) {
305 lchild = rbcmp(parent, data, ldata);
306 if (!lchild) return 0; /* cannot insert duplicate data */
307 lchild = (lchild < 0);
308 if (lchild) {
309 if (!parent->left) {
310 parent->left = node = rballoc(parent, data, ldata);
311 break;
312 }
313 parent = parent->left;
314 } else {
315 if (!parent->right) {
316 parent->right = node = rballoc(parent, data, ldata);
317 break;
318 }
319 parent = parent->right;
320 }
321 }
322 if (!node) return 0;
323
324 child = node;
325 while (parent->red) { /* note this implies parent->up != 0 */
326 gramps = parent->up;
327 lparent = gramps->left == parent;
328 uncle = lparent? gramps->right : gramps->left;
329
330 if (uncle && uncle->red) {
331 /* red uncle: recolor parent, uncle, gramps and propagate upward */
332 parent->red = uncle->red = 0;
333 parent = gramps->up;
334 if (!parent) break; /* quit if gramps is root */
335 gramps->red = 1; /* was black */
336 child = gramps; /* child always has two black children */
337 lchild = (parent->left == child);
338 continue;
339 }
340
341 /* black uncle: balance tree (4 cases) and quit */
342 if (lchild == lparent) {
343 if (lparent) {
344 /* ( (c[R] < p[R] < x[B]) < g[B] < u[B] )
345 * --> (c[R] < p[B] < (x[B] < g[R] < u[B]) )
346 */
347 gramps->left = parent->right; parent->right = gramps;
348 if (gramps->left) gramps->left->up = gramps;
349 } else {
350 /* ( u[B] < g[B] < (x[B] < p[R] < c[R]) )
351 * --> ( (u[B] < g[R] < x[B]) < p[B] < c[R])
352 */
353 gramps->right = parent->left; parent->left = gramps;
354 if (gramps->right) gramps->right->up = gramps;
355 }
356 parent->up = gramps->up; gramps->up = parent;
357 if (!parent->up) *proot = parent;
358 else if (parent->up->left == gramps) parent->up->left = parent;
359 else parent->up->right = parent;
360 parent->red = 0;
361 } else {
362 if (lparent) {
363 /* ( (x[B] < p[R] < (clB < c[R] < crB)) < g[B] < u[B] )
364 * --> ( (x[B] < p[R] < clB) < c[B] < (crB < g[R] < u[B]) )
365 */
366 parent->right = child->left; child->left = parent;
367 gramps->left = child->right; child->right = gramps;
368 if (parent->right) parent->right->up = parent;
369 if (gramps->left) gramps->left->up = gramps;
370 } else {
371 /* ( u[B] < g[B] < ((clB < c[R] < crB) < p[R] < x[B]) )
372 * --> ( (u[B] < g[R] < clB) < c[B] < (crB < p[R] < x[B]) )
373 */
374 parent->left = child->right; child->right = parent;
375 gramps->right = child->left; child->left = gramps;
376 if (parent->left) parent->left->up = parent;
377 if (gramps->right) gramps->right->up = gramps;
378 }
379 child->up = gramps->up; gramps->up = parent->up = child;
380 if (!child->up) *proot = child;
381 else if (child->up->left == gramps) child->up->left = child;
382 else child->up->right = child;
383 child->red = 0;
384 }
385 gramps->red = 1;
386 break;
387 }
388
389 return node;
390 }
391
392 /* end of red-black tree code */
393 /* ------------------------------------------------------------------------ */
394
395 static int
genwrap(int argc,char * argv[])396 genwrap(int argc, char *argv[])
397 {
398 FILE *out=0, *in=0;
399 char *outname = 0;
400 char *pkgname = argv[0];
401 char **dirs = malloc(sizeof(char *) * argc);
402 int ndirs = 0;
403 char **libs = malloc(sizeof(char *) * argc);
404 int nlibs = 0;
405 char **ins = malloc(sizeof(char *) * argc);
406 int nins = 0;
407 int flink = F_LINK; /* 1 supress trailing _, 2 upper case */
408 int i;
409 rbtree *node;
410
411 for (argc--,argv++ ; argc ; argc--,argv++) {
412 if (argv[0][0] == '-') {
413 if (argv[0][1]=='o' && !argv[0][2]) {
414 argc--,argv++;
415 if (!argc || outname || out)
416 return usage();
417 outname = argv[0];
418 } else if (argv[0][1]=='I' && argv[0][2]) {
419 dirs[ndirs++] = argv[0]+2;
420 } else if ((argv[0][1]=='L' || argv[0][1]=='l') && argv[0][2]) {
421 libs[nlibs++] = argv[0];
422 } else if (argv[0][1]=='f' || argv[0][1]=='F') {
423 if (argv[0][2]=='_' && !argv[0][3]) flink = 0;
424 else if (!argv[0][2]) flink = 1;
425 else return usage();
426 flink |= (argv[0][1]=='F')? 2 : 0;
427 } else {
428 return usage();
429 }
430 } else {
431 if (!out) {
432 out = out_open(outname? outname : "ywrap.c");
433 if (!out) {
434 E_("unable to create output file");
435 return 2;
436 }
437 }
438 ins[nins++] = argv[0];
439 O_("");
440 fprintf(out, "/*----------------begin %s */\n", argv[0]);
441 in = i_open(argv[0], dirs, ndirs);
442 if (!in) {
443 fclose(out);
444 return 3;
445 }
446 /* i_process emits any wrapper code, adds to routines, values, names */
447 if (i_process(out, in, flink, argv[0])) {
448 fclose(out);
449 return 4;
450 }
451 }
452 }
453
454 O_("");
455 O_("/*----------------list include files */");
456 O_("");
457 O_("static char *y0_includes[] = {");
458 for (i=0 ; i<nins ; i++)
459 fprintf(out, " \"%s\",\n", ins[i]);
460 O_(" 0,");
461 if (nlibs) {
462 O_(" /*--------------dependent libraries this package requires */");
463 fprintf(out, " \"%s", libs[0]);
464 for (i=1 ; i<nlibs ; i++)
465 fprintf(out, " %s", libs[i]);
466 O_("\",");
467 }
468 O_(" 0");
469 O_("};");
470
471 O_("");
472 O_("/*----------------collect pointers and names */");
473 O_("");
474 O_("static BuiltIn *y0_routines[] = {");
475 for (node=var_first ; node ; node=node->next)
476 if (!(node->type&VAR_DATA)) fprintf(out, " &Y_%s,\n", node->name);
477 O_(" 0");
478 O_("};");
479 O_("");
480 O_("static void *y0_values[] = {");
481 for (node=var_first ; node ; node=node->next)
482 if (node->type&VAR_DATA)
483 fprintf(out, " %s,\n", node->cname? node->cname : node->name);
484 O_(" 0");
485 O_("};");
486 O_("");
487 O_("static char *y0_names[] = {");
488 for (node=var_first ; node ; node=node->next)
489 if (!(node->type&VAR_DATA)) fprintf(out, " \"%s\",\n", node->name);
490 for (node=var_first ; node ; node=node->next)
491 if (node->type&VAR_DATA) fprintf(out, " \"%s\",\n", node->name);
492 O_(" 0");
493 O_("};");
494
495 O_("");
496 O_("/*----------------define package initialization function */");
497 O_("");
498 fprintf(out, "PLUG_EXPORT char *yk_%s(char ***,\n", pkgname);
499 O_(" BuiltIn ***, void ***, char ***);");
500 fprintf(out, "static char *y0_pkgname = \"%s\";\n", pkgname);
501 O_("");
502 O_("char *");
503 fprintf(out, "yk_%s(char ***ifiles,\n", pkgname);
504 O_(" BuiltIn ***code, void ***data, char ***varname)");
505 O_("{");
506 O_(" *ifiles = y0_includes;");
507 O_(" *code = y0_routines;");
508 O_(" *data = y0_values;");
509 O_(" *varname = y0_names;");
510 O_(" return y0_pkgname;");
511 O_("}");
512
513 fclose(out);
514 return 0;
515 }
516
517 static FILE *
out_open(char * name)518 out_open(char *name)
519 {
520 FILE *out = fopen(name, "w");
521 if (!out) {
522 fprintf(stderr, "codger: unable to create %s\n", name);
523 return 0;
524 }
525
526 O_("/* codger-generated yorick package wrapper file */");
527 O_("#include \"play.h\"");
528 O_("#include \"ydata.h\"");
529 return out;
530 }
531
532 static FILE *
i_open(char * name,char ** dirs,int ndirs)533 i_open(char *name, char **dirs, int ndirs)
534 {
535 /* first try curent directory, then directories in -I options */
536 FILE *in = fopen(name, "r");
537 if (!in) {
538 char tmp[2048];
539 long i, n, len = strlen(name);
540 for (i=ndirs-1 ; i>=0 && !in ; i--) { /* scan -I in reverse order */
541 n = strlen(dirs[i]);
542 if (n+len > 2046) {
543 E_("codger: pkg.i name plus -Idir option >2046 characters");
544 return 0;
545 }
546 strcpy(tmp, dirs[i]);
547 if (n && tmp[n-1]!='/') strcpy(tmp+n, "/");
548 strcat(tmp+n, name);
549 in = fopen(tmp, "r");
550 }
551 if (!in)
552 fprintf(stderr, "codger: unable to find %s, need -I?", name);
553 else
554 fprintf(stdout, "found %s in %s\n", name, dirs[i+1]);
555 } else {
556 fprintf(stdout, "found %s in current directory\n", name);
557 }
558 return in;
559 }
560
561 static char *next_line(FILE *in);
562 static char *skip_white(char *pos);
563 static char *next_nonblank(FILE *in, int hash);
564 static char *skip_single(char *pos);
565 static char *skip_quote(FILE *in, char *pos);
566 static char *skip_comment(FILE *in, char *pos);
567 static char *skip_hash(FILE *in, char *pos);
568 static char *skip_brace(FILE *in, char *pos);
569 static char *next_action(FILE *in, char *pos);
570 static char *next_token(FILE *in, char *pos, int incomment);
571 static int is_fortran(FILE *in, char **ppos);
572 static int name_len(char *pos);
573 static void handle_extern(FILE *out, char *pos, int len);
574 static void handle_reshape(FILE *out, char *pos, int len);
575 static void handle_extcomm(FILE *out, char *pos, int len, int fort);
576 static void handle_procomm(FILE *out, FILE *in, char *pos, int fort);
577 static void put_extern(FILE *out, rbtree *var, int check);
578 static void put_cname(rbtree *var, char *pos, int len);
579 static int get_ctype(FILE *in, char **ppos);
580 static void i_warning(char *msg);
581
582 #define IS_WHITE(c) (c==' ' || c=='\t' || c=='\n' || c=='\r')
583 #define IS_EWHITE(c) ((!c) || c==' ' || c=='\t' || c=='\n' || c=='\r')
584 #define IS_ALPH(c) ((c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_')
585 #define IS_ALPHNUM(c) (IS_ALPH(c) || (c>='0' && c<='9'))
586
587 static long line_num;
588 static char *file_name;
589 static int fortran_policy;
590 static int n_errs;
591
592 static int
i_process(FILE * out,FILE * in,int flink,char * inname)593 i_process(FILE *out, FILE *in, int flink, char *inname)
594 {
595 char *pos;
596 int fort, lname;
597
598 file_name = inname;
599 line_num = 0;
600 fortran_policy = flink;
601 n_errs = 0;
602
603 for (pos=next_action(in,"") ; pos ; pos=next_action(in,pos)) {
604 if (pos[0] == 'e') { /* extern statement */
605 pos = next_token(in, pos+6, 0);
606 if (!pos) break;
607 lname = name_len(pos);
608 if (!lname)
609 i_warning("bad extern statement");
610 else
611 /* this becomes current variable for PROTOTYPE, EXTERNAL comments */
612 handle_extern(out, pos, lname);
613
614 } else if (pos[0] == 'P') { /* PROTOTYPE comment */
615 fort = is_fortran(in, &pos);
616 /* can go ahead and generate wrapper immediately,
617 * also mark current variable definitely a builtin
618 */
619 handle_procomm(out, in, pos, fort);
620 pos = skip_comment(in, pos);
621
622 } else if (pos[0] == 'r') { /* reshape statement */
623 pos = next_token(in, pos+7, 0);
624 if (!pos) break;
625 if (pos[0] == ',') {
626 pos = next_token(in, pos+1, 0);
627 if (!pos) break;
628 }
629 lname = name_len(pos);
630 if (!lname)
631 i_warning("bad reshape statement");
632 else
633 /* mark variable definitely data if not already marked */
634 handle_reshape(out, pos, lname);
635
636 } else if (pos[0] == 'E') { /* EXTERNAL comment */
637 fort = is_fortran(in, &pos);
638 lname = name_len(pos);
639 if (!lname)
640 i_warning("bad EXTERNAL comment");
641 else
642 /* mark current variable definitely data, with compiled alias */
643 handle_extcomm(out, pos, lname, fort);
644 pos = skip_comment(in, pos);
645 }
646 }
647 fclose(in);
648
649 put_extern(out, var_last, 1);
650
651 /* make sure output file is uncompilable if errors detected */
652 if (n_errs) {
653 fprintf(out, "\n#error %d codger errors in %s*/\n\n", n_errs, inname);
654 return 1;
655 }
656 return 0;
657 }
658
659 static void
i_warning(char * msg)660 i_warning(char *msg)
661 {
662 n_errs++;
663 fprintf(stderr, "codger: %s, LINE: %ld FILE: %s\n",
664 msg, line_num, file_name);
665 }
666
667 static int
is_fortran(FILE * in,char ** ppos)668 is_fortran(FILE *in, char **ppos)
669 {
670 char *pos = *ppos;
671 int fort = 0;
672 pos += 8; /* skips EXTERNAL */
673 if (!IS_EWHITE(pos[0])) pos++; /* skips PROTOTYPE */
674 pos = next_token(in, pos, 1);
675 if (pos[0]=='F' && !strncmp(pos+1,"ORTRAN",6) && IS_EWHITE(pos[7])) {
676 fort = 1;
677 pos = next_token(in, pos+7, 1);
678 }
679 *ppos = pos;
680 return fort;
681 }
682
683 static int
name_len(char * pos)684 name_len(char *pos)
685 {
686 int len = 0;
687 if (pos && IS_ALPH(pos[0]))
688 for (len++ ; IS_ALPHNUM(pos[len]) ; len++);
689 return len;
690 }
691
692 static char *
next_line(FILE * in)693 next_line(FILE *in)
694 {
695 static char line[4096];
696 if (feof(in)) return 0;
697 line_num++;
698 return fgets(line, 4096, in);
699 }
700
701 static char *
skip_white(char * pos)702 skip_white(char *pos)
703 {
704 if (pos)
705 while (pos[0] && IS_WHITE(pos[0])) pos++;
706 return pos;
707 }
708
709 static char *
next_nonblank(FILE * in,int hash)710 next_nonblank(FILE *in, int hash)
711 {
712 char *pos;
713 do {
714 pos = next_line(in);
715 if (!pos) return 0;
716 pos = skip_white(pos);
717 if (hash && pos[0]=='#') {
718 pos = skip_hash(in, pos);
719 if (!pos) return 0;
720 }
721 } while (!pos[0]);
722 return pos;
723 }
724
725 static char *
skip_single(char * pos)726 skip_single(char *pos)
727 {
728 /* pos[0] == ' on input, scan to closing ' */
729 pos++;
730 if (pos[0]=='\\' && (++pos)[0]) ++pos;
731 while (pos[0] && pos[0]!='\'') pos++;
732 return pos[0]? pos+1 : pos;
733 }
734
735 static char *
skip_quote(FILE * in,char * pos)736 skip_quote(FILE *in, char *pos)
737 {
738 /* pos[0] == " on input, scan to closing " */
739 pos++;
740 while (pos[0]!='"') {
741 if (!pos[0]) {
742 pos = next_line(in);
743 if (!pos) return 0;
744 } else if (pos[0]=='\\') {
745 pos++;
746 if (pos[0]<='7' && pos[0]>='0') {
747 pos++;
748 if (pos[0]<='7' && pos[0]>='0') {
749 pos++;
750 if (pos[0]<='7' && pos[0]>='0') pos++;
751 }
752 } else if (pos[0] == 'x') {
753 pos++;
754 while ((pos[0]<='9' && pos[0]>='0')
755 || (pos[0]<='F' && pos[0]>='A')
756 || (pos[0]<='f' && pos[0]>='a')) pos++;
757 } else {
758 pos++;
759 }
760 } else {
761 pos++;
762 }
763 }
764 return pos+1;
765 }
766
767 static char *
skip_comment(FILE * in,char * pos)768 skip_comment(FILE *in, char *pos)
769 {
770 /* scan to closing star slash */
771 do {
772 while (pos[0])
773 if ((pos++)[0]=='*' && pos[0]=='/') return pos+1;
774 pos = next_line(in);
775 } while (pos);
776 return 0;
777 }
778
779 static char *
skip_hash(FILE * in,char * pos)780 skip_hash(FILE *in, char *pos)
781 {
782 /* yorick bug:
783 * #if at beginning of line inside slash-star comment
784 * is correctly commented out,
785 * but #endif cannot be similarly commented out if inside an #if
786 * this code repeats that bug
787 */
788 /* pos[0] == # on input, scan to next valid line */
789 int depth = 0;
790 for(;;) {
791 pos = skip_white(pos+1);
792 if (depth && !strncmp(pos,"endif",5) && IS_EWHITE(pos[5])) {
793 depth--;
794 } else if (pos[0]=='i' && pos[1]=='f' && (pos[2]==' ' || pos[2]=='\t')) {
795 pos = skip_white(pos+3);
796 if (depth || (pos[0]=='0' && IS_EWHITE(pos[1])))
797 depth++;
798 }
799 for (;;) {
800 pos = next_line(in);
801 if (!pos) return 0;
802 pos = skip_white(pos);
803 if (pos[0]=='#') break;
804 if (depth || !pos[0]) continue;
805 return pos; /* non-blank, non-# */
806 }
807 }
808 }
809
810 static char *
skip_brace(FILE * in,char * pos)811 skip_brace(FILE *in, char *pos)
812 {
813 /* pos[0] == { on input, scan to matching } */
814 int depth = 1;
815 pos++;
816 do {
817 pos = skip_white(pos);
818 if (!pos[0]) {
819 pos = next_nonblank(in, 1);
820 if (!pos) return 0;
821 }
822 if (pos[0]=='}') {
823 pos++;
824 depth--;
825 } else if (pos[0]=='{') {
826 pos++;
827 depth++;
828 } else if (pos[0]=='/') {
829 if (pos[1]=='/') {
830 pos = next_nonblank(in, 1);
831 } else if (pos[1]=='*') {
832 pos = skip_comment(in, pos+2);
833 } else {
834 pos++;
835 }
836 } else if (pos[0]=='"') {
837 pos = skip_quote(in, pos);
838 if (!pos) return 0;
839 } else if (pos[0]=='\'') {
840 pos = skip_single(pos);
841 } else {
842 pos++;
843 }
844 } while (pos && depth);
845 return pos;
846 }
847
848 static char *
next_action(FILE * in,char * pos)849 next_action(FILE *in, char *pos)
850 {
851 /* scan to next extern, reshape, PROTOTYPE, or EXTERNAL */
852 while (pos) {
853 while (pos[0]) {
854 /* clear out remainder of line */
855 if (pos[0]=='"') {
856 pos = skip_quote(in, pos);
857 } else if (pos[0]=='/') {
858 if (pos[1]=='*') pos = skip_comment(in, pos+2);
859 else if (pos[1]=='/') for (pos+=2 ; pos[0] ; pos++);
860 else pos++;
861 } else if (pos[0]=='{') {
862 pos = skip_brace(in, pos);
863 } else {
864 pos++;
865 }
866 if (!pos) return 0;
867 }
868 if (!pos) return 0;
869 pos = next_nonblank(in, 1);
870 if (!pos) return 0;
871 /* check if beginning of line is an action for codger */
872 if (pos[0]=='e') {
873 if (!strncmp(pos+1,"xtern",5) && !IS_ALPHNUM(pos[6]))
874 return pos; /* extern */
875 } else if (pos[0]=='r') {
876 if (!strncmp(pos+1,"eshape",6) && !IS_ALPHNUM(pos[7]))
877 return pos; /* reshape */
878 } else if (pos[0]=='/' && pos[1]=='*') {
879 pos = next_token(in, pos+2, 1);
880 if (pos[0]=='P') {
881 if (!strncmp(pos+1,"ROTOTYPE",8) && IS_EWHITE(pos[9]))
882 return pos; /* PROTOTYPE comment */
883 } else if (pos[0]=='E') {
884 if (!strncmp(pos+1,"XTERNAL",7) && IS_EWHITE(pos[8]))
885 return pos; /* EXTERNAL comment */
886 }
887 pos = skip_comment(in, pos);
888 }
889 }
890 return pos;
891 }
892
893 static char *
next_token(FILE * in,char * pos,int incomment)894 next_token(FILE *in, char *pos, int incomment)
895 {
896 if (!pos) return 0;
897 for (;;) {
898 pos = skip_white(pos);
899 while (!pos[0]) {
900 pos = next_line(in);
901 if (!pos) return 0;
902 pos = skip_white(pos);
903 }
904 if (incomment || pos[0]!='/') break;
905 if (pos[1]=='*') pos = skip_comment(in, pos+2);
906 else if (pos[1]=='/') for (pos+=2 ; pos[0] ; pos++);
907 else break;
908 }
909 return pos;
910 }
911
912 static void
handle_extern(FILE * out,char * pos,int len)913 handle_extern(FILE *out, char *pos, int len)
914 {
915 rbtree *prev = var_last;
916 rbtree *v = rbinsert(&var_table, pos, len);
917 if (!v) {
918 /* multiple externs for same var ugly but legal? */
919 if (!rblookup(var_table, pos, len))
920 i_warning("MEMORY MANAGER FAILED");
921
922 } else {
923 put_extern(out, prev, 1);
924 }
925 }
926
927 static void
handle_reshape(FILE * out,char * pos,int len)928 handle_reshape(FILE *out, char *pos, int len)
929 {
930 if (var_last && rblookup(var_table, pos, len)==var_last) {
931 if (var_last->type & VAR_CODE) {
932 i_warning("cannot reshape builtin function");
933 } else if (var_last->type & VAR_DATA) {
934 var_last->type |= VAR_RESHAPE;
935 } else {
936 var_last->type = VAR_DATA | VAR_RESHAPE;
937 /* generate extern declaration immediately */
938 put_extern(out, var_last, 0);
939 }
940 }
941 }
942
943 static void
handle_extcomm(FILE * out,char * pos,int len,int fort)944 handle_extcomm(FILE *out, char *pos, int len, int fort)
945 {
946 if (var_last) {
947 if (var_last->type & VAR_CODE) {
948 i_warning("cannot EXTERNAL builtin function");
949 } else if (var_last->type & VAR_RESHAPE) {
950 i_warning("EXTERNAL must come before reshape");
951 } else if (var_last->type & VAR_DATA) {
952 i_warning("duplicate EXTERNAL comment illegal");
953 } else {
954 var_last->type = VAR_DATA | (fort? VAR_FORT : 0);
955 put_cname(var_last, pos, len);
956 /* generate extern declaration immediately */
957 put_extern(out, var_last, 0);
958 }
959 }
960 }
961
962 static void
put_cname(rbtree * var,char * pos,int len)963 put_cname(rbtree *var, char *pos, int len)
964 {
965 int fort = (var->type & VAR_FORT) != 0;
966 /* bit 1 of fortran_policy supresses trailing _ */
967 int underscore = (fort && !(fortran_policy & 1));
968 var->cname = malloc(len+underscore+1);
969 if (var->cname) {
970 strncpy(var->cname, pos, len);
971 if (underscore) var->cname[len++] = '_';
972 var->cname[len] = '\0';
973 if (fort) {
974 int i;
975 /* bit 2 of fortran_policy means upper case, else lower case */
976 if (fortran_policy & 2) {
977 for (i=0 ; i<len ; i++)
978 if (var->cname[i]>='a' && var->cname[i]<='z')
979 var->cname[i] ^= ('a' ^ 'A');
980 } else {
981 for (i=0 ; i<len ; i++)
982 if (var->cname[i]>='A' && var->cname[i]<='Z')
983 var->cname[i] ^= ('a' ^ 'A');
984 }
985 }
986 } else {
987 i_warning("MEMORY MANAGER FAILED");
988 }
989 }
990
991 static void
put_extern(FILE * out,rbtree * var,int check)992 put_extern(FILE *out, rbtree *var, int check)
993 {
994 if (var && (!check || !var->type)) {
995 if (var->type & VAR_DATA) {
996 fprintf(out, "extern char %s[4];\n", var->cname?var->cname:var->name);
997 } else {
998 fprintf(out, "extern BuiltIn Y_%s;\n", var->name);
999 if (!var->type) var->type = VAR_CODE;
1000 }
1001 }
1002 }
1003
1004 /* in alphabetical order */
1005 static char *proto_types[11] = {
1006 "char", "complex", "double", "float", "int", "long",
1007 "pointer", "short", "string", "void", 0 };
1008 static char *c_types[11] = {
1009 "char ", "double ", "double ", "float ", "int ", "long ",
1010 "void *", "short ", "char *", "void ", 0 };
1011 /* bit 1 return not ok, 2 arg not okay, 4 complex */
1012 static int c_modes[11] = { 0, 5, 0, 0, 0, 0, 1, 0, 1, 2, -1 };
1013 static char *push_result[11] = {
1014 "PushIntValue((int)", 0, "PushDoubleValue(", "PushDoubleValue((double)",
1015 "PushIntValue(", "PushLongValue(", 0, "PushIntValue((short)", 0, "", 0 };
1016 static char *push_finish[11] = {
1017 ")", 0, ")", ")", ")", ")", 0, ")", 0, "", 0 };
1018 static char *yarg_char[11] = {
1019 "c", "z", "d", "f", "i", "l", "p", "s", "q", 0, 0 };
1020
1021 static int
get_ctype(FILE * in,char ** ppos)1022 get_ctype(FILE *in, char **ppos)
1023 {
1024 char *pos = *ppos;
1025 int lname = name_len(pos);
1026 int i = -1;
1027 if (lname > 1) {
1028 for (i=0 ; ; i++) {
1029 if (!proto_types[i] || pos[0]<proto_types[i][0]) return -1;
1030 if (pos[0] > proto_types[i][0]) continue;
1031 if (!strncmp(proto_types[i]+1, pos+1, lname-1) &&
1032 !proto_types[i][lname]) break;
1033 }
1034 pos += lname;
1035 *ppos = pos;
1036 }
1037 return i;
1038 }
1039
1040 static void
handle_procomm(FILE * out,FILE * in,char * pos,int fort)1041 handle_procomm(FILE *out, FILE *in, char *pos, int fort)
1042 {
1043 char proto[256], delim;
1044 int lname, ret, arg, nargs, len, i, star;
1045
1046 if (!var_last) return;
1047 if (var_last->type & VAR_DATA) {
1048 i_warning("cannot PROTOTYPE data variable");
1049 return;
1050 }
1051 var_last->type = VAR_CODE | (fort? VAR_FORT : 0);
1052 /* generate extern declaration for Y_ wrapper immediately */
1053 put_extern(out, var_last, 0);
1054
1055 /* remains to generate
1056 * (1) prototype for compiled function
1057 * (2) Y_ wrapper function definition
1058 */
1059 ret = get_ctype(in, &pos);
1060 if (ret<0 || (c_modes[ret]&1)) {
1061 i_warning("bad PROTOTYPE return type");
1062 return;
1063 }
1064 pos = next_token(in, pos, 1);
1065 lname = name_len(pos);
1066 if (!lname) {
1067 i_warning("bad PROTOTYPE function name");
1068 return;
1069 }
1070 put_cname(var_last, pos, lname);
1071 pos = next_token(in, pos+lname, 1);
1072 for (nargs=0,delim = '(' ; ; nargs++) {
1073 /* skip delimiter */
1074 if (pos[0]!=delim && (nargs!=1 || pos[0]!=(delim=','))) {
1075 if (nargs && pos[0]==')') break; /* only legal way out */
1076 i_warning("bad PROTOTYPE syntax");
1077 return;
1078 } else if (nargs >= 256) {
1079 i_warning("PROTOTYPE has >256 function parameters");
1080 return;
1081 }
1082 pos = next_token(in, pos+1, 1); /* seek argument type */
1083 arg = get_ctype(in, &pos);
1084 if (arg<0 || (nargs && (c_modes[arg]&2))) {
1085 i_warning("bad PROTOTYPE argument type");
1086 return;
1087 }
1088 pos = next_token(in, pos, 1);
1089 if ((c_modes[arg]&2) && pos[0]!=')') {
1090 i_warning("bad PROTOTYPE void argument");
1091 return;
1092 }
1093 lname = name_len(pos);
1094 if ((lname==5 && !strncmp(pos, "array", 5)) ||
1095 (!lname && pos[0]=='*')) { /* array qualifier */
1096 arg |= 64;
1097 pos = next_token(in, pos+(lname?lname:1), 1);
1098 lname = name_len(pos);
1099 }
1100 proto[nargs] = arg;
1101 if (lname) /* skip optional argument name */
1102 pos = next_token(in, pos+lname, 1);
1103 }
1104
1105 /* first, generate compiled prototype (used in wrapper) */
1106 fprintf(out, "\nextern %s%s(", c_types[ret], var_last->cname);
1107 len = 8+strlen(c_types[ret])+strlen(var_last->cname);
1108 for (i=0 ; i<nargs ; i++) {
1109 if (len > 60) {
1110 fprintf(out, "\n ");
1111 len = 2;
1112 }
1113 arg = proto[i];
1114 star = ((arg & 64) != 0) || (c_modes[arg&63]&4);
1115 arg &= 63;
1116 fprintf(out, "%s%s%s",
1117 c_types[arg], star? "*":"", (i==nargs-1)?");\n":", ");
1118 len += strlen(c_types[arg]) + star + 2;
1119 }
1120
1121 /* finally, generate the Y_ wrapper definition itself */
1122 fprintf(out, "void\nY_%s(int n)\n{\n", var_last->name);
1123 if (nargs==1 && (c_modes[(unsigned char)proto[0]&63]&2) &&
1124 (c_modes[ret]&2)) nargs=0;
1125 if (nargs==1 && (c_modes[(unsigned char)proto[0]&63]&2)) {
1126 fprintf(out, " if (n>1) YError(\"%s takes void argument\");\n",
1127 var_last->name);
1128 } else {
1129 fprintf(out, " if (n!=%d) YError(\"%s takes exactly %d arguments\");\n",
1130 nargs, var_last->name, nargs);
1131 }
1132 fprintf(out, " %s%s(", push_result[ret], var_last->cname);
1133 len = 2+strlen(push_result[ret])+strlen(var_last->cname);
1134 for (i=0 ; i<nargs ; i++) {
1135 if (len > 60) {
1136 fprintf(out, "\n ");
1137 len = 4;
1138 }
1139 arg = proto[i];
1140 star = ((arg & 64) != 0) || (c_modes[arg&63]&4);
1141 arg &= 63;
1142 if (yarg_char[arg]) {
1143 fprintf(out, "yarg_%s%s(%d%s)%s", star? "":"s", yarg_char[arg],
1144 nargs-1-i, star? ",0":"", (i==nargs-1)?"":", ");
1145 len += 13-3*star + ((i<9)?1:2);
1146 }
1147 }
1148 fprintf(out, ")%s;\n}\n\n", push_finish[ret]);
1149 }
1150