va_end(ap);
}
+pptoken *pptoken_make(ftepp_t *ftepp)
+{
+ pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
+ token->token = ftepp->token;
+ if (token->token == TOKEN_WHITE)
+ token->value = util_strdup(" ");
+ else
+ token->value = util_strdup(ftepp_tokval(ftepp));
+ memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
+ return token;
+}
+
+void pptoken_delete(pptoken *self)
+{
+ mem_d(self->value);
+ mem_d(self);
+}
+
ppmacro *ppmacro_new(lex_ctx ctx, const char *name)
{
ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
void ppmacro_delete(ppmacro *self)
{
+ size_t i;
+ for (i = 0; i < vec_size(self->params); ++i)
+ mem_d(self->params[i]);
vec_free(self->params);
+ for (i = 0; i < vec_size(self->output); ++i)
+ pptoken_delete(self->output[i]);
vec_free(self->output);
mem_d(self->name);
mem_d(self);
void ftepp_delete(ftepp_t *self)
{
+ size_t i;
+ for (i = 0; i < vec_size(self->macros); ++i)
+ ppmacro_delete(self->macros[i]);
vec_free(self->macros);
vec_free(self->conditions);
+ lex_close(self->lex);
mem_d(self);
}
+static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
+{
+ if (ignore_cond ||
+ !vec_size(ftepp->conditions) ||
+ vec_last(ftepp->conditions).on)
+ {
+ printf("%s", str);
+ }
+}
+
ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
{
size_t i;
return true;
}
+/* this one skips EOLs as well */
+static bool ftepp_skipallwhite(ftepp_t *ftepp)
+{
+ if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
+ return true;
+ do {
+ ftepp_next(ftepp);
+ } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
+ if (ftepp->token >= TOKEN_EOF) {
+ ftepp_error(ftepp, "unexpected end of preprocessor directive");
+ return false;
+ }
+ return true;
+}
+
/**
* The huge macro parsing code...
*/
+static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
+{
+ do {
+ ftepp_next(ftepp);
+ if (!ftepp_skipspace(ftepp))
+ return false;
+ if (ftepp->token == ')')
+ break;
+ switch (ftepp->token) {
+ case TOKEN_IDENT:
+ case TOKEN_TYPENAME:
+ case TOKEN_KEYWORD:
+ break;
+ default:
+ ftepp_error(ftepp, "unexpected token in parameter list");
+ return false;
+ }
+ vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
+ ftepp_next(ftepp);
+ if (!ftepp_skipspace(ftepp))
+ return false;
+ } while (ftepp->token == ',');
+ if (ftepp->token != ')') {
+ ftepp_error(ftepp, "expected closing paren after macro parameter list");
+ return false;
+ }
+ ftepp_next(ftepp);
+ /* skipspace happens in ftepp_define */
+ return true;
+}
+
+static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
+{
+ pptoken *ptok;
+ while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
+ ptok = pptoken_make(ftepp);
+ vec_push(macro->output, ptok);
+ ftepp_next(ftepp);
+ }
+ if (ftepp->token != TOKEN_EOL) {
+ ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
+ return false;
+ }
+ return true;
+}
+
static bool ftepp_define(ftepp_t *ftepp)
{
ppmacro *macro;
}
(void)ftepp_next(ftepp);
+
+ if (ftepp->token == '(') {
+ macro->has_params = true;
+ if (!ftepp_define_params(ftepp, macro))
+ return false;
+ }
+
if (!ftepp_skipspace(ftepp))
return false;
- if (ftepp->token != TOKEN_EOL) {
- ftepp_error(ftepp, "stray tokens after macro");
+
+ if (!ftepp_define_body(ftepp, macro))
return false;
- }
+
vec_push(ftepp->macros, macro);
return true;
}
+static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
+{
+ size_t o;
+ ftepp_next(ftepp);
+
+ if (!macro->has_params) {
+ for (o = 0; o < vec_size(macro->output); ++o) {
+ ftepp_out(ftepp, macro->output[o]->value, false);
+ }
+ return true;
+ }
+
+ if (!ftepp_skipallwhite(ftepp))
+ return false;
+ return true;
+}
+
+/**
+ * When a macro is used we have to handle parameters as well
+ * as special-concatenation via ## or stringification via #
+ */
+
/**
* #if - the FTEQCC way:
* defined(FOO) => true if FOO was #defined regardless of parameters or contents
* <numbers> => True if the number is not 0
* !<factor> => True if the factor yields false
+ * !!<factor> => ERROR on 2 or more unary nots
* <macro> => becomes the macro's FIRST token regardless of parameters
* <e> && <e> => True if both expressions are true
* <e> || <e> => True if either expression is true
* <string> => False
* <ident> => False (remember for macros the <macro> rule applies instead)
- * Unary + and - are skipped
+ * Unary + and - are weird and wrong in fteqcc so we don't allow them
* parenthesis in expressions are allowed
* parameter lists on macros are errors
* No mathematical calculations are executed
static bool ftepp_if_expr(ftepp_t *ftepp, bool *out)
{
ppmacro *macro;
+ bool wasnot = false;
if (!ftepp_skipspace(ftepp))
return false;
+ while (ftepp->token == '!') {
+ wasnot = true;
+ ftepp_next(ftepp);
+ if (!ftepp_skipspace(ftepp))
+ return false;
+ }
+
switch (ftepp->token) {
case TOKEN_IDENT:
case TOKEN_TYPENAME:
case TOKEN_KEYWORD:
if (!strcmp(ftepp_tokval(ftepp), "defined")) {
- ftepp->lex->flags.noops = true;
ftepp_next(ftepp);
if (!ftepp_skipspace(ftepp))
return false;
ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
return false;
}
- ftepp->lex->flags.noops = false;
ftepp_next(ftepp);
if (!ftepp_skipspace(ftepp))
return false;
ftepp_error(ftepp, "junk in #if");
return false;
}
+ if (wasnot)
+ *out = !*out;
+ ftepp->lex->flags.noops = false;
ftepp_next(ftepp);
if (!ftepp_skipspace(ftepp))
return false;
+ ftepp->lex->flags.noops = true;
if (ftepp->token == ')')
return true;
{
bool result = false;
- ftepp->lex->flags.noops = false;
-
memset(cond, 0, sizeof(*cond));
(void)ftepp_next(ftepp);
if (!ftepp_if_expr(ftepp, &result))
return false;
- ftepp->lex->flags.noops = true;
cond->on = result;
return true;
return true;
}
-static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
-{
- if (ignore_cond ||
- !vec_size(ftepp->conditions) ||
- vec_last(ftepp->conditions).on)
- {
- printf("%s", str);
- }
-}
-
static bool ftepp_preprocess(ftepp_t *ftepp)
{
- bool newline = true;
+ ppmacro *macro;
+ bool newline = true;
ftepp->lex->flags.preprocessing = true;
+ ftepp->lex->flags.mergelines = false;
+ ftepp->lex->flags.noops = true;
ftepp_next(ftepp);
do
newline = false;
switch (ftepp->token) {
+ case TOKEN_KEYWORD:
+ case TOKEN_IDENT:
+ case TOKEN_TYPENAME:
+ macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+ if (!macro) {
+ ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+ ftepp_next(ftepp);
+ break;
+ }
+ if (!ftepp_macro_call(ftepp, macro))
+ ftepp->token = TOKEN_ERROR;
+ break;
case '#':
if (!ftepp->newline) {
ftepp_out(ftepp, ftepp_tokval(ftepp), false);
ftepp_next(ftepp);
break;
}
+ ftepp->lex->flags.mergelines = true;
if (ftepp_next(ftepp) >= TOKEN_EOF) {
ftepp_error(ftepp, "error in preprocessor directive");
ftepp->token = TOKEN_ERROR;
}
if (!ftepp_hash(ftepp))
ftepp->token = TOKEN_ERROR;
+ ftepp->lex->flags.mergelines = false;
break;
case TOKEN_EOL:
newline = true;
}
} while (!ftepp->errors && ftepp->token < TOKEN_EOF);
+ newline = ftepp->token == TOKEN_EOF;
ftepp_delete(ftepp);
- return (ftepp->token == TOKEN_EOF);
+ return newline;
}
bool ftepp_preprocess_file(const char *filename)