/* qc-lex.l lexer for quakec Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2001/06/12 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ %option bison-locations %option bison-bridge %option reentrant %option prefix="qc_yy" %option noyywrap %option debug %option stack %top{ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include #include #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/cpp.h" #include "tools/qfcc/include/debug.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/grab.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/pragma.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/rua-lang.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" } %{ #ifndef YY_PROTO # define YY_PROTO(x) x #else # define YY_FLEX_REALLOC_HACK #endif #define YY_NO_INPUT // debian flex #define YY_NO_UNPUT // debian flex #define YY_NO_YYINPUT #define YY_NO_YYUNPUT #define YYSTYPE rua_tok_t #define YYLTYPE rua_loc_t typedef struct { bool saw_true; bool saw_else; bool own_state; bool enabled; int line; } rua_cond_t; typedef struct DARRAY_TYPE (rua_cond_t) rua_cond_stack_t; typedef struct { YY_BUFFER_STATE buffer; rua_cond_stack_t cond_stack; rua_loc_t location; } rua_incl_t; typedef struct DARRAY_TYPE (rua_macro_t *) rua_macro_list_t; typedef struct DARRAY_TYPE (rua_incl_t) rua_include_stack_t; typedef struct rua_extra_s { int start_state; bool preprocessor; bool recording; bool params; bool expand; bool suppressed; bool no_lex; bool did_space; rua_parser_t *parser; pre_yypstate *pre_state; pre_yypstate *args_state; rua_cond_stack_t cond_stack; rua_include_stack_t include_stack; dstring_t *dstr; symtab_t *macro_tab; rua_macro_t *pending_macro; // function-type waiting for args rua_macro_t *macro; // macro being expanded yyscan_t subscanner; rua_loc_t location; char str_text[8]; // EPHEMERAL! for short token strings } rua_extra_t; #define YY_EXTRA_TYPE rua_extra_t * rua_extra_t *qc_yyget_extra (yyscan_t yyscanner) __attribute__((pure)); int yyget_lineno (yyscan_t yyscanner) __attribute__((pure)); int yyget_column (yyscan_t yyscanner) __attribute__((pure)); YYSTYPE *yyget_lval (yyscan_t yyscanner) __attribute__((pure)); YYLTYPE *yyget_lloc (yyscan_t yyscanner) __attribute__((pure)); int yyget_leng (yyscan_t yyscanner) __attribute__((pure)); char *yyget_text (yyscan_t yyscanner) __attribute__((pure)); int yyget_debug (yyscan_t yyscanner) __attribute__((pure)); FILE *yyget_in (yyscan_t yyscanner) __attribute__((pure)); FILE *yyget_out (yyscan_t yyscanner) __attribute__((pure)); static int directive (const char *token, yyscan_t scanner); static void update_loc (rua_loc_t *loc, size_t textlen); static void save_text (rua_tok_t *tok, const char *text, size_t textlen, int state, rua_extra_t *extra); static void next_line (rua_loc_t *loc, yyscan_t scanner); static void dump_state_stack (yyscan_t scanner); static void dump_token (rua_tok_t *tok, rua_loc_t *loc, int state); static void dump_debug (int act, const char *text, int state); typedef enum { rua_eof = 1, rua_ignore, rua_error, rua_id, rua_number, rua_vector, rua_string, rua_char, rua_space, rua_ellipsis, rua_asx, rua_incop, rua_grab, rua_va_opt, rua_num_term, } rua_term; // for debian flex #ifndef yystart #define yystart() YY_START #define yybuffer YY_BUFFER_STATE #endif #define YY_USER_ACTION \ update_loc (yylloc, yyleng); \ save_text (yylval, yytext, yyleng, yystart(), extra); \ if (0) { dump_debug (yy_act, yytext, yystart()); } \ if (0) { dump_token (yylval, yylloc, yystart ()); } \ if (0) { dump_state_stack(yyscanner); } %} s [ \t] m [\-+] D [0-9] IDs [a-zA-Z_] IDc [a-zA-Z_0-9] ID {IDs}{IDc}* FD [fFdD] UL ([uU]?([lL][lL]?)?) ULFD ({UL}|{FD}) RANGE \.\. ELLIPSIS \.\.\. FRAMEID {ID}(\.{ID})* PRAGMAID {ID}(-{ID})* s_string \"(\\.|[^"\\])*\" c_string '(\\.|[^'\\])*' h_string <[^>]*> q_string \"[^\"]*\" pp_number \.?{D}({IDc}|'{IDc}|[eEpP]{m}|\.)* pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}? %x GRAB_FRAME GRAB_OTHER GRAB_WRITE %x COMMENT LCOMMENT %x DIRECTIVE TEXT %x PRAGMA VECTOR %s PREPROC PREEXPR %s MACRO ARGS %x CONT %% auto extra = qc_yyget_extra (yyscanner); yyset_debug (0, yyscanner); grab_frame = GRAB_FRAME; grab_other = GRAB_OTHER; grab_write = GRAB_WRITE; "/*" { warning (0, "nested /* in comment"); yymore();} \*+"/" { yy_pop_state (yyscanner); return -rua_space; } \r*\n { next_line (yylloc, yyscanner); yymore();} [^*/\n]* { yymore(); }/* munch on anything but possible end of comment */ \/+[^*\n]* { yymore(); }/* handle /s not followed by * */ \*+/[^/] { yymore(); }/* handle *s not followed by / */ <> { error (0, "EOF in comment"); return 0; } <*>"/*" { yy_push_state (COMMENT, yyscanner); yymore(); } <*>"//" { yymore(); yy_push_state (LCOMMENT, yyscanner); } [^\\\r\n]+ /* consume all but \ and EOL (see line continuation) */ [\\]+/[^\r\n] /* consume \ */ \r*\n { next_line (yylloc, yyscanner); yy_pop_state (yyscanner); int state = yystart (); if (state == PREEXPR || state == PREPROC || state == DIRECTIVE || state == MACRO) { return PRE_EOD; } } ^# { return '#'; } ^#{s}+/{D} { yy_push_state (PREPROC, yyscanner); extra->preprocessor = true; return PRE_LINE; } ^. { yyless (yyleng - 1); yy_pop_state (yyscanner); } <*>^{s}*#{s}* { yy_push_state (DIRECTIVE, yyscanner); extra->preprocessor = true; } <> { return 0; } ^{s}* { return -rua_space; } {ID} { { int tok = directive (yytext, yyscanner); if (tok >= 0) { return tok; } } } {ID} { return PRE_ID; } {ID}\( { return PRE_IDp; } \r*\n { next_line (yylloc, yyscanner); return -rua_space; } \r*\n | \r*\n | \r*\n | \r*\n { next_line (yylloc, yyscanner); return PRE_EOD; } [^\\\r\n]* { return PRE_TEXT; } [\\]* { return PRE_TEXT; } ## { return PRE_CONCAT; } # { return '#'; } {h_string} { return PRE_HSTRING; } {q_string} { return PRE_QSTRING; } {q_string} { return PRE_QSTRING; } defined { return PRE_DEFINED; } {ID} { return PRE_ID; } { {ID} | @{ID} { return -rua_id; } } @ { return '@'; } {pp_number} { return -rua_number; } {pp_vnumber} { return -rua_vector; } {pp_number} { return -rua_number; } {m} { return yytext[0]; } ' { return -rua_eof; } {s_string} { return -rua_string; }; {c_string} { return -rua_char; } [+\-*/&|^%]= { return -rua_asx; } "%%=" { return -rua_asx; } "<<=" { return -rua_asx; } ">>=" { return -rua_asx; } [!(){}.*/&|^~+\-=\[\];,#%?:] { return yytext[0]; } "·" { return QC_DOT; } "⋀" { return QC_WEDGE; } "•" { return QC_DOT; } "∧" { return QC_WEDGE; } "∨" { return QC_REGRESSIVE; } "†" { return QC_REVERSE; } "∗" { return QC_STAR; } "×" { return QC_CROSS; } "⋆" { return QC_DUAL; } "%%" { return QC_MOD; } {ELLIPSIS} { return -rua_ellipsis; } "<<" { return QC_SHL; } ">>" { return QC_SHR; } "&&" { return QC_AND; } "||" { return QC_OR; } "==" { return QC_EQ; } "!=" { return QC_NE; } "<=" { return QC_LE; } ">=" { return QC_GE; } "<" { return QC_LT; } ">" { return QC_GT; } "++" { return -rua_incop; } "--" { return -rua_incop; } "$"{s}*{FRAMEID} { return -rua_grab; } {FRAMEID} { add_frame_macro (yytext); } [^\r\n]* /* skip */ {s_string} { const char *s = make_string (yytext, 0); write_frame_macros (s); BEGIN (GRAB_OTHER); // ignore rest of line } {PRAGMAID} | @{PRAGMAID} { return PRE_ID; } \r*\n { next_line (yylloc, yyscanner); return PRE_EOD; } <*>\\\r*\n { /* line continuation */ next_line (yylloc, yyscanner); } <> { warning (0, "\\ at end of file"); yy_pop_state (yyscanner); return 0; } <*>\r*\n { next_line (yylloc, yyscanner); if (yyg->yy_start_stack_ptr) { yy_pop_state (yyscanner); extra->preprocessor = false; } } <*>{s}+ { return -rua_space; } <*>. { error (0, "all your typo are belong to us:%d %d-%d", yystart(), yylloc->column, yylloc->last_column); } %% static directive_t cond_directives[] = { {"if", PRE_IF}, {"ifdef", PRE_IFDEF}, {"ifndef", PRE_IFNDEF}, {"else", PRE_ELSE}, {"elif", PRE_ELIF}, {"elifdef", PRE_ELIFDEF}, {"elifndef", PRE_ELIFNDEF}, {"endif", PRE_ENDIF}, }; const char * rua_directive_get_key (const void *dir, void *unused) { return ((directive_t*)dir)->name; } const char * rua_keyword_get_key (const void *kw, void *unused) { return ((keyword_t*)kw)->name; } static int directive (const char *token, yyscan_t scanner) { static hashtab_t *cond_directive_tab; yy_pop_state (scanner); // pop DIRECTIVE off the stack yy_push_state (PREPROC, scanner); if (!cond_directive_tab) { cond_directive_tab = Hash_NewTable (253, rua_directive_get_key, 0, 0, 0); for (size_t i = 0; i < ARRCOUNT(cond_directives); i++) { Hash_Add (cond_directive_tab, &cond_directives[i]); } } auto extra = qc_yyget_extra (scanner); directive_t *directive = Hash_Find (cond_directive_tab, token); if (!directive && !extra->suppressed && extra->parser->directive) { directive = extra->parser->directive (token); if (!directive) { error (0, "invalid directive `%s`", token); } } if (!directive) { yy_pop_state (scanner);// suppressed non-conditional directive yy_push_state (LCOMMENT, scanner); extra->preprocessor = false; return -1; } //auto lex = qc_yyget_extra (scanner); //lex->current_lang = &lex->pre_lang; return directive->value; } enum { suff_error = -1, suff_implicit, suff_unsigned, suff_long, suff_unsigned_long, suff_float, suff_double, suff_long_double, }; static int parse_suffix (const char *suffix, bool fp) { int expl = suff_implicit; bool unsign = false; if (!*suffix) { return 0; } if (fp) { if (*suffix == 'f' || *suffix == 'F') { expl = suff_float; suffix++; } else if (*suffix == 'd' || *suffix == 'D') { // treat as explicit double unless it's a proper C decimal // suffix, in which case the decimal part will be ignored // (non-standard, but no decimal support) expl = suff_double; suffix++; if (*suffix == 'f' || *suffix == 'F' || *suffix == 'd' || *suffix == 'D' || *suffix == 'l' || *suffix == 'L') { warning (0, "decimal fp treated as binary fp"); expl = suff_double; suffix++; } } else if (*suffix == 'l' || *suffix == 'L') { expl = suff_long_double; suffix++; } } else { if (*suffix == 'f' || *suffix == 'F') { expl = suff_float; suffix++; } else if (*suffix == 'd' || *suffix == 'D') { expl = suff_double; suffix++; } else { if (*suffix == 'u' || *suffix == 'U') { unsign = true; expl = suff_unsigned; suffix++; } if (*suffix == 'l' || *suffix == 'L') { expl = unsign ? suff_unsigned_long : suff_long; suffix++; if (*suffix == 'l' || *suffix == 'L') { suffix++; } if (!unsign && (*suffix == 'u' || *suffix == 'U')) { expl = suff_unsigned_long; suffix++; } } } } if (*suffix) { return suff_error; } return expl; } static const expr_t * parse_number (const rua_tok_t *tok, yyscan_t scanner) { bool binary = false; const char *type = "integer"; bool hex = false; bool fp = false; char buf[tok->textlen + 1], *dst = buf; const char *src = tok->text; if ((*dst = *src++) == '0') { switch ((*++dst = *src++)) { case 'b': case 'B': binary = true; type = "binary"; break; case 'x': case 'X': hex = true; type = "hexadecimal"; break; case '.': fp = true; break; case '1' ... '9': type = "octal"; // unless fp becomes true break; case '\'': case '_': if (*src == 'b' || *src == 'B' || *src == 'x' || *src == 'X') { error (0, "digit separator outside digit sequence"); return 0; } break; } } if (*dst) { if (*dst == '.') { dst++; fp = true; } else { dst += (*dst != '\'' && *dst != '_'); } while ((*dst = *src++)) { if (hex && (*dst == 'p' || *dst == 'P')) { fp = true; } if (!hex && (*dst == 'e' || *dst == 'E')) { fp = true; } if (*dst == '.') { fp = true; } // strip out digit separators (' is standard C, _ is a rust // thing, but it does look a bit nicerer than ', so why not). dst += (*dst != '\'' && *dst != '_'); } } // use long long to avoid bit-size issues on windows long long lvalue = 0; double fvalue = 0; char *endptr = 0; if (binary) { // to get here, 0b (or 0B) was seen, so buf is guaranted to start with // that lvalue = strtoll (buf + 2, &endptr, 2); } else { if (fp) { fvalue = strtod (buf, &endptr); } else { lvalue = strtoll (buf, &endptr, 0); } } int expl = parse_suffix (endptr, fp); if (expl < 0) { error (0, "invalid suffix \"%s\" on %s constant", endptr, fp ? "floating" : type); return 0; } if (fp) { if (expl == suff_float) { return new_float_expr (fvalue); } else { if (expl == suff_long_double) { warning (0, "long double treated as double"); expl = suff_double; } return new_double_expr (fvalue, expl == suff_implicit); } } else { if (expl == suff_unsigned) { return new_uint_expr (lvalue); } else if (expl == suff_long || expl == suff_implicit) { return new_long_expr (lvalue, expl == suff_implicit); } else if (expl == suff_unsigned_long) { return new_ulong_expr (lvalue); } else if (expl == suff_float) { return new_float_expr (lvalue); } else if (expl == suff_double) { return new_double_expr (lvalue, false); } else { internal_error (0, "invalid suffix enum: %d", expl); } } } typedef struct { yyscan_t scanner; yybuffer buffer; } buffer_raii_t; static void qc_restore_buffer (buffer_raii_t *raii) { yy_switch_to_buffer (raii->buffer, raii->scanner); } static void qc_delete_buffer (buffer_raii_t *raii) { yy_delete_buffer (raii->buffer, raii->scanner); } static const expr_t * parse_vector (const rua_tok_t *tok, yyscan_t scanner) { const char *end = tok->text + tok->textlen; while (end > tok->text && *--end != '\'') continue; const char *start = tok->text + 1; auto yyg = (struct yyguts_t *)scanner; auto __attribute__((cleanup (qc_restore_buffer))) saved_buffer = (buffer_raii_t) { .scanner = scanner, .buffer = YY_CURRENT_BUFFER, }; auto __attribute__((cleanup (qc_delete_buffer))) buffer = (buffer_raii_t) { .scanner = scanner, .buffer = yy_scan_bytes (start, end - start, scanner), }; int token; rua_tok_t vtok = { .location = tok->location, }; vtok.location.column++; const expr_t *components[4+1] = {}; // currently, max of 4 bool negate[4] = {}; int width = 0; yy_push_state (VECTOR, scanner); do { token = yylex (&vtok, &vtok.location, scanner); if (-token == rua_number) { auto expr = parse_number (&vtok, scanner); if (expr) { if (width < 4) { components[width] = expr; } width++; } } else if (token == '-') { if (width < 4) { negate[width] = true; } } } while (token && token != -rua_eof); yy_pop_state (scanner); if (width > 4) { error (0, "too many components in vector literal"); width = 4; return 0; } bool fp = false; for (int i = 0; i < width; i++) { if (!components[i]->implicit) { error (0, "explict numeric constant in vector literal." " Suggest suffix after closing '."); return 0; } fp |= is_double (get_type (components[i])); } // end points at the final ' and thus any suffix is after that int expl = parse_suffix (++end, fp); if (expl < 0) { error (0, "invalid suffix \"%s\" on %s vector constant", end, fp ? "floating" : "integer"); return 0; } union { pr_float_t f[4]; pr_int_t i[4]; pr_double_t d[4]; pr_long_t l[4]; pr_type_t t[PR_SIZEOF (lvec4)]; } data; const type_t *type = nullptr; if (expl == suff_long_double) { warning (0, "long double treated as double"); expl = suff_double; } if (expl == suff_float) { for (int i = 0; i < width; i++) { auto c = components[i]; if (is_double (get_type (c))) { data.f[i] = expr_double (c); } else { data.f[i] = expr_long (c); } if (negate[i]) { data.f[i] = -data.f[i]; } } type = &type_float; } else if (expl == suff_double) { for (int i = 0; i < width; i++) { auto c = components[i]; if (is_double (get_type (c))) { data.d[i] = expr_double (c); } else { data.d[i] = expr_long (c); } if (negate[i]) { data.d[i] = -data.d[i]; } } type = &type_double; } else if (expl == suff_implicit) { if (fp) { for (int i = 0; i < width; i++) { auto c = components[i]; if (is_double (get_type (c))) { data.f[i] = expr_double (c); } else { data.f[i] = expr_long (c); } if (negate[i]) { data.f[i] = -data.f[i]; } } type = &type_float; } else { for (int i = 0; i < width; i++) { auto c = components[i]; data.i[i] = expr_long (c); if (negate[i]) { data.i[i] = -data.i[i]; } } type = &type_int; } } else if (expl == suff_unsigned) { for (int i = 0; i < width; i++) { auto c = components[i]; data.i[i] = fp ? expr_double (c) : expr_long (c); if (negate[i]) { data.i[i] = -data.i[i]; } } type = &type_uint; } else if (expl == suff_long || expl == suff_unsigned_long) { for (int i = 0; i < width; i++) { auto c = components[i]; data.l[i] = expr_long (c); if (negate[i]) { data.l[i] = -data.l[i]; } } type = expl == suff_unsigned_long ? &type_ulong : &type_long; } type = vector_type (type, width); return new_value_expr (new_type_value (type, data.t), expl == suff_implicit); } static int parse_string (rua_val_t *lval, const rua_tok_t *tok, int type, yyscan_t scanner) { const char *str = make_string (tok->text, 0); if (type == rua_char) { if (str[1]) { warning (0, "multibyte char constant"); } lval->expr = new_int_expr (*str, false); return QC_VALUE; } else { lval->expr = new_string_expr (str); return QC_STRING; } } static void next_line (rua_loc_t *loc, yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); loc->line = loc->last_line; loc->column = loc->last_column; loc->last_column = 1; loc->last_line++; if (!extra->recording && options.preprocess_output) { puts (""); } } static void update_loc (rua_loc_t *loc, size_t textlen) { loc->line = loc->last_line; loc->column = loc->last_column; // \n handling rules will take care of the column and line loc->last_column += textlen; } static void save_text (rua_tok_t *tok, const char *text, size_t textlen, int state, rua_extra_t *extra) { /*if (state == TEXT) { while (isspace (*text)) { text++; } } if (state == COMMENT || state == LCOMMENT || isspace (*text)) { tok->str_text[0] = ' '; tok->str_text[1] = 0; tok->text = tok->str_text; textlen = 1; } else */if (extra && textlen < sizeof (extra->str_text)) { strncpy (extra->str_text, text, textlen); extra->str_text[textlen] = 0; tok->text = extra->str_text; } else { tok->text = save_string (text); } tok->textlen = textlen; } static bool join_tokens (rua_tok_t *out, const rua_tok_t *p1, const rua_tok_t *p2, rua_extra_t *extra) { if (!p1 || p1->token == -rua_space || p1->token == -rua_ignore) { if (!p2 || p2->token == -rua_space || p2->token == -rua_ignore) { *out = (rua_tok_t) { .token = -rua_ignore, .text = "", }; } else { *out = *p2; } return true; } if (!p2 || p2->token == -rua_space || p1->token == -rua_ignore) { // p1 cannot be null, space or ignore here *out = *p1; return true; } auto str = va (0, "%s%s", p1->text, p2->text); yy_scan_string (str, extra->subscanner); rua_tok_t tok = { .text = str, }; int token = yylex (&tok, &tok.location, extra->subscanner); if (token && yylex (&tok, &tok.location, extra->subscanner)) { error (0, "pasting \"%s\" and \"%s\" does not give a valid" " preprocessing token", p1->text, p2->text); out->token = -rua_space; out->text = " "; return false; } tok.token = token; *out = tok; return true; } static bool stringize_arg (rua_tok_t *out, rua_macro_t *arg, rua_extra_t *extra) { dstring_copystr (extra->dstr, "\""); for (auto e = arg->tokens; e; e = e->next) { auto str = quote_string (e->text); dstring_appendstr (extra->dstr, str); } dstring_appendstr (extra->dstr, "\""); *out = (rua_tok_t) { .textlen = extra->dstr->size - 1, .token = -rua_string, .text = save_string (extra->dstr->str), }; return true; } static int preproc_token (rua_extra_t *extra, PRE_YYSTYPE *lval, const rua_tok_t *tok, yyscan_t scanner) { int token = tok->token; if (!token) { return token; } if (token < 0) { rua_term term = -token; switch (term) { case rua_ignore: case rua_num_term: case rua_grab: case rua_va_opt: internal_error (0, "unexpected rua token: %d", term); case rua_eof: case rua_error: case rua_id: case rua_ellipsis: case rua_asx: case rua_incop: break; case rua_number: if (!extra->recording) { token = -rua_error; if ((lval->expr = parse_number (tok, scanner))) { token = PRE_VALUE; } } break; case rua_vector: if (!extra->recording) { token = -rua_error; if (!(lval->expr = parse_vector (tok, scanner))) { token = PRE_VALUE; } } break; case rua_string: case rua_char: break; case rua_space: if (!extra->recording || extra->params) { token = -rua_ignore; } break; } } if (-token != rua_ignore && extra->recording) { if (token == PRE_ID) { token = -rua_id; } lval->t.token = token; if (token != PRE_EOD && token != ',' && token != '(' && token != ')') { token = PRE_TOKEN; } } return token; } static const expr_t * convert_long (const expr_t *value) { pr_long_t v = expr_long (value); if (is_int (type_default)) { if (v < INT32_MIN || v > UINT32_MAX) { warning (0, "integer value truncated"); } return new_int_expr (v, true); } else { float f = v; if (f != v) { warning (0, "cannot represent value"); } return new_float_expr (f); } } static int qc_token (rua_extra_t *extra, rua_val_t *lval, const rua_tok_t *tok, yyscan_t scanner) { int token = tok->token; if (token < 0) { rua_term term = -token; switch (term) { case rua_ignore: case rua_num_term: case rua_va_opt: internal_error (0, "unexpected rua token: %d", term); case rua_eof: case rua_error: break; case rua_id: token = extra->parser->keyword_or_id (lval, tok->text); break; case rua_grab: { int ret = do_grab (tok->text); if (ret >= 0) { lval->expr = new_int_expr (ret, false); token = QC_VALUE; } else { yy_push_state (-ret, scanner); } } break; case rua_number: token = -rua_error; lval->expr = parse_number (tok, scanner); if (lval->expr) { token = QC_VALUE; if (lval->expr->implicit) { if (is_long (get_type (lval->expr))) { lval->expr = convert_long (lval->expr); } } } break; case rua_vector: token = -rua_error; if ((lval->expr = parse_vector (tok, scanner))) { token = QC_VALUE; } break; case rua_string: case rua_char: token = parse_string (lval, tok, -token, scanner); break; case rua_space: if (options.preprocess_output) { printf (" "); } break; case rua_ellipsis: token = QC_ELLIPSIS; break; case rua_asx: token = QC_ASX; lval->op = tok->text[0]; switch (tok->text[1]) { case '=': break; case '%': lval->op = QC_MOD; break; case '<': lval->op = QC_SHL; break; case '>': lval->op = QC_SHR; break; } break; case rua_incop: token = QC_INCOP; lval->op = tok->text[0]; break; } } return token; } static void rua_reset_args (rua_macro_t *macro) { macro->num_args = 0; int num_params = macro->num_params; if (num_params < 0) { num_params = -num_params; } if (num_params && !macro->args) { macro->args = calloc (num_params, sizeof (rua_macro_t *)); for (int i = 0; i < num_params; i++) { macro->args[i] = malloc (sizeof (rua_macro_t)); } } for (int i = 0; i < num_params; i++) { auto arg = macro->args[i]; *arg = (rua_macro_t) { .tail = &arg->tokens, }; } } static void rua_start_args (yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); extra->recording = true; rua_reset_args (extra->pending_macro); yy_push_state (ARGS, scanner); } static void rua_end_args (yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); extra->recording = false; yy_pop_state (scanner); auto macro = extra->pending_macro; if (macro->num_params < 0) { auto arg = macro->args[~macro->num_params]; auto t = arg->tokens; while (t && (t->token == -rua_space || t->token == -rua_ignore)) { t = t->next; } if (!t) { arg->tokens = 0; arg->tail = &arg->tokens; } } } static void __attribute__((used)) dump_macro (rua_macro_t *macro) { printf ("\n%s: ", macro->name); for (auto t = macro->tokens; t; t = t->next) { printf ("%s", t->text); } if (macro->args) { for (int i = 0; i < macro->num_args; i++) { dump_macro (macro->args[i]); } } } static int qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t scanner) { auto loc = &tok->location; if (-token == rua_ignore) { return YYPUSH_MORE; } pr.loc = *loc; if (extra->preprocessor) { auto state = extra->pre_state; PRE_YYSTYPE lval = { .t = *tok }; token = preproc_token (extra, &lval, tok, scanner); if (-token == rua_ignore) { return YYPUSH_MORE; } return pre_yypush_parse (state, token, &lval, loc, scanner); } else { if (!extra->suppressed) { if (options.preprocess_only) { if (options.preprocess_output) { if (token != -rua_space || !extra->did_space) { printf ("%s", tok->text); } extra->did_space = token == -rua_space; } return token ? YYPUSH_MORE : 0; } QC_YYSTYPE lval = {}; token = qc_token (extra, &lval, tok, scanner); if (token >= 0) { auto p = extra->parser; return p->parse (p->state, token, &lval, loc, scanner); } } } return YYPUSH_MORE; } static rua_tok_t next_macro_token (rua_macro_t *macro) { auto e = *macro->cursor; macro->cursor = e.next; return e; } static const rua_tok_t * get_arg_token (bool last, const rua_tok_t *arg, const rua_macro_t *macro, yyscan_t scanner) { symbol_t *sym; if (arg->token == -rua_va_opt) { sym = symtab_lookup (macro->params, arg->text); auto m = sym->macro; m->update (m, scanner); if (last) { if (m->tokens) { return (rua_tok_t *) m->tail; } return 0; } else { return m->tokens; } } if (arg->token == -rua_id && macro->params && (sym = symtab_lookup (macro->params, arg->text)) && !macro->args[sym->offset]->next) { auto a = macro->args[sym->offset]; if (last) { if (a->tokens) { return (rua_tok_t *) a->tail; } return 0; } else { return a->tokens; } } return arg; } static void copy_token (rua_macro_t *dst, const rua_tok_t *t) { rua_tok_t *e = malloc (sizeof (*e)); *e = *t; e->next = 0; *dst->tail = e; dst->tail = &e->next; } static void do_arg_copy (rua_macro_t *dst, const rua_macro_t *a, bool skip_first, bool skip_last) { if (!a->tokens) { return; } for (auto t = skip_first ? a->tokens->next : a->tokens; t; t = t->next) { if (skip_last && !t->next) { break; } copy_token (dst, t); } } static void copy_arg_token (rua_macro_t *dst, bool skip_first, bool skip_last, const rua_tok_t *arg, const rua_macro_t *macro) { symbol_t *sym; if (arg->token == -rua_va_opt) { sym = symtab_lookup (macro->params, arg->text); // __VA_OPT__'s macro was updated by get_arg_token do_arg_copy (dst, sym->macro, skip_first, skip_last); } else if (arg->token == -rua_id && macro->params && (sym = symtab_lookup (macro->params, arg->text)) && !macro->args[sym->offset]->next) { do_arg_copy (dst, macro->args[sym->offset], skip_first, skip_last); } else if (!skip_first && !skip_last) { copy_token (dst, arg); } } static void collect_args (rua_macro_t *m, rua_macro_t *macro, yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); m->next = extra->pending_macro; extra->pending_macro = m; auto t = macro->cursor; for (; t && t->token == -rua_space; t = t->next) continue; // to be in an arg, a function-like macro must either have // both open and close parans, or none if (t && t->token == '(') { rua_reset_args (m); int paren = 1; auto a = rua_macro_arg (t, scanner); for (t = t->next; t; t = t->next) { if (t->token == '(') { paren++; } else if (t->token == ')') { if (!(--paren)) { t = t->next; break; } } if (t->token == ',' && paren == 1) { a = rua_macro_arg (t, scanner); } else { rua_macro_append (a, t, scanner); } } macro->cursor = t; } extra->pending_macro = m->next; } static rua_macro_t * alloc_macro (const char *name, bool params) { rua_macro_t *macro = malloc (sizeof (*macro)); *macro = (rua_macro_t) { .name = name, .params = params ? new_symtab (0, stab_param) : 0, .tail = ¯o->tokens, }; return macro; } static void append_token (rua_macro_t *macro, const rua_tok_t *token) { if (token->token == -rua_space) { if (!macro->tokens) { // ignore leading space return; } auto t = (rua_tok_t *) macro->tail; if (t->token == '#' || t->token == PRE_CONCAT) { // ignore space after '#' and '##' return; } if (t->token == -rua_space) { // ignore space after space return; } } auto t = (rua_tok_t *) macro->tail; if (token->token != PRE_CONCAT || !macro->tokens || t->token != -rua_space) { t = malloc (sizeof (*t)); macro->num_tokens++; } *t = *token; t->next = 0; *macro->tail = t; macro->tail = &t->next; } static void queue_macro (rua_extra_t *extra, rua_macro_t *macro) { macro->next = extra->macro; extra->macro = macro; macro->cursor = macro->tokens; } static bool check_macro (rua_macro_t *macro) { int num_args = macro->num_args; if (num_args == 1 && !macro->args) { num_args = 0; } if (macro->num_params >= 0) { if (num_args != macro->num_params) { error (0, "macro \"%s\" passed %d arguments, but takes just %d", macro->name, num_args, macro->num_params); return false; } } else { if (num_args < ~macro->num_params) { error (0, "macro \"%s\" requires %d arguments, but only %d given", macro->name, ~macro->num_params, num_args); return false; } } return true; } static int next_token (rua_tok_t *tok, yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); rescan: while (extra->macro && !extra->macro->cursor) { auto macro = extra->macro; extra->macro = macro->next; macro->next = 0; } rua_tok_t e = {}; if (extra->macro) { e = next_macro_token (extra->macro); } else if (!extra->no_lex) { tok->token = yylex (tok, &extra->location, scanner); tok->location = extra->location; if ((extra->pending_macro || extra->recording) && tok->text == extra->str_text) { tok->text = save_string (tok->text); } e = *tok; } int token = e.token; if (extra->pending_macro && !extra->recording) { if (token != -rua_space && token != '(') { auto pending_macro = extra->pending_macro; append_token (pending_macro, &e); extra->pending_macro = pending_macro->next; if (extra->macro) { auto macro = extra->macro; pending_macro->params = macro->params; pending_macro->args = macro->args; } queue_macro (extra, pending_macro); // get the token that looked like a macro e = next_macro_token (extra->macro); *tok = e; return e.token; } } if (extra->macro) { symbol_t *sym; auto macro = extra->macro; if (token == '#') { auto n = e.next; rua_macro_t *arg; macro->cursor = macro->cursor->next; // consume arg if (n->token == -rua_va_opt) { sym = symtab_lookup (macro->params, n->text); auto m = sym->macro; m->update (m, scanner); arg = m; } else { sym = symtab_lookup (macro->params, n->text); arg = macro->args[sym->offset]; } stringize_arg (&e, arg, extra); } else if (e.next && e.next->token == PRE_CONCAT) { rua_macro_t *m = malloc (sizeof (*m)); *m = (rua_macro_t) { .tail = &m->tokens, }; bool sf = false; bool sl; do { auto p = get_arg_token (true, &e, macro, scanner); auto n = get_arg_token (false, e.next->next, macro, scanner); rua_tok_t cat; if (!(sl = join_tokens (&cat, p, n, extra))) { cat.location = e.next->location; cat.token = -rua_space; cat.text = " "; } copy_arg_token (m, sf, sl, &e, macro); if (cat.token) { copy_token (m, &cat); } next_macro_token (extra->macro); // consume ## e = next_macro_token (extra->macro); sf = sl; } while (e.next && e.next->token == PRE_CONCAT); copy_arg_token (m, sl, false, &e, macro); m->cursor = m->tokens; m->next = extra->macro; extra->macro = m; goto rescan; } else if (token == -rua_va_opt) { if (!macro->params || !macro->args[~macro->num_params] || !macro->args[~macro->num_params]->tokens) { goto rescan; } sym = symtab_lookup (macro->params, e.text); auto m = sym->macro; m->update (m, scanner); queue_macro (extra, m); goto rescan; } else if (token == -rua_id && macro->params && (sym = symtab_lookup (macro->params, e.text)) && !macro->args[sym->offset]->next) { auto arg = macro->args[sym->offset]; queue_macro (extra, arg); goto rescan; } else if (token == -rua_id && !macro->name // in an arg && (sym = symtab_lookup (extra->macro_tab, e.text)) && !sym->macro->next && !sym->macro->params) { // force object-type macros in macro arguments to be expanded auto m = sym->macro; if (m->update) { m->update (m, scanner); } queue_macro (extra, m); goto rescan; #if 0 // causes preproc-1.r to fail (see XXX below) } else if (token == -rua_id && !macro->name // in an arg && (sym = symtab_lookup (extra->macro_tab, e.text)) && !sym->macro->next && sym->macro->params) { // force function-type macros in macro arguments to be expanded auto m = sym->macro; auto c = macro->cursor; // XXX breakage for preproc-1.r: collect_args assumes the macro // is complete, but in the case of preproc-1.r, it is not. In fact, // with the final ) removed from the F macro, tokens are consumed // to the end of the file. Thus, nt x = F(LPAREN(), 0, <:-); // expands to int x = 42 ); instead of int x = 42; // However, this block is needed for the H5C(H5A()) test collect_args (m, macro, scanner); if (macro->cursor != c) { if (m->update) { m->update (m, scanner); } queue_macro (extra, m); goto rescan; } #endif } *tok = e; } if (extra->pending_macro) { PRE_YYSTYPE lval = { .t = e }; auto pending_macro = extra->pending_macro; if (extra->recording) { token = preproc_token (extra, &lval, &e, scanner); int s = pre_yypush_parse (extra->args_state, token, &lval, &e.location, scanner); if (s == YYPUSH_MORE) { goto rescan; } rua_end_args (scanner); extra->pending_macro = pending_macro->next; if (check_macro (pending_macro)) { queue_macro (extra, pending_macro); } goto rescan; } else if (token == -rua_space) { append_token (pending_macro, &e); goto rescan; } else if (token == '(') { extra->pending_macro = (rua_macro_t *) pending_macro->args; pending_macro->args = 0; rua_start_args (scanner); int s = pre_yypush_parse (extra->args_state, PRE_ARGS, &lval, &e.location, scanner); if (s != YYPUSH_MORE) { internal_error (0, "can't start parsing macro args"); } goto rescan; } } symbol_t *sym; if (extra->preprocessor && extra->expand && token == PRE_ID) { token = -rua_id; } if ((extra->expand || !(extra->preprocessor || extra->suppressed)) && token == -rua_id && (sym = symtab_lookup (extra->macro_tab, e.text)) && !sym->macro->next) { auto macro = sym->macro; if (macro->update) { macro->update (macro, scanner); } if (macro->params) { // function-type macro, need to check for ( so set up a temporary // macro to hold the tokens before the first non-space token auto pending_macro = alloc_macro (0, false); pending_macro->args = (rua_macro_t **) macro; pending_macro->next = extra->pending_macro; extra->pending_macro = pending_macro; // record the token with the macro name if (e.text == extra->str_text) { e.text = save_string (e.text); } append_token (pending_macro, &e); } else { // object-type macro, just expand queue_macro (extra, macro); } goto rescan; } return token; } int rua_parse (FILE *in, rua_parser_t *parser) { rua_extra_t extra = { .parser = parser, .pre_state = pre_yypstate_new (), .args_state = pre_yypstate_new (), .cond_stack = DARRAY_STATIC_INIT (8), .include_stack = DARRAY_STATIC_INIT (8), .dstr = dstring_new (), .macro_tab = cpp_macros ? cpp_macros : new_symtab (0, stab_global), .location = { 1, 1, 1, 1, pr.loc.file }, }; yyscan_t scanner; yylex_init_extra (&extra, &scanner); yylex_init (&extra.subscanner); yyset_in (in, scanner); int status; rua_tok_t tok = {}; do { int token = next_token (&tok, scanner); if (!token && extra.cond_stack.size) { int ind = extra.cond_stack.size - 1; auto cond = extra.cond_stack.a[ind]; error (0, "end of file in conditional started on %d", cond.line); } if (!token && extra.include_stack.size) { //printf ("*** pop include %d\n", (int) extra.include_stack.size); auto incl = DARRAY_REMOVE (&extra.include_stack); extra.location = incl.location; struct yyguts_t *yyg = (struct yyguts_t*) scanner;//FIXME yy_delete_buffer (YY_CURRENT_BUFFER, scanner); set_line_file (-1, 0, 2); yy_switch_to_buffer (incl.buffer, scanner); DARRAY_CLEAR (&extra.cond_stack); extra.cond_stack = incl.cond_stack; continue; } status = qc_process (&extra, token, &tok, scanner); } while (status == YYPUSH_MORE); yylex_destroy (extra.subscanner); yylex_destroy (scanner); pre_yypstate_delete (extra.pre_state); pre_yypstate_delete (extra.args_state); free (extra.cond_stack.a); free (extra.include_stack.a); dstring_delete (extra.dstr); return status; } rua_macro_t * rua_start_macro (const char *name, bool params, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return 0; } extra->recording = true; extra->params = params; if (name) { int len = strlen (name); if (name[len - 1] == '(') { name = save_substring (name, len - 1); } else { name = save_string (name); } } yy_pop_state (scanner); yy_push_state (MACRO, scanner); return alloc_macro (name, params); } rua_macro_t * rua_end_params (rua_macro_t *macro, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return 0; } extra->params = false; //macro->params->parent = extra->macro_tab; return macro; } rua_macro_t * rua_macro_append (rua_macro_t *macro, const rua_tok_t *token, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed || !macro) { return 0; } append_token (macro, token); return macro; } rua_macro_t * rua_macro_param (rua_macro_t *macro, const rua_tok_t *token, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return 0; } if (token->token == -rua_space) { // ignore spaces return macro; } if (token->token != -rua_id && token->token != -rua_ellipsis) { error (0, "expected parameter name, found \"%s\"", token->text); return macro; } if (macro->num_params < 0) { error (0, "... must be the last parameter"); return macro; } else if (symtab_lookup (macro->params, token->text)) { error (0, "duplicate macro parameter \"%s\"", token->text); return macro; } auto sym = new_symbol (token->text); sym->sy_type = sy_var; sym->offset = macro->num_params++; symtab_addsymbol (macro->params, sym); if (token->token == -rua_ellipsis) { macro->num_params = -macro->num_params; } return macro; } static rua_tok_t build_va_opt (rua_macro_t *macro, rua_tok_t *t, rua_tok_t *u, int va_opt_ind) { const char *va_opt_name = va (0, "__va_opt__.%d", va_opt_ind); rua_tok_t va_opt_tok = { .next = u->next, .location = t->location, .textlen = strlen (va_opt_name), .token = -rua_va_opt, .text = save_string (va_opt_name), }; va_opt_tok.location.last_line = u->location.last_line; va_opt_tok.location.last_column = u->location.last_column; while (t->token != '(') { t = t->next; } auto e = u; for (u = t; u->next != e; u = u->next) continue; u->next = 0; auto va_opt = alloc_macro (0, false); *va_opt = (rua_macro_t) { .tail = &va_opt->tokens, .update = rua_macro_va_opt, .args = malloc (sizeof (rua_macro_t *)), }; va_opt->args[0] = alloc_macro (0, false); *va_opt->args[0] = (rua_macro_t) { .tokens = t->next, .tail = &u->next, }; auto sym = new_symbol (va_opt_name); sym->sy_type = sy_macro; sym->macro = va_opt; symtab_addsymbol (macro->params, sym); return va_opt_tok; } void rua_macro_finish (rua_macro_t *macro, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return; } if (macro->tokens) { while (((rua_tok_t *) macro->tail)->token == -rua_space) { // space is never added as the first token, so it's guaranteed // there is always at least two tokens rua_tok_t *t; for (t = macro->tokens; t->next->next; t = t->next) continue; free (t->next); t->next = 0; macro->tail = &t->next; macro->num_tokens--; } if (macro->tokens->token == PRE_CONCAT || ((rua_tok_t *) macro->tail)->token == PRE_CONCAT) { error (0, "'##' cannot appear at either end of a macro expansion"); return; } int va_opt_ind = -1; for (auto t = macro->tokens; t; t = t->next) { if (t->token == '#' && (!t->next || t->next->token != -rua_id || (!symtab_lookup (macro->params, t->next->text) && strcmp (t->next->text, "__VA_OPT__") != 0))) { error (0, "'#' is not followed by a macro parameter"); return; } if (t->token == -rua_id && strcmp (t->text, "__VA_OPT__") == 0) { auto u = t->next; for (; u && u->token == -rua_space; u = u->next) continue; if (!u) { unterminated_va_opt: error (0, "unterminated __VA_OPT__"); return; } else if (u->token != '(') { error (0, "__VA_OPT__ must be followed by an open " "parenthesis"); return; } if (u->next && u->next->token == PRE_CONCAT) { hashhash_error: error (0, "'##' cannot appear at either end of __VA_OPT__"); return; } int paren = 1; for (u = u->next; u; u = u->next) { if (u->token == '(') { paren++; } else if (u->token == ')') { if (!(--paren)) { *t = build_va_opt (macro, t, u, ++va_opt_ind); break; } } else if (u->token == -rua_id && strcmp (u->text, "__VA_OPT__") == 0) { error (0, "__VA_OPT__ may not appear in a __VA_OPT__"); return; } else if (u->token == PRE_CONCAT && paren == 1 && u->next && u->next->token == ')') { goto hashhash_error; } } if (!u) { goto unterminated_va_opt; } } } } auto macro_tab = extra->macro_tab; auto sym = symtab_lookup (macro_tab, macro->name); if (sym) { auto o = sym->macro->tokens; auto n = macro->tokens; while (o && n && o->text == n->text) { o = o->next; n = n->next; } if (o || n) { error (0, "\"%s\" redefined", macro->name); } return; } sym = new_symbol (macro->name); sym->sy_type = sy_macro; sym->macro = macro; symtab_addsymbol (macro_tab, sym); } rua_macro_t * rua_macro_arg (const rua_tok_t *token, void *scanner) { auto extra = qc_yyget_extra (scanner); auto macro = extra->pending_macro; rua_macro_t *arg; if (macro->num_params < 0 && macro->num_args == -macro->num_params) { arg = macro->args[macro->num_args - 1]; rua_macro_append (arg, token, scanner); } else { if (macro->num_args < macro->num_params || macro->num_args < -macro->num_params) { arg = macro->args[macro->num_args++]; } else { // count the excess args for error reporting macro->num_args++; arg = 0; } } return arg; } void rua_start_pragma (void *scanner) { if (options.preprocess_output) { printf ("#pragma"); } yy_pop_state (scanner); yy_push_state (PRAGMA, scanner); } void rua_start_text (void *scanner) { yy_pop_state (scanner); yy_push_state (TEXT, scanner); } void rua_start_include (void *scanner) { auto extra = qc_yyget_extra (scanner); extra->expand = true; yy_pop_state (scanner); yy_push_state (PREPROC, scanner); } void rua_start_expr (void *scanner) { auto extra = qc_yyget_extra (scanner); extra->expand = !extra->suppressed; yy_pop_state (scanner); yy_push_state (PREEXPR, scanner); } void rua_expand_on (void *scanner) { auto extra = qc_yyget_extra (scanner); extra->expand = !extra->suppressed; } void rua_expand_off (void *scanner) { auto extra = qc_yyget_extra (scanner); extra->expand = false; } void rua_end_directive (void *scanner) { auto extra = qc_yyget_extra (scanner); extra->preprocessor = false; extra->recording = false; extra->expand = false; yy_pop_state (scanner); } static void dump_debug (int act, const char *text, int start) { if (act == 0) { printf("--scanner backing up\n" ); } else if (act < YY_NUM_RULES) { printf("--accepting rule at line %ld\n", (long)yy_rule_linenum[act]); } else if (act == YY_NUM_RULES) { printf("--accepting default rule\n"); } else if (act == YY_NUM_RULES + 1 ) { printf("--(end of buffer or a NUL)\n"); } else { printf("--EOF (start condition %d)\n", start); } } static void dump_state_stack (yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); auto yyg = (struct yyguts_t *)scanner; for (int i = 0; i < yyg->yy_start_stack_ptr; i++) { printf ("%d ", yyg->yy_start_stack[i]); } printf (": %d s:%s p:%s r:%s e:%s a:%s %s:%d\n", yystart (), extra ? extra->suppressed ? "true" : "false" : "n/a", extra ? extra->preprocessor ? "true" : "false" : "n/a", extra ? extra->recording ? "true" : "false" : "n/a", extra ? extra->expand ? "true" : "false" : "n/a", extra ? extra->params ? "true" : "false" : "n/a", pr.strings ? GETSTR(pr.loc.file) : "", pr.loc.line); } static void dump_token (rua_tok_t *tok, rua_loc_t *loc, int state) { printf ("start: %2d [%3d %3d] [%3d %3d] %d '%s'\n", state, loc->line, loc->column, loc->last_line, loc->last_column, tok->token, quote_string (tok->text)); } void rua_start_if (bool expand, void *scanner) { auto extra = qc_yyget_extra (scanner); rua_cond_t cond = { .saw_true = false, .saw_else = false, .own_state = true, .enabled = false, .line = pr.loc.line, }; if (extra->cond_stack.size) { auto c = extra->cond_stack.a[extra->cond_stack.size - 1]; cond.own_state = c.own_state & c.enabled; } DARRAY_APPEND (&extra->cond_stack, cond); extra->expand = expand; yy_pop_state (scanner); yy_push_state (PREEXPR, scanner); } void rua_start_else (bool expand, void *scanner) { auto extra = qc_yyget_extra (scanner); extra->expand = expand; yy_pop_state (scanner); yy_push_state (PREEXPR, scanner); } void rua_if (bool pass, void *scanner) { auto extra = qc_yyget_extra (scanner); if (!extra->cond_stack.size) { internal_error (0, "#if without start_if"); return; } auto cond = &extra->cond_stack.a[extra->cond_stack.size - 1]; cond->saw_true = cond->enabled = pass; //yy_pop_state (scanner); // remove DIRECTIVE/PREEXPR state if (cond->own_state && !cond->enabled) { extra->suppressed = true; } //printf ("#if on %s:%d %d\n", GETSTR(pr.loc.file), // cond->line, extra->suppressed); // put PREEXPR on the stack for EOD to pop //yy_push_state (PREEXPR, scanner); } void rua_else (bool pass, const char *tok, void *scanner) { auto extra = qc_yyget_extra (scanner); if (!extra->cond_stack.size) { error (0, "#else without #if"); return; } //yy_pop_state (scanner); // remove DIRECTIVE state auto cond = &extra->cond_stack.a[extra->cond_stack.size - 1]; if (cond->saw_else) { error (0, "#%s after #else", tok); return; } if (cond->own_state && !cond->enabled) { extra->suppressed = false; //yy_pop_state (scanner); } pass &= !cond->saw_true; cond->enabled = pass; cond->saw_true |= pass; cond->saw_else = strcmp (tok, "else") == 0; //printf ("#else on %s:%d for %d %d\n", GETSTR(pr.loc.file), // pr.loc.line, cond->line, extra->suppressed); cond->line = pr.loc.line; if (cond->own_state && !cond->enabled) { extra->suppressed = true; } // put PREEXPR on the stack for EOD to pop //yy_push_state (PREEXPR, scanner); } void rua_endif (void *scanner) { auto extra = qc_yyget_extra (scanner); if (!extra->cond_stack.size) { error (0, "#endif without #if"); return; } //yy_pop_state (scanner); // remove DIRECTIVE state auto cond = DARRAY_REMOVE (&extra->cond_stack); //printf ("#endif on %s:%d for %d %d\n", GETSTR(pr.loc.file), // pr.loc.line, cond.line, extra->suppressed); if (cond.own_state && !cond.enabled) { extra->suppressed = false; //yy_pop_state (scanner); } // put PREEXPR on the stack for EOD to pop //yy_push_state (PREEXPR, scanner); } bool rua_defined (const char *name, void *scanner) { auto extra = qc_yyget_extra (scanner); auto macro_tab = extra->macro_tab; return symtab_lookup (macro_tab, name); } void rua_undefine (const char *name, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return; } auto macro_tab = extra->macro_tab; auto sym = symtab_lookup (macro_tab, name); if (sym) { symtab_removesymbol (macro_tab, sym); } } void rua_include_file (const char *name, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return; } struct yyguts_t * yyg = (struct yyguts_t*)scanner;//FIXME int quote = *name; name = make_string (name, 0); bool is_system; auto found = cpp_find_file (name, quote, &is_system); if (!found || !(yyin = fopen (found, "rb"))) { error (0, "fatal error: %s: %s", name, strerror (errno)); exit (1); } //printf ("*** push include %s\n", name); set_line_file (1, found, 1); DARRAY_APPEND (&extra->include_stack, ((rua_incl_t) { .buffer = YY_CURRENT_BUFFER, .cond_stack = extra->cond_stack, .location = extra->location, })); extra->cond_stack = (rua_cond_stack_t) DARRAY_STATIC_INIT (8); auto buffer = yy_create_buffer (yyin, YY_BUF_SIZE, scanner); yy_switch_to_buffer (buffer, scanner); extra->location = (rua_loc_t) { .line = 1, .column = 1, .last_line = 1, .last_column = 1, .file = ReuseString (found), }; } void rua_embed_file (const char *name, void *scanner) { auto extra = qc_yyget_extra (scanner); if (extra->suppressed) { return; } internal_error (0, "not implemented"); (void)yy_top_state; } int rua_parse_define (const char *def) { int status; yyscan_t scanner; rua_tok_t tok = { .location = { 1, 1, 1, 1 }, .token = -1, }; rua_extra_t extra = { .preprocessor = true, .pre_state = pre_yypstate_new (), .macro_tab = cpp_macros, }; yylex_init_extra (&extra, &scanner); yy_scan_string (def, scanner); yy_push_state (PREPROC, scanner); do { if (tok.token == -1) { tok.token = PRE_DEFINE; } else { tok.token = yylex (&tok, &tok.location, scanner); if (tok.text == extra.str_text) { tok.text = save_string (tok.text); } status = 0; } while (tok.token) { status = qc_process (&extra, tok.token, &tok, scanner); if (status != YYPUSH_MORE || !extra.macro) { break; } tok = *extra.macro->cursor; extra.macro->cursor = extra.macro->cursor->next; if (!extra.macro->cursor) { extra.macro = extra.macro->next; } } } while (status == YYPUSH_MORE); yylex_destroy (scanner); pre_yypstate_delete (extra.pre_state); dstring_delete (extra.dstr); return status; } void rua_line_info (const expr_t *line_expr, const char *file, const expr_t *flags_expr, void *scanner) { auto extra = qc_yyget_extra (scanner); int line = expr_long (line_expr); int flags = flags_expr ? expr_long (flags_expr) : 0; if (file) { if (*file != '"') { error (0, "\"%s\" is not a valid filename", file); return; } file = make_string (file, 0); } set_line_file (line, file, flags); extra->location = pr.loc; } void rua_macro_file (rua_macro_t *macro, void *scanner) { auto extra = qc_yyget_extra (scanner); int file = extra->location.file; if (macro->tokens && macro->tokens->location.file == file) { return; } macro->tokens = 0; macro->tail = ¯o->tokens; macro->num_tokens = 0; rua_tok_t *expr = malloc (sizeof (*expr)); macro->num_tokens++; const char *file_str = save_string (va (0, "\"%s\"", quote_string (GETSTR (file)))); *expr = (rua_tok_t) { .location = extra->location, .textlen = strlen (file_str), .token = -rua_string, .text = file_str, }; *macro->tail = expr; macro->tail = &expr->next; } void rua_macro_line (rua_macro_t *macro, void *scanner) { auto extra = qc_yyget_extra (scanner); int line = extra->location.line; if (macro->tokens && macro->tokens->location.line == line) { return; } macro->tokens = 0; macro->tail = ¯o->tokens; macro->num_tokens = 0; rua_tok_t *expr = malloc (sizeof (*expr)); macro->num_tokens++; const char *line_str = save_string (va (0, "%d", line)); *expr = (rua_tok_t) { .location = extra->location, .textlen = strlen (line_str), .token = -rua_number, .text = line_str, }; *macro->tail = expr; macro->tail = &expr->next; } static void expand_arg (rua_macro_t *macro, rua_macro_t *arg, yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); rua_extra_t arg_extra = *extra; arg_extra.macro = 0; arg_extra.no_lex = true; queue_macro (&arg_extra, arg); qc_yyset_extra (&arg_extra, scanner); while (true) { rua_tok_t e; if (next_token (&e, scanner)) { if (macro->tokens || (e.token != -rua_space && e.token != -rua_ignore)) { append_token (macro, &e); } continue; } break; } qc_yyset_extra (extra, scanner); } static bool macro_empty (rua_macro_t *macro, yyscan_t scanner) { auto extra = qc_yyget_extra (scanner); for (auto t = macro->tokens; t; t = t->next) { if (t->token != -rua_id) { return false; } symbol_t *sym; if (macro->params && (sym = symtab_lookup (macro->params, t->text)) && !macro->args[sym->offset]->next) { auto arg = macro->args[sym->offset]; arg->next = macro; bool empty = macro_empty (arg, scanner); arg->next = 0; if (!empty) { return false; } } if ((sym = symtab_lookup (extra->macro_tab, t->text)) && !sym->macro->next) { auto m = sym->macro; if (m->params) { macro->cursor = t; collect_args (m, macro, scanner); if (t == macro->cursor) { return false; } } m->next = macro; bool empty = macro_empty (m, scanner); m->next = 0; if (!empty) { return false; } } } return true; } void rua_macro_va_opt (rua_macro_t *macro, void *scanner) { macro->tokens = 0; macro->tail = ¯o->tokens; auto extra = qc_yyget_extra (scanner); auto cur = extra->macro; if (!cur || !cur->params || cur->num_params >= 0) { warning (0, "__VA_OPT__ can appear only in the expansion of a " "variadic macro"); return; } if (macro_empty (cur->args[~cur->num_params], scanner)) { return; } auto arg = macro->args[0]; arg->params = cur->params; arg->num_params = cur->num_params; arg->num_args = cur->num_args; arg->args = cur->args; expand_arg (macro, arg, scanner); } void rua_macro_va_args (rua_macro_t *macro, void *scanner) { macro->tokens = 0; macro->tail = ¯o->tokens; auto extra = qc_yyget_extra (scanner); auto cur = extra->macro; if (!cur || !cur->params || cur->num_params >= 0) { warning (0, "__VA_ARGS__ can appear only in the expansion of a " "variadic macro"); return; } auto arg = cur->args[~cur->num_params]; if (arg->tokens) { macro->tokens = arg->tokens; macro->tail = arg->tail; } }