about summary refs log tree commit diff stats
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
parent703d19b256bc533d73cf4f206e5c443579aaf4c1 (diff)
downloadbox64-4715ef2633534a0427a1e92006a09fde5c3faf44.tar.gz
box64-4715ef2633534a0427a1e92006a09fde5c3faf44.zip
[WRAPPERHELPER] Added box32 and line number support in the wrapperhelper (#1890)
-rw-r--r--.gitignore1
-rw-r--r--wrapperhelper/README.md15
-rw-r--r--wrapperhelper/include-override/aarch64/stdc-predef.h8
-rw-r--r--wrapperhelper/include-override/common/stdc-predef.h2
-rw-r--r--wrapperhelper/include-override/x86/bits/pthreadtypes-arch.h55
-rw-r--r--wrapperhelper/include-override/x86/stdc-predef.h401
-rw-r--r--wrapperhelper/include-override/x86_64/stdc-predef.h10
-rw-r--r--wrapperhelper/src/generator.c919
-rw-r--r--wrapperhelper/src/generator.h8
-rw-r--r--wrapperhelper/src/lang.c324
-rw-r--r--wrapperhelper/src/lang.h10
-rw-r--r--wrapperhelper/src/log.c80
-rw-r--r--wrapperhelper/src/log.h27
-rw-r--r--wrapperhelper/src/machine.c267
-rw-r--r--wrapperhelper/src/machine.h5
-rw-r--r--wrapperhelper/src/main.c101
-rw-r--r--wrapperhelper/src/parse.c1064
-rw-r--r--wrapperhelper/src/prepare.c341
-rw-r--r--wrapperhelper/src/prepare.h2
-rw-r--r--wrapperhelper/src/preproc.c1757
20 files changed, 3626 insertions, 1771 deletions
diff --git a/.gitignore b/.gitignore
index 23c58d48..8d8f4989 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,7 @@ backup/
 /wrapperhelper/sanundefined
 /wrapperhelper/src/machine.gen
 /wrapperhelper/*.h
+!/wrapperhelper/example-libc.h
 
 # macOS
 .DS_Store
diff --git a/wrapperhelper/README.md b/wrapperhelper/README.md
index e543af37..282fdf6c 100644
--- a/wrapperhelper/README.md
+++ b/wrapperhelper/README.md
@@ -25,7 +25,9 @@ To use the wrapper helper, run the following command in the folder containing th
 bin/wrapperhelper -I/path/to/system/include "path_to_support_file" "path_to_private.h" "path_to_private.h"
 ```
 
-You may add as many `-I` options as needed.
+You may add as many `-I` options as needed. The folders `include-override/<arch>` and `include-override/common` are always prioritized, as if they appeared first in the command line.
+
+You may also use the `-32` and `-64` switches to generate `box32` or `box64` files respectively. Alterately, you can use the `--emu arch` and `--target arch` options to select more precisely the emlated and executing platforms, though only `x86`, `x86_64` and `aarch64` are supported for now. By default, everything is as if `-64` was supplied.
 
 The first file is a `C` file containing every declaration required. The second file is the "requests" input. The third file is the output file, which may be a different file.
 
@@ -100,30 +102,25 @@ The reading and writing of the `_private.h` files is implemented in `generator.c
 
 ## Known issues
 
-This project only works for `box64`; more work is required for this to be compatible with `box32`.
-
 Only native structures are read. This means that the current version of `wrapperhelper` does not detect an issue when a structure has different members or alignments in two different architectures.
 
 No checking of signatures under `#ifdef`s is made.
 
-Line numbers are missing entirely. For most errors, the corresponding file is not written with the error message.
-
 Phase 5 is partially implemented, but could be greatly improved.
 
 The following features are missing from the generator:
-- Large structures as a parameter
-- Large structure as a return type (more than 16 bytes)
+- Structures with at least two elements as a parameter
+- Large structure as a return type (more than 16 bytes on 64bits, or 8 bytes on 32bits)
 - Atomic types
 
 The following features are missing from the preprocessor:
 - General token concatenation (though the concatenation of two `PTOK_IDENT` works without issue)
 - Stringify
 - Skipped unexpected token warnings
-- Proper out-of-memory error handling
 
 The following features are missing from the parser:
-- `_Atomic(type-name)`
 - `_Alignas(type-name)` and `_Alignas(constant-expression)`
 - `(type-name){initializer-list}`
 - Old style function declarations
 - Function definitions are ignored, not parsed
+- Attributes are ignored everywhere (with a  `#define __attribute__(_)`)
diff --git a/wrapperhelper/include-override/aarch64/stdc-predef.h b/wrapperhelper/include-override/aarch64/stdc-predef.h
index b2609a3c..df1d78b5 100644
--- a/wrapperhelper/include-override/aarch64/stdc-predef.h
+++ b/wrapperhelper/include-override/aarch64/stdc-predef.h
@@ -123,16 +123,16 @@
 //#define __BITINT_MAXWIDTH__ 65535
 //#define __CHAR16_TYPE__ short unsigned int
 //#define __CHAR32_TYPE__ unsigned int
-#define __INT8_C(c) c
+//#define __INT8_C(c) c
 #define __INT8_MAX__ 0x7f
 //#define __INT8_TYPE__ signed char
-#define __INT16_C(c) c
+//#define __INT16_C(c) c
 #define __INT16_MAX__ 0x7fff
 //#define __INT16_TYPE__ short int
-#define __INT32_C(c) c
+//#define __INT32_C(c) c
 #define __INT32_MAX__ 0x7fffffff
 //#define __INT32_TYPE__ int
-#define __INT64_C(c) c ## L
+//#define __INT64_C(c) c ## L
 #define __INT64_MAX__ 0x7fffffffffffffffL
 //#define __INT64_TYPE__ long int
 #define __INT_FAST8_MAX__ 0x7f
diff --git a/wrapperhelper/include-override/common/stdc-predef.h b/wrapperhelper/include-override/common/stdc-predef.h
index ace6ec7d..3484bbce 100644
--- a/wrapperhelper/include-override/common/stdc-predef.h
+++ b/wrapperhelper/include-override/common/stdc-predef.h
@@ -1,4 +1,2 @@
 // Ignore all attributes
 #define __attribute__(_)
-
-#include_next "stdc-predef.h"
diff --git a/wrapperhelper/include-override/x86/bits/pthreadtypes-arch.h b/wrapperhelper/include-override/x86/bits/pthreadtypes-arch.h
new file mode 100644
index 00000000..b23ff3b0
--- /dev/null
+++ b/wrapperhelper/include-override/x86/bits/pthreadtypes-arch.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _BITS_PTHREADTYPES_ARCH_H
+#define _BITS_PTHREADTYPES_ARCH_H	1
+
+#include <bits/wordsize.h>
+
+#ifdef __x86_64__
+# if __WORDSIZE == 64
+#  define __SIZEOF_PTHREAD_MUTEX_T 40
+#  define __SIZEOF_PTHREAD_ATTR_T 56
+#  define __SIZEOF_PTHREAD_RWLOCK_T 56
+#  define __SIZEOF_PTHREAD_BARRIER_T 32
+# else
+#  define __SIZEOF_PTHREAD_MUTEX_T 32
+#  define __SIZEOF_PTHREAD_ATTR_T 32
+#  define __SIZEOF_PTHREAD_RWLOCK_T 44
+#  define __SIZEOF_PTHREAD_BARRIER_T 20
+# endif
+#else
+# define __SIZEOF_PTHREAD_MUTEX_T 24
+# define __SIZEOF_PTHREAD_ATTR_T 36
+# define __SIZEOF_PTHREAD_RWLOCK_T 32
+# define __SIZEOF_PTHREAD_BARRIER_T 20
+#endif
+#define __SIZEOF_PTHREAD_MUTEXATTR_T 4
+#define __SIZEOF_PTHREAD_COND_T 48
+#define __SIZEOF_PTHREAD_CONDATTR_T 4
+#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
+#define __SIZEOF_PTHREAD_BARRIERATTR_T 4
+
+#define __LOCK_ALIGNMENT
+#define __ONCE_ALIGNMENT
+
+#ifndef __x86_64__
+/* Extra attributes for the cleanup functions.  */
+# define __cleanup_fct_attribute __attribute__ ((__regparm__ (1)))
+#endif
+
+#endif	/* bits/pthreadtypes.h */
diff --git a/wrapperhelper/include-override/x86/stdc-predef.h b/wrapperhelper/include-override/x86/stdc-predef.h
new file mode 100644
index 00000000..4f59430a
--- /dev/null
+++ b/wrapperhelper/include-override/x86/stdc-predef.h
@@ -0,0 +1,401 @@
+// C standard
+#define __STDC__         1
+#define __STDC_HOSTED__  1
+#define __STDC_UTF_16__  1
+#define __STDC_UTF_32__  1
+#define __STDC_VERSION__ 201710L
+// Generic x86_64 infos
+#define __ELF__                 1
+#define __NO_INLINE__           1
+#define __ORDER_BIG_ENDIAN__    4321
+#define __ORDER_LITTLE_ENDIAN__ 1234
+#define __ORDER_PDP_ENDIAN__    3412
+#define __PIC__                 2
+#define __pic__                 2
+#define __PIE__                 2
+#define __pie__                 2
+#define __USER_LABEL_PREFIX__
+#define __gnu_linux__           1
+#define __linux__               1
+#define __linux                 1
+#define linux                   1
+#define __unix__                1
+#define __unix                  1
+#define unix                    1
+// GCC
+//#define __GCC_ASM_FLAG_OUTPUTS__ 1
+//#define __GCC_ATOMIC_BOOL_LOCK_FREE 2
+//#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
+//#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
+//#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
+//#define __GCC_ATOMIC_INT_LOCK_FREE 2
+//#define __GCC_ATOMIC_LLONG_LOCK_FREE 2
+//#define __GCC_ATOMIC_LONG_LOCK_FREE 2
+//#define __GCC_ATOMIC_POINTER_LOCK_FREE 2
+//#define __GCC_ATOMIC_SHORT_LOCK_FREE 2
+//#define __GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1
+//#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
+//#define __GCC_CONSTRUCTIVE_SIZE 64
+//#define __GCC_DESTRUCTIVE_SIZE 64
+//#define __GCC_HAVE_DWARF2_CFI_ASM 1
+//#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
+//#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
+//#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
+//#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
+//#define __GCC_IEC_559 2
+//#define __GCC_IEC_559_COMPLEX 2
+//#define __GNUC__ 14
+//#define __GNUC_EXECUTION_CHARSET_NAME "UTF-8"
+//#define __GNUC_MINOR__ 2
+//#define __GNUC_PATCHLEVEL__ 1
+//#define __GNUC_STDC_INLINE__ 1
+//#define __GNUC_WIDE_EXECUTION_CHARSET_NAME "UTF-32LE"
+//#define __GXX_ABI_VERSION 1019
+//#define __PRAGMA_REDEFINE_EXTNAME 1
+//#define __VERSION__ "14.2.1 20240805"
+// Specific x86_64 architecture
+#define __FINITE_MATH_ONLY__          0
+#define __HAVE_SPECULATION_SAFE_VALUE 1
+#define __ILP32__                     1
+#define _ILP32                        1
+#define __REGISTER_PREFIX__
+#define __FXSR__                      1
+#define __LAHF_SAHF__ 1
+#define __MMX__                       1
+#define __SEG_FS                      1
+#define __SEG_GS                      1
+#define __SSE__                       1
+#define __SSE2__                      1
+#define __SSP_STRONG__                3
+#define __code_model_32__             1
+#define __i386__                      1
+#define __i386                        1
+#define i386                          1
+#define __k8__                        1
+#define __k8                          1
+// Atomic
+#define __ATOMIC_RELAXED     0
+#define __ATOMIC_CONSUME     1
+#define __ATOMIC_ACQUIRE     2
+#define __ATOMIC_RELEASE     3
+#define __ATOMIC_ACQ_REL     4
+#define __ATOMIC_SEQ_CST     5
+#define __ATOMIC_HLE_ACQUIRE 65536
+#define __ATOMIC_HLE_RELEASE 131072
+// Metainfo on types
+#define __BIGGEST_ALIGNMENT__  16
+#define __BYTE_ORDER__         __ORDER_LITTLE_ENDIAN__
+#define __CHAR_BIT__           8
+#define __FLOAT_WORD_ORDER__   __ORDER_LITTLE_ENDIAN__
+#define __SIZEOF_SHORT__       2
+#define __SIZEOF_WCHAR_T__     4
+#define __SIZEOF_INT__         4
+#define __SIZEOF_WINT_T__      4
+#define __SIZEOF_LONG__        4
+#define __SIZEOF_LONG_LONG__   8
+#define __SIZEOF_POINTER__     4
+#define __SIZEOF_PTRDIFF_T__   4
+#define __SIZEOF_SIZE_T__      4
+#define __SIZEOF_FLOAT__       4
+#define __SIZEOF_DOUBLE__      8
+#define __SIZEOF_LONG_DOUBLE__ 12
+#define __SIZEOF_FLOAT80__     12
+#define __SIZEOF_FLOAT128__    16
+// Integers
+//#define __BITINT_MAXWIDTH__ 65535
+//#define __CHAR16_TYPE__ short unsigned int
+//#define __CHAR32_TYPE__ unsigned int
+//#define __INT8_C(c) c
+#define __INT8_MAX__ 0x7f
+//#define __INT8_TYPE__ signed char
+//#define __INT16_C(c) c
+#define __INT16_MAX__ 0x7fff
+//#define __INT16_TYPE__ short int
+//#define __INT32_C(c) c
+#define __INT32_MAX__ 0x7fffffff
+//#define __INT32_TYPE__ int
+//#define __INT64_C(c) c ## LL
+#define __INT64_MAX__ 0x7fffffffffffffffLL
+//#define __INT64_TYPE__ long long int
+#define __INT_FAST8_MAX__ 0x7f
+//#define __INT_FAST8_TYPE__ signed char
+#define __INT_FAST8_WIDTH__ 8
+#define __INT_FAST16_MAX__ 0x7fffffff
+//#define __INT_FAST16_TYPE__ int
+#define __INT_FAST16_WIDTH__ 32
+#define __INT_FAST32_MAX__ 0x7fffffff
+//#define __INT_FAST32_TYPE__ int
+#define __INT_FAST32_WIDTH__ 32
+#define __INT_FAST64_MAX__ 0x7fffffffffffffffLL
+//#define __INT_FAST64_TYPE__ long long int
+#define __INT_FAST64_WIDTH__ 64
+#define __INT_LEAST8_MAX__ 0x7f
+//#define __INT_LEAST8_TYPE__ signed char
+#define __INT_LEAST8_WIDTH__ 8
+#define __INT_LEAST16_MAX__ 0x7fff
+//#define __INT_LEAST16_TYPE__ short int
+#define __INT_LEAST16_WIDTH__ 16
+#define __INT_LEAST32_MAX__ 0x7fffffff
+//#define __INT_LEAST32_TYPE__ int
+#define __INT_LEAST32_WIDTH__ 32
+#define __INT_LEAST64_MAX__ 0x7fffffffffffffffLL
+//#define __INT_LEAST64_TYPE__ long long int
+#define __INT_LEAST64_WIDTH__ 64
+#define __INT_MAX__ 0x7fffffff
+#define __INT_WIDTH__ 32
+#define __INTMAX_C(c) c ## LL
+#define __INTMAX_MAX__ 0x7fffffffffffffffLL
+//#define __INTMAX_TYPE__ long long int
+#define __INTMAX_WIDTH__ 64
+#define __INTPTR_MAX__ 0x7fffffff
+//#define __INTPTR_TYPE__ int
+#define __INTPTR_WIDTH__ 32
+#define __LONG_LONG_MAX__ 0x7fffffffffffffffLL
+#define __LONG_LONG_WIDTH__ 64
+#define __LONG_MAX__ 0x7fffffffL
+#define __LONG_WIDTH__ 32
+#define __PTRDIFF_MAX__ 0x7fffffff
+//#define __PTRDIFF_TYPE__ int
+#define __PTRDIFF_WIDTH__ 32
+#define __SCHAR_MAX__ 0x7f
+#define __SCHAR_WIDTH__ 8
+#define __SHRT_MAX__ 0x7fff
+#define __SHRT_WIDTH__ 16
+#define __SIG_ATOMIC_MAX__ 0x7fffffff
+#define __SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1)
+//#define __SIG_ATOMIC_TYPE__ int
+#define __SIG_ATOMIC_WIDTH__ 32
+#define __SIZE_MAX__ 0xffffffffU
+//#define __SIZE_TYPE__ unsigned int
+#define __SIZE_WIDTH__ 32
+#define __UINT8_C(c) c
+#define __UINT8_MAX__ 0xff
+//#define __UINT8_TYPE__ unsigned char
+#define __UINT16_C(c) c
+#define __UINT16_MAX__ 0xffff
+//#define __UINT16_TYPE__ short unsigned int
+#define __UINT32_C(c) c ## U
+#define __UINT32_MAX__ 0xffffffffU
+//#define __UINT32_TYPE__ unsigned int
+#define __UINT64_C(c) c ## ULL
+#define __UINT64_MAX__ 0xffffffffffffffffULL
+//#define __UINT64_TYPE__ long long unsigned int
+#define __UINT_FAST8_MAX__ 0xff
+//#define __UINT_FAST8_TYPE__ unsigned char
+#define __UINT_FAST16_MAX__ 0xffffffffU
+//#define __UINT_FAST16_TYPE__ unsigned int
+#define __UINT_FAST32_MAX__ 0xffffffffU
+//#define __UINT_FAST32_TYPE__ unsigned int
+#define __UINT_FAST64_MAX__ 0xffffffffffffffffULL
+//#define __UINT_FAST64_TYPE__ long long unsigned int
+#define __UINT_LEAST8_MAX__ 0xff
+//#define __UINT_LEAST8_TYPE__ unsigned char
+#define __UINT_LEAST16_MAX__ 0xffff
+//#define __UINT_LEAST16_TYPE__ short unsigned int
+#define __UINT_LEAST32_MAX__ 0xffffffffU
+//#define __UINT_LEAST32_TYPE__ unsigned int
+#define __UINT_LEAST64_MAX__ 0xffffffffffffffffULL
+//#define __UINT_LEAST64_TYPE__ long long unsigned int
+#define __UINTMAX_C(c) c ## ULL
+#define __UINTMAX_MAX__ 0xffffffffffffffffULL
+//#define __UINTMAX_TYPE__ long long unsigned int
+#define __UINTPTR_MAX__ 0xffffffffU
+//#define __UINTPTR_TYPE__ unsigned int
+#define __WCHAR_MAX__ 0x7fffffffL
+#define __WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
+//#define __WCHAR_TYPE__ long int
+#define __WCHAR_WIDTH__ 32
+#define __WINT_MAX__ 0xffffffffU
+#define __WINT_MIN__ 0U
+//#define __WINT_TYPE__ unsigned int
+#define __WINT_WIDTH__ 32
+// Floats
+//#define __BFLT16_DECIMAL_DIG__ 4
+//#define __BFLT16_DENORM_MIN__ 9.18354961579912115600575419704879436e-41BF16
+//#define __BFLT16_DIG__ 2
+//#define __BFLT16_EPSILON__ 7.81250000000000000000000000000000000e-3BF16
+//#define __BFLT16_HAS_DENORM__ 1
+//#define __BFLT16_HAS_INFINITY__ 1
+//#define __BFLT16_HAS_QUIET_NAN__ 1
+//#define __BFLT16_IS_IEC_60559__ 0
+//#define __BFLT16_MANT_DIG__ 8
+//#define __BFLT16_MAX_10_EXP__ 38
+//#define __BFLT16_MAX__ 3.38953138925153547590470800371487867e+38BF16
+//#define __BFLT16_MAX_EXP__ 128
+//#define __BFLT16_MIN_10_EXP__ (-37)
+//#define __BFLT16_MIN__ 1.17549435082228750796873653722224568e-38BF16
+//#define __BFLT16_MIN_EXP__ (-125)
+//#define __BFLT16_NORM_MAX__ 3.38953138925153547590470800371487867e+38BF16
+#define __DBL_DECIMAL_DIG__ 17
+#define __DBL_DENORM_MIN__ ((double)4.94065645841246544176568792868221372e-324L)
+#define __DBL_DIG__ 15
+#define __DBL_EPSILON__ ((double)2.22044604925031308084726333618164062e-16L)
+#define __DBL_HAS_DENORM__ 1
+#define __DBL_HAS_INFINITY__ 1
+#define __DBL_HAS_QUIET_NAN__ 1
+#define __DBL_IS_IEC_60559__ 1
+#define __DBL_MANT_DIG__ 53
+#define __DBL_MAX_10_EXP__ 308
+#define __DBL_MAX__ ((double)1.79769313486231570814527423731704357e+308L)
+#define __DBL_MAX_EXP__ 1024
+#define __DBL_MIN_10_EXP__ (-307)
+#define __DBL_MIN__ ((double)2.22507385850720138309023271733240406e-308L)
+#define __DBL_MIN_EXP__ (-1021)
+#define __DBL_NORM_MAX__ ((double)1.79769313486231570814527423731704357e+308L)
+//#define __DEC32_EPSILON__ 1E-6DF
+//#define __DEC32_MANT_DIG__ 7
+//#define __DEC32_MAX__ 9.999999E96DF
+//#define __DEC32_MAX_EXP__ 97
+//#define __DEC32_MIN__ 1E-95DF
+//#define __DEC32_MIN_EXP__ (-94)
+//#define __DEC32_SUBNORMAL_MIN__ 0.000001E-95DF
+//#define __DEC64_EPSILON__ 1E-15DD
+//#define __DEC64_MANT_DIG__ 16
+//#define __DEC64_MAX__ 9.999999999999999E384DD
+//#define __DEC64_MAX_EXP__ 385
+//#define __DEC64_MIN__ 1E-383DD
+//#define __DEC64_MIN_EXP__ (-382)
+//#define __DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD
+//#define __DEC128_EPSILON__ 1E-33DL
+//#define __DEC128_MANT_DIG__ 34
+//#define __DEC128_MAX__ 9.999999999999999999999999999999999E6144DL
+//#define __DEC128_MAX_EXP__ 6145
+//#define __DEC128_MIN__ 1E-6143DL
+//#define __DEC128_MIN_EXP__ (-6142)
+//#define __DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL
+//#define __DEC_EVAL_METHOD__ 2
+//#define __DECIMAL_BID_FORMAT__ 1
+//#define __DECIMAL_DIG__ 21
+//#define __FLT16_DECIMAL_DIG__ 5
+//#define __FLT16_DENORM_MIN__ 5.96046447753906250000000000000000000e-8F16
+//#define __FLT16_DIG__ 3
+//#define __FLT16_EPSILON__ 9.76562500000000000000000000000000000e-4F16
+//#define __FLT16_HAS_DENORM__ 1
+//#define __FLT16_HAS_INFINITY__ 1
+//#define __FLT16_HAS_QUIET_NAN__ 1
+//#define __FLT16_IS_IEC_60559__ 1
+//#define __FLT16_MANT_DIG__ 11
+//#define __FLT16_MAX_10_EXP__ 4
+//#define __FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16
+//#define __FLT16_MAX_EXP__ 16
+//#define __FLT16_MIN_10_EXP__ (-4)
+//#define __FLT16_MIN__ 6.10351562500000000000000000000000000e-5F16
+//#define __FLT16_MIN_EXP__ (-13)
+//#define __FLT16_NORM_MAX__ 6.55040000000000000000000000000000000e+4F16
+//#define __FLT32_DECIMAL_DIG__ 9
+//#define __FLT32_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F32
+//#define __FLT32_DIG__ 6
+//#define __FLT32_EPSILON__ 1.19209289550781250000000000000000000e-7F32
+//#define __FLT32_HAS_DENORM__ 1
+//#define __FLT32_HAS_INFINITY__ 1
+//#define __FLT32_HAS_QUIET_NAN__ 1
+//#define __FLT32_IS_IEC_60559__ 1
+//#define __FLT32_MANT_DIG__ 24
+//#define __FLT32_MAX_10_EXP__ 38
+//#define __FLT32_MAX__ 3.40282346638528859811704183484516925e+38F32
+//#define __FLT32_MAX_EXP__ 128
+//#define __FLT32_MIN_10_EXP__ (-37)
+//#define __FLT32_MIN__ 1.17549435082228750796873653722224568e-38F32
+//#define __FLT32_MIN_EXP__ (-125)
+//#define __FLT32_NORM_MAX__ 3.40282346638528859811704183484516925e+38F32
+//#define __FLT32X_DECIMAL_DIG__ 17
+//#define __FLT32X_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F32x
+//#define __FLT32X_DIG__ 15
+//#define __FLT32X_EPSILON__ 2.22044604925031308084726333618164062e-16F32x
+//#define __FLT32X_HAS_DENORM__ 1
+//#define __FLT32X_HAS_INFINITY__ 1
+//#define __FLT32X_HAS_QUIET_NAN__ 1
+//#define __FLT32X_IS_IEC_60559__ 1
+//#define __FLT32X_MANT_DIG__ 53
+//#define __FLT32X_MAX_10_EXP__ 308
+//#define __FLT32X_MAX__ 1.79769313486231570814527423731704357e+308F32x
+//#define __FLT32X_MAX_EXP__ 1024
+//#define __FLT32X_MIN_10_EXP__ (-307)
+//#define __FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x
+//#define __FLT32X_MIN_EXP__ (-1021)
+//#define __FLT32X_NORM_MAX__ 1.79769313486231570814527423731704357e+308F32x
+//#define __FLT64_DECIMAL_DIG__ 17
+//#define __FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64
+//#define __FLT64_DIG__ 15
+//#define __FLT64_EPSILON__ 2.22044604925031308084726333618164062e-16F64
+//#define __FLT64_HAS_DENORM__ 1
+//#define __FLT64_HAS_INFINITY__ 1
+//#define __FLT64_HAS_QUIET_NAN__ 1
+//#define __FLT64_IS_IEC_60559__ 1
+//#define __FLT64_MANT_DIG__ 53
+//#define __FLT64_MAX_10_EXP__ 308
+//#define __FLT64_MAX__ 1.79769313486231570814527423731704357e+308F64
+//#define __FLT64_MAX_EXP__ 1024
+//#define __FLT64_MIN_10_EXP__ (-307)
+//#define __FLT64_MIN__ 2.22507385850720138309023271733240406e-308F64
+//#define __FLT64_MIN_EXP__ (-1021)
+//#define __FLT64_NORM_MAX__ 1.79769313486231570814527423731704357e+308F64
+//#define __FLT64X_DECIMAL_DIG__ 21
+//#define __FLT64X_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951F64x
+//#define __FLT64X_DIG__ 18
+//#define __FLT64X_EPSILON__ 1.08420217248550443400745280086994171e-19F64x
+//#define __FLT64X_HAS_DENORM__ 1
+//#define __FLT64X_HAS_INFINITY__ 1
+//#define __FLT64X_HAS_QUIET_NAN__ 1
+//#define __FLT64X_IS_IEC_60559__ 1
+//#define __FLT64X_MANT_DIG__ 64
+//#define __FLT64X_MAX_10_EXP__ 4932
+//#define __FLT64X_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+//#define __FLT64X_MAX_EXP__ 16384
+//#define __FLT64X_MIN_10_EXP__ (-4931)
+//#define __FLT64X_MIN__ 3.36210314311209350626267781732175260e-4932F64x
+//#define __FLT64X_MIN_EXP__ (-16381)
+//#define __FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+//#define __FLT128_DECIMAL_DIG__ 36
+//#define __FLT128_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966F128
+//#define __FLT128_DIG__ 33
+//#define __FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128
+//#define __FLT128_HAS_DENORM__ 1
+//#define __FLT128_HAS_INFINITY__ 1
+//#define __FLT128_HAS_QUIET_NAN__ 1
+//#define __FLT128_IS_IEC_60559__ 1
+//#define __FLT128_MANT_DIG__ 113
+//#define __FLT128_MAX_10_EXP__ 4932
+//#define __FLT128_MAX__ 1.18973149535723176508575932662800702e+4932F128
+//#define __FLT128_MAX_EXP__ 16384
+//#define __FLT128_MIN_10_EXP__ (-4931)
+//#define __FLT128_MIN__ 3.36210314311209350626267781732175260e-4932F128
+//#define __FLT128_MIN_EXP__ (-16381)
+//#define __FLT128_NORM_MAX__ 1.18973149535723176508575932662800702e+4932F128
+#define __FLT_DECIMAL_DIG__ 9
+#define __FLT_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F
+#define __FLT_DIG__ 6
+#define __FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F
+#define __FLT_EVAL_METHOD__ 2
+#define __FLT_EVAL_METHOD_TS_18661_3__ 2
+#define __FLT_HAS_DENORM__ 1
+#define __FLT_HAS_INFINITY__ 1
+#define __FLT_HAS_QUIET_NAN__ 1
+#define __FLT_IS_IEC_60559__ 1
+#define __FLT_MANT_DIG__ 24
+#define __FLT_MAX_10_EXP__ 38
+#define __FLT_MAX__ 3.40282346638528859811704183484516925e+38F
+#define __FLT_MAX_EXP__ 128
+#define __FLT_MIN_10_EXP__ (-37)
+#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F
+#define __FLT_MIN_EXP__ (-125)
+#define __FLT_NORM_MAX__ 3.40282346638528859811704183484516925e+38F
+#define __FLT_RADIX__ 2
+#define __LDBL_DECIMAL_DIG__ 21
+#define __LDBL_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951L
+#define __LDBL_DIG__ 18
+#define __LDBL_EPSILON__ 1.08420217248550443400745280086994171e-19L
+#define __LDBL_HAS_DENORM__ 1
+#define __LDBL_HAS_INFINITY__ 1
+#define __LDBL_HAS_QUIET_NAN__ 1
+#define __LDBL_IS_IEC_60559__ 1
+#define __LDBL_MANT_DIG__ 64
+#define __LDBL_MAX_10_EXP__ 4932
+#define __LDBL_MAX__ 1.18973149535723176502126385303097021e+4932L
+#define __LDBL_MAX_EXP__ 16384
+#define __LDBL_MIN_10_EXP__ (-4931)
+#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
+#define __LDBL_MIN_EXP__ (-16381)
+#define __LDBL_NORM_MAX__ 1.18973149535723176502126385303097021e+4932L
+
+#include_next "stdc-predef.h"
diff --git a/wrapperhelper/include-override/x86_64/stdc-predef.h b/wrapperhelper/include-override/x86_64/stdc-predef.h
index 5ed6af02..5c1e4fee 100644
--- a/wrapperhelper/include-override/x86_64/stdc-predef.h
+++ b/wrapperhelper/include-override/x86_64/stdc-predef.h
@@ -68,7 +68,7 @@
 #define __SSE_MATH__                  1
 #define __SSE2__                      1
 #define __SSE2_MATH__                 1
-#define __SSP_STRONG__          3
+#define __SSP_STRONG__                3
 #define __amd64__                     1
 #define __amd64                       1
 #define __code_model_small__          1
@@ -109,16 +109,16 @@
 //#define __BITINT_MAXWIDTH__ 65535
 //#define __CHAR16_TYPE__ short unsigned int
 //#define __CHAR32_TYPE__ unsigned int
-#define __INT8_C(c) c
+//#define __INT8_C(c) c
 #define __INT8_MAX__ 0x7f
 //#define __INT8_TYPE__ signed char
-#define __INT16_C(c) c
+//#define __INT16_C(c) c
 #define __INT16_MAX__ 0x7fff
 //#define __INT16_TYPE__ short int
-#define __INT32_C(c) c
+//#define __INT32_C(c) c
 #define __INT32_MAX__ 0x7fffffff
 //#define __INT32_TYPE__ int
-#define __INT64_C(c) c ## L
+//#define __INT64_C(c) c ## L
 #define __INT64_MAX__ 0x7fffffffffffffffL
 //#define __INT64_TYPE__ long int
 #define __INT_FAST8_MAX__ 0x7f
diff --git a/wrapperhelper/src/generator.c b/wrapperhelper/src/generator.c
index cf4d1851..a4762c5e 100644
--- a/wrapperhelper/src/generator.c
+++ b/wrapperhelper/src/generator.c
@@ -1,6 +1,7 @@
 #include "generator.h"
 
 #include "lang.h"
+#include "log.h"
 #include "prepare.h"
 
 static const char *rft2str[8] = {
@@ -23,13 +24,14 @@ void request_print(const request_t *req) {
 	case RQT_FUN_MY:
 	case RQT_FUN_D:
 		if (req->def.fun.typ) {
-			printf(" function%s %s%s%s",
+			printf(" function%s%s %s%s%s",
 				rft2str[req->def.rty],
+				req->def.fun.needs_S ? " (with S)" : "",
 				string_content(req->def.fun.typ),
 				req->def.fun.fun2 ? " -> " : "",
 				req->def.fun.fun2 ? string_content(req->def.fun.fun2) : "");
 		} else {
-			printf(" untyped function%s", rft2str[req->def.rty]);
+			printf(" untyped function%s%s", rft2str[req->def.rty], req->def.fun.needs_S ? " (with S)" : "");
 		}
 		break;
 	case RQT_DATA:
@@ -50,13 +52,14 @@ void request_print(const request_t *req) {
 		case RQT_FUN_MY:
 		case RQT_FUN_D:
 			if (req->val.fun.typ) {
-				printf(" function%s %s%s%s",
+				printf(" function%s%s %s%s%s",
 					rft2str[req->val.rty],
+					req->val.fun.needs_S ? " (with S)" : "",
 					string_content(req->val.fun.typ),
 					req->val.fun.fun2 ? " -> " : "",
 					req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "");
 			} else {
-				printf(" untyped function%s", rft2str[req->val.rty]);
+				printf(" untyped function%s%s", rft2str[req->val.rty], req->val.fun.needs_S ? " (with S)" : "");
 			}
 			break;
 		case RQT_DATA:
@@ -107,18 +110,20 @@ void request_print_check(const request_t *req) {
 			       && !strcmp(string_content(req->def.fun.typ) + 2, string_content(req->val.fun.typ) + 3);
 		}
 		if (!similar) {
-			printf("%s%s: function with %s%sdefault%s%s%s%s%s and dissimilar %ssolved%s%s%s%s%s\n",
+			printf("%s%s: function with %s%sdefault%s%s%s%s%s%s and dissimilar %ssolved%s%s%s%s%s%s\n",
 				string_content(req->obj_name),
 				req->weak ? " (weak)" : "",
 				req->default_comment ? "commented " : "",
 				req->def.fun.typ ? "" : "untyped ",
 				rft2str[req->def.rty],
+				req->def.fun.needs_S ? " (with S)" : "",
 				req->def.fun.typ ? " " : "",
 				string_content(req->def.fun.typ),
 				req->def.fun.fun2 ? " -> " : "",
 				req->def.fun.fun2 ? string_content(req->def.fun.fun2) : "",
 				req->val.fun.typ ? "" : "untyped ",
 				rft2str[req->val.rty],
+				req->val.fun.needs_S ? " (with S)" : "",
 				req->val.fun.typ ? " " : "",
 				string_content(req->val.fun.typ),
 				req->val.fun.fun2 ? " -> " : "",
@@ -225,11 +230,16 @@ static void request_output(FILE *f, const request_t *req) {
 	if (!req->has_val) {
 		if (IS_RQT_FUNCTION(req->def.rty)) {
 			if (!req->def.fun.typ) {
-				fprintf(f, "//GO%s%s(%s, \n", req->weak ? "W" : "", rqt_suffix[req->def.rty], string_content(req->obj_name));
+				fprintf(f, "//GO%s%s%s(%s, \n",
+					req->weak ? "W" : "",
+					req->def.fun.needs_S ? "S" : "",
+					rqt_suffix[req->def.rty],
+					string_content(req->obj_name));
 			} else {
-				fprintf(f, "%sGO%s%s(%s, %s%s%s%s%s)%s\n",
+				fprintf(f, "%sGO%s%s%s(%s, %s%s%s%s%s)%s\n",
 					req->default_comment ? "//" : "",
 					req->weak ? "W" : "",
+					req->def.fun.needs_S ? "S" : "",
 					rqt_suffix[req->def.rty],
 					string_content(req->obj_name),
 					valid_reqtype(req->def.fun.typ) ? "" : "\"",
@@ -260,9 +270,10 @@ static void request_output(FILE *f, const request_t *req) {
 			int is_comment =
 				(IS_RQT_FUNCTION(req->def.rty) && req->def.fun.typ && !req->default_comment)
 				  ? (req->val.rty != req->def.rty) : (req->val.rty != RQT_FUN);
-			fprintf(f, "%sGO%s%s(%s, %s%s%s)\n",
+			fprintf(f, "%sGO%s%s%s(%s, %s%s%s)\n",
 				is_comment ? "//" : "",
 				req->weak ? "W" : "",
+				req->val.fun.needs_S ? "S" : "",
 				rqt_suffix[req->val.rty],
 				string_content(req->obj_name),
 				string_content(req->val.fun.typ),
@@ -321,12 +332,13 @@ VECTOR_IMPL(references, reference_del)
 VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 	prepare_t *prep = prepare_new_file(f, filename);
 	if (!prep) {
-		printf("Failed to create the prepare structure for the reference file\n");
+		log_memory("failed to create the prepare structure for the reference file\n");
 		return NULL;
 	}
 	
 	VECTOR(references) *ret = vector_new(references);
 	if (!ret) {
+		log_memory("failed to create a new reference vector\n");
 		prepare_del(prep);
 		return NULL;
 	}
@@ -346,37 +358,37 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 	int if_depth = 0, entered_depth = 0;
 	string_t *line = string_new();
 	if (!line) {
-		printf("Error: failed to allocate new string for new reference line\n");
+		log_memory("failed to allocate new string for new reference line\n");
 	}
 	
 #define ADD_CHAR(c, has_destr, what) \
-		if (!string_add_char(line, c)) {               \
-			printf("Error: failed to add " what "\n"); \
-			if (has_destr) preproc_token_del(&tok);    \
-			goto failed;                               \
+		if (!string_add_char(line, c)) {            \
+			log_memory("failed to add " what "\n"); \
+			if (has_destr) preproc_token_del(&tok); \
+			goto failed;                            \
 		}
 #define ADD_CSTR(cstr, has_destr, what) \
-		if (!string_add_cstr(line, cstr)) {            \
-			printf("Error: failed to add " what "\n"); \
-			if (has_destr) preproc_token_del(&tok);    \
-			goto failed;                               \
+		if (!string_add_cstr(line, cstr)) {         \
+			log_memory("failed to add " what "\n"); \
+			if (has_destr) preproc_token_del(&tok); \
+			goto failed;                            \
 		}
 #define ADD_STR(str, has_destr, what) \
-		if (!string_add_string(line, str)) {              \
-			printf("Error: failed to add " what "\n"); \
-			if (has_destr) preproc_token_del(&tok);    \
-			goto failed;                               \
+		if (!string_add_string(line, str)) {        \
+			log_memory("failed to add " what "\n"); \
+			if (has_destr) preproc_token_del(&tok); \
+			goto failed;                            \
 		}
 #define PUSH_LINE(has_destr) \
 		string_trim(line);                                                                   \
 		if (!vector_push(references, ret, ((reference_t){.typ = REF_LINE, .line = line}))) { \
-			printf("Error: failed to memorize reference line %d\n", lineno);                 \
+			log_memory("failed to memorize reference line %d\n", lineno);                    \
 			if (has_destr) preproc_token_del(&tok);                                          \
 			goto failed;                                                                     \
 		}                                                                                    \
 		line = string_new();                                                                 \
 		if (!line) {                                                                         \
-			printf("Error: failed to allocate new string for new reference line\n");         \
+			log_memory("failed to allocate new string for new reference line\n");            \
 		}
 	
 	while (1) {
@@ -397,7 +409,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			string_clear(line);
 			tok = pre_next_token(prep, 0);
 			if (tok.tokt != PPTOK_IDENT) {
-				printf("Error: invalid reference file: invalid preprocessor line\n");
+				log_error(&tok.loginfo, "invalid reference file: invalid preprocessor line\n");
 				preproc_token_del(&tok);
 				goto failed;
 			}
@@ -405,13 +417,13 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				string_del(tok.tokv.str);
 				tok = pre_next_token(prep, 0);
 				if (tok.tokt != PPTOK_IDENT) {
-					printf("Error: invalid reference file: invalid '#ifdef' line\n");
+					log_error(&tok.loginfo, "invalid reference file: invalid '#ifdef' line\n");
 					preproc_token_del(&tok);
 					goto failed;
 				}
 				++if_depth;
 				if (!vector_push(references, ret, ((reference_t){.typ = REF_IFDEF, .line = tok.tokv.str}))) {
-					printf("Error: failed to memorize reference line %d\n", lineno);
+					log_error(&tok.loginfo, "failed to memorize reference line %d\n", lineno);
 					string_del(tok.tokv.str);
 					goto failed;
 				}
@@ -420,14 +432,14 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				string_del(tok.tokv.str);
 				tok = pre_next_token(prep, 0);
 				if (tok.tokt != PPTOK_IDENT) {
-					printf("Error: invalid reference file: invalid '#ifndef' line\n");
+					log_error(&tok.loginfo, "invalid reference file: invalid '#ifndef' line\n");
 					preproc_token_del(&tok);
 					goto failed;
 				}
 				if (if_depth == entered_depth) ++entered_depth;
 				++if_depth;
 				if (!vector_push(references, ret, ((reference_t){.typ = REF_IFNDEF, .line = tok.tokv.str}))) {
-					printf("Error: failed to memorize reference line %d\n", lineno);
+					log_error(&tok.loginfo, "failed to memorize reference line %d\n", lineno);
 					string_del(tok.tokv.str);
 					goto failed;
 				}
@@ -438,7 +450,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				if (if_depth == entered_depth + 1) ++entered_depth;
 				else if (if_depth == entered_depth) --entered_depth;
 				if (!vector_push(references, ret, ((reference_t){.typ = REF_ELSE}))) {
-					printf("Error: failed to memorize reference line %d\n", lineno);
+					log_error(&tok.loginfo, "failed to memorize reference line %d\n", lineno);
 					goto failed;
 				}
 			} else if (!strcmp(string_content(tok.tokv.str), "endif")) {
@@ -447,11 +459,11 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				if (if_depth == entered_depth) --entered_depth;
 				--if_depth;
 				if (!vector_push(references, ret, ((reference_t){.typ = REF_ENDIF}))) {
-					printf("Error: failed to memorize reference line %d\n", lineno);
+					log_error(&tok.loginfo, "failed to memorize reference line %d\n", lineno);
 					goto failed;
 				}
 			} else {
-				printf("Error: invalid reference file: invalid preprocessor command '%s'\n", string_content(tok.tokv.str));
+				log_error(&tok.loginfo, "invalid reference file: invalid preprocessor command '%s'\n", string_content(tok.tokv.str));
 				string_del(tok.tokv.str);
 				goto failed;
 			}
@@ -477,10 +489,12 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 		         || !strcmp(string_content(tok.tokv.str), "GO2")
 		         || !strcmp(string_content(tok.tokv.str), "GOD")
 		         || !strcmp(string_content(tok.tokv.str), "GOM")
+		         || !strcmp(string_content(tok.tokv.str), "GOS")
 		         || !strcmp(string_content(tok.tokv.str), "GOW")
 		         || !strcmp(string_content(tok.tokv.str), "GOW2")
 		         || !strcmp(string_content(tok.tokv.str), "GOWD")
-		         || !strcmp(string_content(tok.tokv.str), "GOWM"))) {
+		         || !strcmp(string_content(tok.tokv.str), "GOWM")
+		         || !strcmp(string_content(tok.tokv.str), "GOWS"))) {
 			string_clear(line);
 			if (is_comment) prepare_mark_nocomment(prep);
 			int isweak = (string_content(tok.tokv.str)[2] == 'W');
@@ -495,21 +509,23 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 						(string_content(tok.tokv.str)[isweak ? 3 : 2] == '2') ? RQT_FUN_2 :
 						(string_content(tok.tokv.str)[isweak ? 3 : 2] == 'D') ? RQT_FUN_D :
 						(string_content(tok.tokv.str)[isweak ? 3 : 2] == 'M') ? RQT_FUN_MY : RQT_FUN,
+					.fun.needs_S = (string_content(tok.tokv.str)[isweak ? 3 : 2] == 'S'),
 					.fun.typ = NULL,
 					.fun.fun2 = NULL,
 				},
+				.val = {0},
 			};
 			string_del(tok.tokv.str);
 			tok = pre_next_token(prep, 0);
 			if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) {
-				printf("Error: invalid reference file: invalid GO line %d (lparen)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (lparen)\n", lineno);
 				preproc_token_del(&tok);
 				goto failed;
 			}
 			// Empty destructor
 			tok = pre_next_token(prep, 0);
 			if (tok.tokt != PPTOK_IDENT) {
-				printf("Error: invalid reference file: invalid GO line %d (obj_name)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (obj_name)\n", lineno);
 				preproc_token_del(&tok);
 				goto failed;
 			}
@@ -517,7 +533,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			// Token moved
 			tok = pre_next_token(prep, 0);
 			if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
-				printf("Error: invalid reference file: invalid GO line %d (comma)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (comma)\n", lineno);
 				string_del(req.obj_name);
 				preproc_token_del(&tok);
 				goto failed;
@@ -530,7 +546,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				tok = pre_next_token(prep, 0);
 				if ((req.def.rty == RQT_FUN_2) || (req.def.rty == RQT_FUN_D)) {
 					if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
-						printf("Error: invalid reference file: invalid GO line %d (comma 2)\n", lineno);
+						log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (comma 2)\n", lineno);
 						string_del(req.obj_name);
 						string_del(req.def.fun.typ);
 						preproc_token_del(&tok);
@@ -539,7 +555,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 					// Empty destructor
 					tok = pre_next_token(prep, 0);
 					if (tok.tokt != PPTOK_IDENT) {
-						printf("Error: invalid reference file: invalid GO line %d (redirect)\n", lineno);
+						log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (redirect)\n", lineno);
 						string_del(req.obj_name);
 						string_del(req.def.fun.typ);
 						preproc_token_del(&tok);
@@ -550,7 +566,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 					tok = pre_next_token(prep, 0);
 				}
 				if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
-					printf("Error: invalid reference file: invalid GO line %d (rparen)\n", lineno);
+					log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (rparen)\n", lineno);
 					string_del(req.obj_name);
 					string_del(req.def.fun.typ);
 					if (req.def.fun.fun2) string_del(req.def.fun.fun2);
@@ -561,7 +577,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				tok = pre_next_token(prep, 0);
 			}
 			if (tok.tokt != PPTOK_NEWLINE) {
-				printf("Error: invalid reference file: invalid GO line %d (newline)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid GO line %d (newline)\n", lineno);
 				string_del(req.obj_name);
 				if (req.def.fun.typ) string_del(req.def.fun.typ);
 				if (req.def.fun.fun2) string_del(req.def.fun.fun2);
@@ -569,7 +585,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				goto failed;
 			}
 			if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
-				printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
+				log_memory("failed to add reference for %s\n", string_content(req.obj_name));
 				string_del(req.obj_name);
 				if (req.def.fun.typ) string_del(req.def.fun.typ);
 				if (req.def.fun.fun2) string_del(req.def.fun.fun2);
@@ -598,18 +614,19 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 					.dat.has_size = 0,
 					.dat.sz = 0,
 				},
+				.val = {0},
 			};
 			string_del(tok.tokv.str);
 			tok = pre_next_token(prep, 0);
 			if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) {
-				printf("Error: invalid reference file: invalid DATA line %d (lparen)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (lparen)\n", lineno);
 				preproc_token_del(&tok);
 				goto failed;
 			}
 			// Empty destructor
 			tok = pre_next_token(prep, 0);
 			if (tok.tokt != PPTOK_IDENT) {
-				printf("Error: invalid reference file: invalid DATA line %d (obj_name)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (obj_name)\n", lineno);
 				preproc_token_del(&tok);
 				goto failed;
 			}
@@ -617,7 +634,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			// Token moved
 			tok = pre_next_token(prep, 0);
 			if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
-				printf("Error: invalid reference file: invalid DATA line %d (comma)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (comma)\n", lineno);
 				string_del(req.obj_name);
 				preproc_token_del(&tok);
 				goto failed;
@@ -627,8 +644,8 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			if (tok.tokt == PPTOK_NUM) {
 				num_constant_t cst;
 				// Assume target is 64 bits (box64)
-				if (!num_constant_convert(tok.tokv.str, &cst, 0)) {
-					printf("Error: invalid reference file: invalid DATA line %d (num conversion)\n", lineno);
+				if (!num_constant_convert(&tok.loginfo, tok.tokv.str, &cst, 0)) {
+					log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (num conversion)\n", lineno);
 					string_del(req.obj_name);
 					preproc_token_del(&tok);
 					goto failed;
@@ -637,7 +654,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				case NCT_FLOAT:
 				case NCT_DOUBLE:
 				case NCT_LDOUBLE:
-					printf("Error: invalid reference file: invalid DATA line %d (num conversion)\n", lineno);
+					log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (num conversion)\n", lineno);
 					string_del(req.obj_name);
 					string_del(tok.tokv.str);
 					goto failed;
@@ -650,7 +667,7 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				string_del(tok.tokv.str); // Delete token
 				tok = pre_next_token(prep, 0);
 				if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
-					printf("Error: invalid reference file: invalid DATA line %d (rparen)\n", lineno);
+					log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (rparen)\n", lineno);
 					string_del(req.obj_name);
 					preproc_token_del(&tok);
 					goto failed;
@@ -659,13 +676,13 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				tok = pre_next_token(prep, 0);
 			}
 			if (tok.tokt != PPTOK_NEWLINE) {
-				printf("Error: invalid reference file: invalid DATA line %d (newline)\n", lineno);
+				log_error(&tok.loginfo, "invalid reference file: invalid DATA line %d (newline)\n", lineno);
 				string_del(req.obj_name);
 				preproc_token_del(&tok);
 				goto failed;
 			}
 			if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
-				printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
+				log_memory("failed to add reference for %s\n", string_content(req.obj_name));
 				request_del(&req);
 				// Empty destructor
 				goto failed;
@@ -678,18 +695,18 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			} else if (tok.tokt == PPTOK_BLANK) {
 				ADD_CHAR(tok.tokv.c, 0, "comment content")
 			} else {
-				printf("Error: unknown token type in comment %u\n", tok.tokt);
+				log_error(&tok.loginfo, "unknown token type in comment %u\n", tok.tokt);
 				preproc_token_del(&tok);
 				goto failed;
 			}
 			if (!pre_next_newline_token(prep, line)) {
-				printf("Error: failed to add comment content\n");
+				log_memory("failed to add comment content\n");
 				goto failed;
 			}
 			PUSH_LINE(0)
 			++lineno;
 		} else {
-			printf("Error: invalid reference file: invalid token:\n");
+			log_error(&tok.loginfo, "invalid reference file: invalid token:\n");
 			preproc_token_print(&tok);
 			preproc_token_del(&tok);
 			goto failed;
@@ -708,7 +725,8 @@ success:
 	return ret;
 }
 
-static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map) {
+// Simple versions (in practice, only use x86_64 and aarch64 as emu/target pair)
+static int is_simple_type_ptr_to_simple(type_t *typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map) {
 	if (typ->converted) {
 		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
 		*needs_my = 1;
@@ -721,7 +739,7 @@ static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my, khash
 		return 1; // Assume pointers to builtin are simple
 	case TYPE_ARRAY:
 		if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
-		return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_ptr_to_simple(typ->val.array.typ, needs_D, needs_my, conv_map);
 	case TYPE_STRUCT_UNION:
 		if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
 		if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
@@ -729,7 +747,7 @@ static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my, khash
 		typ->_internal_use = 1;
 		for (size_t i = 0; i < typ->val.st->nmembers; ++i) {
 			st_member_t *mem = &typ->val.st->members[i];
-			if (!is_simple_type_ptr_to(mem->typ, needs_D, needs_my, conv_map)) {
+			if (!is_simple_type_ptr_to_simple(mem->typ, needs_D, needs_my, conv_map)) {
 				typ->_internal_use = 0;
 				return 0;
 			}
@@ -737,18 +755,18 @@ static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my, khash
 		typ->_internal_use = 0;
 		return 1;
 	case TYPE_ENUM:
-		return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_ptr_to_simple(typ->val.typ, needs_D, needs_my, conv_map);
 	case TYPE_PTR:
-		return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_ptr_to_simple(typ->val.typ, needs_D, needs_my, conv_map);
 	case TYPE_FUNCTION:
 		*needs_my = 1;
 		return 1;
 	default:
-		printf("Error: is_simple_type_ptr_to on unknown type %u\n", typ->typ);
+		printf("Error: is_simple_type_ptr_to_simple on unknown type %u\n", typ->typ);
 		return 0;
 	}
 }
-static int is_simple_type(type_t *typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map) {
+static int is_simple_type_simple(type_t *typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map) {
 	if (typ->converted) {
 		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
 		*needs_my = 1;
@@ -763,7 +781,7 @@ static int is_simple_type(type_t *typ, int *needs_D, int *needs_my, khash_t(conv
 		    && (typ->val.builtin != BTT_IFLOAT128); // Assume builtin are simple except for __float128
 	case TYPE_ARRAY:
 		if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
-		return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_ptr_to_simple(typ->val.array.typ, needs_D, needs_my, conv_map);
 	case TYPE_STRUCT_UNION:
 		if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
 		// if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
@@ -772,7 +790,7 @@ static int is_simple_type(type_t *typ, int *needs_D, int *needs_my, khash_t(conv
 		typ->_internal_use = 1;
 		for (size_t i = 0; i < typ->val.st->nmembers; ++i) {
 			st_member_t *mem = &typ->val.st->members[i];
-			if (!is_simple_type(mem->typ, needs_D, needs_my, conv_map)) {
+			if (!is_simple_type_simple(mem->typ, needs_D, needs_my, conv_map)) {
 				typ->_internal_use = 0;
 				return 0;
 			}
@@ -780,27 +798,28 @@ static int is_simple_type(type_t *typ, int *needs_D, int *needs_my, khash_t(conv
 		typ->_internal_use = 0;
 		return 1;
 	case TYPE_ENUM:
-		return is_simple_type(typ->val.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_simple(typ->val.typ, needs_D, needs_my, conv_map);
 	case TYPE_PTR:
-		return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my, conv_map);
+		return is_simple_type_ptr_to_simple(typ->val.typ, needs_D, needs_my, conv_map);
 	case TYPE_FUNCTION:
 		// Functions should be handled differently (GO instead of DATA)
 		return 0;
 	default:
-		printf("Error: is_simple_type on unknown type %u\n", typ->typ);
+		printf("Error: is_simple_type_simple on unknown type %u\n", typ->typ);
 		return 0;
 	}
 }
 
-static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map, string_t *obj_name) {
-	if (typ->converted) {
-		if (!string_add_string(dest, typ->converted)) {
+static int convert_type_simple(string_t *dest, type_t *emu_typ, type_t *target_typ,
+                               int is_ret, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map, string_t *obj_name) {
+	if (emu_typ->converted) {
+		if (!string_add_string(dest, emu_typ->converted)) {
 			printf("Error: failed to add explicit type conversion\n");
 			return 0;
 		}
 		return 1;
 	}
-	khiter_t it = kh_get(conv_map, conv_map, typ);
+	khiter_t it = kh_get(conv_map, conv_map, emu_typ);
 	if (it != kh_end(conv_map)) {
 		if (!string_add_string(dest, kh_val(conv_map, it))) {
 			printf("Error: failed to add explicit type conversion\n");
@@ -808,15 +827,19 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 		}
 		return 1;
 	}
-	if (typ->is_atomic) {
-		printf("TODO: convert_type for atomic types\n");
+	if ((emu_typ->is_atomic) || (target_typ->is_atomic)) {
+		printf("Error: TODO: convert_type_simple for atomic types\n");
 		return 0;
 	}
-	switch (typ->typ) {
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: %s type is different between emulated and target\n", string_content(obj_name), is_ret ? "return" : "argument");
+		*needs_my = 1;
+	}
+	switch (emu_typ->typ) {
 	case TYPE_BUILTIN: {
 		int has_char = 0;
 		char c;
-		switch (typ->val.builtin) {
+		switch (emu_typ->val.builtin) {
 		case BTT_VOID: has_char = 1; c = 'v'; break;
 		case BTT_BOOL: has_char = 1; c = 'i'; break;
 		case BTT_CHAR: has_char = 1; c = 'c'; break;
@@ -854,40 +877,40 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 		case BTT_LONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
 		case BTT_CLONGDOUBLE: *needs_D = 1; has_char = 1; c = 'Y'; break;
 		case BTT_ILONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
-		case BTT_FLOAT128: printf("Error: TODO: %s\n", builtin2str[typ->val.builtin]); has_char = 0; break;
-		case BTT_CFLOAT128: printf("Error: TODO: %s\n", builtin2str[typ->val.builtin]); has_char = 0; break;
-		case BTT_IFLOAT128: printf("Error: TODO: %s\n", builtin2str[typ->val.builtin]); has_char = 0; break;
+		case BTT_FLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
+		case BTT_CFLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
+		case BTT_IFLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
 		case BTT_VA_LIST: *needs_my = 1; has_char = 1; c = 'A'; break;
 		default:
-			printf("Error: convert_type on unknown builtin %u\n", typ->val.builtin);
+			printf("Error: convert_type_simple on unknown builtin %u\n", emu_typ->val.builtin);
 			return 0;
 		}
 		if (has_char) {
 			if (!string_add_char(dest, c)) {
-				printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
+				printf("Error: failed to add type char for %s\n", builtin2str[emu_typ->val.builtin]);
 				return 0;
 			}
 			return 1;
 		} else {
-			printf("Internal error: unknown state builtin=%u\n", typ->val.builtin);
+			printf("Internal error: unknown state builtin=%u\n", emu_typ->val.builtin);
 			return 0;
 		} }
 	case TYPE_ARRAY:
-		printf("Error: convert_type on raw array\n");
+		printf("Error: convert_type_simple on raw array\n");
 		return 0;
 	case TYPE_STRUCT_UNION:
-		if (!typ->is_validated || typ->is_incomplete) {
-			printf("Error: incomplete return type for %s\n", string_content(obj_name));
+		if (!emu_typ->is_validated || emu_typ->is_incomplete) {
+			printf("Error: incomplete structure for %s\n", string_content(obj_name));
 			return 0;
 		}
 		if (is_ret) {
-			if (typ->szinfo.size <= 8) {
+			if (emu_typ->szinfo.size <= 8) {
 				if (!string_add_char(dest, 'U')) {
 					printf("Error: failed to add type char for structure return\n");
 					return 0;
 				}
 				return 1;
-			} else if (typ->szinfo.size <= 16) {
+			} else if (emu_typ->szinfo.size <= 16) {
 				if (!string_add_char(dest, 'H')) {
 					printf("Error: failed to add type char for large structure return\n");
 					return 0;
@@ -901,16 +924,21 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 				return 1;
 			}
 		} else {
-			if (typ->val.st->nmembers == 1) {
-				return convert_type(dest, typ->val.st->members[0].typ, is_ret, needs_D, needs_my, conv_map, obj_name);
+			if ((emu_typ->val.st->nmembers == 1) && (target_typ->typ == TYPE_STRUCT_UNION) && (target_typ->val.st->nmembers == 1)) {
+				return convert_type_simple(dest, emu_typ->val.st->members[0].typ, target_typ->val.st->members[0].typ, is_ret, needs_D, needs_my, conv_map, obj_name);
 			}
-			printf("TODO: convert_type on structure as argument (%s)\n", string_content(obj_name));
+			printf("Error: TODO: convert_type_simple on structure as argument (%s)\n", string_content(obj_name));
 			return 0;
 		}
 	case TYPE_ENUM:
-		return convert_type(dest, typ->val.typ, is_ret, needs_D, needs_my, conv_map, obj_name);
+		if (target_typ->typ == TYPE_ENUM) {
+			return convert_type_simple(dest, emu_typ->val.typ, target_typ->val.typ, is_ret, needs_D, needs_my, conv_map, obj_name);
+		} else {
+			printf("Fatal: convert_type_simple(enum, non-enum)\n");
+			return 0;
+		}
 	case TYPE_PTR:
-		if (is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my, conv_map)) {
+		if (is_simple_type_ptr_to_simple(emu_typ->val.typ, needs_D, needs_my, conv_map)) {
 			if (!string_add_char(dest, 'p')) {
 				printf("Error: failed to add type char for simple pointer\n");
 				return 0;
@@ -919,34 +947,35 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 		} else {
 			*needs_my = 1;
 			if (!string_add_char(dest, 'p')) {
-				printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
+				printf("Error: failed to add type char for complex pointer\n");
 				return 0;
 			}
 			return 1;
 		}
 	case TYPE_FUNCTION:
-		printf("Error: convert_type on raw function\n");
+		printf("Error: convert_type_simple on raw function\n");
 		return 0;
 	default:
-		printf("Error: convert_type on unknown type %u\n", typ->typ);
+		printf("Error: convert_type_simple on unknown type %u\n", emu_typ->typ);
 		return 0;
 	}
 }
-static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) {
-	if (typ->converted) return 1;
-	if (typ->is_atomic) {
-		printf("TODO: convert_type_post for atomic types\n");
+static int convert_type_post_simple(string_t *dest, type_t *emu_typ, type_t *target_typ, string_t *obj_name) {
+	if (emu_typ->converted) return 1;
+	if (emu_typ->is_atomic) {
+		printf("Error: TODO: convert_type_post_simple for atomic types\n");
 		return 0;
 	}
-	switch (typ->typ) {
+	(void)target_typ;
+	switch (emu_typ->typ) {
 	case TYPE_BUILTIN: return 1;
 	case TYPE_ARRAY: return 1;
 	case TYPE_STRUCT_UNION:
-		if (!typ->is_validated || typ->is_incomplete) {
-			printf("Error: incomplete return type for %s\n", string_content(obj_name));
+		if (!emu_typ->is_validated || emu_typ->is_incomplete) {
+			printf("Error: incomplete structure for %s\n", string_content(obj_name));
 			return 0;
 		}
-		if (typ->szinfo.size <= 16) {
+		if (emu_typ->szinfo.size <= 16) {
 			return 1;
 		} else {
 			if (!string_add_char(dest, 'p')) {
@@ -958,15 +987,674 @@ static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) {
 	case TYPE_ENUM: return 1;
 	case TYPE_PTR: return 1;
 	case TYPE_FUNCTION: return 1;
+	}
+	printf("Error: convert_type_post_simple on unknown type %u\n", emu_typ->typ);
+	return 0;
+}
+
+int solve_request_simple(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(conv_map) *conv_map) {
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: emulated and target types are different (emulated is %u, target is %u)\n",
+			string_content(req->obj_name), emu_typ->typ, target_typ->typ);
+		return 0;
+	}
+	if (emu_typ->typ == TYPE_FUNCTION) {
+		int needs_D = 0, needs_my = req->def.fun.typ && (req->def.rty == RQT_FUN_MY), needs_2 = 0;
+		int convert_post;
+		size_t idx_conv;
+		req->val.fun.typ = string_new();
+		if (!req->val.fun.typ) {
+			printf("Error: failed to create function type string\n");
+			return 0;
+		}
+		if (!convert_type_simple(req->val.fun.typ, emu_typ->val.fun.ret, target_typ->val.fun.ret, 1, &needs_D, &needs_my, conv_map, req->obj_name))
+			goto fun_fail;
+		idx_conv = string_len(req->val.fun.typ);
+		if (!string_add_char(req->val.fun.typ, 'F')) {
+			printf("Error: failed to add convention char\n");
+			goto fun_fail;
+		}
+		convert_post = convert_type_post_simple(req->val.fun.typ, emu_typ->val.fun.ret, target_typ->val.fun.ret, req->obj_name);
+		if (!convert_post) goto fun_fail;
+		if (emu_typ->val.fun.nargs == (size_t)-1) {
+			printf("Warning: %s: assuming empty specification is void specification\n", string_content(req->obj_name));
+			if (convert_post == 1) {
+				if (!string_add_char(req->val.fun.typ, 'v')) {
+					printf("Error: failed to add void specification char\n");
+					goto fun_fail;
+				}
+			}
+		} else if (!emu_typ->val.fun.nargs && !emu_typ->val.fun.has_varargs) {
+			if (convert_post == 1) {
+				if (!string_add_char(req->val.fun.typ, 'v')) {
+					printf("Error: failed to add void specification char\n");
+					goto fun_fail;
+				}
+			}
+		} else {
+			for (size_t i = 0; i < emu_typ->val.fun.nargs; ++i) {
+				if (!convert_type_simple(req->val.fun.typ, emu_typ->val.fun.args[i], target_typ->val.fun.args[i], 0, &needs_D, &needs_my, conv_map, req->obj_name))
+					goto fun_fail;
+			}
+			if (emu_typ->val.fun.has_varargs) {
+				if (req->def.fun.typ
+				      && (string_len(req->def.fun.typ) == string_len(req->val.fun.typ) + 1)
+				      && !strncmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ), string_len(req->val.fun.typ))
+				      && ((string_content(req->def.fun.typ)[string_len(req->val.fun.typ)] == 'M')
+				       || (string_content(req->def.fun.typ)[string_len(req->val.fun.typ)] == 'N'))) {
+					if (!string_add_char(req->val.fun.typ, string_content(req->def.fun.typ)[string_len(req->val.fun.typ)])) {
+						printf("Error: failed to add type char '%c' for %s\n",
+							string_content(req->def.fun.typ)[string_len(req->val.fun.typ)],
+							builtin2str[emu_typ->val.builtin]);
+						goto fun_fail;
+					}
+				} else {
+					needs_my = 1;
+					if (!string_add_char(req->val.fun.typ, 'V')) {
+						printf("Error: failed to add type char 'V' for %s\n", builtin2str[emu_typ->val.builtin]);
+						goto fun_fail;
+					}
+				}
+			}
+		}
+		
+	// fun_succ:
+		// Add 'E' by default, unless we have the same function as before
+		if (needs_my && (req->default_comment
+		                  || (req->def.rty != RQT_FUN_MY)
+		                  || strcmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ)))) {
+			if (!string_add_char_at(req->val.fun.typ, 'E', idx_conv + 1)) {
+				printf("Error: failed to add emu char\n");
+				goto fun_fail;
+			}
+		}
+		if (req->def.fun.typ && (req->def.rty == RQT_FUN_2) && !needs_my) {
+			needs_2 = 1;
+			req->val.fun.fun2 = string_dup(req->def.fun.fun2);
+			if (!req->val.fun.fun2) {
+				printf("Error: failed to duplicate string (request for function %s with default redirection)\n", string_content(req->obj_name));
+				return 0;
+			}
+		} else if (req->def.fun.typ && (req->def.rty == RQT_FUN_D) && !needs_my) {
+			needs_2 = 0;
+			req->val.fun.fun2 = string_dup(req->def.fun.fun2);
+			if (!req->val.fun.fun2) {
+				printf("Error: failed to duplicate string (request for function %s with long double types)\n", string_content(req->obj_name));
+				return 0;
+			}
+		} else if (!needs_my && needs_D) {
+			req->val.fun.fun2 = string_new();
+			if (!req->val.fun.fun2) {
+				printf("Error: failed to create empty string (request for function %s with long double types)\n", string_content(req->obj_name));
+				return 0;
+			}
+		}
+		req->val.rty =
+			needs_my ? RQT_FUN_MY :
+			needs_2 ? RQT_FUN_2 :
+			needs_D ? RQT_FUN_D : RQT_FUN;
+		req->has_val = 1;
+		return 1;
+		
+	fun_fail:
+		string_del(req->val.fun.typ);
+		return 0;
+	} else {
+		int needs_D = 0, needs_my = req->def.dat.has_size && (req->def.rty == RQT_DATAM);
+		if (is_simple_type_simple(emu_typ, &needs_D, &needs_my, conv_map)) {
+			// TODO: Hmm...
+			req->val.rty = needs_my ? RQT_DATAM : req->def.rty;
+			req->val.dat.has_size = 1;
+			req->val.dat.sz = emu_typ->szinfo.size;
+			req->has_val = 1;
+			return 1;
+		} else {
+			log_TODO_nopos("solve_request_simple for data %s with non-simple type ", string_content(req->obj_name));
+			type_print(emu_typ);
+			printf("\n");
+			return 0;
+		}
+	}
+}
+int solve_request_map_simple(request_t *req, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map) {
+	int hasemu = 0, hastarget = 0;
+	khiter_t emuit, targetit;
+	emuit = kh_get(decl_map, emu_decl_map, string_content(req->obj_name));
+	if (emuit == kh_end(emu_decl_map)) {
+		goto failed;
+	}
+	if ((kh_val(emu_decl_map, emuit)->storage == STORAGE_STATIC) || (kh_val(emu_decl_map, emuit)->storage == STORAGE_TLS_STATIC)) {
+		goto failed;
+	}
+	targetit = kh_get(decl_map, target_decl_map, string_content(req->obj_name));
+	if (targetit == kh_end(target_decl_map)) {
+		goto failed;
+	}
+	if ((kh_val(target_decl_map, targetit)->storage == STORAGE_STATIC) || (kh_val(target_decl_map, targetit)->storage == STORAGE_TLS_STATIC)) {
+		goto failed;
+	}
+	return solve_request_simple(req, kh_val(emu_decl_map, emuit)->typ, kh_val(target_decl_map, targetit)->typ, conv_map);
+	
+failed:
+	if (string_content(req->obj_name)[0] != '_') {
+		if (!hasemu && !hastarget) {
+			printf("Error: %s was not declared in the emulated and target architectures\n", string_content(req->obj_name));
+		} else if (!hasemu) {
+			printf("Error: %s was not declared only in the emulated architecture\n", string_content(req->obj_name));
+		} else if (!hastarget) {
+			printf("Error: %s was not declared only in the target architecture\n", string_content(req->obj_name));
+		} else {
+			printf("Error: internal error: failed but found for %s\n", string_content(req->obj_name));
+		}
+	}
+	return 0;
+}
+int solve_references_simple(VECTOR(references) *refs, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map) {
+	int ret = 1;
+	int cond_depth = 0, ok_depth = 0;
+	vector_for(references, ref, refs) {
+		switch (ref->typ) {
+		case REF_REQ:
+			if (ok_depth == cond_depth) {
+				if (!solve_request_map_simple(&ref->req, emu_decl_map, target_decl_map, conv_map)) ret = 0;
+			} else {
+				ref->req.ignored = 1;
+			}
+			break;
+		case REF_LINE:
+			break;
+		case REF_IFDEF:
+			++cond_depth;
+			break;
+		case REF_IFNDEF:
+			if (cond_depth == ok_depth) ++ok_depth;
+			++cond_depth;
+			break;
+		case REF_ELSE:
+			if (cond_depth == ok_depth) --ok_depth;
+			else if (cond_depth == ok_depth + 1) ++ok_depth;
+			break;
+		case REF_ENDIF:
+			if (cond_depth == ok_depth) --ok_depth;
+			--cond_depth;
+			break;
+		}
+	}
+	return ret;
+}
+
+// Complex versions
+enum safeness_e {
+	SAFE_ABORT,  // Failure
+	SAFE_OK,     // Simple, can output 'p'
+	SAFE_EXPAND, // Complex but automatable, needs to output 'b..._'
+};
+static enum safeness_e get_safeness_ptr(type_t *emu_typ, type_t *target_typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map, string_t *obj_name) {
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: pointer with different types between emu and target\n", string_content(obj_name));
+		return SAFE_ABORT;
+	}
+	if (emu_typ->converted) {
+		printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+		return SAFE_OK;
+	} else if (kh_get(conv_map, conv_map, emu_typ) != kh_end(conv_map)) {
+		printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+		return SAFE_OK;
+	}
+	switch (emu_typ->typ) {
+	case TYPE_BUILTIN:
+		if (emu_typ->val.builtin != target_typ->val.builtin) {
+			// printf("Warning: %s: emu and target have pointers to different size type\n", string_content(obj_name));
+			return SAFE_ABORT;
+		}
+		if (emu_typ->szinfo.size != target_typ->szinfo.size) {
+			// printf("Warning: %s: emu and target have pointers to different size type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		}
+		/* if (emu_typ->szinfo.align != target_typ->szinfo.align) {
+			// printf("Warning: %s: emu and target have pointers to different alignment type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		} */
+		// Assume pointers to builtins of the same size and alignment are simple
+		return SAFE_OK;
+	case TYPE_ARRAY:
+		if (emu_typ->szinfo.size != target_typ->szinfo.size) {
+			// printf("Warning: %s: emu and target have pointers to arrays with different size type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		}
+		if (emu_typ->szinfo.align != target_typ->szinfo.align) {
+			// printf("Warning: %s: emu and target have pointers to arrays with different alignment type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		}
+		if (emu_typ->val.array.array_sz != target_typ->val.array.array_sz) {
+			printf("Error: %s: emu and target have arrays of different size\n", string_content(obj_name));
+			return SAFE_ABORT; // Shouldn't happen
+		}
+		// Elements also have the same size
+		if ((emu_typ->val.array.array_sz == (size_t)-1) || (target_typ->val.array.array_sz == (size_t)-1)) {
+			printf("Error: %s: has variable length arrays\n", string_content(obj_name));
+			return SAFE_ABORT; // VLA require manual intervention
+		}
+		return get_safeness_ptr(emu_typ->val.array.typ, target_typ->val.array.typ, needs_D, needs_my, conv_map, obj_name);
+	case TYPE_STRUCT_UNION:
+		if (emu_typ->val.st->is_struct != target_typ->val.st->is_struct) {
+			printf("Error: %s: incoherent struct/union type between emulated and target architectures for %s\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->is_incomplete != target_typ->is_incomplete) {
+			printf("Error: %s: incoherent struct/union completion type between emulated and target architectures for %s\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->is_incomplete) {
+			return SAFE_OK; // Undefined structures are OK since they are opaque
+		}
+		if (emu_typ->val.st->nmembers != target_typ->val.st->nmembers) {
+			printf("Error: %s: struct/union %s has different number of members between emulated and target architectures\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->val.st->has_self_recursion) {
+			// Self-reference implies manual intervention
+			*needs_my = 1;
+			return SAFE_OK;
+		}
+		if (emu_typ->val.st->nmembers == 1) {
+			// Assume structs and unions of one element has the same ABI as that element directly
+			return get_safeness_ptr(emu_typ->val.st->members[0].typ, target_typ->val.st->members[0].typ, needs_D, needs_my, conv_map, obj_name);
+		}
+		if (emu_typ->val.st->is_struct) {
+			// Structures are OK if all named members are OK and at the same memory offset
+			for (size_t i = 0; i < emu_typ->val.st->nmembers; ++i) {
+				st_member_t *emu_mem = &emu_typ->val.st->members[i];
+				st_member_t *target_mem = &target_typ->val.st->members[i];
+				if (emu_mem->name) {
+					if ((emu_mem->byte_offset != target_mem->byte_offset) || (emu_mem->bit_offset != target_mem->bit_offset)) {
+						return SAFE_EXPAND;
+					}
+					enum safeness_e saf = get_safeness_ptr(emu_mem->typ, target_mem->typ, needs_D, needs_my, conv_map, obj_name);
+					if (saf != SAFE_OK) return saf;
+				}
+			}
+			return SAFE_OK;
+		} else {
+			// Unions are OK if all named members are OK (memory offset is always 0)
+			for (size_t i = 0; i < emu_typ->val.st->nmembers; ++i) {
+				st_member_t *emu_mem = &emu_typ->val.st->members[i];
+				st_member_t *target_mem = &target_typ->val.st->members[i];
+				if (emu_mem->name) {
+					enum safeness_e saf = get_safeness_ptr(emu_mem->typ, target_mem->typ, needs_D, needs_my, conv_map, obj_name);
+					if (saf != SAFE_OK) return saf;
+				}
+			}
+			return SAFE_OK;
+		}
+	case TYPE_ENUM:
+		return get_safeness_ptr(emu_typ->val.typ, target_typ->val.typ, needs_D, needs_my, conv_map, obj_name);
+	case TYPE_PTR:
+		return SAFE_EXPAND;
+	case TYPE_FUNCTION:
+		*needs_my = 1;
+		return SAFE_OK;
 	default:
-		printf("Error: convert_type_post on unknown type %u\n", typ->typ);
+		printf("Error: get_safeness_ptr on unknown type %u\n", emu_typ->typ);
+		return SAFE_ABORT;
+	}
+}
+static enum safeness_e get_safeness(type_t *emu_typ, type_t *target_typ, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map, string_t *obj_name) {
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: data with different types between emu and target\n", string_content(obj_name));
+		return SAFE_ABORT; // Invalid type
+	}
+	if (emu_typ->converted) {
+		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+		return SAFE_OK;
+	} else if (kh_get(conv_map, conv_map, emu_typ) != kh_end(conv_map)) {
+		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+		return SAFE_OK;
+	}
+	switch (emu_typ->typ) {
+	case TYPE_BUILTIN:
+		if ((emu_typ->val.builtin != target_typ->val.builtin)
+		    || (emu_typ->szinfo.size != target_typ->szinfo.size)
+		    || (emu_typ->szinfo.align != target_typ->szinfo.align)
+		    || (emu_typ->val.builtin == BTT_FLOAT128)
+		    || (emu_typ->val.builtin == BTT_CFLOAT128)
+		    || (emu_typ->val.builtin == BTT_IFLOAT128)) {
+			// Assume all builtins are simple except for __float128 and those with different size or alignment
+			*needs_my = 1;
+		}
+		return SAFE_OK;
+	case TYPE_ARRAY:
+		if (emu_typ->szinfo.size != target_typ->szinfo.size) {
+			// printf("Warning: %s: emu and target have pointers to different size type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		}
+		if (emu_typ->szinfo.align != target_typ->szinfo.align) {
+			// printf("Warning: %s: emu and target have pointers to different alignment type\n", string_content(obj_name));
+			return SAFE_EXPAND;
+		}
+		if (emu_typ->val.array.array_sz != target_typ->val.array.array_sz) {
+			printf("Error: %s: emu and target have arrays of different size\n", string_content(obj_name));
+			return SAFE_ABORT; // Shouldn't happen
+		}
+		// Elements also have the same size
+		if ((emu_typ->val.array.array_sz == (size_t)-1) || (target_typ->val.array.array_sz == (size_t)-1)) {
+			printf("Error: %s: has variable length arrays\n", string_content(obj_name));
+			return SAFE_ABORT; // VLA require manual intervention
+		}
+		return get_safeness_ptr(emu_typ->val.array.typ, target_typ->val.array.typ, needs_D, needs_my, conv_map, obj_name);
+	case TYPE_STRUCT_UNION:
+		if (emu_typ->val.st->is_struct != target_typ->val.st->is_struct) {
+			printf("Error: %s: incoherent struct/union type between emulated and target architectures for %s\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->is_incomplete != target_typ->is_incomplete) {
+			printf("Error: %s: incoherent struct/union completion type between emulated and target architectures for %s\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->is_incomplete) {
+			printf("Warning: %s: undefined struct/union %s considered as simple\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_OK; // Assume undefined structures are OK since they are opaque
+		}
+		if (emu_typ->val.st->nmembers != target_typ->val.st->nmembers) {
+			printf("Error: %s: struct/union %s has different number of members between emulated and target architectures\n",
+				string_content(obj_name), emu_typ->val.st->tag ? string_content(emu_typ->val.st->tag) : "<no tag>");
+			return SAFE_ABORT;
+		}
+		if (emu_typ->val.st->has_self_recursion) {
+			// Self-reference implies manual intervention
+			*needs_my = 1;
+			return SAFE_OK;
+		}
+		for (size_t i = 0; i < emu_typ->val.st->nmembers; ++i) {
+			switch (get_safeness(emu_typ->val.st->members[i].typ, target_typ->val.st->members[i].typ, needs_D, needs_my, conv_map, obj_name)) {
+			case SAFE_OK: break;
+			case SAFE_ABORT:
+			case SAFE_EXPAND:
+			default: return SAFE_ABORT;
+			}
+		}
+		return SAFE_OK;
+	case TYPE_ENUM:
+		return get_safeness(emu_typ->val.typ, target_typ->val.typ, needs_D, needs_my, conv_map, obj_name);
+	case TYPE_PTR:
+		// Pointers have different sizes here
+		return SAFE_EXPAND;
+	case TYPE_FUNCTION:
+		// Functions should be handled differently (GO instead of DATA)
+		return SAFE_ABORT;
+	default:
+		printf("Error: get_safeness on unknown type %u\n", emu_typ->typ);
+		return SAFE_ABORT;
+	}
+}
+
+// needs_S != NULL iff type is return
+static int convert_type(string_t *dest, type_t *emu_typ, type_t *target_typ,
+                        _Bool *needs_S, int *needs_D, int *needs_my, khash_t(conv_map) *conv_map, string_t *obj_name) {
+	if (emu_typ->converted) {
+		if (!string_add_string(dest, emu_typ->converted)) {
+			printf("Error: failed to add explicit type conversion\n");
+			return 0;
+		}
+		return 1;
+	}
+	khiter_t it = kh_get(conv_map, conv_map, emu_typ);
+	if (it != kh_end(conv_map)) {
+		if (!string_add_string(dest, kh_val(conv_map, it))) {
+			printf("Error: failed to add explicit type conversion\n");
+			return 0;
+		}
+		return 1;
+	}
+	if ((emu_typ->is_atomic) || (target_typ->is_atomic)) {
+		printf("Error: TODO: convert_type for atomic types\n");
+		return 0;
+	}
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: %s type is different between emulated and target\n", string_content(obj_name), needs_S ? "return" : "argument");
 		return 0;
 	}
+	switch (emu_typ->typ) {
+	case TYPE_BUILTIN: {
+		int has_char = 0;
+		char c;
+		switch (emu_typ->val.builtin) {
+		case BTT_VOID: has_char = 1; c = 'v'; break;
+		case BTT_BOOL: has_char = 1; c = 'i'; break;
+		case BTT_CHAR: has_char = 1; c = 'c'; break;
+		case BTT_SCHAR: has_char = 1; c = 'c'; break;
+		case BTT_UCHAR: has_char = 1; c = 'C'; break;
+		case BTT_SHORT: has_char = 1; c = 'w'; break;
+		case BTT_SSHORT: has_char = 1; c = 'w'; break;
+		case BTT_USHORT: has_char = 1; c = 'W'; break;
+		case BTT_INT: has_char = 1; c = 'i'; break;
+		case BTT_SINT: has_char = 1; c = 'i'; break;
+		case BTT_UINT: has_char = 1; c = 'u'; break;
+		case BTT_LONG: has_char = 1; c = 'l'; break;
+		case BTT_SLONG: has_char = 1; c = 'l'; break;
+		case BTT_ULONG: has_char = 1; c = 'L'; break;
+		case BTT_LONGLONG: has_char = 1; c = 'I'; break;
+		case BTT_SLONGLONG: has_char = 1; c = 'I'; break;
+		case BTT_ULONGLONG: has_char = 1; c = 'U'; break;
+		case BTT_INT128: has_char = 1; c = 'H'; break;
+		case BTT_SINT128: has_char = 1; c = 'H'; break;
+		case BTT_UINT128: has_char = 1; c = 'H'; break;
+		case BTT_S8: has_char = 1; c = 'c'; break;
+		case BTT_U8: has_char = 1; c = 'C'; break;
+		case BTT_S16: has_char = 1; c = 'w'; break;
+		case BTT_U16: has_char = 1; c = 'W'; break;
+		case BTT_S32: has_char = 1; c = 'i'; break;
+		case BTT_U32: has_char = 1; c = 'u'; break;
+		case BTT_S64: has_char = 1; c = 'I'; break;
+		case BTT_U64: has_char = 1; c = 'U'; break;
+		case BTT_FLOAT: has_char = 1; c = 'f'; break;
+		case BTT_CFLOAT: has_char = 1; c = 'x'; break;
+		case BTT_IFLOAT: has_char = 1; c = 'f'; break;
+		case BTT_DOUBLE: has_char = 1; c = 'd'; break;
+		case BTT_CDOUBLE: has_char = 1; c = 'X'; break;
+		case BTT_IDOUBLE: has_char = 1; c = 'd'; break;
+		case BTT_LONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
+		case BTT_CLONGDOUBLE: *needs_D = 1; has_char = 1; c = 'Y'; break;
+		case BTT_ILONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
+		case BTT_FLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
+		case BTT_CFLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
+		case BTT_IFLOAT128: printf("Error: TODO: %s\n", builtin2str[emu_typ->val.builtin]); return 0;
+		case BTT_VA_LIST: *needs_my = 1; has_char = 1; c = 'p'; break;
+		default:
+			printf("Error: convert_type on unknown builtin %u\n", emu_typ->val.builtin);
+			return 0;
+		}
+		if (has_char) {
+			if (!string_add_char(dest, c)) {
+				printf("Error: failed to add type char for complex pointer\n");
+				return 0;
+			}
+			return 1;
+		} else {
+			printf("Internal error: unknown state builtin=%u\n", emu_typ->val.builtin);
+			return 0;
+		} }
+	case TYPE_ARRAY: {
+		// May come from the content of a pointer or structure
+		if ((emu_typ->val.array.array_sz == (size_t)-1) || (target_typ->val.array.array_sz == (size_t)-1)) {
+			printf("Error: %s: has variable length arrays\n", string_content(obj_name));
+			return 0; // VLA require manual intervention
+		}
+		if (emu_typ->val.array.array_sz != target_typ->val.array.array_sz) {
+			printf("Error: %s: emu and target have arrays of different size\n", string_content(obj_name));
+			return 0; // Shouldn't happen
+		}
+		if (!emu_typ->val.array.array_sz) {
+			// printf("Warning: %s: has zero-length array\n", string_content(obj_name));
+			return 1;
+		}
+		size_t idx = string_len(dest);
+		if (!convert_type(dest, emu_typ->val.array.typ, target_typ->val.array.typ, needs_S, needs_D, needs_my, conv_map, obj_name))
+			return 0;
+		size_t end = string_len(dest);
+		if (idx == end) return 1;
+		if (!string_reserve(dest, idx + (end - idx) * emu_typ->val.array.array_sz)) {
+			printf("Error: failed to reserve string capacity (for array of type conversing length %zu and size %zu)\n",
+				end - idx, emu_typ->val.array.array_sz);
+			return 0;
+		}
+		for (size_t i = 1; i < emu_typ->val.array.array_sz; ++i) {
+			memcpy(string_content(dest) + idx + (end - idx) * i, string_content(dest) + idx, end - idx);
+			// HACKHACKHACK ===
+			string_len(dest) += end - idx;
+			// === HACKHACKHACK
+		}
+		return 1; }
+	case TYPE_STRUCT_UNION:
+		if (!emu_typ->is_validated || emu_typ->is_incomplete) {
+			printf("Error: incomplete structure for %s\n", string_content(obj_name));
+			return 0;
+		}
+		if (needs_S) {
+			*needs_S = 1;
+			// TODO: remove this and add support in the python wrappers for structure returns
+			if (!string_add_char(dest, 'p')) {
+				printf("Error: failed to add type char for very large structure return\n");
+				return 0;
+			}
+			return 1;
+		} else {
+			if ((emu_typ->val.st->nmembers == 1) && (target_typ->typ == TYPE_STRUCT_UNION) && (target_typ->val.st->nmembers == 1)) {
+				return convert_type(dest, emu_typ->val.st->members[0].typ, target_typ->val.st->members[0].typ, needs_S, needs_D, needs_my, conv_map, obj_name);
+			}
+			printf("Error: TODO: convert_type on structure as argument (%s)\n", string_content(obj_name));
+			return 0;
+		}
+	case TYPE_ENUM:
+		return convert_type(dest, emu_typ->val.typ, target_typ->val.typ, needs_S, needs_D, needs_my, conv_map, obj_name);
+	case TYPE_PTR:
+		switch (get_safeness_ptr(emu_typ->val.typ, target_typ->val.typ, needs_D, needs_my, conv_map, obj_name)) {
+		default:
+			printf("Internal error: %s: pointer to unknown type\n", string_content(obj_name));
+			return 0;
+			
+		case SAFE_ABORT:
+			return 0;
+			
+		case SAFE_OK:
+			if (!string_add_char(dest, 'p')) {
+				printf("Error: failed to add type char for simple pointer\n");
+				return 0;
+			}
+			return 1;
+			
+		case SAFE_EXPAND:
+			if (!string_add_char(dest, emu_typ->is_const ? 'r' : 'b')) {
+				printf("Error: failed to add start type char for wrapping pointer\n");
+				return 0;
+			}
+			// Find the underlying type to convert
+			emu_typ = emu_typ->val.typ;
+			target_typ = target_typ->val.typ;
+		do_expanded:
+			switch (emu_typ->typ) {
+			case TYPE_BUILTIN:
+				if (!convert_type(dest, emu_typ, target_typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+					return 0;
+				}
+				break;
+			case TYPE_ARRAY:
+				if (!convert_type(dest, emu_typ, target_typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+					return 0;
+				}
+				break;
+			case TYPE_STRUCT_UNION:
+				if (!emu_typ->val.st->is_defined) {
+					printf("Internal error: EXPAND with undefined struct/union ptr\n");
+					return 0;
+				}
+				if (emu_typ->val.st->nmembers == 1) { // Single-member struct/union
+					emu_typ = emu_typ->val.st->members[0].typ;
+					target_typ = target_typ->val.st->members[0].typ;
+					goto do_expanded;
+				}
+				for (size_t i = 0; i < emu_typ->val.st->nmembers; ++i) {
+					if (!convert_type(dest, emu_typ->val.st->members[i].typ, target_typ->val.st->members[i].typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+						return 0;
+					}
+				}
+				break;
+			case TYPE_ENUM:
+				emu_typ = emu_typ->val.typ;
+				target_typ = target_typ->val.typ;
+				if (!convert_type(dest, emu_typ, target_typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+					return 0;
+				}
+				break;
+			case TYPE_PTR:
+				if (!convert_type(dest, emu_typ, target_typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+					return 0;
+				}
+				break;
+			case TYPE_FUNCTION:
+				if (!convert_type(dest, emu_typ, target_typ, needs_S, needs_D, needs_my, conv_map, obj_name)) {
+					return 0;
+				}
+				break;
+			}
+			if (!string_add_char(dest, '_')) {
+				printf("Error: failed to add end type char for wrapping pointer\n");
+				return 0;
+			}
+			return 1;
+		}
+	case TYPE_FUNCTION:
+		printf("Internal error: convert_type on raw function\n");
+		return 1;
+	default:
+		printf("Error: convert_type on unknown type %u\n", emu_typ->typ);
+		return 0;
+	}
+}
+// TODO: move this function to the python script (implement correct structure returns)
+static int convert_type_post(string_t *dest, type_t *emu_typ, type_t *target_typ, string_t *obj_name) {
+	if (emu_typ->converted) return 1;
+	if (emu_typ->is_atomic) {
+		printf("Error: TODO: convert_type_post for atomic types\n");
+		return 0;
+	}
+	(void)target_typ;
+	switch (emu_typ->typ) {
+	case TYPE_BUILTIN: return 1;
+	case TYPE_ARRAY: return 1;
+	case TYPE_STRUCT_UNION:
+		if (!emu_typ->is_validated || emu_typ->is_incomplete) {
+			printf("Error: incomplete structure for %s\n", string_content(obj_name));
+			return 0;
+		}
+		// Hard coded
+		if (!string_add_char(dest, 'p')) {
+			printf("Error: failed to add type char for very large structure return as parameter\n");
+			return 0;
+		}
+		return 2;
+	case TYPE_ENUM: return 1;
+	case TYPE_PTR: return 1;
+	case TYPE_FUNCTION: return 1;
+	}
+	printf("Error: convert_type_post on unknown type %u\n", emu_typ->typ);
+	return 0;
 }
 
 int solve_request(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(conv_map) *conv_map) {
-	if (!type_t_equal(emu_typ, target_typ)) {
-		printf("Error: TODO: %s: emulated and target types are different\n", string_content(req->obj_name));
+	if (emu_typ->typ != target_typ->typ) {
+		printf("Error: %s: emulated and target types are different (emulated is %u, target is %u)\n",
+			string_content(req->obj_name), emu_typ->typ, target_typ->typ);
 		return 0;
 	}
 	if (emu_typ->typ == TYPE_FUNCTION) {
@@ -978,16 +1666,17 @@ int solve_request(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(c
 			printf("Error: failed to create function type string\n");
 			return 0;
 		}
-		if (!convert_type(req->val.fun.typ, emu_typ->val.fun.ret, 1, &needs_D, &needs_my, conv_map, req->obj_name)) goto fun_fail;
+		if (!convert_type(req->val.fun.typ, emu_typ->val.fun.ret, target_typ->val.fun.ret, &req->val.fun.needs_S, &needs_D, &needs_my, conv_map, req->obj_name))
+			goto fun_fail;
 		idx_conv = string_len(req->val.fun.typ);
 		if (!string_add_char(req->val.fun.typ, 'F')) {
 			printf("Error: failed to add convention char\n");
 			goto fun_fail;
 		}
-		convert_post = convert_type_post(req->val.fun.typ, emu_typ->val.fun.ret, req->obj_name);
+		convert_post = convert_type_post(req->val.fun.typ, emu_typ->val.fun.ret, target_typ->val.fun.ret, req->obj_name);
 		if (!convert_post) goto fun_fail;
 		if (emu_typ->val.fun.nargs == (size_t)-1) {
-			printf("Warning: assuming empty specification is void specification\n");
+			printf("Warning: %s: assuming empty specification is void specification\n", string_content(req->obj_name));
 			if (convert_post == 1) {
 				if (!string_add_char(req->val.fun.typ, 'v')) {
 					printf("Error: failed to add void specification char\n");
@@ -1003,7 +1692,8 @@ int solve_request(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(c
 			}
 		} else {
 			for (size_t i = 0; i < emu_typ->val.fun.nargs; ++i) {
-				if (!convert_type(req->val.fun.typ, emu_typ->val.fun.args[i], 0, &needs_D, &needs_my, conv_map, req->obj_name)) goto fun_fail;
+				if (!convert_type(req->val.fun.typ, emu_typ->val.fun.args[i], target_typ->val.fun.args[i], NULL, &needs_D, &needs_my, conv_map, req->obj_name))
+					goto fun_fail;
 			}
 			if (emu_typ->val.fun.has_varargs) {
 				if (req->def.fun.typ
@@ -1070,15 +1760,20 @@ int solve_request(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(c
 		return 0;
 	} else {
 		int needs_D = 0, needs_my = req->def.dat.has_size && (req->def.rty == RQT_DATAM);
-		if (is_simple_type(emu_typ, &needs_D, &needs_my, conv_map)) {
-			// TODO: Hmm...
+		switch (get_safeness(emu_typ, target_typ, &needs_D, &needs_my, conv_map, req->obj_name)) {
+		case SAFE_EXPAND:
+			needs_my = 1;
+			/* FALLTHROUGH */
+		case SAFE_OK:
 			req->val.rty = needs_my ? RQT_DATAM : req->def.rty;
 			req->val.dat.has_size = 1;
 			req->val.dat.sz = emu_typ->szinfo.size;
 			req->has_val = 1;
 			return 1;
-		} else {
-			printf("Error: TODO: solve_request for data %s with non-simple type ", string_content(req->obj_name));
+			
+		case SAFE_ABORT:
+		default:
+			log_TODO_nopos("solve_request for data %s with non-simple type ", string_content(req->obj_name));
 			type_print(emu_typ);
 			printf("\n");
 			return 0;
@@ -1095,6 +1790,7 @@ int solve_request_map(request_t *req, khash_t(decl_map) *emu_decl_map, khash_t(d
 	if ((kh_val(emu_decl_map, emuit)->storage == STORAGE_STATIC) || (kh_val(emu_decl_map, emuit)->storage == STORAGE_TLS_STATIC)) {
 		goto failed;
 	}
+	hasemu = 1;
 	targetit = kh_get(decl_map, target_decl_map, string_content(req->obj_name));
 	if (targetit == kh_end(target_decl_map)) {
 		goto failed;
@@ -1102,6 +1798,7 @@ int solve_request_map(request_t *req, khash_t(decl_map) *emu_decl_map, khash_t(d
 	if ((kh_val(target_decl_map, targetit)->storage == STORAGE_STATIC) || (kh_val(target_decl_map, targetit)->storage == STORAGE_TLS_STATIC)) {
 		goto failed;
 	}
+	hastarget = 1;
 	return solve_request(req, kh_val(emu_decl_map, emuit)->typ, kh_val(target_decl_map, targetit)->typ, conv_map);
 	
 failed:
diff --git a/wrapperhelper/src/generator.h b/wrapperhelper/src/generator.h
index 4d8ef120..89044af7 100644
--- a/wrapperhelper/src/generator.h
+++ b/wrapperhelper/src/generator.h
@@ -28,6 +28,7 @@ typedef struct request_s {
 			struct {
 				string_t *typ;
 				string_t *fun2;
+				_Bool needs_S;
 			} fun;
 			struct {
 				int has_size;
@@ -57,6 +58,13 @@ void references_print_check(const VECTOR(references) *refs);
 void output_from_references(FILE *f, const VECTOR(references) *reqs);
 
 VECTOR(references) *references_from_file(const char *filename, FILE *f); // Takes ownership of f
+
+// Simple solvers (emu and target have the same pointer size)
+int solve_request_simple(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(conv_map) *conv_map);
+int solve_request_map_simple(request_t *req, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map);
+int solve_references_simple(VECTOR(references) *reqs, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map);
+
+// Complex solvers (emu and target have different pointer size)
 int solve_request(request_t *req, type_t *emu_typ, type_t *target_typ, khash_t(conv_map) *conv_map);
 int solve_request_map(request_t *req, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map);
 int solve_references(VECTOR(references) *reqs, khash_t(decl_map) *emu_decl_map, khash_t(decl_map) *target_decl_map, khash_t(conv_map) *conv_map);
diff --git a/wrapperhelper/src/lang.c b/wrapperhelper/src/lang.c
index c3b5ca19..85c2096e 100644
--- a/wrapperhelper/src/lang.c
+++ b/wrapperhelper/src/lang.c
@@ -156,44 +156,46 @@ const char *sym2str[LAST_SYM + 1] = {
 };
 
 void preproc_token_print(const preproc_token_t *tok) {
+	printf("Token: ");
+	loginfo_print(&tok->loginfo, 50);
 	switch (tok->tokt) {
 	case PPTOK_INVALID:
-		printf("Token: %7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?');
+		printf("%7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?');
 		break;
 	case PPTOK_IDENT:
-		printf("Token: %7s '%s'\n", "IDENT", string_content(tok->tokv.str));
+		printf("%7s '%s'\n", "IDENT", string_content(tok->tokv.str));
 		break;
 	case PPTOK_IDENT_UNEXP:
-		printf("Token: %7s '%s'\n", "IDENT'", string_content(tok->tokv.str));
+		printf("%7s '%s'\n", "IDENT'", string_content(tok->tokv.str));
 		break;
 	case PPTOK_NUM:
-		printf("Token: %7s '%s'\n", "NUM", string_content(tok->tokv.str));
+		printf("%7s '%s'\n", "NUM", string_content(tok->tokv.str));
 		break;
 	case PPTOK_STRING:
-		printf("Token: %7s %c%s%c\n", "STRING",
+		printf("%7s %c%s%c\n", "STRING",
 			tok->tokv.sisstr ? '"' : '\'', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '\'');
 		break;
 	case PPTOK_INCL:
-		printf("Token: %7s %c%s%c\n", "INCL",
+		printf("%7s %c%s%c\n", "INCL",
 			tok->tokv.sisstr ? '"' : '<', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '>');
 		break;
 	case PPTOK_SYM:
-		printf("Token: %7s  %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym);
+		printf("%7s  %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym);
 		break;
 	case PPTOK_NEWLINE:
-		printf("Token: %7s\n", "NEWLINE");
+		printf("%7s\n", "NEWLINE");
 		break;
 	case PPTOK_BLANK:
-		printf("Token: %7s\n", "\e[2;31m(blank)\e[m");
+		printf("%7s\n", "\e[2;31m(blank)\e[m");
 		break;
 	case PPTOK_START_LINE_COMMENT:
-		printf("Token: %7s\n", "\e[2;31m( // ) \e[m");
+		printf("%7s\n", "\e[2;31m( // ) \e[m");
 		break;
 	case PPTOK_EOF:
-		printf("Token: %7s\n", "EOF");
+		printf("%7s\n", "EOF");
 		break;
 	default:
-		printf("Token: ??? %u\n", tok->tokt);
+		printf("??? %u\n", tok->tokt);
 	}
 }
 
@@ -266,49 +268,51 @@ const char *kw2str[LAST_KEYWORD + 1] = {
 };
 
 void proc_token_print(const proc_token_t *tok) {
+	printf("Token: ");
+	loginfo_print(&tok->loginfo, 50);
 	switch (tok->tokt) {
 	case PTOK_INVALID:
-		printf("Token: %7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?');
+		printf("%7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?');
 		break;
 	case PTOK_IDENT:
-		printf("Token: %7s '%s'\n", "IDENT", string_content(tok->tokv.str));
+		printf("%7s '%s'\n", "IDENT", string_content(tok->tokv.str));
 		break;
 	case PTOK_KEYWORD:
-		printf("Token: %7s '%s' (%u)\n", "KEYWORD", kw2str[tok->tokv.kw], tok->tokv.kw);
+		printf("%7s '%s' (%u)\n", "KEYWORD", kw2str[tok->tokv.kw], tok->tokv.kw);
 		break;
 	case PTOK_NUM:
-		printf("Token: %7s '%s'\n", "NUM", string_content(tok->tokv.str));
+		printf("%7s '%s'\n", "NUM", string_content(tok->tokv.str));
 		break;
 	case PTOK_STRING:
-		printf("Token: %7s %c%s%c\n", "STRING",
+		printf("%7s %c%s%c\n", "STRING",
 			tok->tokv.sisstr ? '"' : '\'', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '\'');
 		break;
 	case PTOK_SYM:
-		printf("Token: %7s  %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym);
+		printf("%7s  %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym);
 		break;
 	case PTOK_PRAGMA:
 		switch (tok->tokv.pragma.typ) {
 		case PRAGMA_ALLOW_INTS:
-			printf("Token: %7s Allow ints\n", "PRAGMA");
+			printf("%7s Allow ints\n", "PRAGMA");
 			break;
 		case PRAGMA_SIMPLE_SU:
-			printf("Token: %7s Mark simple: struct or union %s is simple\n", "PRAGMA", string_content(tok->tokv.pragma.val));
+			printf("%7s Mark simple: struct or union %s is simple\n", "PRAGMA", string_content(tok->tokv.pragma.val));
 			break;
 		case PRAGMA_EXPLICIT_CONV:
-			printf("Token: %7s Relaxed explicit conversion: destination is %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
+			printf("%7s Relaxed explicit conversion: destination is %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
 			break;
 		case PRAGMA_EXPLICIT_CONV_STRICT:
-			printf("Token: %7s Strict explicit conversion: destination is %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
+			printf("%7s Strict explicit conversion: destination is %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
 			break;
 		default:
-			printf("Token: %7s ???\n", "PRAGMA");
+			printf("%7s ??? %u\n", "PRAGMA", tok->tokv.pragma.typ);
 		}
 		break;
 	case PTOK_EOF:
-		printf("Token: %7s\n", "EOF");
+		printf("%7s\n", "EOF");
 		break;
 	default:
-		printf("Token: ??? %u\n", tok->tokt);
+		printf("??? %u\n", tok->tokt);
 	}
 }
 
@@ -356,26 +360,26 @@ khash_t(str2kw) *str2kw;
 
 int init_str2kw(void) {
 	if (!(str2kw = kh_init(str2kw))) {
-		printf("Failed to create the string to keyword map (init)\n");
+		log_memory("failed to create the string to keyword map (init)\n");
 		return 0;
 	}
 	int iret; khiter_t it;
 	for (enum token_keyword_type_e kw = 0; kw <= LAST_KEYWORD; ++kw) {
 		it = kh_put(str2kw, str2kw, kw2str[kw], &iret);
 		if (iret < 0) {
-			printf("Failed to create the string to keyword map (keyword %u)\n", kw);
+			log_memory("failed to create the string to keyword map (keyword %u)\n", kw);
 			kh_destroy(str2kw, str2kw);
 			return 0;
 		}
 		kh_val(str2kw, it) = kw;
 	}
 #define ADD_ALIAS(alias, k) \
-	it = kh_put(str2kw, str2kw, #alias, &iret);                                            \
-	if (iret < 0) {                                                                        \
-		printf("Failed to create the string to keyword map (keyword alias " #alias ")\n"); \
-		kh_destroy(str2kw, str2kw);                                                        \
-		return 0;                                                                          \
-	}                                                                                      \
+	it = kh_put(str2kw, str2kw, #alias, &iret);                                                \
+	if (iret < 0) {                                                                            \
+		log_memory("failed to create the string to keyword map (keyword alias " #alias ")\n"); \
+		kh_destroy(str2kw, str2kw);                                                            \
+		return 0;                                                                              \
+	}                                                                                          \
 	kh_val(str2kw, it) = KW_ ## k;
 	ADD_ALIAS(__alignof__,   ALIGNOF)
 	// ADD_ALIAS(__asm,         asm)
@@ -404,8 +408,8 @@ void del_str2kw(void) {
 	kh_destroy(str2kw, str2kw);
 }
 
-// ptr_is_32bits is actially long_is_32bits
-int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits) {
+// ptr_is_32bits is actually long_is_32bits
+int num_constant_convert(loginfo_t *li, string_t *str, num_constant_t *cst, int ptr_is_32bits) {
 	if (string_len(str) == 0) return 0; // Should never happen
 #define contains(c) strchr(string_content(str), c)
 	if (contains('.')
@@ -430,10 +434,10 @@ int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits)
 			ok = (endc == string_end(str));
 		}
 		if (!ok) {
-			printf("Error: '%s' is not a valid number\n", string_content(str));
+			log_error(li, "'%s' is not a valid number\n", string_content(str));
 			return 0;
 		} else if (errno == ERANGE) {
-			printf("Warning: floating point constant is too large\n");
+			log_warning(li, "floating point constant is too large\n");
 			return 1;
 		}
 		return 1;
@@ -451,14 +455,14 @@ int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits)
 	remove_suffix:
 		if ((string_content(str)[endidx - 1] == 'l') || (string_content(str)[endidx - 1] == 'L')) {
 			if (suffix_type & SUFFIX_SZ) {
-				printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str));
+				log_error(li, "'%s' is not a valid number (invalid suffix)\n", string_content(str));
 				return 0;
 			}
 			if (endidx == 1) return 0; // Should never happen
 			if ((string_content(str)[endidx - 2] == 'l') || (string_content(str)[endidx - 2] == 'L')) {
 				if (endidx == 2) return 0; // Should never happen
 				if (string_content(str)[endidx - 2] != string_content(str)[endidx - 1]) {
-					printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str));
+					log_error(li, "'%s' is not a valid number (invalid suffix)\n", string_content(str));
 					return 0;
 				}
 				endidx -= 2;
@@ -471,7 +475,7 @@ int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits)
 		}
 		if ((string_content(str)[endidx - 1] == 'u') || (string_content(str)[endidx - 1] == 'U')) {
 			if (suffix_type & SUFFIX_SGN) {
-				printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str));
+				log_error(li, "'%s' is not a valid number (invalid suffix)\n", string_content(str));
 				return 0;
 			}
 			endidx -= 1;
@@ -480,7 +484,7 @@ int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits)
 		}
 		// 0u has startidx=0 endidx=1
 		if (endidx <= startidx) {
-			printf("Error: '%s' is not a valid number\n", string_content(str));
+			log_error(li, "'%s' is not a valid number\n", string_content(str));
 		}
 		for (size_t i = startidx; i < endidx; ++i) {
 			if ((string_content(str)[i] >= '0') && (string_content(str)[i] <= ((base == 8) ? '7' : '9'))) {
@@ -490,7 +494,7 @@ int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits)
 			} else if ((base == 16) && (string_content(str)[i] >= 'a') && (string_content(str)[i] <= 'f')) {
 				ret = base * ret + 10 + (unsigned)(string_content(str)[i] - 'a');
 			} else {
-				printf("Error: '%s' is not a valid number\n", string_content(str));
+				log_error(li, "'%s' is not a valid number\n", string_content(str));
 				return 0;
 			}
 		}
@@ -590,6 +594,151 @@ void expr_del(expr_t *e) {
 	free(e);
 }
 
+static void expr_print_aux(expr_t *e, int parent_level) {
+	switch (e->typ) {
+	case ETY_VAR:
+		// Level 0, no parenthesis
+		printf("%s", string_content(e->val.var));
+		break;
+		
+	case ETY_CONST:
+		// Level 0, no parenthesis
+		switch (e->val.cst.typ) {
+		case NCT_FLOAT: printf("%ff", e->val.cst.val.f); break;
+		case NCT_DOUBLE: printf("%f", e->val.cst.val.d); break;
+		case NCT_LDOUBLE: printf("%Lfl", e->val.cst.val.l); break;
+		case NCT_INT32: printf("%d", e->val.cst.val.i32); break;
+		case NCT_UINT32: printf("%uu", e->val.cst.val.u32); break;
+		case NCT_INT64: printf("%ldll", e->val.cst.val.i64); break;
+		case NCT_UINT64: printf("%lullu", e->val.cst.val.u64); break;
+		}
+		break;
+		
+	case ETY_CALL:
+		// Level 1, no parenthesis
+		expr_print_aux(e->val.call.fun, 1);
+		printf("(");
+		for (size_t i = 0; i < e->val.call.nargs; ++i) {
+			expr_print_aux(e->val.call.fun, 15);
+		}
+		printf(")");
+		break;
+		
+	case ETY_ACCESS:
+		// Level 1, no parenthesis
+		expr_print_aux(e->val.access.val, 1);
+		printf(".%s", string_content(e->val.access.member));
+		break;
+		
+	case ETY_PTRACCESS:
+		// Level 1, no parenthesis
+		expr_print_aux(e->val.access.val, 1);
+		printf(".%s", string_content(e->val.access.member));
+		break;
+		
+	case ETY_UNARY: {
+		const char *preop = NULL;
+		switch (e->val.unary.typ) {
+		case UOT_POSTINCR:
+			// Level 1, no parenthesis
+			expr_print_aux(e->val.unary.e, 1);
+			printf("++");
+			break;
+		case UOT_POSTDECR:
+			// Level 1, no parenthesis
+			expr_print_aux(e->val.unary.e, 1);
+			printf("--");
+			break;
+		case UOT_PREINCR: preop = "++"; break;
+		case UOT_PREDECR: preop = "--"; break;
+		case UOT_REF: preop = "&"; break;
+		case UOT_POS: preop = "+"; break;
+		case UOT_NEG: preop = "-"; break;
+		case UOT_DEREF: preop = "*"; break;
+		case UOT_ANOT: preop = "~"; break;
+		case UOT_BNOT: preop = "!"; break;
+		}
+		if (preop) {
+			if (parent_level < 2) printf("(");
+			printf("%s", preop);
+			expr_print_aux(e->val.unary.e, 2);
+			if (parent_level < 2) printf(")");
+		}
+		break; }
+		
+	case ETY_BINARY: {
+		const char *midop, *postop = NULL;
+		int mlevel, llevel, rlevel;
+		switch (e->val.binary.typ) {
+		case BOT_ADD:        mlevel =  5; llevel =  5; rlevel =  4; midop = " + "; break;
+		case BOT_SUB:        mlevel =  5; llevel =  5; rlevel =  4; midop = " - "; break;
+		case BOT_MUL:        mlevel =  4; llevel =  4; rlevel =  3; midop = " * "; break;
+		case BOT_DIV:        mlevel =  4; llevel =  4; rlevel =  3; midop = " / "; break;
+		case BOT_MOD:        mlevel =  4; llevel =  4; rlevel =  3; midop = " % "; break;
+		case BOT_LSH:        mlevel =  6; llevel =  6; rlevel =  5; midop = " << "; break;
+		case BOT_RSH:        mlevel =  6; llevel =  6; rlevel =  5; midop = " >> "; break;
+		case BOT_LT:         mlevel =  7; llevel =  7; rlevel =  6; midop = " < "; break;
+		case BOT_GT:         mlevel =  7; llevel =  7; rlevel =  6; midop = " > "; break;
+		case BOT_LE:         mlevel =  7; llevel =  7; rlevel =  6; midop = " <= "; break;
+		case BOT_GE:         mlevel =  7; llevel =  7; rlevel =  6; midop = " >= "; break;
+		case BOT_EQ:         mlevel =  8; llevel =  8; rlevel =  7; midop = " == "; break;
+		case BOT_NE:         mlevel =  8; llevel =  8; rlevel =  7; midop = " != "; break;
+		case BOT_AAND:       mlevel =  9; llevel =  9; rlevel =  8; midop = " & "; break;
+		case BOT_AXOR:       mlevel = 10; llevel = 10; rlevel =  9; midop = " ^ "; break;
+		case BOT_AOR:        mlevel = 11; llevel = 11; rlevel = 10; midop = " | "; break;
+		case BOT_BAND:       mlevel = 12; llevel = 12; rlevel = 11; midop = " && "; break;
+		case BOT_BOR:        mlevel = 13; llevel = 13; rlevel = 12; midop = " || "; break;
+		case BOT_ASSGN_EQ:   mlevel = 15; llevel =  2; rlevel = 15; midop = " = "; break;
+		case BOT_ASSGN_ADD:  mlevel = 15; llevel =  2; rlevel = 15; midop = " += "; break;
+		case BOT_ASSGN_SUB:  mlevel = 15; llevel =  2; rlevel = 15; midop = " -= "; break;
+		case BOT_ASSGN_MUL:  mlevel = 15; llevel =  2; rlevel = 15; midop = " *= "; break;
+		case BOT_ASSGN_DIV:  mlevel = 15; llevel =  2; rlevel = 15; midop = " /= "; break;
+		case BOT_ASSGN_MOD:  mlevel = 15; llevel =  2; rlevel = 15; midop = " %= "; break;
+		case BOT_ASSGN_LSH:  mlevel = 15; llevel =  2; rlevel = 15; midop = " <<= "; break;
+		case BOT_ASSGN_RSH:  mlevel = 15; llevel =  2; rlevel = 15; midop = " >>= "; break;
+		case BOT_ASSGN_AAND: mlevel = 15; llevel =  2; rlevel = 15; midop = " &= "; break;
+		case BOT_ASSGN_AXOR: mlevel = 15; llevel =  2; rlevel = 15; midop = " ^= "; break;
+		case BOT_ASSGN_AOR:  mlevel = 15; llevel =  2; rlevel = 15; midop = " |= "; break;
+		case BOT_COMMA:      mlevel = 16; llevel = 16; rlevel = 15; midop = ", "; break;
+		case BOT_ARRAY:      mlevel =  1; llevel =  1; rlevel = 16; midop = "["; postop = "]"; break;
+		default: mlevel = 17; llevel = rlevel = -1; midop = " ? "; break;
+		}
+		if (parent_level < mlevel) printf("(");
+		expr_print_aux(e->val.binary.e1, llevel);
+		printf("%s", midop);
+		expr_print_aux(e->val.binary.e2, rlevel);
+		if (postop) printf("%s", midop);
+		if (parent_level < mlevel) printf(")");
+		break; }
+		
+	case ETY_TERNARY:
+		switch (e->val.ternary.typ) {
+		case TOT_COND:
+			if (parent_level < 15) printf("(");
+			expr_print_aux(e->val.ternary.e1, 14);
+			printf(" ? ");
+			expr_print_aux(e->val.ternary.e2, 16);
+			expr_print_aux(e->val.ternary.e2, 15);
+			printf(" : ");
+			if (parent_level < 15) printf(")");
+			break;
+		}
+		break;
+		
+	case ETY_CAST:
+		if (parent_level < 3) printf("(");
+		printf("(");
+		type_print(e->val.cast.typ);
+		printf(")");
+		expr_print(e->val.cast.e);
+		if (parent_level < 3) printf(")");
+		break;
+	}
+}
+void expr_print(expr_t *e) {
+	return expr_print_aux(e, 16);
+}
+
 void struct_del_weak(struct_t *st) {
 	if (--st->nrefs) return;
 	if (!st->tag) struct_del(st);
@@ -701,7 +850,7 @@ void struct_map_del(khash_t(struct_map) *map) {
 type_t *type_new(void) {
 	type_t *ret = malloc(sizeof *ret);
 	if (!ret) {
-		printf("Failed to create a new type\n");
+		log_memory("failed to create a new type\n");
 		return NULL;
 	}
 	ret->szinfo.align = ret->szinfo.size = 0;
@@ -715,7 +864,7 @@ type_t *type_new(void) {
 type_t *type_new_ptr(type_t *target) {
 	type_t *ret = malloc(sizeof *ret);
 	if (!ret) {
-		printf("Failed to create a new pointer type\n");
+		log_memory("failed to create a new pointer type\n");
 		return NULL;
 	}
 	ret->szinfo.align = ret->szinfo.size = 0;
@@ -755,7 +904,7 @@ int type_copy_into(type_t *dest, const type_t *ref) {
 			}
 			type_t **args = malloc(sizeof *args * ref->val.fun.nargs);
 			if (!args) {
-				printf("Error: failed to allocate new argument array\n");
+				log_memory("failed to allocate new argument array\n");
 				return 0;
 			}
 			for (size_t i = 0; i < ref->val.fun.nargs; ++i) {
@@ -777,7 +926,7 @@ int type_copy_into(type_t *dest, const type_t *ref) {
 struct_t *struct_new(int is_struct, string_t *tag) {
 	struct_t *ret = malloc(sizeof *ret);
 	if (!ret) {
-		printf("Failed to create a new struct\n");
+		log_memory("Error: failed to create a new struct\n");
 		return NULL;
 	}
 	ret->is_struct = is_struct;
@@ -785,6 +934,7 @@ struct_t *struct_new(int is_struct, string_t *tag) {
 	ret->is_defined = 0;
 	ret->is_simple = 0;
 	ret->nrefs = 1;
+	ret->has_self_recursion = 0; // Undefined structures cannot have self recursion
 	return ret;
 }
 
@@ -831,7 +981,7 @@ int type_t_equal_aux(type_t *typ1, type_t *typ2, int is_strict) {
 		if (!typ1->val.st->tag != !typ2->val.st->tag) return 0;
 		if (typ1->val.st->tag) return !strcmp(string_content(typ1->val.st->tag), string_content(typ2->val.st->tag));
 		if (!typ1->val.st->is_defined || !typ2->val.st->is_defined) {
-			printf("Warning: incomplete anonymous structure/union\n");
+			log_warning_nopos("comparing against incomplete anonymous structure/union\n");
 			return 0;
 		}
 		if (typ1->val.st->nmembers != typ2->val.st->nmembers) return 0;
@@ -872,7 +1022,7 @@ type_t *type_try_merge(type_t *typ, khash_t(type_set) *set) {
 	int iret;
 	khiter_t it = kh_put(type_set, set, typ, &iret);
 	if (iret < 0) {
-		printf("Error: failed to add type to type_set\n");
+		log_memory("Error: failed to add type to type_set\n");
 		return NULL;
 	} else if (iret == 0) {
 		if (typ == kh_key(set, it)) return typ;
@@ -1030,11 +1180,12 @@ void struct_print(const struct_t *st) {
 	}
 	if (st->is_defined) {
 		printf(
-			"%s %s <with %zu members%s> { ",
+			"%s %s <with %zu members%s%s> { ",
 			st->is_struct ? "struct" : "union",
 			st->tag ? string_content(st->tag) : "<no tag>",
 			st->nmembers,
-			st->has_incomplete ? ", with incomplete" : "");
+			st->has_incomplete ? ", with incomplete" : "",
+			st->has_self_recursion ? ", with self-recursion" : "");
 		for (size_t i = 0; i < st->nmembers; ++i) {
 			if (i) printf(", ");
 			type_print(st->members[i].typ);
@@ -1052,29 +1203,29 @@ void struct_print(const struct_t *st) {
 file_t *file_new(machine_t *target) {
 	file_t *ret = malloc(sizeof *ret);
 	if (!ret) {
-		printf("Failed to create a new translation unit structure (init)\n");
+		log_memory("failed to create a new translation unit structure (init)\n");
 		return NULL;
 	}
 	if (!(ret->struct_map = kh_init(struct_map))) {
-		printf("Failed to create a new translation unit structure (structure map)\n");
+		log_memory("failed to create a new translation unit structure (structure map)\n");
 		free(ret);
 		return NULL;
 	}
 	if (!(ret->type_map = kh_init(type_map))) {
-		printf("Failed to create a new translation unit structure (type map)\n");
+		log_memory("failed to create a new translation unit structure (type map)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		free(ret);
 		return NULL;
 	}
 	if (!(ret->enum_map = kh_init(type_map))) {
-		printf("Failed to create a new translation unit structure (enumeration map)\n");
+		log_memory("failed to create a new translation unit structure (enumeration map)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		kh_destroy(type_map, ret->type_map);
 		free(ret);
 		return NULL;
 	}
 	if (!(ret->decl_map = kh_init(decl_map))) {
-		printf("Failed to create a new translation unit structure (declaration map)\n");
+		log_memory("failed to create a new translation unit structure (declaration map)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		kh_destroy(type_map, ret->type_map);
 		kh_destroy(type_map, ret->enum_map);
@@ -1082,7 +1233,7 @@ file_t *file_new(machine_t *target) {
 		return NULL;
 	}
 	if (!(ret->type_set = kh_init(type_set))) {
-		printf("Failed to create a new translation unit structure (type set)\n");
+		log_memory("failed to create a new translation unit structure (type set)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		kh_destroy(type_map, ret->type_map);
 		kh_destroy(type_map, ret->enum_map);
@@ -1091,7 +1242,7 @@ file_t *file_new(machine_t *target) {
 		return NULL;
 	}
 	if (!(ret->const_map = kh_init(const_map))) {
-		printf("Failed to create a new translation unit structure (const map)\n");
+		log_memory("failed to create a new translation unit structure (const map)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		kh_destroy(type_map, ret->type_map);
 		kh_destroy(type_map, ret->enum_map);
@@ -1101,7 +1252,7 @@ file_t *file_new(machine_t *target) {
 		return NULL;
 	}
 	if (!(ret->relaxed_type_conversion = kh_init(conv_map))) {
-		printf("Failed to create a new translation unit structure (relaxed type conversion map)\n");
+		log_memory("failed to create a new translation unit structure (relaxed type conversion map)\n");
 		kh_destroy(struct_map, ret->struct_map);
 		kh_destroy(type_map, ret->type_map);
 		kh_destroy(type_map, ret->enum_map);
@@ -1117,7 +1268,7 @@ file_t *file_new(machine_t *target) {
 	for (enum type_builtin_e i = 0; i < LAST_BUILTIN + 1; ++i) {
 		type_t *t = type_new();
 		if (!t) {
-			printf("Failed to create a new translation unit structure (builtin type)\n");
+			log_memory("failed to create a new translation unit structure (builtin type)\n");
 			for (; i--;) {
 				free(ret->builtins[i]);
 			}
@@ -1135,11 +1286,14 @@ file_t *file_new(machine_t *target) {
 		t->nrefs = 2;
 		t->typ = TYPE_BUILTIN;
 		t->val.builtin = i;
-		validate_type(target, t);
+		if (!target->has_int128 && ((i == BTT_INT128) || (i == BTT_SINT128) || (i == BTT_UINT128))) {
+			t->converted = string_new_cstr("<invalid int128>");
+		}
+		validate_type(&(loginfo_t){0}, target, t);
 		ret->builtins[i] = t;
 		kh_put(type_set, ret->type_set, t, &iret);
 		if (iret < 0) {
-			printf("Failed to create a new translation unit structure (failed to add intrinsic type to type_set)\n");
+			log_memory("failed to create a new translation unit structure (failed to add intrinsic type to type_set)\n");
 			kh_destroy(struct_map, ret->struct_map);
 			kh_destroy(type_map, ret->type_map);
 			kh_destroy(conv_map, ret->relaxed_type_conversion);
@@ -1150,7 +1304,7 @@ file_t *file_new(machine_t *target) {
 			free(ret);
 			return NULL;
 		} else if (iret == 0) {
-			printf("Failed to create a new translation unit structure (duplicate intrinsic type in type_set)\n");
+			log_memory("failed to create a new translation unit structure (duplicate intrinsic type in type_set)\n");
 			for (++i; i--;) {
 				free(ret->builtins[i]);
 			}
@@ -1167,32 +1321,34 @@ file_t *file_new(machine_t *target) {
 	}
 	// ret is valid and can now be deleted by file_del
 	
-	// Add __builtin_va_list, __int128_t, __uint128_t as typedef
+	// Add __builtin_va_list, __int128_t, __uint128_t as builtin typedef
 	char *sdup;
 #define ADD_TYPEDEF(name, btt) \
-	sdup = strdup(#name);                                                                               \
-		if (!sdup) {                                                                                        \
-			printf("Failed to create a new translation unit structure (" #name " name)\n");                 \
-			file_del(ret);                                                                                  \
-			return NULL;                                                                                    \
-		}                                                                                                   \
-		it = kh_put(type_map, ret->type_map, sdup, &iret);                                                  \
-		if (iret < 0) {                                                                                     \
-			printf("Failed to create a new translation unit structure (add " #name " typedef)\n");          \
-			free(sdup);                                                                                     \
-			file_del(ret);                                                                                  \
-			return NULL;                                                                                    \
-		} else if (iret == 0) {                                                                             \
-			printf("Failed to create a new translation unit structure (" #name " is already a typedef)\n"); \
-			free(sdup);                                                                                     \
-			file_del(ret);                                                                                  \
-			return NULL;                                                                                    \
-		}                                                                                                   \
-		kh_val(ret->type_map, it) = ret->builtins[BTT_ ## btt];                                             \
+		sdup = strdup(#name);                                                                                   \
+		if (!sdup) {                                                                                            \
+			log_memory("failed to create a new translation unit structure (" #name " name)\n");                 \
+			file_del(ret);                                                                                      \
+			return NULL;                                                                                        \
+		}                                                                                                       \
+		it = kh_put(type_map, ret->type_map, sdup, &iret);                                                      \
+		if (iret < 0) {                                                                                         \
+			log_memory("failed to create a new translation unit structure (add " #name " typedef)\n");          \
+			free(sdup);                                                                                         \
+			file_del(ret);                                                                                      \
+			return NULL;                                                                                        \
+		} else if (iret == 0) {                                                                                 \
+			log_memory("failed to create a new translation unit structure (" #name " is already a typedef)\n"); \
+			free(sdup);                                                                                         \
+			file_del(ret);                                                                                      \
+			return NULL;                                                                                        \
+		}                                                                                                       \
+		kh_val(ret->type_map, it) = ret->builtins[BTT_ ## btt];                                                 \
 		++ret->builtins[BTT_ ## btt]->nrefs;
 	ADD_TYPEDEF(__builtin_va_list, VA_LIST)
-	ADD_TYPEDEF(__int128_t, INT128)
-	ADD_TYPEDEF(__uint128_t, UINT128)
+	if (target->has_int128) {
+		ADD_TYPEDEF(__int128_t, INT128)
+		ADD_TYPEDEF(__uint128_t, UINT128)
+	}
 	
 	return ret;
 }
diff --git a/wrapperhelper/src/lang.h b/wrapperhelper/src/lang.h
index e10b8542..e921013c 100644
--- a/wrapperhelper/src/lang.h
+++ b/wrapperhelper/src/lang.h
@@ -5,6 +5,7 @@
 
 #include "cstring.h"
 #include "khash.h"
+#include "log.h"
 #include "machine.h"
 #include "vector.h"
 
@@ -75,6 +76,7 @@ typedef struct preproc_token_s {
 		PPTOK_START_LINE_COMMENT,
 		PPTOK_EOF
 	} tokt;
+	loginfo_t loginfo;
 	union {
 		string_t *str;
 		struct {
@@ -150,6 +152,7 @@ typedef struct proc_token_s {
 		PTOK_PRAGMA,
 		PTOK_EOF
 	} tokt;
+	loginfo_t loginfo;
 	union proc_token_val_u {
 		string_t *str;
 		struct {
@@ -193,7 +196,7 @@ typedef struct num_constant_s {
 		uint64_t u64;
 	} val;
 } num_constant_t;
-int num_constant_convert(string_t *str, num_constant_t *cst, int ptr_is_32bits);
+int num_constant_convert(loginfo_t *li, string_t *str, num_constant_t *cst, int ptr_is_32bits);
 KHASH_MAP_DECLARE_STR(const_map, num_constant_t)
 
 typedef struct expr_s {
@@ -288,6 +291,7 @@ typedef struct expr_s {
 	} val;
 } expr_t;
 void expr_del(expr_t *e);
+void expr_print(expr_t *e);
 
 typedef struct size_info_s {
 	size_t size, align;
@@ -387,7 +391,8 @@ typedef struct st_member_s {
 	type_t *typ;
 	_Bool is_bitfield;
 	size_t bitfield_width;
-	// TODO: add byte_offset then check in generator against both archs for every named members
+	// Filled by validate_type
+	size_t byte_offset; unsigned char bit_offset;
 } st_member_t;
 typedef struct struct_s {
 	string_t *tag;
@@ -396,6 +401,7 @@ typedef struct struct_s {
 	int is_struct; // 0 = union, 1 = struct
 	int is_simple; // Pointers to the structure (in 64 bits) are simple pointers
 	int has_incomplete; // 1 if the last element of the structure is a VLA or if an element of the union recursively contains a VLA
+	int has_self_recursion; // 1 if the structure contains a reference to itself
 	size_t nmembers;
 	st_member_t *members;
 } struct_t;
diff --git a/wrapperhelper/src/log.c b/wrapperhelper/src/log.c
new file mode 100644
index 00000000..a50b52a1
--- /dev/null
+++ b/wrapperhelper/src/log.c
@@ -0,0 +1,80 @@
+#include "log.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void loginfo_print(const loginfo_t *info, int print_sz) {
+	if (!info) {
+		if (print_sz > 0) printf("%*s", print_sz, "");
+		return;
+	}
+	if (!info->filename) {
+		if (print_sz > 0) printf("%*s", print_sz, "");
+		return;
+	}
+	print_sz -= printf("%s:", info->filename);
+	if (!info->lineno) {
+		print_sz -= printf(" ");
+		if (print_sz > 0) printf("%*s", print_sz, "");
+		return;
+	}
+	print_sz -= printf("%zu:", info->lineno);
+	if (!info->colno) {
+		print_sz -= printf(" ");
+		if (print_sz > 0) printf("%*s", print_sz, "");
+		return;
+	}
+	print_sz -= printf("%zu", info->colno);
+	if (!info->lineno_end || ((info->lineno_end == info->lineno) && (info->colno_end == info->colno))) {
+		print_sz -= printf(": ");
+		if (print_sz > 0) printf("%*s", print_sz, "");
+		return;
+	}
+	print_sz -= printf("-");
+	if (info->lineno_end != info->lineno) {
+		print_sz -= printf("%zu:", info->lineno_end);
+	}
+	print_sz -= printf("%zu: ", info->colno_end + 1);
+	if (print_sz > 0) printf("%*s", print_sz, "");
+}
+
+void log_error(const loginfo_t *info, const char *format, ...) {
+	printf("Error: ");
+	loginfo_print(info, 0);
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+}
+void log_internal(const loginfo_t *info, const char *format, ...) {
+	printf("Internal error: ");
+	loginfo_print(info, 0);
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+}
+void log_memory(const char *format, ...) {
+	printf("Fatal error: memory error: ");
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+}
+void log_TODO(const loginfo_t *info, const char *format, ...) {
+	printf("Error: ");
+	loginfo_print(info, 0);
+	printf("TODO: ");
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+}
+void log_warning(const loginfo_t *info, const char *format, ...) {
+	printf("Warning: ");
+	loginfo_print(info, 0);
+	va_list args;
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+}
diff --git a/wrapperhelper/src/log.h b/wrapperhelper/src/log.h
new file mode 100644
index 00000000..e1f210b6
--- /dev/null
+++ b/wrapperhelper/src/log.h
@@ -0,0 +1,27 @@
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <stddef.h>
+
+typedef struct loginfo_s {
+	const char *filename; // NULL = no log info
+	size_t lineno; // 0 = no (start) line/column number
+	size_t colno;
+	size_t lineno_end; // 0 = no end line/column number
+	size_t colno_end;
+} loginfo_t;
+
+#define ATTRIBUTE_FORMAT(i, j) __attribute__((format(printf, i, j)))
+
+void loginfo_print(const loginfo_t *info, int print_sz); // print_sz = min characters output
+void log_error(const loginfo_t *info, const char *format, ...) ATTRIBUTE_FORMAT(2, 3);
+void log_internal(const loginfo_t *info, const char *format, ...) ATTRIBUTE_FORMAT(2, 3);
+void log_memory(const char *format, ...) ATTRIBUTE_FORMAT(1, 2);
+void log_TODO(const loginfo_t *info, const char *format, ...) ATTRIBUTE_FORMAT(2, 3);
+void log_warning(const loginfo_t *info, const char *format, ...) ATTRIBUTE_FORMAT(2, 3);
+#define log_error_nopos(...) log_error(&(loginfo_t){0}, __VA_ARGS__)
+#define log_internal_nopos(...) log_internal(&(loginfo_t){0}, __VA_ARGS__)
+#define log_TODO_nopos(...) log_TODO(&(loginfo_t){0}, __VA_ARGS__)
+#define log_warning_nopos(...) log_warning(&(loginfo_t){0}, __VA_ARGS__)
+
+#endif // __LOG_H__
diff --git a/wrapperhelper/src/machine.c b/wrapperhelper/src/machine.c
index b89a64ae..499bcb2f 100644
--- a/wrapperhelper/src/machine.c
+++ b/wrapperhelper/src/machine.c
@@ -5,7 +5,7 @@
 #include "lang.h"
 
 machine_t machine_x86_64;
-// machine_t machine_x86;
+machine_t machine_x86;
 machine_t machine_aarch64;
 
 #define PASTE2(a, b) a ## b
@@ -17,23 +17,23 @@ machine_t machine_aarch64;
 
 #define PATHS_OFFSET_PRE 2 // There are two paths that are always included before any other
 #define ADD_PATH(path) \
-	if (!(MACHINE_VAL.include_path[failure_id] = strdup(path))) {           \
-		printf("Failed to add include path to " MACHINE_STR " platform\n"); \
-		goto PASTE(failed_, PASTE(CUR_MACHINE, _paths));                    \
-	}                                                                       \
+	if (!(MACHINE_VAL.include_path[failure_id] = strdup(path))) {               \
+		log_memory("failed to add include path to " MACHINE_STR " platform\n"); \
+		goto PASTE(failed_, PASTE(CUR_MACHINE, _paths));                        \
+	}                                                                           \
 	++failure_id;
 #define INIT_PATHS \
-	MACHINE_VAL.npaths = PATHS_OFFSET_PRE + npaths + paths_offset_post;     \
-	if (!(MACHINE_VAL.include_path =                                        \
-	      malloc(MACHINE_VAL.npaths * sizeof *MACHINE_VAL.include_path))) { \
-		printf("Failed to add include path to " MACHINE_STR " platform\n"); \
-		goto PASTE(failed_, PASTE(CUR_MACHINE, _nopath));                   \
-	}                                                                       \
-	failure_id = 0;                                                         \
-	ADD_PATH("include-override/" MACHINE_STR)                               \
-	ADD_PATH("include-override/common")                                     \
-	while (failure_id < PATHS_OFFSET_PRE + npaths) {                        \
-		ADD_PATH(extra_include_path[failure_id - PATHS_OFFSET_PRE])         \
+	MACHINE_VAL.npaths = PATHS_OFFSET_PRE + npaths + paths_offset_post;         \
+	if (!(MACHINE_VAL.include_path =                                            \
+	      malloc(MACHINE_VAL.npaths * sizeof *MACHINE_VAL.include_path))) {     \
+		log_memory("failed to add include path to " MACHINE_STR " platform\n"); \
+		goto PASTE(failed_, PASTE(CUR_MACHINE, _nopath));                       \
+	}                                                                           \
+	failure_id = 0;                                                             \
+	ADD_PATH("include-override/" MACHINE_STR)                                   \
+	ADD_PATH("include-override/common")                                         \
+	while (failure_id < PATHS_OFFSET_PRE + npaths) {                            \
+		ADD_PATH(extra_include_path[failure_id - PATHS_OFFSET_PRE])             \
 	}
 
 int init_machines(size_t npaths, const char *const *extra_include_path) {
@@ -48,24 +48,45 @@ int init_machines(size_t npaths, const char *const *extra_include_path) {
 #pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
 #define CUR_MACHINE x86_64
 	machine_x86_64.size_long = 8;
+	machine_x86_64.align_longdouble = 16;
+	machine_x86_64.size_longdouble = 16;
 	machine_x86_64.align_valist = 8;
 	machine_x86_64.size_valist = 24;
-	machine_x86_64.unsigned_char = 1;
+	machine_x86_64.max_align = 8;
+	machine_x86_64.has_int128 = 1;
+	machine_x86_64.unsigned_char = 0;
 	machine_x86_64.unnamed_bitfield_aligns = 0;
 	INIT_PATHS
 #define DO_PATH ADD_PATH
 #include "machine.gen"
 #undef DO_PATH
 #undef CUR_MACHINE
-#pragma GCC diagnostic pop
 	
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
+#define CUR_MACHINE x86
+	machine_x86.size_long = 4;
+	machine_x86.align_longdouble = 4;
+	machine_x86.size_longdouble = 12;
+	machine_x86.align_valist = 4;
+	machine_x86.size_valist = 4;
+	machine_x86.max_align = 4;
+	machine_x86.has_int128 = 0;
+	machine_x86.unsigned_char = 0;
+	machine_x86.unnamed_bitfield_aligns = 0;
+	INIT_PATHS
+#define DO_PATH ADD_PATH
+#include "machine.gen"
+#undef DO_PATH
+#undef CUR_MACHINE
+	
 #define CUR_MACHINE aarch64
 	machine_aarch64.size_long = 8;
+	machine_aarch64.align_longdouble = 16;
+	machine_aarch64.size_longdouble = 16;
 	machine_aarch64.align_valist = 8;
 	machine_aarch64.size_valist = 32;
-	machine_aarch64.unsigned_char = 0;
+	machine_aarch64.max_align = 8;
+	machine_aarch64.has_int128 = 1;
+	machine_aarch64.unsigned_char = 1;
 	machine_aarch64.unnamed_bitfield_aligns = 1;
 	INIT_PATHS
 #define DO_PATH ADD_PATH
@@ -82,6 +103,13 @@ failed_aarch64_paths:
 	}
 	free(machine_aarch64.include_path);
 failed_aarch64_nopath:
+	failure_id = machine_x86.npaths;
+failed_x86_paths:
+	while (failure_id--) {
+		free(machine_x86.include_path[failure_id]);
+	}
+	free(machine_x86.include_path);
+failed_x86_nopath:
 	failure_id = machine_x86_64.npaths;
 failed_x86_64_paths:
 	while (failure_id--) {
@@ -100,100 +128,143 @@ static void machine_del(machine_t *m) {
 }
 void del_machines(void) {
 	machine_del(&machine_x86_64);
+	machine_del(&machine_x86);
 	machine_del(&machine_aarch64);
 }
 
 machine_t *convert_machine_name(const char *archname) {
 	if (!strcmp(archname, "x86_64"))
 		return &machine_x86_64;
+	if (!strcmp(archname, "x86"))
+		return &machine_x86;
 	if (!strcmp(archname, "aarch64"))
 		return &machine_aarch64;
 	return NULL;
 }
 
-int validate_type(machine_t *target, type_t *typ) {
+void fill_self_recursion(type_t *typ, struct_t *st) {
+	if (typ->_internal_use) return; // Recursion, but not self recursion
+	typ->_internal_use = 1;
+	switch (typ->typ) {
+	case TYPE_BUILTIN: break;
+	case TYPE_ARRAY:
+		fill_self_recursion(typ->val.array.typ, st);
+		break;
+	case TYPE_PTR:
+		fill_self_recursion(typ->val.typ, st);
+		break;
+	case TYPE_FUNCTION:
+		if (typ->val.fun.nargs != (size_t)-1) {
+			for (size_t i = 0; (i < typ->val.fun.nargs) && !st->has_self_recursion; ++i) {
+				fill_self_recursion(typ->val.fun.args[i], st);
+			}
+		}
+		if (!st->has_self_recursion) fill_self_recursion(typ->val.fun.ret, st);
+		break;
+	case TYPE_STRUCT_UNION:
+		if (typ->val.st == st) {
+			st->has_self_recursion = 1;
+			break;
+		}
+		if (!typ->val.st->is_defined) break;
+		for (size_t i = 0; (i < typ->val.st->nmembers) && !st->has_self_recursion; ++i) {
+			fill_self_recursion(typ->val.st->members[i].typ, st);
+		}
+		break;
+	case TYPE_ENUM:
+		fill_self_recursion(typ->val.typ, st);
+		break;
+	}
+	typ->_internal_use = 0;
+}
+
+int validate_type(loginfo_t *loginfo, machine_t *target, type_t *typ) {
 	if (typ->is_validated) return 1;
 	typ->is_validated = 1;
 	if (typ->is_restrict) {
 		if (typ->typ != TYPE_PTR) {
-			printf("Error: only pointers to object types may be restrict-qualified\n");
+			log_error(loginfo, "only pointers to object types may be restrict-qualified\n");
 			return 0;
 		}
 		if (typ->val.typ->typ == TYPE_FUNCTION) {
-			printf("Error: only pointers to object types may be restrict-qualified\n");
+			log_error(loginfo, "only pointers to object types may be restrict-qualified\n");
 			return 0;
 		}
 	}
 	if (typ->is_atomic) {
 		if ((typ->typ == TYPE_ARRAY) || (typ->typ == TYPE_FUNCTION)) {
-			printf("Error: array types and function types may not be atomic-qualified\n");
+			log_error(loginfo, "array types and function types may not be atomic-qualified\n");
 			return 0;
 		}
 	}
 	switch (typ->typ) {
 	case TYPE_BUILTIN:
 		switch (typ->val.builtin) {
-		case BTT_VOID:        typ->szinfo.align = typ->szinfo.size = 0; break;
-		case BTT_BOOL:        typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_CHAR:        typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_SCHAR:       typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_UCHAR:       typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_SHORT:       typ->szinfo.align = typ->szinfo.size = 2; break;
-		case BTT_SSHORT:      typ->szinfo.align = typ->szinfo.size = 2; break;
-		case BTT_USHORT:      typ->szinfo.align = typ->szinfo.size = 2; break;
-		case BTT_INT:         typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_SINT:        typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_UINT:        typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_LONG:        typ->szinfo.align = typ->szinfo.size = target->size_long; break;
-		case BTT_SLONG:       typ->szinfo.align = typ->szinfo.size = target->size_long; break;
-		case BTT_ULONG:       typ->szinfo.align = typ->szinfo.size = target->size_long; break;
-		case BTT_LONGLONG:    typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_SLONGLONG:   typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_ULONGLONG:   typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_INT128:      typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_SINT128:     typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_UINT128:     typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_S8:          typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_U8:          typ->szinfo.align = typ->szinfo.size = 1; break;
-		case BTT_S16:         typ->szinfo.align = typ->szinfo.size = 2; break;
-		case BTT_U16:         typ->szinfo.align = typ->szinfo.size = 2; break;
-		case BTT_S32:         typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_U32:         typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_S64:         typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_U64:         typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_FLOAT:       typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_CFLOAT:      typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_IFLOAT:      typ->szinfo.align = typ->szinfo.size = 4; break;
-		case BTT_DOUBLE:      typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_CDOUBLE:     typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_IDOUBLE:     typ->szinfo.align = typ->szinfo.size = 8; break;
-		case BTT_LONGDOUBLE:  typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_CLONGDOUBLE: typ->szinfo.align = 16; typ->szinfo.size = 32; break;
-		case BTT_ILONGDOUBLE: typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_FLOAT128:    typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_CFLOAT128:   typ->szinfo.align = 16; typ->szinfo.size = 32; break;
-		case BTT_IFLOAT128:   typ->szinfo.align = typ->szinfo.size = 16; break;
-		case BTT_VA_LIST:     typ->szinfo.align = target->align_valist; typ->szinfo.size = target->size_valist; break;
+		case BTT_VOID:        typ->szinfo.align = typ->szinfo.size = 0; return 1;
+		case BTT_BOOL:
+		case BTT_CHAR:
+		case BTT_SCHAR:
+		case BTT_UCHAR:
+		case BTT_S8:
+		case BTT_U8:          typ->szinfo.align = typ->szinfo.size = 1; return 1;
+		case BTT_SHORT:
+		case BTT_SSHORT:
+		case BTT_USHORT:
+		case BTT_S16:
+		case BTT_U16:         typ->szinfo.align = typ->szinfo.size = 2; return 1;
+		case BTT_INT:
+		case BTT_SINT:
+		case BTT_UINT:
+		case BTT_S32:
+		case BTT_U32:         typ->szinfo.align = typ->szinfo.size = 4; return 1;
+		case BTT_LONGLONG:
+		case BTT_SLONGLONG:
+		case BTT_ULONGLONG:
+		case BTT_S64:
+		case BTT_U64:         typ->szinfo.align = target->max_align; typ->szinfo.size = 8; return 1;
+		case BTT_LONG:
+		case BTT_SLONG:
+		case BTT_ULONG:       typ->szinfo.align = typ->szinfo.size = target->size_long; return 1;
+		case BTT_FLOAT:
+		case BTT_IFLOAT:      typ->szinfo.align = typ->szinfo.size = 4; return 1;
+		case BTT_CFLOAT:
+		case BTT_DOUBLE:
+		case BTT_IDOUBLE:     typ->szinfo.align = target->max_align; typ->szinfo.size = 8; return 1;
+		case BTT_CDOUBLE:     typ->szinfo.align = target->max_align; typ->szinfo.size = 16; return 1;
+		case BTT_LONGDOUBLE:
+		case BTT_ILONGDOUBLE: typ->szinfo.align = target->align_longdouble; typ->szinfo.size = target->size_longdouble; return 1;
+		case BTT_CLONGDOUBLE: typ->szinfo.align = target->align_longdouble; typ->szinfo.size = 2*target->size_longdouble; return 1;
+		case BTT_INT128:
+		case BTT_SINT128:
+		case BTT_UINT128:
+			if (!target->has_int128) {
+				if (loginfo->filename) log_error(loginfo, "target does not have type __int128\n");
+				typ->szinfo.align = typ->szinfo.size = 0; return 0;
+			}
+			/* FALLTHROUGH */
+		case BTT_FLOAT128:
+		case BTT_IFLOAT128:   typ->szinfo.align = typ->szinfo.size = 16; return 1;
+		case BTT_CFLOAT128:   typ->szinfo.align = 16; typ->szinfo.size = 32; return 1;
+		case BTT_VA_LIST:     typ->szinfo.align = target->align_valist; typ->szinfo.size = target->size_valist; return 1;
 		default:
-			printf("Unknown builtin %u, cannot fill size info\n", typ->val.builtin);
+			log_error(loginfo, "unknown builtin %u, cannot fill size info\n", typ->val.builtin);
 			return 0;
 		}
-		return 1;
 	case TYPE_ARRAY:
 		if (typ->val.array.typ->is_incomplete || (typ->val.array.typ->typ == TYPE_FUNCTION)) {
-			printf("Error: array types must point to complete object types\n");
+			log_error(loginfo, "array types must point to complete object types\n");
 			return 0;
 		}
 		if ((typ->val.array.typ->typ == TYPE_STRUCT_UNION) && typ->val.array.typ->val.st->has_incomplete) {
-			printf("Error: array types may not (inductively) point to structures which last element is incomplete\n");
+			log_error(loginfo, "array types may not (inductively) point to structures which last element is incomplete\n");
 			return 0;
 		}
 		if ((typ->is_atomic) || (typ->is_const) || (typ->is_restrict) || (typ->is_volatile)) {
 			// qualifier-type-list in array declaration is only allowed in function argument declaration under certain circumstances
-			printf("Error: array types may not be qualified\n");
+			log_error(loginfo, "array types may not be qualified\n");
 			return 0;
 		}
-		if (!validate_type(target, typ->val.array.typ)) return 0;
+		if (!validate_type(loginfo, target, typ->val.array.typ)) return 0;
 		if (typ->val.array.array_sz == (size_t)-1) {
 			typ->szinfo.size = 0;
 			typ->szinfo.align = (typ->val.array.typ->szinfo.align < 16) ? 16 : typ->val.array.typ->szinfo.align;
@@ -208,10 +279,10 @@ int validate_type(machine_t *target, type_t *typ) {
 	case TYPE_PTR:
 		typ->szinfo.size = target->size_long;
 		typ->szinfo.align = target->size_long;
-		return validate_type(target, typ->val.typ);
+		return validate_type(loginfo, target, typ->val.typ);
 	case TYPE_FUNCTION:
 		if ((typ->val.fun.ret->typ == TYPE_FUNCTION) || (typ->val.fun.ret->typ == TYPE_ARRAY)) {
-			printf("Error: function types may not return function or array types\n");
+			log_error(loginfo, "function types may not return function or array types\n");
 			return 0;
 		}
 		if (typ->val.fun.nargs != (size_t)-1) {
@@ -219,57 +290,72 @@ int validate_type(machine_t *target, type_t *typ) {
 				// Adjust the argument if necessary
 				// Assume arrays are already converted
 				if (typ->val.fun.args[i]->typ == TYPE_ARRAY) {
-					printf("Error: function argument %zu is an array\n", i + 1);
+					log_error(loginfo, "function argument %zu is an array\n", i + 1);
 					return 0;
 				}
 				if (typ->val.fun.args[i]->typ == TYPE_FUNCTION) {
 					// Adjustment to pointer
 					type_t *t2 = type_new_ptr(typ->val.fun.args[i]);
 					if (!t2) {
-						printf("Error: failed to adjust type of argument from function to pointer\n");
+						log_error(loginfo, "failed to adjust type of argument from function to pointer\n");
 						return 0;
 					}
 					typ->val.fun.args[i] = t2;
 				}
-				if (!validate_type(target, typ->val.fun.args[i])) return 0;
+				if (!validate_type(loginfo, target, typ->val.fun.args[i])) return 0;
 			}
 		}
 		typ->szinfo.size = 0;
 		typ->szinfo.align = 0;
-		return validate_type(target, typ->val.fun.ret);
+		return validate_type(loginfo, target, typ->val.fun.ret);
 	case TYPE_STRUCT_UNION: {
 		if (!typ->val.st->is_defined) return typ->is_incomplete;
-		size_t max_align = 1, cur_sz = 0, cur_bit = 0;
+		size_t max_align = 1, cur_sz = 0; unsigned char cur_bit = 0;
 		for (size_t i = 0; i < typ->val.st->nmembers; ++i) {
 			// Adjust the argument if necessary
 			st_member_t *mem = &typ->val.st->members[i];
 			if (mem->typ->typ == TYPE_FUNCTION) {
-				printf("Error: structures may not contain function members\n");
+				log_error(loginfo, "structures may not contain function members\n");
 				return 0;
 			}
 			if (mem->typ->is_incomplete) {
 				if ((i != typ->val.st->nmembers - 1) || !typ->val.st->is_struct || (mem->typ->typ != TYPE_ARRAY)) {
 					// The last element of a structure may be a VLA
-					printf("Error: structures may not contain incomplete members\n");
+					log_error(loginfo, "structures may not contain incomplete members\n");
 					return 0;
 				}
 				typ->val.st->has_incomplete = 1;
 			}
-			if (!validate_type(target, mem->typ)) return 0;
+			if (!validate_type(loginfo, target, mem->typ)) return 0;
+			if (!typ->val.st->has_self_recursion) fill_self_recursion(mem->typ, typ->val.st);
 			if (!typ->val.st->is_struct && (mem->typ->typ == TYPE_STRUCT_UNION)) {
 				typ->val.st->has_incomplete |= mem->typ->val.st->has_incomplete;
+			} else if ((mem->typ->typ == TYPE_STRUCT_UNION) && mem->typ->val.st->has_incomplete) {
+				log_error(loginfo, "structures may not (inductively) contain structures which last element is incomplete\n");
+				return 0;
+			} else if (typ->val.st->is_struct) {
+				if (mem->typ->is_incomplete) {
+					if (i && (i == typ->val.st->nmembers - 1) && (mem->typ->typ == TYPE_ARRAY)) {
+						typ->val.st->has_incomplete |= mem->typ->val.st->has_incomplete;
+					} else {
+						log_error(loginfo, "structures may not have any incomplete element, except that the last, but not first, element may be an incomplete array\n");
+						return 0;
+					}
+				}
 			}
+			mem->byte_offset = cur_sz;
+			mem->bit_offset = cur_bit;
 			if (mem->is_bitfield) {
 				if (!typ->val.st->is_struct) {
-					printf("Error: TODO: bitfield in union\n");
+					log_error(loginfo, "TODO: bitfield in union\n");
 					return 0;
 				}
 				if (mem->typ->is_atomic) {
-					printf("Error: atomic bitfields are not supported\n");
+					log_error(loginfo, "atomic bitfields are not supported\n");
 					return 0;
 				}
 				if (mem->typ->typ != TYPE_BUILTIN) {
-					printf("Error: bitfields can only have a specific subset of types\n");
+					log_error(loginfo, "bitfields can only have a specific subset of types\n");
 					return 0;
 				}
 				if ((mem->typ->val.builtin != BTT_BOOL) && (mem->typ->val.builtin != BTT_CHAR)
@@ -285,11 +371,11 @@ int validate_type(machine_t *target, type_t *typ) {
 				 && (mem->typ->val.builtin != BTT_S64) && (mem->typ->val.builtin != BTT_U64)) {
 					// C standard: allow _Bool, (s/u)int
 					// Implementation: also allow (u/s)char, (u/s)short, (u/s)long, (u/s)long long, [u]intx_t
-					printf("Error: bitfields can only have a specific subset of types\n");
+					log_error(loginfo, "bitfields can only have a specific subset of types\n");
 					return 0;
 				}
 				if (mem->typ->szinfo.size < mem->bitfield_width / 8) {
-					printf("Error: bitfield member %c%s%c has width (%zu) greater than its container size (%zu * 8)\n",
+					log_error(loginfo, "bitfield member %c%s%c has width (%zu) greater than its container size (%zu * 8)\n",
 						mem->name ? '\'' : '<',
 						mem->name ? string_content(mem->name) : "unnamed",
 						mem->name ? '\'' : '>',
@@ -302,7 +388,8 @@ int validate_type(machine_t *target, type_t *typ) {
 					size_t cur_block = cur_sz / mem->typ->szinfo.align;
 					size_t end_block = (cur_sz + (cur_bit + mem->bitfield_width - 1) / 8) / mem->typ->szinfo.align;
 					if (cur_block == end_block) {
-						cur_bit += mem->bitfield_width;
+						cur_sz += mem->bitfield_width / 8;
+						cur_bit += mem->bitfield_width % 8;
 						cur_sz += cur_bit / 8;
 						cur_bit %= 8;
 					} else {
@@ -312,7 +399,7 @@ int validate_type(machine_t *target, type_t *typ) {
 				} else {
 					if (max_align < mem->typ->szinfo.align) max_align = mem->typ->szinfo.align;
 					cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + mem->typ->szinfo.size;
-					printf("Error: TODO: unnamed zero-width bitfield member\n");
+					log_error(loginfo, "TODO: unnamed zero-width bitfield member\n");
 					return 0;
 				}
 			} else {
@@ -337,7 +424,7 @@ int validate_type(machine_t *target, type_t *typ) {
 		return 1; }
 	case TYPE_ENUM:
 		if (typ->val.typ->typ != TYPE_BUILTIN) {
-			printf("Error: the underlying type of an enum is not a builtin type\n");
+			log_error(loginfo, "the underlying type of an enum is not a builtin type\n");
 			return 0;
 		}
 		typ->szinfo = typ->val.typ->szinfo;
diff --git a/wrapperhelper/src/machine.h b/wrapperhelper/src/machine.h
index 566f1d43..67867c70 100644
--- a/wrapperhelper/src/machine.h
+++ b/wrapperhelper/src/machine.h
@@ -5,6 +5,7 @@
 
 #include "cstring.h"
 #include "khash.h"
+#include "log.h"
 #include "vector.h"
 
 struct type_s;  // lang.h
@@ -16,7 +17,9 @@ typedef struct machine_s {
 	
 	// Parsing
 	size_t size_long;
+	size_t align_longdouble, size_longdouble;
 	size_t align_valist, size_valist;
+	size_t max_align; _Bool has_int128;
 	_Bool unsigned_char;
 	// Structure parsing
 	_Bool unnamed_bitfield_aligns;
@@ -26,6 +29,6 @@ int init_machines(size_t npaths, const char *const *extra_include_path);
 void del_machines(void);
 machine_t *convert_machine_name(const char *archname);
 
-int validate_type(machine_t *target, struct type_s *typ);
+int validate_type(loginfo_t *loginfo, machine_t *target, struct type_s *typ);
 
 #endif // MACHINE_H
diff --git a/wrapperhelper/src/main.c b/wrapperhelper/src/main.c
index efa410ab..14a67ce6 100644
--- a/wrapperhelper/src/main.c
+++ b/wrapperhelper/src/main.c
@@ -12,8 +12,8 @@
 
 static void help(char *arg0) {
 	printf("Usage: %s --help\n"
-	       "       %s {-I/path/to/include}* [--prepare|--preproc|--proc] [--arch <arch>] <filename_in>\n"
-	       "       %s {-I/path/to/include}* [--emu <arch>] [--target <arch>] <filename_in> <filename_reqs> <filename_out>\n"
+	       "       %s {-I/path/to/include}* [--prepare|--preproc|--proc] [--arch <arch>|-32|-64|--32|--64] <filename_in>\n"
+	       "       %s {-I/path/to/include}* [[--emu <arch>] [--target <arch>]|-32|-64|--32|--64] <filename_in> <filename_reqs> <filename_out>\n"
 	       "\n"
 	       "  --prepare  Dump all preprocessor tokens (prepare phase)\n"
 	       "  --preproc  Dump all processor tokens (preprocessor phase)\n"
@@ -29,8 +29,10 @@ static void help(char *arg0) {
 	       "  --arch <arch>    Use the architecture <arch>\n"
 	       "  --emu <arch>     Use the architecture <arch> as emulated architecture\n"
 	       "  --target <arch>  Use the architecture <arch> as target/running architecture\n"
+	       "  -32  --32        Use the x86 architecture as arch/emulated, aarch64 as target\n"
+	       "  -64  --64        Use the x86_64 architecture as arch/emulated, aarch64 as target\n"
 	       "\n"
-	       "  <arch> is one of 'x86_64', 'aarch64'\n",
+	       "  <arch> is one of 'x86', 'x86_64', 'aarch64'\n",
 	       arg0, arg0, arg0);
 }
 
@@ -41,14 +43,21 @@ enum main_state {
 	MAIN_PROC,
 };
 
+enum bits_state {
+	BITS_NONE,
+	BITS_32,
+	BITS_64,
+};
+
 int main(int argc, char **argv) {
 	setbuf(stdout, NULL);
 	if (!setlocale(LC_NUMERIC, "C")) {
-		printf("Error: failed to set LC_NUMERIC to C\n");
+		log_error_nopos("failed to set LC_NUMERIC to C\n");
 		return 2;
 	}
 	
 	enum main_state ms = MAIN_RUN;
+	enum bits_state bs = BITS_NONE;
 	const char *in_file = NULL, *ref_file = NULL, *out_file = NULL;
 	VECTOR(charp) *paths = vector_new(charp);
 	const char *archname = NULL, *emuname = NULL, *targetname = NULL;
@@ -69,13 +78,13 @@ int main(int argc, char **argv) {
 			// Ignore
 		} else if (!strcmp(argv[i], "-I") && (i + 1 < argc)) {
 			if (!vector_push(charp, paths, argv[i + 1])) {
-				printf("Error: failed to add path to buffer\n");
+				log_memory("failed to add path to buffer\n");
 				return 2;
 			}
 			++i;
 		} else if ((argv[i][0] == '-') && (argv[i][1] == 'I') && (argv[i][2] != '\0')) {
 			if (!vector_push(charp, paths, argv[i] + 2)) {
-				printf("Error: failed to add path to buffer\n");
+				log_memory("failed to add path to buffer\n");
 				return 2;
 			}
 		} else if (!strcmp(argv[i], "--arch")) {
@@ -83,7 +92,7 @@ int main(int argc, char **argv) {
 			if (i < argc) {
 				archname = argv[i];
 			} else {
-				printf("Error: invalid '--arch' option in last position\n");
+				log_error_nopos("invalid '--arch' option in last position\n");
 				help(argv[0]);
 				return 0;
 			}
@@ -92,7 +101,7 @@ int main(int argc, char **argv) {
 			if (i < argc) {
 				emuname = argv[i];
 			} else {
-				printf("Error: invalid '--emu' option in last position\n");
+				log_error_nopos("invalid '--emu' option in last position\n");
 				help(argv[0]);
 				return 0;
 			}
@@ -101,16 +110,20 @@ int main(int argc, char **argv) {
 			if (i < argc) {
 				targetname = argv[i];
 			} else {
-				printf("Error: invalid '--target' option in last position\n");
+				log_error_nopos("invalid '--target' option in last position\n");
 				help(argv[0]);
 				return 0;
 			}
+		} else if (!strcmp(argv[i], "-32") || !strcmp(argv[i], "--32")) {
+			bs = BITS_32;
+		} else if (!strcmp(argv[i], "-64") || !strcmp(argv[i], "--64")) {
+			bs = BITS_64;
 		} else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--filename")) {
 			++i;
 			if (i < argc) {
 				isfile = 1;
 			} else {
-				printf("Error: invalid '--filename' option in last position\n");
+				log_error_nopos("invalid '--filename' option in last position\n");
 				help(argv[0]);
 				return 0;
 			}
@@ -125,7 +138,7 @@ int main(int argc, char **argv) {
 			} else if (!out_file) {
 				out_file = argv[i];
 			} else {
-				printf("Error: too many unknown options considered as file\n");
+				log_error_nopos("too many unknown options considered as file\n");
 				help(argv[0]);
 				return 2;
 			}
@@ -137,15 +150,23 @@ int main(int argc, char **argv) {
 	case MAIN_PROC:
 	check_proc:
 		if (!in_file || ref_file || out_file) {
-			printf("Error: too many unknown options/not enough arguments\n");
+			log_error_nopos("too many unknown options/not enough arguments\n");
 			help(argv[0]);
 			return 2;
 		}
 		if (emuname || targetname) {
-			printf("Error: invalid option '--emu' or '--target' in prepare/preprocessor/processor mode\n");
+			log_error_nopos("invalid option '--emu' or '--target' in prepare/preprocessor/processor mode\n");
 			help(argv[0]);
 			return 2;
 		}
+		if (bs != BITS_NONE) {
+			if (archname) {
+				log_error_nopos("invalid option '--arch' with '--32' or '--64' in prepare/preprocessor/processor mode\n");
+				help(argv[0]);
+				return 2;
+			}
+			archname = (bs == BITS_32) ? "x86" : "x86_64";
+		}
 		if (!archname) archname = "x86_64";
 		break;
 	case MAIN_RUN:
@@ -154,15 +175,24 @@ int main(int argc, char **argv) {
 			goto check_proc;
 		}
 		if (!in_file || !ref_file || !out_file) {
-			printf("Error: too many unknown options/not enough arguments\n");
+			log_error_nopos("too many unknown options/not enough arguments\n");
 			help(argv[0]);
 			return 2;
 		}
 		if (archname) {
-			printf("Error: invalid option '--arch' in run mode\n");
+			log_error_nopos("invalid option '--arch' in run mode\n");
 			help(argv[0]);
 			return 2;
 		}
+		if (bs != BITS_NONE) {
+			if (emuname || targetname) {
+				log_error_nopos("invalid option '--emu' or '--target' with '--32' or '--64' in run mode\n");
+				help(argv[0]);
+				return 2;
+			}
+			emuname = (bs == BITS_32) ? "x86" : "x86_64";
+			targetname = "aarch64";
+		}
 		if (!emuname) emuname = "x86_64";
 		if (!targetname) targetname = "aarch64";
 		break;
@@ -173,6 +203,7 @@ int main(int argc, char **argv) {
 	}
 	if (!init_machines(vector_size(charp, paths), (const char*const*)vector_content(charp, paths))) {
 		vector_del(charp, paths);
+		del_str2kw();
 		return 2;
 	}
 	vector_del(charp, paths);
@@ -180,22 +211,30 @@ int main(int argc, char **argv) {
 	FILE *f = fopen(in_file, "r");
 	if (!f) {
 		err(2, "Error: failed to open %s", in_file);
+		del_machines();
+		del_str2kw();
 		return 2;
 	}
 	switch (ms) {
 	case MAIN_RUN: {
 		machine_t *emu = convert_machine_name(emuname);
 		if (!emu) {
-			printf("Error: invalid emulation architecture '%s'\n", emuname);
+			log_error_nopos("invalid emulation architecture '%s'\n", emuname);
+			del_machines();
+			del_str2kw();
+			return 0;
 		}
 		machine_t *target = convert_machine_name(targetname);
 		if (!target) {
-			printf("Error: invalid target architecture '%s'\n", targetname);
+			log_error_nopos("invalid target architecture '%s'\n", targetname);
+			del_machines();
+			del_str2kw();
+			return 0;
 		}
 		
 		file_t *emu_content = parse_file(emu, in_file, f); // Takes ownership of f
 		if (!emu_content) {
-			printf("Error: failed to parse the file\n");
+			log_error_nopos("failed to parse the file\n");
 			del_machines();
 			del_str2kw();
 			return 0;
@@ -204,11 +243,14 @@ int main(int argc, char **argv) {
 		f = fopen(in_file, "r");
 		if (!f) {
 			err(2, "Error: failed to re-open %s", in_file);
+			file_del(emu_content);
+			del_machines();
+			del_str2kw();
 			return 2;
 		}
 		file_t *target_content = parse_file(target, in_file, f); // Takes ownership of f
 		if (!target_content) {
-			printf("Error: failed to parse the file\n");
+			log_error_nopos("failed to parse the file\n");
 			file_del(emu_content);
 			del_machines();
 			del_str2kw();
@@ -218,6 +260,8 @@ int main(int argc, char **argv) {
 		FILE *ref = fopen(ref_file, "r");
 		if (!ref) {
 			err(2, "Error: failed to open %s", ref_file);
+			file_del(emu_content);
+			file_del(target_content);
 			del_machines();
 			del_str2kw();
 			return 2;
@@ -231,8 +275,14 @@ int main(int argc, char **argv) {
 			return 2;
 		}
 		// vector_for(references, req, refs) request_print(req);
-		if (!solve_references(refs, emu_content->decl_map, target_content->decl_map, emu_content->relaxed_type_conversion)) {
-			printf("Warning: failed to solve all default requests\n");
+		if (target->size_long != emu->size_long) {
+			if (!solve_references(refs, emu_content->decl_map, target_content->decl_map, emu_content->relaxed_type_conversion)) {
+				log_warning_nopos("failed to solve all default requests\n");
+			}
+		} else {
+			if (!solve_references_simple(refs, emu_content->decl_map, target_content->decl_map, emu_content->relaxed_type_conversion)) {
+				log_warning_nopos("failed to solve all default requests\n");
+			}
 		}
 		// vector_for(references, req, refs) request_print(req);
 		references_print_check(refs);
@@ -254,14 +304,15 @@ int main(int argc, char **argv) {
 		del_machines();
 		del_str2kw();
 		return 0; }
+		
 	case MAIN_PROC: {
 		machine_t *arch = convert_machine_name(archname);
 		if (!arch) {
-			printf("Error: invalid architecture '%s'\n", archname);
+			log_error_nopos("invalid architecture '%s'\n", archname);
 		}
 		file_t *content = parse_file(arch, in_file, f); // Takes ownership of f
 		if (!content) {
-			printf("Error: failed to parse the file\n");
+			log_error_nopos("failed to parse the file\n");
 			del_machines();
 			del_str2kw();
 			return 0;
@@ -334,7 +385,7 @@ int main(int argc, char **argv) {
 	case MAIN_PREPROC: {
 		machine_t *arch = convert_machine_name(archname);
 		if (!arch) {
-			printf("Error: invalid architecture '%s'\n", archname);
+			log_error_nopos("invalid architecture '%s'\n", archname);
 		}
 		dump_preproc(arch, in_file, f); // Takes ownership of f
 		del_machines();
@@ -342,6 +393,6 @@ int main(int argc, char **argv) {
 		return 0; }
 	}
 	
-	printf("Internal error: failed to run mode %u\n", ms);
+	log_internal_nopos("failed to run mode %u\n", ms);
 	return 2;
 }
diff --git a/wrapperhelper/src/parse.c b/wrapperhelper/src/parse.c
index 0254d8df..8f593f22 100644
--- a/wrapperhelper/src/parse.c
+++ b/wrapperhelper/src/parse.c
@@ -12,7 +12,7 @@
 void dump_prepare(const char *filename, FILE *file) {
 	prepare_t *prep = prepare_new_file(file, filename);
 	if (!prep) {
-		printf("Failed to create the prepare structure\n");
+		log_memory("failed to create the prepare structure\n");
 		return;
 	}
 	while (1) {
@@ -30,7 +30,7 @@ void dump_preproc(machine_t *target, const char *filename, FILE *file) {
 	char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL;
 	preproc_t *prep = preproc_new_file(target, file, dirname, filename);
 	if (!prep) {
-		printf("Failed to create the preproc structure\n");
+		log_memory("failed to create the preproc structure\n");
 		if (dirname) free(dirname);
 		return;
 	}
@@ -90,29 +90,31 @@ VECTOR_IMPL_STATIC(size_t, (void))
 #define VALIDATION_DECL 1
 #define VALIDATION_LAST_DECL 2
 #define VALIDATION_FUN 3
-static int validate_storage_type(machine_t *target, enum decl_storage storage,
+static int validate_storage_type(loginfo_t *loginfo, machine_t *target, enum decl_storage storage,
                                  type_t *typ, enum token_sym_type_e sym) {
 	// We may still do adjustments here
-	if (!validate_type(target, typ)) return 0;
+	if (!validate_type(loginfo, target, typ)) return 0;
 	if (typ->typ == TYPE_FUNCTION) {
 		if ((storage == TMPSTO_TLS) || (storage == TMPSTO_TLS_EXTERN) || (storage == TMPSTO_TLS_STATIC)) {
-			printf("Error: functions cannot be thread local\n");
+			log_error(loginfo, "functions cannot be thread local\n");
 			return 0;
 		}
 		if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON)) {
 			return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL;
 		} else if (sym == SYM_LBRACKET) {
 			return VALIDATION_FUN;
+		} else {
+			log_error(loginfo, "unexpected symbol %s (%u) after function declaration\n", sym2str[sym], sym);
+			return 0;
 		}
 	} else {
 		if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON) || ((storage != TMPSTO_TYPEDEF) && (sym == SYM_EQ))) {
 			return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL;
+		} else {
+			log_error(loginfo, "unexpected symbol %s (%u) after %s declaration\n", sym2str[sym], sym, (storage == TMPSTO_TYPEDEF) ? "type" : "variable");
+			return 0;
 		}
 	}
-	printf("TODO: validate_storage_type on storage %u, sym %s (%u), valid type ", storage, sym2str[sym], sym);
-	type_print(typ);
-	printf("\n");
-	return 0;
 }
 
 static void promote_csts(num_constant_t *v1, num_constant_t *v2) {
@@ -197,6 +199,8 @@ static void promote_csts(num_constant_t *v1, num_constant_t *v2) {
 	}
 }
 
+#define TOKEN_MATCH_ATTR(attr) (!strcmp(string_content(tok->tokv.str), #attr) || !strcmp(string_content(tok->tokv.str), "__" #attr "__"))
+
 VECTOR_DECLARE_STATIC(exprs, expr_t*)
 #define expr_del_ptr(p) expr_del(*(p))
 VECTOR_IMPL_STATIC(exprs, expr_del_ptr)
@@ -310,95 +314,6 @@ static int parse_declaration_specifier(machine_t *target, khash_t(struct_map) *s
         type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map,
         khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum fun_spec *fspec, enum decl_spec *spec, type_t *typ);
 
-
-void expr_print(expr_t *e) {
-	switch (e->typ) {
-	case ETY_VAR: printf("%s", string_content(e->val.var)); break;
-	case ETY_CONST:
-		switch (e->val.cst.typ) {
-		case NCT_FLOAT: printf("%ff", e->val.cst.val.f); break;
-		case NCT_DOUBLE: printf("%f", e->val.cst.val.d); break;
-		case NCT_LDOUBLE: printf("%Lfl", e->val.cst.val.l); break;
-		case NCT_INT32: printf("%d", e->val.cst.val.i32); break;
-		case NCT_UINT32: printf("%uu", e->val.cst.val.u32); break;
-		case NCT_INT64: printf("%ldll", e->val.cst.val.i64); break;
-		case NCT_UINT64: printf("%lullu", e->val.cst.val.u64); break;
-		}
-		break;
-	case ETY_CALL: printf("(call)"); break;
-	case ETY_ACCESS: printf("(.)"); break;
-	case ETY_PTRACCESS: printf("(->)"); break;
-	case ETY_UNARY:
-		switch (e->val.unary.typ) {
-		case UOT_POSTINCR: printf("(++)"); expr_print(e->val.unary.e); break;
-		case UOT_POSTDECR: printf("(--)"); expr_print(e->val.unary.e); break;
-		case UOT_PREINCR: printf("++"); expr_print(e->val.unary.e); break;
-		case UOT_PREDECR: printf("--"); expr_print(e->val.unary.e); break;
-		case UOT_REF: printf("&"); expr_print(e->val.unary.e); break;
-		case UOT_POS: printf("(+)"); expr_print(e->val.unary.e); break;
-		case UOT_NEG: printf("(-)"); expr_print(e->val.unary.e); break;
-		case UOT_DEREF: printf("(*)"); expr_print(e->val.unary.e); break;
-		case UOT_ANOT: printf("~"); expr_print(e->val.unary.e); break;
-		case UOT_BNOT: printf("!"); expr_print(e->val.unary.e); break;
-		}
-		break;
-	case ETY_BINARY: {
-		printf("(");
-		expr_print(e->val.binary.e1);
-		switch (e->val.binary.typ) {
-		case BOT_ADD: printf(") + ("); break;
-		case BOT_SUB: printf(") - ("); break;
-		case BOT_MUL: printf(") * ("); break;
-		case BOT_DIV: printf(") / ("); break;
-		case BOT_MOD: printf(") %% ("); break;
-		case BOT_LSH: printf(") << ("); break;
-		case BOT_RSH: printf(") >> ("); break;
-		case BOT_LT: printf(") < ("); break;
-		case BOT_GT: printf(") > ("); break;
-		case BOT_LE: printf(") <= ("); break;
-		case BOT_GE: printf(") >= ("); break;
-		case BOT_EQ: printf(") == ("); break;
-		case BOT_NE: printf(") != ("); break;
-		case BOT_AAND: printf(") & ("); break;
-		case BOT_AXOR: printf(") ^ ("); break;
-		case BOT_AOR: printf(") | ("); break;
-		case BOT_BAND: printf(") && ("); break;
-		case BOT_BOR: printf(") || ("); break;
-		case BOT_ASSGN_EQ: printf(") = ("); break;
-		case BOT_ASSGN_ADD: printf(") += ("); break;
-		case BOT_ASSGN_SUB: printf(") -= ("); break;
-		case BOT_ASSGN_MUL: printf(") *= ("); break;
-		case BOT_ASSGN_DIV: printf(") /= ("); break;
-		case BOT_ASSGN_MOD: printf(") %%= ("); break;
-		case BOT_ASSGN_LSH: printf(") <<= ("); break;
-		case BOT_ASSGN_RSH: printf(") >>= ("); break;
-		case BOT_ASSGN_AAND: printf(") &= ("); break;
-		case BOT_ASSGN_AXOR: printf(") ^= ("); break;
-		case BOT_ASSGN_AOR: printf(") |= ("); break;
-		case BOT_COMMA: printf("), ("); break;
-		case BOT_ARRAY: printf(")["); break;
-		}
-		expr_print(e->val.binary.e2);
-		printf(e->val.binary.typ == BOT_ARRAY ? "]" : ")");
-		break; }
-	case ETY_TERNARY: {
-		printf("(");
-		expr_print(e->val.ternary.e1);
-		switch (e->val.ternary.typ) {
-		case TOT_COND: printf(") ? "); break;
-		}
-		expr_print(e->val.ternary.e2);
-		switch (e->val.ternary.typ) {
-		case TOT_COND: printf(" : ("); break;
-		}
-		expr_print(e->val.ternary.e3);
-		printf(")");
-		break; }
-	case ETY_CAST: printf("("); type_print(e->val.cast.typ); printf(")"); expr_print(e->val.cast.e); break;
-	}
-}
-
-
 static int is_type_spec_qual_kw(enum token_keyword_type_e kw) {
 	return
 		(kw == KW_ATOMIC)    ||
@@ -437,7 +352,7 @@ static int parse_type_name(machine_t *target, khash_t(struct_map) *struct_map, k
 	}
 	*typ = type_try_merge(*typ, type_set);
 	if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == end_sym)) {
-		return validate_type(target, *typ);
+		return validate_type(&tok->loginfo, target, *typ);
 	}
 	struct parse_declarator_dest_s dest2;
 	dest2.argt.dest = NULL;
@@ -454,18 +369,20 @@ static int parse_type_name(machine_t *target, khash_t(struct_map) *struct_map, k
 	}
 	type_del(*typ);
 	if (!dest2.argt.dest) {
-		printf("Internal error: argument type is NULL\n");
+		log_internal(&tok->loginfo, "parse_type_name: argument type is NULL\n");
 		// Empty destructor
 		goto failed;
 	}
 	if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != end_sym)) {
+		log_error(&tok->loginfo, "invalid token %s %u after type name\n",
+		            (tok->tokt == PTOK_SYM) ? "symbol" : "type", (tok->tokt == PTOK_SYM) ? tok->tokv.sym : tok->tokt);
 		type_del(dest2.argt.dest);
 		proc_token_del(tok);
 		goto failed;
 	}
 	*typ = dest2.argt.dest;
 	*typ = type_try_merge(*typ, type_set);
-	return validate_type(target, *typ);
+	return validate_type(&tok->loginfo, target, *typ);
 	
 failed:
 	return 0;
@@ -476,13 +393,13 @@ static expr_t *parse_expression(machine_t *target, khash_t(struct_map) *struct_m
         khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, int expr_level) {
 	// Note that expr_level >= 1; expr_level = 0 doesn't appear in the grammar
 	if ((expr_level < 1) || (expr_level > 16)) {
-		printf("Internal error: invalid expression level %d\n", expr_level);
+		log_internal(&tok->loginfo, "Internal error: invalid expression level %d\n", expr_level);
 		return NULL;
 	}
 	
 	VECTOR(expr_pops) *op_stack = vector_new(expr_pops);
 	if (!op_stack) {
-		printf("Error: failed to create operation stack");
+		log_memory("failed to create operation stack");
 		proc_token_del(tok);
 		return NULL;
 	}
@@ -494,18 +411,16 @@ pushed_expr:
 	e = NULL;
 	
 expr_new_token:
-	// printf("expr_new_token with level %d / %d\n", has_level, expr_level);
-	// proc_token_print(tok);
 	if (tok->tokt == PTOK_IDENT) {
 		if (has_level != -1) {
-			printf("Error: invalid expression: unexpected identifier '%s'\n", string_content(tok->tokv.str));
+			log_error(&tok->loginfo, "invalid expression: unexpected identifier '%s'\n", string_content(tok->tokv.str));
 			string_del(tok->tokv.str);
 			goto failed;
 		}
 		has_level = 0;
 		e = malloc(sizeof *e);
 		if (!e) {
-			printf("Error: failed to create new expression atom\n");
+			log_memory("failed to create new expression atom\n");
 			string_del(tok->tokv.str);
 			goto failed;
 		}
@@ -515,19 +430,19 @@ expr_new_token:
 		goto expr_new_token;
 	} else if (tok->tokt == PTOK_NUM) {
 		if (has_level != -1) {
-			printf("Error: invalid expression: unexpected number '%s'\n", string_content(tok->tokv.str));
+			log_error(&tok->loginfo, "invalid expression: unexpected number '%s'\n", string_content(tok->tokv.str));
 			string_del(tok->tokv.str);
 			goto failed;
 		}
 		has_level = 0;
 		e = malloc(sizeof *e);
 		if (!e) {
-			printf("Error: failed to create new expression atom\n");
+			log_memory("failed to create new expression atom\n");
 			string_del(tok->tokv.str);
 			goto failed;
 		}
 		e->typ = ETY_CONST;
-		if (!num_constant_convert(tok->tokv.str, &e->val.cst, target->size_long == 4)) {
+		if (!num_constant_convert(&tok->loginfo, tok->tokv.str, &e->val.cst, target->size_long == 4)) {
 			string_del(tok->tokv.str);
 			goto failed;
 		}
@@ -536,19 +451,19 @@ expr_new_token:
 		goto expr_new_token;
 	} else if ((tok->tokt == PTOK_STRING) && !tok->tokv.sisstr) {
 		if (has_level != -1) {
-			printf("Error: invalid expression: unexpected character constant '%s'\n", string_content(tok->tokv.sstr));
+			log_error(&tok->loginfo, "invalid expression: unexpected character constant '%s'\n", string_content(tok->tokv.sstr));
 			string_del(tok->tokv.sstr);
 			goto failed;
 		}
 		if (string_len(tok->tokv.sstr) != 1) {
-			printf("Error: TODO: invalid expression: multibyte character constant '%s'\n", string_content(tok->tokv.sstr));
+			log_TODO(&tok->loginfo, "invalid expression: multibyte character constant '%s'\n", string_content(tok->tokv.sstr));
 			string_del(tok->tokv.sstr);
 			goto failed;
 		}
 		has_level = 0;
 		e = malloc(sizeof *e);
 		if (!e) {
-			printf("Error: failed to create new expression atom\n");
+			log_memory("failed to create new expression atom\n");
 			string_del(tok->tokv.sstr);
 			goto failed;
 		}
@@ -570,7 +485,7 @@ expr_new_token:
 				.val.unop = opt                                                            \
 			};                                                                             \
 			if (!vector_push(expr_pops, op_stack, pop)) {                                  \
-				printf("Error: failed to add partial operation to operation stack\n");     \
+				log_memory("failed to add partial operation to operation stack\n");     \
 				/* Empty destructor */                                                     \
 				goto failed;                                                               \
 			}                                                                              \
@@ -589,7 +504,7 @@ expr_new_token:
 				.val.binop = {.op = opt, .e1 = e}                                          \
 			};                                                                             \
 			if (!vector_push(expr_pops, op_stack, pop)) {                                  \
-				printf("Error: failed to add partial operation to operation stack\n");     \
+				log_memory("failed to add partial operation to operation stack\n");     \
 				/* Empty destructor */                                                     \
 				goto failed;                                                               \
 			}                                                                              \
@@ -598,12 +513,13 @@ expr_new_token:
 			goto pushed_expr;                                                              \
 		}                                                                                  \
 	}
+	
 	if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_PLUSPLUS)) {
 		if ((has_level != -1) && (has_level <= 1)) {
 			has_level = 1;
 			expr_t *new_e = malloc(sizeof *new_e);
 			if (!new_e) {
-				printf("Error: failed to create new expression atom\n");
+				log_memory("failed to create new expression atom\n");
 				string_del(tok->tokv.str);
 				goto failed;
 			}
@@ -620,7 +536,7 @@ expr_new_token:
 			has_level = 1;
 			expr_t *new_e = malloc(sizeof *new_e);
 			if (!new_e) {
-				printf("Error: failed to create new expression atom\n");
+				log_memory("failed to create new expression atom\n");
 				string_del(tok->tokv.str);
 				goto failed;
 			}
@@ -641,7 +557,7 @@ expr_new_token:
 				.val.binop = {.last_sym = SYM_RSQBRACKET, .op = BOT_ARRAY, .e1 = e}
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				/* Empty destructor */
 				goto failed;
 			}
@@ -657,13 +573,13 @@ expr_new_token:
 			
 			*tok = proc_next_token(prep);
 			if (tok->tokt != PTOK_IDENT) {
-				printf("Error: invalid expression: unexpected token after access symbol\n");
+				log_error(&tok->loginfo, "invalid expression: unexpected token after access symbol\n");
 				proc_token_del(tok);
 				goto failed;
 			}
 			expr_t *new_e = malloc(sizeof *new_e);
 			if (!new_e) {
-				printf("Error: failed to create new expression atom\n");
+				log_memory("failed to create new expression atom\n");
 				string_del(tok->tokv.str);
 				goto failed;
 			}
@@ -683,24 +599,24 @@ expr_new_token:
 	UNOP(SYM_STAR,     UOT_DEREF,   2, 3)
 	UNOP(SYM_TILDE,    UOT_ANOT,    2, 3)
 	UNOP(SYM_EXCL,     UOT_BNOT,    2, 3)
-	BINOP(SYM_STAR,     BOT_MUL,    4,  4,  3)
-	BINOP(SYM_SLASH,    BOT_DIV,    4,  4,  3)
-	BINOP(SYM_PERCENT,  BOT_MOD,    4,  4,  3)
-	BINOP(SYM_PLUS,     BOT_ADD,    5,  5,  4)
-	BINOP(SYM_DASH,     BOT_SUB,    5,  5,  4)
-	BINOP(SYM_LTLT,     BOT_LSH,    6,  6,  5)
-	BINOP(SYM_GTGT,     BOT_RSH,    6,  6,  5)
-	BINOP(SYM_LT,       BOT_LT,     7,  7,  6)
-	BINOP(SYM_GT,       BOT_GT,     7,  7,  6)
-	BINOP(SYM_LTEQ,     BOT_LE,     7,  7,  6)
-	BINOP(SYM_GTEQ,     BOT_GE,     7,  7,  6)
-	BINOP(SYM_EQEQ,     BOT_EQ,     8,  8,  7)
-	BINOP(SYM_EXCLEQ,   BOT_NE,     8,  8,  7)
-	BINOP(SYM_AMP,      BOT_AAND,   9,  9,  8)
-	BINOP(SYM_HAT,      BOT_AXOR,  10, 10,  9)
-	BINOP(SYM_PIPE,     BOT_AOR,   11, 11, 10)
-	BINOP(SYM_AMPAMP,   BOT_BAND,  12, 12, 11)
-	BINOP(SYM_PIPEPIPE, BOT_BOR,   13, 13, 12)
+	BINOP(SYM_STAR,      BOT_MUL,         4,  4,  3)
+	BINOP(SYM_SLASH,     BOT_DIV,         4,  4,  3)
+	BINOP(SYM_PERCENT,   BOT_MOD,         4,  4,  3)
+	BINOP(SYM_PLUS,      BOT_ADD,         5,  5,  4)
+	BINOP(SYM_DASH,      BOT_SUB,         5,  5,  4)
+	BINOP(SYM_LTLT,      BOT_LSH,         6,  6,  5)
+	BINOP(SYM_GTGT,      BOT_RSH,         6,  6,  5)
+	BINOP(SYM_LT,        BOT_LT,          7,  7,  6)
+	BINOP(SYM_GT,        BOT_GT,          7,  7,  6)
+	BINOP(SYM_LTEQ,      BOT_LE,          7,  7,  6)
+	BINOP(SYM_GTEQ,      BOT_GE,          7,  7,  6)
+	BINOP(SYM_EQEQ,      BOT_EQ,          8,  8,  7)
+	BINOP(SYM_EXCLEQ,    BOT_NE,          8,  8,  7)
+	BINOP(SYM_AMP,       BOT_AAND,        9,  9,  8)
+	BINOP(SYM_HAT,       BOT_AXOR,       10, 10,  9)
+	BINOP(SYM_PIPE,      BOT_AOR,        11, 11, 10)
+	BINOP(SYM_AMPAMP,    BOT_BAND,       12, 12, 11)
+	BINOP(SYM_PIPEPIPE,  BOT_BOR,        13, 13, 12)
 	if ((expr_level >= 14) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) {
 		if ((has_level != -1) && (has_level <= 13)) {
 			struct expr_partial_op pop = {
@@ -710,7 +626,7 @@ expr_new_token:
 				.val.ternop = {.arg23_sep = SYM_COLON, .once_done2_want_level = 14, .op = TOT_COND, .e1 = e}
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				// Empty destructor
 				goto failed;
 			}
@@ -719,18 +635,18 @@ expr_new_token:
 			goto pushed_expr;
 		}
 	}
-	BINOP(SYM_EQ,        BOT_ASSGN_EQ,   15, 2, 15)
-	BINOP(SYM_STAREQ,    BOT_ASSGN_MUL,  15, 2, 15)
-	BINOP(SYM_SLASHEQ,   BOT_ASSGN_DIV,  15, 2, 15)
-	BINOP(SYM_PERCENTEQ, BOT_ASSGN_MOD,  15, 2, 15)
-	BINOP(SYM_PLUSEQ,    BOT_ASSGN_ADD,  15, 2, 15)
-	BINOP(SYM_DASHEQ,    BOT_ASSGN_SUB,  15, 2, 15)
-	BINOP(SYM_LTLTEQ,    BOT_ASSGN_LSH,  15, 2, 15)
-	BINOP(SYM_GTGTEQ,    BOT_ASSGN_RSH,  15, 2, 15)
-	BINOP(SYM_AMPEQ,     BOT_ASSGN_AAND, 15, 2, 15)
-	BINOP(SYM_HATEQ,     BOT_ASSGN_AXOR, 15, 2, 15)
-	BINOP(SYM_PIPEEQ,    BOT_ASSGN_AOR,  15, 2, 15)
-	BINOP(SYM_COMMA,    BOT_COMMA, 16, 16, 15)
+	BINOP(SYM_EQ,        BOT_ASSGN_EQ,   15,  2, 15)
+	BINOP(SYM_STAREQ,    BOT_ASSGN_MUL,  15,  2, 15)
+	BINOP(SYM_SLASHEQ,   BOT_ASSGN_DIV,  15,  2, 15)
+	BINOP(SYM_PERCENTEQ, BOT_ASSGN_MOD,  15,  2, 15)
+	BINOP(SYM_PLUSEQ,    BOT_ASSGN_ADD,  15,  2, 15)
+	BINOP(SYM_DASHEQ,    BOT_ASSGN_SUB,  15,  2, 15)
+	BINOP(SYM_LTLTEQ,    BOT_ASSGN_LSH,  15,  2, 15)
+	BINOP(SYM_GTGTEQ,    BOT_ASSGN_RSH,  15,  2, 15)
+	BINOP(SYM_AMPEQ,     BOT_ASSGN_AAND, 15,  2, 15)
+	BINOP(SYM_HATEQ,     BOT_ASSGN_AXOR, 15,  2, 15)
+	BINOP(SYM_PIPEEQ,    BOT_ASSGN_AOR,  15,  2, 15)
+	BINOP(SYM_COMMA,     BOT_COMMA,      16, 16, 15)
 	
 	// expr2 ::= sizeof expr2
 	// which includes expr2 ::= sizeof ( expr16 )
@@ -745,7 +661,7 @@ expr_new_token:
 				.val.c = '\0'
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -758,7 +674,7 @@ expr_new_token:
 		if (IS_BEGIN_TYPE_NAME) {
 			type_t *typ = type_new();
 			if (!typ) {
-				printf("Error: failed to create new type info structure\n");
+				log_memory("failed to create new type info structure\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -766,14 +682,14 @@ expr_new_token:
 				goto failed;
 			}
 			if (!typ->is_validated || typ->is_incomplete || (typ->typ == TYPE_FUNCTION)) {
-				printf("Error: cannot get the size of a function or incomplete type\n");
+				log_error(&tok->loginfo, "cannot get the size of a function or incomplete type\n");
 				type_del(typ);
 				proc_token_del(tok);
 				goto failed;
 			}
 			e = malloc(sizeof *e);
 			if (!e) {
-				printf("Error: failed to create new expression atom\n");
+				log_memory("failed to create new expression atom\n");
 				type_del(typ);
 				proc_token_del(tok);
 				goto failed;
@@ -784,6 +700,7 @@ expr_new_token:
 			has_level = 2;
 			type_del(typ);
 			if (!e->val.cst.val.u64) {
+				log_internal(&tok->loginfo, "size of type is 0\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -798,7 +715,7 @@ expr_new_token:
 				.val.c = 0
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -809,7 +726,7 @@ expr_new_token:
 				.val.c = 0
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -821,7 +738,7 @@ expr_new_token:
 	if ((has_level == -1) && (expr_level >= 2) && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ALIGNOF)) {
 		*tok = proc_next_token(prep);
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) {
-			printf("Error: invalid _Alignof expression\n");
+			log_error(&tok->loginfo, "unexpected token, expected symbol '('\n");
 			proc_token_del(tok);
 			goto failed;
 		}
@@ -829,7 +746,7 @@ expr_new_token:
 		*tok = proc_next_token(prep);
 		type_t *typ = type_new();
 		if (!typ) {
-			printf("Error: failed to create new type info structure\n");
+			log_memory("failed to create new type info structure\n");
 			proc_token_del(tok);
 			goto failed;
 		}
@@ -837,14 +754,14 @@ expr_new_token:
 			goto failed;
 		}
 		if (!typ->is_validated || typ->is_incomplete || (typ->typ == TYPE_FUNCTION)) {
-			printf("Error: cannot get the alignment of a function or incomplete type\n");
+			log_error(&tok->loginfo, "cannot get the alignment of a function or incomplete type\n");
 			type_del(typ);
 			proc_token_del(tok);
 			goto failed;
 		}
 		e = malloc(sizeof *e);
 		if (!e) {
-			printf("Error: failed to create new expression atom\n");
+			log_memory("failed to create new expression atom\n");
 			type_del(typ);
 			proc_token_del(tok);
 			goto failed;
@@ -855,6 +772,7 @@ expr_new_token:
 		has_level = 2;
 		type_del(typ);
 		if (!e->val.cst.val.u64) {
+			log_internal(&tok->loginfo, "alignment of type is 0\n");
 			proc_token_del(tok);
 			goto failed;
 		}
@@ -876,7 +794,7 @@ expr_new_token:
 				expr_t *new_e = malloc(sizeof *new_e);
 				new_e = malloc(sizeof *new_e);
 				if (!new_e) {
-					printf("Error: failed to create new expression atom\n");
+					log_memory("failed to create new expression atom\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -891,7 +809,7 @@ expr_new_token:
 			// We want a function call with at least one argument
 			VECTOR(exprs) *exprs = vector_new_cap(exprs, 1);
 			if (!exprs) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -902,7 +820,7 @@ expr_new_token:
 				.val.funcall = {.f = e, .exprs = exprs}
 			};
 			if (!vector_push(expr_pops, op_stack, pop)) {
-				printf("Error: failed to add partial operation to operation stack\n");
+				log_memory("failed to add partial operation to operation stack\n");
 				vector_del(exprs, exprs);
 				proc_token_del(tok);
 				goto failed;
@@ -916,7 +834,7 @@ expr_new_token:
 			if ((expr_level >= 1) && IS_BEGIN_TYPE_NAME) {
 				type_t *typ = type_new();
 				if (!typ) {
-					printf("Error: failed to create new type info structure\n");
+					log_memory("failed to create new type info structure\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -926,12 +844,12 @@ expr_new_token:
 					goto failed;
 				}
 				if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) {
-					printf("Error: TODO: initializer-list\n");
+					log_TODO(&tok->loginfo, "initializer-list\n");
 					type_del(typ);
 					proc_token_del(tok);
 					goto failed;
 				} else if (expr_level < 3) {
-					printf("Error: cast expression (level 3) but the expression level is at most 2\n");
+					log_error(&tok->loginfo, "cast expression (level 3) but the required expression level is 1 or 2 (expected symbol '{')\n");
 					type_del(typ);
 					proc_token_del(tok);
 					goto failed;
@@ -943,7 +861,7 @@ expr_new_token:
 						.val.typ = typ
 					};
 					if (!vector_push(expr_pops, op_stack, pop)) {
-						printf("Error: failed to add partial operation to operation stack\n");
+						log_memory("failed to add partial operation to operation stack\n");
 						/* Empty destructor */
 						goto failed;
 					}
@@ -959,7 +877,7 @@ expr_new_token:
 					.val.c = 0
 				};
 				if (!vector_push(expr_pops, op_stack, pop)) {
-					printf("Error: failed to add partial operation to operation stack\n");
+					log_memory("failed to add partial operation to operation stack\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -985,13 +903,13 @@ expr_new_token:
 				}
 				break;
 			case EPO_SIZEOF:
-				printf("Error: TODO: find type of expression\n");
+				log_TODO(&tok->loginfo, "find type of expression\n");
 				proc_token_del(tok);
 				goto failed;
 			case EPO_CAST:
 				new_e = malloc(sizeof *new_e);
 				if (!new_e) {
-					printf("Error: failed to create new expression atom\n");
+					log_memory("failed to create new expression atom\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -1009,13 +927,13 @@ expr_new_token:
 					has_level = pop->once_done_is_level;
 					expr_level = pop->once_done_want_level;
 					if (!vector_push(exprs, pop->val.funcall.exprs, e)) {
-						printf("Error: failed to add argument to argument stack\n");
+						log_memory("failed to add argument to argument stack\n");
 						// Empty destructor
 						goto failed;
 					}
 					e = malloc(sizeof *e);
 					if (!e) {
-						printf("Error: failed to create new expression atom\n");
+						log_memory("failed to create new expression atom\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -1029,7 +947,7 @@ expr_new_token:
 					goto expr_new_token;
 				} else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) {
 					if (!vector_push(exprs, pop->val.funcall.exprs, e)) {
-						printf("Error: failed to add argument to argument stack\n");
+						log_memory("failed to add argument to argument stack\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -1042,7 +960,7 @@ expr_new_token:
 			case EPO_UNARY:
 				new_e = malloc(sizeof *new_e);
 				if (!new_e) {
-					printf("Error: failed to create new expression atom\n");
+					log_memory("failed to create new expression atom\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -1063,7 +981,7 @@ expr_new_token:
 			case EPO_BINARY_ARG:
 				new_e = malloc(sizeof *new_e);
 				if (!new_e) {
-					printf("Error: failed to create new expression atom\n");
+					log_memory("failed to create new expression atom\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -1090,7 +1008,7 @@ expr_new_token:
 			case EPO_TERNARY_ARG2:
 				new_e = malloc(sizeof *new_e);
 				if (!new_e) {
-					printf("Error: failed to create new expression atom\n");
+					log_memory("failed to create new expression atom\n");
 					proc_token_del(tok);
 					goto failed;
 				}
@@ -1113,15 +1031,14 @@ expr_new_token:
 		vector_del(expr_pops, op_stack);
 		return e;
 	}
-	printf("Error: invalid expression: unexpected token\n");
-	proc_token_print(tok);
+	log_error(&tok->loginfo, "invalid expression: unexpected token\n");
 	proc_token_del(tok);
 failed:
 	vector_del(expr_pops, op_stack);
 	if (e) expr_del(e);
 	return NULL;
 }
-static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constant_t *dest, int fatal) {
+static int eval_expression(loginfo_t *li, machine_t *target, expr_t *e, khash_t(const_map) *const_map, num_constant_t *dest, int fatal) {
 	// Evaluate the expression (we suppose it is constant)
 	switch (e->typ) {
 	case ETY_VAR: {
@@ -1130,7 +1047,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			*dest = kh_val(const_map, it);
 			return 1;
 		}
-		if (fatal) printf("Error: failed to evaluate expression: expression is not constant (variable)\n");
+		if (fatal) log_error(li, "failed to evaluate expression: expression is not constant (variable)\n");
 		return 0; }
 		
 	case ETY_CONST:
@@ -1140,18 +1057,16 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 	// case ETY_GENERIC:
 		
 	case ETY_CALL:
-		if (fatal) printf("Error: failed to evaluate expression: expression is not constant (function call)\n");
+		if (fatal) log_error(li, "failed to evaluate expression: expression is not constant (function call)\n");
 		return 0;
 		
 	case ETY_ACCESS:
 	case ETY_PTRACCESS:
-		if (fatal) printf("Error: failed to evaluate expression: expression is not constant (member access)\n");
+		if (fatal) log_error(li, "failed to evaluate expression: expression is not constant (member access)\n");
 		return 0;
 		
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
 	case ETY_UNARY:
-		if (!eval_expression(e->val.unary.e, const_map, dest, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.unary.e, const_map, dest, fatal)) return 0;
 		
 		switch (e->val.unary.typ) {
 		case UOT_POSTINCR:
@@ -1160,7 +1075,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 		case UOT_PREDECR:
 		case UOT_REF:
 		case UOT_DEREF:
-			if (fatal) printf("Error: failed to evaluate expression: expression is not constant (assignment or memory accesses)\n");
+			if (fatal) log_error(li, "failed to evaluate expression: expression is not constant (assignment or memory accesses)\n");
 			return 0;
 		case UOT_POS:
 			return 1; // Nothing to do
@@ -1180,7 +1095,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			case NCT_FLOAT:
 			case NCT_DOUBLE:
 			case NCT_LDOUBLE:
-				if (fatal) printf("Error: failed to evaluate expression: cannot bitwise-negate a floating point number\n");
+				if (fatal) log_error(li, "failed to evaluate expression: cannot bitwise-negate a floating point number\n");
 				return 0;
 			case NCT_INT32:  dest->val.i32 = ~dest->val.i32; return 1;
 			case NCT_UINT32: dest->val.u32 = ~dest->val.u32; return 1;
@@ -1190,9 +1105,12 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			}
 		case UOT_BNOT:
 			switch (dest->typ) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
 			case NCT_FLOAT:   dest->typ = NCT_INT32; dest->val.i32 = !dest->val.f;   return 1;
 			case NCT_DOUBLE:  dest->typ = NCT_INT32; dest->val.i32 = !dest->val.d;   return 1;
 			case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.l;   return 1;
+#pragma GCC diagnostic pop
 			case NCT_INT32:   dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i32; return 1;
 			case NCT_UINT32:  dest->typ = NCT_INT32; dest->val.i32 = !dest->val.u32; return 1;
 			case NCT_INT64:   dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i64; return 1;
@@ -1204,8 +1122,8 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 		
 	case ETY_BINARY: {
 		num_constant_t dest1, dest2;
-		if (!eval_expression(e->val.binary.e1, const_map, &dest1, fatal)) return 0;
-		if (!eval_expression(e->val.binary.e2, const_map, &dest2, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.binary.e1, const_map, &dest1, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.binary.e2, const_map, &dest2, fatal)) return 0;
 		
 		switch (e->val.binary.typ) {
 		case BOT_ASSGN_EQ:
@@ -1220,7 +1138,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 		case BOT_ASSGN_AXOR:
 		case BOT_ASSGN_AOR:
 		case BOT_ARRAY: // Is this possible?
-			if (fatal) printf("Error: failed to evaluate expression: expression is not constant (assignments)\n");
+			if (fatal) log_error(li, "failed to evaluate expression: expression is not constant (assignments)\n");
 			return 0;
 #define DOIT(op) \
 			promote_csts(&dest1, &dest2); \
@@ -1240,7 +1158,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			case NCT_FLOAT: \
 			case NCT_DOUBLE: \
 			case NCT_LDOUBLE: \
-				if (fatal) printf("Error: failed to evaluate expression: binary operation %u incompatible with floating point numbers\n", e->val.binary.typ); \
+				if (fatal) log_error(li, "failed to evaluate expression: binary operation %u incompatible with floating point numbers\n", e->val.binary.typ); \
 				return 0; \
 			case NCT_INT32:   dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \
 			case NCT_UINT32:  dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \
@@ -1251,14 +1169,14 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 #define DOIT_BOOL(op) \
 			promote_csts(&dest1, &dest2); \
 			switch (dest1.typ) { \
-			case NCT_FLOAT:   dest->typ = NCT_INT32; dest->val.i32 = dest1.val.f   op dest2.val.f  ; return 1; \
-			case NCT_DOUBLE:  dest->typ = NCT_INT32; dest->val.i32 = dest1.val.d   op dest2.val.d  ; return 1; \
-			case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.l   op dest2.val.l  ; return 1; \
-			case NCT_INT32:   dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \
-			case NCT_UINT32:  dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u32 op dest2.val.u32; return 1; \
-			case NCT_INT64:   dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i64 op dest2.val.i64; return 1; \
-			case NCT_UINT64:  dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u64 op dest2.val.u64; return 1; \
-			default: return 0;                                                                                 \
+			case NCT_FLOAT:   dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.f   op dest2.val.f  ; return 1; \
+			case NCT_DOUBLE:  dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.d   op dest2.val.d  ; return 1; \
+			case NCT_LDOUBLE: dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.l   op dest2.val.l  ; return 1; \
+			case NCT_INT32:   dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.i32 op dest2.val.i32; return 1; \
+			case NCT_UINT32:  dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \
+			case NCT_INT64:   dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.i64 op dest2.val.i64; return 1; \
+			case NCT_UINT64:  dest->typ = NCT_UINT32; dest->val.u32 = dest1.val.u64 op dest2.val.u64; return 1; \
+			default: return 0;                                                                                  \
 			}
 		case BOT_ADD: DOIT(+)
 		case BOT_SUB: DOIT(-)
@@ -1271,13 +1189,19 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 		case BOT_GT: DOIT_BOOL(>)
 		case BOT_LE: DOIT_BOOL(<=)
 		case BOT_GE: DOIT_BOOL(>=)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
 		case BOT_EQ: DOIT_BOOL(==)
 		case BOT_NE: DOIT_BOOL(!=)
+#pragma GCC diagnostic pop
 		case BOT_AAND: DOIT_INT(&)
 		case BOT_AXOR: DOIT_INT(^)
 		case BOT_AOR: DOIT_INT(|)
-		case BOT_BAND: DOIT(&&)
-		case BOT_BOR: DOIT(||)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+		case BOT_BAND: DOIT_BOOL(&&)
+		case BOT_BOR: DOIT_BOOL(||)
+#pragma GCC diagnostic pop
 		case BOT_COMMA: *dest = dest2; return 1;
 #undef DOIT_BOOL
 #undef DOIT_INT
@@ -1287,17 +1211,20 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 		
 	case ETY_TERNARY: {
 		num_constant_t dest1, dest2, dest3;
-		if (!eval_expression(e->val.ternary.e1, const_map, &dest1, fatal)) return 0;
-		if (!eval_expression(e->val.ternary.e2, const_map, &dest2, fatal)) return 0;
-		if (!eval_expression(e->val.ternary.e3, const_map, &dest3, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.ternary.e1, const_map, &dest1, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.ternary.e2, const_map, &dest2, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.ternary.e3, const_map, &dest3, fatal)) return 0;
 		
 		switch (e->val.ternary.typ) {
 		case TOT_COND:
 			promote_csts(&dest2, &dest3);
 			switch (dest1.typ) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
 			case NCT_FLOAT:   *dest = dest1.val.f   ? dest2 : dest3; return 1;
 			case NCT_DOUBLE:  *dest = dest1.val.d   ? dest2 : dest3; return 1;
 			case NCT_LDOUBLE: *dest = dest1.val.l   ? dest2 : dest3; return 1;
+#pragma GCC diagnostic pop
 			case NCT_INT32:   *dest = dest1.val.i32 ? dest2 : dest3; return 1;
 			case NCT_UINT32:  *dest = dest1.val.u32 ? dest2 : dest3; return 1;
 			case NCT_INT64:   *dest = dest1.val.i64 ? dest2 : dest3; return 1;
@@ -1306,17 +1233,49 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			}
 		default: return 0;
 		} }
-#pragma GCC diagnostic pop
 		
 	// case ETY_INIT_LIST:
 		
 	case ETY_CAST:
-		if (!eval_expression(e->val.cast.e, const_map, dest, fatal)) return 0;
+		if (!eval_expression(li, target, e->val.cast.e, const_map, dest, fatal)) return 0;
 		
 		if (e->val.cast.typ->typ == TYPE_BUILTIN) {
 			switch (e->val.cast.typ->val.builtin) {
+			case BTT_VOID:
+				if (fatal) log_error(li, "invalid cast to void\n");
+				return 0;
+			case BTT_BOOL:
+				switch (dest->typ) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+				case NCT_FLOAT:   dest->val.u32 = (_Bool)dest->val.f; break;
+				case NCT_DOUBLE:  dest->val.u32 = (_Bool)dest->val.d; break;
+				case NCT_LDOUBLE: dest->val.u32 = (_Bool)dest->val.l; break;
+#pragma GCC diagnostic pop
+				case NCT_INT32:   dest->val.u32 = (_Bool)dest->val.i32; break;
+				case NCT_UINT32:  dest->val.u32 = (_Bool)dest->val.u32; break;
+				case NCT_INT64:   dest->val.u32 = (_Bool)dest->val.i64; break;
+				case NCT_UINT64:  dest->val.u32 = (_Bool)dest->val.u64; break;
+				}
+				dest->typ = NCT_UINT32;
+				return 1;
+			case BTT_SCHAR:
+			case BTT_S8:
+			cast_s8:
+				switch (dest->typ) {
+				case NCT_FLOAT:   dest->val.i32 = (int8_t)dest->val.f; break;
+				case NCT_DOUBLE:  dest->val.i32 = (int8_t)dest->val.d; break;
+				case NCT_LDOUBLE: dest->val.i32 = (int8_t)dest->val.l; break;
+				case NCT_INT32:   dest->val.i32 = (int8_t)dest->val.i32; break;
+				case NCT_UINT32:  dest->val.i32 = (int8_t)dest->val.u32; break;
+				case NCT_INT64:   dest->val.i32 = (int8_t)dest->val.i64; break;
+				case NCT_UINT64:  dest->val.i32 = (int8_t)dest->val.u64; break;
+				}
+				dest->typ = NCT_INT32;
+				return 1;
 			case BTT_UCHAR:
 			case BTT_U8:
+			cast_u8:
 				switch (dest->typ) {
 				case NCT_FLOAT:   dest->val.u32 = (uint8_t)dest->val.f; break;
 				case NCT_DOUBLE:  dest->val.u32 = (uint8_t)dest->val.d; break;
@@ -1328,9 +1287,37 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 				}
 				dest->typ = NCT_UINT32;
 				return 1;
+			case BTT_SHORT:
+			case BTT_SSHORT:
+			case BTT_S16:
+				switch (dest->typ) {
+				case NCT_FLOAT:   dest->val.i32 = (int16_t)dest->val.f; break;
+				case NCT_DOUBLE:  dest->val.i32 = (int16_t)dest->val.d; break;
+				case NCT_LDOUBLE: dest->val.i32 = (int16_t)dest->val.l; break;
+				case NCT_INT32:   dest->val.i32 = (int16_t)dest->val.i32; break;
+				case NCT_UINT32:  dest->val.i32 = (int16_t)dest->val.u32; break;
+				case NCT_INT64:   dest->val.i32 = (int16_t)dest->val.i64; break;
+				case NCT_UINT64:  dest->val.i32 = (int16_t)dest->val.u64; break;
+				}
+				dest->typ = NCT_INT32;
+				return 1;
+			case BTT_USHORT:
+			case BTT_U16:
+				switch (dest->typ) {
+				case NCT_FLOAT:   dest->val.u32 = (uint16_t)dest->val.f; break;
+				case NCT_DOUBLE:  dest->val.u32 = (uint16_t)dest->val.d; break;
+				case NCT_LDOUBLE: dest->val.u32 = (uint16_t)dest->val.l; break;
+				case NCT_INT32:   dest->val.u32 = (uint16_t)dest->val.i32; break;
+				case NCT_UINT32:  dest->val.u32 = (uint16_t)dest->val.u32; break;
+				case NCT_INT64:   dest->val.u32 = (uint16_t)dest->val.i64; break;
+				case NCT_UINT64:  dest->val.u32 = (uint16_t)dest->val.u64; break;
+				}
+				dest->typ = NCT_UINT32;
+				return 1;
 			case BTT_INT:
 			case BTT_SINT:
 			case BTT_S32:
+			cast_s32:
 				switch (dest->typ) {
 				case NCT_FLOAT:   dest->val.i32 = (int32_t)dest->val.f; break;
 				case NCT_DOUBLE:  dest->val.i32 = (int32_t)dest->val.d; break;
@@ -1344,6 +1331,7 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 				return 1;
 			case BTT_UINT:
 			case BTT_U32:
+			cast_u32:
 				switch (dest->typ) {
 				case NCT_FLOAT:   dest->val.u32 = (uint32_t)dest->val.f; break;
 				case NCT_DOUBLE:  dest->val.u32 = (uint32_t)dest->val.d; break;
@@ -1355,10 +1343,24 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 				}
 				dest->typ = NCT_UINT32;
 				return 1;
-			case BTT_ULONG:
-				// Warning: assuming sizeof(long) == 8 on the current target
-				// TODO: use machine to select between U32 and U64
+			case BTT_LONGLONG:
+			case BTT_SLONGLONG:
+			case BTT_S64:
+			cast_s64:
+				switch (dest->typ) {
+				case NCT_FLOAT:   dest->val.i64 = (int64_t)dest->val.f; break;
+				case NCT_DOUBLE:  dest->val.i64 = (int64_t)dest->val.d; break;
+				case NCT_LDOUBLE: dest->val.i64 = (int64_t)dest->val.l; break;
+				case NCT_INT32:   dest->val.i64 = (int64_t)dest->val.i32; break;
+				case NCT_UINT32:  dest->val.i64 = (int64_t)dest->val.u32; break;
+				case NCT_INT64: break;
+				case NCT_UINT64:  dest->val.i64 = (int64_t)dest->val.u64; break;
+				}
+				dest->typ = NCT_INT64;
+				return 1;
+			case BTT_ULONGLONG:
 			case BTT_U64:
+			cast_u64:
 				switch (dest->typ) {
 				case NCT_FLOAT:   dest->val.u64 = (uint64_t)dest->val.f; break;
 				case NCT_DOUBLE:  dest->val.u64 = (uint64_t)dest->val.d; break;
@@ -1370,25 +1372,31 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 				}
 				dest->typ = NCT_UINT64;
 				return 1;
-			case BTT_VOID:
-			case BTT_BOOL:
-			case BTT_CHAR: // May be signed or unsigned depending on the machine
-			case BTT_SCHAR:
-			case BTT_SHORT:
-			case BTT_SSHORT:
-			case BTT_USHORT:
+			case BTT_CHAR:
+				if (target->unsigned_char) goto cast_u8;
+				else                       goto cast_s8;
 			case BTT_LONG:
 			case BTT_SLONG:
-			case BTT_LONGLONG:
-			case BTT_SLONGLONG:
-			case BTT_ULONGLONG:
+				if (target->size_long == 4) {
+					goto cast_s32;
+				} else if (target->size_long == 8) {
+					goto cast_s64;
+				} else {
+					log_internal(li, "unsupported cast to long with target sizeof(long) == %zu\n", target->size_long);
+					return 0;
+				}
+			case BTT_ULONG:
+				if (target->size_long == 4) {
+					goto cast_u32;
+				} else if (target->size_long == 8) {
+					goto cast_u64;
+				} else {
+					log_internal(li, "unsupported cast to unsigned long with target sizeof(long) == %zu\n", target->size_long);
+					return 0;
+				}
 			case BTT_INT128:
 			case BTT_SINT128:
 			case BTT_UINT128:
-			case BTT_S8:
-			case BTT_S16:
-			case BTT_U16:
-			case BTT_S64:
 			case BTT_FLOAT:
 			case BTT_CFLOAT:
 			case BTT_IFLOAT:
@@ -1403,11 +1411,11 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 			case BTT_IFLOAT128:
 			case BTT_VA_LIST:
 			default:
-				if (fatal) printf("Error: TODO: cast to builtin %s in constant expression\n", builtin2str[e->val.cast.typ->val.builtin]);
+				if (fatal) log_TODO(li, "unsupported cast to builtin %s in constant expression\n", builtin2str[e->val.cast.typ->val.builtin]);
 				return 0;
 			}
 		} else {
-			if (fatal) printf("Error: cast in constant expression\n");
+			if (fatal) log_TODO(li, "unsupported cast in constant expression\n");
 			return 0;
 		}
 		
@@ -1426,7 +1434,7 @@ static int parse_declaration_specifier(machine_t *target, khash_t(struct_map) *s
 		// Empty destructor
 		*tok = proc_next_token(prep);
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) {
-			printf("Error: unexpected token in static assertion declaration\n");
+			log_error(&tok->loginfo, "unexpected token in static assertion declaration\n");
 			proc_token_del(tok);
 			goto failed;
 		}
@@ -1437,13 +1445,13 @@ static int parse_declaration_specifier(machine_t *target, khash_t(struct_map) *s
 			goto failed;
 		}
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_COMMA)) {
-			printf("Error: unexpected token in static assertion expression\n");
+			log_error(&tok->loginfo, "unexpected token in static assertion expression\n");
 			expr_del(e);
 			proc_token_del(tok);
 			goto failed;
 		}
 		num_constant_t eval;
-		if (!eval_expression(e, const_map, &eval, 1)) {
+		if (!eval_expression(&tok->loginfo, target, e, const_map, &eval, 1)) {
 			expr_del(e);
 			// Empty destructor
 			goto failed;
@@ -1451,37 +1459,40 @@ static int parse_declaration_specifier(machine_t *target, khash_t(struct_map) *s
 		expr_del(e);
 		*tok = proc_next_token(prep);
 		if ((tok->tokt != PTOK_STRING) || !tok->tokv.sisstr) {
-			printf("Error: unexpected token in static assertion message\n");
+			log_error(&tok->loginfo, "unexpected token in static assertion message\n");
 			proc_token_del(tok);
 			goto failed;
 		}
 		string_t *errmsg = tok->tokv.str;
 		*tok = proc_next_token(prep);
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) {
-			printf("Error: unexpected token in static assertion message\n");
+			log_error(&tok->loginfo, "unexpected token in static assertion message\n");
 			proc_token_del(tok);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_SEMICOLON)) {
-			printf("Error: unexpected token after static assertion\n");
+			log_error(&tok->loginfo, "unexpected token after static assertion\n");
 			proc_token_del(tok);
 			goto failed;
 		}
 		
 		int iserror;
 		switch (eval.typ) {
-		case NCT_FLOAT:   iserror = (int)eval.val.f == 0; break;
-		case NCT_DOUBLE:  iserror = (int)eval.val.d == 0; break;
-		case NCT_LDOUBLE: iserror = (int)eval.val.l == 0; break;
-		case NCT_INT32:   iserror = eval.val.i32    == 0; break;
-		case NCT_UINT32:  iserror = eval.val.u32    == 0; break;
-		case NCT_INT64:   iserror = eval.val.i64    == 0; break;
-		case NCT_UINT64:  iserror = eval.val.u64    == 0; break;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+		case NCT_FLOAT:   iserror = !eval.val.f;   break;
+		case NCT_DOUBLE:  iserror = !eval.val.d;   break;
+		case NCT_LDOUBLE: iserror = !eval.val.l;   break;
+#pragma GCC diagnostic pop
+		case NCT_INT32:   iserror = !eval.val.i32; break;
+		case NCT_UINT32:  iserror = !eval.val.u32; break;
+		case NCT_INT64:   iserror = !eval.val.i64; break;
+		case NCT_UINT64:  iserror = !eval.val.u64; break;
 		default: iserror = 1;
 		}
 		if (iserror) {
-			printf("Error: static assertion failed: %s\n", string_content(errmsg));
+			log_error(&tok->loginfo, "static assertion failed: %s\n", string_content(errmsg));
 			string_del(errmsg);
 			// Empty destructor
 			goto failed;
@@ -1493,15 +1504,14 @@ static int parse_declaration_specifier(machine_t *target, khash_t(struct_map) *s
 	
 parse_cur_token_decl:
 	if (tok->tokt == PTOK_EOF) {
-		printf("Error: unexpected end of file in declaration\n");
-		proc_token_print(tok);
+		log_error(&tok->loginfo, "unexpected end of file in declaration\n");
 		goto failed;
 	}
 	// Storage
 	if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_AUTO)) {
 		if (*storage == TMPSTO_NONE) *storage = TMPSTO_AUTO;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1510,7 +1520,7 @@ parse_cur_token_decl:
 		if (*storage == TMPSTO_NONE) *storage = TMPSTO_EXTERN;
 		else if (*storage == TMPSTO_TLS) *storage = TMPSTO_TLS_EXTERN;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1518,7 +1528,7 @@ parse_cur_token_decl:
 	} else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_REGISTER)) {
 		if (*storage == TMPSTO_NONE) *storage = TMPSTO_REG;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1527,7 +1537,7 @@ parse_cur_token_decl:
 		if (*storage == TMPSTO_NONE) *storage = TMPSTO_STATIC;
 		else if (*storage == TMPSTO_TLS) *storage = TMPSTO_TLS_STATIC;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1537,7 +1547,7 @@ parse_cur_token_decl:
 		else if (*storage == TMPSTO_EXTERN) *storage = TMPSTO_TLS_EXTERN;
 		else if (*storage == TMPSTO_STATIC) *storage = TMPSTO_TLS_STATIC;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1545,7 +1555,7 @@ parse_cur_token_decl:
 	} else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_TYPEDEF)) {
 		if (*storage == TMPSTO_NONE) *storage = TMPSTO_TYPEDEF;
 		else {
-			printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1568,9 +1578,30 @@ parse_cur_token_decl:
 		// Empty destructor
 		*tok = proc_next_token(prep);
 		if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) {
-			printf("Error: TODO: _Atomic(type-name)\n");
+			if (*spec != SPEC_NONE) {
+				log_error(&tok->loginfo, "unexpected symbol '('\n");
+				// Empty destructor
+				goto failed;
+			}
+			*spec = SPEC_TYPE;
+			if (!parse_type_name(target, struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) {
+				goto failed;
+			}
+			if (!typ->is_validated) {
+				log_internal(&tok->loginfo, "_Atomic() type is not validated\n");
+				type_del(typ);
+				// Empty destructor
+				goto failed;
+			}
+			if ((typ->typ == TYPE_ARRAY) || (typ->typ == TYPE_FUNCTION) || typ->is_atomic || typ->is_const || typ->is_restrict || typ->is_volatile) {
+				log_error(&tok->loginfo, "the type name in an _Atomic() may not be an array, a function, atomic, or qualified\n");
+				type_del(typ);
+				// Empty destructor
+				goto failed;
+			}
 			// Empty destructor
-			goto failed;
+			*tok = proc_next_token(prep);
+			goto parse_cur_token_decl;
 		} else {
 			typ->is_atomic = 1;
 			goto parse_cur_token_decl;
@@ -1591,16 +1622,16 @@ parse_cur_token_decl:
 	
 	// Specifier
 #define SPEC(bt,post) \
-		if (*spec == SPEC_NONE) {                                                                   \
-			*spec = SPEC_BUILTIN;                                                                   \
-			typ->typ = TYPE_BUILTIN;                                                                \
-			typ->val.builtin = BTT_ ## bt;                                                          \
-			post                                                                                    \
-		} else {                                                                                    \
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \
-			goto failed;                                                                            \
-		}                                                                                           \
-		*tok = proc_next_token(prep);                                                               \
+		if (*spec == SPEC_NONE) {                                                                              \
+			*spec = SPEC_BUILTIN;                                                                              \
+			typ->typ = TYPE_BUILTIN;                                                                           \
+			typ->val.builtin = BTT_ ## bt;                                                                     \
+			post                                                                                               \
+		} else {                                                                                               \
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \
+			goto failed;                                                                                       \
+		}                                                                                                      \
+		*tok = proc_next_token(prep);                                                                          \
 		goto parse_cur_token_decl;
 #define SPEC_SIGNED(bt, allow_int) \
 		if ((*spec == SPEC_NONE) || (allow_int && (*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT))) {                    \
@@ -1614,7 +1645,7 @@ parse_cur_token_decl:
 			*spec = allow_int ? *spec : SPEC_BUILTIN;                                                                             \
 			typ->val.builtin = BTT_U ## bt;                                                                                       \
 		} else {                                                                                                                  \
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);                               \
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);                    \
 			goto failed;                                                                                                          \
 		}                                                                                                                         \
 		*tok = proc_next_token(prep);                                                                                             \
@@ -1635,7 +1666,7 @@ parse_cur_token_decl:
 		} else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) {
 			typ->val.builtin = BTT_CLONGDOUBLE;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1654,7 +1685,7 @@ parse_cur_token_decl:
 		} else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) {
 			typ->val.builtin = BTT_ILONGDOUBLE;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1684,7 +1715,7 @@ parse_cur_token_decl:
 			*spec = SPEC_BUILTIN;
 			typ->val.builtin = BTT_LONGDOUBLE;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1703,7 +1734,7 @@ parse_cur_token_decl:
 			typ->typ = TYPE_BUILTIN;
 			typ->val.builtin = BTT_IFLOAT;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1722,7 +1753,7 @@ parse_cur_token_decl:
 			typ->typ = TYPE_BUILTIN;
 			typ->val.builtin = BTT_IFLOAT128;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1737,7 +1768,7 @@ parse_cur_token_decl:
 		} else if (*spec == SPEC_BUILTIN_NOINT) {
 			*spec = SPEC_BUILTIN;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1770,7 +1801,7 @@ parse_cur_token_decl:
 		} else if (*spec == SPEC_IMAGINARY) {
 			*spec = SPEC_LONGIMAGINARY;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1793,7 +1824,7 @@ parse_cur_token_decl:
 		} else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) {
 			typ->val.builtin = BTT_SSHORT;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1814,7 +1845,7 @@ parse_cur_token_decl:
 		} else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) {
 			typ->val.builtin = BTT_USHORT;
 		} else {
-			printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
+			log_error(&tok->loginfo, "unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]);
 			goto failed;
 		}
 		*tok = proc_next_token(prep);
@@ -1828,19 +1859,13 @@ parse_cur_token_decl:
 		// The ident is the type-specifier
 		khiter_t it = kh_get(type_map, type_map, string_content(tok->tokv.str));
 		if (it == kh_end(type_map)) {
-			printf("Error: invalid type '%s' (ident is not a typedef)\n"
-					"Current state:\n"
-					"  storage: %p/%u\n"
-					"  spec: %u\n"
-					"  type: ", string_content(tok->tokv.str), storage, storage ? *storage : TMPSTO_NONE, *spec);
-			type_print(typ);
-			printf("\n");
+			log_error(&tok->loginfo, "invalid type '%s' (ident is not a typedef)\n", string_content(tok->tokv.str));
 			string_del(tok->tokv.str);
 			goto failed;
 		} else {
 			*spec = SPEC_TYPE;
 			if (!type_copy_into(typ, kh_val(type_map, it))) {
-				printf("Failed to duplicate type infos\n");
+				log_memory("failed to duplicate type infos\n");
 				string_del(tok->tokv.str);
 				goto failed;
 			}
@@ -1855,32 +1880,34 @@ parse_cur_token_decl:
 		
 		// Empty destructor
 		*tok = proc_next_token(prep);
-		khiter_t it = kh_end(struct_map); // Iterator into the struct_map
+		string_t *tag;
 		if (tok->tokt == PTOK_IDENT) {
-			string_t *tag = tok->tokv.str;
+			tag = tok->tokv.str;
 			// Token moved
 			*tok = proc_next_token(prep);
 			
+			khiter_t it; // Iterator into the struct_map
 			int iret;
 			it = kh_put(struct_map, struct_map, string_content(tag), &iret);
 			if (iret < 0) {
-				printf("Error: failed to add structure to struct map\n");
+				log_memory("failed to add structure to struct map\n");
 				proc_token_del(tok);
 				goto failed;
 			} else if (iret == 0) {
 				// Structure already declared or defined
 				if (kh_val(struct_map, it)->is_struct != is_struct) {
-					printf("Error: incoherent struct/union tagging of %s\n", string_content(tag));
+					log_error(&tok->loginfo, "incoherent struct/union tagging of %s\n", string_content(tag));
 					string_del(tag);
 					proc_token_del(tok);
 					goto failed;
 				}
 				++kh_val(struct_map, it)->nrefs;
 				string_del(tag);
+				tag = kh_val(struct_map, it)->tag;
 			} else {
 				kh_val(struct_map, it) = struct_new(is_struct, tag);
 				if (!kh_val(struct_map, it)) {
-					printf("Error: failed to create new structure metadata structure\n");
+					log_memory("failed to create new structure metadata structure\n");
 					string_del(tag);
 					proc_token_del(tok);
 					goto failed;
@@ -1890,10 +1917,11 @@ parse_cur_token_decl:
 			typ->val.st = kh_val(struct_map, it);
 			typ->is_incomplete = !typ->val.st->is_defined;
 		} else {
+			tag = NULL;
 			typ->typ = TYPE_STRUCT_UNION;
 			typ->val.st = struct_new(is_struct, NULL);
 			if (!typ->val.st) {
-				printf("Error: failed to create new structure metadata structure\n");
+				log_memory("failed to create new structure metadata structure\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -1901,24 +1929,18 @@ parse_cur_token_decl:
 		}
 		if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) {
 			if (typ->val.st->is_defined) {
-				printf("Error: TODO: struct redefinition\n"
-						"Current state:\n"
-						"  storage: %p/%u\n"
-						"  spec: %u\n"
-						"  type: ", storage, storage ? *storage : TMPSTO_NONE, *spec);
-				type_print(typ);
-				printf("\n");
+				log_TODO(&tok->loginfo, "struct redefinition (tag is %s)\n", typ->val.st->tag ? string_content(typ->val.st->tag) : "<no tag>");
 				goto failed;
 			}
 			
 			VECTOR(st_members) *members = vector_new(st_members);
 			if (!members) {
-				printf("Failed to create a members vector\n");
+				log_memory("failed to create a members vector\n");
 				goto failed;
 			}
 			type_t *typ2 = type_new();
 			if (!typ2) {
-				printf("Failed to create a type info structure\n");
+				log_memory("failed to create a type info structure\n");
 				vector_del(st_members, members);
 				goto failed;
 			}
@@ -1941,13 +1963,13 @@ parse_cur_token_decl:
 					// A struct-declaration that does not declare an anonymous structure or anonymous union
 					// shall contain a struct-declarator-list.
 					if ((typ2->typ != TYPE_STRUCT_UNION) || typ2->val.st->tag) {
-						printf("Error: missing struct-declarator-list\n");
+						log_error(&tok->loginfo, "missing struct-declarator-list\n");
 						vector_del(st_members, members);
 						type_del(typ2);
 						goto failed;
 					}
 					if (!vector_push(st_members, members, ((st_member_t){.name = NULL, .typ = typ2, .is_bitfield = 0}))) {
-						printf("Error: failed to add anonymous structure member\n");
+						log_memory("failed to add anonymous structure member\n");
 						vector_del(st_members, members);
 						type_del(typ2);
 						// Empty destructor
@@ -1955,7 +1977,7 @@ parse_cur_token_decl:
 					}
 					typ2 = type_new();
 					if (!typ2) {
-						printf("Failed to create a type info structure\n");
+						log_memory("failed to create a type info structure\n");
 						vector_del(st_members, members);
 						// Empty destructor
 						goto failed;
@@ -1974,7 +1996,7 @@ parse_cur_token_decl:
 				dest2.structms.const_map = const_map;
 				dest2.structms.dest = members;
 				if (!parse_declarator(target, &dest2, prep, tok, TMPSTO_NONE, FSPEC_NONE, typ2, 0, 1, 1, 1)) {
-					printf("Error parsing struct-declarator-list\n");
+					log_error(&tok->loginfo, "error parsing struct-declarator-list\n");
 					vector_del(st_members, members);
 					type_del(typ2);
 					// Token is deleted
@@ -1982,14 +2004,14 @@ parse_cur_token_decl:
 				}
 				type_del(typ2);
 				if ((tok->tokt != PTOK_SYM) && (tok->tokv.sym != SYM_SEMICOLON)) {
-					printf("Error parsing struct-declarator-list (invalid next token)\n");
+					log_error(&tok->loginfo, "error parsing struct-declarator-list (invalid next token)\n");
 					vector_del(st_members, members);
 					proc_token_del(tok);
 					goto failed;
 				}
 				typ2 = type_new();
 				if (!typ2) {
-					printf("Failed to create a type info structure\n");
+					log_memory("failed to create a type info structure\n");
 					vector_del(st_members, members);
 					// Empty destructor
 					goto failed;
@@ -1998,7 +2020,7 @@ parse_cur_token_decl:
 			}
 			type_del(typ2);
 			if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) {
-				printf("Error parsing struct-declarator-list (invalid next token)\n");
+				log_error(&tok->loginfo, "error parsing struct-declarator-list (invalid next token)\n");
 				vector_del(st_members, members);
 				proc_token_del(tok);
 				goto failed;
@@ -2006,6 +2028,13 @@ parse_cur_token_decl:
 			
 			typ->is_incomplete = 0;
 			typ->is_validated = 0;
+			if (tag) {
+				khiter_t it = kh_get(type_set, type_set, typ);
+				if (it != kh_end(type_set)) {
+					kh_key(type_set, it)->is_incomplete = 0;
+					kh_key(type_set, it)->is_validated = 0;
+				}
+			}
 			typ->val.st->has_incomplete = 0; // Filled by the validate_type step
 			typ->val.st->nmembers = vector_size(st_members, members);
 			typ->val.st->members = vector_steal(st_members, members);
@@ -2013,8 +2042,8 @@ parse_cur_token_decl:
 			*tok = proc_next_token(prep);
 			goto parse_cur_token_decl;
 		} else {
-			if (it == kh_end(struct_map)) {
-				printf("Error: invalid structure declaration: missing tag and/or definition\n");
+			if (!tag) {
+				log_error(&tok->loginfo, "invalid structure declaration: missing tag and/or definition\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -2033,19 +2062,19 @@ parse_cur_token_decl:
 		}
 		if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LBRACKET)) {
 			if (!tag) {
-				printf("Error: unexpected token after keyword 'enum'\n");
+				log_error(&tok->loginfo, "unexpected token after keyword 'enum'\n");
 				proc_token_del(tok);
 				goto failed;
 			}
 			khiter_t it = kh_get(type_map, enum_map, string_content(tag));
 			if (it == kh_end(enum_map)) {
-				printf("Error: enumeration %s has not been defined yet\n", string_content(tag)); // TODO?
+				log_error(&tok->loginfo, "enumeration %s has not been defined yet\n", string_content(tag)); // TODO?
 				string_del(tag);
 				proc_token_del(tok);
 				goto failed;
 			}
 			if (!type_copy_into(typ, kh_val(enum_map, it))) {
-				printf("Failed to duplicate enum type infos\n");
+				log_memory("failed to duplicate enum type infos\n");
 				string_del(tag);
 				proc_token_del(tok);
 				goto failed;
@@ -2068,25 +2097,93 @@ parse_cur_token_decl:
 		num_constant_t cst = { .typ = NCT_INT32, .val.i32 = -1 };
 		// Empty destructor
 		*tok = proc_next_token(prep);
+		if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RBRACKET)) {
+			log_error(&tok->loginfo, "expected enum constant name before '}'\n");
+			proc_token_del(tok);
+			goto failed;
+		}
 		int iret;
 		while ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) {
 			if (tok->tokt != PTOK_IDENT) {
-				printf("Error: invalid token in enumeration definition\n");
+				log_error(&tok->loginfo, "unexpected token in enumeration definition\n");
 				if (tag) string_del(tag);
 				proc_token_del(tok);
 				goto failed;
 			}
 			char *ident = string_steal(tok->tokv.str);
 			*tok = proc_next_token(prep);
+			while ((tok->tokt == PTOK_IDENT) && !strcmp(string_content(tok->tokv.str), "__attribute__")) {
+				// Attribute
+				string_del(tok->tokv.str);
+				*tok = proc_next_token(prep);
+				if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) {
+					log_error(&tok->loginfo, "unexpected token, expected '('\n");
+					free(ident);
+					if (tag) string_del(tag);
+					proc_token_del(tok);
+					goto failed;
+				}
+				// Empty destructor
+				*tok = proc_next_token(prep);
+				if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) {
+					log_error(&tok->loginfo, "unexpected token, expected '('\n");
+					free(ident);
+					if (tag) string_del(tag);
+					// Empty destructor
+					goto failed;
+				}
+				// Empty destructor
+				*tok = proc_next_token(prep);
+			enum_attribute_list:
+				if ((tok->tokt == PTOK_IDENT) && TOKEN_MATCH_ATTR(deprecated)) {
+					log_warning(&tok->loginfo, "ignoring enum constant attribute 'deprecated'\n");
+					string_del(tok->tokv.str);
+					*tok = proc_next_token(prep);
+					if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) {
+						// Empty destructor
+						*tok = proc_next_token(prep);
+						goto enum_attribute_list;
+					} else goto enum_attribute_list_end;
+				}
+				if ((tok->tokt == PTOK_IDENT) && TOKEN_MATCH_ATTR(unavailable)) {
+					log_warning(&tok->loginfo, "ignoring enum constant attribute 'unavailable'\n");
+					string_del(tok->tokv.str);
+					*tok = proc_next_token(prep);
+					if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) {
+						// Empty destructor
+						*tok = proc_next_token(prep);
+						goto enum_attribute_list;
+					} else goto enum_attribute_list_end;
+				}
+			enum_attribute_list_end:
+				if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) {
+					log_error(&tok->loginfo, "unexpected token, expected ident or ')'\n");
+					free(ident);
+					if (tag) string_del(tag);
+					// Empty destructor
+					goto failed;
+				}
+				// Empty destructor
+				*tok = proc_next_token(prep);
+				if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) {
+					log_error(&tok->loginfo, "unexpected token, expected ')'\n");
+					free(ident);
+					if (tag) string_del(tag);
+					// Empty destructor
+					goto failed;
+				}
+				// Empty destructor
+				*tok = proc_next_token(prep);
+			}
 			khiter_t it = kh_put(const_map, const_map, ident, &iret);
 			if (iret < 0) {
-				printf("Error: failed to add constant %s to the constants map\n", ident);
+				log_memory("failed to add constant %s to the constants map\n", ident);
 				free(ident);
 				if (tag) string_del(tag);
 				proc_token_del(tok);
 				goto failed;
 			} else if (iret == 0) {
-				printf("Error: constant %s is already in the constants map\n", ident);
+				log_error(&tok->loginfo, "constant %s is already defined\n", ident);
 				free(ident);
 				if (tag) string_del(tag);
 				proc_token_del(tok);
@@ -2114,7 +2211,7 @@ parse_cur_token_decl:
 					break;
 				case NCT_UINT64:
 					if (cst.val.u64 == UINT64_MAX) {
-						printf("Error: enum constant is too big\n");
+						log_error(&tok->loginfo, "enum constant is too big\n");
 						if (tag) string_del(tag);
 						proc_token_del(tok);
 						goto failed;
@@ -2124,7 +2221,7 @@ parse_cur_token_decl:
 				case NCT_DOUBLE:
 				case NCT_LDOUBLE:
 				default:
-					printf("Internal error: enum constant is a float/double/ldouble\n");
+					log_internal(&tok->loginfo, "enum constant is a float/double/ldouble\n");
 					if (tag) string_del(tag);
 					proc_token_del(tok);
 					goto failed;
@@ -2137,13 +2234,13 @@ parse_cur_token_decl:
 					goto failed;
 				}
 				if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_RBRACKET))) {
-					printf("Error: unexpected token during enumeration declaration\n");
+					log_error(&tok->loginfo, "unexpected token during enumeration declaration\n");
 					expr_del(e);
 					if (tag) string_del(tag);
 					proc_token_del(tok);
 					goto failed;
 				}
-				if (!eval_expression(e, const_map, &cst, 1)) {
+				if (!eval_expression(&tok->loginfo, target, e, const_map, &cst, 1)) {
 					expr_del(e);
 					if (tag) string_del(tag);
 					// Empty destructor
@@ -2156,7 +2253,7 @@ parse_cur_token_decl:
 				if (cst.val.i32 < 0) {
 					has_neg = 1;
 					if (btt == BTT_U64) {
-						printf("Error: enum constant is too big\n");
+						log_error(&tok->loginfo, "enum constant is too big\n");
 						if (tag) string_del(tag);
 						// Empty destructor
 						goto failed;
@@ -2177,7 +2274,7 @@ parse_cur_token_decl:
 						not_in_i32 = 1;
 					}
 					if (btt == BTT_U64) {
-						printf("Error: enum constant is too big\n");
+						log_error(&tok->loginfo, "enum constant is too big\n");
 						if (tag) string_del(tag);
 						// Empty destructor
 						goto failed;
@@ -2198,7 +2295,7 @@ parse_cur_token_decl:
 				if (cst.val.u64 > (uint32_t)INT32_MAX) {
 					not_in_i32 = 1;
 					if (has_neg && (cst.val.u64 > (uint64_t)INT64_MAX)) {
-						printf("Error: enum constant is too big\n");
+						log_error(&tok->loginfo, "enum constant is too big\n");
 						if (tag) string_del(tag);
 						// Empty destructor
 						goto failed;
@@ -2210,7 +2307,7 @@ parse_cur_token_decl:
 			case NCT_DOUBLE:
 			case NCT_LDOUBLE:
 			default:
-				printf("Error: invalid floating-point enumeration constant\n");
+				log_error(&tok->loginfo, "invalid floating-point enumeration constant\n");
 				if (tag) string_del(tag);
 				// Empty destructor
 				goto failed;
@@ -2224,19 +2321,19 @@ parse_cur_token_decl:
 			char *ctag = string_steal(tag);
 			khiter_t it = kh_put(type_map, enum_map, ctag, &iret);
 			if (iret < 0) {
-				printf("Error: failed to add enumeration %s to the type map\n", ctag);
+				log_memory("failed to add enumeration %s to the type map\n", ctag);
 				free(ctag);
 				// Empty destructor
 				goto failed;
 			} else if (iret == 0) {
-				printf("Error: enumeration %s already exists\n", ctag);
+				log_error(&tok->loginfo, "enumeration %s already exists\n", ctag);
 				free(ctag);
 				// Empty destructor
 				goto failed;
 			}
 			type_t *new_typ = type_new();
 			if (!new_typ) {
-				printf("Error: failed to create type info for enumeration %s\n", ctag);
+				log_memory("failed to create type info for enumeration %s\n", ctag);
 				free(ctag);
 				kh_del(type_map, enum_map, it);
 				// Empty destructor
@@ -2247,7 +2344,7 @@ parse_cur_token_decl:
 			typ->val.typ = new_typ->val.typ = (*builtins)[btt];
 			typ->val.typ->nrefs += 2;
 			new_typ = type_try_merge(new_typ, type_set);
-			validate_type(target, new_typ); // Assume it returns 1
+			validate_type(&tok->loginfo, target, new_typ); // Assume it returns 1
 			kh_val(enum_map, it) = new_typ;
 		} else {
 			typ->typ = TYPE_ENUM;
@@ -2269,14 +2366,7 @@ parse_cur_token_decl:
 	return 1;
 	
 invalid_token:
-	printf("Error: unexpected token (parse_declaration_specifier)\n"
-			"Current state:\n"
-			"  storage: %p/%u\n"
-			"  spec: %u\n"
-			"  type: ", storage, storage ? *storage : TMPSTO_NONE, *spec);
-	type_print(typ);
-	printf("\n");
-	proc_token_print(tok);
+	log_error(&tok->loginfo, "unexpected token (parse_declaration_specifier)\n");
 	proc_token_del(tok);
 	
 failed:
@@ -2294,7 +2384,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 	type_t *cur_bottom = NULL;
 	VECTOR(size_t) *nptr_stack = vector_new_cap(size_t, 1);
 	if (!nptr_stack) {
-		printf("Failed to parse init_declarator_list (allocate nptr_stack)\n");
+		log_memory("failed to allocate nptr stack\n");
 		proc_token_del(tok);
 		goto failed0;
 	}
@@ -2302,31 +2392,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 	while (1) {
 		switch (tok->tokt) {
 		case PTOK_IDENT:
-			if (!has_ident) {
-				if (allow_decl) {
-					cur_ident = tok->tokv.str;
-					has_ident = 1;
-					*tok = proc_next_token(prep);
-					break;
-				} else {
-					printf("Error: unexpected identifier: abstract declarator do not contain a name\n"
-					       "Current state:\n"
-					       "  storage: %u\n"
-					       "  type: ", storage);
-					type_print(typ);
-					printf("\n");
-					proc_token_print(tok);
-					proc_token_del(tok);
-					goto failed;
-				}
+			if (!has_ident && allow_decl) {
+				cur_ident = tok->tokv.str;
+				has_ident = 1;
+				*tok = proc_next_token(prep);
+				break;
 			} else {
-				printf("Error: unexpected identifier\n"
-				       "Current state:\n"
-				       "  storage: %u\n"
-				       "  type: ", storage);
-				type_print(typ);
-				printf("\n");
-				proc_token_print(tok);
+				log_error(&tok->loginfo, "unexpected identifier\n");
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -2337,7 +2409,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					
 					VECTOR(types) *args = vector_new(types);
 					if (!args) {
-						printf("Error: failed to create new type (function argument)\n");
+						log_memory("failed to create new type (function argument)\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2350,7 +2422,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							vector_del(types, args);
 							new_typ = type_new();
 							if (!new_typ) {
-								printf("Error: failed to create new function type\n");
+								log_memory("failed to create new function type\n");
 								// Empty destructor
 								goto failed;
 							}
@@ -2362,7 +2434,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							goto end_fun;
 						}
 						if (!proc_unget_token(prep, tok)) {
-							printf("Error: failed to unget processor token\n");
+							log_memory("failed to unget processor token\n");
 							// Empty destructor
 							vector_del(types, args);
 							goto failed;
@@ -2378,7 +2450,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							// Empty destructor
 							*tok = proc_next_token(prep);
 							if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) {
-								printf("Error: invalid token after function variadic argument\n");
+								log_error(&tok->loginfo, "invalid token after function variadic argument\n");
 								proc_token_del(tok);
 								vector_del(types, args);
 								goto failed;
@@ -2387,7 +2459,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						} else {
 							type_t *typ2 = type_new();
 							if (!typ2) {
-								printf("Error: failed to create new type (function argument)\n");
+								log_memory("failed to create new type (function argument)\n");
 								// Empty destructor
 								vector_del(types, args);
 								goto failed;
@@ -2403,7 +2475,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							}
 							if (spec2 == SPEC_NONE) {
 								// _Static_assert declaration; empty destructor
-								printf("Invalid _Static_assert declaration\n");
+								log_error(&tok->loginfo, "unexpected _Static_assert declaration\n");
 								vector_del(types, args);
 								type_del(typ2);
 								goto failed;
@@ -2414,13 +2486,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 									// Need to convert type to a pointer
 									type_t *typ3 = type_new();
 									if (!typ3) {
-										printf("Error: failed to allocate new type\n");
+										log_memory("failed to allocate new type\n");
 										type_del(typ2);
 										// Empty destructor
 										goto failed;
 									}
 									if (!type_copy_into(typ3, typ2)) {
-										printf("Error: failed to duplicate array type to temporary type\n");
+										log_memory("failed to duplicate array type to temporary type\n");
 										type_del(typ3);
 										type_del(typ2);
 										// Empty destructor
@@ -2432,7 +2504,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 									typ2 = type_try_merge(typ3, PDECL_TYPE_SET);
 								}
 								if (!vector_push(types, args, typ2)) {
-									printf("Error: failed to add argument to argument vector\n");
+									log_memory("failed to add argument to argument vector\n");
 									vector_del(types, args);
 									type_del(typ2);
 									// Empty destructor
@@ -2445,13 +2517,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 									// Need to convert type to a pointer
 									type_t *typ3 = type_new();
 									if (!typ3) {
-										printf("Error: failed to allocate new type\n");
+										log_memory("failed to allocate new type\n");
 										type_del(typ2);
 										// Empty destructor
 										goto failed;
 									}
 									if (!type_copy_into(typ3, typ2)) {
-										printf("Error: failed to duplicate array type to temporary type\n");
+										log_memory("failed to duplicate array type to temporary type\n");
 										type_del(typ3);
 										type_del(typ2);
 										// Empty destructor
@@ -2463,7 +2535,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 									typ2 = type_try_merge(typ3, PDECL_TYPE_SET);
 								}
 								if (!vector_push(types, args, typ2)) {
-									printf("Error: failed to add argument to argument vector\n");
+									log_memory("failed to add argument to argument vector\n");
 									vector_del(types, args);
 									type_del(typ2);
 									// Empty destructor
@@ -2490,13 +2562,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							}
 							type_del(typ2);
 							if (!dest2.argt.dest) {
-								printf("Internal error: argument type is NULL\n");
+								log_internal(&tok->loginfo, "argument type is NULL\n");
 								vector_del(types, args);
 								// Empty destructor
 								goto failed;
 							}
 							if (!vector_push(types, args, dest2.argt.dest)) {
-								printf("Error: failed to add argument to argument vector\n");
+								log_memory("failed to add argument to argument vector\n");
 								vector_del(types, args);
 								type_del(dest2.argt.dest);
 								// Empty destructor
@@ -2509,7 +2581,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 								*tok = proc_next_token(prep);
 								continue;
 							}
-							printf("Error: invalid token after function argument\n");
+							log_error(&tok->loginfo, "invalid token after function argument\n");
 							vector_del(types, args);
 							proc_token_del(tok);
 							goto failed;
@@ -2518,7 +2590,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				no_arg:
 					new_typ = type_new();
 					if (!new_typ) {
-						printf("Error: failed to create new function type\n");
+						log_memory("failed to create new function type\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2547,7 +2619,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					break;
 				} else {
 					if (!vector_push(size_t, nptr_stack, 0)) {
-						printf("Failed to parse init_declarator_list (open parenthesis)\n");
+						log_memory("Failed to push 0 to the nptr stack for opening a parenthesis\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2556,13 +2628,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 			} else if (tok->tokv.sym == SYM_STAR) {
 				if (has_ident) {
-					printf("Error: invalid token '*' after identifier in declaration\n");
+					log_error(&tok->loginfo, "invalid token '*' after identifier in declaration\n");
 					// Empty destructor
 					goto failed;
 				} else {
 					type_t *new_typ = type_new_ptr(typ);
 					if (!new_typ) {
-						printf("Failed to parse init_declarator_list (create new pointer typ)\n");
+						log_memory("failed to create a new pointer type info structure\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2574,14 +2646,14 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 			} else if (tok->tokv.sym == SYM_LSQBRACKET) {
 				if (!has_ident) {
 					if (!allow_abstract) {
-						printf("Error: invalid token '[' before identifier in declaration\n");
+						log_error(&tok->loginfo, "invalid token '[' before identifier in declaration\n");
 						// Empty destructor
 						goto failed;
 					}
 					has_ident = 1;
 				}
 				if (array_atomic || array_const || array_restrict || array_static || array_volatile) {
-					printf("Error: invalid array after array with type qualifier(s) and/or 'static'\n");
+					log_error(&tok->loginfo, "invalid array after array with type qualifier(s) and/or 'static'\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -2598,10 +2670,10 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				size_t nelems; _Bool is_incomplete;
 				while (1) {
 #define DO_CHECKS \
-	if (is_init || is_list || !allow_decl || (cur_bottom && (typ->typ != TYPE_ARRAY))) {                         \
-		printf("Error: type qualifiers and 'static' may only appear in function argument array declarations\n"); \
-		/* Empty destructor */                                                                                   \
-		goto failed;                                                                                             \
+	if (is_init || is_list || !allow_decl || (cur_bottom && (typ->typ != TYPE_ARRAY))) {                                    \
+		log_error(&tok->loginfo, "type qualifiers and 'static' may only appear in function argument array declarations\n"); \
+		/* Empty destructor */                                                                                              \
+		goto failed;                                                                                                        \
 	}
 					if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) {
 						DO_CHECKS
@@ -2629,7 +2701,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RSQBRACKET)) {
 					if (array_static) {
 						// Missing expression
-						printf("Error: unexpected token ']' in static length array declaration\n");
+						log_error(&tok->loginfo, "unexpected token ']' in static length array declaration\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2639,7 +2711,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				} else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_STAR)) {
 					if (array_static) {
 						// Missing expression
-						printf("Error: unexpected token '*' in static length array declaration\n");
+						log_error(&tok->loginfo, "unexpected token '*' in static length array declaration\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2650,7 +2722,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					*tok = proc_next_token(prep);
 					if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) {
 						// TODO: ...[*expr]
-						printf("Error: unexpected token during variable length array declaration\n");
+						log_error(&tok->loginfo, "unexpected token during variable length array declaration\n");
 						proc_token_del(tok);
 						goto failed;
 					}
@@ -2663,13 +2735,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						goto failed;
 					}
 					if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) {
-						printf("Error: unexpected token during array declaration\n");
+						log_error(&tok->loginfo, "unexpected token during array declaration\n");
 						expr_del(e);
 						proc_token_del(tok);
 						goto failed;
 					}
 					num_constant_t cst;
-					if (eval_expression(e, PDECL_CONST_MAP, &cst, is_init || is_list || !allow_decl)) {
+					if (eval_expression(&tok->loginfo, target, e, PDECL_CONST_MAP, &cst, is_init || is_list || !allow_decl)) {
 						expr_del(e);
 						int is_neg;
 						switch (cst.typ) {
@@ -2683,7 +2755,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						default: is_neg = 1;
 						}
 						if (is_neg) {
-							printf("Error: the size of an array must be nonnegative");
+							log_error(&tok->loginfo, "the size of an array must be nonnegative");
 							// Empty destructor
 							goto failed;
 						}
@@ -2702,7 +2774,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				if (cur_bottom) {
 					type_t *tmp = type_new();
 					if (!tmp) {
-						printf("Failed to parse init_declarator_list (create new array type)\n");
+						log_memory("failed to create new array type info\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2721,7 +2793,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				} else {
 					type_t *new_typ = type_new();
 					if (!new_typ) {
-						printf("Failed to parse init_declarator_list (create new array type)\n");
+						log_memory("failed to create new array type info\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2737,7 +2809,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 			} else if (tok->tokv.sym == SYM_RPAREN) {
 				if (!has_ident) {
 					if (!allow_abstract) {
-						printf("Error: invalid token ')' before identifier in declaration\n");
+						log_error(&tok->loginfo, "invalid token ')' before identifier in declaration\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2745,7 +2817,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 				if (vector_size(size_t, nptr_stack) == 1) {
 					if (!is_init && !is_list) goto rparen_ok_ret;
-					printf("Error: closing unopened parenthesis in declaration\n");
+					log_error(&tok->loginfo, "closing unopened parenthesis in declaration\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -2773,12 +2845,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 			        || (is_init && (tok->tokv.sym == SYM_LBRACKET))) {
 			rparen_ok_ret: // Last function argument
 				if (!allow_abstract && !has_ident) {
-					printf("Error: invalid symbol '%s' before identifier\n", sym2str[tok->tokv.sym]);
+					log_error(&tok->loginfo, "invalid symbol '%s' before identifier\n", sym2str[tok->tokv.sym]);
 					// Empty destructor
 					goto failed;
 				}
 				if (vector_size(size_t, nptr_stack) != 1) {
-					printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]);
+					log_error(&tok->loginfo, "invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]);
 					// Empty destructor
 					goto failed;
 				}
@@ -2786,7 +2858,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				// Try to free some redundant types
 				typ = type_try_merge(typ, PDECL_TYPE_SET);
 				
-				int validation = validate_storage_type(target, storage, typ, tok->tokv.sym);
+				int validation = validate_storage_type(&tok->loginfo, target, storage, typ, tok->tokv.sym);
 				if (!validation) {
 					// Empty destructor
 					goto failed;
@@ -2796,7 +2868,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					// Function definition; tok is '{'
 					if (!is_init || !is_list || has_list) {
 						// We are not at the top-level or we have an initialization list
-						printf("Error: invalid function definition\n");
+						log_error(&tok->loginfo, "invalid function definition\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2807,7 +2879,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					
 					declaration_t *decl = malloc(sizeof *decl);
 					if (!decl) {
-						printf("Failed to create new declaration\n");
+						log_memory("failed to create new declaration\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2819,7 +2891,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					char *cident = string_steal(cur_ident); cur_ident = NULL;
 					khiter_t it = kh_put(decl_map, dest->f->decl_map, cident, &iret);
 					if (iret < 0) {
-						printf("Failed to add function '%s' to the declaration map\n", cident);
+						log_memory("failed to add function '%s' to the declaration map\n", cident);
 						free(cident);
 						free(decl);
 						// Empty destructor
@@ -2827,7 +2899,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					} else if (iret == 0) {
 						free(decl);
 						if (kh_val(dest->f->decl_map, it)->defined) {
-							printf("Error: function '%s' is already in the declaration map\n", cident);
+							log_error(&tok->loginfo, "function '%s' is already in the declaration map\n", cident);
 							free(cident);
 							// Empty destructor
 							goto failed;
@@ -2850,11 +2922,11 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							else goto success;
 						}
 					} while (!proc_token_isend(tok));
-					printf("Error: unexpected token in function body\n");
+					log_error(&tok->loginfo, "unexpected token in function body\n");
 					goto failed;
 				}
 				if ((fspec != FSPEC_NONE) && (typ->typ != TYPE_FUNCTION)) {
-					printf("Error: unexpected function specifier\n");
+					log_error(&tok->loginfo, "unexpected function specifier\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -2863,7 +2935,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				if (storage == TMPSTO_TYPEDEF) {
 					if (!is_init || !is_list) {
 						// We are not at the top-level (note that storage is set to NONE in function arguments)
-						printf("Error: invalid function definition\n");
+						log_error(&tok->loginfo, "invalid function definition\n");
 						// Empty destructor
 						goto failed;
 					}
@@ -2872,13 +2944,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					char *cident = string_steal(cur_ident); cur_ident = NULL;
 					khiter_t it = kh_put(type_map, dest->f->type_map, cident, &iret);
 					if (iret < 0) {
-						printf("Failed to add '%s' to the type map\n", cident);
+						log_memory("failed to add '%s' to the type map\n", cident);
 						free(cident);
 						// Empty destructor
 						goto failed;
 					} else if (iret == 0) {
 						if (!type_t_equal(typ, kh_val(dest->f->type_map, it))) {
-							printf("Error: '%s' is already in the type map with a different type\n", cident);
+							log_error(&tok->loginfo, "'%s' is already in the type map with a different type\n", cident);
 							free(cident);
 							type_del(typ);
 							// Empty destructor
@@ -2894,7 +2966,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					if (is_init && is_list) {
 						decl = malloc(sizeof *decl);
 						if (!decl) {
-							printf("Failed to create new declaration\n");
+							log_memory("failed to create new declaration\n");
 							// Empty destructor
 							goto failed;
 						}
@@ -2908,7 +2980,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						char *cident = string_steal(cur_ident); cur_ident = NULL;
 						khiter_t it = kh_put(decl_map, dest->f->decl_map, cident, &iret);
 						if (iret < 0) {
-							printf("Failed to add '%s' to the declaration map\n", cident);
+							log_memory("failed to add '%s' to the declaration map\n", cident);
 							free(cident);
 							free(decl);
 							// Empty destructor
@@ -2916,7 +2988,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						} else if (iret == 0) {
 							if (!type_t_equal(typ, kh_val(dest->f->decl_map, it)->typ)
 							 || ((storage == TMPSTO_NONE) && (kh_val(dest->f->decl_map, it)->storage == STORAGE_NONE))) {
-								printf("Error: '%s' is already in the declaration map (storage=%u)\n", cident, storage);
+								log_error(&tok->loginfo, "'%s' is already in the declaration map\n", cident);
 								free(cident);
 								free(decl);
 								type_del(typ);
@@ -2939,12 +3011,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 								if (typ == base_type) {
 									typ = type_new();
 									if (!typ) {
-										printf("Error: failed to allocate new type\n");
+										log_memory("failed to allocate new type\n");
 										// Empty destructor
 										goto failed;
 									}
 									if (!type_copy_into(typ, base_type)) {
-										printf("Error: failed to allocate new type\n");
+										log_memory("failed to allocate new type\n");
 										// Empty destructor
 										goto failed;
 									}
@@ -2963,13 +3035,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						goto success;
 					} else if (!is_init && is_list) {
 						if (!vector_push(st_members, dest->structms.dest, ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 0}))) {
-							printf("Error: failed to add structure member %s\n", string_content(cur_ident));
+							log_memory("failed to add structure member %s\n", string_content(cur_ident));
 							string_del(cur_ident);
 							// Empty destructor
 							goto failed;
 						}
 					} else {
-						printf("Internal error: unknown is_init/is_list combination %d%d\n", is_init, is_list);
+						log_internal(&tok->loginfo, "unknown is_init/is_list combination %d%d\n", is_init, is_list);
 						// Empty destructor
 						goto failed;
 					}
@@ -2977,12 +3049,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_EQ)) {
 					// Initialization
 					if (!is_init) {
-						printf("Error: unexpected initializer\n");
+						log_error(&tok->loginfo, "unexpected initializer\n");
 						goto failed;
 					}
 					if (decl) {
 						if (decl->defined) {
-							printf("Error: invalid declaration initializer: variable was already declared\n");
+							log_error(&tok->loginfo, "invalid declaration initializer: variable was already declared\n");
 							goto failed;
 						}
 						decl->defined = 1;
@@ -3001,7 +3073,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 							}
 						} while (!proc_token_isend(tok));
 						if (proc_token_isend(tok)) {
-							printf("Error: unexpected token in declaration initializer\n");
+							log_error(&tok->loginfo, "unexpected token in declaration initializer\n");
 							proc_token_del(tok);
 							goto failed;
 						}
@@ -3009,13 +3081,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 						expr_t *e = parse_expression(target, PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS,
 						                             PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, 15);
 						if (!e) {
-							printf("Error: invalid declaration initializer\n");
+							log_error(&tok->loginfo, "invalid declaration initializer\n");
 							goto failed;
 						}
 						expr_del(e);
 					}
 					if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) {
-						printf("Error: unexpected token in declaration initializer\n");
+						log_error(&tok->loginfo, "unexpected token in declaration initializer\n");
 						proc_token_del(tok);
 						goto failed;
 					}
@@ -3032,7 +3104,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 			} else if (!is_init && is_list && (tok->tokv.sym == SYM_COLON)) {
 				if (vector_size(size_t, nptr_stack) != 1) {
-					printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]);
+					log_error(&tok->loginfo, "invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]);
 					// Empty destructor
 					goto failed;
 				}
@@ -3048,20 +3120,20 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					goto failed;
 				}
 				if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) {
-					printf("Error: unexpected token in bitfield width\n");
+					log_error(&tok->loginfo, "unexpected token in bitfield width\n");
 					expr_del(e);
 					proc_token_del(tok);
 					goto failed;
 				}
 				num_constant_t eval;
-				if (!eval_expression(e, dest->structms.const_map, &eval, 1)) {
+				if (!eval_expression(&tok->loginfo, target, e, dest->structms.const_map, &eval, 1)) {
 					expr_del(e);
 					// Empty destructor
 					goto failed;
 				}
 				expr_del(e);
 				
-				int validation = validate_storage_type(target, storage, typ, tok->tokv.sym);
+				int validation = validate_storage_type(&tok->loginfo, target, storage, typ, tok->tokv.sym);
 				if (!validation) {
 					// Empty destructor
 					goto failed;
@@ -3070,12 +3142,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				if (validation == VALIDATION_FUN) {
 					// We are not at the top-level or we have an initialization list
 					// Should never happen
-					printf("Error: invalid function definition in structure definition\n");
+					log_error(&tok->loginfo, "invalid function definition in structure definition\n");
 					// Empty destructor
 					goto failed;
 				}
 				if ((fspec != FSPEC_NONE) && (typ->typ != TYPE_FUNCTION)) {
-					printf("Error: unexpected function specifier\n");
+					log_error(&tok->loginfo, "unexpected function specifier\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -3084,7 +3156,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				switch (eval.typ) {
 				case NCT_INT32:
 					if (eval.val.i32 < 0) {
-						printf("Error: invalid negative bitfield width\n");
+						log_error(&tok->loginfo, "invalid negative bitfield width\n");
 						goto failed;
 					}
 					width = (size_t)eval.val.i32;
@@ -3094,7 +3166,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 					break;
 				case NCT_INT64:
 					if (eval.val.i64 < 0) {
-						printf("Error: invalid negative bitfield width\n");
+						log_error(&tok->loginfo, "invalid negative bitfield width\n");
 						goto failed;
 					}
 					width = (size_t)eval.val.i64;
@@ -3106,13 +3178,13 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				case NCT_DOUBLE:
 				case NCT_LDOUBLE:
 				default:
-					printf("Error: invalid non-integer bitfield width\n");
+					log_error(&tok->loginfo, "invalid non-integer bitfield width\n");
 					goto failed;
 				}
 				
 				if (!vector_push(st_members, dest->structms.dest,
 				        ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 1, .bitfield_width = width}))) {
-					printf("Error: failed to add structure member %s\n", string_content(cur_ident));
+					log_memory("failed to add structure member %s\n", string_content(cur_ident));
 					string_del(cur_ident);
 					// Empty destructor
 					goto failed;
@@ -3131,12 +3203,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 		case PTOK_KEYWORD:
 			if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) {
 				if (has_ident) {
-					printf("Error: invalid keyword '_Atomic' after identifier\n");
+					log_error(&tok->loginfo, "invalid keyword '_Atomic' after identifier\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
 				} else if (!vector_last(size_t, nptr_stack)) {
-					printf("Error: invalid keyword '_Atomic' before symbol '*'\n");
+					log_error(&tok->loginfo, "invalid keyword '_Atomic' before symbol '*'\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
@@ -3147,12 +3219,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 			} else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CONST)) {
 				if (has_ident) {
-					printf("Error: invalid keyword 'const' after identifier\n");
+					log_error(&tok->loginfo, "invalid keyword 'const' after identifier\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
 				} else if (!vector_last(size_t, nptr_stack)) {
-					printf("Error: invalid keyword 'const' before symbol '*'\n");
+					log_error(&tok->loginfo, "invalid keyword 'const' before symbol '*'\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
@@ -3163,12 +3235,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 			} else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_RESTRICT)) {
 				if (has_ident) {
-					printf("Error: invalid keyword 'restrict' after identifier\n");
+					log_error(&tok->loginfo, "invalid keyword 'restrict' after identifier\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
 				} else if (!vector_last(size_t, nptr_stack)) {
-					printf("Error: invalid keyword 'restrict' before symbol '*'\n");
+					log_error(&tok->loginfo, "invalid keyword 'restrict' before symbol '*'\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
@@ -3179,12 +3251,12 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 				}
 			} else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOLATILE)) {
 				if (has_ident) {
-					printf("Error: invalid keyword 'volatile' after identifier\n");
+					log_error(&tok->loginfo, "invalid keyword 'volatile' after identifier\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
 				} else if (!vector_last(size_t, nptr_stack)) {
-					printf("Error: invalid keyword 'volatile' before symbol '*'\n");
+					log_error(&tok->loginfo, "invalid keyword 'volatile' before symbol '*'\n");
 					proc_token_print(tok);
 					// Empty destructor
 					goto failed;
@@ -3200,13 +3272,7 @@ static int parse_declarator(machine_t *target, struct parse_declarator_dest_s *d
 		case PTOK_STRING:
 		case PTOK_PRAGMA:
 		case PTOK_EOF:
-			printf("Error: unexpected token (parse_declarator %d%d%d%d)\n"
-			       "Current state:\n"
-			       "  storage: %u\n"
-			       "  type: ", is_init, is_list, allow_decl, allow_abstract, storage);
-			type_print(typ);
-			printf("\n");
-			proc_token_print(tok);
+			log_error(&tok->loginfo, "unexpected token (in parse_declarator %d%d%d%d)\n", is_init, is_list, allow_decl, allow_abstract);
 			proc_token_del(tok);
 			goto failed;
 		}
@@ -3226,44 +3292,44 @@ failed0:
 
 static int finalize_file(machine_t *target, file_t *file) {
 #define MARK_SIMPLE(sname) \
-	it = kh_get(struct_map, file->struct_map, #sname);                          \
-	if (it != kh_end(file->struct_map)) {                                       \
-		kh_val(file->struct_map, it)->is_simple = 1;                            \
-	} else {                                                                    \
-		it = kh_get(type_map, file->type_map, #sname);                          \
-		if (it != kh_end(file->type_map)) {                                     \
-			type_t *typ2 = kh_val(file->type_map, it);                          \
-			if (typ2->typ != TYPE_STRUCT_UNION) {                               \
-				printf("Error: invalid typedef " #sname ": not a structure\n"); \
-				return 0;                                                       \
-			}                                                                   \
-			typ2->val.st->is_simple = 1;                                        \
-		}                                                                       \
+	it = kh_get(struct_map, file->struct_map, #sname);                            \
+	if (it != kh_end(file->struct_map)) {                                         \
+		kh_val(file->struct_map, it)->is_simple = 1;                              \
+	} else {                                                                      \
+		it = kh_get(type_map, file->type_map, #sname);                            \
+		if (it != kh_end(file->type_map)) {                                       \
+			type_t *typ2 = kh_val(file->type_map, it);                            \
+			if (typ2->typ != TYPE_STRUCT_UNION) {                                 \
+				log_error_nopos("invalid typedef " #sname ": not a structure\n"); \
+				return 0;                                                         \
+			}                                                                     \
+			typ2->val.st->is_simple = 1;                                          \
+		}                                                                         \
 	}
 #define SET_WEAK(converted) \
-	validate_type(target, typ);                                              \
-	typ = type_try_merge(typ, file->type_set);                               \
-	it = kh_put(conv_map, file->relaxed_type_conversion, typ, &iret);        \
-	if (iret < 0) {                                                          \
-		printf("Error: failed to add relaxed conversion to type map\n");     \
-		type_del(typ);                                                       \
-		return 0;                                                            \
-	} else if (iret == 0) {                                                  \
-		printf("Error: type already has a relaxed conversion\n");            \
-		type_del(typ);                                                       \
-		return 0;                                                            \
-	}                                                                        \
+	validate_type(&(loginfo_t){0}, target, typ);                      \
+	typ = type_try_merge(typ, file->type_set);                        \
+	it = kh_put(conv_map, file->relaxed_type_conversion, typ, &iret); \
+	if (iret < 0) {                                                   \
+		log_memory("failed to add relaxed conversion to type map\n"); \
+		type_del(typ);                                                \
+		return 0;                                                     \
+	} else if (iret == 0) {                                           \
+		log_error_nopos("type already has a relaxed conversion\n");   \
+		type_del(typ);                                                \
+		return 0;                                                     \
+	}                                                                 \
 	kh_val(file->relaxed_type_conversion, it) = string_new_cstr(#converted);
 #define SET_WEAK_PTR_TO(to_typ, converted) \
-	it = kh_get(type_map, file->type_map, #to_typ);         \
-	if (it != kh_end(file->type_map)) {                     \
-		typ = type_new_ptr(kh_val(file->type_map, it));     \
-		if (!typ) {                                         \
-			printf("Failed to create type " #to_typ "*\n"); \
-			return 0;                                       \
-		}                                                   \
-		++kh_val(file->type_map, it)->nrefs;                \
-		SET_WEAK(converted)                                 \
+	it = kh_get(type_map, file->type_map, #to_typ);             \
+	if (it != kh_end(file->type_map)) {                         \
+		typ = type_new_ptr(kh_val(file->type_map, it));         \
+		if (!typ) {                                             \
+			log_memory("failed to create type " #to_typ "*\n"); \
+			return 0;                                           \
+		}                                                       \
+		++kh_val(file->type_map, it)->nrefs;                    \
+		SET_WEAK(converted)                                     \
 	}
 	
 	type_t *typ;
@@ -3273,13 +3339,25 @@ static int finalize_file(machine_t *target, file_t *file) {
 	SET_WEAK_PTR_TO(FILE, S)
 	// #pragma type_letters b xcb_connection_t*
 	SET_WEAK_PTR_TO(xcb_connection_t, S)
+	if (target->size_long == 4) {
+		// Only on x86, not on x86_64
+		it = kh_get(type_map, file->type_map, "locale_t");
+		if (it != kh_end(file->type_map)) {
+			typ = kh_val(file->type_map, it);
+			SET_WEAK(a)
+		}
+	}
 	// #pragma mark_simple ...
-	MARK_SIMPLE(FTS)
-	MARK_SIMPLE(FTS64)
-	MARK_SIMPLE(glob_t)
-	MARK_SIMPLE(glob64_t)
+	if (target->size_long == 8) {
+		// Only on x86_64, not on x86
+		MARK_SIMPLE(FTS)
+		MARK_SIMPLE(FTS64)
+		MARK_SIMPLE(glob_t)
+		MARK_SIMPLE(glob64_t)
+	}
 #undef MARK_SIMPLE
 #undef SET_WEAK
+#undef SET_WEAK_PTR_TO
 	return 1;
 }
 
@@ -3287,20 +3365,20 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 	char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL;
 	preproc_t *prep = preproc_new_file(target, file, dirname, filename);
 	if (!prep) {
-		printf("Failed to create the preproc structure\n");
+		log_memory("failed to create the preproc structure\n");
 		if (dirname) free(dirname);
 		return NULL;
 	}
 	file_t *ret = file_new(target);
 	if (!ret) {
-		printf("Failed to create the file structure\n");
+		log_memory("failed to create the file structure\n");
 		preproc_del(prep);
 		return NULL;
 	}
 	
 	type_t *typ = type_new();
 	if (!typ) {
-		printf("Failed to create a type info structure\n");
+		log_memory("failed to create a type info structure\n");
 		goto failed;
 	}
 	while (1) {
@@ -3315,17 +3393,17 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 					int iret;
 					char *dup = strdup(typenames[i]);
 					if (!dup) {
-						printf("Failed to create a type info structure\n");
+						log_memory("failed to create a type info structure\n");
 						goto failed;
 					}
 					type_t *t = ret->builtins[BTT_START_INT_EXT + i];
 					khiter_t it = kh_put(type_map, ret->type_map, dup, &iret);
 					if (iret < 0) {
-						printf("Failed to add an intrinsic to the type map\n");
+						log_memory("failed to add an intrinsic to the type map\n");
 						goto failed;
 					} else if (iret == 0) {
 						if (!type_t_equal(t, kh_val(ret->type_map, it))) {
-							printf("Error: %s is already defined\n", dup);
+							log_error(&tok.loginfo, "%s is already defined\n", dup);
 							free(dup);
 							goto failed;
 						}
@@ -3347,13 +3425,13 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 					if (it != kh_end(ret->struct_map)) {
 						type_t *typ2 = kh_val(ret->type_map, it);
 						if (typ2->typ != TYPE_STRUCT_UNION) {
-							printf("Error: failed to find struct/union named %s\n", string_content(sutag));
+							log_error(&tok.loginfo, "failed to find struct/union named %s\n", string_content(sutag));
 							string_del(sutag);
 							goto failed;
 						}
 						su = typ2->val.st;
 					} else {
-						printf("Error: failed to find struct/union named %s\n", string_content(sutag));
+						log_error(&tok.loginfo, "failed to find struct/union named %s\n", string_content(sutag));
 						string_del(sutag);
 						goto failed;
 					}
@@ -3366,7 +3444,7 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 				string_t *converted = tok.tokv.pragma.val;
 				type_t *typ2 = type_new();
 				if (!typ2) {
-					printf("Error: failed to create new type info structure\n");
+					log_memory("failed to create new type info structure\n");
 					string_del(converted);
 					type_del(typ2);
 					goto failed;
@@ -3380,13 +3458,13 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 				int iret;
 				khiter_t it = kh_put(conv_map, ret->relaxed_type_conversion, typ2, &iret);
 				if (iret < 0) {
-					printf("Error: failed to add relaxed conversion to type map\n");
+					log_memory("failed to add relaxed conversion to type map\n");
 					string_del(converted);
 					type_del(typ2);
 					// Empty destructor
 					goto failed;
 				} else if (iret == 0) {
-					printf("Error: type already has a relaxed conversion\n");
+					log_error(&tok.loginfo, "type already has a relaxed conversion\n");
 					string_del(converted);
 					type_del(typ2);
 					// Empty destructor
@@ -3400,7 +3478,7 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 				string_t *converted = tok.tokv.pragma.val;
 				type_t *typ2 = type_new();
 				if (!typ2) {
-					printf("Error: failed to create new type info structure\n");
+					log_memory("failed to create new type info structure\n");
 					string_del(converted);
 					type_del(typ2);
 					goto failed;
@@ -3413,7 +3491,7 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 				}
 				type_del(typ2); // typ2 is in the type set, so it is already used
 				if (typ2->converted) {
-					printf("Error: type already has a strict conversion\n");
+					log_error(&tok.loginfo, "type already has a strict conversion\n");
 					string_del(converted);
 					// Empty destructor
 					goto failed;
@@ -3424,7 +3502,7 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 				break; }
 			}
 		} else if (proc_token_iserror(&tok)) {
-			printf("Error: unexpected error token\n");
+			log_error(&tok.loginfo, "unexpected error token\n");
 			proc_token_del(&tok);
 			goto failed;
 		} else {
@@ -3442,11 +3520,11 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 					goto failed;
 				}
 			} else {
-				if (validate_storage_type(target, storage, typ, tok.tokv.sym) != VALIDATION_LAST_DECL) {
+				if (validate_storage_type(&tok.loginfo, target, storage, typ, tok.tokv.sym) != VALIDATION_LAST_DECL) {
 					goto failed;
 				}
 				if (fspec != FSPEC_NONE) {
-					printf("Error: unexpected function specifier\n");
+					log_error(&tok.loginfo, "unexpected function specifier\n");
 					// Empty destructor
 					goto failed;
 				}
@@ -3455,7 +3533,7 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 			type_del(typ);
 			typ = type_new();
 			if (!typ) {
-				printf("Failed to create a type info structure\n");
+				log_memory("failed to create a type info structure\n");
 				goto failed;
 			}
 		}
@@ -3465,7 +3543,7 @@ success:
 	preproc_del(prep);
 	type_del(typ);
 	if (!finalize_file(target, ret)) {
-		printf("Error: failed to add builtin aliases\n");
+		log_memory("failed to add builtin aliases\n");
 		file_del(ret);
 		return NULL;
 	}
diff --git a/wrapperhelper/src/prepare.c b/wrapperhelper/src/prepare.c
index 616f2332..ff19a067 100644
--- a/wrapperhelper/src/prepare.c
+++ b/wrapperhelper/src/prepare.c
@@ -2,11 +2,17 @@
 
 #include <string.h>
 
+typedef struct char_s {
+	int c;
+	loginfo_t li;
+} char_t;
+
 struct prepare_s {
 	FILE *f;
-	int buf[4];
-	int buf_len; // <= 4 (though 3 *should* be enough)
+	char_t buf[5];
+	int buf_len; // <= 5 (though 4 *should* be enough)
 	char *srcn;
+	loginfo_t li;
 	enum prepare_state {
 		PREPST_NONE = 0,
 		PREPST_NL,
@@ -21,14 +27,24 @@ struct prepare_s {
 prepare_t *prepare_new_file(FILE *f, const char *filename) {
 	prepare_t *ret = malloc(sizeof *ret);
 	if (!ret) {
+		log_memory("failed to allocate new preparator structure\n");
 		fclose(f);
 		return NULL;
 	}
+	
+	char *srcn = strdup(filename);
+	if (!srcn) {
+		log_memory("failed to duplicate filename\n");
+		free(ret);
+		return NULL;
+	}
+	
 	*ret = (prepare_t){
 		.f = f,
-		.buf = {0, 0, 0},
+		.buf = {{0}, {0}, {0}, {0}},
 		.buf_len = 0,
-		.srcn = strdup(filename),
+		.srcn = srcn,
+		.li = { .filename = srcn, .lineno = 1, .colno = 1, .lineno_end = 0, .colno_end = 0 },
 		.st = PREPST_NL,
 	};
 	return ret;
@@ -40,28 +56,72 @@ void prepare_del(prepare_t *prep) {
 	free(prep);
 }
 
-static int get_char(prepare_t *src) {
+// Do not call this more than twice in a row if the last character retrieved is '\\'
+static void unget_char(prepare_t *src, char_t c) {
+	src->buf[src->buf_len++] = c;
+}
+// Transforms \n, \r\n and \r into \n
+static char_t get_char(prepare_t *src) {
+	if (src->buf_len) {
+		return src->buf[--src->buf_len];
+	}
 start_get_char:
-	int c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f);
-	src->buf_len = 0;
+	int c = getc(src->f);
 	if (c == '\\') {
-		c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f);
-		if (c == '\n') goto start_get_char;
-		src->buf[src->buf_len++] = c;
-		return '\\';
+		c = getc(src->f);
+		if (c == '\n') {
+			++src->li.lineno;
+			src->li.colno = 1;
+			goto start_get_char;
+		} else if (c == '\r') {
+			++src->li.lineno;
+			src->li.colno = 1;
+			c = getc(src->f);
+			if (c == '\n') {
+				goto start_get_char;
+			} else {
+				unget_char(src, (char_t){.c = c, .li = src->li});
+				goto start_get_char;
+			}
+		}
+		src->li.colno += 2;
+		src->buf[src->buf_len++] = (char_t){
+			.c = c,
+			.li = { .filename = src->li.filename, .lineno = src->li.lineno, .colno = src->li.colno - 1, .lineno_end = 0, .colno_end = 0 }
+		};
+		return (char_t){
+			.c = '\\',
+			.li = { .filename = src->li.filename, .lineno = src->li.lineno, .colno = src->li.colno - 2, .lineno_end = 0, .colno_end = 0 }
+		};
 	}
-	return c;
-}
-// Do not call this more than twice in a row if the last character retrieved is '\\'
-static void unget_char(prepare_t *src, int c) {
-	src->buf[src->buf_len++] = c;
+	char_t ret = {
+		.c = c,
+		.li = { .filename = src->li.filename, .lineno = src->li.lineno, .colno = src->li.colno, .lineno_end = 0, .colno_end = 0 }
+	};
+	if (c == '\n') {
+		++src->li.lineno;
+		src->li.colno = 1;
+	} else if (c == '\r') {
+		++src->li.lineno;
+		src->li.colno = 1;
+		ret.c = '\n';
+		c = getc(src->f);
+		if (c != '\n') {
+			unget_char(src, (char_t){.c = c, .li = src->li});
+		}
+	} else {
+		++src->li.colno;
+	}
+	return ret;
 }
 
-static void fill_ident(prepare_t *src, string_t *buf) {
+static void fill_ident(prepare_t *src, string_t *buf, size_t *lineno_end, size_t *colno_end) {
 	while (1) {
-		int c = get_char(src);
-		if ((c == '_') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) {
-			string_add_char(buf, (char)c);
+		char_t c = get_char(src);
+		if ((c.c == '_') || ((c.c >= '0') && (c.c <= '9')) || ((c.c >= 'A') && (c.c <= 'Z')) || ((c.c >= 'a') && (c.c <= 'z'))) {
+			string_add_char(buf, (char)c.c);
+			*lineno_end = c.li.lineno;
+			*colno_end = c.li.colno;
 		} else {
 			unget_char(src, c);
 			return;
@@ -69,14 +129,16 @@ static void fill_ident(prepare_t *src, string_t *buf) {
 	}
 }
 
-static void fill_num(prepare_t *src, string_t *buf) {
+static void fill_num(prepare_t *src, string_t *buf, size_t *lineno_end, size_t *colno_end) {
 	int started_exp = 0;
 	while (1) {
-		int c = get_char(src);
-		if ((c == '_') || (c == '.') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
-		 || (started_exp && ((c == '+') || (c == '-')))) {
-			started_exp = (c == 'e') || (c == 'E') || (c == 'p') || (c == 'P');
-			string_add_char(buf, (char)c);
+		char_t c = get_char(src);
+		if ((c.c == '_') || (c.c == '.') || ((c.c >= '0') && (c.c <= '9')) || ((c.c >= 'A') && (c.c <= 'Z')) || ((c.c >= 'a') && (c.c <= 'z'))
+		 || (started_exp && ((c.c == '+') || (c.c == '-')))) {
+			started_exp = (c.c == 'e') || (c.c == 'E') || (c.c == 'p') || (c.c == 'P');
+			string_add_char(buf, (char)c.c);
+			*lineno_end = c.li.lineno;
+			*colno_end = c.li.colno;
 		} else {
 			unget_char(src, c);
 			return;
@@ -84,32 +146,42 @@ static void fill_num(prepare_t *src, string_t *buf) {
 	}
 }
 
-static void fill_str(prepare_t *src, string_t *buf, char end_c, int can_esc) {
+static void fill_str(prepare_t *src, string_t *buf, char end_c, int can_esc, size_t *lineno_end, size_t *colno_end) {
 	int has_esc = 0;
 	while (1) {
-		int c = get_char(src);
-		if (has_esc && (c >= 0) && (c <= 0x7F) && (c != '\n')) {
+		char_t c = get_char(src);
+		if (has_esc && (c.c >= 0) && (c.c <= 0x7F) && (c.c != '\n')) {
 			// Not technically standard compliant (should support \ooo, \x..., \u..., \U...)
 			// Since we don't really care about parsing the content, only the delimiters, this is good enough
 			string_add_char(buf, '\\');
-			string_add_char(buf, (char)c);
+			string_add_char(buf, (char)c.c);
 			has_esc = 0;
-		} else if (c == '\\') {
+			*lineno_end = c.li.lineno;
+			*colno_end = c.li.colno;
+		} else if (c.c == '\\') {
+			*lineno_end = c.li.lineno;
+			*colno_end = c.li.colno;
 			if (can_esc) {
 				has_esc = 1;
 			} else {
 				string_add_char(buf, '\\');
 			}
-		} else if ((c >= 0) && (c <= 0x7F) && (c != '\n') && (c != end_c)) {
+		} else if ((c.c >= 0) && (c.c <= 0x7F) && (c.c != '\n') && (c.c != end_c)) {
 			has_esc = 0;
-			string_add_char(buf, (char)c);
+			string_add_char(buf, (char)c.c);
+			*lineno_end = c.li.lineno;
+			*colno_end = c.li.colno;
 		} else {
+			// c.c is invalid (> 0x80) or a '\n', or can_esc = 0 and c.c = end_c
 			if (has_esc) {
-				// c is invalid or a '\n', or can_esc = 0 and c = end_c
 				string_add_char(buf, '\\');
 			}
-			if (c != end_c)
+			if (c.c != end_c) {
 				unget_char(src, c);
+			} else {
+				*lineno_end = c.li.lineno;
+				*colno_end = c.li.colno;
+			}
 			return;
 		}
 	}
@@ -157,42 +229,47 @@ static const struct symbs_s {
 preproc_token_t pre_next_token(prepare_t *src, int allow_comments) {
 	if (src->st == PREPST_COMMENT) {
 		// In comments, keep everything as 'BLANK' except for idents, newlines and EOF
-		int c = get_char(src);
-		if (c == EOF) {
+		char_t c = get_char(src);
+		if (c.c == EOF) {
 			// Force newline at EOF
 			unget_char(src, c);
 			src->st = PREPST_NL;
 			return (preproc_token_t){
 				.tokt = PPTOK_NEWLINE,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
-		} else if ((c == '_') || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
+		} else if ((c.c == '_') || ((c.c >= 'a') && (c.c <= 'z')) || ((c.c >= 'A') && (c.c <= 'Z'))) {
 			preproc_token_t ret;
 			ret.tokt = PPTOK_IDENT;
+			ret.loginfo = c.li;
 			ret.tokv.str = string_new_cap(1);
-			string_add_char(ret.tokv.str, (char)c);
-			fill_ident(src, ret.tokv.str);
+			string_add_char(ret.tokv.str, (char)c.c);
+			fill_ident(src, ret.tokv.str, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 			return ret;
-		} else if ((c >= 0) && (c <= 0x7F)) {
+		} else if ((c.c >= 0) && (c.c <= 0x7F)) {
 			return (preproc_token_t){
 				.tokt = PPTOK_BLANK,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
 		} else {
 			return (preproc_token_t){
 				.tokt = PPTOK_INVALID,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
 		}
 	}
 	
 start_next_token:
-	int c = get_char(src);
-	if (c == EOF) {
+	char_t c = get_char(src);
+	if (c.c == EOF) {
 		if (src->st == PREPST_NL) {
 			return (preproc_token_t){
 				.tokt = PPTOK_EOF,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
 		} else {
 			// Force newline at EOF
@@ -200,60 +277,67 @@ start_next_token:
 			src->st = PREPST_NL;
 			return (preproc_token_t){
 				.tokt = PPTOK_NEWLINE,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
 		}
 	}
 	
-	if (src->st == PREPST_INCL && (c == '<')) {
+	if (src->st == PREPST_INCL && (c.c == '<')) {
 		src->st = PREPST_NONE;
 		preproc_token_t ret;
 		ret.tokt = PPTOK_INCL;
+		ret.loginfo = c.li;
 		ret.tokv.sisstr = 0;
 		ret.tokv.sstr = string_new();
-		fill_str(src, ret.tokv.sstr, '>', 0);
+		fill_str(src, ret.tokv.sstr, '>', 0, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 		return ret;
 	}
-	if (c == '\'') {
+	if (c.c == '\'') {
 		src->st = PREPST_NONE;
 		preproc_token_t ret;
 		ret.tokt = PPTOK_STRING;
+		ret.loginfo = c.li;
 		ret.tokv.sisstr = 0;
 		ret.tokv.sstr = string_new_cap(1); // Usually only one character is inside a char literal
-		fill_str(src, ret.tokv.sstr, '\'', 1);
+		fill_str(src, ret.tokv.sstr, '\'', 1, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 		return ret;
 	}
-	if (c == '"') {
+	if (c.c == '"') {
 		preproc_token_t ret;
 		ret.tokt = (src->st == PREPST_INCL) ? PPTOK_INCL : PPTOK_STRING;
 		src->st = PREPST_NONE;
+		ret.loginfo = c.li;
 		ret.tokv.sisstr = 1;
 		ret.tokv.sstr = string_new();
-		fill_str(src, ret.tokv.sstr, '"', ret.tokt == PPTOK_STRING);
+		fill_str(src, ret.tokv.sstr, '"', ret.tokt == PPTOK_STRING, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 		return ret;
 	}
-	if ((c == ' ') || (c == '\f') || (c == '\t') || (c == '\v')) {
+	if ((c.c == ' ') || (c.c == '\f') || (c.c == '\t') || (c.c == '\v')) {
 		if (src->st == PREPST_DEFID) {
 			src->st = PREPST_NONE;
 			return (preproc_token_t){
 				.tokt = PPTOK_BLANK,
-				.tokv.c = (char)c
+				.loginfo = c.li,
+				.tokv.c = (char)c.c
 			};
 		} else goto start_next_token;
 	}
-	if (c == '\n') {
+	if (c.c == '\n') {
 		src->st = PREPST_NL;
 		return (preproc_token_t){
 			.tokt = PPTOK_NEWLINE,
-			.tokv.c = (char)c
+			.loginfo = c.li,
+			.tokv.c = (char)c.c
 		};
 	}
-	if ((c == '_') || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
+	if ((c.c == '_') || ((c.c >= 'a') && (c.c <= 'z')) || ((c.c >= 'A') && (c.c <= 'Z'))) {
 		preproc_token_t ret;
 		ret.tokt = PPTOK_IDENT;
+		ret.loginfo = c.li;
 		ret.tokv.str = string_new_cap(1);
-		string_add_char(ret.tokv.str, (char)c);
-		fill_ident(src, ret.tokv.str);
+		string_add_char(ret.tokv.str, (char)c.c);
+		fill_ident(src, ret.tokv.str, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 		src->st =
 			((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include"))) ? PREPST_INCL :
 			((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include_next"))) ? PREPST_INCL :
@@ -262,107 +346,131 @@ start_next_token:
 			PREPST_NONE;
 		return ret;
 	}
-	if ((c >= '0') && (c <= '9')) {
+	if ((c.c >= '0') && (c.c <= '9')) {
 		src->st = PREPST_NONE;
 		preproc_token_t ret;
 		ret.tokt = PPTOK_NUM;
+		ret.loginfo = c.li;
 		ret.tokv.str = string_new_cap(1);
-		string_add_char(ret.tokv.str, (char)c);
-		fill_num(src, ret.tokv.str);
+		string_add_char(ret.tokv.str, (char)c.c);
+		fill_num(src, ret.tokv.str, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 		return ret;
 	}
-	if (c == '.') {
+	if (c.c == '.') {
+		loginfo_t oldli = c.li;
 		c = get_char(src);
-		if ((c >= '0') && (c <= '9')) {
+		if ((c.c >= '0') && (c.c <= '9')) {
 			src->st = PREPST_NONE;
 			preproc_token_t ret;
 			ret.tokt = PPTOK_NUM;
+			ret.loginfo = oldli;
+			ret.loginfo.lineno_end = c.li.lineno;
+			ret.loginfo.colno_end = c.li.colno;
 			ret.tokv.str = string_new_cap(2);
 			string_add_char(ret.tokv.str, '.');
-			string_add_char(ret.tokv.str, (char)c);
-			fill_num(src, ret.tokv.str);
+			string_add_char(ret.tokv.str, (char)c.c);
+			fill_num(src, ret.tokv.str, &ret.loginfo.lineno_end, &ret.loginfo.colno_end);
 			return ret;
 		} else {
 			unget_char(src, c);
-			c = '.';
+			c.c = '.';
+			c.li = oldli;
 		}
 	}
-	if (c == '/') {
-		c = get_char(src);
-		if (c == '/') {
+	if (c.c == '/') {
+		char_t c2 = get_char(src);
+		if (c2.c == '/') {
 			if (allow_comments) {
 				src->st = PREPST_COMMENT;
 				return (preproc_token_t){
 					.tokt = PPTOK_START_LINE_COMMENT,
-					.tokv.c = '/'
+					.loginfo = {
+						.filename = c2.li.filename,
+						.lineno = c.li.lineno, .colno = c.li.colno,
+						.lineno_end = c2.li.lineno, .colno_end = c2.li.colno,
+					},
+					.tokv.c = '/',
 				};
 			}
 			
 			do {
-				c = get_char(src);
-			} while ((c != EOF) && (c != '\n'));
-			if (c != EOF) {
+				c2 = get_char(src);
+			} while ((c2.c != EOF) && (c2.c != '\n'));
+			if (c2.c != EOF) {
 				if (src->st == PREPST_NL)
 					goto start_next_token;
 				else {
 					src->st = PREPST_NL;
 					return (preproc_token_t){
 						.tokt = PPTOK_NEWLINE,
-						.tokv.c = (char)c
+						.loginfo = c2.li,
+						.tokv.c = (char)c2.c
 					};
 				}
 			}
 			
-			src->st = PREPST_NONE;
-			printf("Unfinished comment while preparing %s\n", src->srcn);
+			// Force newline at EOF
+			unget_char(src, c2);
+			src->st = PREPST_NL;
 			return (preproc_token_t){
-				.tokt = PPTOK_INVALID,
-				.tokv.c = (char)c
+				.tokt = PPTOK_NEWLINE,
+				.loginfo = c2.li,
+				.tokv.c = (char)c2.c
 			};
-		} else if (c == '*') {
+		} else if (c2.c == '*') {
 			if (allow_comments) {
-				printf("Unsupported multiline comment with allow_comment in %s\n", src->srcn);
+				c.li.lineno_end = c2.li.lineno;
+				c.li.colno_end = c2.li.colno;
+				log_error(&c.li, "unsupported multiline comment with allow_comment\n");
 				return (preproc_token_t){
 					.tokt = PPTOK_INVALID,
-					.tokv.c = (char)c
+					.loginfo = c.li,
+					.tokv.c = (char)c.c
 				};
 			}
 			
-			c = get_char(src);
+			c2 = get_char(src);
 			int last_star = 0;
-			while ((c != EOF) && (!last_star || (c != '/'))) {
-				last_star = c == '*';
-				c = get_char(src);
+			while ((c2.c != EOF) && (!last_star || (c2.c != '/'))) {
+				last_star = c2.c == '*';
+				c2 = get_char(src);
 			}
-			if (c != EOF) goto start_next_token;
+			if (c2.c != EOF) goto start_next_token;
 			
+			c.li.lineno_end = c2.li.lineno;
+			c.li.colno_end = c2.li.colno;
+			log_error(&c.li, "unfinished multiline comment\n");
 			src->st = PREPST_NONE;
-			printf("Unfinished comment while preparing %s\n", src->srcn);
 			return (preproc_token_t){
 				.tokt = PPTOK_INVALID,
-				.tokv.c = (char)c
+				.loginfo = c2.li,
+				.tokv.c = (char)c2.c
 			};
 		} else {
-			unget_char(src, c);
-			c = '/';
+			unget_char(src, c2);
 		}
 	}
 	
 	struct symbs_s const *sym = NULL;
 	for (int i = 0; i < BASE_NSYMS; ++i) {
-		if (c == symbs[i].c) {
+		if (c.c == symbs[i].c) {
 			sym = &symbs[i];
 			break;
 		}
 	}
 	if (sym) {
+		loginfo_t lisym = c.li;
+		loginfo_t liend = c.li;
 		while (sym->nnext) {
 			c = get_char(src);
 			int found = 0;
 			for (int i = 0; i < sym->nnext; ++i) {
-				if (c == sym->next[i].c) {
+				if (c.c == sym->next[i].c) {
 					found = 1;
 					sym = &sym->next[i];
+					liend = c.li;
+					lisym.lineno_end = c.li.lineno;
+					lisym.colno_end = c.li.colno;
 					break;
 				}
 			}
@@ -372,41 +480,66 @@ start_next_token:
 			}
 		}
 		if (sym->sym == LAST_SYM + 1) {
-			unget_char(src, sym->c);
-			sym = &symbs[0]; // This is where no check is made (see comment in the definition of symbs)
+			// This is where no check is made (see comment in the definition of symbs)
+			unget_char(src, (char_t){.c = sym->c, .li = liend});
+			sym = &symbs[0];
+			lisym.lineno_end = lisym.colno_end = 0;
 		}
 		src->st = ((src->st == PREPST_NL) && (sym->sym == SYM_HASH)) ? PREPST_HASH : PREPST_NONE;
 		return (preproc_token_t){
 			.tokt = PPTOK_SYM,
+			.loginfo = lisym,
 			.tokv.sym = sym->sym
 		};
 	}
 	
 	src->st = PREPST_NONE;
-	printf("Invalid character 0x%X (%c) while preparing %s\n", (unsigned)c, (c >= 0x20) && (c < 127) ? c : '?', src->srcn);
+	log_error(&c.li, "invalid character 0x%02X (%c)\n", (unsigned)c.c, (c.c >= 0x20) && (c.c < 0x7F) ? c.c : '?');
 	return (preproc_token_t){
 		.tokt = PPTOK_INVALID,
-		.tokv.c = (char)c
+		.loginfo = c.li,
+		.tokv.c = (char)c.c,
 	};
 }
 
+void prepare_set_line(prepare_t *src, char *filename, size_t lineno) {
+	if (filename) {
+		if (src->srcn) free(filename);
+		src->srcn = filename;
+		src->li.filename = filename;
+	}
+	size_t colno = 1;
+	for (int i = src->buf_len; i--; ) {
+		src->buf[i].li.lineno = lineno;
+		src->buf[i].li.colno = colno;
+		if (src->buf[i].c == '\n') {
+			++lineno;
+			colno = 1;
+		} else {
+			++colno;
+		}
+	}
+	src->li.lineno = lineno;
+	src->li.colno = colno;
+}
+
 // Warning: unsafe method
 void prepare_mark_nocomment(prepare_t *src) {
 	src->st = PREPST_NONE;
 }
 int pre_next_newline_token(prepare_t *src, string_t *buf) {
 	while (1) {
-		int c = get_char(src);
-		if (c == EOF) {
+		char_t c = get_char(src);
+		if (c.c == EOF) {
 			// Force newline at EOF
 			unget_char(src, c);
 			src->st = PREPST_NL;
 			return 1;
-		} else if (c == '\n') {
+		} else if (c.c == '\n') {
 			src->st = PREPST_NL;
 			return 1;
-		} else if ((c >= 0) && (c <= 0x7F)) {
-			if (!string_add_char(buf, (char)c)) return 0;
+		} else if ((c.c >= 0) && (c.c <= 0x7F)) {
+			if (!string_add_char(buf, (char)c.c)) return 0;
 		} else {
 			return 0;
 		}
diff --git a/wrapperhelper/src/prepare.h b/wrapperhelper/src/prepare.h
index c8424261..9192fc78 100644
--- a/wrapperhelper/src/prepare.h
+++ b/wrapperhelper/src/prepare.h
@@ -14,6 +14,8 @@ prepare_t *prepare_new_file(FILE *f, const char *filename); // Takes ownership o
 void prepare_del(prepare_t *src);
 preproc_token_t pre_next_token(prepare_t *src, int allow_comments);
 
+void prepare_set_line(prepare_t *src, char *filename, size_t lineno); // Takes ownership of filename if != NULL
+
 void prepare_mark_nocomment(prepare_t *src); // Change the state (usually from COMMENT) to NONE
 int pre_next_newline_token(prepare_t *src, string_t *buf); // In a comment append everything until the EOL or EOF to the buffer
 
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;