[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).
This commit is contained in:
Bill Currie 2023-10-22 20:56:12 +09:00
parent df3ffaaf0f
commit 5794c68bda
7 changed files with 652 additions and 44 deletions

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -862,6 +862,7 @@ DecodeArgs (int argc, char **argv)
}
if (options.verbosity >= 3) {
pre_yydebug = 1;
qc_yydebug = 1;
qp_yydebug = 1;
}

View file

@ -0,0 +1,279 @@
/*
pre-parse.y
parser for the preprocessor
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
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 <string.h>
#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 <op> '=' 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 <op> SIZEOF UNARY INCOP REVERSE STAR DUAL
%left HYPERUNARY
%left '.' '(' '['
%token <value.expr> VALUE STRING
%token <location> TOKEN
// end of tokens comment between qc and preprocessor
%token QSTRING HSTRING
%token INCLUDE EMBED
%token DEFINE UNDEF
%token <text> TEXT ID IDp
%token ERROR WARNING
%token PRAGMA LINE
%token IF IFDEF IFNDEF ELSE ELIF ELIFDEF ELIFNDEF ENDIF
%token DEFINED EOD
%token CONCAT
%type <macro> body body_text
%type <dstr> text text_text
%type <value.expr> 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); $<dstr>$ = dstring_new (); } text_text
{ $text = $text_text; }
;
text_text
: TEXT { dstring_appendstr ($<dstr>0, $1); $$ = $<dstr>0; }
| text_text TEXT { dstring_appendstr ($1, $2); $$ = $1; }
;
body: <macro>{ $$ = rua_start_macro (scanner); } body_text
{ $body = $body_text; }
;
body_text
: TOKEN { $$ = rua_macro_append ($<macro>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", $<text>1);
$1 = new_long_expr (1, false);
}
}
$$ = $1;
}
| QSTRING
{
error (0, "token \"%s\" is not valid in preprocessor"
" expressions", $<text>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); }
;
%%

View file

@ -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); }
<COMMENT>"/*" { warning (0, "nested /* in comment"); }
<COMMENT>\*+"/" { yy_pop_state (yyscanner); }
<COMMENT>\n { next_line (yylloc); }
@ -156,10 +179,50 @@ pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}?
<COMMENT>\/+[^*\n]* /* handle /s not followed by * */
<COMMENT>\*+[^/\n]* /* handle *s not followed by * */
<COMMENT><<EOF>> { 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 */
<LCOMMENT>[^\\\r\n]* /* consume all but \ and EOL (see line continuation) */
<LCOMMENT>[\\]* /* consume \ */
<*>^#{s}* {
yy_push_state (DIRECTIVE, yyscanner);
extra->preprocessor = true;
}
<*>^{s}* { yy_push_state (BOL, yyscanner); }
<BOL>#{s}* { BEGIN (DIRECTIVE); extra->preprocessor = true; }
<BOL>. {
yy_pop_state (yyscanner);
undo_loc (yylloc);
yyless (0);
}
<DIRECTIVE>{ID} { {
int tok = directive (yytext, yyscanner);
if (tok >= 0) {
return tok;
}
} }
<PREPROC,MACRO>{ID} { return PRE_ID; }
<PREPROC>{ID}\( { return PRE_IDp; }
<DIRECTIVE>\r*\n |
<PREEXPR>\r*\n |
<MACRO,TEXT>\r*\n |
<PREPROC>\r*\n { next_line (yylloc); return PRE_EOD; }
<SUPPRESS>^[^#\n \t]+ { BEGIN (SUPPRESSC); }
<SUPPRESSC>.* /* nom nom */
<SUPPRESSC>\n { next_line (yylloc); BEGIN (SUPPRESS); }
<SUPPRESS>\n { next_line (yylloc); }
<SUPPRESS><<EOF>> { error (0, "unterminated #if"); return 0; }
<TEXT>[^\\\r\n]* { return PRE_TEXT; }
<TEXT>[\\]* { return PRE_TEXT; }
<MACRO>## { return PRE_CONCAT; }
<MACRO># { return '#'; }
<PREEXPR>{h_string} { return PRE_HSTRING; }
<PREEXPR>{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);
}

View file

@ -163,8 +163,10 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
%left HYPERUNARY
%left '.' '(' '['
%token <expr> VALUE STRING TOKEN
// end of tokens common between qc and qp
%token <symbol> CLASS_NAME NAME
%token <expr> VALUE STRING
%token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE
%token RETURN AT_RETURN ELLIPSIS