Function prototype generator
Josh Marantz
josh at concept.viewlogic.com
Tue Jan 15 10:01:52 AEST 1991
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 at viewlogic.com
Why not pass the time by playing a little solitaire?
More information about the Comp.lang.c
mailing list