/*{{{ includes */ #include #include #include #include #include #include #include #include "aterm2.h" #include "atb-tool.h" /*}}} */ /*{{{ defines */ #define strprefix(a,b) (!strncmp((a),(b),strlen(b))) /*}}} */ /*{{{ globals */ static char *prg = NULL; /*}}} */ /*{{{ char *ident_to_C(char *ident) */ /** * Convert a ToolBus identifier to a C identifier */ char *ident_to_C(char *ident) { int i; static char buf[BUFSIZ]; strncpy(buf, ident, BUFSIZ-1); for(i=0; buf[i]; i++) if(buf[i] == '-') buf[i] = '_'; return buf; } /*}}} */ /*{{{ char *uppercase(char *ident) */ /** * Convert a string to uppercase */ char *uppercase(char *ident) { int i; static char buf[BUFSIZ]; for(i=0; ident[i] && i < BUFSIZ; i++) buf[i] = toupper(ident[i]); buf[i] = '\0'; return buf; } /*}}} */ /*{{{ ATermList read_tifs(int fd, const char *tool) */ /** * Read some tifs from a file descriptor */ ATermList read_tifs(int fd, const char *tool) { ATerm tif; ATermList tifs = ATempty; ATermPlaceholder ph; char *primitive, *name; do { tif = ATBreadTerm(fd); if(ATmatch(tif, ",)>", &primitive, &ph, NULL)) { if(strprefix(primitive, "rec-") && ATmatch((ATerm)ph, "", &name) && streq(name, tool)) tifs = ATinsert(tifs, tif); } } while(!ATmatch(tif, "end-of-tifs")); return tifs; } /*}}} */ /*{{{ ATermList generalize_tifs(ATermList tifs) */ /** * Generalize incoming tifs. */ ATermList generalize_tifs(ATermList tifs) { ATermList result = ATempty; while(!ATisEmpty(tifs)) { int i; char *name; ATermList newargs = ATempty; ATerm tif = ATgetFirst(tifs), newtif, tool; ATermAppl appl; if(ATmatch(tif, "rec-terminate(,)", NULL, NULL) || ATmatch(tif, "rec-ack-event(,)", NULL, NULL)) { result = ATinsert(result, tif); } else if(ATmatch(tif, ",)>", &name, &tool, &appl)) { Symbol sym = ATgetSymbol(appl); for(i=ATgetArity(sym)-1; i>=0; --i) { ATerm arg = ATgetArgument(appl, i); if(ATisEqual(arg, ATparse("")) || ATisEqual(arg, ATparse(""))) newargs = ATinsert(newargs, arg); else newargs = ATinsert(newargs, ATparse("")); } newtif = ATmake(",)>)>", name, tool, ATgetName(sym), newargs); result = ATinsert(result, newtif); } tifs = ATgetNext(tifs); } return result; } /*}}} */ /*{{{ ATermList unify_arguments(ATermList args1, ATermList args2) */ /** * Unify two lists of arguments. */ ATermList unify_arguments(ATermList args1, ATermList args2) { int i = ATgetLength(args1); ATermList result = ATempty; if(ATgetLength(args2) != i) return NULL; for(--i; i>=0; i--) { ATerm arg1 = ATelementAt(args1, i); ATerm arg2 = ATelementAt(args2, i); /* When arg1 and arg2 are both or , leave them alone, otherwise use */ if((ATisEqual(arg1, ATparse("")) || ATisEqual(arg1, ATparse(""))) && ATisEqual(arg1, arg2)) { result = ATinsert(result, arg1); } else { result = ATinsert(result, ATparse("")); } } return result; } /*}}} */ /*{{{ ATermList unify_tifs(ATermList tifs) */ /** * Unify a list of tifs. */ ATermList unify_tifs(ATermList tifs) { char *primitive; ATermList result = ATempty; ATerm tool; while(!ATisEmpty(tifs)) { ATermList rest; char *mgu_name, *cur_name; ATermList mgu_args, cur_args; ATerm mgu, tif = ATgetFirst(tifs); if(ATmatch(tif, "rec-terminate(,)", NULL, NULL) || ATmatch(tif, "rec-ack-event(,)", NULL, NULL)) { result = ATinsert(result, tif); tifs = ATgetNext(tifs); continue; } /* Now built-in primitive, must be primitive rec-do or rec-eval */ if(!ATmatch(tif, ",)>)>", &primitive, &tool, &mgu_name, &mgu_args)) ATerror("illegal tif: %t\n", tif); rest = ATgetNext(tifs); tifs = ATempty; for( ; !ATisEmpty(rest); rest=ATgetNext(rest)) { char *cur_prim; ATerm cur_tool, cur = ATgetFirst(rest); if(!ATmatch(cur, ",)>)>", &cur_prim, &cur_tool, &cur_name, &cur_args) || !streq(cur_name, mgu_name)) { tifs = ATinsert(tifs, cur); continue; } assert(ATisEqual(cur_tool, tool)); if(!streq(cur_prim, primitive)) ATerror("primitives collide: %t vs. %t\n", tif, cur); mgu_args = unify_arguments(mgu_args, cur_args); if(!mgu_args) ATerror("arity mismatch: %t vs. %t\n", tif, cur); } mgu = ATmake(")>", mgu_name, mgu_args); result = ATinsert(result, ATmake(",)>", primitive, tool, mgu)); } return result; } /*}}} */ /*{{{ void generate_prologue(FILE *f, char *tool, char *msg) */ /** * Generate file prologue (with timestamp!) */ void generate_prologue(FILE *f, char *tool, char *msg) { time_t t = time(NULL); fprintf(f, "/**\n"); fprintf(f, " * This file is generated by %s. Do not edit!\n", prg); fprintf(f, " * Generated from tifs for tool '%s'\n", tool); fprintf(f, " * %s generated at %s", msg, ctime(&t)); fprintf(f, " */\n\n"); } /*}}} */ /*{{{ void generate_argument_list(FILE *f, ATermList args) */ /** * Generate a list of arguments given a list of terms. */ void generate_argument_list(FILE *f, ATermList args) { fprintf(f, "int conn"); while(!ATisEmpty(args)) { ATerm arg = ATgetFirst(args); args = ATgetNext(args); fprintf(f, ", "); if(ATgetType(arg) != AT_PLACEHOLDER) fprintf(f, "ATerm"); else { arg = ATgetPlaceholder((ATermPlaceholder)arg); if(ATmatch(arg, "int")) fprintf(f, "int"); else if(ATmatch(arg, "str")) fprintf(f, "char *"); else fprintf(f, "ATerm"); } } } /*}}} */ /*{{{ void generate_declarations(FILE *f, ATermList tifs) */ /** * Generate function declarations. */ void generate_declarations(FILE *f, ATermList tifs) { char *name; ATermList args; fprintf(f, "/* Prototypes for functions called from the event handler */\n"); while(!ATisEmpty(tifs)) { ATerm tif = ATgetFirst(tifs); tifs = ATgetNext(tifs); if(ATmatch(tif, "rec-terminate(,)", NULL, NULL)) { fprintf(f, "void rec_terminate(int conn, ATerm);\n"); } else if(ATmatch(tif, "rec-ack-event(,)", NULL, NULL)) { fprintf(f, "void rec_ack_event(int conn, ATerm);\n"); } else if(ATmatch(tif, "rec-do(,)>)", NULL, &name, &args)) { fprintf(f, "void %s(", ident_to_C(name)); generate_argument_list(f, args); fprintf(f, ");\n"); } else if(ATmatch(tif, "rec-eval(,)>)", NULL, &name, &args)) { fprintf(f, "ATerm %s(", ident_to_C(name)); generate_argument_list(f, args); fprintf(f, ");\n"); } /* Ignoring other patterns */ } } /*}}} */ /*{{{ void generate_header(FILE *f, ATermList tifs, char *tool) */ /** * Generate the header file. */ void generate_header(FILE *f, ATermList tifs, char *tool) { char *protect_def = uppercase(ident_to_C(tool)); generate_prologue(f, tool, "Headerfile"); fprintf(f, "#ifndef _%s_H\n", protect_def); fprintf(f, "#define _%s_H\n\n", protect_def); fprintf(f, "#include \n\n"); generate_declarations(f, tifs); fprintf(f, "extern ATerm %s_handler(int conn, ATerm term);\n", ident_to_C(tool)); fprintf(f, "extern ATerm %s_checker(int conn, ATerm sigs);\n", ident_to_C(tool)); fprintf(f, "\n#endif\n"); } /*}}} */ /*{{{ int generate_signature(FILE *f, ATermList tifs) */ int generate_signature(FILE *f, ATermList tifs) { int count = ATgetLength(tifs); fprintf(f, "#define NR_SIG_ENTRIES\t%d\n\n", count); fprintf(f, "static char *signature[NR_SIG_ENTRIES] = {\n"); while(!ATisEmpty(tifs)) { ATerm tif = ATgetFirst(tifs); tifs = ATgetNext(tifs); ATfprintf(f, " \"%t\",\n", tif); } fprintf(f, "};\n\n"); return count; } /*}}} */ /*{{{ void generate_checker(FILE *f, char *tool) */ /** * Generate the signature checker. */ void generate_checker(FILE *f, char *tool, int nrsigs) { fprintf(f, "/* Check the signature of the tool '%s' */\n", tool); fprintf(f, "ATerm %s_checker(int conn, ATerm siglist)\n", ident_to_C(tool)); fprintf(f, "{\n"); fprintf(f, " return ATBcheckSignature(siglist, signature, " "NR_SIG_ENTRIES);\n"); fprintf(f, "}\n\n"); } /*}}} */ /*{{{ void generate_variables(FILE *f, ATermList tifs) */ /** * Generate variable declarations */ void generate_variables(FILE *f, ATermList tifs) { ATermList args; int i; int max_strings = 0; int max_ints = 0; int max_terms = 0; int nrints, nrstrings, nrterms; /* Calculate the maximum number of ints, strings, and terms. */ while(!ATisEmpty(tifs)) { ATerm tif = ATgetFirst(tifs); tifs = ATgetNext(tifs); nrints = 0; nrstrings = 0; nrterms = 0; if(ATmatch(tif, "rec-terminate(,)", NULL, NULL)) { max_terms = MAX(1, max_terms); } else if(ATmatch(tif, "rec-ack-event(,)", NULL, NULL)) { max_terms = MAX(1, max_terms); } else if(ATmatch(tif, "rec-do(,)>)", NULL, NULL, &args) || ATmatch(tif, "rec-eval(,)>)", NULL, NULL, &args)) { while(!ATisEmpty(args)) { ATerm arg = ATgetFirst(args); args = ATgetNext(args); if(ATgetType(arg) != AT_PLACEHOLDER) { nrterms++; } else { arg = ATgetPlaceholder((ATermPlaceholder)arg); if(ATmatch(arg, "int")) nrints++; else if(ATmatch(arg, "str")) nrstrings++; else nrterms++; } } max_ints = MAX(nrints, max_ints); max_strings = MAX(nrstrings, max_strings); max_terms = MAX(nrterms, max_terms); } /* Ignoring other patterns */ } /* Generate variable declarations */ fprintf(f, " /* We need some temporary variables during matching */\n"); if(max_ints > 0) { fprintf(f, " int i0"); for(i=1; i 0) { fprintf(f, " char *s0"); for(i=1; i 0) { fprintf(f, " ATerm t0"); for(i=1; i,)", NULL, NULL)) { generate_match(f, ATparse("rec-terminate()"), "rec_terminate", (ATermList)ATparse("[]"), ATfalse); } else if(ATmatch(tif, "rec-ack-event(,)", NULL, NULL)) { generate_match(f, ATparse("rec-ack-event()"), "rec_ack_event", (ATermList)ATparse("[]"), ATfalse); } else if(ATmatch(tif, "rec-do(,)>)", NULL, &name, &args)) { ATerm pattern = ATmake("rec-do()>)", name, args); generate_match(f, pattern, ident_to_C(name), args, ATfalse); } else if(ATmatch(tif, "rec-eval(,)>)", NULL, &name, &args)) { ATerm pattern = ATmake("rec-eval()>)", name, args); generate_match(f, pattern, ident_to_C(name), args, ATtrue); } else { /* Ignoring other patterns */ } } fprintf(f, " if(ATmatch(term, \"rec-do(signature(,))\", " "&in, &out)) {\n"); fprintf(f, " ATerm result = %s_checker(conn, in);\n", ident_to_C(tool)); fprintf(f, " if(!ATmatch(result, \"[]\"))\n"); fprintf(f, " ATfprintf(stderr, \"warning: not in input signature:" "\\n\\t%%\\n\\tl\\n\", result);\n"); fprintf(f, " return NULL;\n"); fprintf(f, " }\n\n"); fprintf(f, " ATerror(\"tool %s cannot handle term %%t\", term);\n", tool); fprintf(f, " return NULL; /* Silence the compiler */\n"); fprintf(f, "}\n\n"); } /*}}} */ /*{{{ void generate_code(FILE *f, ATermList tifs, orig, char *tool, header) */ /** * Generate tif.c code file. */ void generate_code(FILE *f, ATermList tifs, ATermList original, char *tool, char *header) { int count; generate_prologue(f, tool, "Implementation"); fprintf(f, "#include \"%s\"\n\n", header); count = generate_signature(f, original); generate_handler(f, tifs, tool); generate_checker(f, tool, count); } /*}}} */ /*{{{ static void usage(char *prg) */ /** * Print usage information and exit. */ static void usage(char *prg) { fprintf(stderr, "usage: %s -tool [options] .tifs\n", prg); fprintf(stderr, "options:\n"); fprintf(stderr, " -handler " "specify default handler name\n"); fprintf(stderr, " -checker " "specify default signature checker name\n"); fprintf(stderr, " -output " "specify which .h and .c files to generate\n"); exit(1); } /*}}} */ /*{{{ int main(int argc, char *argv[]) */ /** * Process the commandline arguments and start tiffin'. */ int main(int argc, char *argv[]) { int i, fd; ATerm topOfStack; char *tool = NULL, *handler = NULL, *checker = NULL, *output = NULL; char *tifname = NULL; ATermList tifs, original; char *p, *codename, *headername; FILE *file; char buf[2][BUFSIZ]; int ATargc = 2; char *ATargv[] = { "", "-silent" }; prg = argv[0]; for(i=1; i