/* * Reads function-like macro arguments. Returns true if the argument list ends * with "...". Otherwise false. */ static bool read_funclike_define_args(CppContext *ctx, Dict *param) { for (;;) { Token *tok = read_cpp_token(ctx); if (is_punct(tok, ')')) return false; if (dict_size(param)) { if (!is_punct(tok, ',')) error_token(tok, "',' expected, but got '%s'", token_to_string(tok)); tok = read_cpp_token(ctx); } if (!tok || tok->toktype == TOKTYPE_NEWLINE) error_token(tok, "missing ')' in macro parameter list"); if (is_punct(tok, KEYWORD_THREEDOTS)) { Token *subst = make_token(ctx, TOKTYPE_MACRO_PARAM, (TokenValue)dict_size(param)); dict_put(param, to_string("__VA_ARGS__"), subst); Token *tok1 = read_cpp_token(ctx); if (!is_punct(tok1, ')')) error_token(tok1, "')' expected, but got '%s'", token_to_string(tok1)); return true; } if (tok->toktype != TOKTYPE_IDENT) error_token(tok, "identifier expected, but got '%s'", token_to_string(tok)); Token *subst = make_token(ctx, TOKTYPE_MACRO_PARAM, (TokenValue)dict_size(param)); dict_put(param, tok->val.str, subst); } }
/* * Reads a file name of #include directive. If the file name is quoted with <>, * "std" will set to true. If quoted with doublequote, set to false. We use * expand_one() rather than read_cpp_token(), because macros are allowed to be * used in #include. * (C99 6.10.2 Source file inclusion) */ static void read_cpp_header_name(CppContext *ctx, String **name, bool *std) { if (LIST_IS_EMPTY(ctx->ungotten)) { *name = read_header_name(ctx, std); if (name) return; } Token *tok = expand_one(ctx); if (!tok || tok->toktype == TOKTYPE_NEWLINE) error_token(tok, "expected file name, but got '%s'", token_to_string(tok)); if (tok->toktype == TOKTYPE_STRING) { *name = tok->val.str; *std = false; return; } List *tokens = make_list(); if (is_punct(tok, '<')) { for (;;) { Token *tok = expand_one(ctx); if (!tok || tok->toktype == TOKTYPE_NEWLINE) error_token(tok, "premature end of header name"); if (is_punct(tok, '>')) break; list_push(tokens, tok); } *name = join_tokens(tokens, false); *std = true; return; } error_token(tok, "'<' expected, but got '%s'", token_to_string(tok)); }
/* * C99 6.10.9 Pragma operator. * * _Pragma("tokens ...") is equivalent to #pragma tokens .... */ static void handle_pragma_macro(CppContext *ctx, Token *ignore) { Token *tok = read_cpp_token(ctx); if (!is_punct(tok, '(')) error_token(tok, "'(' expected, but got '%s'", token_to_string(tok)); Token *body = read_cpp_token(ctx); if (body->toktype != TOKTYPE_STRING) error_token(body, "string expected, but got '%s'", token_to_string(body)); tok = read_cpp_token(ctx); if (!is_punct(tok, ')')) error_token(tok, "')' expected, but got '%s'", token_to_string(tok)); File *file = make_string_file(body->val.str); do_include(ctx, file); handle_pragma(ctx); }
/* * #undef * (C99 6.10.5 Scope of macro definisions, paragraph 2) */ static void read_undef(CppContext *ctx) { Token *name = read_cpp_token(ctx); if (!name || name->toktype != TOKTYPE_IDENT) error_token(name, "undef works only to an identifier, but got '%s'", token_to_string(name)); expect_newline(ctx); dict_delete(ctx->defs, name->val.str); }
char *epur_single(char *str, t_epur *e) { int l; l = 0; while (l < 5) { if (error_token(str, e, l) == -1) return (NULL); if (str[e->i] == e->com[l] && str[e->i + 1] != e->com[l] && str[e->i - 1] != '2') { if (str[e->i - 1] != ' ' && str[e->i + 1] != ' ') { e->tmp[e->k++] = ' '; e->tmp[e->k++] = e->com[l]; e->tmp[e->k++] = ' '; e->i++; } epur_single_next(str, e, l++); } else if (check_double(str, e, l++) == -1) return (NULL); } return (e->tmp); }
static Token *cppnum_to_int(Token *tok) { char *p = STRING_BODY(tok->val.str); int base = 10; int val = 0; // Read prefix such as "0" or "0x". if (*p == '0') { p++; if (*p == 'x' || *p == 'X') { base = 16; p++; } else if (*p == 'b' || *p == 'B') { // Binary constant using '0b' prefix is GNU extension base = 2; p++; } else { base = 8; } } // Read numbers until non-number character. for (; *p; p++) { int v; if ('0' <= *p && *p <= '9') v = *p - '0'; else if ('a' <= *p && *p <= 'f') v = *p - 'a' + 10; else if ('A' <= *p && *p <= 'F') v = *p - 'A' + 10; else break; if (v >= base) error_token(tok, "invalid digit '%c' in base %d number", *p, base); val *= base; val += v; } // Ignore all suffixes for now while (*p == 'U' || *p == 'u' || *p == 'L' || *p == 'l') p++; if (*p) error_token(tok, "invalid char '%c' in a number '%s'", *p, STRING_BODY(tok->val.str)); Token *r = copy_token(tok); r->toktype = TOKTYPE_INT; r->val.i = val; return r; }
/* * #error * (C99 6.10.5 Error directive) */ static void read_error_directive(CppContext *ctx, Token *define) { String *buf = make_string(); Token *tok = read_cpp_token(ctx); while(tok && tok->toktype != TOKTYPE_NEWLINE) { o1(buf, ' '); string_append(buf, token_to_string(tok)); tok = read_cpp_token(ctx); } error_token(define, "error: #error:%s", STRING_BODY(buf)); }
/* * #line * (C99 6.10.4 Line control) * * Line directive must be one of the following form in macro-expanded form: * * #line digit-sequence * #line digit-sequence "s-char-sequenceopt" */ static void handle_line_directive(CppContext *ctx) { Token *tok = expand_one(ctx); if (!tok || tok->toktype != TOKTYPE_CPPNUM) error_token(tok, "number expected, but got '%s'", token_to_string(tok)); int line = cppnum_to_num(tok)->val.i; tok = expand_one(ctx); if (tok && tok->toktype == TOKTYPE_NEWLINE) { ctx->file->line = line; return; } if (tok && tok->toktype == TOKTYPE_STRING) { expect_newline(ctx); ctx->file->line = line; ctx->file->filename = tok->val.str; return; } error_token(tok, "filename expected, but got '%s'", token_to_string(tok)); }
/* * Reads "defined" unary operator of the form "defined <identifier>" or * "defined(<identifier>)". The token "defined" is already read when the * function is called. * * (C99 6.10.1 Conditional inclusion, paragraph 1) */ static Token *read_defined(CppContext *ctx) { Token *tok = read_cpp_token(ctx); if (is_punct(tok, '(')) { tok = read_cpp_token(ctx); Token *tok1 = read_cpp_token(ctx); if (!tok1 || !is_punct(tok1, ')')) error_token(tok1, "')' expected, but got '%s'", token_to_string(tok1)); } Token *r = copy_token(tok); r->toktype = TOKTYPE_CPPNUM; r->val.i = is_defined(ctx, tok); return r; }
static void paste(String *b, Token *tok) { switch (tok->toktype) { case TOKTYPE_IDENT: case TOKTYPE_CPPNUM: string_append(b, STRING_BODY(tok->val.str)); return; case TOKTYPE_PUNCT: string_append(b, token_to_string(tok)); return; default: error_token(tok, "pasting invalid token: '%s'", token_to_string(tok)); } }
/* * Evaluate a given tokens as an integer constant expression and returns the * result. */ static int eval_const_expr(CppContext *cppctx, List *tokens) { if (LIST_LEN(tokens) == 1 && ((Token *)LIST_REF(tokens, 0))->toktype == TOKTYPE_CPPNUM) return ((Token *)LIST_REF(tokens, 0))->val.i; CppContext *virt = make_virt_cpp_context(cppctx, tokens); ReadContext *readctx = make_read_context(cppctx->file, NULL, virt); readctx->in_const_expr = true; Var *var = read_comma_expr(readctx); Token *tok = read_token(readctx); if (tok) error_token(tok, "newline expected, but got '%s'", token_to_string(tok)); ASSERT(var->stype == VAR_IMM); if (!ctype_equal(var->ctype, CTYPE_INT)) error_cpp_ctx(cppctx, "integer expected"); return var->val.i; }
static void read_directive(CppContext *ctx) { Token *tok; if (read_if(ctx, "define")) read_define(ctx); else if (read_if(ctx, "undef")) read_undef(ctx); else if (read_if(ctx, "if")) handle_cond_incl(ctx, COND_IF); else if (read_if(ctx, "elif")) handle_cond_incl(ctx, COND_ELIF); else if (read_if(ctx, "else")) handle_cond_incl(ctx, COND_ELSE); else if (read_if(ctx, "ifdef")) handle_cond_incl(ctx, COND_IFDEF); else if (read_if(ctx, "ifndef")) handle_cond_incl(ctx, COND_IFNDEF); else if (read_if(ctx, "endif")) handle_cond_incl(ctx, COND_ENDIF); else if (read_if(ctx, "include")) handle_include(ctx); else if (read_if(ctx, "line")) handle_line_directive(ctx); else if (read_if(ctx, "pragma")) handle_pragma(ctx); else if ( (tok = read_if(ctx, "error")) ) { read_error_directive(ctx, tok); } else { tok = read_cpp_token(ctx); if (tok && tok->toktype == TOKTYPE_NEWLINE) // 6.10.7 NULL directive. Do nothing. return; error_token(tok, "unsupported preprocessor directive: '%s'", token_to_string(tok)); } }
/* * Reads comma-separated arguments of function-like macro invocation. Comma * characters in matching parentheses are not considered as separator. * * (C99 6.10.3 Macro replacement, sentence 10) */ static List *read_args_int(CppContext *ctx, Macro *macro) { List *r = make_list(); List *arg = make_list(); int depth = 0; Token *tok = peek_cpp_token(ctx); if (!tok || !is_punct(tok, '(')) return NULL; read_cpp_token(ctx); for (Token *tok1 = read_cpp_token(ctx); ; tok1 = read_cpp_token(ctx)) { if (!tok1) error_token(tok, "unterminated macro argument list"); if (tok1->toktype == TOKTYPE_NEWLINE) continue; if (depth) { if (is_punct(tok1, ')')) depth--; list_push(arg, tok1); continue; } if (is_punct(tok1, '(')) depth++; if (is_punct(tok1, ')')) { unget_cpp_token(ctx, tok1); list_push(r, arg); return r; } bool in_threedots = macro->is_varg && LIST_LEN(r) + 1 == macro->nargs; if (is_punct(tok1, ',') && !in_threedots) { list_push(r, arg); arg = make_list(); continue; } list_push(arg, tok1); } }
static bool is_defined(CppContext *ctx, Token *tok) { if (!tok || tok->toktype != TOKTYPE_IDENT) error_token(tok, "identifier expected, but got '%s'", token_to_string(tok)); return dict_has(ctx->defs, tok->val.str); }
void expect_newline(CppContext *ctx) { Token *tok = read_cpp_token(ctx); if (tok && tok->toktype != TOKTYPE_NEWLINE) error_token(tok, "newline expected, but got '%s'", token_to_string(tok)); }