grahamr@azure.UUCP (Graham Ross) (12/22/83)
Cdecl is a little program that translates between C declarations and pseudo-English ones. The sources are in the form of a "shell archive". There are no version numbers on this program, but, "right now and for a limited time," this is guaranteed to be the latest version. It will compile and run on 2.9, 4.1a, and 4.2 (tested) and probably any Unix since V7 (untested). There's a good chance it is portable to other C environments that have stdio. I wrote it myself and guarantee it to be free of licensed software. Moreover, I hereby give it away again. Directions for installing cdecl: make a clean directory. make a file "x" in that directory. tear off the portion of this file that follows the perforated line and put it in "x". say "sh x" say "make" install cdecl in some bin directory install cdecl.1 in /usr/man/man1 the first time you run cdecl, say "help" for instructions Cheers, Graham Ross tektronix!tekmdp!grahamr ---------------------------------------------------------------------- echo "x - cdecl.1" cat >cdecl.1 <<'!!EOF!!' .de Ex .LP .RS 2 .. .de Ee .RE .LP .. .TH cdecl 1 .SH NAME cdecl \- Compose C declarations .SH SYNOPSIS .B cdecl .SH DESCRIPTION .I Cdecl is a program for encoding and decoding C type-declarations. It reads standard input for statements in the language described below. A transformation is made from that language to C. The results of this transformation are written on standard output. .PP .I Cdecl's scope is intentionally small. It doesn't help you figure out storage classes or initializations. .SH "COMMAND LANGUAGE" There are four statements in the language. The "declare" statement composes a C type-declaration from a verbose description. The "cast" statement composes a C type-cast as might appear in an expression. The "explain" statement decodes a C type-declaration or cast, producing a verbose description. The "help" statement describes the others. .PP The following grammar describes the language. In the grammar, words in "<>" are non-terminals, bare lower-case words are terminals that stand for themselves. Bare upper-case words are other lexical tokens: NOTHING means the empty string; NAME means a C identifier; NUMBER means a string of decimal digits; and NL means the new-line character. .LP .nf <program> ::= NOTHING | <program> <stat> NL <stat> ::= NOTHING | declare NAME as <decl> | cast NAME into <decl> | explain <huh> | help <decl> ::= array of <decl> | array of NUMBER <decl> | function returning <decl> | function ( <namelist> ) returning <decl> | pointer to <decl> | <type> <huh> ::= <type> <cdecl> | ( <type> <abstract> ) NAME <cdecl> ::= <cdecl1> | * <cdecl> <cdecl1> ::= <cdecl1> ( ) | <cdecl1> ( <namelist> ) | <cdecl1> [ ] | <cdecl1> [ NUMBER ] | ( <cdecl> ) | NAME <abstract> ::= NOTHING | ( ) | ( <abstract> ) ( ) | * <abstract> | <abstract> [ ] | <abstract> [ NUMBER ] | ( <abstract> ) <type> ::= <typename> | <modlist> | <modlist> <typename> | struct NAME | union NAME | enum NAME <typename> ::= int | char | double | float <modlist> ::= <modifier> | <modlist> <modifier> <modifier> ::= short | long | unsigned <namelist> ::= NAME | <namelist> , NAME .fi .SH EXAMPLES To declare an array of pointers to functions like malloc(3), do .Ex declare fptab as array of pointer to function returning pointer to char .Ee The result of this command is .Ex char *(*fptab[])() .Ee When you see this declaration in someone else's code, you can make sense out of it by doing .Ex explain char *(*fptab[])() .Ee The proper declaration for signal(2) cannot be described in .IR cdecl 's language. (It can't be described in C either!) An adequate declaration for most purposes is given by .Ex declare signal as function returning pointer to function returning int .Ee The function declaration that results has two sets of empty parentheses. The author of such a function might wonder where to put the parameters. .Ex declare signal as function (args) returning pointer to function returning int .Ee provides the solution: .Ex int (*signal(args))() .Ee You can use .I cdecl as you create a C program with an editor like vi(1). You simply type in the "English" version of the declaration and apply .I cdecl as a filter to the line. In vi(1), type "!!cdecl<esc>". .SH DIAGNOSTICS The declare and cast statements try to point out constructions that are not supported in C. In some cases, a guess is made as to what was really intended. In these cases, the C result is a toy declaration whose semantics will work only in Algol-68. Also, certain non-portable constructs are flagged. For some C processors, these declarations will work fine. .PP Syntax errors cause the parser to play dead until a newline is read. .SH "SEE ALSO" Section 8.4 of the C Reference Manual. .SH BUGS The pseudo-English syntax is excessively verbose. .PP There is a wealth of semantic checking that isn't being done. .PP .I Cdecl thinks all the declarations you utter are going to be used as external definitions. Some declaration contexts in C allow more flexibility than this. An example of this is: .Ex declare argv as array of array of char .Ee where .I cdecl responds with .Ex .nf Unsupported in C -- Inner array of unspecified size (maybe you mean "array of pointer") char argv[][] .fi .Ee !!EOF!! echo "x - cdgram.y" cat >cdgram.y <<'!!EOF!!' %{ #include <stdio.h> #define MB_SHORT 0001 #define MB_LONG 0002 #define MB_UNSIGNED 0004 #define MB_INT 0010 #define MB_CHAR 0020 #define MB_FLOAT 0040 #define MB_DOUBLE 0100 int modbits = 0; int arbdims = 1; char *savedtype = 0; char *savedname = 0; char *ds(), *cat(); char *index(), *malloc(); char prev = 0; %} %union { char *dynstr; struct { char *left; char *right; } halves; } %token DECLARE CAST INTO AS HELP EXPLAIN %token FUNCTION RETURNING POINTER TO ARRAY OF %token <dynstr> NAME NUMBER STRUCTUNION UNSIGNED LONG SHORT %token <dynstr> INT CHAR FLOAT DOUBLE %type <dynstr> mod_list tname type modifier namelist %type <dynstr> cdecl cdecl1 cast cdims adims c_type %type <halves> adecl %start prog %% prog : /* empty */ | prog stat ; stat : HELP '\n' { help(); } | DECLARE NAME AS adecl '\n' { printf("%s %s%s%s",savedtype,$4.left,$2,$4.right); #ifdef MKPROGRAM if (prev == 'f') printf("\n{\n}\n"); else printf(";\n"); #else printf("\n"); #endif free($4.left); free($4.right); free($2); } | CAST NAME INTO adecl '\n' { if (prev == 'f') unsupp("Cast into function", "cast into pointer to function"); else if (prev=='A' || prev=='a') unsupp("Cast into array","cast into pointer"); printf("(%s",savedtype); if (strlen($4.left)+strlen($4.right)) printf(" %s%s",$4.left,$4.right); printf(")%s\n",$2); free($4.left); free($4.right); free($2); } | EXPLAIN type cdecl '\n' { printf("declare %s as %s%s\n",savedname,$3,$2); } | EXPLAIN '(' type cast ')' NAME '\n' { printf("cast %s into %s%s\n",$6,$4,$3); } | '\n' | error '\n' { yyerrok; } ; cdecl : cdecl1 | '*' cdecl { $$ = cat($2,ds("pointer to "),NULL); } ; cdecl1 : cdecl1 '(' ')' { $$ = cat($1,ds("function returning "),NULL); } | cdecl1 '(' namelist ')' { $$ = cat($1,ds("function ("), cat($3,ds(") returning "),NULL)); } | cdecl1 cdims { $$ = cat($1,ds("array of "),$2); } | '(' cdecl ')' { $$ = $2; } | NAME { savename($1); $$ = ds(""); } ; namelist : NAME | namelist ',' NAME { $$ = cat($1,ds(","),$3); } ; cast : /* empty */ { $$ = ds(""); } | '(' ')' { $$ = ds("function returning "); } | '(' cast ')' '(' ')' { $$ = cat($2,ds("function returning "),NULL); } | '*' cast { $$ = cat($2,ds("pointer to "),NULL); } | cast cdims { $$ = cat($1,ds("array of "),$2); } | '(' cast ')' { $$ = $2; } ; cdims : '[' ']' { $$ = ds(""); } | '[' NUMBER ']' { $$ = cat($2,ds(" "),NULL); } ; adecl : FUNCTION RETURNING adecl { if (prev == 'f') unsupp("Function returning function", "function returning pointer to function"); else if (prev=='A' || prev=='a') unsupp("Function returning array", "function returning pointer"); $$.left = $3.left; $$.right = cat(ds("()"),$3.right,NULL); prev = 'f'; } | FUNCTION '(' namelist ')' RETURNING adecl { if (prev == 'f') unsupp("Function returning function", "function returning pointer to function"); else if (prev=='A' || prev=='a') unsupp("Function returning array", "function returning pointer"); $$.left = $6.left; $$.right = cat(ds("("),$3,ds(")")); $$.right = cat($$.right,$6.right,NULL); prev = 'f'; } | ARRAY OF adims adecl { if (prev == 'f') unsupp("Array of function", "array of pointer to function"); else if (prev == 'a') unsupp("Inner array of unspecified size", "array of pointer"); if (arbdims) prev = 'a'; else prev = 'A'; $$.left = $4.left; $$.right = cat($3,$4.right,NULL); } | POINTER TO adecl { if (prev == 'a') unsupp("Pointer to array of unspecified size", "pointer to object"); if (prev=='a' || prev=='A' || prev=='f') { $$.left = cat($3.left,ds("(*"),NULL); $$.right = cat(ds(")"),$3.right,NULL); } else { $$.left = cat($3.left,ds("*"),NULL); $$.right = $3.right; } prev = 'p'; } | type { savetype($1); $$.left = ds(""); $$.right = ds(""); prev = 't'; } ; adims : /* empty */ { arbdims = 1; $$ = ds("[]"); } | NUMBER { arbdims = 0; $$ = cat(ds("["),$1,ds("]")); } ; type : tinit c_type { mbcheck(); $$ = $2; } ; tinit : /* empty */ { modbits = 0; } ; c_type : mod_list { $$ = $1; } | tname { $$ = $1; } | mod_list tname { $$ = cat($1,ds(" "),$2); } | STRUCTUNION NAME { $$ = cat($1,ds(" "),$2); } ; tname : INT { modbits |= MB_INT; $$ = $1; } | CHAR { modbits |= MB_CHAR; $$ = $1; } | FLOAT { modbits |= MB_FLOAT; $$ = $1; } | DOUBLE { modbits |= MB_DOUBLE; $$ = $1; } ; mod_list: modifier { $$ = $1; } | mod_list modifier { $$ = cat($1,ds(" "),$2); } ; modifier: UNSIGNED { modbits |= MB_UNSIGNED; $$ = $1; } | LONG { modbits |= MB_LONG; $$ = $1; } | SHORT { modbits |= MB_SHORT; $$ = $1; } ; %% #include "cdlex.c" #define LORS (MB_LONG|MB_SHORT) #define UORL (MB_UNSIGNED|MB_LONG) #define UORS (MB_UNSIGNED|MB_SHORT) #define CORL (MB_CHAR|MB_LONG) #define CORS (MB_CHAR|MB_SHORT) #define CORU (MB_CHAR|MB_UNSIGNED) mbcheck() { if ((modbits&LORS) == LORS) unsupp("conflicting 'short' and 'long'",NULL); if ((modbits&UORL) == UORL) unport("unsigned with long (Ritchie)"); if ((modbits&UORS) == UORS) unport("unsigned with short (Ritchie)"); if ((modbits&CORL) == CORL) unsupp("long char",NULL); if ((modbits&CORS) == CORS) unsupp("short char",NULL); if ((modbits&CORU) == CORU) unport("unsigned char (Ritchie)"); } savetype(s) char *s; { savedtype = s; } savename(s) char *s; { savedname = s; } !!EOF!! echo "x - cdlex.l" cat >cdlex.l <<'!!EOF!!' %{ #include <ctype.h> char *visible(); %} N [0-9] A [A-Z_a-z] AN [0-9A-Z_a-z] %% array return ARRAY; as return AS; cast return CAST; declare return DECLARE; explain return EXPLAIN; function return FUNCTION; help return HELP; into return INTO; of return OF; pointer return POINTER; returning return RETURNING; to return TO; char { yylval.dynstr = ds(yytext); return CHAR; } double { yylval.dynstr = ds(yytext); return DOUBLE; } enum { yylval.dynstr = ds(yytext); return STRUCTUNION; } float { yylval.dynstr = ds(yytext); return FLOAT; } int { yylval.dynstr = ds(yytext); return INT; } long { yylval.dynstr = ds(yytext); return LONG; } short { yylval.dynstr = ds(yytext); return SHORT; } struct { yylval.dynstr = ds(yytext); return STRUCTUNION; } union { yylval.dynstr = ds(yytext); return STRUCTUNION; } unsigned { yylval.dynstr = ds(yytext); return UNSIGNED; } {A}{AN}* { yylval.dynstr = ds(yytext); return NAME; } {N}+ { yylval.dynstr = ds(yytext); return NUMBER; } [\t ] ; [,*[\]()\n] return *yytext; . { printf("bad character '%s'\n",visible(*yytext)); return *yytext; } %% char * visible(c) { static char buf[5]; c &= 0377; if (isprint(c)) { buf[0] = c; buf[1] = '\0'; } else sprintf(buf,"\\%02o",c); return buf; } !!EOF!! echo "x - cdsupp.c" cat >cdsupp.c <<'!!EOF!!' #include <stdio.h> char *malloc(); main() { yyparse(); } unsupp(s,hint) char *s,*hint; { printf("Warning: Unsupported in C -- %s\n",s); if (hint != NULL) printf("\t(maybe you mean \"%s\")\n",hint); } unport(s) char *s; { printf("Warning: Non-portable construction -- %s\n",s); } yyerror(s) char *s; { printf("%s\n",s); } yywrap() { return 1; } /* * Support for dynamic strings: * cat creates a string from three input strings. * The input strings are freed by cat (so they better have been malloced). * ds makes a malloced string from one that's not. */ char * cat(s1,s2,s3) char *s1,*s2,*s3; { register char *newstr; register unsigned len = 0; if (s1 != NULL) len = strlen(s1) + 1; if (s2 != NULL) len += strlen(s2); if (s3 != NULL) len += strlen(s3); newstr = malloc(len); if (s1 != NULL) { strcpy(newstr,s1); free(s1); } if (s2 != NULL) { strcat(newstr,s2); free(s2); } if (s3 != NULL) { strcat(newstr,s3); free(s3); } return newstr; } char * ds(s) char *s; { register char *p; p = malloc((unsigned)(strlen(s)+1)); strcpy(p,s); return p; } static char *helptext[] = { "command:\n", " declare <name> as <english>\n", " cast <name> into <english>\n", " explain <gibberish>\n", "english:\n", " function returning <english>\n", " function ( <namelist> ) returning <english>\n", " array of <english>\n", " array of <number> <english>\n", " pointer to <english>\n", " <type>\n", "type:\n", " <C-type>\n", " <modlist>\n", " <modlist> <C-type>\n", " <sue> <name>\n", "namelist:\n", " <name>\n", " <namelist> , <name>\n", "modlist:\n", " <modifier>\n", " <modlist> , <modifier>\n", "name is a C identifier\n", "gibberish is a C declaration, like \"int *x\" or \"(int *)x\"\n", "C-type is int, char, double or float\n", "modifier is short, long or unsigned\n", "sue is struct, union or enum\n", NULL }; help() { register char **p; for (p=helptext; *p!=NULL; p++) printf("\t%s",*p); } !!EOF!! echo "x - makefile" cat >makefile <<'!!EOF!!' cdecl : cdgram.o cdsupp.o cc -o cdecl cdgram.o cdsupp.o cdgram.o : cdgram.c cdlex.c cdlex.c : cdlex.l lex cdlex.l mv lex.yy.c cdlex.c cdgram.c : cdgram.y yacc cdgram.y mv y.tab.c cdgram.c !!EOF!!