dalenber@p.cs.uiuc.edu (06/24/88)
For the past few weeks I have been writing a C-function prototype generator. I would say that it is now about 85% finished; it does just about everything I want it to do. The only area where it is really lacking is in handling array arguments to functions. For example, given the function definition: int func (arg) char arg[]; { /* ... */ } It will generate the prototype: extern int func (char arg[]); For this case, it is obvious that the argument in the prototype should be "char *arg", but what about multi-dimensional arrays? Should: int func (arg) int arg[][10]; { /* ... */ } produce: extern int func (char **arg); ? I seem to remember some notes about "pointer to array" being a legal type in "ANSI C"; how would definitions of this type differ from those above? Would it be legal to define a function returning a pointer to an array of int? And if so, how would such a thing be written? Should I just convert all array references to pointer references? If any ANSI-C gurus can answer these questions, I'll be eternally greatful (well, maybe not eternally :-) ). The second reason I am posting this note is ask the C community at large, "what features would you want in a prototype generator?" At present, the program accepts function definitions in either the old fashioned style used in the examples above, or the new ANSI style, formatted in any manner, and massages the information into prototype format. It produces output in the form #ifdef __STDC__ <function prototypes> #else <old-fashioned function allusions> #endif The token "__STDC__" can be set at run time to be any symbol. Also at run time, options can be given to exclude any static functions from the output, as well as the prototype for "main". What I'd like to know is, what else should it do? I'm particularly unsure how to handle multiple files. Should I generate one large output on standard-output, or into a user specified file; or should I automatically put the output for "file.c" into "file.p" (or something of that nature). A good user interface is very important, and I know that what I find natural may seem odd to others. Please send me your comments, and ideas. In a few weeks, after I've finished implementing everything, I'll post the source in comp.sources.{unix or misc} for all the world to enjoy. Much thanks in advance for any help I receive. Russel Dalenberg UUCP: {pur-ee,convex,ihnp4}!uiucdcs!dalenber ARPA: dalenber@p.cs.uiuc.edu CSNET: dalenber@UIUC.CSNET
jfh@rpp386.UUCP (John F. Haugh II) (06/26/88)
i already have one of these which was recently posted someplace on the net. is this a second version or what??? - john. -- John F. Haugh II +--------- Cute Chocolate Quote --------- HASA, Division "S" | "USENET should not be confused with UUCP: killer!rpp386!jfh | something that matters, like CHOCOLATE" DOMAIN: jfh@rpp386.uucp | -- with my apologizes
swonk@ccicpg.UUCP (Glen Swonk) (05/12/89)
Can anyone point me to a source for generating function protypes? I know some compilers can do this but I would like a portable mechanism for doing this. The real reason for doing this is to generate a database for the Norton Guides. Also, would this be an easier task to do with lex and yacc or with a C program? thanks, glenn -- Glenn L. Swonk CCI Computers (714)458-7282 9801 Muirlands Boulevard Irvine, CA 92718 uunet!ccicpg!swonk
josh@concept.viewlogic.com (Josh Marantz) (01/15/91)
The following is a program I wrote to parse a K&R C program and generate function prototypes for all the functions. I've found that it works well in many circumstances. It is immune to most programming style issues that plague quick & dirty C parsers, but it has some limitations. It takes the C file foo.i after its been processed by CPP, and generates two files: foo.ext and foo.sta, to hold the external definitions and the static definitions. The only known limitation is that I found it too hard to parse function declarations that take function pointers as arguments: int foo(x) int (*x) (); { } You can, however, do this: typedef int (*intproc) (); int foo(x) intproc x; { } I've found this is not a bad limitation in practice, and I always define new types function pointers to be passed as arguments. It generates prototypes of the form extern int foo vlARGS((intproc)); You should define vlARGS to as #ifdef __STDC__ #define vlARGS(a) a #else #define vlARGS(a) #endif Then you can use the same headers for both Ansi and K&R. Other than the known limitation, this program has been a workhorse for me for a couple of years now. I never write function declarations manually anymore. I've avoided posting it because I'm a little ashamed of the style, and I haven't had time to fix it up, and because of the procedure pointer limitation. But it seems like the demand for such a beast is high, so here it is. First, a script to run it on unix (unix is not required -- this runs fine under DOS using the MS-C preprocessor): mkproto: ---------------------------------------------------------------- #!/bin/csh /lib/cpp $1.c $argv[2-] > $1.i proto $1 ---------------------------------------------------------------- Now, the proto.c file: ---------------------------------------------------------------- #include <stdio.h> #include <ctype.h> #define MAX_VARS 20 #define MAX_CHARS 80 char inname[MAX_CHARS]; char word[MAX_CHARS]; char var[MAX_CHARS]; char type[MAX_CHARS]; char proc_name[MAX_CHARS]; char vars[MAX_VARS][MAX_CHARS]; char types[MAX_VARS][MAX_CHARS]; int braces = 0, parens = 0, brackets = 0, line_count = 1; int bslash = 0, squote = 0, dquote = 0; int num_var, num_matches; static FILE *infile, *statics, *externs; #define IS_IDENT(c) \ (isalnum ((c)) || ((c) == '$') || ((c) == '_') || ((c) == '*') || \ ((c) == '[') || ((c) == ']') || ((c) == '+')) static FILE *open_file(name, ext, mode) char *name, *ext, *mode; { char fname[80]; FILE *f; sprintf (fname, "%s.%s", name, ext); if ((f = fopen (fname, mode)) == NULL) { perror (fname); exit (1); } /* if */ return (f); } /* static FILE *open_file */ main(argc, argv) int argc; char *argv[]; { if (argc != 2) { fprintf (stderr, "Usage: %s module\n", argv[0]); fprintf (stderr, "Reads for cpp output module.i.\n"); fprintf (stderr, "Writes external prototypes to module.ext.\n"); fprintf (stderr, "Writes static prototypes to module.sta.\n"); exit (1); } /* if */ sprintf (inname, "%s.i", argv[1]); infile = open_file (argv[1], "i", "r"); externs = open_file (argv[1], "ext", "w"); statics = open_file (argv[1], "sta", "w"); while (getword () != EOF) { if ((strcmp (word, "extern") == 0) || (strcmp (word, "{") == 0) || (strcmp (word, ";") == 0) || (strcmp (word, ",") == 0) || (strcmp (word, "pragma") == 0) || (strcmp (word, "typedef") == 0)) next_statement (); else if (IS_IDENT (word[0])) try_proc (); else error ("Unexpected token"); } /* while */ } /* main */ next_statement() { while ((braces != 0) || ((strcmp (word, ";") != 0) && (strcmp (word, "}") != 0))) getword (); } /* next_statement */ getword() { int c, i, done, x; i = 0; done = 0; while (!done) { c = getc (infile); if (c == EOF) { if (braces || parens || brackets) fprintf (stderr, "%s: %d: {}=%d, ()=%d, []=%d\n", inname, line_count, braces, parens, brackets); fclose (infile); fclose (statics); fclose (externs); exit (0); } /* if */ else if (IS_IDENT (c) && !dquote && !squote) { word[i] = c; i++; } /* if */ else if (isspace (c) || (c == '\f') && !dquote && !squote) { if (c == '\n') line_count++; if (i > 0) { word[i] = 0; c = 0; done = 1; } /* if */ } /* else if */ else if ((c == '#') && !dquote && !squote) { if ((fgets (word, MAX_CHARS - 1, infile) == NULL) || ((sscanf (word, " %d \"%s\"", &line_count, inname) != 2) && (sscanf (word, "line %d \"%s\"", &line_count, inname) != 2))) { x = strlen (word) - 1; if ((x >= 0) && (word[x] == '\n')) word[x] = '\0'; error ("Unknown # directive"); line_count++; } else { x = strlen (inname) - 1; if ((x >= 0) && (inname[x] == '"')) inname[x] = '\0'; } } /* else if */ else { if (i == 0) { switch (c) { case '{': if (!dquote && !squote) braces++; break; case '}': if (!dquote && !squote) braces--; break; case '(': if (!dquote && !squote) parens++; break; case ')': if (!dquote && !squote) parens--; break; case '[': if (!dquote && !squote) brackets++; break; case ']': if (!dquote && !squote) brackets--; break; case '"': if (!bslash && !squote) dquote = !dquote; break; case 39: if (!bslash && !dquote) squote = !squote; break; case '\\': bslash = !bslash; break; } /* switch */ if (c != '\\') bslash = 0; word[0] = c; word[1] = '\0'; if (!dquote && !squote) done = 1; } /* if */ else { ungetc (c, infile); word[i] = 0; c = 0; done = 1; } /* else */ } /* else if */ } /* while */ return (c); } /* getword */ try_proc() { int i, end_of_type; FILE *f; if (strcmp (word, "static") == 0) { proc_name[0] = '\0'; f = statics; } /* if */ else { strcpy (proc_name, "extern "); f = externs; } /* else */ do { strcat (proc_name, word); strcat (proc_name, " "); } while (getword () == 0); if ((strcmp (word, ";") == 0) || /* variable/type declaration */ (strcmp (word, ",") == 0) || /* multi var/type declaration */ (strcmp (word, "[") == 0) || /* array initializer */ (strcmp (word, "=") == 0) || /* static initializer */ (strcmp (word, "{") == 0) || /* struct/union definition */ (strcmp (word, ":") == 0)) /* label */ { next_statement (); return; } /* if */ if (strcmp (word, "(") != 0) {error ("( expected"); return;} /* Accumulate variable names */ num_var = 0; do { if (getword () != 0) { if ((num_var == 0) && (strcmp (word, ")") == 0)) break; error ("expected arg"); return; } if (word[0] == '*') { /* Procedure ptr declaration */ next_statement (); return; } strcpy (vars[num_var], word); strcpy (types[num_var], "int"); num_var++; } while (getword () == ','); if (IS_IDENT (word[0]) || /* identifier */ (strcmp (word, "(") == 0)) /* function argument decl */ { next_statement (); return; } /* if */ if (strcmp (word, ")") != 0) {error (") expected"); return;} if (num_var == 0) { getword (); if (strcmp (word, "{") == 0) { /* Proc with no args */ fprintf (f, "%svlARGS((void));\n", proc_name); next_statement (); } /* if */ else if ((strcmp (word, ";") == 0) || (strcmp (word, ",") == 0)) ; /* proc declaration w/o extern */ else error ("Unexpected termination of procedure declaration"); return; } /* if */ /* Find argument type declarations */ end_of_type = 9999; for (num_matches = 0; num_matches < num_var;) { type[0] = 0; while (getword () == 0) { end_of_type = strlen (type) - 1; strcat (type, word); strcpy (var, word); strcat (type, " "); } /* while */ if (end_of_type < 1) { error ("Empty declaration"); return; } /* if */ /* If we hit a ; the first time through, we are looking at an existing prototyped procedure declaration! Punt! */ if ((strcmp (word, ";") == 0) && (end_of_type == 9999)) return; if ((strcmp (word, ";") != 0) && (strcmp (word, ",") != 0)) { strcpy (word, proc_name); error ("arg type not found, declaration supressed"); return; } /* if */ else { type[end_of_type] = '\0'; match_variable (var); while (strcmp (word, ",") == 0) { getword (); match_variable (word); getword (); } /* while */ if (strcmp (word, ";") != 0) { error ("Arg decl did not end with ';'"); return; } /* if */ } /* else */ } /* for */ fprintf (f, "%svlARGS((", proc_name); for (i = 0; i < num_var - 1; i++) fprintf (f, "%s, ", types[i]); fprintf (f, "%s));\n", types[num_var - 1]); next_statement (); } /* try_proc */ match_variable(compare) char *compare; { int i; char prefix[10], suffix[10]; prefix[0] = suffix[0] = '\0'; while (*compare == ' ') compare++; while (*compare == '*') { compare++; strcat (prefix, "*"); } /* while */ while ((strlen (compare) > 2) && (strcmp (&compare[strlen (compare) - 2], "[]") == 0)) { compare[strlen (compare) - 2] = '\0'; strcat (suffix, "[]"); } /* while */ for (i = 0; i < num_var; i++) { if (strcmp (compare, vars[i]) == 0) { if (prefix[0] == '\0') sprintf (types[i], "%s%s", type, suffix); else sprintf (types[i], "%s %s%s", type, prefix, suffix); num_matches++; return; } /* if */ } /* for */ sprintf (word, "%s(%s)", proc_name, compare); error ("Failed to match type"); } /* match_variable */ error(s) char *s; { fprintf (stderr, "%s: %d: %s: %s\n", inname, line_count, s, word); next_statement (); } /* error */ ---------------------------------------------------------------- Good luck, and feel free to make improvements and send them back to me! -- Joshua Marantz Viewlogic Systems, Inc. josh@viewlogic.com Why not pass the time by playing a little solitaire?