mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-19 07:51:08 +00:00
[qfcc] Implement macro recording and defined()
So far, very consistent with gcc's cpp from my limited testing, though some error handling may be a little different.
This commit is contained in:
parent
5794c68bda
commit
5cab587207
8 changed files with 163 additions and 25 deletions
|
@ -36,7 +36,21 @@ typedef struct rua_loc_s {
|
|||
int file;
|
||||
} rua_loc_t;
|
||||
|
||||
typedef struct symtab_s symtab_t;
|
||||
|
||||
typedef struct rua_expr_s {
|
||||
struct rua_expr_s *next;
|
||||
rua_loc_t location;
|
||||
int textlen;
|
||||
int token;
|
||||
const char *text;
|
||||
} rua_expr_t;
|
||||
|
||||
typedef struct rua_macro_s {
|
||||
const char *name;
|
||||
symtab_t *params;
|
||||
rua_expr_t *tokens;
|
||||
rua_expr_t **tail;
|
||||
} rua_macro_t;
|
||||
|
||||
typedef struct rua_tok_s {
|
||||
|
@ -55,7 +69,9 @@ typedef struct rua_tok_s {
|
|||
};
|
||||
} rua_tok_t;
|
||||
|
||||
rua_macro_t *rua_start_macro (void *scanner);
|
||||
rua_macro_t *rua_start_macro (const char *name, void *scanner);
|
||||
rua_macro_t *rua_macro_param (rua_macro_t *macro, rua_tok_t *token,
|
||||
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);
|
||||
|
@ -65,6 +81,7 @@ 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);
|
||||
bool rua_defined (const char *sym, void *scanner);
|
||||
|
||||
#include "tools/qfcc/source/pre-parse.h"
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ int strpool_findstr (strpool_t *strpool, const char *str);
|
|||
\return The unique copy of the string.
|
||||
*/
|
||||
const char *save_string (const char *str);
|
||||
const char *save_substring (const char *str, int len);
|
||||
|
||||
const char *save_cwd (void);
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef enum {
|
|||
sy_func, ///< symbol refers to a function
|
||||
sy_class, ///< symbol refers to a class
|
||||
sy_convert, ///< symbol refers to a conversion function
|
||||
sy_macro, ///< symbol refers to a macro definition
|
||||
} sy_type_e;
|
||||
|
||||
typedef struct symconv_s {
|
||||
|
@ -80,6 +81,7 @@ typedef struct symbol_s {
|
|||
const struct expr_s *expr; ///< sy_expr
|
||||
struct function_s *func; ///< sy_func
|
||||
symconv_t convert; ///< sy_convert
|
||||
struct rua_macro_s *macro; ///< sy_macro
|
||||
} s;
|
||||
} symbol_t;
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ is_lvalue (const expr_t *expr)
|
|||
break;
|
||||
case sy_convert:
|
||||
break;
|
||||
case sy_macro:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ex_temp:
|
||||
|
|
|
@ -116,7 +116,7 @@ parse_error (void *scanner)
|
|||
%left '.' '(' '['
|
||||
|
||||
%token <value.expr> VALUE STRING
|
||||
%token <location> TOKEN
|
||||
%token TOKEN
|
||||
// end of tokens comment between qc and preprocessor
|
||||
|
||||
%token QSTRING HSTRING
|
||||
|
@ -130,9 +130,9 @@ parse_error (void *scanner)
|
|||
%token DEFINED EOD
|
||||
%token CONCAT
|
||||
|
||||
%type <macro> body body_text
|
||||
%type <macro> params body
|
||||
%type <dstr> text text_text
|
||||
%type <value.expr> unary_expr expr id
|
||||
%type <value.expr> unary_expr expr id defined defined_id
|
||||
|
||||
%{
|
||||
#define BEXPR(a,op,b) new_long_expr (expr_long (a) op expr_long (b), false)
|
||||
|
@ -151,20 +151,29 @@ 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); }
|
||||
| DEFINE ID <macro> { $$ = rua_start_macro ($2, scanner); }
|
||||
body { rua_macro_finish ($body, scanner); }
|
||||
| DEFINE IDp <macro> { $$ = rua_start_macro ($2, scanner); }
|
||||
params ')' <macro> { $$ = $3; }
|
||||
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
|
||||
| IFDEF ID extra_warn { rua_if (rua_defined ($2, scanner), scanner); }
|
||||
| IFNDEF ID extra_warn { rua_if (!rua_defined ($2, scanner), scanner); }
|
||||
| ELSE extra_warn { rua_else (true, "else", scanner); }
|
||||
| ELIF expand expr { rua_else (expr_long ($3), "elif", scanner); }
|
||||
| ELIFDEF ID extra_warn
|
||||
{
|
||||
rua_else (rua_defined ($2, scanner), "elifdef", scanner);
|
||||
}
|
||||
| ELIFNDEF ID extra_warn
|
||||
{
|
||||
rua_else (!rua_defined ($2, scanner), "elifndef", scanner);
|
||||
}
|
||||
| ENDIF extra_warn { rua_endif (scanner); }
|
||||
;
|
||||
|
||||
|
@ -184,14 +193,12 @@ text_text
|
|||
| 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); }
|
||||
body: /* empty */ { $$ = $<macro>0; }
|
||||
| body body_token { $$ = rua_macro_append ($1, yyvsp, scanner); }
|
||||
;
|
||||
|
||||
body_token : TOKEN | ',' | '(' | ')' ;
|
||||
|
||||
expand
|
||||
: { rua_start_expr (scanner); }
|
||||
;
|
||||
|
@ -207,8 +214,8 @@ string
|
|||
;
|
||||
|
||||
params
|
||||
: ID
|
||||
| params ',' ID
|
||||
: TOKEN { $$ = rua_macro_param ($<macro>0, yyvsp, scanner); }
|
||||
| params ',' TOKEN { $$ = rua_macro_param ($1, yyvsp, scanner); }
|
||||
;
|
||||
|
||||
args: arg
|
||||
|
@ -219,8 +226,18 @@ arg : /* empty */
|
|||
| expr
|
||||
;
|
||||
|
||||
id : ID {$$ = 0; }
|
||||
| IDp args ')' {$$ = 0; }
|
||||
id : ID { $$ = 0; }
|
||||
| IDp args ')' { $$ = 0; }
|
||||
;
|
||||
|
||||
defined
|
||||
: defined_id { $$ = $1; }
|
||||
| '(' defined_id ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
//the token's text is quite ephemeral when short (the usual case)
|
||||
defined_id
|
||||
: ID { $$ = new_long_expr (rua_defined ($1, scanner), false); }
|
||||
;
|
||||
|
||||
unary_expr
|
||||
|
@ -247,7 +264,7 @@ unary_expr
|
|||
$$ = new_long_expr (1, false);
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| DEFINED { $$ = new_long_expr (0, false); /*FIXME*/ }
|
||||
| DEFINED defined { $$ = $2; }
|
||||
| '+' unary_expr { $$ = $2; }
|
||||
| '-' unary_expr { $$ = UEXPR (-, $2); }
|
||||
| '~' unary_expr { $$ = UEXPR (~, $2); }
|
||||
|
|
|
@ -101,6 +101,7 @@ typedef struct rua_extra_s {
|
|||
qc_yypstate *qc_state;
|
||||
pre_yypstate *pre_state;
|
||||
rua_cond_stack_t cond_stack;
|
||||
symtab_t *macro_tab;
|
||||
} rua_extra_t;
|
||||
#define YY_EXTRA_TYPE rua_extra_t *
|
||||
|
||||
|
@ -160,9 +161,9 @@ pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}?
|
|||
|
||||
%x GRAB_FRAME GRAB_OTHER GRAB_WRITE
|
||||
%x COMMENT LCOMMENT
|
||||
%x BOL DIRECTIVE TEXT MACRO SUPPRESS SUPPRESSC PRAGMA
|
||||
%x BOL DIRECTIVE TEXT SUPPRESS SUPPRESSC PRAGMA
|
||||
%x VECTOR
|
||||
%s PREPROC PREEXPR
|
||||
%s PREPROC PREEXPR MACRO
|
||||
|
||||
%%
|
||||
auto extra = qc_yyget_extra (yyscanner);
|
||||
|
@ -222,6 +223,8 @@ pp_vnumber '({s}*{m}?{pp_number}){2,4}{s}*'{ULFD}?
|
|||
<MACRO># { return '#'; }
|
||||
<PREEXPR>{h_string} { return PRE_HSTRING; }
|
||||
<PREEXPR>{q_string} { return PRE_QSTRING; }
|
||||
<PREEXPR>defined { return PRE_DEFINED; }
|
||||
<PREEXPR>{ID} { return PRE_ID; }
|
||||
|
||||
^#{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 1); }
|
||||
^#line{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 5); }
|
||||
|
@ -1081,9 +1084,11 @@ qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t 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) {
|
||||
if (extra->recording) {
|
||||
tok->token = token;
|
||||
token = PRE_TOKEN;
|
||||
if (token != PRE_EOD && token != ',' && token != ')') {
|
||||
token = PRE_TOKEN;
|
||||
}
|
||||
}
|
||||
return pre_yypush_parse (state, token, tok, loc, scanner);
|
||||
} else {
|
||||
|
@ -1104,6 +1109,7 @@ qc_yyparse (FILE *in)
|
|||
.qc_state = qc_yypstate_new (),
|
||||
.pre_state = pre_yypstate_new (),
|
||||
.cond_stack = DARRAY_STATIC_INIT (8),
|
||||
.macro_tab = new_symtab (0, stab_global),
|
||||
};
|
||||
|
||||
yylex_init_extra (&extra, &scanner);
|
||||
|
@ -1121,25 +1127,100 @@ qc_yyparse (FILE *in)
|
|||
}
|
||||
|
||||
rua_macro_t *
|
||||
rua_start_macro (void *scanner)
|
||||
rua_start_macro (const char *name, void *scanner)
|
||||
{
|
||||
auto extra = qc_yyget_extra (scanner);
|
||||
extra->recording = true;
|
||||
|
||||
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 0;
|
||||
|
||||
rua_macro_t *macro = malloc (sizeof (*macro));
|
||||
*macro = (rua_macro_t) {
|
||||
.name = name,
|
||||
.params = new_symtab (0, stab_param),
|
||||
.tail = ¯o->tokens,
|
||||
};
|
||||
return macro;
|
||||
}
|
||||
|
||||
rua_macro_t *
|
||||
rua_macro_append (rua_macro_t *macro, rua_tok_t *token, void *scanner)
|
||||
{
|
||||
rua_expr_t *expr = malloc (sizeof (*expr));
|
||||
*expr = (rua_expr_t) {
|
||||
.location = token->location,
|
||||
.textlen = token->textlen,
|
||||
.token = token->token,
|
||||
.text = save_string (token->text),
|
||||
};
|
||||
*macro->tail = expr;
|
||||
macro->tail = &expr->next;
|
||||
return macro;
|
||||
}
|
||||
|
||||
rua_macro_t *
|
||||
rua_macro_param (rua_macro_t *macro, rua_tok_t *token, void *scanner)
|
||||
{
|
||||
if (token->token != PRE_ID) {
|
||||
error (0, "expected parameter name, found \"%s\"", token->text);
|
||||
return macro;
|
||||
}
|
||||
if (symtab_lookup (macro->params, token->text)) {
|
||||
error (0, "duplicate macro parameter \"%s\"", token->text);
|
||||
return macro;
|
||||
}
|
||||
auto sym = new_symbol (token->text);
|
||||
symtab_addsymbol (macro->params, sym);
|
||||
return macro;
|
||||
}
|
||||
|
||||
void
|
||||
rua_macro_finish (rua_macro_t *macro, void *scanner)
|
||||
{
|
||||
if (macro->tokens) {
|
||||
if (macro->tokens->token == PRE_CONCAT
|
||||
|| ((rua_expr_t *) macro->tail)->token == PRE_CONCAT) {
|
||||
error (0, "'##' cannot appear at either end of a macro expansion");
|
||||
return;
|
||||
}
|
||||
for (auto t = macro->tokens; t; t = t->next) {
|
||||
if (t->token == '#'
|
||||
&& (!t->next
|
||||
|| t->next->token != PRE_ID
|
||||
|| !symtab_lookup (macro->params, t->next->text))) {
|
||||
error (0, "'#' is not followed by a macro parameter");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto extra = qc_yyget_extra (scanner);
|
||||
auto macro_tab = extra->macro_tab;
|
||||
auto sym = symtab_lookup (macro_tab, macro->name);
|
||||
if (sym) {
|
||||
auto o = sym->s.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->s.macro = macro;
|
||||
symtab_addsymbol (macro_tab, sym);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1243,3 +1324,11 @@ rua_endif (void *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);
|
||||
}
|
||||
|
|
|
@ -150,6 +150,15 @@ save_string (const char *str)
|
|||
return s;
|
||||
}
|
||||
|
||||
const char *
|
||||
save_substring (const char *str, int len)
|
||||
{
|
||||
char substr[len + 1];
|
||||
strncpy (substr, str, len);
|
||||
substr[len] = 0;
|
||||
return save_string (substr);
|
||||
}
|
||||
|
||||
const char *
|
||||
save_cwd (void)
|
||||
{
|
||||
|
|
|
@ -61,6 +61,7 @@ static const char * const sy_type_names[] = {
|
|||
"sy_func",
|
||||
"sy_class",
|
||||
"sy_convert",
|
||||
"sy_macro",
|
||||
};
|
||||
|
||||
const char *
|
||||
|
|
Loading…
Reference in a new issue