+static FILE *out;
+static int indent;
+
+/** If set, implicit casts are printed. */
+bool print_implicit_casts = false;
+
+/** If set parenthesis are printed to indicate operator precedence. */
+bool print_parenthesis = false;
+
+static void print_statement(const statement_t *statement);
+static void print_expression_prec(const expression_t *expression, unsigned prec);
+
+void change_indent(int delta)
+{
+ indent += delta;
+ assert(indent >= 0);
+}
+
+void print_indent(void)
+{
+ for (int i = 0; i < indent; ++i)
+ fputc('\t', out);
+}
+
+/**
+ * Returns 1 if a given precedence level has right-to-left
+ * associativity, else 0.
+ *
+ * @param precedence the operator precedence
+ */
+static int right_to_left(unsigned precedence)
+{
+ switch (precedence) {
+ case PREC_ASSIGNMENT:
+ case PREC_CONDITIONAL:
+ case PREC_UNARY:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Return the precedence of an expression given by its kind.
+ *
+ * @param kind the expression kind
+ */
+static unsigned get_expression_precedence(expression_kind_t kind)
+{
+ static const unsigned prec[] = {
+ [EXPR_UNKNOWN] = PREC_PRIMARY,
+ [EXPR_INVALID] = PREC_PRIMARY,
+ [EXPR_REFERENCE] = PREC_PRIMARY,
+ [EXPR_REFERENCE_ENUM_VALUE] = PREC_PRIMARY,
+ [EXPR_CHARACTER_CONSTANT] = PREC_PRIMARY,
+ [EXPR_WIDE_CHARACTER_CONSTANT] = PREC_PRIMARY,
+ [EXPR_CONST] = PREC_PRIMARY,
+ [EXPR_STRING_LITERAL] = PREC_PRIMARY,
+ [EXPR_WIDE_STRING_LITERAL] = PREC_PRIMARY,
+ [EXPR_COMPOUND_LITERAL] = PREC_UNARY,
+ [EXPR_CALL] = PREC_POSTFIX,
+ [EXPR_CONDITIONAL] = PREC_CONDITIONAL,
+ [EXPR_SELECT] = PREC_POSTFIX,
+ [EXPR_ARRAY_ACCESS] = PREC_POSTFIX,
+ [EXPR_SIZEOF] = PREC_UNARY,
+ [EXPR_CLASSIFY_TYPE] = PREC_UNARY,
+ [EXPR_ALIGNOF] = PREC_UNARY,
+
+ [EXPR_FUNCNAME] = PREC_PRIMARY,
+ [EXPR_BUILTIN_SYMBOL] = PREC_PRIMARY,
+ [EXPR_BUILTIN_CONSTANT_P] = PREC_PRIMARY,
+ [EXPR_BUILTIN_PREFETCH] = PREC_PRIMARY,
+ [EXPR_OFFSETOF] = PREC_PRIMARY,
+ [EXPR_VA_START] = PREC_PRIMARY,
+ [EXPR_VA_ARG] = PREC_PRIMARY,
+ [EXPR_STATEMENT] = PREC_PRIMARY,
+ [EXPR_LABEL_ADDRESS] = PREC_PRIMARY,
+
+ [EXPR_UNARY_NEGATE] = PREC_UNARY,
+ [EXPR_UNARY_PLUS] = PREC_UNARY,
+ [EXPR_UNARY_BITWISE_NEGATE] = PREC_UNARY,
+ [EXPR_UNARY_NOT] = PREC_UNARY,
+ [EXPR_UNARY_DEREFERENCE] = PREC_UNARY,
+ [EXPR_UNARY_TAKE_ADDRESS] = PREC_UNARY,
+ [EXPR_UNARY_POSTFIX_INCREMENT] = PREC_POSTFIX,
+ [EXPR_UNARY_POSTFIX_DECREMENT] = PREC_POSTFIX,
+ [EXPR_UNARY_PREFIX_INCREMENT] = PREC_UNARY,
+ [EXPR_UNARY_PREFIX_DECREMENT] = PREC_UNARY,
+ [EXPR_UNARY_CAST] = PREC_UNARY,
+ [EXPR_UNARY_CAST_IMPLICIT] = PREC_UNARY,
+ [EXPR_UNARY_ASSUME] = PREC_PRIMARY,
+ [EXPR_UNARY_DELETE] = PREC_UNARY,
+ [EXPR_UNARY_DELETE_ARRAY] = PREC_UNARY,
+ [EXPR_UNARY_THROW] = PREC_ASSIGNMENT,
+
+ [EXPR_BINARY_ADD] = PREC_ADDITIVE,
+ [EXPR_BINARY_SUB] = PREC_ADDITIVE,
+ [EXPR_BINARY_MUL] = PREC_MULTIPLICATIVE,
+ [EXPR_BINARY_DIV] = PREC_MULTIPLICATIVE,
+ [EXPR_BINARY_MOD] = PREC_MULTIPLICATIVE,
+ [EXPR_BINARY_EQUAL] = PREC_EQUALITY,
+ [EXPR_BINARY_NOTEQUAL] = PREC_EQUALITY,
+ [EXPR_BINARY_LESS] = PREC_RELATIONAL,
+ [EXPR_BINARY_LESSEQUAL] = PREC_RELATIONAL,
+ [EXPR_BINARY_GREATER] = PREC_RELATIONAL,
+ [EXPR_BINARY_GREATEREQUAL] = PREC_RELATIONAL,
+ [EXPR_BINARY_BITWISE_AND] = PREC_AND,
+ [EXPR_BINARY_BITWISE_OR] = PREC_OR,
+ [EXPR_BINARY_BITWISE_XOR] = PREC_XOR,
+ [EXPR_BINARY_LOGICAL_AND] = PREC_LOGICAL_AND,
+ [EXPR_BINARY_LOGICAL_OR] = PREC_LOGICAL_OR,
+ [EXPR_BINARY_SHIFTLEFT] = PREC_SHIFT,
+ [EXPR_BINARY_SHIFTRIGHT] = PREC_SHIFT,
+ [EXPR_BINARY_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_MUL_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_DIV_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_MOD_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_ADD_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_SUB_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_SHIFTLEFT_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_SHIFTRIGHT_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_BITWISE_AND_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_BITWISE_XOR_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_BITWISE_OR_ASSIGN] = PREC_ASSIGNMENT,
+ [EXPR_BINARY_COMMA] = PREC_EXPRESSION,
+
+ [EXPR_BINARY_ISGREATER] = PREC_PRIMARY,
+ [EXPR_BINARY_ISGREATEREQUAL] = PREC_PRIMARY,
+ [EXPR_BINARY_ISLESS] = PREC_PRIMARY,
+ [EXPR_BINARY_ISLESSEQUAL] = PREC_PRIMARY,
+ [EXPR_BINARY_ISLESSGREATER] = PREC_PRIMARY,
+ [EXPR_BINARY_ISUNORDERED] = PREC_PRIMARY
+ };
+ assert((unsigned)kind < (sizeof(prec)/sizeof(prec[0])));
+ unsigned res = prec[kind];
+
+ assert(res != PREC_BOTTOM);
+ return res;
+}
+
+/**
+ * Print a constant expression.
+ *
+ * @param cnst the constant expression
+ */
+static void print_const(const const_expression_t *cnst)
+{
+ if (cnst->base.type == NULL)
+ return;
+
+ const type_t *const type = skip_typeref(cnst->base.type);
+
+ if (is_type_atomic(type, ATOMIC_TYPE_BOOL)) {
+ fputs(cnst->v.int_value ? "true" : "false", out);
+ } else if (is_type_integer(type)) {
+ fprintf(out, "%lld", cnst->v.int_value);
+ } else if (is_type_float(type)) {
+ long double const val = cnst->v.float_value;
+#ifdef _WIN32
+ /* ARG, no way to print long double */
+ fprintf(out, "%.20g", (double)val);
+#else
+ fprintf(out, "%.20Lg", val);
+#endif
+ if (isfinite(val) && truncl(val) == val)
+ fputs(".0", out);
+ } else {
+ panic("unknown constant");
+ }
+
+ char const* suffix;
+ switch (type->atomic.akind) {
+ case ATOMIC_TYPE_UINT: suffix = "U"; break;
+ case ATOMIC_TYPE_LONG: suffix = "L"; break;
+ case ATOMIC_TYPE_ULONG: suffix = "UL"; break;
+ case ATOMIC_TYPE_LONGLONG: suffix = "LL"; break;
+ case ATOMIC_TYPE_ULONGLONG: suffix = "ULL"; break;
+ case ATOMIC_TYPE_FLOAT: suffix = "F"; break;
+ case ATOMIC_TYPE_LONG_DOUBLE: suffix = "L"; break;
+
+ default: return;
+ }
+ fputs(suffix, out);
+}
+
+/**
+ * Print a quoted string constant.
+ *
+ * @param string the string constant
+ * @param border the border char
+ * @param skip number of chars to skip at the end
+ */
+static void print_quoted_string(const string_t *const string, char border, int skip)
+{
+ fputc(border, out);
+ const char *end = string->begin + string->size - skip;
+ for (const char *c = string->begin; c != end; ++c) {
+ unsigned char const tc = *c;
+ if (tc == border) {
+ fputc('\\', out);
+ }
+ switch (tc) {
+ case '\\': fputs("\\\\", out); break;
+ case '\a': fputs("\\a", out); break;
+ case '\b': fputs("\\b", out); break;
+ case '\f': fputs("\\f", out); break;
+ case '\n': fputs("\\n", out); break;
+ case '\r': fputs("\\r", out); break;
+ case '\t': fputs("\\t", out); break;
+ case '\v': fputs("\\v", out); break;
+ case '\?': fputs("\\?", out); break;
+ case 27:
+ if (c_mode & _GNUC) {
+ fputs("\\e", out); break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (tc < 0x80 && !isprint(tc)) {
+ fprintf(out, "\\%03o", (unsigned)tc);
+ } else {
+ fputc(tc, out);
+ }
+ break;
+ }
+ }
+ fputc(border, out);
+}
+
+/**
+ * Prints a wide string literal expression.
+ *
+ * @param wstr the wide string literal expression
+ * @param border the border char
+ * @param skip number of chars to skip at the end
+ */
+static void print_quoted_wide_string(const wide_string_t *const wstr,
+ char border, int skip)