mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 13:11:00 +00:00
[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:
parent
df3ffaaf0f
commit
5794c68bda
7 changed files with 652 additions and 44 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -862,6 +862,7 @@ DecodeArgs (int argc, char **argv)
|
|||
}
|
||||
|
||||
if (options.verbosity >= 3) {
|
||||
pre_yydebug = 1;
|
||||
qc_yydebug = 1;
|
||||
qp_yydebug = 1;
|
||||
}
|
||||
|
|
279
tools/qfcc/source/pre-parse.y
Normal file
279
tools/qfcc/source/pre-parse.y
Normal 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); }
|
||||
;
|
||||
|
||||
%%
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue