Exercise 5-18 (dcl - Decode, describe declarations, input errors)

Chapter_5     Exercise_5-17     undeclare Exercise_5-19







Exercise 5-18     K&R, p. 126


Exercise 5-18. Make dcl() recover from input errors.




dcl.c         download


#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
*/





Notes:  See declare for the text files.
[-v] signals an optional -v argument.
File names "undcl(copy).txt" and "description(copy).txt" are within quotes because the shell interprets parentheses (subshell).









Chapter_5     Exercise_5-17     undeclare BACK_TO_TOP Exercise_5-19



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo