mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-01-31 03:50:36 +00:00
Basic structure of ftepp
This commit is contained in:
parent
07ca2e6407
commit
4bee1bdb6e
1 changed files with 210 additions and 7 deletions
217
ftepp.c
217
ftepp.c
|
@ -24,10 +24,44 @@
|
|||
#include "lexer.h"
|
||||
|
||||
typedef struct {
|
||||
lex_file *lex;
|
||||
bool on;
|
||||
bool was_on;
|
||||
bool had_else;
|
||||
} ppcondition;
|
||||
|
||||
typedef struct {
|
||||
lex_file *lex;
|
||||
int token;
|
||||
bool newline;
|
||||
unsigned int errors;
|
||||
|
||||
ppcondition *conditions;
|
||||
} ftepp_t;
|
||||
|
||||
#define ftepp_tokval(f) ((f)->lex->tok.value)
|
||||
#define ftepp_ctx(f) ((f)->lex->tok.ctx)
|
||||
|
||||
static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
ftepp->errors++;
|
||||
|
||||
va_start(ap, fmt);
|
||||
con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
ftepp->errors++;
|
||||
|
||||
va_start(ap, fmt);
|
||||
con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
ftepp_t* ftepp_init()
|
||||
{
|
||||
|
@ -39,23 +73,192 @@ ftepp_t* ftepp_init()
|
|||
return ftepp;
|
||||
}
|
||||
|
||||
static inline int ftepp_next(ftepp_t *ftepp)
|
||||
{
|
||||
return (ftepp->token = lex_do(ftepp->lex));
|
||||
}
|
||||
|
||||
/* Important: this does not skip newlines! */
|
||||
static bool ftepp_skipspace(ftepp_t *ftepp)
|
||||
{
|
||||
while (ftepp_next(ftepp) == TOKEN_WHITE) {}
|
||||
return (ftepp->token < TOKEN_EOF);
|
||||
}
|
||||
|
||||
static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
|
||||
{
|
||||
ftepp_error(ftepp, "TODO: #if");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
|
||||
{
|
||||
ftepp_error(ftepp, "TODO: #ifdef");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ftepp_define(ftepp_t *ftepp)
|
||||
{
|
||||
ftepp_error(ftepp, "TODO: #define");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ftepp_else_allowed(ftepp_t *ftepp)
|
||||
{
|
||||
if (!vec_size(ftepp->conditions)) {
|
||||
ftepp_error(ftepp, "#else without #if");
|
||||
return false;
|
||||
}
|
||||
if (vec_last(ftepp->conditions).had_else) {
|
||||
ftepp_error(ftepp, "multiple #else for a single #if");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ftepp_hash(ftepp_t *ftepp)
|
||||
{
|
||||
ppcondition cond;
|
||||
ppcondition *pc;
|
||||
|
||||
lex_ctx ctx = ftepp_ctx(ftepp);
|
||||
|
||||
if (!ftepp_skipspace(ftepp))
|
||||
return false;
|
||||
|
||||
switch (ftepp->token) {
|
||||
case TOKEN_IDENT:
|
||||
if (!strcmp(ftepp_tokval(ftepp), "define")) {
|
||||
return ftepp_define(ftepp);
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
|
||||
if (!ftepp_ifdef(ftepp, &cond))
|
||||
return false;
|
||||
vec_push(ftepp->conditions, cond);
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
|
||||
if (!ftepp_ifdef(ftepp, &cond))
|
||||
return false;
|
||||
cond.on = !cond.on;
|
||||
vec_push(ftepp->conditions, cond);
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
|
||||
if (!ftepp_else_allowed(ftepp))
|
||||
return false;
|
||||
if (!ftepp_ifdef(ftepp, &cond))
|
||||
return false;
|
||||
pc = &vec_last(ftepp->conditions);
|
||||
pc->on = !pc->was_on && cond.on;
|
||||
pc->was_on = pc->was_on || pc->on;
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
|
||||
if (!ftepp_else_allowed(ftepp))
|
||||
return false;
|
||||
if (!ftepp_ifdef(ftepp, &cond))
|
||||
return false;
|
||||
cond.on = !cond.on;
|
||||
pc = &vec_last(ftepp->conditions);
|
||||
pc->on = !pc->was_on && cond.on;
|
||||
pc->was_on = pc->was_on || pc->on;
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
|
||||
if (!ftepp_else_allowed(ftepp))
|
||||
return false;
|
||||
if (!ftepp_if(ftepp, &cond))
|
||||
return false;
|
||||
pc = &vec_last(ftepp->conditions);
|
||||
pc->on = !pc->was_on && cond.on;
|
||||
pc->was_on = pc->was_on || pc->on;
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "if")) {
|
||||
if (!ftepp_if(ftepp, &cond))
|
||||
return false;
|
||||
vec_push(ftepp->conditions, cond);
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "else")) {
|
||||
if (!ftepp_else_allowed(ftepp))
|
||||
return false;
|
||||
pc = &vec_last(ftepp->conditions);
|
||||
pc->on = !pc->was_on;
|
||||
pc->had_else = true;
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
|
||||
if (!vec_size(ftepp->conditions)) {
|
||||
ftepp_error(ftepp, "#endif without #if");
|
||||
return false;
|
||||
}
|
||||
vec_pop(ftepp->conditions);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TOKEN_KEYWORD:
|
||||
if (!strcmp(ftepp_tokval(ftepp), "if")) {
|
||||
if (!ftepp_if(ftepp, &cond))
|
||||
return false;
|
||||
vec_push(ftepp->conditions, cond);
|
||||
return true;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
|
||||
return false;
|
||||
case TOKEN_EOL:
|
||||
ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
|
||||
return false;
|
||||
case TOKEN_EOF:
|
||||
ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ftepp_preprocess(ftepp_t *ftepp)
|
||||
{
|
||||
int token;
|
||||
bool newline = true;
|
||||
|
||||
ftepp->lex->flags.preprocessing = true;
|
||||
|
||||
for (token = lex_do(ftepp->lex); token < TOKEN_EOF; token = lex_do(ftepp->lex))
|
||||
do
|
||||
{
|
||||
switch (token) {
|
||||
case TOKEN_EOL: printf("\n"); break;
|
||||
ftepp_next(ftepp);
|
||||
|
||||
if (ftepp->token >= TOKEN_EOF)
|
||||
break;
|
||||
|
||||
ftepp->newline = newline;
|
||||
newline = false;
|
||||
|
||||
switch (ftepp->token) {
|
||||
case '#':
|
||||
if (!ftepp->newline) {
|
||||
printf("%s", ftepp_tokval(ftepp));
|
||||
break;
|
||||
}
|
||||
if (!ftepp_hash(ftepp))
|
||||
return false;
|
||||
break;
|
||||
case TOKEN_EOL:
|
||||
newline = true;
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
printf("%s", ftepp_tokval(ftepp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!ftepp->errors && ftepp->token < TOKEN_EOF);
|
||||
|
||||
return (token == TOKEN_EOF);
|
||||
return (ftepp->token == TOKEN_EOF);
|
||||
}
|
||||
|
||||
bool ftepp_preprocess_file(const char *filename)
|
||||
|
|
Loading…
Reference in a new issue