Exercise 5-18. Make the basic
dclprogram recover from input errors.
K&R’s dcl program (Section 5.12) parses C declarations into English. It calls error() which prints a message and then exits — making it useless for batch input. The fix: on error, skip to the next newline and try to continue. This requires turning dcl‘s recursive calls into error-aware calls that propagate an error flag upward, and resetting state before retrying.
Solution
/* K&R Exercise 5-18 — dcl with error recovery
* Compile: gcc -ansi -Wall ex5-18.c -o dcl18
* Input: C declaration strings, one per line
* Example: int (*fp)(char *)
* char **argv */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXTOKEN 100
enum { NAME, PARENS, BRACKETS };
static char token[MAXTOKEN];
static char name[MAXTOKEN];
static char datatype[MAXTOKEN];
static char out[1000];
static int tokentype;
static int errf; /* error flag */
int gettoken(void)
{
int c;
char *p = token;
while ((c = getchar()) == ' ' || c == '\t')
;
if (c == '(') {
if ((c = getchar()) == ')') {
strcpy(token, "()"); return tokentype = PARENS;
} else {
ungetc(c, stdin); return tokentype = '(';
}
} else if (c == '[') {
*p++ = c;
while ((*p++ = getchar()) != ']')
;
*p = '\0';
return tokentype = BRACKETS;
} else if (isalpha(c)) {
*p++ = c;
while (isalnum(c = getchar()) || c == '_')
*p++ = c;
*p = '\0';
ungetc(c, stdin);
return tokentype = NAME;
}
return tokentype = c;
}
void dcl(void);
void dirdcl(void)
{
int type;
if (tokentype == '(') {
dcl();
if (tokentype != ')') {
printf("error: missing )\n");
errf = 1; return;
}
} else if (tokentype == NAME) {
strcpy(name, token);
} else {
printf("error: expected name or (dcl)\n");
errf = 1; return;
}
while ((type = gettoken()) == PARENS || type == BRACKETS) {
if (type == PARENS)
strcat(out, " function returning");
else {
strcat(out, " array");
strcat(out, token);
strcat(out, " of");
}
}
}
void dcl(void)
{
int ns = 0;
while (gettoken() == '*') ns++;
dirdcl();
if (errf) return;
while (ns-- > 0)
strcat(out, " pointer to");
}
int main(void)
{
int c;
while (gettoken() != EOF) {
errf = 0;
strcpy(datatype, token);
out[0] = '\0';
dcl();
if (errf) {
/* skip rest of line and retry */
while ((c = getchar()) != '\n' && c != EOF)
;
printf(" (skipped malformed declaration)\n");
} else if (tokentype != '\n') {
printf("error: syntax error at '%s'\n", token);
} else {
printf("%s: %s %s\n", name, out, datatype);
}
}
return 0;
}
Test It
printf "int (*fp)()\nchar **argv\nbad *** input\ndouble *x\n" | ./dcl18
Sample Output
fp: function returning pointer to int argv: pointer to pointer to char error: expected name or (dcl) (skipped malformed declaration) x: pointer to double
What This Exercise Teaches
- Error recovery in recursive descent parsers — the standard approach is “panic mode”: on error, discard input until a synchronisation point (here, the next newline), reset state, and retry
- Error flag propagation — setting
errf = 1and checking it at each recursive level avoids printing partial results for malformed input - Exit vs continue — a library function should recover; a standalone program might exit; the choice depends on whether the caller can meaningfully continue
Set Up Your C Environment
← Exercise 5-17 |
Chapter 5 Solutions |
Exercise 5-19 →
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie