about summary refs log tree commit diff stats
path: root/wrapperhelper/src/preproc.c
diff options
context:
space:
mode:
authorrajdakin <rajdakin@gmail.com>2024-09-30 12:31:45 +0200
committerGitHub <noreply@github.com>2024-09-30 12:31:45 +0200
commit4715ef2633534a0427a1e92006a09fde5c3faf44 (patch)
treeb3fe587555e1979d48df6666533f11496161b859 /wrapperhelper/src/preproc.c
parent703d19b256bc533d73cf4f206e5c443579aaf4c1 (diff)
downloadbox64-4715ef2633534a0427a1e92006a09fde5c3faf44.tar.gz
box64-4715ef2633534a0427a1e92006a09fde5c3faf44.zip
[WRAPPERHELPER] Added box32 and line number support in the wrapperhelper (#1890)
Diffstat (limited to 'wrapperhelper/src/preproc.c')
-rw-r--r--wrapperhelper/src/preproc.c1757
1 files changed, 916 insertions, 841 deletions
diff --git a/wrapperhelper/src/preproc.c b/wrapperhelper/src/preproc.c
index bbef6844..10ac978b 100644
--- a/wrapperhelper/src/preproc.c
+++ b/wrapperhelper/src/preproc.c
@@ -10,6 +10,10 @@
 #include "machine.h"
 #include "prepare.h"
 
+//#define LOG_OPEN
+//#define LOG_INCLUDE
+//#define LOG_CLOSE
+
 typedef struct mtoken_s {
 	enum mtoken_e {
 		MTOK_TOKEN,
@@ -79,7 +83,7 @@ static mtoken_t *mtoken_new_concat(mtoken_t *l, mtoken_t *r) { // Takes ownershi
 	return ret;
 }
 
-static inline void print_macro_tok(mtoken_t *m) {
+static inline void macro_tok_print(mtoken_t *m) {
 	switch (m->typ) {
 	case MTOK_TOKEN:
 		printf("token type %u", m->val.tok.tokt);
@@ -94,9 +98,9 @@ static inline void print_macro_tok(mtoken_t *m) {
 		
 	case MTOK_CONCAT:
 		printf("concat {");
-		print_macro_tok(m->val.concat.l);
+		macro_tok_print(m->val.concat.l);
 		printf("} {");
-		print_macro_tok(m->val.concat.r);
+		macro_tok_print(m->val.concat.r);
 		printf("}");
 		return;
 		
@@ -261,7 +265,9 @@ static int try_open_dir(preproc_t *src, string_t *filename) {
 	}
 	strcpy(fn + incl_len + 1, string_content(filename));
 	FILE *f = fopen(fn, "r");
-	// printf("Trying %s: %p\n", fn, f);
+#ifdef LOG_OPEN
+	printf("Trying %s: %p\n", fn, f);
+#endif
 	int ret;
 	if (f) {
 		char *new_dirname = strchr(fn, '/') ? strndup(fn, (size_t)(strrchr(fn, '/') - fn)) : NULL;
@@ -288,7 +294,9 @@ static int try_open_sys(preproc_t *src, string_t *filename, size_t array_off) {
 		fn[incl_len] = '/';
 		strcpy(fn + incl_len + 1, string_content(filename));
 		FILE *f = fopen(fn, "r");
-		// printf("Trying %s: %p\n", fn, f);
+#ifdef LOG_OPEN
+		printf("Trying %s: %p\n", fn, f);
+#endif
 		if (f) {
 			char *new_dirname = strchr(fn, '/') ? strndup(fn, (size_t)(strrchr(fn, '/') - fn)) : NULL;
 			int ret = vector_push(ppsource, src->prep, PREPARE_NEW_FILE(f, fn, src->cur_file, src->dirname, src->is_sys, src->cur_pathno));
@@ -365,12 +373,12 @@ preproc_t *preproc_new_file(machine_t *target, FILE *f, char *dirname, const cha
 	// Also include 'stdc-predef.h' (it will be parsed before the requested file)
 	string_t *stdc_predef = string_new_cstr("stdc-predef.h");
 	if (!stdc_predef) {
-		printf("Error: failed to create new string 'stdc-predef.h'\n");
+		log_memory("failed to create new string 'stdc-predef.h'\n");
 		preproc_del(ret);
 		return NULL;
 	}
 	if (!try_open_sys(ret, stdc_predef, 0)) {
-		printf("Error: failed to open file 'stdc-predef.h'\n");
+		log_error_nopos("failed to open file 'stdc-predef.h'\n");
 		string_del(stdc_predef);
 		preproc_del(ret);
 		return NULL;
@@ -387,22 +395,24 @@ static void preprocs_del(VECTOR(preproc) **p) {
 VECTOR_DECLARE_STATIC(preprocs, VECTOR(preproc)*)
 VECTOR_IMPL_STATIC(preprocs, preprocs_del)
 
-static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const VECTOR(preproc) *toks,
-                                       khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros);
+static VECTOR(preproc) *preproc_do_expand(loginfo_t *li, const khash_t(macros_map) *macros, const VECTOR(preproc) *toks,
+                                          khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros);
 	// Does not take any ownership, returns a vector with independent ownerships
 	// opt_used_macros is NULL in regular expansion, non-NULL in #if-expansions
-static VECTOR(preproc) *
-	proc_solve_macro(const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs,
-	                                       khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros);
+static VECTOR(preproc) *preproc_solve_macro(loginfo_t *li,
+                                            const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs,
+                                            khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros);
 	// Moves mname to solved_macros or frees mname, returns a vector with independent ownerships
 	// margs may be NULL if m->is_funlike is false
 	// May change margs if m->has_varargs, but takes no ownership
 	// opt_used_macros is NULL in regular expansion, non-NULL in #if-expansions
 
-static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs,
-                 khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) {
+static VECTOR(preproc) *preproc_solve_macro(loginfo_t *li,
+                                            const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs,
+                                            khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) {
+#define LOG_MEMORY(fmt, ...) log_memory(fmt " while expanding %s\n" __VA_ARGS__, mname)
 	if (m->is_funlike && !margs) {
-		printf("<internal error: m->is_funlike && !margs>\n");
+		log_internal(li, "m->is_funlike && !margs in preproc_solve_macro(... %s ...)\n", mname);
 		free(mname);
 		return NULL;
 	}
@@ -410,7 +420,7 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 	 && (m->nargs != vector_size(preprocs, margs)) // General case
 	 && (!m->has_varargs || (m->nargs > vector_size(preprocs, margs))) // Variadics
 	 && (m->nargs || ((vector_size(preprocs, margs) == 1) && (vector_size(preproc, vector_last(preprocs, margs)) == 0)))) { // Zero argument
-		printf("Invalid argument count for macro %s\n", mname);
+		log_error(li, "invalid argument count for macro %s\n", mname);
 		free(mname);
 		return NULL;
 	}
@@ -420,12 +430,12 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			// No varargs, so add an empty one
 			VECTOR(preproc) *marg = vector_new(preproc);
 			if (!marg) {
-				printf("Failed to create __VA_ARGS__ while expanding %s\n", mname);
+				LOG_MEMORY("failed to create __VA_ARGS__");
 				free(mname);
 				return NULL;
 			}
 			if (!vector_push(preprocs, margs, marg)) {
-				printf("Failed to add __VA_ARGS__ while expanding %s\n", mname);
+				LOG_MEMORY("failed to add __VA_ARGS__");
 				vector_del(preproc, marg);
 				free(mname);
 				return NULL;
@@ -436,13 +446,13 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			size_t size0 = vector_size(preproc, mvarg);
 			vector_for_from(preprocs, it, margs, m->nargs + 1) {
 				if (!vector_push(preproc, mvarg, ((preproc_token_t){.tokt = PPTOK_SYM, .tokv.sym = SYM_COMMA}))) {
-					printf("Failed to add comma to __VA_ARGS__ while expanding %s\n", mname);
+					LOG_MEMORY("failed to add comma to __VA_ARGS__");
 					vector_pop_nodel_slice(preproc, mvarg, vector_size(preproc, mvarg) - size0);
 					free(mname);
 					return NULL;
 				}
 				if (!vector_push_vec(preproc, mvarg, *it)) {
-					printf("Failed to add extra argument to __VA_ARGS__ while expanding %s\n", mname);
+					LOG_MEMORY("failed to add extra argument to __VA_ARGS__");
 					vector_pop_nodel_slice(preproc, mvarg, vector_size(preproc, mvarg) - size0);
 					free(mname);
 					return NULL;
@@ -453,13 +463,13 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 	// Avoid 0-allocations
 	VECTOR(preproc) **margs2 = calloc(margs ? (vector_size(preprocs, margs) ? vector_size(preprocs, margs) : 1) : 1, sizeof *margs2);
 	if (!margs2) {
-		printf("Memory error while expanding %s\n", mname);
+		LOG_MEMORY("failed to allocate expanded arguments array");
 		free(mname);
 		return NULL;
 	}
 	VECTOR(preproc) *ret = vector_new(preproc);
 	if (!ret) {
-		printf("Memory error while expanding %s\n", mname);
+		LOG_MEMORY("failed to allocate return vector");
 		free(margs2);
 		free(mname);
 		return NULL;
@@ -467,7 +477,7 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 	
 	VECTOR(mtoken) *st = vector_new_cap(mtoken, vector_size(mtoken, m->toks));
 	if (!st) {
-		printf("Memory error while expanding %s\n", mname);
+		LOG_MEMORY("failed to allocate auxiliary vector");
 		vector_del(preproc, ret);
 		free(margs2);
 		free(mname);
@@ -483,6 +493,7 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 		case MTOK_CONCAT: {
 			vector_last(mtoken, st) = mtok->val.concat.r;
 			if (!vector_push(mtoken, st, mtok->val.concat.l)) {
+				LOG_MEMORY("failed to add concatenation left m-token");
 				vector_del(preproc, ret);
 				ret = NULL;
 				goto solve_done;
@@ -495,20 +506,32 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			if (concat_cur == 2) {
 				preproc_token_t *tok1 = &vector_last(preproc, ret); // Guaranteed to exist
 				preproc_token_t *tok2 = &mtok->val.tok;
-				if (((tok1->tokt == PPTOK_IDENT) || (tok1->tokt == PPTOK_IDENT_UNEXP) || (tok1->tokt == PPTOK_NUM))
-				 && ((tok2->tokt == PPTOK_IDENT) || (tok2->tokt == PPTOK_IDENT_UNEXP) || (tok2->tokt == PPTOK_NUM))) {
+#define ISIDENT(tt) (((tt) == PPTOK_IDENT) || ((tt) == PPTOK_IDENT_UNEXP))
+#define FIND(s, c) strchr(string_content((s)), (c))
+#define ONLYDIGS(s) (!FIND((s), '.') && !FIND((s), '+') && !FIND((s), '-'))
+				if (ISIDENT(tok1->tokt) && (ISIDENT(tok2->tokt) || ((tok2->tokt == PPTOK_NUM) && ONLYDIGS(tok2->tokv.str)))) {
+					do_add = 0;
+					tok1->tokt = PPTOK_IDENT;
+					if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) {
+						LOG_MEMORY("failed to concatenate identifiers");
+						vector_del(preproc, ret);
+						ret = NULL;
+						goto solve_done;
+					}
+				} else if ((tok1->tokt == PPTOK_NUM) && (ISIDENT(tok2->tokt) || (tok2->tokt == PPTOK_NUM))) {
 					do_add = 0;
-					// TODO: check if concat is possible, what behaviour should be in the case below
-					if (tok1->tokt == PPTOK_IDENT_UNEXP) tok1->tokt = PPTOK_IDENT;
 					if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) {
-						printf("Memory error while expanding %s\n", mname);
+						LOG_MEMORY("failed to concatenate identifiers");
 						vector_del(preproc, ret);
 						ret = NULL;
 						goto solve_done;
 					}
 				} else {
-					printf("Warning: unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname);
+					log_warning(li, "unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname);
 				}
+#undef ISIDENT
+#undef FIND
+#undef ONLYDIGS
 			}
 			if (do_add) {
 				preproc_token_t tok2;
@@ -518,33 +541,34 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 				case PPTOK_NEWLINE:
 				case PPTOK_BLANK:
 				case PPTOK_START_LINE_COMMENT:
-				case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.c = tok1->tokv.c}; break;
-				case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sym = tok1->tokv.sym}; break;
+				case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.c = tok1->tokv.c}; break;
+				case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.sym = tok1->tokv.sym}; break;
 				case PPTOK_IDENT:
 				case PPTOK_IDENT_UNEXP:
 				case PPTOK_NUM: {
 					string_t *dup = string_dup(tok1->tokv.str);
 					if (!dup) {
-						printf("Failed to duplicate string while expanding %s\n", mname);
+						LOG_MEMORY("failed to duplicate string");
 						vector_del(preproc, ret);
 						ret = NULL;
 						goto solve_done;
 					}
-					tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.str = dup};
+					tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.str = dup};
 					break; }
 				case PPTOK_INCL:
 				case PPTOK_STRING: {
 					string_t *dup = string_dup(tok1->tokv.sstr);
 					if (!dup) {
-						printf("Failed to duplicate string while expanding %s\n", mname);
+						LOG_MEMORY("failed to duplicate string");
 						vector_del(preproc, ret);
 						ret = NULL;
 						goto solve_done;
 					}
-					tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr};
+					tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr};
 					break; }
 				}
 				if (!vector_push(preproc, ret, tok2)) {
+					LOG_MEMORY("failed to add token to output vector");
 					preproc_token_del(&tok2);
 					vector_del(preproc, ret);
 					ret = NULL;
@@ -562,7 +586,12 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			VECTOR(preproc) *toks_to_add;
 			if (!need_concat && !concat_cur) {
 				if (!margs2[mtok->val.argid]) {
-					margs2[mtok->val.argid] = proc_do_expand(macros, vector_content(preprocs, margs)[mtok->val.argid], solved_macros, opt_used_macros);
+					margs2[mtok->val.argid] = preproc_do_expand(li, macros, vector_content(preprocs, margs)[mtok->val.argid], solved_macros, opt_used_macros);
+					if (!margs2[mtok->val.argid]) {
+						vector_del(preproc, ret);
+						ret = NULL;
+						goto solve_done;
+					}
 				}
 				toks_to_add = margs2[mtok->val.argid];
 			} else {
@@ -572,20 +601,32 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			if (len && (concat_cur == 2)) {
 				preproc_token_t *tok1 = &vector_last(preproc, ret); // Guaranteed to exist
 				preproc_token_t *tok2 = vector_begin(preproc, toks_to_add);
-				if (((tok1->tokt == PPTOK_IDENT) || (tok1->tokt == PPTOK_IDENT_UNEXP) || (tok1->tokt == PPTOK_NUM))
-				 && ((tok2->tokt == PPTOK_IDENT) || (tok2->tokt == PPTOK_IDENT_UNEXP) || (tok2->tokt == PPTOK_NUM))) {
+#define ISIDENT(tt) (((tt) == PPTOK_IDENT) || ((tt) == PPTOK_IDENT_UNEXP))
+#define FIND(s, c) strchr(string_content((s)), (c))
+#define ONLYDIGS(s) (!FIND((s), '.') && !FIND((s), '+') && !FIND((s), '-'))
+				if (ISIDENT(tok1->tokt) && (ISIDENT(tok2->tokt) || ((tok2->tokt == PPTOK_NUM) && ONLYDIGS(tok2->tokv.str)))) {
+					tta_start = 1; --len;
+					tok1->tokt = PPTOK_IDENT;
+					if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) {
+						LOG_MEMORY("failed to concatenate identifiers");
+						vector_del(preproc, ret);
+						ret = NULL;
+						goto solve_done;
+					}
+				} else if ((tok1->tokt == PPTOK_NUM) && (ISIDENT(tok2->tokt) || (tok2->tokt == PPTOK_NUM))) {
 					tta_start = 1; --len;
-					// TODO: check if concat is possible, what behaviour should be in the case below
-					if ((tok2->tokt == PPTOK_IDENT_UNEXP) && (tok1->tokt == PPTOK_IDENT)) tok1->tokt = PPTOK_IDENT;
 					if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) {
-						printf("Memory error while expanding %s\n", mname);
+						LOG_MEMORY("failed to concatenate identifiers");
 						vector_del(preproc, ret);
 						ret = NULL;
 						goto solve_done;
 					}
 				} else {
-					printf("Warning: unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname);
+					log_warning(li, "unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname);
 				}
+#undef ISIDENT
+#undef FIND
+#undef ONLYDIGS
 			}
 			if (len) {
 				preproc_token_t tok2;
@@ -595,33 +636,34 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 					case PPTOK_NEWLINE:
 					case PPTOK_BLANK:
 					case PPTOK_START_LINE_COMMENT:
-					case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.c = tok1->tokv.c}; break;
-					case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sym = tok1->tokv.sym}; break;
+					case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.c = tok1->tokv.c}; break;
+					case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.sym = tok1->tokv.sym}; break;
 					case PPTOK_IDENT:
 					case PPTOK_IDENT_UNEXP:
 					case PPTOK_NUM: {
 						string_t *dup = string_dup(tok1->tokv.str);
 						if (!dup) {
-							printf("Failed to duplicate string while expanding %s\n", mname);
+							LOG_MEMORY("failed to duplicate string");
 							vector_del(preproc, ret);
 							ret = NULL;
 							goto solve_done;
 						}
-						tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.str = dup};
+						tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.str = dup};
 						break; }
 					case PPTOK_INCL:
 					case PPTOK_STRING: {
 						string_t *dup = string_dup(tok1->tokv.sstr);
 						if (!dup) {
-							printf("Failed to duplicate string while expanding %s\n", mname);
+							LOG_MEMORY("failed to duplicate string");
 							vector_del(preproc, ret);
 							ret = NULL;
 							goto solve_done;
 						}
-						tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr};
+						tok2 = (preproc_token_t){.tokt = tok1->tokt, .loginfo = *li, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr};
 						break; }
 					}
 					if (!vector_push(preproc, ret, tok2)) {
+						LOG_MEMORY("failed to add token to output vector");
 						preproc_token_del(&tok2);
 						vector_del(preproc, ret);
 						ret = NULL;
@@ -637,15 +679,15 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 			break; }
 			
 		case MTOK_STRINGIFY:
-			// TODO: better stringifier
 			if (concat_cur == 2) {
-				printf("Warning: unsupported concatenation with strings while expanding %s\n", mname);
+				log_warning(li, "invalid concatenation with string while expanding %s\n", mname);
 			}
+			// TODO: better stringifier
 			preproc_token_t tok2;
 			{
 				string_t *dup = string_new_cap(15);
 				if (!dup) {
-					printf("Failed to duplicate string while expanding %s\n", mname);
+					LOG_MEMORY("failed to allocate stringify string");
 					vector_del(preproc, ret);
 					ret = NULL;
 					goto solve_done;
@@ -665,9 +707,10 @@ static VECTOR(preproc) *proc_solve_macro(const khash_t(macros_map) *macros, char
 				string_add_char(dup, 'r');
 				string_add_char(dup, 'g');
 				string_add_char(dup, '>');
-				tok2 = (preproc_token_t){.tokt = PPTOK_STRING, .tokv.sstr = dup, .tokv.sisstr = 1};
+				tok2 = (preproc_token_t){.tokt = PPTOK_STRING, .loginfo = *li, .tokv.sstr = dup, .tokv.sisstr = 1};
 			}
 			if (!vector_push(preproc, ret, tok2)) {
+				LOG_MEMORY("failed to add token to output vector");
 				preproc_token_del(&tok2);
 				vector_del(preproc, ret);
 				ret = NULL;
@@ -698,7 +741,7 @@ solve_done:
 		int iret;
 		kh_put(string_set, solved_macros, mname, &iret);
 		if (iret < 0) {
-			printf("Memory error while expanding %s\n", mname);
+			LOG_MEMORY("failed to add current macro to the set of expanded macros");
 			vector_del(preproc, ret);
 			free(mname);
 			return NULL;
@@ -706,7 +749,7 @@ solve_done:
 		
 		// Next expand every remaining macros
 		vector_trim(preproc, ret);
-		VECTOR(preproc) *ret2 = proc_do_expand(macros, ret, solved_macros, opt_used_macros);
+		VECTOR(preproc) *ret2 = preproc_do_expand(li, macros, ret, solved_macros, opt_used_macros);
 		vector_del(preproc, ret);
 		ret = ret2;
 		if (!ret) return NULL; // There was an error, abort
@@ -714,7 +757,7 @@ solve_done:
 		// Finally pop mname (in case we are expanding an argument)
 		khiter_t it = kh_get(string_set, solved_macros, mname);
 		if (it == kh_end(solved_macros)) {
-			printf("Unknown error while expanding a macro\n");
+			log_internal(li, "failed to find current macro in the set of expanded macros while expanding %s\n" , mname);
 			vector_del(preproc, ret);
 			return NULL;
 		}
@@ -724,13 +767,17 @@ solve_done:
 #pragma GCC diagnostic pop
 		kh_del(string_set, solved_macros, it);
 	}
+#undef LOG_MEMORY
 	
 	return ret;
 }
-static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const VECTOR(preproc) *toks,
-                                       khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) {
+static VECTOR(preproc) *preproc_do_expand(loginfo_t *li, const khash_t(macros_map) *macros, const VECTOR(preproc) *toks,
+                                          khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) {
 	VECTOR(preproc) *toks2 = vector_new_cap(preproc, vector_size(preproc, toks));
-	if (!toks2) return NULL;
+	if (!toks2) {
+		log_memory("failed to allocate full macro expansion auxiliary vector\n");
+		return NULL;
+	}
 	vector_for(preproc, tok, toks) {
 		preproc_token_t tok2;
 		switch (tok->tokt) {
@@ -738,35 +785,38 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 		case PPTOK_NEWLINE:
 		case PPTOK_BLANK:
 		case PPTOK_START_LINE_COMMENT:
-		case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.c = tok->tokv.c}; break;
-		case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.sym = tok->tokv.sym}; break;
+		case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok->tokt, .loginfo = tok->loginfo, .tokv.c = tok->tokv.c}; break;
+		case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok->tokt, .loginfo = tok->loginfo, .tokv.sym = tok->tokv.sym}; break;
 		case PPTOK_IDENT:
 		case PPTOK_IDENT_UNEXP:
 		case PPTOK_NUM: {
 			string_t *dup = string_dup(tok->tokv.str);
 			if (!dup) {
-				printf("Failed to duplicate string during full macro expansion\n");
+				log_memory("failed to duplicate string during full macro expansion\n");
 				vector_del(preproc, toks2);
 				return NULL;
 			}
-			tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.str = dup};
+			tok2 = (preproc_token_t){.tokt = tok->tokt, .loginfo = tok->loginfo, .tokv.str = dup};
 			break; }
 		case PPTOK_INCL:
 		case PPTOK_STRING: {
 			string_t *dup = string_dup(tok->tokv.sstr);
 			if (!dup) {
-				printf("Failed to duplicate string during full macro expansion\n");
+				log_memory("failed to duplicate string during full macro expansion\n");
 				vector_del(preproc, toks2);
 				return NULL;
 			}
-			tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.sstr = dup, .tokv.sisstr = tok->tokv.sisstr};
+			tok2 = (preproc_token_t){.tokt = tok->tokt, .loginfo = tok->loginfo, .tokv.sstr = dup, .tokv.sisstr = tok->tokv.sisstr};
 			break; }
 		}
 		vector_push(preproc, toks2, tok2); // cap > size, thus this always succeed
 	}
 	
 	VECTOR(preproc) *ret = vector_new_cap(preproc, vector_size(preproc, toks));
-	if (!ret) return NULL;
+	if (!ret) {
+		log_memory("failed to allocate full macro expansion result vector\n");
+		return NULL;
+	}
 	vector_for(preproc, tok, toks2) {
 		switch (tok->tokt) {
 		case PPTOK_IDENT: {
@@ -792,14 +842,14 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 					char *mname2 = strdup(string_content(mname));
 					if (!mname2) {
 						// Abort
-						printf("Failed to add %s to the list of used macros (strdup returned NULL)\n", string_content(mname));
+						log_memory("failed to add %s to the list of used macros (strdup returned NULL)\n", string_content(mname));
 						goto expand_done;
 					}
 					kh_put(string_set, opt_used_macros, mname2, &iret);
 					if (iret < 0) {
 						// Abort
 						free(mname2);
-						printf("Failed to add %s to the list of used macros (kh_put ireturned %d)\n", string_content(mname), iret);
+						log_memory("failed to add %s to the list of used macros (kh_put ireturned %d)\n", string_content(mname), iret);
 						goto expand_done;
 					} else if (iret == 0) {
 						// Just free mname2, it was already present
@@ -810,13 +860,13 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 					string_t *num_str = string_new_cap(1);
 					if (!num_str) {
 						// Abort
-						printf("Failed to create defined() output\n");
+						log_memory("failed to create defined() output\n");
 						goto expand_done;
 					}
 					khiter_t it = kh_get(macros_map, macros, string_content(mname));
 					string_add_char(num_str, (it == kh_end(macros)) ? '0' : '1');
-					if (!vector_push(preproc, ret, ((preproc_token_t){.tokt = PPTOK_NUM, .tokv.str = num_str}))) {
-						printf("Failed to add defined() to the output\n");
+					if (!vector_push(preproc, ret, ((preproc_token_t){.tokt = PPTOK_NUM, .loginfo = *li, .tokv.str = num_str}))) {
+						log_memory("failed to add defined() result to the output\n");
 						string_del(num_str);
 						goto expand_done;
 					}
@@ -842,7 +892,7 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 							unsigned depth = 1;
 							VECTOR(preprocs) *margs = vector_new(preprocs);
 							if (!margs) {
-								printf("Memory error (parsing macro use %s)\n", string_content(tok->tokv.str));
+								log_memory("failed to allocate macro arguments for macro %s during full macro expansion\n", string_content(tok->tokv.str));
 								if (margs) vector_del(preprocs, margs);
 								goto expand_done;
 							}
@@ -853,7 +903,7 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 								++tok2;
 								if ((depth == 1) && (tok2->tokt == PPTOK_SYM) && (tok2->tokv.sym == SYM_COMMA)) {
 									// Possible optimization: emplace NULL if vector_size(marg) == 0
-									// This would avoid allocating a new vector, but needs support in proc_solve_macro
+									// This would avoid allocating a new vector, but needs support in preproc_solve_macro
 									vector_trim(preproc, marg);
 									if (!vector_push(preprocs, margs, marg)) goto gather_args_err_mem;
 									marg = vector_new(preproc);
@@ -878,7 +928,7 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 							
 							if (0) {
 							gather_args_err_mem:
-								printf("Memory error (parsing macro use %s)\n", string_content(tok->tokv.str));
+								log_memory("failed to gather macro arguments for macro %s during full macro expansion\n", string_content(tok->tokv.str));
 								if (marg) vector_del(preproc, marg);
 								vector_del(preprocs, margs);
 								goto expand_done;
@@ -888,14 +938,14 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 							char *mname = string_steal(tok->tokv.str);
 							tok = tok2;
 							
-							VECTOR(preproc) *expanded = proc_solve_macro(macros, mname, m, margs, solved_macros, opt_used_macros);
+							VECTOR(preproc) *expanded = preproc_solve_macro(li, macros, mname, m, margs, solved_macros, opt_used_macros);
 							vector_del(preprocs, margs);
 							if (!expanded) {
 								// Error expanding the macro
 								goto expand_done;
 							}
 							if (!vector_push_vec(preproc, ret, expanded)) {
-								printf("Memory error (pushing expanded macro to expanded macro)\n");
+								log_memory("pushing expanded macro to full macro expansion\n");
 								vector_del(preproc, expanded);
 								goto expand_done;
 							}
@@ -904,13 +954,13 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 							break;
 						}
 					} else {
-						VECTOR(preproc) *expanded = proc_solve_macro(macros, string_steal(tok->tokv.str), m, NULL, solved_macros, opt_used_macros);
+						VECTOR(preproc) *expanded = preproc_solve_macro(li, macros, string_steal(tok->tokv.str), m, NULL, solved_macros, opt_used_macros);
 						if (!expanded) {
 							++tok; // Current token is already freed (string stolen)
 							goto expand_done;
 						}
 						if (!vector_push_vec(preproc, ret, expanded)) {
-							printf("Failed to extend output for full macro expansion\n");
+							log_memory("failed to add macro expansion in output vector of full macro expansion\n");
 							++tok; // Current token is already freed (string stolen)
 							goto expand_done;
 						}
@@ -933,7 +983,7 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 		case PPTOK_START_LINE_COMMENT:
 		case PPTOK_EOF:
 			if (!vector_push(preproc, ret, *tok)) {
-				printf("Failed to duplicate token during full macro expansion\n");
+				log_memory("failed to add token to output vector during full macro expansion\n");
 				goto expand_done;
 			}
 			break;
@@ -969,6 +1019,8 @@ static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const
 #define OPLVL_AOR 9  // Bitwise (Arithmetic)
 #define OPLVL_BAN 10 // Boolean
 #define OPLVL_BOR 11 // Boolean
+#define OPLVL_ALL 12 // Evaluate the entire stack
+#define OPLVL_ALS 13 // Evaluate the entire stack, (don't update the states (ignored))
 #define OPTYP_MUL 1
 #define OPTYP_DIV 2
 #define OPTYP_MOD 3
@@ -1003,22 +1055,242 @@ typedef struct preproc_eval_aux_s {
 	int64_t v0;
 	unsigned st1;
 	const preproc_token_t *v1;
-	int64_t v2;
-	int64_t v3;
-	int64_t v4;
-	int64_t v5;
-	int64_t v6;
-	int64_t v7;
-	int64_t v8;
-	int64_t v9;
+	int64_t v2; loginfo_t li2;
+	int64_t v3; loginfo_t li3;
+	int64_t v4; loginfo_t li4;
+	int64_t v5; loginfo_t li5;
+	int64_t v6; loginfo_t li6;
+	int64_t v7; loginfo_t li7;
+	int64_t v8; loginfo_t li8;
+	int64_t v9; loginfo_t li9;
+	loginfo_t libool;
 	unsigned n_colons; // Number of ':' expected (needs to skip to the end), ie number of ternary where the truth-y branch is taken
 } preproc_eval_aux_t;
 VECTOR_DECLARE_STATIC(ppeaux, preproc_eval_aux_t)
 VECTOR_IMPL_STATIC(ppeaux, (void))
+static int64_t eval_stack(preproc_eval_aux_t *st, loginfo_t li, int max_lv, int optype, _Bool is_unsigned, int *success) {
+	if (!li.lineno_end) {
+		li.lineno_end = li.lineno;
+		li.colno_end = li.colno;
+	}
+	
+	int64_t acc = st->v0;
+	st->st0 = 0;
+	
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (st->stX)
+	if (st->st2 == OPTYP_MUL) {
+		acc = st->v2 * acc;
+		st->st2 = 0;
+		li.lineno = st->li2.lineno;
+		li.colno = st->li2.colno;
+	} else if (st->st2 == OPTYP_DIV) {
+		if (!acc) {
+			log_error(&li, "division by zero during #if evaluation\n");
+			*success = 0;
+			return 0;
+		}
+		acc = st->v2 / acc;
+		st->st2 = 0;
+		li.lineno = st->li2.lineno;
+		li.colno = st->li2.colno;
+	} else if (st->st2 == OPTYP_MOD) {
+		if (!acc) {
+			log_error(&li, "division by zero during #if evaluation\n");
+			*success = 0;
+			return 0;
+		}
+		acc = st->v2 % acc;
+		st->st2 = 0;
+		li.lineno = st->li2.lineno;
+		li.colno = st->li2.colno;
+	} else if (st->st2) {
+		log_internal(&st->li2, "unknown st2 %d during #if evaluation\n", st->st2);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 2) {
+		st->st2 = optype;
+		st->v2 = acc;
+		st->li2 = li;
+		return acc;
+	}
+	if (st->st3 == OPTYP_ADD) {
+		acc = st->v3 + acc;
+		st->st3 = 0;
+		li.lineno = st->li3.lineno;
+		li.colno = st->li3.colno;
+	} else if (st->st3 == OPTYP_SUB) {
+		acc = st->v3 - acc;
+		st->st3 = 0;
+		li.lineno = st->li3.lineno;
+		li.colno = st->li3.colno;
+	} else if (st->st3) {
+		log_internal(&st->li3, "unknown st3 %d during #if evaluation\n", st->st3);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 3) {
+		st->st3 = optype;
+		st->v3 = acc;
+		st->li3 = li;
+		return acc;
+	}
+	if (st->st4 == OPTYP_LSL) {
+		acc = st->v4 << acc;
+		st->st4 = 0;
+		li.lineno = st->li4.lineno;
+		li.colno = st->li4.colno;
+	} else if (st->st4 == OPTYP_LSR) {
+		acc = is_unsigned ? (int64_t)((uint64_t)st->v4 >> (uint64_t)acc) : (st->v4 >> acc);
+		st->st4 = 0;
+		li.lineno = st->li4.lineno;
+		li.colno = st->li4.colno;
+	} else if (st->st4) {
+		log_internal(&st->li4, "unknown st4 %d during #if evaluation\n", st->st4);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 4) {
+		st->st4 = optype;
+		st->v4 = acc;
+		st->li4 = li;
+		return acc;
+	}
+	if (st->st5 == OPTYP_LET) {
+		acc = is_unsigned ? ((uint64_t)st->v5 < (uint64_t)acc) : (st->v5 < acc);
+		is_unsigned = 0;
+		st->st5 = 0;
+		li.lineno = st->li5.lineno;
+		li.colno = st->li5.colno;
+	} else if (st->st5 == OPTYP_LEE) {
+		acc = is_unsigned ? ((uint64_t)st->v5 <= (uint64_t)acc) : (st->v5 <= acc);
+		is_unsigned = 0;
+		st->st5 = 0;
+		li.lineno = st->li5.lineno;
+		li.colno = st->li5.colno;
+	} else if (st->st5 == OPTYP_GRT) {
+		acc = is_unsigned ? ((uint64_t)st->v5 > (uint64_t)acc) : (st->v5 > acc);
+		is_unsigned = 0;
+		st->st5 = 0;
+		li.lineno = st->li5.lineno;
+		li.colno = st->li5.colno;
+	} else if (st->st5 == OPTYP_GRE) {
+		acc = is_unsigned ? ((uint64_t)st->v5 >= (uint64_t)acc) : (st->v5 >= acc);
+		is_unsigned = 0;
+		st->st5 = 0;
+		li.lineno = st->li5.lineno;
+		li.colno = st->li5.colno;
+	} else if (st->st5) {
+		log_internal(&st->li5, "unknown st5 %d during #if evaluation\n", st->st5);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 5) {
+		st->st5 = optype;
+		st->v5 = acc;
+		st->li5 = li;
+		return acc;
+	}
+	if (st->st6 == OPTYP_EQU) {
+		acc = st->v6 == acc;
+		st->st6 = 0;
+		li.lineno = st->li6.lineno;
+		li.colno = st->li6.colno;
+	} else if (st->st6 == OPTYP_NEQ) {
+		acc = st->v6 != acc;
+		st->st6 = 0;
+		li.lineno = st->li6.lineno;
+		li.colno = st->li6.colno;
+	} else if (st->st6) {
+		log_internal(&st->li6, "unknown st6 %d during #if evaluation\n", st->st6);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 6) {
+		st->st6 = optype;
+		st->v6 = acc;
+		st->li6 = li;
+		return acc;
+	}
+	if (st->st7 == OPTYP_AAN) {
+		acc = st->v7 & acc;
+		st->st7 = 0;
+		li.lineno = st->li7.lineno;
+		li.colno = st->li7.colno;
+	} else if (st->st7) {
+		log_internal(&st->li7, "unknown st7 %d during #if evaluation\n", st->st7);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 7) {
+		st->st7 = optype;
+		st->v7 = acc;
+		st->li7 = li;
+		return acc;
+	}
+	if (st->st8 == OPTYP_XOR) {
+		acc = st->v8 ^ acc;
+		st->st8 = 0;
+		li.lineno = st->li8.lineno;
+		li.colno = st->li8.colno;
+	} else if (st->st8) {
+		log_internal(&st->li8, "unknown st8 %d during #if evaluation\n", st->st8);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 8) {
+		st->st8 = optype;
+		st->v8 = acc;
+		st->li8 = li;
+		return acc;
+	}
+	if (st->st9 == OPTYP_AOR) {
+		acc = st->v9 | acc;
+		st->st9 = 0;
+		li.lineno = st->li9.lineno;
+		li.colno = st->li9.colno;
+	} else if (st->st9) {
+		log_internal(&st->li9, "unknown st9 %d during #if evaluation\n", st->st9);
+		*success = 0;
+		return 0;
+	}
+	if (max_lv == 9) {
+		st->st9 = optype;
+		st->v9 = acc;
+		st->li9 = li;
+		return acc;
+	}
+	if (max_lv == 10) { // OPLVL == OPLVL_BAN, OPTYP == OPTYP_BAN
+		li.lineno = st->libool.lineno;
+		li.colno = st->libool.colno;
+		st->libool = li;
+		return acc;
+	}
+	if (max_lv == 11) { // OPLVL == OPLVL_BOR, OPTYP == OPTYP_BOR
+		li.lineno = st->libool.lineno;
+		li.colno = st->libool.colno;
+		st->libool = li;
+		return acc;
+	}
+	if (st->st_bool) {
+		acc = !!acc;
+		st->st_bool = 0;
+	}
+	if ((max_lv == OPLVL_ALL) || (max_lv == OPLVL_ALS)) {
+		return acc;
+	}
+#pragma GCC diagnostic pop
+	log_internal(&li, "invalid max level %d in preprocessor partial expression evaluation\n", max_lv);
+	*success = 0;
+	return acc;
+}
 static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_is_32bits) {
+	int success;
 	VECTOR(ppeaux) *stack = vector_new_cap(ppeaux, 1);
 	if (!stack) {
-		printf("Failed to allocate #if evaluation stack vector\n");
+		log_memory("failed to allocate #if evaluation stack vector\n");
 		*aux_ret = 0;
 		return 0;
 	}
@@ -1029,13 +1301,13 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 		return 0;
 	}
 	vector_push(ppeaux, stack, (preproc_eval_aux_t){0}); // vector_cap >= 1
-	int64_t acc; _Bool is_unsigned = 0;
+	int64_t acc; loginfo_t li0; _Bool is_unsigned = 0;
 	vector_for(preproc, tok, cond) {
 		if (tok->tokt == PPTOK_NUM) {
 			// Evaluate token as an integer if st0 == 0, error otherwise
 			if (vector_last(ppeaux, stack).st0 == 0) {
 				num_constant_t cst;
-				if (!num_constant_convert(tok->tokv.str, &cst, ptr_is_32bits)) {
+				if (!num_constant_convert(&tok->loginfo, tok->tokv.str, &cst, ptr_is_32bits)) {
 					goto eval_fail;
 				}
 				switch (cst.typ) {
@@ -1047,21 +1319,23 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 				case NCT_DOUBLE:
 				case NCT_LDOUBLE:
 				default:
-					printf("Number '%s' is not a valid integer (during #if evaluation)\n", string_content(tok->tokv.str));
+					log_error(&tok->loginfo, "number '%s' is not a valid integer (during #if evaluation)\n", string_content(tok->tokv.str));
 					goto eval_fail;
 				}
+				li0 = tok->loginfo;
 				goto push_acc_to_st0;
 			} else {
-				printf("Invalid number during #if evaluation\n");
+				log_error(&tok->loginfo, "unexpected number during #if evaluation\n");
 				goto eval_fail;
 			}
 		} else if ((tok->tokt == PPTOK_IDENT) || (tok->tokt == PPTOK_IDENT_UNEXP)) {
 			// Evaluate token as 0 if st0 == 0, error otherwise
 			if (vector_last(ppeaux, stack).st0 == 0) {
 				acc = 0;
+				li0 = tok->loginfo;
 				goto push_acc_to_st0;
 			} else {
-				printf("Invalid ident '%s' during #if evaluation\n", string_content(tok->tokv.str));
+				log_error(&tok->loginfo, "unexpected ident '%s' during #if evaluation\n", string_content(tok->tokv.str));
 				goto eval_fail;
 			}
 		} else if (tok->tokt == PPTOK_SYM) {
@@ -1070,30 +1344,26 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 			switch (tok->tokv.sym) {
 			case SYM_TILDE:
 				// Unary (st0 == 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					// Unary, prepare lv1 then continue to the next token
-					if (!(vector_last(ppeaux, stack).st1++)) {
-						// Also update v1, since this is the first level 1 operator
-						vector_last(ppeaux, stack).v1 = tok;
-					}
-					goto done_partial_eval;
-				} else {
-					printf("Invalid %snary '%s' in #if expression\n", "u", sym2str[tok->tokv.sym]);
+				if (vector_last(ppeaux, stack).st0 != 0) {
+					log_error(&tok->loginfo, "unexpected unary '~' in #if expression\n");
 					goto eval_fail;
 				}
+				if (!(vector_last(ppeaux, stack).st1++)) {
+					// Also update v1, since this is the first level 1 operator
+					vector_last(ppeaux, stack).v1 = tok;
+				}
+				goto done_partial_eval;
 			case SYM_EXCL:
 				// Unary (st0 == 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					// Unary, prepare lv1 then continue to the next token
-					if (!(vector_last(ppeaux, stack).st1++)) {
-						// Also update v1, since this is the first level 1 operator
-						vector_last(ppeaux, stack).v1 = tok;
-					}
-					goto done_partial_eval;
-				} else {
-					printf("Invalid %snary '%s' in #if expression\n", "u", sym2str[tok->tokv.sym]);
+				if (vector_last(ppeaux, stack).st0 != 0) {
+					log_error(&tok->loginfo, "unexpected unary '!' in #if expression\n");
 					goto eval_fail;
 				}
+				if (!(vector_last(ppeaux, stack).st1++)) {
+					// Also update v1, since this is the first level 1 operator
+					vector_last(ppeaux, stack).v1 = tok;
+				}
+				goto done_partial_eval;
 			case SYM_PLUS:
 				// May be unary (st0 == 0) or binary (st0 != 0)
 				if (vector_last(ppeaux, stack).st0 == 0) {
@@ -1106,7 +1376,7 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 				} else {
 					op_lvl = OPLVL_ADD;
 					op_typ = OPTYP_ADD;
-					break;
+					goto add_binop;
 				}
 			case SYM_DASH:
 				// May be unary (st0 == 0) or binary (st0 != 0)
@@ -1120,363 +1390,222 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 				} else {
 					op_lvl = OPLVL_SUB;
 					op_typ = OPTYP_SUB;
-					break;
+					goto add_binop;
 				}
 			case SYM_STAR:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_MUL;
-					op_typ = OPTYP_MUL;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_MUL;
+				op_typ = OPTYP_MUL;
+				goto add_binop;
 			case SYM_SLASH:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_DIV;
-					op_typ = OPTYP_DIV;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_DIV;
+				op_typ = OPTYP_DIV;
+				goto add_binop;
 			case SYM_PERCENT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_MOD;
-					op_typ = OPTYP_MOD;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_MOD;
+				op_typ = OPTYP_MOD;
+				goto add_binop;
 			case SYM_HAT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_XOR;
-					op_typ = OPTYP_XOR;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_XOR;
+				op_typ = OPTYP_XOR;
+				goto add_binop;
 			case SYM_AMP:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_AAN;
-					op_typ = OPTYP_AAN;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_AAN;
+				op_typ = OPTYP_AAN;
+				goto add_binop;
 			case SYM_PIPE:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_AOR;
-					op_typ = OPTYP_AOR;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_AOR;
+				op_typ = OPTYP_AOR;
+				goto add_binop;
 			case SYM_EQEQ:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_EQU;
-					op_typ = OPTYP_EQU;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_EQU;
+				op_typ = OPTYP_EQU;
+				goto add_binop;
 			case SYM_EXCLEQ:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_NEQ;
-					op_typ = OPTYP_NEQ;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_NEQ;
+				op_typ = OPTYP_NEQ;
+				goto add_binop;
 			case SYM_LT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_LET;
-					op_typ = OPTYP_LET;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_LET;
+				op_typ = OPTYP_LET;
+				goto add_binop;
 			case SYM_GT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_GRT;
-					op_typ = OPTYP_GRT;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_GRT;
+				op_typ = OPTYP_GRT;
+				goto add_binop;
 			case SYM_LTEQ:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_LEE;
-					op_typ = OPTYP_LEE;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_LEE;
+				op_typ = OPTYP_LEE;
+				goto add_binop;
 			case SYM_GTEQ:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_GRE;
-					op_typ = OPTYP_GRE;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_GRE;
+				op_typ = OPTYP_GRE;
+				goto add_binop;
 			case SYM_AMPAMP:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_BAN;
-					op_typ = OPTYP_BAN;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_BAN;
+				op_typ = OPTYP_BAN;
+				goto add_binop;
 			case SYM_PIPEPIPE:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_BOR;
-					op_typ = OPTYP_BOR;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_BOR;
+				op_typ = OPTYP_BOR;
+				goto add_binop;
 			case SYM_LTLT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_LSL;
-					op_typ = OPTYP_LSL;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_LSL;
+				op_typ = OPTYP_LSL;
+				goto add_binop;
 			case SYM_GTGT:
 				// Binary (st0 != 0)
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]);
-					goto eval_fail;
-				} else {
-					op_lvl = OPLVL_LSR;
-					op_typ = OPTYP_LSR;
-					break;
-				}
+				if (vector_last(ppeaux, stack).st0 == 0) goto invalid_binop;
+				op_lvl = OPLVL_LSR;
+				op_typ = OPTYP_LSR;
+				goto add_binop;
 				
 			case SYM_LPAREN:
 				// May be placed anywhere a constant is expected (st0 == 0) and simply pushes a new stack
-				if (vector_last(ppeaux, stack).st0 == 0) {
-					if (!vector_push(ppeaux, stack, (preproc_eval_aux_t){0})) {
-						printf("Failed to push to the stack during #if evaluation\n");
-						goto eval_fail;
-					}
-					goto done_partial_eval;
-				} else {
-					printf("Invalid opening parenthesis in #if expression\n");
-					goto eval_fail;
-				}
-			case SYM_RPAREN:
-			case SYM_QUESTION:
-				// May be placed anywhere and simply pushes a new stack (paren) or more complex operation (ternary)
-				if ((tok->tokv.sym == SYM_RPAREN) && (vector_size(ppeaux, stack) == 1)) {
-					printf("Invalid closing parenthesis during #if evaluation\n");
-					goto eval_fail;
-				}
-				if (!vector_last(ppeaux, stack).st0) {
-					printf("Invalid %s during #if evaluation\n", (tok->tokv.sym == SYM_RPAREN) ? "closing parenthesis" : "question mark");
-					goto eval_fail;
-				}
-				
-				// Evaluate the top of the stack, then pop
-				acc = vector_last(ppeaux, stack).v0;
-				vector_last(ppeaux, stack).st0 = 0;
-				
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX)
-				if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) {
-					acc = vector_last(ppeaux, stack).v2 * acc;
-					vector_last(ppeaux, stack).st2 = 0;
-				} else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) {
-					if (!acc) {
-						printf("Error: division by zero\n");
-						goto eval_fail;
-					}
-					acc = vector_last(ppeaux, stack).v2 / acc;
-					vector_last(ppeaux, stack).st2 = 0;
-				} else if (vector_last(ppeaux, stack).st2 == OPTYP_MOD) {
-					if (!acc) {
-						printf("Error: division by zero\n");
-						goto eval_fail;
-					}
-					acc = vector_last(ppeaux, stack).v2 % acc;
-					vector_last(ppeaux, stack).st2 = 0;
-				} else if (vector_last(ppeaux, stack).st2) {
-					printf("<internal error> Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2);
+				if (vector_last(ppeaux, stack).st0 != 0) {
+					log_error(&tok->loginfo, "unexpected opening parenthesis in #if expression\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) {
-					acc = vector_last(ppeaux, stack).v3 + acc;
-					vector_last(ppeaux, stack).st3 = 0;
-				} else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) {
-					acc = vector_last(ppeaux, stack).v3 - acc;
-					vector_last(ppeaux, stack).st3 = 0;
-				} else if (vector_last(ppeaux, stack).st3) {
-					printf("<internal error> Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3);
+				if (!vector_push(ppeaux, stack, (preproc_eval_aux_t){0})) {
+					log_memory("failed to push a new stack during #if evaluation\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) {
-					acc = vector_last(ppeaux, stack).v4 << acc;
-					vector_last(ppeaux, stack).st4 = 0;
-				} else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) {
-					acc = is_unsigned ? (int64_t)((uint64_t)vector_last(ppeaux, stack).v4 >> (uint64_t)acc) : (vector_last(ppeaux, stack).v4 >> acc);
-					vector_last(ppeaux, stack).st4 = 0;
-				} else if (vector_last(ppeaux, stack).st4) {
-					printf("<internal error> Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4);
+				goto done_partial_eval;
+			case SYM_RPAREN:
+				// May be placed anywhere after a constant (st0 != 0)
+				if (vector_size(ppeaux, stack) == 1) {
+					log_error(&tok->loginfo, "unexpected symbol ')' (parenthesis not opened) during #if evaluation\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st5 == OPTYP_LET) {
-					acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 < (uint64_t)acc) : (vector_last(ppeaux, stack).v5 < acc);
-					is_unsigned = 0;
-					vector_last(ppeaux, stack).st5 = 0;
-				} else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) {
-					acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 <= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 <= acc);
-					is_unsigned = 0;
-					vector_last(ppeaux, stack).st5 = 0;
-				} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) {
-					acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 > (uint64_t)acc) : (vector_last(ppeaux, stack).v5 > acc);
-					is_unsigned = 0;
-					vector_last(ppeaux, stack).st5 = 0;
-				} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) {
-					acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 >= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 >= acc);
-					is_unsigned = 0;
-					vector_last(ppeaux, stack).st5 = 0;
-				} else if (vector_last(ppeaux, stack).st5) {
-					printf("<internal error> Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5);
+				if (vector_last(ppeaux, stack).n_colons) {
+					log_error(&tok->loginfo, "unexpected symbol ')', expected symbol ':' during #if evaluation\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) {
-					acc = vector_last(ppeaux, stack).v6 == acc;
-					vector_last(ppeaux, stack).st6 = 0;
-				} else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) {
-					acc = vector_last(ppeaux, stack).v6 != acc;
-					vector_last(ppeaux, stack).st6 = 0;
-				} else if (vector_last(ppeaux, stack).st6) {
-					printf("<internal error> Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6);
+				if (!vector_last(ppeaux, stack).st0) {
+					log_error(&tok->loginfo, "unexpected symbol ')' during #if evaluation\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) {
-					acc = vector_last(ppeaux, stack).v7 & acc;
-					vector_last(ppeaux, stack).st7 = 0;
-				} else if (vector_last(ppeaux, stack).st7) {
-					printf("<internal error> Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7);
+				
+			eval_rparen:
+				// Evaluate the top of the stack, then pop
+				success = 1;
+				acc = eval_stack(&vector_last(ppeaux, stack), li0, OPLVL_ALS, 0, is_unsigned, &success);
+				if (!success) {
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) {
-					acc = vector_last(ppeaux, stack).v8 ^ acc;
-					vector_last(ppeaux, stack).st8 = 0;
-				} else if (vector_last(ppeaux, stack).st8) {
-					printf("<internal error> Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8);
+				
+			eval_rparen_acc:
+				vector_pop(ppeaux, stack);
+				goto push_acc_to_st0;
+				
+			case SYM_QUESTION: {
+				// May be placed anywhere and may skip some tokens depending on acc
+				if (!vector_last(ppeaux, stack).st0) {
+					log_error(&tok->loginfo, "unexpected ternary operator during #if evaluation\n");
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) {
-					acc = vector_last(ppeaux, stack).v9 | acc;
-					vector_last(ppeaux, stack).st9 = 0;
-				} else if (vector_last(ppeaux, stack).st9) {
-					printf("<internal error> Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9);
+				
+				// Evaluate the top of the stack, then increase n_colons or skip tokens
+				success = 1;
+				acc = eval_stack(&vector_last(ppeaux, stack), li0, OPLVL_ALL, 0, is_unsigned, &success);
+				if (!success) {
 					goto eval_fail;
 				}
-				if (vector_last(ppeaux, stack).st_bool) {
-					acc = !!acc;
-					vector_last(ppeaux, stack).st_bool = 0;
-				}
-#pragma GCC diagnostic pop
 				
-				if (tok->tokv.sym == SYM_RPAREN) {
-					vector_pop(ppeaux, stack);
-					goto push_acc_to_st0;
+			eval_question_acc:
+				is_unsigned = 0;
+				if (acc) {
+					// Increase n_colons
+					++vector_last(ppeaux, stack).n_colons;
+					goto done_partial_eval;
 				} else {
-				eval_question_acc:
-					is_unsigned = 0;
-					if (acc) {
-						// Increase n_colons
-						++vector_last(ppeaux, stack).n_colons;
-						goto done_partial_eval;
-					} else {
-						// Skip to the corresponding colon
-						unsigned nquestions = 0, nparens = 0;
-						// Note that we don't really care about the content of the ignored part; it may be syntaxically incorrect
-						for (++tok; tok < vector_end(preproc, cond); ++tok) {
-							if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
-								++nparens;
-							} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
-								if (nparens) --nparens;
-								else {
-									printf("Unclosed parenthesis in #if evaluation\n");
-									goto eval_fail;
-								}
-							} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
-								++nquestions;
-							} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_COLON)) {
-								if (nquestions) --nquestions;
-								else break;
-							}
-						}
-						if (tok == vector_end(preproc, cond)) {
-							printf("Unfinished ternary operator in #if evaluation\n");
-							goto eval_fail;
-						}
-						goto done_partial_eval;
-					}
-				}
-			case SYM_COLON:
-				if (vector_last(ppeaux, stack).n_colons) {
-					// Decrease n_colons and skip to the rparen/end of vector
-					if (vector_size(ppeaux, stack) == 1) {
-						goto done_complete_stack; // No rparen, skip to the end of the vector
-					}
-					// Skip to the next rparen; also, we skip the correct count of ':' since we don't care about those anymore
-					// --vector_last(ppeaux, stack).n_colons;
-					unsigned nparens = 0;
+					// Skip to the corresponding colon
+					unsigned nquestions = 0, nparens = 0;
 					// Note that we don't really care about the content of the ignored part; it may be syntaxically incorrect
 					for (++tok; tok < vector_end(preproc, cond); ++tok) {
 						if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
 							++nparens;
 						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
 							if (nparens) --nparens;
+							else {
+								log_error(&tok->loginfo, "unexpected symbol ')', expected ':' during #if evaluation\n");
+								goto eval_fail;
+							}
+						} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
+							++nquestions;
+						} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_COLON)) {
+							if (nquestions) --nquestions;
 							else break;
 						}
 					}
 					if (tok == vector_end(preproc, cond)) {
-						printf("Unfinished ternary operator in #if evaluation\n");
+						log_error(&tok->loginfo, "ternary operator never finished during #if evaluation\n");
 						goto eval_fail;
 					}
-					--tok;
 					goto done_partial_eval;
-				} else {
-					printf("Invalid colon symbol during #if evaluation\n");
+				}
+			}
+			case SYM_COLON: {
+				if (!vector_last(ppeaux, stack).n_colons) {
+					log_error(&tok->loginfo, "unexpected symbol ':' during #if evaluation\n");
+					goto eval_fail;
+				}
+				if (!vector_last(ppeaux, stack).st0) {
+					log_error(&tok->loginfo, "unexpected symbol ':' during #if evaluation\n");
+					goto eval_fail;
+				}
+			eval_colon_acc:
+				// Skip to the end of expression or to the corresponding rparen
+				if (vector_size(ppeaux, stack) == 1) {
+					goto done_complete_stack; // No parenthesis opened, skip to the end of the vector
+				}
+				// Skip to the next rparen; also, we skip correctly counting of ':' since we don't care about those anymore
+				unsigned nparens = 0;
+				// Note that we don't really care about the content of the ignored part; it may be syntaxically incorrect
+				for (++tok; tok < vector_end(preproc, cond); ++tok) {
+					if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
+						++nparens;
+					} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
+						if (nparens) --nparens;
+						else break;
+					}
+				}
+				// We need to exit at a RPAREN
+				if (tok == vector_end(preproc, cond)) {
+					log_error(&vector_last(preproc, cond).loginfo, "missing closing parenthesis in #if expression\n");
 					goto eval_fail;
 				}
+				vector_last(ppeaux, stack).n_colons = 0;
+				goto eval_rparen; }
 				
 			case SYM_LBRACKET:
 			case SYM_RBRACKET:
@@ -1504,228 +1633,165 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 			case SYM_DASHDASH:
 			case SYM_COMMA:
 			default:
-				printf("Invalid symbol ID %u during #if evaluation\n", tok->tokv.sym);
+				log_error(&tok->loginfo, "invalid symbol ID %u (%s) during #if evaluation\n",
+					tok->tokv.sym, (tok->tokv.sym <= LAST_SYM) ? sym2str[tok->tokv.sym] : "invalid");
 				goto eval_fail;
 			}
-			acc = vector_last(ppeaux, stack).v0;
-			vector_last(ppeaux, stack).st0 = 0;
 			
-			if (op_lvl < 2) {
-				printf("<internal error> Invalid op_lvl %d < 2 during #if evaluation\n", op_lvl);
+			// invalid_binop: unexpected binary operation
+			// add_binop: add a binary operation
+			if (0) {
+			invalid_binop:
+				log_error(&tok->loginfo, "unexpected binary operator '%s' in #if expression\n", sym2str[tok->tokv.sym]);
 				goto eval_fail;
-			}
-			
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX)
-			if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) {
-				acc = vector_last(ppeaux, stack).v2 * acc;
-				vector_last(ppeaux, stack).st2 = 0;
-			} else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) {
-				if (!acc) {
-					printf("Error: division by zero\n");
+			add_binop:
+				if (op_lvl < 2) {
+					log_internal(&tok->loginfo, "invalid op_lvl %d < 2 during #if evaluation\n", op_lvl);
 					goto eval_fail;
 				}
-				acc = vector_last(ppeaux, stack).v2 / acc;
-				vector_last(ppeaux, stack).st2 = 0;
-			} else if (vector_last(ppeaux, stack).st2 == OPTYP_MOD) {
-				if (!acc) {
-					printf("Error: division by zero\n");
+				if (op_lvl >= OPLVL_ALL) {
+					log_internal(&tok->loginfo, "invalid op_lvl %d > OPLVL_ALL during #if evaluation\n", op_lvl);
 					goto eval_fail;
 				}
-				acc = vector_last(ppeaux, stack).v2 % acc;
-				vector_last(ppeaux, stack).st2 = 0;
-			} else if (vector_last(ppeaux, stack).st2) {
-				printf("<internal error> Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2);
-				goto eval_fail;
-			}
-			if (op_lvl == 2) {
-				vector_last(ppeaux, stack).st2 = op_typ;
-				vector_last(ppeaux, stack).v2 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) {
-				acc = vector_last(ppeaux, stack).v3 + acc;
-				vector_last(ppeaux, stack).st3 = 0;
-			} else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) {
-				acc = vector_last(ppeaux, stack).v3 - acc;
-				vector_last(ppeaux, stack).st3 = 0;
-			} else if (vector_last(ppeaux, stack).st3) {
-				printf("<internal error> Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3);
-				goto eval_fail;
-			}
-			if (op_lvl == 3) {
-				vector_last(ppeaux, stack).st3 = op_typ;
-				vector_last(ppeaux, stack).v3 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) {
-				acc = vector_last(ppeaux, stack).v4 << acc;
-				vector_last(ppeaux, stack).st4 = 0;
-			} else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) {
-				acc = is_unsigned ? (int64_t)((uint64_t)vector_last(ppeaux, stack).v4 >> (uint64_t)acc) : (vector_last(ppeaux, stack).v4 >> acc);
-				vector_last(ppeaux, stack).st4 = 0;
-			} else if (vector_last(ppeaux, stack).st4) {
-				printf("<internal error> Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4);
-				goto eval_fail;
-			}
-			if (op_lvl == 4) {
-				vector_last(ppeaux, stack).st4 = op_typ;
-				vector_last(ppeaux, stack).v4 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st5 == OPTYP_LET) {
-				acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 < (uint64_t)acc) : (vector_last(ppeaux, stack).v5 < acc);
-				is_unsigned = 0;
-				vector_last(ppeaux, stack).st5 = 0;
-			} else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) {
-				acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 <= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 <= acc);
-				is_unsigned = 0;
-				vector_last(ppeaux, stack).st5 = 0;
-			} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) {
-				acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 > (uint64_t)acc) : (vector_last(ppeaux, stack).v5 > acc);
-				is_unsigned = 0;
-				vector_last(ppeaux, stack).st5 = 0;
-			} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) {
-				acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 >= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 >= acc);
-				is_unsigned = 0;
-				vector_last(ppeaux, stack).st5 = 0;
-			} else if (vector_last(ppeaux, stack).st5) {
-				printf("<internal error> Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5);
-				goto eval_fail;
-			}
-			if (op_lvl == 5) {
-				vector_last(ppeaux, stack).st5 = op_typ;
-				vector_last(ppeaux, stack).v5 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) {
-				acc = vector_last(ppeaux, stack).v6 == acc;
-				vector_last(ppeaux, stack).st6 = 0;
-			} else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) {
-				acc = vector_last(ppeaux, stack).v6 != acc;
-				vector_last(ppeaux, stack).st6 = 0;
-			} else if (vector_last(ppeaux, stack).st6) {
-				printf("<internal error> Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6);
-				goto eval_fail;
-			}
-			if (op_lvl == 6) {
-				vector_last(ppeaux, stack).st6 = op_typ;
-				vector_last(ppeaux, stack).v6 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) {
-				acc = vector_last(ppeaux, stack).v7 & acc;
-				vector_last(ppeaux, stack).st7 = 0;
-			} else if (vector_last(ppeaux, stack).st7) {
-				printf("<internal error> Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7);
-				goto eval_fail;
-			}
-			if (op_lvl == 7) {
-				vector_last(ppeaux, stack).st7 = op_typ;
-				vector_last(ppeaux, stack).v7 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) {
-				acc = vector_last(ppeaux, stack).v8 ^ acc;
-				vector_last(ppeaux, stack).st8 = 0;
-			} else if (vector_last(ppeaux, stack).st8) {
-				printf("<internal error> Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8);
-				goto eval_fail;
-			}
-			if (op_lvl == 8) {
-				vector_last(ppeaux, stack).st8 = op_typ;
-				vector_last(ppeaux, stack).v8 = acc;
-				goto done_partial_eval;
-			}
-			if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) {
-				acc = vector_last(ppeaux, stack).v9 | acc;
-				vector_last(ppeaux, stack).st9 = 0;
-			} else if (vector_last(ppeaux, stack).st9) {
-				printf("<internal error> Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9);
-				goto eval_fail;
-			}
-			if (op_lvl == 9) {
-				vector_last(ppeaux, stack).st9 = op_typ;
-				vector_last(ppeaux, stack).v9 = acc;
-				goto done_partial_eval;
-			}
-			if (op_lvl == 10) {
-				// We know that sym == SYM_AMPAMP, so we need to skip evaluating the remainder if it equals 0
-				if (acc) goto done_partial_eval;
-				is_unsigned = 0;
-				unsigned nparens = 0;
-				// 0 && y ? z : w => w
-				// 0 && y || z => z
-				// Otherwise, keep skipping to the next token
-				for (++tok; tok < vector_end(preproc, cond); ++tok) {
-					if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
-						++nparens;
-					} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
-						if (nparens) --nparens;
-						else {
-							vector_last(ppeaux, stack).v0 = acc;
-							vector_last(ppeaux, stack).st0 = 1;
-							--tok;
-							goto done_partial_eval;
+				
+				success = 1;
+				acc = eval_stack(&vector_last(ppeaux, stack), li0, op_lvl, op_typ, is_unsigned, &success);
+				if (!success) {
+					goto eval_fail;
+				}
+				
+				if (op_lvl < OPLVL_BAN) {
+					// No post-processing required
+					goto done_partial_eval;
+				}
+				if (op_lvl == OPLVL_BAN) {
+					// op_typ == OPLVL_BAN
+					vector_last(ppeaux, stack).st_bool = 1;
+					if (acc) goto done_partial_eval;
+					// We have five possibilities:
+					// [0] && x <EOL>     ~> 0
+					// [0] && x)          ~> 0)
+					// [0] && x ? y : z   ~> z
+					// [0] && x : y       ~> 0 : y ~> 0
+					// [0] && x || y      ~> y
+					// We are at the '&&'; note that we know the top of the stack has no pending operation
+					unsigned nparens = 0;
+					for (++tok; tok < vector_end(preproc, cond); ++tok) {
+						if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
+							++nparens;
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
+							if (nparens) --nparens;
+							else {
+								if (vector_size(ppeaux, stack) == 1) {
+									log_error(&tok->loginfo, "unexpected symbol ')' (parenthesis not opened) during #if evaluation\n");
+									goto eval_fail;
+								}
+								if (vector_last(ppeaux, stack).n_colons) {
+									log_error(&tok->loginfo, "unexpected symbol ')', expected symbol ':' during #if evaluation\n");
+									goto eval_fail;
+								}
+								
+								goto eval_rparen_acc;
+							}
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
+							if (!nparens) {
+								goto eval_question_acc;
+							}
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_COLON)) {
+							if (!nparens) {
+								if (!vector_last(ppeaux, stack).n_colons) {
+									log_error(&tok->loginfo, "unexpected symbol ':' during #if evaluation\n");
+									goto eval_fail;
+								}
+								goto eval_colon_acc;
+							}
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_PIPEPIPE)) {
+							if (!nparens) {
+								goto done_partial_eval;
+							}
 						}
-					} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
-						goto eval_question_acc;
-					} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_PIPEPIPE)) {
-						break;
 					}
-				}
-				if (tok == vector_end(preproc, cond)) {
-					if (!nparens) goto done_complete_acc;
-					else {
-						printf("Unclosed parenthesis in #if evaluation\n");
+					if (nparens) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ')' during #if evaluation\n");
 						goto eval_fail;
 					}
-				}
-				goto done_partial_eval;
-			}
-			if (op_lvl == 11) {
-				// We know that sym == SYM_PIPEPIPE, so we need to skip evaluating the remainder if it equals 1
-				if (!acc) goto done_partial_eval;
-				is_unsigned = 0;
-				unsigned nparens = 0;
-				// 0 || y ? z : w => w
-				// Otherwise, keep skipping to the next token
-				for (++tok; tok < vector_end(preproc, cond); ++tok) {
-					if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
-						++nparens;
-					} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
-						if (nparens) --nparens;
-						else {
-							vector_last(ppeaux, stack).v0 = acc;
-							vector_last(ppeaux, stack).st0 = 1;
-							--tok;
-							goto done_partial_eval;
+					if (vector_last(ppeaux, stack).n_colons) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ':' during #if evaluation\n");
+						goto eval_fail;
+					}
+					if (vector_size(ppeaux, stack) != 1) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ')' during #if evaluation\n");
+						goto eval_fail;
+					}
+					goto done_complete_acc;
+				}
+				if (op_lvl == OPLVL_BOR) {
+					// op_typ == OPLVL_BOR
+					vector_last(ppeaux, stack).st_bool = 1;
+					if (!acc) goto done_partial_eval;
+					// We have four possibilities:
+					// [!0] || x <EOL>     ~> 1
+					// [!0] || x)          ~> 1)
+					// [!0] || x ? y : z   ~> z
+					// [!0] || x : y       ~> 1 : y ~> 1
+					// We are at the '||'; note that we know the top of the stack has no pending operation
+					unsigned nparens = 0;
+					for (++tok; tok < vector_end(preproc, cond); ++tok) {
+						if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
+							++nparens;
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) {
+							if (nparens) --nparens;
+							else {
+								if (vector_size(ppeaux, stack) == 1) {
+									log_error(&tok->loginfo, "unexpected symbol ')' (parenthesis not opened) during #if evaluation\n");
+									goto eval_fail;
+								}
+								if (vector_last(ppeaux, stack).n_colons) {
+									log_error(&tok->loginfo, "unexpected symbol ')', expected symbol ':' during #if evaluation\n");
+									goto eval_fail;
+								}
+								
+								acc = 1;
+								goto eval_rparen_acc;
+							}
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
+							if (!nparens) {
+								acc = 1;
+								goto eval_question_acc;
+							}
+						} else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_COLON)) {
+							if (!nparens) {
+								if (!vector_last(ppeaux, stack).n_colons) {
+									log_error(&tok->loginfo, "unexpected symbol ':' during #if evaluation\n");
+									goto eval_fail;
+								}
+								acc = 1;
+								goto eval_colon_acc;
+							}
 						}
-					} else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
-						goto eval_question_acc;
 					}
-				}
-				if (tok == vector_end(preproc, cond)) {
-					if (!nparens) goto done_complete_acc;
-					else {
-						printf("Unclosed parenthesis in #if evaluation\n");
+					if (nparens) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ')' during #if evaluation\n");
+						goto eval_fail;
+					}
+					if (vector_last(ppeaux, stack).n_colons) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ':' during #if evaluation\n");
+						goto eval_fail;
+					}
+					if (vector_size(ppeaux, stack) != 1) {
+						log_error(&vector_last(preproc, cond).loginfo, "expected symbol ')' during #if evaluation\n");
 						goto eval_fail;
 					}
+					acc = 1;
+					goto done_complete_acc;
 				}
-				goto done_partial_eval;
+				log_internal(&li0, "invalid op_lvl %d during #if evaluation\n", op_lvl);
+				goto eval_fail;
 			}
-			// if (vector_last(ppeaux, stack).st_bool) {
-			// 	acc = !!acc;
-			// 	vector_last(ppeaux, stack).st_bool = 0;
-			// }
-#pragma GCC diagnostic pop
-			printf("<internal error> Invalid op_lvl %d > 11 during #if evaluation\n", op_lvl);
-			goto eval_fail;
 			
 		done_partial_eval:
 		} else {
-			printf("Invalid token type %u during #if evaluation\n", tok->tokt);
+			log_error(&tok->loginfo, "invalid token type %u during #if evaluation\n", tok->tokt);
 			goto eval_fail;
 		}
 		
@@ -1740,7 +1806,8 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 				else if (sym == SYM_EXCL) acc = !acc;
 				else if (sym == SYM_TILDE) acc = ~acc;
 				else {
-					printf("<internal error> Unknown level 1 unary operator sym ID %u\n", sym);
+					log_internal(&vector_last(ppeaux, stack).v1[vector_last(ppeaux, stack).st1].loginfo,
+					             "unknown level 1 unary operator sym ID %u\n", sym);
 				}
 			}
 			vector_last(ppeaux, stack).v0 = acc;
@@ -1749,114 +1816,16 @@ static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret, int ptr_i
 	}
 	
 	if (vector_size(ppeaux, stack) != 1) {
-		printf("Opened parenthesis never closed in #if expression\n");
+		log_error(&vector_last(preproc, cond).loginfo, "expected symbol ')' during #if evaluation\n");
 		goto eval_fail;
 	}
 	
 done_complete_stack:
-	acc = vector_last(ppeaux, stack).v0;
-	vector_last(ppeaux, stack).st0 = 0;
-	
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX)
-	if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) {
-		acc = vector_last(ppeaux, stack).v2 * acc;
-		vector_last(ppeaux, stack).st2 = 0;
-	} else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) {
-		if (!acc) {
-			printf("Error: division by zero\n");
-			goto eval_fail;
-		}
-		acc = vector_last(ppeaux, stack).v2 / acc;
-		vector_last(ppeaux, stack).st2 = 0;
-	} else if (vector_last(ppeaux, stack).st2 == OPTYP_MOD) {
-		if (!acc) {
-			printf("Error: division by zero\n");
-			goto eval_fail;
-		}
-		acc = vector_last(ppeaux, stack).v2 % acc;
-		vector_last(ppeaux, stack).st2 = 0;
-	} else if (vector_last(ppeaux, stack).st2) {
-		printf("<internal error> Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) {
-		acc = vector_last(ppeaux, stack).v3 + acc;
-		vector_last(ppeaux, stack).st3 = 0;
-	} else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) {
-		acc = vector_last(ppeaux, stack).v3 - acc;
-		vector_last(ppeaux, stack).st3 = 0;
-	} else if (vector_last(ppeaux, stack).st3) {
-		printf("<internal error> Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) {
-		acc = vector_last(ppeaux, stack).v4 << acc;
-		vector_last(ppeaux, stack).st4 = 0;
-	} else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) {
-		acc = is_unsigned ? (int64_t)((uint64_t)vector_last(ppeaux, stack).v4 >> (uint64_t)acc) : (vector_last(ppeaux, stack).v4 >> acc);
-		vector_last(ppeaux, stack).st4 = 0;
-	} else if (vector_last(ppeaux, stack).st4) {
-		printf("<internal error> Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st5 == OPTYP_LET) {
-		acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 < (uint64_t)acc) : (vector_last(ppeaux, stack).v5 < acc);
-		is_unsigned = 0;
-		vector_last(ppeaux, stack).st5 = 0;
-	} else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) {
-		acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 <= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 <= acc);
-		is_unsigned = 0;
-		vector_last(ppeaux, stack).st5 = 0;
-	} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) {
-		acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 > (uint64_t)acc) : (vector_last(ppeaux, stack).v5 > acc);
-		is_unsigned = 0;
-		vector_last(ppeaux, stack).st5 = 0;
-	} else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) {
-		acc = is_unsigned ? ((uint64_t)vector_last(ppeaux, stack).v5 >= (uint64_t)acc) : (vector_last(ppeaux, stack).v5 >= acc);
-		is_unsigned = 0;
-		vector_last(ppeaux, stack).st5 = 0;
-	} else if (vector_last(ppeaux, stack).st5) {
-		printf("<internal error> Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) {
-		acc = vector_last(ppeaux, stack).v6 == acc;
-		vector_last(ppeaux, stack).st6 = 0;
-	} else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) {
-		acc = vector_last(ppeaux, stack).v6 != acc;
-		vector_last(ppeaux, stack).st6 = 0;
-	} else if (vector_last(ppeaux, stack).st6) {
-		printf("<internal error> Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6);
+	success = 1;
+	acc = eval_stack(&vector_last(ppeaux, stack), li0, OPLVL_ALS, 0, is_unsigned, &success);
+	if (!success) {
 		goto eval_fail;
 	}
-	if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) {
-		acc = vector_last(ppeaux, stack).v7 & acc;
-		vector_last(ppeaux, stack).st7 = 0;
-	} else if (vector_last(ppeaux, stack).st7) {
-		printf("<internal error> Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) {
-		acc = vector_last(ppeaux, stack).v8 ^ acc;
-		vector_last(ppeaux, stack).st8 = 0;
-	} else if (vector_last(ppeaux, stack).st8) {
-		printf("<internal error> Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) {
-		acc = vector_last(ppeaux, stack).v9 | acc;
-		vector_last(ppeaux, stack).st9 = 0;
-	} else if (vector_last(ppeaux, stack).st9) {
-		printf("<internal error> Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9);
-		goto eval_fail;
-	}
-	if (vector_last(ppeaux, stack).st_bool) {
-		acc = !!acc;
-		vector_last(ppeaux, stack).st_bool = 0;
-	}
-#pragma GCC diagnostic pop
 	
 done_complete_acc:
 	vector_del(ppeaux, stack);
@@ -1869,7 +1838,7 @@ int proc_unget_token(preproc_t *src, proc_token_t *tok) {
 }
 static proc_token_t proc_next_token_aux(preproc_t *src) {
 	if (!vector_size(ppsource, src->prep)) {
-		return (proc_token_t){ .tokt = PTOK_EOF, .tokv = {.c = (char)EOF} };
+		return (proc_token_t){ .tokt = PTOK_EOF, .loginfo = { 0 }, .tokv = {.c = (char)EOF} };
 	}
 	if (vector_last(ppsource, src->prep).srct == PPSRC_PTOKEN) {
 		proc_token_t ret = vector_last(ppsource, src->prep).srcv.ptok;
@@ -1879,7 +1848,7 @@ static proc_token_t proc_next_token_aux(preproc_t *src) {
 check_if_depth:
 	{
 		if (!vector_size(ppsource, src->prep)) {
-			return (proc_token_t){ .tokt = PTOK_EOF, .tokv = {.c = (char)EOF} };
+			return (proc_token_t){ .tokt = PTOK_EOF, .loginfo = { 0 }, .tokv = {.c = (char)EOF} };
 		}
 		ppsource_t *ppsrc = &vector_last(ppsource, src->prep);
 		if ((ppsrc->srct == PPSRC_PREPARE) && (ppsrc->srcv.prep.ok_depth != ppsrc->srcv.prep.cond_depth)) {
@@ -1896,12 +1865,13 @@ check_if_depth:
 					src->st = PPST_NONE;
 					proc_token_t ret;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = tok.tokv.c;
 					return ret;
 				} else if (tok.tokt == PPTOK_NEWLINE) {
 					src->st = PPST_NL;
 				} else if (tok.tokt == PPTOK_EOF) {
-					printf("Error: file ended before closing all conditionals (ignoring)\n");
+					log_warning(&tok.loginfo, "file ended before closing all conditionals (ignoring)\n");
 					vector_pop(ppsource, src->prep);
 					goto check_if_depth;
 				} else if ((tok.tokt == PPTOK_SYM) && (src->st == PPST_NL) && (tok.tokv.sym == SYM_HASH)) {
@@ -1938,36 +1908,40 @@ check_if_depth:
 							string_del(tok.tokv.str);
 							VECTOR(preproc) *cond = vector_new(preproc);
 							if (!cond) {
-								printf("Error: failed to allocate #elif condition vector\n");
+								log_memory("failed to allocate #elif condition vector\n");
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .loginfo = tok.loginfo, .tokv = {.c = '\0'} };
 							}
 							tok = ppsrc_next_token(src);
+							loginfo_t li = tok.loginfo;
 							while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
+								li.lineno_end = tok.loginfo.lineno_end ? tok.loginfo.lineno_end : tok.loginfo.lineno;
+								li.colno_end = tok.loginfo.colno_end ? tok.loginfo.colno_end : tok.loginfo.colno;
 								if (!vector_push(preproc, cond, tok)) {
-									printf("Error: failed to add token to #elif condition vector\n");
+									log_memory("failed to add token to #elif condition vector\n");
 									vector_del(preproc, cond);
 									src->st = PPST_NONE;
-									return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
+									return (proc_token_t){ .tokt = PTOK_INVALID, .loginfo = tok.loginfo, .tokv = {.c = '\0'} };
 								}
 								tok = ppsrc_next_token(src);
 							}
 							vector_trim(preproc, cond);
 							khash_t(string_set) *solved_macros = kh_init(string_set);
 							if (!solved_macros) {
-								printf("Error: failed to allocate #elif solved_macros set\n");
+								log_memory("failed to allocate #elif solved_macros set\n");
 								vector_del(preproc, cond);
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .loginfo = tok.loginfo, .tokv = {.c = '\0'} };
 							}
 							
-							VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, cond, solved_macros, src->macros_used);
+							VECTOR(preproc) *expanded = preproc_do_expand(&li, src->macros_map, cond, solved_macros, src->macros_used);
 							vector_del(preproc, cond);
 							macros_set_del(solved_macros);
 							if (!expanded) {
-								printf("Error: failed to expand #elif condition\n");
+								// Not required as preproc_do_expand already prints an error, but this can be useful
+								log_error(&li, "failed to expand #elif condition\n");
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .loginfo = tok.loginfo, .tokv = {.c = '\0'} };
 							}
 							
 							// Now we need to compute what is pointed by expanded, and increase cond_depth and ok_depth as needed
@@ -1975,7 +1949,8 @@ check_if_depth:
 							int64_t res = preproc_eval(expanded, &st, src->target->size_long == 4);
 							vector_del(preproc, expanded);
 							if (!st) {
-								printf("Error: failed to evaluate #elif condition in (%s)\n", src->cur_file);
+								// Not required as preproc_eval already prints an error, but this can be useful
+								log_error(&li, "failed to evaluate #elif condition\n");
 								src->st = PPST_NONE;
 								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 							}
@@ -1992,7 +1967,7 @@ check_if_depth:
 								}
 							}
 							if (tok.tokt == PPTOK_EOF) {
-								printf("Error: file ended before closing all conditionals (ignoring)\n");
+								log_warning(&tok.loginfo, "file ended before closing all conditionals (ignoring)\n");
 								vector_pop(ppsource, src->prep);
 								src->st = PPST_NL; // Should be redundant since TOK_NEWLINE is added before TOK_EOF if required
 								// EOF has an empty destructor
@@ -2007,7 +1982,7 @@ check_if_depth:
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-							printf("Invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt);
+							log_error(&tok.loginfo, "invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt);
 							goto preproc_ignore_remaining;
 						}
 						if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) {
@@ -2023,7 +1998,7 @@ check_if_depth:
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-							printf("Invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt);
+							log_error(&tok.loginfo, "invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt);
 							goto preproc_ignore_remaining;
 						}
 						if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) {
@@ -2061,7 +2036,7 @@ check_if_depth:
 						goto preproc_ignore_remaining_goto;
 					}
 					
-					printf("Unknown ignored pp command %s (%s), skipping until EOL\n", string_content(tok.tokv.str), src->cur_file);
+					log_warning(&tok.loginfo, "Unknown ignored pp command %s, skipping until EOL\n", string_content(tok.tokv.str));
 				preproc_ignore_remaining:
 					while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 						preproc_token_del(&tok);
@@ -2092,6 +2067,7 @@ check_if_depth:
 check_next_token:
 	if (!vector_size(ppsource, src->prep)) {
 		ret.tokt = PTOK_EOF;
+		ret.loginfo = (loginfo_t){0};
 		ret.tokv.c = (char)EOF;
 		return ret;
 	}
@@ -2102,6 +2078,7 @@ start_cur_token:
 	case PPTOK_INVALID:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_IDENT:
@@ -2112,6 +2089,7 @@ start_cur_token:
 				macro_t *m = &kh_val(src->macros_map, it);
 				int need_solve = !m->is_funlike;
 				VECTOR(preprocs) *margs = NULL;
+				loginfo_t li = tok.loginfo;
 				if (m->is_funlike) {
 					preproc_token_t tok2 = ppsrc_next_token(src);
 					size_t nnls = 0;
@@ -2146,13 +2124,16 @@ start_cur_token:
 							if ((tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_LPAREN)) ++need_solve;
 							else if ((tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_RPAREN)) --need_solve;
 						}
+						li.lineno_end = tok2.loginfo.lineno_end ? tok2.loginfo.lineno_end : tok2.loginfo.lineno;
+						li.colno_end = tok2.loginfo.colno_end ? tok2.loginfo.colno_end : tok2.loginfo.colno;
 						if (need_solve) {
-							printf("Unfinished fun-like macro %s\n", string_content(tok.tokv.str));
+							log_error(&li, "unfinished function-like macro %s\n", string_content(tok.tokv.str));
 							vector_del(preprocs, margs);
 							vector_del(preproc, marg);
 							string_del(tok.tokv.str);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
+							ret.loginfo = tok2.loginfo;
 							ret.tokv.c = tok.tokv.c;
 							return ret;
 						}
@@ -2166,11 +2147,12 @@ start_cur_token:
 						need_solve = 1;
 					} else {
 						if (!vector_reserve(ppsource, src->prep, vector_size(ppsource, src->prep) + nnls + 1)) {
-							printf("Memory error (undoing lookahead for macro use %s)\n", string_content(tok.tokv.str));
+							log_memory("undoing lookahead for macro use %s\n", string_content(tok.tokv.str));
 							string_del(tok.tokv.str);
 							preproc_token_del(&tok2);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
+							ret.loginfo = li;
 							ret.tokv.c = '\0';
 							return ret;
 						}
@@ -2189,22 +2171,24 @@ start_cur_token:
 					
 					char *mname = string_steal(tok.tokv.str);
 					
-					VECTOR(preproc) *solved = proc_solve_macro(src->macros_map, mname, m, margs, solved_macros, NULL);
+					VECTOR(preproc) *solved = preproc_solve_macro(&li, src->macros_map, mname, m, margs, solved_macros, NULL);
 					if (margs) vector_del(preprocs, margs);
 					macros_set_del(solved_macros);
 					if (!solved) {
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = li;
 						ret.tokv.c = '\0';
 						return ret;
 					}
 					// If the expansion is empty, don't push it
 					if (vector_size(preproc, solved)) {
 						if (!vector_push(ppsource, src->prep, ((ppsource_t){.srct = PPSRC_PPTOKENS, .srcv.pptoks = {.idx = 0, .toks = solved}}))) {
-							printf("Memory error (pushing expanded macro)\n");
+							log_memory("pushing expanded macro\n");
 							vector_del(preproc, solved);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
+							ret.loginfo = li;
 							ret.tokv.c = '\0';
 							return ret;
 						}
@@ -2219,11 +2203,12 @@ start_cur_token:
 				
 				if (0) {
 				solve_err_mem:
-					printf("Memory error (parsing macro use %s)\n", string_content(tok.tokv.str));
+					log_memory("parsing macro use %s\n", string_content(tok.tokv.str));
 					if (margs) vector_del(preprocs, margs);
 					string_del(tok.tokv.str);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = li;
 					ret.tokv.c = '\0';
 					return ret;
 				}
@@ -2240,20 +2225,24 @@ start_cur_token:
 			ret.tokt = PTOK_KEYWORD;
 			ret.tokv.kw = kh_val(str2kw, it);
 		}
+		ret.loginfo = tok.loginfo;
 		return ret; }
 	case PPTOK_NUM:
 		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_NUM;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.str = tok.tokv.str;
 		return ret;
 	case PPTOK_STRING:
 		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_STRING;
+		ret.loginfo = tok.loginfo;
 		ret.tokv = (union proc_token_val_u){.sstr = tok.tokv.sstr, .sisstr = tok.tokv.sisstr};
 		return ret;
 	case PPTOK_INCL:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.c = tok.tokv.sisstr ? '<' : '"';
 		string_del(tok.tokv.sstr);
 		return ret;
@@ -2262,19 +2251,14 @@ start_cur_token:
 			tok = ppsrc_next_token(src);
 			if ((tok.tokt == PPTOK_NEWLINE) || (tok.tokt == PPTOK_EOF)) {
 				// Empty preprocessor command
-				if (tok.tokt == PPTOK_NEWLINE) {
-					ret.tokt = PTOK_EOF;
-					ret.tokv.c = tok.tokv.c;
-					return ret;
-				} else {
-					goto check_next_token;
-				}
+				goto start_cur_token;
 			}
 			if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) goto preproc_hash_err;
 			if (!strcmp(string_content(tok.tokv.str), "include") || !strcmp(string_content(tok.tokv.str), "include_next")) {
 				int is_next = string_content(tok.tokv.str)[7] == '_';
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
+				loginfo_t li = tok.loginfo;
 				string_t *incl_file;
 				int is_sys;
 				if (tok.tokt == PPTOK_INCL) {
@@ -2282,48 +2266,59 @@ start_cur_token:
 					// Assume we only have one #include "..." path, so include_next is always a system include
 					is_sys = is_next || !tok.tokv.sisstr;
 					tok = ppsrc_next_token(src); // Token was moved
+					loginfo_t ignored_infos = tok.loginfo; int has_ignored = 0;
 					while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
-						printf("Warning: ignored tokens after #include directive (%s)\n", src->cur_file);
+						has_ignored = 1;
+						ignored_infos.lineno_end = tok.loginfo.lineno_end ? tok.loginfo.lineno_end : tok.loginfo.lineno;
+						ignored_infos.colno_end = tok.loginfo.colno_end ? tok.loginfo.colno_end : tok.loginfo.colno;
 						preproc_token_del(&tok);
 						tok = ppsrc_next_token(src);
 					}
+					if (has_ignored) {
+						log_warning(&ignored_infos, "ignored tokens after #include%s directive\n", is_next ? "_next" : "");
+					}
 				} else {
 					// Expand macro, then try again
 					VECTOR(preproc) *incl = vector_new(preproc);
 					if (!incl) {
-						printf("Error: failed to allocate #include tokens vector (%s)\n", src->cur_file);
+						log_memory("failed to allocate #include tokens vector\n");
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					}
 					while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 						if (!vector_push(preproc, incl, tok)) {
-							printf("Error: failed to add token to #include tokens vector (%s)\n", src->cur_file);
+							log_memory("failed to add token to #include tokens vector\n");
 							vector_del(preproc, incl);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
+							ret.loginfo = tok.loginfo;
 							ret.tokv.c = '\0';
 							return ret;
 						}
+						li.lineno_end = tok.loginfo.lineno_end ? tok.loginfo.lineno_end : tok.loginfo.lineno;
+						li.colno_end = tok.loginfo.colno_end ? tok.loginfo.colno_end : tok.loginfo.colno;
 						tok = ppsrc_next_token(src);
 					}
 					vector_trim(preproc, incl);
 					khash_t(string_set) *solved_macros = kh_init(string_set);
 					if (!solved_macros) {
-						printf("Error: failed to allocate #include solved_macros set (%s)\n", src->cur_file);
+						log_memory("failed to allocate #include solved_macros set\n");
 						vector_del(preproc, incl);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					}
 					
-					VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, incl, solved_macros, src->macros_used);
+					VECTOR(preproc) *expanded = preproc_do_expand(&li, src->macros_map, incl, solved_macros, src->macros_used);
 					vector_del(preproc, incl);
 					macros_set_del(solved_macros);
 					if (!expanded) {
-						printf("Error: failed to expand #include tokens (%s)\n", src->cur_file);
+						log_error(&li, "failed to expand #include tokens\n");
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
 						ret.tokv.c = '\0';
@@ -2332,27 +2327,27 @@ start_cur_token:
 					
 					// Now we need to check what is pointed by expanded
 					if (!vector_size(preproc, expanded)) {
-						printf("Error: missing #include name (%s)\n", src->cur_file);
+						log_error(&li, "missing #include name\n");
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					}
 					if (vector_content(preproc, expanded)[0].tokt == PPTOK_STRING) {
 						is_sys = is_next || src->is_sys;
 						preproc_token_t *exp = vector_content(preproc, expanded);
-						// TODO
-						printf("Error: TODO: #include <expanded, first is string '%s' (%d)> (%s)\n",
-							string_content(exp->tokv.sstr), exp->tokv.sisstr, src->cur_file);
+						log_error(&li, "TODO: #include <expanded, first is string '%s' (%d)>\n", string_content(exp->tokv.sstr), exp->tokv.sisstr);
 						vector_del(preproc, expanded);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					} else if ((vector_content(preproc, expanded)[0].tokt == PPTOK_SYM) && (vector_content(preproc, expanded)[0].tokv.sym == SYM_LT)
 					        && (vector_last(preproc, expanded).tokt == PPTOK_SYM) && (vector_last(preproc, expanded).tokv.sym == SYM_GT)
 					        && (vector_size(preproc, expanded) >= 3)) {
-						printf("Warning: #include command with macro expansion, assuming no space is present in the file name\n");
+						log_warning(&li, "#include command with macro expansion, assuming no space is present in the file name\n");
 						is_sys = 0;
 						incl_file = string_new();
 						for (vector_preproc_elem *tok2 = expanded->content + 1; tok2 < expanded->content + expanded->vsize - 1; ++tok2) {
@@ -2360,26 +2355,26 @@ start_cur_token:
 							case PPTOK_IDENT:
 							case PPTOK_IDENT_UNEXP:
 								if (!string_add_string(incl_file, tok2->tokv.str)) {
-									printf("Error: failed to add ident to include string (%s)\n", src->cur_file);
+									log_memory("failed to add ident to include string\n");
 									vector_del(preproc, expanded);
 									string_del(incl_file);
 									src->st = PPST_NONE;
 									ret.tokt = PTOK_INVALID;
+									ret.loginfo = tok.loginfo;
 									ret.tokv.c = '\0';
 									return ret;
 								}
 								break;
 							case PPTOK_SYM:
-								for (const char *s = sym2str[tok2->tokv.sym]; *s; ++s) {
-									if (!string_add_char(incl_file, *s)) {
-										printf("Error: failed to add symbol to include string (%s)\n", src->cur_file);
-										vector_del(preproc, expanded);
-										string_del(incl_file);
-										src->st = PPST_NONE;
-										ret.tokt = PTOK_INVALID;
-										ret.tokv.c = '\0';
-										return ret;
-									}
+								if (!string_add_cstr(incl_file, sym2str[tok2->tokv.sym])) {
+									log_memory("failed to add symbol to include string\n");
+									vector_del(preproc, expanded);
+									string_del(incl_file);
+									src->st = PPST_NONE;
+									ret.tokt = PTOK_INVALID;
+									ret.loginfo = tok.loginfo;
+									ret.tokv.c = '\0';
+									return ret;
 								}
 								break;
 								
@@ -2392,32 +2387,39 @@ start_cur_token:
 							case PPTOK_START_LINE_COMMENT:
 							case PPTOK_EOF:
 							default:
-								printf("Error: TODO: add token type %u to include string (%s)\n", tok2->tokt, src->cur_file);
+								log_error(&li, "TODO: add token type %u to include string\n", tok2->tokt);
 								vector_del(preproc, expanded);
 								string_del(incl_file);
 								src->st = PPST_NONE;
 								ret.tokt = PTOK_INVALID;
+								ret.loginfo = tok.loginfo;
 								ret.tokv.c = '\0';
 								return ret;
 							}
 						}
 						vector_del(preproc, expanded);
 					} else {
-						printf("Error: invalid #include command (macro expansion does not result in string or <...>) (%s)\n", src->cur_file);
+						log_error(&li, "invalid #include command (macro expansion does not result in string or <...>)\n");
 						vector_del(preproc, expanded);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					}
 				}
 				// cur_pathno == 0 if cur_file was from an #include "...", otherwise idx + 1
+#ifdef LOG_INCLUDE
+				printf("Opening %s as %s from system path %zu\n", string_content(incl_file),
+				       is_sys ? "system header" : "cur header", is_next ? src->cur_pathno : 0);
+#endif
 				if ((is_sys || !try_open_dir(src, incl_file)) && !try_open_sys(src, incl_file, is_next ? src->cur_pathno : 0)) {
-					printf("Failed to open %s\n", string_content(incl_file));
+					log_error(&li, "failed to open %s\n", string_content(incl_file));
 					string_del(incl_file);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv.c = tok.tokv.sisstr ? '<' : '"';
+					ret.loginfo = li;
+					ret.tokv.c = is_sys ? '<' : '"';
 					return ret;
 				}
 				string_del(incl_file);
@@ -2427,16 +2429,17 @@ start_cur_token:
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-					printf("Invalid token type %u after '#define' preprocessor command\n", tok.tokt);
+					log_error(&tok.loginfo, "invalid token type %u after '#define' preprocessor command\n", tok.tokt);
 					goto preproc_hash_err;
 				}
 				string_t *defname = tok.tokv.str;
 				macro_t m = (macro_t){ .is_funlike = 0, .has_varargs = 0, .nargs = 0, .toks = vector_new(mtoken) };
 				if (!m.toks) {
-					printf("Failed to allocate token vector for macro %s, returning EOF\n", string_content(defname));
+					log_memory("failed to allocate token vector for macro %s\n", string_content(defname));
 					string_del(defname); // Token is now freed
-					ret.tokt = PTOK_EOF;
-					ret.tokv.c = (char)EOF;
+					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				khash_t(argid_map) *args = NULL;
@@ -2445,11 +2448,12 @@ start_cur_token:
 					m.is_funlike = 1;
 					args = kh_init(argid_map);
 					if (!args) {
-						printf("Failed to allocate args map for macro %s, returning EOF\n", string_content(defname));
+						log_memory("failed to allocate args map for macro %s\n", string_content(defname));
 						string_del(defname); // Token is now freed
 						vector_del(mtoken, m.toks);
-						ret.tokt = PTOK_EOF;
-						ret.tokv.c = (char)EOF;
+						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					m.nargs = 0;
@@ -2466,17 +2470,18 @@ start_cur_token:
 								char *tok_str = strdup("__VA_ARGS__");
 								khiter_t kh_k = kh_put(argid_map, args, tok_str, &kh_ret); // Moves the string content
 								if (kh_ret < 0) { // Failed to move, needs to free here
-									printf("Failed to push arg %s for macro %s, returning EOF\n", tok_str, string_content(defname));
+									log_memory("failed to push arg %s for macro %s\n", tok_str, string_content(defname));
 									string_del(defname);
 									free(tok_str);
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
-									ret.tokt = PTOK_EOF;
-									ret.tokv.c = (char)EOF;
+									ret.tokt = PTOK_INVALID;
+									ret.loginfo = tok.loginfo;
+									ret.tokv.c = '\0';
 									return ret;
 								}
 								if (kh_ret == 0) {
-									printf("Duplicate arg %s defining macro %s\n", tok_str, string_content(defname));
+									log_error(&tok.loginfo, "duplicate argument name %s defining macro %s\n", tok_str, string_content(defname));
 									string_del(defname);
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
@@ -2486,7 +2491,7 @@ start_cur_token:
 								// Empty token destructor
 								tok = ppsrc_next_token(src);
 								if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
-									printf("Invalid token type %u after variadic macro arguments definition\n", tok.tokt);
+									log_error(&tok.loginfo, "invalid token type %u after variadic macro arguments definition\n", tok.tokt);
 									string_del(defname);
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
@@ -2499,17 +2504,18 @@ start_cur_token:
 								char *tok_str = string_steal(tok.tokv.str);
 								khiter_t kh_k = kh_put(argid_map, args, tok_str, &kh_ret); // Moves the string content
 								if (kh_ret < 0) { // Failed to move, needs to free here
-									printf("Failed to push arg %s for macro %s, returning EOF\n", tok_str, string_content(defname));
+									log_memory("failed to push arg %s for macro %s\n", tok_str, string_content(defname));
 									string_del(defname);
 									free(tok_str);
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
-									ret.tokt = PTOK_EOF;
-									ret.tokv.c = (char)EOF;
+									ret.tokt = PTOK_INVALID;
+									ret.loginfo = tok.loginfo;
+									ret.tokv.c = '\0';
 									return ret;
 								}
 								if (kh_ret == 0) {
-									printf("Duplicate arg %s defining macro %s\n", tok_str, string_content(defname));
+									log_error(&tok.loginfo, "duplicate argument name %s defining macro %s\n", tok_str, string_content(defname));
 									string_del(defname);
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
@@ -2534,7 +2540,7 @@ start_cur_token:
 									// Empty token destructor
 									tok = ppsrc_next_token(src);
 									if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
-										printf("Invalid token type %u after variadic macro arguments definition\n", tok.tokt);
+										log_error(&tok.loginfo, "invalid token type %u after variadic macro arguments definition\n", tok.tokt);
 										string_del(defname);
 										vector_del(mtoken, m.toks);
 										argid_map_del(args);
@@ -2543,7 +2549,7 @@ start_cur_token:
 									ok = 1;
 									break;
 								} else {
-									printf("Invalid %s type %u during variadic macro arguments definition\n",
+									log_error(&tok.loginfo, "invalid %s type %u in macro arguments definition\n",
 										(tok.tokt == PPTOK_SYM) ? "symbol" : "token",
 										(tok.tokt == PPTOK_SYM) ? tok.tokv.sym : tok.tokt);
 									string_del(defname);
@@ -2552,7 +2558,7 @@ start_cur_token:
 									goto preproc_hash_err;
 								}
 							} else {
-								printf("Invalid %s type %u as macro arguments definition name\n",
+								log_error(&tok.loginfo, "invalid %s type %u as macro arguments definition name\n",
 									(tok.tokt == PPTOK_SYM) ? "symbol" : "token", (tok.tokt == PPTOK_SYM) ? tok.tokv.sym : tok.tokt);
 								string_del(defname);
 								vector_del(mtoken, m.toks);
@@ -2562,7 +2568,7 @@ start_cur_token:
 						}
 					}
 					if (!ok) {
-						printf("Invalid macro definition for %s\n", string_content(defname));
+						log_error(&tok.loginfo, "invalid macro definition for %s\n", string_content(defname));
 						string_del(defname);
 						vector_del(mtoken, m.toks);
 						argid_map_del(args);
@@ -2582,7 +2588,7 @@ start_cur_token:
 				while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 					if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASH)) {
 						if (state & ST_STR) {
-							printf("Warning: duplicated stringify in macro definition (defining %s)\n", string_content(defname));
+							log_warning(&tok.loginfo, "duplicated stringify in macro definition (defining %s)\n", string_content(defname));
 						} else {
 							state |= ST_STR;
 						}
@@ -2592,9 +2598,9 @@ start_cur_token:
 					}
 					if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASHHASH)) {
 						if (state & ST_CONCAT) {
-							printf("Warning: duplicated concatenation in macro definition (defining %s)\n", string_content(defname));
+							log_warning(&tok.loginfo, "duplicated concatenation in macro definition (defining %s)\n", string_content(defname));
 						} else if (!vector_size(mtoken, m.toks)) {
-							printf("Warning: invalid concatenation at start of macro definition (defining %s)\n", string_content(defname));
+							log_warning(&tok.loginfo, "invalid concatenation at start of macro definition (defining %s)\n", string_content(defname));
 						} else {
 							state |= ST_CONCAT;
 						}
@@ -2612,23 +2618,59 @@ start_cur_token:
 						}
 					}
 					if (argid != -1u) {
-						mtok = mtoken_new_arg(argid, state & ST_STR); // TODO: check for != NULL
+						mtok = mtoken_new_arg(argid, state & ST_STR);
+						if (!mtok) {
+							log_memory("failed to allocate new m-token (defining %s)\n", string_content(defname));
+							string_del(defname);
+							macro_del(&m);
+							src->st = PPST_NONE;
+							ret.tokt = PTOK_INVALID;
+							ret.loginfo = tok.loginfo;
+							ret.tokv.c = tok.tokv.c;
+							return ret;
+						}
 						state &= ~ST_STR;
 					} else {
-						mtok = mtoken_new_token(tok); // Token moved // TODO: check for != NULL
+						mtok = mtoken_new_token(tok); // Token moved
+						if (!mtok) {
+							log_memory("failed to allocate new m-token (defining %s)\n", string_content(defname));
+							string_del(defname);
+							macro_del(&m);
+							if (tok.tokt == PPTOK_NEWLINE) goto check_next_token;
+							else goto start_cur_token;
+						}
 						if (state & ST_STR) {
-							printf("Warning: invalid stringify before token (defining %s)\n", string_content(defname));
+							log_warning(&tok.loginfo, "invalid stringify before token (defining %s)\n", string_content(defname));
 							state &= ~ST_STR;
 						}
 					}
 					if (state & ST_CONCAT) {
 						mtoken_t *mtok2 = vector_last(mtoken, m.toks); // Guaranteed to exist before setting ST_CONCAT
-						mtok = mtoken_new_concat(mtok2, mtok); // TODO: check for != NULL
+						mtok = mtoken_new_concat(mtok2, mtok);
+						if (!mtok) {
+							log_memory("failed to allocate new m-token concatenation (defining %s)\n", string_content(defname));
+							string_del(defname);
+							macro_del(&m);
+							vector_pop_nodel(mtoken, m.toks);
+							src->st = PPST_NONE;
+							ret.tokt = PTOK_INVALID;
+							ret.loginfo = tok.loginfo;
+							ret.tokv.c = tok.tokv.c;
+							return ret;
+						}
 						vector_last(mtoken, m.toks) = mtok;
 						state &= ~ST_CONCAT;
 					} else {
-						// TODO: check for error
-						vector_push(mtoken, m.toks, mtok);
+						if (!vector_push(mtoken, m.toks, mtok)) {
+							log_memory("failed to add m-token (defining %s)\n", string_content(defname));
+							string_del(defname);
+							macro_del(&m);
+							src->st = PPST_NONE;
+							ret.tokt = PTOK_INVALID;
+							ret.loginfo = tok.loginfo;
+							ret.tokv.c = tok.tokv.c;
+							return ret;
+						}
 					}
 					// mtok moved to the vector
 					tok = ppsrc_next_token(src);
@@ -2638,41 +2680,47 @@ start_cur_token:
 				if (args) argid_map_del(args);
 				if (tok.tokt == PPTOK_INVALID) {
 					// Abort
+					log_error(&tok.loginfo, "unexpected invalid input token\n");
 					string_del(defname);
 					macro_del(&m);
-					if (tok.tokt == PPTOK_NEWLINE) goto check_next_token;
-					else goto start_cur_token;
+					src->st = PPST_NONE;
+					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
+					ret.tokv.c = tok.tokv.c;
+					return ret;
 				} else {
 					// NL and EOF have empty destructors
 					khiter_t kh_k;
 					int iret;
 					char *mname_dup = string_steal(defname);
-					// if (m.is_funlike) printf("OK for %s: %u args", mname_dup, m.nargs);
-					// else printf("OK for %s: no arg", mname_dup);
-					// printf("\n");
-					// vector_for(mtoken, it, m.toks) {
-					// 	printf("Macro token: ");
-					// 	print_macro_tok(*it);
-					// 	printf("\n");
-					// }
 					kh_k = kh_put(string_set, src->macros_defined, mname_dup, &iret);
-					// TODO: check iret?
-					if (iret >= 1) {
+					if (iret < 0) {
+						// Abort
+						log_memory("failed to remember macro %s as defined, aborting\n", mname_dup);
+						free(mname_dup);
+						macro_del(&m);
+						src->st = PPST_NONE;
+						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
+						ret.tokv.c = tok.tokv.c;
+						return ret;
+					} else if (iret > 0) {
 						mname_dup = strdup(mname_dup);
 					}
 					kh_k = kh_put(macros_map, src->macros_map, mname_dup, &iret);
 					if (iret < 0) {
 						// Abort
-						printf("Failed to remember macro %s, aborting\n", mname_dup);
+						log_memory("failed to remember macro %s, aborting\n", mname_dup);
 						free(mname_dup);
 						macro_del(&m);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = tok.tokv.c;
 						return ret;
 					} else if (iret == 0) {
 						// Ignore
-						// Too noisy  printf("Duplicated macro %s\n", mname_dup);
+						// Too noisy  log_warning(&tok.loginfo, "Duplicated macro %s\n", mname_dup);
 						free(mname_dup);
 						macro_del(&m);
 					} else {
@@ -2685,7 +2733,7 @@ start_cur_token:
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-					printf("Invalid token type %u after '#undef' preprocessor command\n", tok.tokt);
+					log_error(&tok.loginfo, "invalid token type %u after '#undef' preprocessor command\n", tok.tokt);
 					goto preproc_hash_err;
 				}
 				string_t *mname = tok.tokv.str;
@@ -2708,7 +2756,7 @@ start_cur_token:
 				if (tok.tokt == PPTOK_NEWLINE) goto check_next_token;
 				else goto start_cur_token;
 			} else if (!strcmp(string_content(tok.tokv.str), "error")) {
-				printf("Error: #error command (%s):", src->cur_file);
+				log_error(&tok.loginfo, "#error command:");
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
@@ -2740,10 +2788,11 @@ start_cur_token:
 				printf("\n");
 				vector_clear(ppsource, src->prep);
 				ret.tokt = PTOK_INVALID;
+				ret.loginfo = tok.loginfo;
 				ret.tokv.c = (char)EOF;
 				return ret;
 			} else if (!strcmp(string_content(tok.tokv.str), "warning")) {
-				printf("Warning: #warning command (%s):", src->cur_file);
+				log_warning(&tok.loginfo, "#warning command:");
 				tok = ppsrc_next_token(src);
 				while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 					switch (tok.tokt) {
@@ -2754,10 +2803,12 @@ start_cur_token:
 					case PPTOK_SYM:
 						printf("%s%s", (tok.tokv.sym == SYM_COMMA) ? "" : " ", sym2str[tok.tokv.sym]);
 						break;
+					case PPTOK_STRING:
+						printf(" %c%s%c", tok.tokv.sisstr ? '"' : '\'', string_content(tok.tokv.sstr), tok.tokv.sisstr ? '"' : '\'');
+						break;
 						
 					case PPTOK_INVALID:
 					case PPTOK_NUM:
-					case PPTOK_STRING:
 					case PPTOK_INCL:
 					case PPTOK_NEWLINE:
 					case PPTOK_BLANK:
@@ -2775,15 +2826,16 @@ start_cur_token:
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-					printf("Unknown pragma directive, skipping until EOL\n");
+					log_error(&tok.loginfo, "unknown pragma directive, skipping until EOL\n");
 					goto preproc_hash_err;
 				} else if (!strcmp(string_content(tok.tokv.str), "wrappers")) {
 					string_del(tok.tokv.str);
 					tok = ppsrc_next_token(src);
 					if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-						printf("Unknown pragma wrappers directive, skipping until EOL\n");
+						log_error(&tok.loginfo, "unknown pragma wrappers directive, skipping until EOL\n");
 						goto preproc_hash_err;
 					} else if (!strcmp(string_content(tok.tokv.str), "allow_ints_ext")) {
+						ret.loginfo = tok.loginfo;
 						while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 							preproc_token_del(&tok);
 							tok = ppsrc_next_token(src);
@@ -2798,11 +2850,12 @@ start_cur_token:
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-							printf("Invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
+							log_error(&tok.loginfo, "invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
 							goto preproc_hash_err;
 						}
 						src->st = PPST_NL;
 						ret.tokt = PTOK_PRAGMA;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.pragma.typ = PRAGMA_SIMPLE_SU;
 						ret.tokv.pragma.val = tok.tokv.str;
 						tok = ppsrc_next_token(src);
@@ -2820,11 +2873,12 @@ start_cur_token:
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-							printf("Invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
+							log_error(&tok.loginfo, "invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
 							goto preproc_hash_err;
 						}
 						src->st = PPST_PRAGMA_EXPLICIT;
 						ret.tokt = PTOK_PRAGMA;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.pragma.typ = PRAGMA_EXPLICIT_CONV;
 						ret.tokv.pragma.val = tok.tokv.str;
 						return ret;
@@ -2832,69 +2886,78 @@ start_cur_token:
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-							printf("Invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
+							log_error(&tok.loginfo, "invalid pragma wrappers explicit_simple directive, skipping until EOL\n");
 							goto preproc_hash_err;
 						}
 						src->st = PPST_PRAGMA_EXPLICIT;
 						ret.tokt = PTOK_PRAGMA;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.pragma.typ = PRAGMA_EXPLICIT_CONV_STRICT;
 						ret.tokv.pragma.val = tok.tokv.str;
 						return ret;
 					} else {
-						printf("Unknown pragma wrappers directive '%s', skipping until EOL\n", string_content(tok.tokv.str));
+						log_error(&tok.loginfo, "unknown pragma wrappers directive '%s', skipping until EOL\n", string_content(tok.tokv.str));
 						goto preproc_hash_err;
 					}
 				} else {
-					printf("Unknown pragma directive '%s', skipping until EOL\n", string_content(tok.tokv.str));
+					log_error(&tok.loginfo, "unknown pragma directive '%s', skipping until EOL\n", string_content(tok.tokv.str));
 					goto preproc_hash_err;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "if")) {
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #if source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #if source type %u\n", vector_last(ppsource, src->prep).srct);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = '\0';
 					return ret;
 				}
 				string_del(tok.tokv.str);
 				VECTOR(preproc) *cond = vector_new(preproc);
 				if (!cond) {
-					printf("Error: failed to allocate #if condition vector\n");
+					log_memory("failed to allocate #if condition vector\n");
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = '\0';
 					return ret;
 				}
 				tok = ppsrc_next_token(src);
+				loginfo_t li = tok.loginfo;
 				while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 					if (!vector_push(preproc, cond, tok)) {
-						printf("Error: failed to add token to #if condition vector\n");
+						log_memory("failed to add token to #if condition vector\n");
 						vector_del(preproc, cond);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
+						ret.loginfo = tok.loginfo;
 						ret.tokv.c = '\0';
 						return ret;
 					}
+					li.lineno_end = tok.loginfo.lineno_end ? tok.loginfo.lineno_end : tok.loginfo.lineno;
+					li.colno_end = tok.loginfo.colno_end ? tok.loginfo.colno_end : tok.loginfo.colno;
 					tok = ppsrc_next_token(src);
 				}
 				vector_trim(preproc, cond);
 				khash_t(string_set) *solved_macros = kh_init(string_set);
 				if (!solved_macros) {
-					printf("Error: failed to allocate #if solved_macros set\n");
+					log_memory("failed to allocate #if solved_macros set\n");
 					vector_del(preproc, cond);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = '\0';
 					return ret;
 				}
 				
-				VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, cond, solved_macros, src->macros_used);
+				VECTOR(preproc) *expanded = preproc_do_expand(&li, src->macros_map, cond, solved_macros, src->macros_used);
 				vector_del(preproc, cond);
 				macros_set_del(solved_macros);
 				if (!expanded) {
-					printf("Error: failed to expand #if condition\n");
+					log_error(&li, "Error: failed to expand #if condition\n");
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = '\0';
 					return ret;
 				}
@@ -2904,9 +2967,10 @@ start_cur_token:
 				int64_t res = preproc_eval(expanded, &st, src->target->size_long == 4);
 				vector_del(preproc, expanded);
 				if (!st) {
-					printf("Error: failed to evaluate #if condition (%s)\n", src->cur_file);
+					log_error(&li, "Error: failed to evaluate #if condition\n");
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
+					ret.loginfo = tok.loginfo;
 					ret.tokv.c = '\0';
 					return ret;
 				}
@@ -2922,13 +2986,13 @@ start_cur_token:
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "ifdef")) {
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #ifdef source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #ifdef source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-					printf("Invalid token type %u after '#ifdef' preprocessor command\n", tok.tokt);
+					log_error(&tok.loginfo, "invalid token type %u after '#ifdef' preprocessor command\n", tok.tokt);
 					goto preproc_hash_err;
 				}
 				khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str));
@@ -2952,13 +3016,13 @@ start_cur_token:
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "ifndef")) {
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #ifndef source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #ifndef source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				string_del(tok.tokv.str);
 				tok = ppsrc_next_token(src);
 				if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
-					printf("Invalid token type %u after '#ifndef' preprocessor command\n", tok.tokt);
+					log_error(&tok.loginfo, "invalid token type %u after '#ifndef' preprocessor command\n", tok.tokt);
 					goto preproc_hash_err;
 				}
 				khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str));
@@ -2983,7 +3047,7 @@ start_cur_token:
 			} else if (!strcmp(string_content(tok.tokv.str), "elif")) {
 				// We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #elif source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #elif source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) {
@@ -2991,13 +3055,13 @@ start_cur_token:
 					--vector_last(ppsource, src->prep).srcv.prep.ok_depth;
 					goto preproc_hash_err_goto;
 				} else {
-					printf("Warning: unexpected #elif preprocessor command\n");
+					log_warning(&tok.loginfo, "unexpected #elif preprocessor command\n");
 					goto preproc_hash_err;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "elifdef")) {
 				// We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #elifdef source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #elifdef source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				string_del(tok.tokv.str);
@@ -3017,13 +3081,13 @@ start_cur_token:
 					--vector_last(ppsource, src->prep).srcv.prep.ok_depth;
 					goto preproc_hash_err_goto;
 				} else {
-					printf("Warning: unexpected #elifdef preprocessor command\n");
+					log_warning(&tok.loginfo, "unexpected #elifdef preprocessor command\n");
 					goto preproc_hash_err;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "elifndef")) {
 				// We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #elifndef source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #elifndef source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				string_del(tok.tokv.str);
@@ -3043,13 +3107,13 @@ start_cur_token:
 					--vector_last(ppsource, src->prep).srcv.prep.ok_depth;
 					goto preproc_hash_err_goto;
 				} else {
-					printf("Warning: unexpected #elifndef preprocessor command\n");
+					log_warning(&tok.loginfo, "unexpected #elifndef preprocessor command\n");
 					goto preproc_hash_err;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "else")) {
 				// We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #else source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #else source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) {
@@ -3057,24 +3121,24 @@ start_cur_token:
 					--vector_last(ppsource, src->prep).srcv.prep.ok_depth;
 					goto preproc_hash_err_goto;
 				} else {
-					printf("Warning: unexpected #else preprocessor command\n");
+					log_warning(&tok.loginfo, "unexpected #else preprocessor command\n");
 					goto preproc_hash_err;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "endif")) {
 				if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) {
-					printf("Error: invalid #endif source type %u\n", vector_last(ppsource, src->prep).srct);
+					log_error(&tok.loginfo, "invalid #endif source type %u\n", vector_last(ppsource, src->prep).srct);
 					goto preproc_hash_err;
 				}
 				if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) {
 					--vector_last(ppsource, src->prep).srcv.prep.ok_depth;
 					--vector_last(ppsource, src->prep).srcv.prep.cond_depth;
 				} else {
-					printf("Warning: unexpected #endif preprocessor command\n");
+					log_warning(&tok.loginfo, "unexpected #endif preprocessor command\n");
 				}
 				goto preproc_hash_err;
 			}
 			
-			printf("Unknown pp command %s (%s), skipping until EOL\n", string_content(tok.tokv.str), src->cur_file);
+			log_warning(&tok.loginfo, "Unknown preprocessor command %s, skipping until EOL\n", string_content(tok.tokv.str));
 		preproc_hash_err:
 			while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
 				preproc_token_del(&tok);
@@ -3093,12 +3157,14 @@ start_cur_token:
 		}
 		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_SYM;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.sym = tok.tokv.sym;
 		return ret;
 	case PPTOK_NEWLINE:
 		if (src->st == PPST_PRAGMA_EXPLICIT) {
 			src->st = PPST_NL;
 			ret.tokt = PTOK_SYM;
+			ret.loginfo = tok.loginfo;
 			ret.tokv.sym = SYM_SEMICOLON;
 			return ret;
 		}
@@ -3107,18 +3173,22 @@ start_cur_token:
 	case PPTOK_BLANK:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_START_LINE_COMMENT:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
+		ret.loginfo = tok.loginfo;
 		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_EOF:
 		if ((vector_last(ppsource, src->prep).srct == PPSRC_PREPARE) && vector_last(ppsource, src->prep).srcv.prep.cond_depth) {
-			printf("Error: file ended before closing all conditionals (ignoring)\n");
+			log_warning(&tok.loginfo, "file ended before closing all conditionals (ignoring)\n");
 		}
-		// printf("Closing %s\n", src->cur_file);
+#ifdef LOG_CLOSE
+		printf("Closing %s\n", src->cur_file);
+#endif
 		if (vector_last(ppsource, src->prep).srct == PPSRC_PREPARE) {
 			if (src->dirname) free(src->dirname);
 			if (src->cur_file) free(src->cur_file);
@@ -3133,7 +3203,9 @@ start_cur_token:
 		if (src->st == PPST_PRAGMA_EXPLICIT) {
 			src->st = PPST_NL;
 			ret.tokt = PTOK_SYM;
+			ret.loginfo = (loginfo_t){0}; // The token's loginfo may have been deleted by the vector_pop above
 			ret.tokv.sym = SYM_SEMICOLON;
+			// Empty destructor
 			return ret;
 		}
 		src->st = PPST_NL; // Should be redundant since TOK_NEWLINE is added before TOK_EOF if required
@@ -3142,9 +3214,10 @@ start_cur_token:
 		goto check_next_token;
 	
 	default:
-		printf("Unknown next pp token type %u, sending EOF\n", tok.tokt);
-		ret.tokt = PTOK_EOF;
-		ret.tokv.c = (char)EOF;
+		log_error(&tok.loginfo, "unknown preprocessor token type %u, sending INVALID\n", tok.tokt);
+		ret.tokt = PTOK_INVALID;
+		ret.loginfo = tok.loginfo;
+		ret.tokv.c = '\0';
 		return ret;
 	}
 }
@@ -3155,7 +3228,7 @@ proc_token_t proc_next_token(preproc_t *src) {
 			proc_token_t ret2 = proc_next_token_aux(src);
 			if ((ret2.tokt == PTOK_STRING) && ret2.tokv.sisstr) {
 				if (!string_add_string(ret.tokv.sstr, ret2.tokv.sstr)) {
-					printf("Error: failed to concatenate adjacent strings\n");
+					log_memory("failed to concatenate adjacent strings\n");
 					string_del(ret.tokv.sstr);
 					string_del(ret2.tokv.sstr);
 					src->st = PPST_NONE;
@@ -3164,9 +3237,11 @@ proc_token_t proc_next_token(preproc_t *src) {
 					return ret;
 				}
 				string_del(ret2.tokv.sstr);
+				ret.loginfo.lineno_end = ret2.loginfo.lineno_end ? ret2.loginfo.lineno_end : ret2.loginfo.lineno;
+				ret.loginfo.colno_end = ret2.loginfo.colno_end ? ret2.loginfo.colno_end : ret2.loginfo.colno;
 			} else {
 				if (!proc_unget_token(src, &ret2)) {
-					printf("Error: failed to unget token next to string token\n");
+					log_memory("failed to unget token next to string token\n");
 					string_del(ret.tokv.sstr);
 					proc_token_del(&ret2);
 					src->st = PPST_NONE;