Question

I'm currently reading this edition of Compiler: Principles, Techniques & Tools.

I've never coded in C before (Although I've dabbled in C++) but from other programming knowledge most of the code makes sense, however I noticed a quirk which is that the functions are defined as such:

emit(t, tval)
    int t, tval
{
}

Figuring something was amiss, I looked it up and sure enough, that method of defining functions seems to be obsolete. I was hoping that someone could perhaps read the bits of code which I retyped below and warn me of any bad practices or obsolete techniques which I otherwise might not notice and pick up on. Also, any heads up on newer features of C which may help me write the code neater would be greatly appreciated. I am primarily looking for code which takes advantage of semantics, features, functions, etc that are no longer on the standard C specification, rather than from a stylistic point of view.

Also, if you have a copy of the book, and wouldn't mind flicking through it and seeing (or even from memory) if you can spot some other obsolete or redundant methods of doing things, that'd be fantastic too!

I wasn't sure whether this would fit better in CodeReview, if so, please comment and I'll delete and repost there, but I think since it's from a popular programming text, it might be more fitting here. Apologies if I'm incorrect.

global.h

#include <stdio.h>
#include <ctype.h>

#define BSIZE   128
#define NONE    -1
#define EOS     '\0'

#define NUM     256
#define DIV     257
#define MOD     258
#define ID      259
#define DONE    260

int tokenval;
int lineno;

struct entry {
    char *lexptr;
    int token;
};

struct entry symtable[];

lexer.c

#include "global.h"

char lexbuf[BSIZE];
int lineno = 1;
int tokenval = NONE;

int lexan()
{
    int t;

    while (1) {
        t = getchar();

        if (t == ' ' || t == '\t')
            ;
        else if (t == '\n')
            lineno++;
        else if (isdigit(t)) {
            ungetc(t, stdin);
            scanf("%d", &tokenval);
            return NUM;
        }
        else if (isalpha(t)) {
            int p, b = 0;
            while (isalnum(t)) {
                lexbuf[b] = t;
                b++;
                if (b >= BSIZE)
                    error("compiler error");
            }
            lexbuf[b] = EOS;
            if (t != EOF)
                ungetc(t, stdin);
            p = lookup(lexbuf);
            if (p == 0)
                p = insert(lexbuf, ID);
            tokenval = p;
            return symtable[p].token
        }
        else if (t == EOF)
            return DONE;
    }
}

parser.c

#include "global.h"

int lookahead;

parse()
{
    lookahead = lexan();
    while (lookahead != DONE) {
        expr(); match(';');
    }
}

expr()
{
    int t;
    term();
    while (1)
        switch (lookahead) {
            case '+': case '-':
                t = lookahead;
                match(lookahead); term(); emit(t, NONE);
                continue;
            default:
                return;
        }
}

term()
{
    int t;
    factor();
    while (1)
        switch (lookahead) {
            case '*': case '/':
                t = lookahead;
                match(lookahead); factor(); emit(t, NONE);
                continue;
            default:
                return;
        }
}

factor()
{
    switch (lookahead) {
        case '(':
            match('('); expr(); match(')'); break;
        case NUM:
            emit(NUM, tokenval); match(NUM); break;
        case ID:
            emit(ID, tokenval); match(ID); break;
        default:
            error("Syntax error");
    }
}

match (t)
    int t;
{
    if (lookahead == t)
        lookahead = lexan();
    else error("Syntax error");
}
Was it helpful?

Solution

From just skimming over the code, other than the old-style function argument declarations you've already brought up, the only other outdated feature I see is the lack of return type on functions that don't return anything. For example, the parse function returns nothing, which this old code denotes by declaring it as just parse(), yet modern code would require void parse(). There's also the issue that functions that don't take any arguments should have void between their parentheses (e.g., void parse(void)), but I don't think this is strictly required.

OTHER TIPS

Some unordered things:
Global variables (tokenval, lineno...) are bad style.

if (t == ' ' || t == '\t')
    ;

Only my opinion, but far more readable:

if (t == ' ' || t == '\t') {}

There are some function calls which can fail
but do not have error checking (at least scanf, maybe more)

return symtable[p].token

This shouldn´t compile, missing ;

Omitting return types like parse() is alo bad style.
And things like

match (t)
    int t;

should be

match (int t)

(again, return type is missing too)

And maybe i´m get downvoted for being stupid, but:
Dynamic-sized array definitions like char lexbuf[BSIZE]; ...
with all the different standards, i lost track where it is allowed and where not,
but if you want to be sure that you can compile it anywhere,
allocate it yourself (pointer, malloc, free)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top