exercise 5-19

2026-10-01

#include <stdio.h>
#include <string.h>     //for strcpy(), strcat()
#include <ctype.h>      //for isalpha(), isalnum()
//compile with: gcc -Wall -Wextra cc-c.c -o cc-c
//run with:     printf 'argv * * char\n' | ./cc-c             # char **argv          (5.12's undcl: char (*(*argv)))
//         or:  printf 'daytab [13] * int\n' | ./cc-c         # int *daytab[13]      (5.12's undcl: int (*daytab[13]))
//         or:  printf 'daytab * [13] int\n' | ./cc-c         # int (*daytab)[13]    (parentheses needed: kept)
//         or:  printf 'x () * [] * () char\n' | ./cc-c       # char (*(*x())[])()
//         or:  printf 'x * * [] char\n' | ./cc-c             # char (**x)[]

//K&R2 exercise 5-19: modify undcl so that it does not add redundant
//parentheses to declarations.
//
//Builds on section 5.12's undcl (archived just below 5-18). undcl
//wrapped EVERY * as (*...), giving char (*(*argv)) for char **argv.
//
//When are the parentheses needed? Only when the NEXT token is () or
//[]. Those bind tighter than *, so without parentheses they'd attach
//to the name instead of to the pointer:
//
//    *daytab[13]       daytab is an array of pointers
//    (*daytab)[13]     daytab is a pointer to an array
//
//Anything else after the * -- another *, the type name, or the end of
//the line -- can't steal it, so "*" alone is enough.
//
//So at each *, undcl must LOOK AHEAD one token before deciding. It
//reads the next token, then marks it to be handed out again: a flag,
//prevtoken, makes the next gettoken() call return the same token
//instead of reading new input. That's "peek": see a token without
//using it up.
//
//    x * * [] char
//    *   next is *    ->  no ( )    *x
//    *   next is []   ->  ( )       (**x)
//    []                             (**x)[]
//    char                           char (**x)[]
//
//Also fixed: the inner loop now stops at EOF as well as '\n'. undcl's
//loop waited only for '\n', so a last line without one made it loop
//forever printing "invalid input" (gettoken returns EOF every time).
#define MAXTOKEN 100
#define MAXOUT   1000

enum { NAME, PARENS, BRACKETS };

int gettoken(void);

int tokentype;              //type of last token
char token[MAXTOKEN];       //last token string
char out[MAXOUT];
int prevtoken = 0;          //1: next gettoken() repeats the last token

//undcl:  convert word descriptions to declarations
int main(void)
{
    int type;
    char temp[MAXTOKEN + MAXOUT];   //room for token, a blank and out

    while (gettoken() != EOF) {
        strcpy(out, token);
        while ((type = gettoken()) != '\n' && type != EOF)
            if (type == PARENS || type == BRACKETS)
                strcat(out, token);
            else if (type == '*') {
                type = gettoken();      //peek at the next token...
                prevtoken = 1;          //...and hand it out again
                if (type == PARENS || type == BRACKETS)
                    sprintf(temp, "(*%s)", out);
                else
                    sprintf(temp, "*%s", out);
                strcpy(out, temp);
            } else if (type == NAME) {
                sprintf(temp, "%s %s", token, out);
                strcpy(out, temp);
            } else
                printf("invalid input at %s\n", token);
        printf("%s\n", out);
    }
    return 0;
}

//gettoken:  return next token
int gettoken(void)
{
    int c, getch(void);
    void ungetch(int);
    char *p = token;

    if (prevtoken) {                    //a peeked-at token: repeat it
        prevtoken = 0;
        return tokentype;
    }
    while ((c = getch()) == ' ' || c == '\t')
        ;
    if (c == '(') {
        if ((c = getch()) == ')') {
            strcpy(token, "()");
            return tokentype = PARENS;
        } else {
            ungetch(c);
            return tokentype = '(';
        }
    } else if (c == '[') {
        for (*p++ = c; (*p++ = getch()) != ']'; )
            ;
        *p = '\0';
        return tokentype = BRACKETS;
    } else if (isalpha(c)) {
        for (*p++ = c; isalnum(c = getch()); )
            *p++ = c;
        *p = '\0';
        ungetch(c);
        return tokentype = NAME;
    } else
        return tokentype = c;
}

win@DESKTOP-MEIH88T:~/webdev-projects$ gcc -Wall -Wextra cc-c.c -o cc-c
win@DESKTOP-MEIH88T:~/webdev-projects$ printf 'int x)\nchar **argv\n' | ./cc-c
error: syntax error
argv:  pointer to pointer to char
win@DESKTOP-MEIH88T:~/webdev-projects$  printf 'int (*pf\nint *daytab[13]\n' | ./cc-c
error: missing )
daytab:  array[13] of pointer to int
win@DESKTOP-MEIH88T:~/webdev-projects$ printf 'int *\nvoid (*comp)()\n' | ./cc-c
error: expected name or (dcl)
comp:  pointer to function returning void
win@DESKTOP-MEIH88T:~/webdev-projects$  printf 'int x[5\nint (*daytab)[13]\n' | ./cc-c
error: missing ]
daytab:  pointer to array[13] of int
win@DESKTOP-MEIH88T:~/webdev-projects$ printf '*x\n\nchar c' | ./cc-c
error: expected a data type
c:  char