mirror of https://github.com/rui314/chibicc
Add #if and #endif
This commit is contained in:
parent
d138864a2a
commit
bf6ff928ad
|
@ -234,6 +234,7 @@ struct Node {
|
|||
};
|
||||
|
||||
Node *new_cast(Node *expr, Type *ty);
|
||||
int64_t const_expr(Token **rest, Token *tok);
|
||||
Obj *parse(Token *tok);
|
||||
|
||||
//
|
||||
|
|
3
parse.c
3
parse.c
|
@ -129,7 +129,6 @@ static int64_t eval2(Node *node, char **label);
|
|||
static int64_t eval_rval(Node *node, char **label);
|
||||
static Node *assign(Token **rest, Token *tok);
|
||||
static Node *logor(Token **rest, Token *tok);
|
||||
static int64_t const_expr(Token **rest, Token *tok);
|
||||
static double eval_double(Node *node);
|
||||
static Node *conditional(Token **rest, Token *tok);
|
||||
static Node *logand(Token **rest, Token *tok);
|
||||
|
@ -1551,7 +1550,7 @@ static int64_t eval_rval(Node *node, char **label) {
|
|||
error_tok(node->tok, "invalid initializer");
|
||||
}
|
||||
|
||||
static int64_t const_expr(Token **rest, Token *tok) {
|
||||
int64_t const_expr(Token **rest, Token *tok) {
|
||||
Node *node = conditional(rest, tok);
|
||||
return eval(node);
|
||||
}
|
||||
|
|
83
preprocess.c
83
preprocess.c
|
@ -1,5 +1,14 @@
|
|||
#include "chibicc.h"
|
||||
|
||||
// `#if` can be nested, so we use a stack to manage nested `#if`s.
|
||||
typedef struct CondIncl CondIncl;
|
||||
struct CondIncl {
|
||||
CondIncl *next;
|
||||
Token *tok;
|
||||
};
|
||||
|
||||
static CondIncl *cond_incl;
|
||||
|
||||
static bool is_hash(Token *tok) {
|
||||
return tok->at_bol && equal(tok, "#");
|
||||
}
|
||||
|
@ -22,6 +31,13 @@ static Token *copy_token(Token *tok) {
|
|||
return t;
|
||||
}
|
||||
|
||||
static Token *new_eof(Token *tok) {
|
||||
Token *t = copy_token(tok);
|
||||
t->kind = TK_EOF;
|
||||
t->len = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
// Append tok2 to the end of tok1.
|
||||
static Token *append(Token *tok1, Token *tok2) {
|
||||
if (!tok1 || tok1->kind == TK_EOF)
|
||||
|
@ -36,6 +52,54 @@ static Token *append(Token *tok1, Token *tok2) {
|
|||
return head.next;
|
||||
}
|
||||
|
||||
// Skip until next `#endif`.
|
||||
static Token *skip_cond_incl(Token *tok) {
|
||||
while (tok->kind != TK_EOF) {
|
||||
if (is_hash(tok) && equal(tok->next, "endif"))
|
||||
return tok;
|
||||
tok = tok->next;
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
// Copy all tokens until the next newline, terminate them with
|
||||
// an EOF token and then returns them. This function is used to
|
||||
// create a new list of tokens for `#if` arguments.
|
||||
static Token *copy_line(Token **rest, Token *tok) {
|
||||
Token head = {};
|
||||
Token *cur = &head;
|
||||
|
||||
for (; !tok->at_bol; tok = tok->next)
|
||||
cur = cur->next = copy_token(tok);
|
||||
|
||||
cur->next = new_eof(tok);
|
||||
*rest = tok;
|
||||
return head.next;
|
||||
}
|
||||
|
||||
// Read and evaluate a constant expression.
|
||||
static long eval_const_expr(Token **rest, Token *tok) {
|
||||
Token *start = tok;
|
||||
Token *expr = copy_line(rest, tok->next);
|
||||
|
||||
if (expr->kind == TK_EOF)
|
||||
error_tok(start, "no expression");
|
||||
|
||||
Token *rest2;
|
||||
long val = const_expr(&rest2, expr);
|
||||
if (rest2->kind != TK_EOF)
|
||||
error_tok(rest2, "extra token");
|
||||
return val;
|
||||
}
|
||||
|
||||
static CondIncl *push_cond_incl(Token *tok) {
|
||||
CondIncl *ci = calloc(1, sizeof(CondIncl));
|
||||
ci->next = cond_incl;
|
||||
ci->tok = tok;
|
||||
cond_incl = ci;
|
||||
return ci;
|
||||
}
|
||||
|
||||
// Visit all tokens in `tok` while evaluating preprocessing
|
||||
// macros and directives.
|
||||
static Token *preprocess2(Token *tok) {
|
||||
|
@ -50,6 +114,7 @@ static Token *preprocess2(Token *tok) {
|
|||
continue;
|
||||
}
|
||||
|
||||
Token *start = tok;
|
||||
tok = tok->next;
|
||||
|
||||
if (equal(tok, "include")) {
|
||||
|
@ -72,6 +137,22 @@ static Token *preprocess2(Token *tok) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (equal(tok, "if")) {
|
||||
long val = eval_const_expr(&tok, tok);
|
||||
push_cond_incl(start);
|
||||
if (!val)
|
||||
tok = skip_cond_incl(tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (equal(tok, "endif")) {
|
||||
if (!cond_incl)
|
||||
error_tok(start, "stray #endif");
|
||||
cond_incl = cond_incl->next;
|
||||
tok = skip_line(tok->next);
|
||||
continue;
|
||||
}
|
||||
|
||||
// `#`-only line is legal. It's called a null directive.
|
||||
if (tok->at_bol)
|
||||
continue;
|
||||
|
@ -86,6 +167,8 @@ static Token *preprocess2(Token *tok) {
|
|||
// Entry point function of the preprocessor.
|
||||
Token *preprocess(Token *tok) {
|
||||
tok = preprocess2(tok);
|
||||
if (cond_incl)
|
||||
error_tok(cond_incl->tok, "unterminated conditional directive");
|
||||
convert_keywords(tok);
|
||||
return tok;
|
||||
}
|
||||
|
|
12
test/macro.c
12
test/macro.c
|
@ -14,6 +14,18 @@ int main() {
|
|||
assert(5, include1, "include1");
|
||||
assert(7, include2, "include2");
|
||||
|
||||
#if 0
|
||||
#include "/no/such/file"
|
||||
assert(0, 1, "1");
|
||||
#endif
|
||||
|
||||
int m = 0;
|
||||
|
||||
#if 1
|
||||
m = 5;
|
||||
#endif
|
||||
assert(5, m, "m");
|
||||
|
||||
printf("OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ static void verror_at(char *filename, char *input, int line_no,
|
|||
line--;
|
||||
|
||||
char *end = loc;
|
||||
while (*end != '\n')
|
||||
while (*end && *end != '\n')
|
||||
end++;
|
||||
|
||||
// Print out the line.
|
||||
|
|
Loading…
Reference in New Issue