[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:
Bill Currie 2023-10-23 18:32:06 +09:00
parent 5794c68bda
commit 5cab587207
8 changed files with 163 additions and 25 deletions

View file

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

View file

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

View file

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

View file

@ -105,6 +105,8 @@ is_lvalue (const expr_t *expr)
break;
case sy_convert:
break;
case sy_macro:
break;
}
break;
case ex_temp:

View file

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

View file

@ -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 = &macro->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);
}

View file

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

View file

@ -61,6 +61,7 @@ static const char * const sy_type_names[] = {
"sy_func",
"sy_class",
"sy_convert",
"sy_macro",
};
const char *