int *pf()
int (*pf)()
char (*apf[])()
char **argv
int (*daytab)[13]
int *daytab[13]
void *comp()
void (*comp)()
char (*(*x())[])()
char (*(*x[3])())[5]
#include <stdio.h> // for printf(), getchar(), EOF
#include <string.h> // for strcat(), strcpy()
#include <ctype.h> // for isalpha(), isalnum()
/*
dcl: optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[optional size]
*/
#define FALSE 0
#define TRUE 1
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 1000 // max length of output string
enum {NAME, PARENS, BRACKETS};
void dcl(void); // parse a declarator
void dirdcl(void); // parse a direct declarator
int gettoken(void);
int VERBOSE = FALSE;
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char name[MAXTOKEN]; // identifier name
char datatype[MAXTOKEN]; // data type: char, int, etc.
char out[MAXOUTPUT]; // output string
int main(int argc, char *argv[]) // convert declaration to words
{
int c;
while (--argc > 0 && (*++argv)[0] == '-') // optional argument
{
while (c = *++argv[0]) // -v, -vv, -v -vv, etc.
{
switch(c)
{
case 'v' :
VERBOSE = TRUE;
break;
default:
printf("Illegal option: '%c'\n", c);
printf("Usage: ./dcl [-v]\n");
printf("-v - verbose\n");
return 1; // exit main(), signalling error
}
}
}
if (argc) // if (argc > 0)
{
printf("Usage: ./dcl [-v]\n");
printf("-v - verbose\n");
return 1; // end program, signalling error
}
while(gettoken() != EOF)
{ // first token on each line is the data type
strcpy(datatype, token);
out[0] = '\0'; // (re)set
dcl(); // parse rest of line
if (tokentype != '\n')
{printf("Syntax error\n");}
printf("%s%s%s %s\n", name, (VERBOSE) ? ": " : "", out, datatype);
}
return 0;
}
void dcl(void) // parse a declarator
{
int ns; // no of stars
for (ns = 0; gettoken() == '*'; ) // count *s
{ns++;}
dirdcl(); // recursive-descent call
while (ns-- > 0)
{
if (VERBOSE)
{strcat(out, " pointer to");}
else {strcat(out, " *");}
}
}
void dirdcl(void) // parse a direct declarator
{
int type;
if (tokentype == '(') // (dcl)
{
dcl(); // recursive-descent call
if (tokentype != ')')
{printf("Error: missing ')'\n");}
}
else if (tokentype == NAME)
{strcpy(name, token);}
else
{printf("Error: expected name or (dcl)\n");}
while((type = gettoken()) == PARENS || type == BRACKETS)
{ // last token should be '\n' or EOF
if (type == PARENS)
{
if (VERBOSE)
{strcat(out, " function returning");}
else{strcat(out, " ()");}
}
else // BRACKETS
{
if (VERBOSE)
{
strcat(out, " array");
strcat(out, token); // token holds [...]
strcat(out, " of");
}
else
{
strcat(out, " ");
strcat(out, token); // token holds [...]
}
}
}
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
if ((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if (c == '[')
{
for (*p++ = c; (*p++ = getch()) != ']'; )
{} // token holds [...]
*p = '\0'; // end string
return tokentype = BRACKETS;
}
else if (isalpha(c))
{
for (*p++ = c; isalnum(c = getch()); )
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = NAME;
}
else {return tokentype = c;} // could be ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc dcl.c -o dcl
./dcl v
Usage: ./dcl [-v]
-v - verbose
./dcl -w
Illegal option: 'w'
Usage: ./dcl [-v]
-v - verbose
./dcl // Enter (input from keyboard)
char (*apf[])() // Enter
apf[] * () char
char **argv // Enter
argv * * char
int (*daytab)[13] // Enter
daytab * [13] int
int *daytab[13] // Enter
daytab [13] * int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl -v // Enter (input from keyboard)
char (*apf[])() // Enter
apf: array[] of pointer to function returning char
char **argv // Enter
argv: pointer to pointer to char
int (*daytab)[13] // Enter
daytab: pointer to array[13] of int
int *daytab[13] // Enter
daytab: array[13] of pointer to int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl < dcl.txt > "undcl(copy).txt"
diff -s undcl.txt "undcl(copy).txt"
// Files undcl.txt and undcl(copy).txt are identical
meld undcl.txt "undcl(copy).txt"
// Files are identical
./dcl -v < dcl.txt > "description(copy).txt"
diff -s description.txt "description(copy).txt"
// Files description.txt and description(copy).txt are identical
meld description.txt "description(copy).txt"
// Files are identical
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
pf () * int
pf * () int
apf [] * () char
argv * * char
daytab * [13] int
daytab [13] * int
comp () * void
comp * () void
x () * [] * () char
x [3] * () * [5] char
pf: function returning pointer to int
pf: pointer to function returning int
apf: array[] of pointer to function returning char
argv: pointer to pointer to char
daytab: pointer to array[13] of int
daytab: array[13] of pointer to int
comp: function returning pointer to void
comp: pointer to function returning void
x: function returning pointer to array[] of pointer to function returning char
x: array[3] of pointer to function returning pointer to array[5] of char
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
pf () * int
pf * () int
apf [] * () char
argv * * char
daytab * [13] int
daytab [13] * int
comp () * void
comp * () void
x () * [] * () char
x [3] * () * [5] char
#include <stdio.h> // for printf(), sprintf(), getchar(), EOF
#include <string.h> // for strcat(), strcpy()
#include <ctype.h> // for isalpha(), isalnum()
/*
dcl: optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[optional size]
*/
// char (*(*x())[])()
// x () * [] * () char
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 1000 // max length of output string
enum {NAME, PARENS, BRACKETS};
int gettoken(void);
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char name[MAXTOKEN]; // identifier name
char datatype[MAXTOKEN]; // data type: char, int, etc.
char out[MAXOUTPUT]; // output string
int main() // convert word description to declaration
{
int type;
char temp[MAXOUTPUT+MAXTOKEN]; // to avoid warnings (temp not big enough)
// sprintf(), string printf(), writes into string temp
while(gettoken() != EOF) // process a file
{ // first token is variable or function name
strcpy(out, token); // reset at the beginning of each line
while ((type = gettoken()) != '\n') // process one line
{
if (type == PARENS || type == BRACKETS)
{strcat(out, token);}
else if (type == '*')
{
sprintf(temp, "(*%s)", out); // string printf()
strcpy(out, temp);
}
else if (type == NAME)
{ // data type
sprintf(temp, "%s %s", token, out);
strcpy(out, temp);
}
else {printf("Invalid token at %s\n", token);}
}
printf("%s\n", out);
}
return 0;
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
if ((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if (c == '[')
{
for (*p++ = c; (*p++ = getch()) != ']'; )
{} // token holds [...]
*p = '\0'; // end string
return tokentype = BRACKETS;
}
else if (isalpha(c))
{
for (*p++ = c; isalnum(c = getch()); )
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = NAME;
}
else {return tokentype = c;} // could be ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc undcl.c -o undcl
./undcl // Enter (input from keyboard)
apf [] * () char // Enter
char (*apf[])()
argv * * char // Enter
char (*(*argv))
comp () * void // Enter
void (*comp())
comp * () void // Enter
void (*comp)()
x () * [] * () char // Enter
char (*(*x())[])()
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./undcl < undcl.txt > "dcl(copy).txt"
meld dcl.txt "dcl(copy).txt"
// redundant parentheses
*/
int *pf()
int (*pf)()
char (*apf[])()
char **argv
int (*daytab)[13]
int *daytab[13]
void *comp()
void (*comp)()
char (*(*x())[])()
char (*(*x[3])())[5]
int (*pf())
int (*pf)()
char (*apf[])()
char (*(*argv))
int (*daytab)[13]
int (*daytab[13])
void (*comp())
void (*comp)()
char (*(*x())[])()
char (*(*x[3])())[5]
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-18. Make dcl recover from input errors.
#include <stdio.h> // for printf(), getchar(), EOF
#include <string.h> // for strcat(), strcpy()
#include <ctype.h> // for isdigit(), isalpha(), isalnum()
/*
dcl: optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[optional size]
*/
#define FALSE 0
#define TRUE 1
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 1000 // max length of output string
enum {NAME, PARENS, BRACKETS};
void dcl(void); // parse a declarator
void dirdcl(void); // parse a direct declarator
int gettoken(void);
int VERBOSE = FALSE; // verbose or not
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char name[MAXTOKEN]; // identifier name
char datatype[MAXTOKEN]; // data type: char, int, etc.
char out[MAXOUTPUT]; // output string
int main(int argc, char *argv[]) // convert declaration to words
{
int c;
while (--argc > 0 && (*++argv)[0] == '-') // optional argument
{
while (c = *++argv[0]) // -v, -vv, -v -vv, etc.
{
switch(c)
{
case 'v' :
VERBOSE = TRUE;
break;
default:
printf("Illegal option: '%c'\n", c);
printf("Usage: ./dcl [-v]\n");
printf("-v - verbose\n");
return 1; // exit main(), signalling error
}
}
}
if (argc) // if (argc > 0)
{
printf("Usage: ./dcl [-v]\n");
printf("-v - verbose\n");
return 1; // end program, signalling error
}
token[0] = '\0';
while(gettoken() != EOF)
{
if (tokentype == '\n')
{continue;} // go to the next line
if (tokentype != NAME) // string
{ // could also compare token to data types, like char, int, float, double
printf("Data type is missing\n");
while(gettoken() != '\n' && tokentype != EOF)
{} // advance to the end of line or file
if (tokentype == EOF) {return 0;} // end program
// else
token[0] = '\0'; // reset
continue; // go to the next line
}
// else
strcpy(datatype, token); // first token on each line is the data type
name[0] = '\0'; // reset for each new line
out[0] = '\0'; // (re)set
dcl(); // parse rest of line
if (tokentype != '\n' && tokentype != EOF)
{
printf("Syntax error\n");
token[0] = '\0'; // reset
while(gettoken() != '\n' && tokentype != EOF)
{ // move to the next line or end of file
strcat(out, token);
token[0] = '\0'; // reset
}
}
if (name[0] != '\0' !! out[0] != '\0')
{printf("%s%s%s %s\n", name, (VERBOSE) ? ": " : "", out, datatype);}
token[0] = '\0'; // reset for the next line
if (tokentype == EOF) {return 0;} // end program
// else continue with the next line
}
return 0;
}
void dcl(void) // parse a declarator
{
int ns; // no of stars
for (ns = 0; gettoken() == '*'; ) // count *s
{ns++;}
if (tokentype == '\n' || tokentype == EOF)
{
printf("Expected direct declaration\n");
return;
}
dirdcl(); // recursive-descent call
while (ns-- > 0)
{
if (VERBOSE)
{strcat(out, " pointer to");}
else {strcat(out, " *");}
}
}
void dirdcl(void) // parse a direct declarator
{
int type;
if (tokentype == '(') // (dcl)
{
dcl(); // recursive-descent call
if (tokentype != ')')
{
printf("Error: missing ')'\n");
return; // do not process the rest of line
}
}
else if (tokentype == NAME)
{strcpy(name, token);}
else
{
printf("Error: expected name or (dcl)\n");
return; // do not process the rest of line
}
// name(), name[], (dcl)(), (dcl)[]
while((type = gettoken()) == PARENS || type == BRACKETS)
{ // last token should be '\n' or EOF
if (type == PARENS)
{
if (VERBOSE)
{strcat(out, " function returning");}
else{strcat(out, " ()");}
}
else // BRACKETS
{
if (VERBOSE)
{
strcat(out, " array");
strcat(out, token); // token holds [...]
strcat(out, " of");
}
else
{
strcat(out, " ");
strcat(out, token); // token holds [...]
}
}
}
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
if ((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if (c == '[')
{
for (*p++ = c; isalnum(c = getch()); ) // isdigit() or 0x, then isxdigit()
{ // token holds [...]
*p++ = c;
}
if (c != ']')
{
printf("Expected ']'\n");
*p = '\0'; // end token string
return tokentype = c;
}
// else
*p++ = c; // ']'
*p = '\0'; // end token string
return tokentype = BRACKETS;
}
else if (isalpha(c))
{
for (*p++ = c; isalnum(c = getch()); ) // no underscore
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = NAME;
}
else {return tokentype = c;} // could be ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc dcl.c -o dcl
./dcl v
Usage: ./dcl [-v]
-v - verbose
./dcl -w
Illegal option: 'w'
Usage: ./dcl [-v]
-v - verbose
./dcl // Enter (input from keyboard)
char (*apf[])() // Enter
apf[] * () char
char **argv // Enter
argv * * char
int (*daytab)[13] // Enter
daytab * [13] int
int *daytab[13] // Enter
daytab [13] * int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl -v // Enter (input from keyboard)
char (*apf[])() // Enter
apf: array[] of pointer to function returning char
char **argv // Enter
argv: pointer to pointer to char
int (*daytab)[13] // Enter
daytab: pointer to array[13] of int
int *daytab[13] // Enter
daytab: array[13] of pointer to int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl // test error recovery
() // Enter
Data type is missing
char **argv // Enter
argv * * char
f() // Enter
Error: expected name or (dcl)
Syntax error
f // name has been reset, it is not argv (from previous line)
int *f() // Enter
f () * int
char f(() // Enter
Syntax error
f() char
int i // Enter
i int
int *f()) // Enter
Syntax error
f () * int
char *argv[] // Enter
argv [] * char
int f(()) // Enter
Syntax error
f() int
long l // Enter
l long
int // Enter
Expected direct declaration
int
int i // Enter
i int
int (*)() // Enter
Error: expected name or (dcl)
* () int
float f i // Enter
f float
int array[10[ // Enter
Expected ']'
Syntax error
array int
int array[10] // Enter
array [10] int
double d // Enter
d double
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl -v // test error recovery
() // Enter
Data type is missing
char **argv // Enter
argv: pointer to pointer to char
f() // Enter
Error: expected name or (dcl)
Syntax error
: f // name has been reset, it is not argv (from previous line)
int *f() // Enter
f: function returning pointer to int
char f(() // Enter
Syntax error
f: () char
int i // Enter
i: int
int *f()) // Enter
Syntax error
f: function returning pointer to int
char *argv[] // Enter
argv: array[] of pointer to char
int f(()) // Enter
Syntax error
f: () int
long l // Enter
l: long
int // Enter
Expected direct declaration
: int
int i // Enter
i: int
int (*)() // Enter
Error: expected name or (dcl)
: pointer to function returning int
float f // Enter
f: float
int array[10[ // Enter
Expected ']'
Syntax error
array: int
int array[10] // Enter
array: array[10] of int
double d // Enter
d: double
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./dcl < dcl.txt > "undcl(copy).txt"
diff -s undcl.txt "undcl(copy).txt"
// Files undcl.txt and undcl(copy).txt are identical
meld undcl.txt "undcl(copy).txt"
// Files are identical
./dcl -v < dcl.txt > "description(copy).txt"
diff -s description.txt "description(copy).txt"
// Files description.txt and description(copy).txt are identical
meld description.txt "description(copy).txt"
// Files are identical
*/
[-v] signals an optional -v argument.
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-19. Modify undcl so that it does not add redundant parentheses to declarations.
#include <stdio.h> // for printf(), sprintf(), getchar(), EOF
#include <string.h> // for strcat(), strcpy()
#include <ctype.h> // for isalpha(), isalnum()
/*
dcl: optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[optional size]
*/
// char (*(*x())[])()
// x () * [] * () char
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 1000 // max length of output string
enum {NAME, PARENS, BRACKETS};
int gettoken(void);
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char name[MAXTOKEN]; // identifier name
char datatype[MAXTOKEN]; // data type: char, int, etc.
char out[MAXOUTPUT]; // output string
int main() // convert word description to declaration
{
int type;
char temp[MAXOUTPUT+MAXTOKEN]; // to avoid warnings (temp not big enough)
// sprintf(), string printf(), writes into string temp,
// holding name[MAXTOKEN] and out[MAXOUTPUT]
while(gettoken() != EOF) // process a file
{ // first token is the name:
strcpy(out, token); // reset at the beginning of each line
type = gettoken(); // within the inner while(), move gettoken() to the end
if (type == EOF) {break;} // out of outer while(), end program
while (type != '\n' && type != EOF) // process one line
{ // line may end with EOF, not '\n'
if (type == PARENS || type == BRACKETS)
{strcat(out, token);} // f(), a[], a[10]
else if (type == '*')
{
if ((type = gettoken()) == PARENS || type == BRACKETS) // read extra token
{ // pointer to function or to array
sprintf(temp, "(*%s)", out);
strcpy(out, temp); // (*pf), (*pa)
strcat(out, token); // process extra token, () or []:
} // (*pf)(), (*pa)[], (*pa)[10]
else // pointer to data type
{
sprintf(temp, "*%s", out); // no extra parentheses added
strcpy(out, temp); // like int *p (data type is read at the end of line)
if (type == EOF)
{ // break here would only exit inner while()
return 1; // end program, signalling error
}
// else // process extra token in the next inner while() iteration
continue; // by skipping gettoken() at the end of inner while()
}
}
else if (type == NAME) // or STRING
{ // data type
sprintf(temp, "%s %s", token, out); // add data type at the beginning
strcpy(out, temp);
}
else {printf("Invalid token at \"%s\"\n", token);}
type = gettoken();
}
printf("%s\n", out);
}
return 0;
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
if ((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if (c == '[')
{
for (*p++ = c; isalnum(c = getch()); ) // isdigit() or 0x, then isxdigit()
{ // token holds [...]
*p++ = c;
}
if (c != ']')
{
printf("Expected ']'\n");
*p = '\0'; // end token string
return tokentype = c;
}
// else
*p++ = c; // ']'
*p = '\0'; // end token string
return tokentype = BRACKETS;
}
else if (isalpha(c))
{
for (*p++ = c; isalnum(c = getch()); )
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = NAME;
}
else {return tokentype = c;} // could be '*' or ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc undcl.c -o undcl
./undcl // Enter (input from keyboard)
apf [] * () char // Enter
char (*apf[])()
argv * * char // Enter
char **argv
comp () * void // Enter
void *comp()
comp * () void // Enter
void (*comp)()
x () * [] * () char // Enter
char (*(*x())[])()
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./undcl < undcl.txt > "dcl(copy).txt"
diff -s dcl.txt "dcl(copy).txt"
// Files dcl.txt and dcl(copy).txt are identical
meld dcl.txt "dcl(copy).txt"
// Files are identical
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-20. Expand dcl to handle declarations with function argument types, qualifiers like const, and so on.
int *pf()
int (**f)()
int (*const volatile const *const f)()
static const int *pf(volatile int, void *(), const short unsigned u)
int f(int (), ...)
int (*pf)()
int const (*const pf)(short signed (), unsigned (*)())
int f(int (int *(), ...), ...)
char (*apf[])()
char (*apf[10])(register short int f(volatile int g()))
int f(int (int *(), char (*)(), ...), ...)
char **argv
const char *const *const cargv
int (*daytab)[13]
int (*tab)(int[10], void *f())
static char *daytab[13]
int *tab(int f(char c, ...), const int[], void (*comp)())
int f(int (int, char, void *(), char (*)(), ...), ...)
int f(int g(int i, char c, void *v(), char (*h)(), ...), ...)
void *comp()
void const static *const comp(int, ...)
void (*comp)()
void (*const comp)(int f(), char[], ...)
char (*(*x())[])()
const char (*(*x(float, double d()))[10])(long, ...)
char (*(*x[3])())[5]
static char (*(*x[3])(const long double[5]))()
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <stdio.h> // for printf(), getchar(), EOF
#include <string.h> // for strcat(), strcpy(), strcmp()
#include <ctype.h> // for isalpha(), isalnum(), isspace()
// TODO: Add references
/*
dcl: datatype optional modifiers optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl(optional arguments)
direct-dcl[optional size]
arguments: argument
argument, arguments
arguments, ...
argument: dcl with optional name
size: integral type
*/
#define FALSE 0
#define TRUE 1
// static const short unsigned int (5 strings or words)
#define MAXNODTMODQ 10 // max no of data type words plus modifiers, qualifiers
#define MAXLVLREC 100 // max level of recursion, int f(char g(long double d))
#define MAXNOP 100 // max no of pointers to a datatype, array, or function
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 5000 // max length of output string
// STRING - data type, name or modifier, ELLIPSIS - variadic arguments, ...
enum {STRING, PARENS, BRACKETS, POINTFA, ARGUMENTS, ELLIPSIS};
// POINTFA - '(' followed by pointer to function or array
// ARGUMENTS - '(' followed by function arguments (parameters in a declaration)
#define ARGSEP ',' // argument separator
char *DTMODQ[] = // data types, modifiers, and qualifiers
{"char", "signed", "unsigned", "short", "int", "long", "float", "double",
"const", "static", "volatile", "register", "void", "" // empty string marks the end
}; // reserved words cannot be used as names
void dcl(void); // parse a declarator
void dirdcl(void); // parse a direct declarator
int gettoken(void);
int VERBOSE = FALSE;
int ARG = FALSE; // argument
int FOUNDNAME = FALSE;
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char datatype[MAXLVLREC][MAXNODTMODQ][MAXTOKEN];
// pointer qualifiers, const, volatile:
char pointqs[MAXLVLREC][MAXNOP][MAXTOKEN*2];
int levrec = 0; // level of recursion, first index of datatype[], pointqs[]
char name[MAXTOKEN]; // identifier name
char out[MAXOUTPUT]; // output string
char* trim(char *); // remove extra spaces, return pointer to new string
int main(int argc, char *argv[]) // convert declaration to words
{
int c;
while (--argc > 0 && (*++argv)[0] == '-') // optional argument
{
while (c = *++argv[0]) // -v, -vv, -v -vv, etc.
{
switch(c)
{
case 'v' :
VERBOSE = TRUE;
break;
default:
printf("Illegal option: '%c'\n", c);
printf("Usage: ./edcl [-v]\n");
printf("-v - verbose\n");
return 1; // exit main(), signalling error
}
}
}
if (argc) // if (argc > 0)
{
printf("Usage: ./edcl [-v]\n");
printf("-v - verbose\n");
return 1; // end program, signalling error
}
tokentype = 0; // initialize
while(tokentype != EOF)
{
FOUNDNAME = FALSE; // reset for each new line
ARG = FALSE; // reset
levrec = 0; // reset
name[0] = '\0'; // (re)set
token[0] = '\0'; // (re)set
out[0] = '\0'; // (re)set
dcl(); // parse line
if (tokentype != '\n' && tokentype != EOF)
{
printf("Syntax error\n");
token[0] = '\0'; // reset
while(gettoken() != '\n' && tokentype != EOF)
{ // move to the next line or end of file
strcat(out, token);
token[0] = '\0'; // reset
}
}
if (name[0] == 0 && out[0] == '\0')
{continue;} // empty line, nothing to print
// else
printf("%s\n", trim(out));
}
return 0;
}
void dcl(void) // parse a declarator
{
gettoken();
if(tokentype == '\n' || tokentype == EOF)
{
// printf("Expected declaration\n");
return;
}
if (tokentype == ELLIPSIS) // variadic arguments, ...
{return;} // return to caller, dirdcl()
// STRING - data type, modifier, qualifier, or name
if (tokentype != STRING && tokentype != '*') // (*pf)(), (*pa)[]
{ // token not a string, tokentype not '*'; (dcl) in dirdcl
printf("Expected data type or pointer\n"); // int i, *pf, *pa
return; // do not process the rest of line
}
int dti = 0; // datatype index, second dimension
// (no of words of current datatype)
while (tokentype == STRING) // read datatype and eventually the name
{ // get several strings as datatype and , modifiers
strcpy(datatype[levrec][dti++], token);
gettoken(); // first tokens on each line are data type, qualifiers, modifiers
}
int i;
int match = FALSE; // test if assumed name matches reserved words
if (dti > 0) // there is a datatype
{
for (i = 0; DTMODQ[i][0] != '\0'; i++) // ""[i][0] == '\0'
{ // the empty string "" ends the string array DTMODQ[]
if (strcmp(datatype[levrec][dti-1], DTMODQ[i]) == 0)
{ // should also check previous values stored in datatype[]
match = TRUE;
break;
}
}
if (!match) // found name
{ // modifiers, qualifiers are reserved words, name is not
strcpy(name, datatype[levrec][dti-1]);
strcat(out, name); // out[] may not be empty, so we cannot use strcpy()
if (VERBOSE) {strcat(out, ": ");}
FOUNDNAME = TRUE;
dti--; // to account for the removed name
if (dti == 0) // datatype should precede the name
{
printf("Datatype is missing\n");
FOUNDNAME = FALSE; // reset
name[0] = '\0'; // reset
return; // do not process the rest of line
}
}
// if (match), datatype[] holds the datatype,
// modifiers, qualifiers, but not the name
if(tokentype == '\n' || tokentype == EOF)
{ // variable declaration, like 'int i'
for (i = 0; i < dti; i++)
{
strcat(out, " ");
strcat(out, datatype[levrec][i]); // don't put space at the end
}
if (!FOUNDNAME) // const static signed short int
{
printf("Name is missing\n");
}
FOUNDNAME = FALSE; // reset
name[0] = '\0'; // reset
return;
}
}
int ns = 0; // no of stars
int foundpn = FALSE; // found pointer name
while (tokentype == '*' && !foundpn) // count *s until pointer name
{
ns++;
pointqs[levrec][ns-1][0] = '\0'; // initialize
gettoken();
while (tokentype == STRING) // volatile const name
{
if (strcmp(token, "volatile") == 0 || strcmp(token, "const") == 0)
{ // do not write *const const volatile const p, write *const volatile p
strcat(pointqs[levrec][ns-1], " ");
strcat(pointqs[levrec][ns-1], token);
}
else // found name
{
strcpy(name, token);
strcat(out, name); // out[] may not be empty, so we cannot use strcpy()
if (VERBOSE) {strcat(out, ": ");}
FOUNDNAME = TRUE; // could also be an argument name
foundpn = TRUE; // break out of outer while()
gettoken();
break; // out of inner while()
}
gettoken();
}
}
if (tokentype == '\n' || tokentype == EOF)
{
while (ns-- > 0)
{
strcat(out, pointqs[levrec][ns]); // const volatile
if (VERBOSE)
{strcat(out, " pointer to");}
else {strcat(out, " *");}
}
for (i = 0; i < dti; i++)
{
strcat(out, " ");
strcat(out, datatype[levrec][i]); // don't put space at the end
}
FOUNDNAME = FALSE; // reset
name[0] = '\0'; // reset
return;
}
levrec++; // after reading current datatype, pointers qualifiers
// dirdcl() may call dcl(), which should have another levrec
dirdcl(); // recursive-descent call
levrec--; // upon writing current datatype, pointers qualifiers
while (ns-- > 0)
{
strcat(out, pointqs[levrec][ns]); // const volatile
if (VERBOSE)
{strcat(out, " pointer to");}
else {strcat(out, " *");}
}
for (i = 0; i < dti; i++)
{
strcat(out, " ");
strcat(out, datatype[levrec][i]); // don't put space at the end
}
}
// moved name processing to dcl(), along with reading
// data types, modifiers, and qualifiers, including for pointers
void dirdcl(void) // parse a direct declarator
{
if (tokentype == POINTFA) // (dcl): int (*pf)(), char (*pa)[]
{ // '(' followed by pointer to function or array
dcl(); // recursive-descent call
if (tokentype != ')')
{
printf("Error: missing ')'\n");
return; // do not process the rest of line
}
// else
gettoken();
}
if (!FOUNDNAME)
{
printf("Name is missing\n");
return; // do not process the rest of line
}
// name(), name[], (dcl)(), (dcl)[]
while(tokentype == PARENS || tokentype == BRACKETS)
{ // last token should be '\n' or EOF
if (tokentype == PARENS)
{
if (VERBOSE)
{strcat(out, " function returning");}
else{strcat(out, " ()");}
}
else // BRACKETS
{
if (VERBOSE)
{
strcat(out, " array");
strcat(out, token); // token holds [...]
strcat(out, " of");
}
else
{
strcat(out, " ");
strcat(out, token); // token holds [...]
}
}
gettoken();
}
if (tokentype == ARGUMENTS) // function with arguments
{
ARG = TRUE; // arguments have optional names
// ARG remains set (TRUE) till the end of line
if (VERBOSE)
{strcat(out, " function taking as arguments: (");}
else {strcat(out, " (");}
dcl();
while(tokentype == ARGSEP)
{
strcat(out, ", ");
dcl();
}
if (tokentype == ELLIPSIS)
{
strcat(out, token); // "..."
gettoken();
}
if (tokentype != ')')
{
printf("Error: missing ')'\n");
return; // do not process the rest of line
}
// else
if (VERBOSE) {strcat(out, ") and returning");}
else {strcat(out, ")");}
gettoken();
}
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
while((c = getch()) == ' ' || c == '\t')
{} // skip whitespace
if (c == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
if (c == '*') // pointer to function or array
{return tokentype = POINTFA;}
// else // function arguments (parameters in a declaration)
return tokentype = ARGUMENTS;
}
}
else if (c == ',') // argument separator
{
return tokentype = ARGSEP;
}
else if (c == '.')
{
if ((c = getch()) != '.' || (c = getch()) != '.')
{
printf("Expected ellipsis, ...\n");
return tokentype = c;
}
else
{
strcpy(token, "...");
return tokentype = ELLIPSIS;
}
}
else if (c == '[')
{
for (*p++ = c; isalnum(c = getch()); ) // isdigit() or 0x, then isxdigit()
{ // token holds [...]
*p++ = c;
}
if (c != ']')
{
printf("Expected ']'\n");
*p = '\0'; // end token string
return tokentype = c;
}
// else
*p++ = c; // ']'
*p = '\0'; // end token string
return tokentype = BRACKETS;
}
else if (isalpha(c)) // letter, not underscore
{ // name or modifier or qualifier
for (*p++ = c; isalnum(c = getch()); ) // letter or digit (decimal or hex)
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = STRING;
}
else {return tokentype = c;} // could be '*' or ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
// remove space after '(', several contiguous spaces are turned into one
char* trim(char *s) // remove extra spaces, return pointer to new string
{
char *p = s, *r = s;
while (*p != 0) // while (*p)
{
if (*p == '(' || *p == '[')
{
*r++ = *p;
p++;
while(*p && isspace(*p)) {p++;}
}
else if (isspace(*p))
{
*r++ = ' '; // keep only one space
while(*p && isspace(*p)) {p++;}
}
else
{
*r++ = *p;
p++;
}
}
*r = '\0'; // end string
return s;
}
/*
gcc edcl.c -o edcl
./edcl // Enter (input from keyboard)
char (*apf[])() // Enter
apf [] * () char
char **argv // Enter
argv * * char
int (*daytab)[13] // Enter
daytab * [13] int
int *daytab[13] // Enter
daytab [13] * int
int (**f)() // Enter
f * * () int
int (*const volatile const *const f)() // Enter
f const * const volatile const * () int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./edcl -v // Enter (input from keyboard)
char (*apf[])() // Enter
apf [] * () char
char **argv // Enter
argv * * char
int (*daytab)[13] // Enter
daytab * [13] int
int *daytab[13] // Enter
daytab [13] * int
int (**f)() // Enter
f * * () int
int (*const volatile const *const f)() // Enter
f: const pointer to const volatile const pointer to function returning int
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./edcl // test error recovery
1 // Enter
Expected data type
Syntax error
char **argv // Enter
argv * * char
. // Enter
Expected ellipsis, ...
.. // Enter
Expected ellipsis, ...
... // Enter
Syntax error
int i // Enter
i int
f() // Enter
Datatype is missing
Syntax error
f // name has been reset, it is not argv (from previous line)
int *f() // Enter
f () * int
char f(() // Enter
Expected data type or pointer
Error: missing ')'
Syntax error
f (char
int i // Enter
i int
int *f()) // Enter
Syntax error
f () * int
char *argv[] // Enter
argv [] * char
./edcl -v // test error recovery
1 // Enter
Expected data type
Syntax error
char **argv // Enter
argv * * char
. // Enter
Expected ellipsis, ...
.. // Enter
Expected ellipsis, ...
... // Enter
Syntax error
int i // Enter
i: int
f() // Enter
Datatype is missing
Syntax error
f // name has been reset, it is not argv (from previous line)
int *f() // Enter
f () * int
char f(() // Enter
Expected data type or pointer
Error: missing ')'
Syntax error
f (char
int i // Enter
i int
int *f()) // Enter
Syntax error
f () * int
char *argv[] // Enter
argv [] * char
./edcl < edcl.txt > "unedcl(copy).txt"
diff -s unedcl.txt "unedcl(copy).txt"
// Files unedcl.txt and unedcl(copy).txt are identical
meld unedcl.txt "unedcl(copy).txt"
// Files are identical
./edcl -v < edcl.txt > "expanded(copy).txt"
diff -s expanded.txt "expanded(copy).txt"
// Files expanded.txt and expanded(copy).txt are identical
meld expanded.txt "expanded(copy).txt"
// Files are identical
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
pf () * int
f * * () int
f const * const volatile const * () int
pf (volatile int, () * void, u const short unsigned) * static const int
f (() int, ...) int
pf * () int
pf const * (() short signed, * () unsigned) int const
f ((() * int, ...) int, ...) int
apf [] * () char
apf [10] * (f (g () volatile int) register short int) char
f ((() * int, * () char, ...) int, ...) int
argv * * char
cargv const * const * const char
daytab * [13] int
tab * ([10] int, f () * void) int
daytab [13] * static char
tab (f (c char, ...) int, [] const int, comp * () void) * int
f ((int, char, () * void, * () char, ...) int, ...) int
f (g (i int, c char, v () * void, h * () char, ...) int, ...) int
comp () * void
comp (int, ...) const * void const static
comp * () void
comp const * (f () int, [] char, ...) void
x () * [] * () char
x (float, d () double) * [10] * (long, ...) const char
x [3] * () * [5] char
x [3] * ([5] const long double) * () static char
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
pf: function returning pointer to int
f: pointer to pointer to function returning int
f: const pointer to const volatile const pointer to function returning int
pf: function taking as arguments: (volatile int, function returning pointer to void, u: const short unsigned) and returning pointer to static const int
f: function taking as arguments: (function returning int, ...) and returning int
pf: pointer to function returning int
pf: const pointer to function taking as arguments: (function returning short signed, pointer to function returning unsigned) and returning int const
f: function taking as arguments: (function taking as arguments: (function returning pointer to int, ...) and returning int, ...) and returning int
apf: array[] of pointer to function returning char
apf: array[10] of pointer to function taking as arguments: (f: function taking as arguments: (g: function returning volatile int) and returning register short int) and returning char
f: function taking as arguments: (function taking as arguments: (function returning pointer to int, pointer to function returning char, ...) and returning int, ...) and returning int
argv: pointer to pointer to char
cargv: const pointer to const pointer to const char
daytab: pointer to array[13] of int
tab: pointer to function taking as arguments: (array[10] of int, f: function returning pointer to void) and returning int
daytab: array[13] of pointer to static char
tab: function taking as arguments: (f: function taking as arguments: (c: char, ...) and returning int, array[] of const int, comp: pointer to function returning void) and returning pointer to int
f: function taking as arguments: (function taking as arguments: (int, char, function returning pointer to void, pointer to function returning char, ...) and returning int, ...) and returning int
f: function taking as arguments: (g: function taking as arguments: (i: int, c: char, v: function returning pointer to void, h: pointer to function returning char, ...) and returning int, ...) and returning int
comp: function returning pointer to void
comp: function taking as arguments: (int, ...) and returning const pointer to void const static
comp: pointer to function returning void
comp: const pointer to function taking as arguments: (f: function returning int, array[] of char, ...) and returning void
x: function returning pointer to array[] of pointer to function returning char
x: function taking as arguments: (float, d: function returning double) and returning pointer to array[10] of pointer to function taking as arguments: (long, ...) and returning const char
x: array[3] of pointer to function returning pointer to array[5] of char
x: array[3] of pointer to function taking as arguments: (array[5] of const long double) and returning pointer to function returning static char
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <stdio.h> // for printf(), sprintf(), getchar(), EOF, NULL
#include <string.h> // for strcat(), strcpy(), strcmp()
#include <ctype.h> // for isalpha(), isalnum()
/*
dcl: datatype optional modifiers optional *s direct-dcl
direct-dcl: name
(dcl)
direct-dcl(optional arguments)
direct-dcl[optional size]
arguments: argument
argument, arguments
arguments, ...
argument: dcl with optional name
size: integral type
*/
#define FALSE 0
#define TRUE 1
#define MAXTOKEN 100 // max token length
#define MAXOUTPUT 5000 // max length of output string
#define ARGSEP ',' // argument separator
// static const short unsigned int (5 strings or words)
#define MAXNODTMODQ 10 // max no of data type words plus modifiers, qualifiers
#define MAXNOPQS 10 // max no of pointer qualifiers
enum {STRING, PARENS, BRACKETS, ELLIPSIS};
char *DTMODQ[] = // data types, modifiers, and qualifiers
{"char", "signed", "unsigned", "short", "int", "long", "float", "double",
"const", "static", "volatile", "register", "void", "" // empty string marks the end
}; // reserved words cannot be used as names
int gettoken(void);
int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char name[MAXTOKEN]; // identifier name
char datatype[MAXTOKEN*MAXNODTMODQ]; // data type, modifiers, qualifiers
char pointqs[MAXTOKEN*MAXNOPQS]; // pointer qualifiers
// read variable, array, function, or argument declaration
char *readDeclaration(void); // return pointer to the output string
// we assume the input file has no errors, so we don't have to do much
// error checking in this program
int main() // convert word description to declaration
{
tokentype = 0; // initialize
char *out;
while(tokentype != EOF) // process a file
{
out = readDeclaration(); // process a line
if (out != NULL)
{printf("%s\n", out);}
}
return 0;
}
// return pointer to the output string
char *readDeclaration(void) // read variable,
{ // array, function, or argument declaration
gettoken();
if (tokentype == EOF)
{return NULL;} // return to caller, readDeclaration() or main()
if (tokentype == '\n' || tokentype == ARGSEP ||
tokentype == ELLIPSIS || tokentype == ')')
{return "";} // the empty string, not NULL
char *p;
char line[MAXOUTPUT]; // read current line
char temp[MAXOUTPUT+MAXTOKEN*MAXNODTMODQ];
// to avoid warnings (temp not big enough)
// sprintf(), string printf(), writes into string temp,
// holding datatype[MAXTOKEN*MAXNODTMODQ] and line[MAXOUTPUT]
int i, match = FALSE;
line[0] = '\0'; // initialize; line[] is now the empty string; ""[0] == '\0'
// first token is normally the name, however, arguments have optional name
if (tokentype == STRING) // name, modifier, or qualifier
{ // modifiers, qualifiers are reserved words, name is not
for (i = 0; DTMODQ[i][0] != '\0'; i++)
{
if (strcmp(token, DTMODQ[i]) == 0)
{
match = TRUE;
break; // out of for()
}
}
if (!match) // found name
{ // if we find no name, line[] remains empty; in both cases
strcat(line, token); // we can append to line[] with strcat()
gettoken();
}
}
// process one line or argument
while (tokentype != '\n' && tokentype != EOF)
{ // line may end with EOF, not '\n' or other separator
if (tokentype == PARENS || tokentype == BRACKETS)
{strcat(line, token);} // f(), a[], a[10]
else if (tokentype == '(') // function with arguments
{ // arguments have optional names
strcat(line, "(");
p = readDeclaration();
if (p == NULL) {return NULL;}
strcat(line, p);
while (tokentype == ARGSEP)
{
strcat(line, ", ");
p = readDeclaration();
if (p == NULL) {return NULL;}
strcat(line, p);
}
if (tokentype == ELLIPSIS)
{
strcat(line, "..."); // strcat(line, token);
gettoken();
if (tokentype != ')')
{
printf("Missing ')'\n");
return NULL; // signal error
}
}
strcat(line, ")");
}
else if (tokentype == ARGSEP || tokentype == ELLIPSIS || tokentype == ')')
{ // for function arguments
p = line;
return p; // return to the caller, which handles the arguments
}
else if (tokentype == '*')
{ // read extra token:
if (gettoken() == '(' || tokentype == PARENS || tokentype == BRACKETS)
{ // pointer to function (with or without arguments) or to array
sprintf(temp, "(*%s)", line);
strcpy(line, temp); // (*pf), (*pa)
if (tokentype == '(') // process extra token in the next inner while() iteration
{continue;} // by skipping gettoken() at the end of inner while()
// else
strcat(line, token); // process extra token, () or []:
} // (*pf)(), (*pa)[], (*pa)[10]
else // pointer to data type
{
sprintf(temp, "*%s", line); // no extra parentheses added
strcpy(line, temp); // like int *p (data type is read at the end of line)
if (tokentype == EOF)
{ // break here would only exit inner while()
return NULL; // end program, signalling error
}
// else // process extra token in the next inner while() iteration
continue; // by skipping gettoken() at the end of inner while()
}
}
else if (tokentype == STRING) // data type, modifier, qualifier
{ // name is not read here, it was read at the beginning of outer while()
strcpy(datatype, token);
while (gettoken() == STRING) // read extra token
{ // static const signed short int, const volatile *
strcat(datatype, " ");
strcat(datatype, token);
}
if (line[0] != '\0')
{ // add datatype at the beginning
sprintf(temp, "%s%s%s", datatype, (line[0] == '[') ? "" : " ", line);
strcpy(line, temp); // array[], not array []
}
else {strcpy(line, datatype);} // strcat(line, datatype);
// process extra token in the next inner while() iteration
continue; // by skipping gettoken() at the end of inner while()
}
else {printf("Invalid token at \"%s\"\n", token);}
gettoken();
}
p = line;
return p;
}
int getch(void);
void ungetch(int);
int gettoken(void)
{
int c;
char *p = token;
while((c = getch()) == ' ' || c == '\t')
{} // skip beginning whitespace
if (c == '(')
{
if ((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if (c == ',') // argument separator
{
return tokentype = ARGSEP;
}
else if (c == '.')
{
if ((c = getch()) != '.' || (c = getch()) != '.')
{
printf("Expected ellipsis, ...\n");
return tokentype = c;
}
else
{
strcpy(token, "...");
return tokentype = ELLIPSIS;
}
}
else if (c == '[')
{
for (*p++ = c; isalnum(c = getch()); ) // isdigit() or 0x, then isxdigit()
{ // token holds [...]
*p++ = c;
}
if (c != ']')
{
printf("Expected ']'\n");
*p = '\0'; // end token string
return tokentype = c;
}
// else
*p++ = c; // ']'
*p = '\0'; // end token string
return tokentype = BRACKETS;
}
else if (isalpha(c))
{
for (*p++ = c; isalnum(c = getch()); )
{*p++ = c;}
*p = '\0'; // end string
ungetch(c);
return tokentype = STRING;
}
else {return tokentype = c;} // could be ')' or '\n' or EOF
}
// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF
int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}
int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf
return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc unedcl.c -o unedcl
./unedcl // Enter (input from keyboard)
apf [] * () char // Enter
char (*apf[])()
argv * * char // Enter
char **argv
comp () * void // Enter
void *comp()
comp * () void // Enter
void (*comp)()
x () * [] * () char // Enter
char (*(*x())[])()
f * * () int // Enter
int (**f)()
f const * const volatile const * () int // Enter
int (*const volatile const *const f)()
f (() int, ...) int // Enter
int f(int (), ...)
f ((() * int, ...) int, ...) int // Enter
int f(int (int *(), ...), ...)
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
./unedcl < unedcl.txt > "edcl(copy).txt"
diff -s edcl.txt "edcl(copy).txt"
// Files edcl.txt and edcl(copy).txt are identical
meld edcl.txt "edcl(copy).txt"
// Files are identical
*/