+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_UNARY,
+ [EXPR_UNARY_POSTFIX_DECREMENT] = PREC_UNARY,
+ [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_BUILTIN_EXPECT] = PREC_PRIMARY,
+ [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: suffix = NULL; break;
+ }
+ if (suffix != NULL)
+ fputs(suffix, out);
+}