1

我目前正在阅读这一版的编译器:原理、技术和工具。

我以前从未用 C 编写过代码(尽管我曾涉足 C++),但从其他编程知识来看,大多数代码都是有意义的,但我注意到一个怪癖,即函数是这样定义的:

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

发现有什么不对劲,我查了一下,果然,定义函数的方法似乎已经过时了。我希望有人可以阅读我在下面重新输入的代码片段,并警告我任何我可能不会注意到和接受的不良做法或过时的技术。此外,任何有关 C 的新功能的提醒,这可能有助于我编写更整洁的代码,我们将不胜感激。我主要是在寻找利用标准 C 规范中不再存在的语义、特性、函数等的代码,而不是从风格的角度来看。

另外,如果您有这本书的副本,并且不介意翻阅它并查看(甚至从记忆中)是否可以发现其他一些过时或多余的做事方法,那也太棒了!

我不确定这是否更适合 CodeReview,如果是,请发表评论,我会在那里删除并重新发布,但我认为由于它来自流行的编程文本,它可能更适合这里。抱歉,如果我不正确。

全局.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[];

词法分析器

#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;
    }
}

解析器.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");
}
4

2 回答 2

2

从略读代码开始,除了您已经提出的旧式函数参数声明之外,我看到的唯一其他过时的功能是在不返回任何内容的函数上缺少返回类型。例如,该parse函数不返回任何内容,旧代码通过将其声明为 just 来表示parse(),但现代代码将需要void parse(). 还有一个问题是不带任何参数的函数应该void括号之间(例如,void parse(void)),但我认为这不是严格要求的。

于 2014-03-27T23:33:49.753 回答
1

一些无序的东西:
全局变量(tokenval,lineno ...)是不好的风格。

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

只有我的意见,但更具可读性:

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

有一些函数调用可能会失败
但没有错误检查(至少 scanf,也许更多)

return symtable[p].token

这不应该编译,丢失;

省略返回类型就像parse()是一种糟糕的风格。
像这样的东西

match (t)
    int t;

应该

match (int t)

(同样,返回类型也丢失了)

也许我因为愚蠢而被否决,但是:
动态大小的数组定义,比如char lexbuf[BSIZE];......
使用所有不同的标准,我忘记了它在哪里被允许和在哪里不允许,
但如果你想确保你可以编译它在任何地方,
自己分配(指针,malloc,免费)

于 2014-03-27T23:36:06.987 回答