%option noyywrap %option yylineno %{ #include #include #include #include "ast/allocator.h" enum { FUNCTION, TYPEDEF }; typedef struct { char * id, * file; int line, type; } Tag; static Tag * tagsa = NULL; static int ntags = 0, target = 1, keywords_only = 0, scope = 0, intypedef = 0; static int warninclude = 0; static FILE * swigfp = NULL; static Allocator * alloc = NULL; static void append_tag (Tag t) { ntags++; tagsa = realloc (tagsa, ntags*sizeof(Tag)); tagsa[ntags-1] = t; tagsa[ntags-1].id = strdup (t.id); tagsa[ntags-1].file = strdup (t.file); tagsa[ntags-1].type = t.type; char * page = strstr (tagsa[ntags-1].file, ".page"); if (page) *page = '\0'; } static Tag * lookup_tag (const char * id) { int i; for (i = 0; i < ntags; i++) if (!strcmp(tagsa[i].id, id)) return &tagsa[i]; return NULL; } static FILE * fdepend = NULL, * ftags = NULL, * myout = NULL; static char * fname; static char * paths[100] = { LIBDIR }, grid[80] = ""; static int npath = 1, hasgrid = 0, debug = 0; static int dimension = 0, bghosts = 0, layers = 0, gpu = 0; static int incode; // are we in code (or in a code block)? static char * strip_path (char * s) { char * s1 = s; do { while (*s1 != '/' && *s1 != '\0') s1++; if (*s1 == '\0') return s; } while (*(++s1) != '/'); while (*s1 == '/') s1++; return s1; } static char * _stack[100]; int stack = -1; static char * push (char * s) { assert (stack + 1 < 100); char * f = allocate (alloc, strlen (s) + 1); strcpy (f, s); _stack[++stack] = f; return f; } static int strcmps (char * s1, char * s2) { while (*s1 != '\0' && *s2 != '\0') { if (*s1 != *s2) return 1; if (*s1 == '/') { while (*s1 == '/') s1++; while (*s2 == '/') s2++; } else s1++, s2++; } return !(*s1 == '\0' && *s2 == '\0'); } static void push_once (char * s) { static char * _processed[100]; static int processed = 0; int i; char * s1 = strip_path (s); for (i = 0; i < processed; i++) if (!strcmps (s1, strip_path (_processed[i]))) return; // already processed assert (processed < 100); _processed[processed++] = push (s); } #define pop() _stack[stack--]; static void singleslash (char * path, FILE * fp) { char * s = path, slash = 0; while (*s != '\0') { if (*s == '/') { if (!slash) fputc (*s, fp); slash = 1; } else { slash = 0; fputc (*s, fp); } s++; } } static FILE * openpath (const char * name, const char * mode, char ** path) { int i; for (i = npath; i >= 0; i--) { char * p = allocate (alloc, strlen (paths[i]) + strlen (name) + 3); strcpy (p, paths[i]); strcat (p, "//"); strcat (p, name); FILE * fp = fopen (p, mode); if (fp) { if (fdepend) { fputc ('\t', fdepend); singleslash (p, fdepend); fputs (" \\\n", fdepend); } *path = p; return fp; } } return NULL; } #define nonspace(s) { while (strchr(" \t\v\n\f", *s)) s++; } #define space(s) { while (!strchr(" \t\v\n\f", *s)) s++; } static char * shortpath (char * path) { char * file = strstr (path, LIBDIR); if (file == path) return file + strlen(LIBDIR) - strlen("src") - 1; // remove root else return path; } static void check_tag (char * text) { if (ftags && keywords_only && incode) { Tag * t; if (target && (t = lookup_tag(text))) { switch (t->type) { case FUNCTION: if (debug) fprintf (stderr, "%s:%d: function call '%s'\n", fname, yylineno, text); break; case TYPEDEF: if (debug) fprintf (stderr, "%s:%d: typedef reference '%s'\n", fname, yylineno, text); break; } fprintf (ftags, "call %s %s %s\n", t->id, shortpath (t->file), t->id); } } } static int yyerror(const char * s); static int comment(void); static void echo() { if (myout) { if (incode) fputs (yytext, myout); else { // only keep newlines char * s = yytext; while (*s != '\0') { if (*s == '\n') fputc ('\n', myout); s++; } } } } static void echo_c (int c) { if (myout) { if (incode) fputc (c, myout); else if (c == '\n') // only keep newlines fputc ('\n', myout); } } %} ID [a-zA-Z0-9_] SP [ \t] WS [ \t\v\n\f] ES (\\([\'\"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+)) BEGINCODE ^[SP]*[~]{3,}(c|literatec)[^\n]*\n ENDCODE ^[SP]*[~]{3,}[^\n]*\n FDECL {ID}+{SP}*\( %% {BEGINCODE} { if (incode) { yylineno--; return yyerror ("code blocks cannot be nested"); } incode = 1; if (myout) fputc ('\n', myout); } {ENDCODE} { if (incode) { incode = 0; if (myout) fputc ('\n', myout); } } \'.\' { echo(); // quoted character } "{" { scope++; } "}" { scope--; if (scope < 0) { if (warninclude) fprintf (stderr, "%s:%d: warning: mismatched '}'\n", fname, yylineno); else { fprintf (stderr, "%s:%d: error: mismatched '}'\n", fname, yylineno); exit (1); } scope = 0; } } ^{SP}*#{SP}*include{SP}+\"[^\"]*\"[^\n]*\n { // include "..." if (fdepend && strstr (yytext, "// nodep")) return 0; echo(); if (!keywords_only) { char * s = strchr(yytext, '"'); s++; char * e = &s[strlen(s) - 1]; while (*e != '"') { *e = '\0'; e--; } *e = '\0'; char * path; FILE * fp = openpath (s, "r", &path); if (fp != NULL) { push_once (path); if (ftags && target) { fputs ("incl ", ftags); singleslash (shortpath(path), ftags); fprintf (ftags, " %s %d\n", fname, yylineno - 1); } if (swigfp && target) { char * dot = strstr (path, ".h"); if (dot) { strcpy (dot, ".i"); fputs ("%include \"", swigfp); singleslash (path, swigfp); fputs ("\"\n", swigfp); } } fclose (fp); } else { fprintf (stderr, "%s:%d: %s: %s: No such file or directory\n", fname, yylineno - 1, warninclude ? "warning" : "error", s); if (!warninclude) return 1; } } } ^{SP}*#{SP}*define{SP}+GRIDNAME{WS}+ { echo(); if (grid[0] == '\0' && !hasgrid) { hasgrid = 1; char * s = fname; while (strchr (s, '/')) { s = strchr (s, '/') + 1; if (!strncmp (s, "grid/", 5)) { s += 5; break; } } strcpy (grid, s); if ((s = strchr (grid, '.'))) *s = '\0'; } } ^{SP}*#{SP}*define{SP}+dimension{WS}+[123]{SP}*$ { char * s = strstr (yytext, "dimension"); space(s); nonspace(s); dimension = atoi(s); } ^{SP}*#{SP}*define{SP}+BGHOSTS{WS}+[12]{SP}*$ { char * s = strstr (yytext, "BGHOSTS"); space(s); nonspace(s); bghosts = atoi(s); } ^{SP}*#{SP}*define{SP}+_GPU{WS}+1{SP}*$ { gpu = 1; } ^{SP}*#{SP}*define{SP}+LAYERS{WS}+1{SP}*$ { layers = 1; } ^{SP}*{ID}+{SP}*\**({SP}+{ID}+{SP}*\**)*{SP}+{ID}+{SP}*\( { // function definition echo(); if (ftags && scope == 0) { // fprintf (stderr, "'%s'\n", yytext); char * s = yytext; int nl = 0; int fstatic = 0; s = strtok (s, " \t\v\n\f("); char * id = s; while (s) { #if 0 // ignore static functions if (!strcmp (s, "static")) fstatic = 1; #endif id = s; s = strtok (NULL, " \t\v\n\f("); if (s) check_tag (id); } s = strdup (id); if (!fstatic && !keywords_only && strcmp(s, "if")) { // fprintf (stderr, "id: '%s'\n", s); Tag t = { s, fname, yylineno - nl, FUNCTION}; int p = 0, para = 1, c; while (para > p && (c = input())) { echo_c (c); if (c == '(') para++; else if (c == ')') para--; } if (c == ')') { while ((c = input())) { echo_c (c); if (c == '{' || c == ';') break; if (!strchr(" \t\v\n\f", c)) break; } if (c == '{') { scope++; append_tag (t); if (debug) fprintf (stderr, "%s:%d: function declaration '%s'\n", tagsa[ntags-1].file, tagsa[ntags-1].line, tagsa[ntags-1].id); if (target) fprintf (ftags, "decl %s %s %d\n", tagsa[ntags-1].id, tagsa[ntags-1].file, tagsa[ntags-1].line); } } } free (s); } } typedef{WS}+ { echo(); if (ftags && !keywords_only) intypedef = scope + 1; } {ID}+{WS}*; { if (intypedef && scope == intypedef - 1) { echo(); char * s = yytext; space(s); *s-- = '\0'; if (*s == ';') *s = '\0'; Tag t = { yytext, fname, yylineno, TYPEDEF}; append_tag (t); if (debug) fprintf (stderr, "%s:%d: typedef '%s'\n", tagsa[ntags-1].file, tagsa[ntags-1].line, tagsa[ntags-1].id); if (target) fprintf (ftags, "decl %s %s %d\n", tagsa[ntags-1].id, tagsa[ntags-1].file, tagsa[ntags-1].line); intypedef = 0; } else REJECT; } {ID}+ { // keyword in target echo(); check_tag (yytext); } "/*" { echo(); if (incode && comment()) return 1; } "//".* { if (!incode) REJECT; /* consume //-comment */ echo(); } . echo(); [\n] echo(); ({SP}?\"([^\"\\\n]|{ES})*\"{WS}*)+ echo(); /* STRING_LITERAL */ %% int yyerror (const char * s) { if (0) yyunput (0, NULL); // just prevents 'yyunput unused' compiler warning fprintf (stderr, "%s:%d: error: %s\n", fname, yylineno, s); return 1; } static int getput(void) { int c = input(); if (myout) fputc (c, myout); return c; } static int comment(void) { int c, lineno = yylineno; while ((c = getput())) { if (c == '*') { while ((c = getput()) == '*') ; if (c == '/') return 0; if (c == 0) break; } } fprintf (stderr, "%s:%d: warning: unterminated comment\n", fname, lineno); return 0; } void stripname (char * path) { char * s = &path[strlen(path)]; while (s != path && *s != '/') *s-- = '\0'; if (s == path) strcpy (path, "."); else *s = '\0'; } char * stripslash (char * path) { char * strip = malloc (strlen (path) + 1), * s = path, * o = strip; int slash = 0; do { if (*s == '/') { if (!slash) *o++ = *s; slash = 1; } else { *o++ = *s; slash = 0; } } while (*s++ != '\0'); return strip; } static int is_code (const char * file) { // check whether file has a .c or .h extension char * s = strstr (file, ".c"); if (!s) s = strstr (file, ".h"); return s && (s[2] == '\0' || s[2] == '.'); } static int include (char * file, FILE * fin, FILE * fout) { fname = stripslash (file); paths[npath] = allocate (alloc, strlen (file) + 1); strcpy (paths[npath], file); stripname (paths[npath]); yyin = fin; myout = fout; yylineno = 1; scope = intypedef = 0; long header = fout ? ftell (fout) : 0; incode = is_code (file); // yy_flex_debug = 1; int ret = yylex(); yylex_destroy(); if (fout && incode) { // Assume the entire file is pure code fseek (fout, header, SEEK_SET); rewind (fin); char s[81]; while (fgets (s, 81, fin)) fputs (s, fout); } free (fname); return ret; } FILE * writepath (char * path, const char * mode); static void compdir (char * file, const char * dir) { push (file); while (stack >= 0) { char * path = pop(); FILE * fin = fopen (path, "r"); if (fin == NULL) { perror (path); exit (1); } FILE * fout = NULL; if (dir) { char * file = strstr (path, "//"); if (file) file += 2; else file = path; char * out = malloc (strlen (dir) + strlen (file) + 2); strcpy (out, dir); strcat (out, "/"); strcat (out, file); fout = writepath (out, "w"); if (fout == NULL) { perror (out); exit (1); } free (out); // all headers are included once only static int nf = 0; fprintf (fout, "#ifndef BASILISK_HEADER_%d\n" "#define BASILISK_HEADER_%d\n", nf, nf); nf++; fputs ("#line 1 \"", fout); singleslash (path, fout); fputs ("\"\n", fout); } if (include (path, fin, fout)) exit (1); fclose (fin); if (fout) { fputs ("\n#endif\n", fout); fclose (fout); } target = 0; } } static void prepend_path (char * path) { int j; for (j = npath; j > 0; j--) paths[j] = paths[j-1]; paths[0] = path; npath++; } void includes (int argc, char ** argv, char ** grid1, int * default_grid, int * dim, int * bg, int * lyrs, int * gpus, const char * dir) { int depend = 0, tags = 0, swig = 0; char * file = NULL, * output = NULL; int i; warninclude = 0; alloc = new_allocator(); char * basilisk_include_path = getenv ("BASILISK_INCLUDE_PATH"); if (basilisk_include_path) { basilisk_include_path = strdup (basilisk_include_path); char * s = strtok (basilisk_include_path, ":"); while (s) { prepend_path (s); s = strtok (NULL, ":"); } } for (i = 1; i < argc; i++) { if (!strncmp (argv[i], "-grid=", 6)) strcpy (grid, &argv[i][6]); else if (!strcmp (argv[i], "-MD")) depend = warninclude = 1; else if (!strcmp (argv[i], "-tags")) tags = warninclude = 1; else if (!strcmp (argv[i], "-python")) swig = 1; else if (!strcmp (argv[i], "-debug")) debug = 1; else if (!strcmp (argv[i], "-o")) output = argv[++i]; else if (!strncmp (argv[i], "-I", 2)) prepend_path (argv[i] + 2); else if (argv[i][0] != '-' && \ (tags || !strcmp (&argv[i][strlen(argv[i]) - 2], ".c"))) { if (file) { fprintf (stderr, "usage: include [OPTIONS] FILE.c\n"); exit (1); } file = argv[i]; } } if (depend && file) { if (!output) output = file; char ndep[80], * s = &output[strlen(output)-1]; while (*s != '.' && s != output) s--; if (output != file || s == output) /* generate dep files with suffixes included for -o option */ strcpy (ndep, output); else { *s = '\0'; strcpy (ndep, output); *s = '.'; } if (strlen(ndep) < 2 || strcmp (&ndep[strlen(ndep)-2], ".d")) { if (tags) strcat (ndep, ".tags.d"); else strcat (ndep, ".d"); } else output[strlen(ndep)-2] = '\0'; // strip trailing ".d"; fdepend = fopen (ndep, "w"); if (!fdepend) { perror (ndep); exit (1); } char * page = strstr (output, ".page"); if (tags && page) { *page = '\0'; fprintf (fdepend, "%s.tags:\t\\\n", output); *page = '.'; } else fprintf (fdepend, "%s:\t\\\n", output); } else if (tags && file) { if (!output) output = file; char ndep[80]; // strip trailing .page strcpy (ndep, output); char * page = strstr (ndep, ".page"); if (page) *page = '\0'; strcat (ndep, ".tags"); ftags = fopen (ndep, "w"); if (!ftags) { perror (ndep); exit (1); } } if (file) { if (swig) { char swigname[80]; strcpy (swigname, file); char * dot = strchr (swigname, '.'); *dot = '\0'; strcat (swigname, ".i"); swigfp = fopen (swigname, "w"); if (!swigfp) { fprintf (stderr, "include: could not open '%s': ", swigname); perror (""); exit (1); } *dot = '\0'; fprintf (swigfp, "%%module %s\n", swigname); fputs ("%include \"", swigfp); fputs (LIBDIR, swigfp); fputs ("/common.i\"\n", swigfp); } target = 1; compdir (file, dir); if (!hasgrid && is_code (file)) { char * path, gridpath[80] = "grid/"; strcat (gridpath, grid[0] != '\0' ? grid : "quadtree"); strcat (gridpath, ".h"); FILE * fp = openpath (gridpath, "r", &path); if (!fp) { fprintf (stderr, "include: invalid grid '%s': ", grid); perror (""); exit (1); } fclose (fp); target = 0; compdir (path, dir); hasgrid = 0; } if (swigfp) { char * path, pythonpath[80] = "python.h"; FILE * fp = openpath (pythonpath, "r", &path); if (!fp) { perror (pythonpath); exit (1); } fclose (fp); target = 0; compdir (path, dir); } if (ftags) { // reprocess the target file for keywords keywords_only = target = 1; compdir (file, dir); } char * path; FILE * fp = openpath ("common.h", "r", &path); assert (fp); fclose (fp); } if (fdepend) { fputc ('\n', fdepend); fclose (fdepend); } if (ftags) fclose (ftags); if (swigfp) fclose (swigfp); *grid1 = grid; *default_grid = !hasgrid; if (dimension > 0) *dim = dimension; *bg = bghosts; *lyrs = layers; *gpus = gpu; free (basilisk_include_path); free_allocator (alloc); }