From 5794c68bdacaaf5cb040a52509946a13db5b7078 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 22 Oct 2023 20:56:12 +0900 Subject: [PATCH] [qfcc] Implement basic conditional compilation Just #if, #else and #endif for now. However, much of the preprocessor parsing working minus the semantics (expressions do work, though). --- tools/qfcc/include/qfcc.h | 1 + tools/qfcc/include/rua-lang.h | 19 ++ tools/qfcc/source/Makemodule.am | 3 + tools/qfcc/source/options.c | 1 + tools/qfcc/source/pre-parse.y | 279 +++++++++++++++++++++++ tools/qfcc/source/qc-lex.l | 389 ++++++++++++++++++++++++++++---- tools/qfcc/source/qc-parse.y | 4 +- 7 files changed, 652 insertions(+), 44 deletions(-) create mode 100644 tools/qfcc/source/pre-parse.y diff --git a/tools/qfcc/include/qfcc.h b/tools/qfcc/include/qfcc.h index 6d36c1ca9..ec799e9a2 100644 --- a/tools/qfcc/include/qfcc.h +++ b/tools/qfcc/include/qfcc.h @@ -119,6 +119,7 @@ const char *file_basename (const char *filename, int keepdot) __attribute__((pur int qc_yyparse (FILE *in); int qp_yyparse (FILE *in); +extern int pre_yydebug; extern int qc_yydebug; extern int qp_yydebug; diff --git a/tools/qfcc/include/rua-lang.h b/tools/qfcc/include/rua-lang.h index 9062c0ff7..a50bf28f4 100644 --- a/tools/qfcc/include/rua-lang.h +++ b/tools/qfcc/include/rua-lang.h @@ -36,6 +36,9 @@ typedef struct rua_loc_s { int file; } rua_loc_t; +typedef struct rua_macro_s { +} rua_macro_t; + typedef struct rua_tok_s { rua_loc_t location; int textlen; @@ -44,9 +47,25 @@ typedef struct rua_tok_s { struct { void *pointer; // mirrors pointer in QC_YYSTYPE char str_text[8];// if len < 8 and spec not used + int token; // when recording macros }; QC_YYSTYPE value; + struct dstring_s *dstr; + rua_macro_t *macro; }; } rua_tok_t; +rua_macro_t *rua_start_macro (void *scanner); +rua_macro_t *rua_macro_append (rua_macro_t *macro, rua_tok_t *token, + void *scanner); +void rua_macro_finish (rua_macro_t *macro, void *scanner); +void rua_start_text (void *scanner); +void rua_start_expr (void *scanner); +void rua_end_directive (void *scanner); +void rua_if (bool pass, void *scanner); +void rua_else (bool pass, const char *tok, void *scanner); +void rua_endif (void *scanner); + +#include "tools/qfcc/source/pre-parse.h" + #endif//__rua_lang_h diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index 91c8edbeb..d960e5216 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -48,6 +48,7 @@ qfcc_SOURCES = \ tools/qfcc/source/opcodes.c \ tools/qfcc/source/options.c \ tools/qfcc/source/pragma.c \ + tools/qfcc/source/pre-parse.y \ tools/qfcc/source/qc-lex.l \ tools/qfcc/source/qc-parse.y \ tools/qfcc/source/qfcc.c \ @@ -88,6 +89,8 @@ BUILT_SOURCES += \ tools/qfcc/source/qp-parse.h \ tools/qfcc/source/qp-lex.c +tools/qfcc/source/pre-parse.c: tools/qfcc/source/pre-parse.y + $(AM_V_YACC)$(YACCCOMPILE) $< -o $@ tools/qfcc/source/qc-parse.c: tools/qfcc/source/qc-parse.y $(AM_V_YACC)$(YACCCOMPILE) $< -o $@ tools/qfcc/source/qc-lex.c: tools/qfcc/source/qc-lex.l diff --git a/tools/qfcc/source/options.c b/tools/qfcc/source/options.c index 592ca9180..6e75ed399 100644 --- a/tools/qfcc/source/options.c +++ b/tools/qfcc/source/options.c @@ -862,6 +862,7 @@ DecodeArgs (int argc, char **argv) } if (options.verbosity >= 3) { + pre_yydebug = 1; qc_yydebug = 1; qp_yydebug = 1; } diff --git a/tools/qfcc/source/pre-parse.y b/tools/qfcc/source/pre-parse.y new file mode 100644 index 000000000..b66468e64 --- /dev/null +++ b/tools/qfcc/source/pre-parse.y @@ -0,0 +1,279 @@ +/* + pre-parse.y + + parser for the preprocessor + + Copyright (C) 2023 Bill Currie + + Author: Bill Currie + Date: 2023/10/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 + +*/ +%define api.prefix {pre_yy} +%define api.pure full +%define api.push-pull push +%define api.token.prefix {PRE_} +%locations +%parse-param {void *scanner} + +%{ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/dstring.h" + +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/pragma.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/type.h" + +#define YYDEBUG 1 +#define YYERROR_VERBOSE 1 +#undef YYERROR_VERBOSE + +#include "tools/qfcc/source/pre-parse.h" + +#define pre_yytext qc_yyget_text (scanner) +char *qc_yyget_text (void *scanner); + +static void +yyerror (YYLTYPE *yylloc, void *scanner, const char *s) +{ + const char *text = quote_string (pre_yytext); +#ifdef YYERROR_VERBOSE + error (0, "%d:%s before '%s'\n", yylloc->first_column, s, text); +#else + error (0, "%s before '%s'", s, text); +#endif +} +#if 0 +static void +parse_error (void *scanner) +{ + const char *text = pre_yytext; + error (0, "parse error before %s", text); +} + +#define PARSE_ERROR do { parse_error (scanner); YYERROR; } while (0) +#endif +%} + +%code requires { +#include "tools/qfcc/include/rua-lang.h" +} +%define api.value.type {rua_tok_t} +%define api.location.type {rua_loc_t} + +%left LOW +%nonassoc IFX +%nonassoc ELSE +%nonassoc BREAK_PRIMARY +%nonassoc ';' +%nonassoc CLASS_NOT_CATEGORY +%nonassoc STORAGEX + +%left COMMA +%right '=' ASX +%right '?' ':' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left LT GT GE LE +%token NAND NOR XNOR +// end of tokens common between qc and qp + +%left SHL SHR +%left '+' '-' +%left '*' '/' '%' MOD SCALE GEOMETRIC +%left HADAMARD CROSS DOT WEDGE REGRESSIVE +%right SIZEOF UNARY INCOP REVERSE STAR DUAL +%left HYPERUNARY +%left '.' '(' '[' + +%token VALUE STRING +%token TOKEN +// end of tokens comment between qc and preprocessor + +%token QSTRING HSTRING + +%token INCLUDE EMBED +%token DEFINE UNDEF +%token TEXT ID IDp +%token ERROR WARNING +%token PRAGMA LINE +%token IF IFDEF IFNDEF ELSE ELIF ELIFDEF ELIFNDEF ENDIF +%token DEFINED EOD +%token CONCAT + +%type body body_text +%type text text_text +%type unary_expr expr id + +%{ +#define BEXPR(a,op,b) new_long_expr (expr_long (a) op expr_long (b), false) +#define UEXPR(op,a) new_long_expr (op expr_long (a), false) +%} + +%% + +start : directive_list + +directive_list + : /*empty*/ + | directive EOD { rua_end_directive (scanner); } directive_list + ; + +directive + : INCLUDE expand string extra_warn + | EMBED expand string extra_ignore + | DEFINE ID body + | DEFINE IDp params ')' body { rua_macro_finish ($body, scanner); } + | UNDEF ID extra_warn + | ERROR text { error (0, "%s", $text->str); dstring_delete ($text); } + | WARNING text { warning (0, "%s", $text->str); dstring_delete ($text); } + | PRAGMA expand pragma_params { pragma_process (); } + | LINE expand expr QSTRING extra_warn + | IF expand expr { rua_if (expr_long ($3), scanner); } + | IFDEF ID extra_warn + | IFNDEF ID extra_warn + | ELSE extra_warn { rua_else (true, "else", scanner); } + | ELIF expand expr { rua_else (expr_long ($3), "elif", scanner); } + | ELIFDEF ID extra_warn + | ELIFNDEF ID extra_warn + | ENDIF extra_warn { rua_endif (scanner); } + ; + +extra_warn + : {} + ; + +extra_ignore + : {} + ; + +text: { rua_start_text (scanner); $$ = dstring_new (); } text_text + { $text = $text_text; } + ; +text_text + : TEXT { dstring_appendstr ($0, $1); $$ = $0; } + | text_text TEXT { dstring_appendstr ($1, $2); $$ = $1; } + ; + +body: { $$ = rua_start_macro (scanner); } body_text + { $body = $body_text; } + ; +body_text + : TOKEN { $$ = rua_macro_append ($0, yyvsp, scanner); } + | body_text TOKEN { $$ = rua_macro_append ($1, yyvsp, scanner); } + ; + +expand + : { rua_start_expr (scanner); } + ; + +pragma_params + : ID { pragma_add_arg (pre_yytext); } + | pragma_params ID { pragma_add_arg (pre_yytext); } + ; + +string + : HSTRING + | QSTRING + ; + +params + : ID + | params ',' ID + ; + +args: arg + | args ',' arg + ; + +arg : /* empty */ + | expr + ; + +id : ID {$$ = 0; } + | IDp args ')' {$$ = 0; } + ; + +unary_expr + : id + | VALUE + { + auto type = get_type ($1); + if (!is_long (type)) { + if (is_double (type)) { + error (0, "floating constant in preprocessor expression"); + $1 = new_long_expr (expr_double ($1), false); + } else { + error (0, "token \"%s\" is not valid in preprocessor" + " expressions", $1); + $1 = new_long_expr (1, false); + } + } + $$ = $1; + } + | QSTRING + { + error (0, "token \"%s\" is not valid in preprocessor" + " expressions", $1); + $$ = new_long_expr (1, false); + } + | '(' expr ')' { $$ = $2; } + | DEFINED { $$ = new_long_expr (0, false); /*FIXME*/ } + | '+' unary_expr { $$ = $2; } + | '-' unary_expr { $$ = UEXPR (-, $2); } + | '~' unary_expr { $$ = UEXPR (~, $2); } + | '!' unary_expr { $$ = UEXPR (!, $2); } + ; + +expr + : unary_expr { $$ = $1; } + | expr AND expr { $$ = BEXPR ($1, &&, $3); } + | expr OR expr { $$ = BEXPR ($1, ||, $3); } + | expr EQ expr { $$ = BEXPR ($1, ==, $3); } + | expr NE expr { $$ = BEXPR ($1, !=, $3); } + | expr LE expr { $$ = BEXPR ($1, <=, $3); } + | expr GE expr { $$ = BEXPR ($1, >=, $3); } + | expr LT expr { $$ = BEXPR ($1, < , $3); } + | expr GT expr { $$ = BEXPR ($1, > , $3); } + | expr SHL expr { $$ = BEXPR ($1, <<, $3); } + | expr SHR expr { $$ = BEXPR ($1, >>, $3); } + | expr '+' expr { $$ = BEXPR ($1, + , $3); } + | expr '-' expr { $$ = BEXPR ($1, - , $3); } + | expr '*' expr { $$ = BEXPR ($1, * , $3); } + | expr '/' expr { $$ = BEXPR ($1, / , $3); } + | expr '&' expr { $$ = BEXPR ($1, & , $3); } + | expr '|' expr { $$ = BEXPR ($1, | , $3); } + | expr '^' expr { $$ = BEXPR ($1, ^ , $3); } + | expr '%' expr { $$ = BEXPR ($1, % , $3); } + ; + +%% diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index 03ee01506..04a8e704e 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -85,8 +85,22 @@ #define YYSTYPE rua_tok_t #define YYLTYPE rua_loc_t +typedef struct { + bool saw_true; + bool saw_else; + bool own_state; + bool enabled; +} rua_cond_t; + +typedef struct DARRAY_TYPE (rua_cond_t) rua_cond_stack_t; + typedef struct rua_extra_s { int start_state; + bool preprocessor; + bool recording; + qc_yypstate *qc_state; + pre_yypstate *pre_state; + rua_cond_stack_t cond_stack; } rua_extra_t; #define YY_EXTRA_TYPE rua_extra_t * @@ -101,21 +115,25 @@ 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 int keyword_or_id (YYSTYPE *lval, const char *token); static void user_action (rua_tok_t *tok, rua_loc_t *loc, - const char *text, size_t textlen, yyscan_t scanner); + const char *text, size_t textlen, int state); +static void undo_loc (rua_loc_t *loc); static void next_line (rua_loc_t *loc); -enum { +typedef enum { rua_eof = 1, rua_error, rua_number, rua_vector, rua_string, rua_char, -}; -#define YY_USER_ACTION user_action (yylval, yylloc, yytext, yyleng, yyscanner); + rua_num_term, +} rua_term; + +#define YY_USER_ACTION user_action (yylval, yylloc, yytext, yyleng, yystart()); %} @@ -134,21 +152,26 @@ 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 COMMENT LCOMMENT PRAGMA +%x GRAB_FRAME GRAB_OTHER GRAB_WRITE +%x COMMENT LCOMMENT +%x BOL DIRECTIVE TEXT MACRO SUPPRESS SUPPRESSC PRAGMA %x VECTOR +%s PREPROC PREEXPR %% + auto extra = qc_yyget_extra (yyscanner); yyset_debug (0, yyscanner); grab_frame = GRAB_FRAME; grab_other = GRAB_OTHER; grab_write = GRAB_WRITE; yylval->pointer = 0; // ensure pointer vals are null -"/*" { yy_push_state (COMMENT, yyscanner); } "/*" { warning (0, "nested /* in comment"); } \*+"/" { yy_pop_state (yyscanner); } \n { next_line (yylloc); } @@ -156,10 +179,50 @@ pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}? \/+[^*\n]* /* handle /s not followed by * */ \*+[^/\n]* /* handle *s not followed by * */ <> { error (0, "EOF in comment"); return 0; } -"//" { yy_push_state (LCOMMENT, yyscanner); }/* cf <*>\r\n */ +<*>"/*" { yy_push_state (COMMENT, yyscanner); } +<*>"//" { yy_push_state (LCOMMENT, yyscanner); }/* cf <*>\r\n */ [^\\\r\n]* /* consume all but \ and EOL (see line continuation) */ [\\]* /* consume \ */ +<*>^#{s}* { + yy_push_state (DIRECTIVE, yyscanner); + extra->preprocessor = true; + } +<*>^{s}* { yy_push_state (BOL, yyscanner); } +#{s}* { BEGIN (DIRECTIVE); extra->preprocessor = true; } +. { + yy_pop_state (yyscanner); + undo_loc (yylloc); + yyless (0); + } + +{ID} { { + int tok = directive (yytext, yyscanner); + if (tok >= 0) { + return tok; + } + } } +{ID} { return PRE_ID; } +{ID}\( { return PRE_IDp; } + +\r*\n | +\r*\n | +\r*\n | +\r*\n { next_line (yylloc); return PRE_EOD; } + +^[^#\n \t]+ { BEGIN (SUPPRESSC); } +.* /* nom nom */ +\n { next_line (yylloc); BEGIN (SUPPRESS); } +\n { next_line (yylloc); } +<> { error (0, "unterminated #if"); return 0; } + +[^\\\r\n]* { return PRE_TEXT; } +[\\]* { return PRE_TEXT; } +## { return PRE_CONCAT; } +# { return '#'; } +{h_string} { return PRE_HSTRING; } +{q_string} { return PRE_QSTRING; } + ^#{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 1); } ^#line{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 5); } @@ -248,16 +311,97 @@ pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}? <*>{s}+ /* skip */ -<*>. error (0, "all your typo are belong to us"); +<*>. { + error (0, "all your typo are belong to us:%d %d-%d", + yystart(), + yylloc->first_column, yylloc->last_column); + } %% +#define ARRCOUNT(_k) (sizeof (_k) / sizeof (_k[0])) + typedef struct { const char *name; int value; specifier_t spec; } keyword_t; +typedef struct { + const char *name; + int value; +} directive_t; + +// preprocessor directives in ruamoko and quakec +static directive_t rua_directives[] = { + {"include", PRE_INCLUDE}, + {"embed", PRE_EMBED}, + {"define", PRE_DEFINE}, + {"undef", PRE_UNDEF}, + {"error", PRE_ERROR}, + {"warning", PRE_WARNING}, + {"pragma", PRE_PRAGMA}, + {"line", PRE_LINE}, +}; + +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}, +}; + +static const char * +directive_get_key (const void *dir, void *unused) +{ + return ((directive_t*)dir)->name; +} + +static int +directive (const char *token, yyscan_t scanner) +{ + static hashtab_t *rua_directive_tab; + static hashtab_t *cond_directive_tab; + + yy_pop_state (scanner); // pop DIRECTIVE off the stack + yy_push_state (PREPROC, scanner); + + if (!rua_directive_tab) { + rua_directive_tab = Hash_NewTable (253, directive_get_key, 0, 0, 0); + cond_directive_tab = Hash_NewTable (253, directive_get_key, 0, 0, 0); + + for (size_t i = 0; i < ARRCOUNT(rua_directives); i++) { + Hash_Add (rua_directive_tab, &rua_directives[i]); + } + for (size_t i = 0; i < ARRCOUNT(cond_directives); i++) { + Hash_Add (cond_directive_tab, &cond_directives[i]); + } + } + directive_t *directive = Hash_Find (cond_directive_tab, token); + if (!directive && yy_top_state (scanner) != SUPPRESS) { + directive = Hash_Find (rua_directive_tab, token); + if (!directive) { + error (0, "invalid directive `%s`", token); + yy_pop_state (scanner); + yy_push_state (LCOMMENT, scanner); + } + } + if (!directive) { + if (yy_top_state (scanner) == SUPPRESS) { + yy_pop_state (scanner);// suppressed non-conditional directive + yy_push_state (LCOMMENT, scanner); + } + return -1; + } + //auto lex = qc_yyget_extra (scanner); + //lex->current_lang = &lex->pre_lang; + return directive->value; +} + // These keywords are part of the Ruamoko language and require the QuakeForge // Ruamoko VM. static keyword_t rua_keywords[] = { @@ -435,17 +579,15 @@ keyword_or_id (YYSTYPE *lval, const char *token) obj_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); rua_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); - #define NUMKEYS(_k) (sizeof (_k) / sizeof (_k[0])) - - for (i = 0; i < NUMKEYS(keywords); i++) + for (i = 0; i < ARRCOUNT(keywords); i++) Hash_Add (keyword_tab, &keywords[i]); - for (i = 0; i < NUMKEYS(qf_keywords); i++) + for (i = 0; i < ARRCOUNT(qf_keywords); i++) Hash_Add (qf_keyword_tab, &qf_keywords[i]); - for (i = 0; i < NUMKEYS(at_keywords); i++) + for (i = 0; i < ARRCOUNT(at_keywords); i++) Hash_Add (at_keyword_tab, &at_keywords[i]); - for (i = 0; i < NUMKEYS(obj_keywords); i++) + for (i = 0; i < ARRCOUNT(obj_keywords); i++) Hash_Add (obj_keyword_tab, &obj_keywords[i]); - for (i = 0; i < NUMKEYS(rua_keywords); i++) + for (i = 0; i < ARRCOUNT(rua_keywords); i++) Hash_Add (rua_keyword_tab, &rua_keywords[i]); } if (options.traditional < 1) { @@ -867,11 +1009,18 @@ next_line (rua_loc_t *loc) } static void -user_action (rua_tok_t *tok, rua_loc_t *loc, const char *text, size_t textlen, - yyscan_t scanner) +undo_loc (rua_loc_t *loc) { - int state = yy_top_state (scanner); - if (state != COMMENT) { + // the next call to update_yylloc will sort out first_line and first_column + loc->last_line = loc->first_line; + loc->last_column = loc->first_column; +} + +static void +user_action (rua_tok_t *tok, rua_loc_t *loc, const char *text, size_t textlen, + int state) +{ + if (state != COMMENT && state != SUPPRESS) { if (textlen < sizeof (tok->text)) { strncpy (tok->str_text, text, textlen); tok->str_text[textlen] = 0; @@ -885,37 +1034,62 @@ user_action (rua_tok_t *tok, rua_loc_t *loc, const char *text, size_t textlen, loc->first_column = loc->last_column; // \n handling rules will take care of the column and line loc->last_column += textlen; +#if 0 + printf ("start: %2d [%3d %3d] [%3d %3d] '%s'\n", state, + loc->first_line, loc->first_column, + loc->last_line, loc->last_column, + quote_string (text)); +#endif } static int -qc_process (qc_yypstate *ps, int token, rua_tok_t *tok, yyscan_t scanner) +qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t scanner) { auto value = &tok->value; auto loc = &tok->location; - switch (-token) { - case rua_number: - token = parse_number (tok, scanner); - if (token == VALUE && value->expr->implicit) { - if (is_long (get_type (value->expr))) { - pr_long_t v = expr_long (value->expr); - if (v < INT32_MIN || v > INT32_MAX) { - warning (0, "integer value truncated"); + if (token < 0) { + rua_term term = -token; + switch (term) { + case rua_eof: + case rua_error: + case rua_num_term: + break; + case rua_number: + token = parse_number (tok, scanner); + if (!extra->preprocessor + && token == VALUE && value->expr->implicit) { + if (is_long (get_type (value->expr))) { + pr_long_t v = expr_long (value->expr); + if (v < INT32_MIN || v > INT32_MAX) { + warning (0, "integer value truncated"); + } + value->expr = new_int_expr (v, true); } - value->expr = new_int_expr (v, true); } - } - break; - case rua_vector: - token = parse_vector (tok, scanner); - break; - case rua_string: - case rua_char: - token = parse_string (tok, -token, scanner); - break; + break; + case rua_vector: + token = parse_vector (tok, scanner); + break; + case rua_string: + case rua_char: + token = parse_string (tok, -token, scanner); + break; + } } if (token >= 0) { - return qc_yypush_parse (ps, token, value, loc, scanner); + //printf ("%d %d %s\n", token, extra->preprocessor, tok->text); + if (extra->preprocessor) { + auto state = extra->pre_state; + if (extra->recording && token != PRE_EOD) { + tok->token = token; + token = PRE_TOKEN; + } + return pre_yypush_parse (state, token, tok, loc, scanner); + } else { + auto state = extra->qc_state; + return qc_yypush_parse (state, token, value, loc, scanner); + } } return YYPUSH_MORE; } @@ -925,18 +1099,147 @@ qc_yyparse (FILE *in) { int status; yyscan_t scanner; - qc_yypstate *ps = qc_yypstate_new (); rua_tok_t tok = { .location = { 1, 1, 1, 1, pr.source_file }, }; - rua_extra_t extra = {}; + rua_extra_t extra = { + .qc_state = qc_yypstate_new (), + .pre_state = pre_yypstate_new (), + .cond_stack = DARRAY_STATIC_INIT (8), + }; yylex_init_extra (&extra, &scanner); yyset_in (in, scanner); do { int token = yylex (&tok, &tok.location, scanner); - status = qc_process (ps, token, &tok, scanner); + status = qc_process (&extra, token, &tok, scanner); } while (status == YYPUSH_MORE); yylex_destroy (scanner); - qc_yypstate_delete (ps); + qc_yypstate_delete (extra.qc_state); + pre_yypstate_delete (extra.pre_state); + free (extra.cond_stack.a); return status; } + +rua_macro_t * +rua_start_macro (void *scanner) +{ + auto extra = qc_yyget_extra (scanner); + extra->recording = true; + + yy_pop_state (scanner); + yy_push_state (MACRO, scanner); + return 0; +} + +rua_macro_t * +rua_macro_append (rua_macro_t *macro, rua_tok_t *token, void *scanner) +{ + return macro; +} + +void +rua_macro_finish (rua_macro_t *macro, void *scanner) +{ +} + +void +rua_start_text (void *scanner) +{ + yy_pop_state (scanner); + yy_push_state (TEXT, scanner); +} + +void +rua_start_expr (void *scanner) +{ + yy_pop_state (scanner); + yy_push_state (PREEXPR, scanner); +} + +void +rua_end_directive (void *scanner) +{ + auto extra = qc_yyget_extra (scanner); + extra->preprocessor = false; + extra->recording = false; + yy_pop_state (scanner); +} + +static void +dump_state_stack (void *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\n", yystart ()); +} + +void +rua_if (bool pass, void *scanner) +{ + auto extra = qc_yyget_extra (scanner); + rua_cond_t cond = { + .saw_true = pass, + .saw_else = false, + .own_state = true, + .enabled = pass, + }; + 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); + yy_pop_state (scanner); // remove DIRECTIVE/PREEXPR state + if (cond.own_state && !cond.enabled) { + yy_push_state (SUPPRESS, scanner); + } + // 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) { + yy_pop_state (scanner); + } + pass &= !cond->saw_true; + cond->enabled = pass; + cond->saw_true |= pass; + cond->saw_else = strcmp (tok, "else") == 0; + if (cond->own_state && !cond->enabled) { + yy_push_state (SUPPRESS, scanner); + } + // 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; + } + if (0) dump_state_stack (scanner); + yy_pop_state (scanner); // remove DIRECTIVE state + auto cond = DARRAY_REMOVE (&extra->cond_stack); + if (cond.own_state && !cond.enabled) { + yy_pop_state (scanner); + } + // put PREEXPR on the stack for EOD to pop + yy_push_state (PREEXPR, scanner); +} diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index b37003775..ecdaa87bf 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -163,8 +163,10 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %left HYPERUNARY %left '.' '(' '[' +%token VALUE STRING TOKEN +// end of tokens common between qc and qp + %token CLASS_NAME NAME -%token VALUE STRING %token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE %token RETURN AT_RETURN ELLIPSIS