+ if(declaration == NULL) {
+#ifndef STRICT_C99
+ /* an implicitely defined function */
+ if(token.type == '(') {
+ parser_print_prefix_pos(token.source_position);
+ fprintf(stderr, "warning: implicit declaration of function '%s'\n",
+ ref->symbol->string);
+
+ declaration = create_implicit_function(ref->symbol,
+ source_position);
+ } else
+#endif
+ {
+ parser_print_error_prefix();
+ fprintf(stderr, "unknown symbol '%s' found.\n", ref->symbol->string);
+ return (expression_t*) ref;
+ }
+ }
+
+ ref->declaration = declaration;
+ ref->expression.datatype = declaration->type;
+
+ return (expression_t*) ref;
+}
+
+static void check_cast_allowed(expression_t *expression, type_t *dest_type)
+{
+ (void) expression;
+ (void) dest_type;
+ /* TODO check if explicit cast is allowed and issue warnings/errors */
+}
+
+static expression_t *parse_cast(void)
+{
+ unary_expression_t *cast = allocate_ast_zero(sizeof(cast[0]));
+
+ cast->expression.type = EXPR_UNARY;
+ cast->type = UNEXPR_CAST;
+ cast->expression.source_position = token.source_position;
+
+ type_t *type = parse_typename();
+
+ expect(')');
+ expression_t *value = parse_sub_expression(20);
+
+ check_cast_allowed(value, type);
+
+ cast->expression.datatype = type;
+ cast->value = value;
+
+ return (expression_t*) cast;
+}
+
+static expression_t *parse_statement_expression(void)
+{
+ statement_expression_t *expression
+ = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_STATEMENT;
+
+ statement_t *statement = parse_compound_statement();
+ expression->statement = statement;
+ if(statement == NULL) {
+ expect(')');
+ return NULL;
+ }
+
+ assert(statement->type == STATEMENT_COMPOUND);
+ compound_statement_t *compound_statement
+ = (compound_statement_t*) statement;
+
+ /* find last statement and use it's type */
+ const statement_t *last_statement = NULL;
+ const statement_t *iter = compound_statement->statements;
+ for( ; iter != NULL; iter = iter->next) {
+ last_statement = iter;
+ }
+
+ if(last_statement->type == STATEMENT_EXPRESSION) {
+ const expression_statement_t *expression_statement =
+ (const expression_statement_t*) last_statement;
+ expression->expression.datatype
+ = expression_statement->expression->datatype;
+ } else {
+ expression->expression.datatype = type_void;
+ }
+
+ expect(')');
+
+ return (expression_t*) expression;
+}
+
+static expression_t *parse_brace_expression(void)
+{
+ eat('(');
+
+ switch(token.type) {
+ case '{':
+ /* gcc extension: a stement expression */
+ return parse_statement_expression();
+
+ TYPE_QUALIFIERS
+ TYPE_SPECIFIERS
+ return parse_cast();
+ case T_IDENTIFIER:
+ if(is_typedef_symbol(token.v.symbol)) {
+ return parse_cast();
+ }
+ }
+
+ expression_t *result = parse_expression();
+ expect(')');
+
+ return result;
+}
+
+static expression_t *parse_function_keyword(void)
+{
+ eat(T___FUNCTION__);
+ /* TODO */
+
+ string_literal_t *expression = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_FUNCTION;
+ expression->expression.datatype = type_string;
+ expression->value = "TODO: FUNCTION";
+
+ return (expression_t*) expression;
+}
+
+static expression_t *parse_pretty_function_keyword(void)
+{
+ eat(T___PRETTY_FUNCTION__);
+ /* TODO */
+
+ string_literal_t *expression = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_PRETTY_FUNCTION;
+ expression->expression.datatype = type_string;
+ expression->value = "TODO: PRETTY FUNCTION";
+
+ return (expression_t*) expression;
+}
+
+static designator_t *parse_designator(void)
+{
+ designator_t *result = allocate_ast_zero(sizeof(result[0]));
+
+ if(token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing member designator",
+ T_IDENTIFIER, 0);
+ eat_brace();
+ return NULL;
+ }
+ result->symbol = token.v.symbol;
+ next_token();
+
+ designator_t *last_designator = result;
+ while(true) {
+ if(token.type == '.') {
+ next_token();
+ if(token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing member designator",
+ T_IDENTIFIER, 0);
+ eat_brace();
+ return NULL;
+ }
+ designator_t *designator = allocate_ast_zero(sizeof(result[0]));
+ designator->symbol = token.v.symbol;
+ next_token();
+
+ last_designator->next = designator;
+ last_designator = designator;
+ continue;
+ }
+ if(token.type == '[') {
+ next_token();
+ designator_t *designator = allocate_ast_zero(sizeof(result[0]));
+ designator->array_access = parse_expression();
+ if(designator->array_access == NULL) {
+ eat_brace();
+ return NULL;
+ }
+ expect(']');
+
+ last_designator->next = designator;
+ last_designator = designator;
+ continue;
+ }
+ break;
+ }
+
+ return result;
+}
+
+static expression_t *parse_offsetof(void)
+{
+ eat(T___builtin_offsetof);
+
+ offsetof_expression_t *expression
+ = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_OFFSETOF;
+ expression->expression.datatype = type_size_t;
+
+ expect('(');
+ expression->type = parse_typename();
+ expect(',');
+ expression->designator = parse_designator();
+ expect(')');
+
+ return (expression_t*) expression;
+}
+
+static expression_t *parse_va_arg(void)
+{
+ eat(T___builtin_va_arg);
+
+ va_arg_expression_t *expression = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_VA_ARG;
+
+ expect('(');
+ expression->arg = parse_assignment_expression();
+ expect(',');
+ expression->expression.datatype = parse_typename();
+ expect(')');
+
+ return (expression_t*) expression;
+}
+
+static expression_t *parse_builtin_symbol(void)
+{
+ builtin_symbol_expression_t *expression
+ = allocate_ast_zero(sizeof(expression[0]));
+ expression->expression.type = EXPR_BUILTIN_SYMBOL;
+
+ /* TODO: set datatype */
+
+ expression->symbol = token.v.symbol;
+
+ next_token();
+
+ return (expression_t*) expression;
+}
+
+static expression_t *parse_primary_expression(void)
+{
+ switch(token.type) {
+ case T_INTEGER:
+ return parse_int_const();
+ case T_FLOATINGPOINT:
+ return parse_float_const();
+ case T_STRING_LITERAL:
+ return parse_string_const();
+ case T_IDENTIFIER:
+ return parse_reference();
+ case T___FUNCTION__:
+ return parse_function_keyword();
+ case T___PRETTY_FUNCTION__:
+ return parse_pretty_function_keyword();
+ case T___builtin_offsetof:
+ return parse_offsetof();
+ case T___builtin_va_arg:
+ return parse_va_arg();
+ case T___builtin_expect:
+ case T___builtin_va_start:
+ case T___builtin_va_end:
+ return parse_builtin_symbol();
+
+ case '(':
+ return parse_brace_expression();
+ }
+
+ parser_print_error_prefix();
+ fprintf(stderr, "unexpected token ");
+ print_token(stderr, &token);
+ fprintf(stderr, "\n");
+ eat_statement();
+
+ return make_invalid_expression();
+}
+
+static expression_t *parse_array_expression(unsigned precedence,
+ expression_t *array_ref)
+{
+ (void) precedence;
+
+ eat('[');
+
+ array_access_expression_t *array_access
+ = allocate_ast_zero(sizeof(array_access[0]));
+
+ array_access->expression.type = EXPR_ARRAY_ACCESS;
+ array_access->array_ref = array_ref;
+ array_access->index = parse_expression();
+
+ type_t *type = array_ref->datatype;
+ if(type != NULL) {
+ if(type->type == TYPE_POINTER) {
+ pointer_type_t *pointer = (pointer_type_t*) type;
+ array_access->expression.datatype = pointer->points_to;
+ } else if(type->type == TYPE_ARRAY) {
+ array_type_t *array_type = (array_type_t*) type;
+ array_access->expression.datatype = array_type->element_type;
+ } else {
+ parser_print_error_prefix();
+ fprintf(stderr, "array access on object with non-pointer type ");
+ print_type_quoted(type);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if(token.type != ']') {
+ parse_error_expected("Problem while parsing array access", ']', 0);
+ return (expression_t*) array_access;
+ }
+ next_token();
+
+ return (expression_t*) array_access;
+}
+
+static bool is_declaration_specifier(const token_t *token,
+ bool only_type_specifiers)
+{
+ switch(token->type) {
+ TYPE_SPECIFIERS
+ return 1;
+ case T_IDENTIFIER:
+ return is_typedef_symbol(token->v.symbol);
+ STORAGE_CLASSES
+ TYPE_QUALIFIERS
+ if(only_type_specifiers)
+ return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static expression_t *parse_sizeof(unsigned precedence)
+{
+ eat(T_sizeof);
+
+ sizeof_expression_t *sizeof_expression
+ = allocate_ast_zero(sizeof(sizeof_expression[0]));
+ sizeof_expression->expression.type = EXPR_SIZEOF;
+ sizeof_expression->expression.datatype = type_size_t;
+
+ if(token.type == '(' && is_declaration_specifier(look_ahead(1), true)) {
+ next_token();
+ sizeof_expression->type = parse_typename();
+ expect(')');
+ } else {
+ expression_t *expression = parse_sub_expression(precedence);
+ sizeof_expression->type = expression->datatype;
+ sizeof_expression->size_expression = expression;
+ }
+
+ return (expression_t*) sizeof_expression;
+}
+
+static expression_t *parse_select_expression(unsigned precedence,
+ expression_t *compound)
+{
+ (void) precedence;
+ assert(token.type == '.' || token.type == T_MINUSGREATER);
+
+ bool is_pointer = (token.type == T_MINUSGREATER);
+ next_token();
+
+ select_expression_t *select = allocate_ast_zero(sizeof(select[0]));
+
+ select->expression.type = EXPR_SELECT;
+ select->compound = compound;
+
+ if(token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing select", T_IDENTIFIER, 0);
+ return (expression_t*) select;
+ }
+ symbol_t *symbol = token.v.symbol;
+ select->symbol = symbol;
+ next_token();
+
+ type_t *type = compound->datatype;
+ if(type == NULL)
+ return make_invalid_expression();
+
+ type_t *type_left = type;
+ if(is_pointer) {
+ if(type->type != TYPE_POINTER) {
+ parser_print_error_prefix();
+ fprintf(stderr, "left hand side of '->' is not a pointer, but ");
+ print_type_quoted(type);
+ fputc('\n', stderr);
+ return make_invalid_expression();
+ }
+ pointer_type_t *pointer_type = (pointer_type_t*) type;
+ type_left = pointer_type->points_to;
+ }
+ type_left = skip_typeref(type_left);
+
+ if(type_left->type != TYPE_COMPOUND_STRUCT
+ && type_left->type != TYPE_COMPOUND_UNION) {
+ parser_print_error_prefix();
+ fprintf(stderr, "request for member '%s' in something not a struct or "
+ "union, but ", symbol->string);
+ print_type_quoted(type_left);
+ fputc('\n', stderr);
+ return make_invalid_expression();
+ }
+
+ compound_type_t *compound_type = (compound_type_t*) type_left;
+ declaration_t *declaration = compound_type->declaration;
+
+ if(!declaration->init.is_defined) {
+ parser_print_error_prefix();
+ fprintf(stderr, "request for member '%s' of incomplete type ",
+ symbol->string);
+ print_type_quoted(type_left);
+ fputc('\n', stderr);
+ return make_invalid_expression();
+ }
+
+ declaration_t *iter = declaration->context.declarations;
+ for( ; iter != NULL; iter = iter->next) {
+ if(iter->symbol == symbol) {
+ break;
+ }
+ }
+ if(iter == NULL) {
+ parser_print_error_prefix();
+ print_type_quoted(type_left);
+ fprintf(stderr, " has no memeber named '%s'\n", symbol->string);
+ return make_invalid_expression();
+ }
+
+ select->compound_entry = iter;
+ select->expression.datatype = iter->type;
+ return (expression_t*) select;
+}
+
+static expression_t *parse_call_expression(unsigned precedence,
+ expression_t *expression)
+{
+ (void) precedence;
+ call_expression_t *call = allocate_ast_zero(sizeof(call[0]));
+ call->expression.type = EXPR_CALL;
+ call->function = expression;
+
+ function_type_t *function_type;
+ type_t *type = expression->datatype;
+ if(type->type != TYPE_FUNCTION) {
+ /* TODO calling pointers to functions is ok */
+ parser_print_error_prefix();
+ fputs("called object '", stderr);
+ print_expression(expression);
+ fputs("' (type ", stderr);
+ print_type_quoted(type);
+ fputs("is not a function\n", stderr);
+
+ function_type = NULL;
+ call->expression.datatype = NULL;
+ } else {
+ function_type = (function_type_t*) type;
+ call->expression.datatype = function_type->result_type;
+ }
+
+ /* parse arguments */
+ eat('(');
+
+ if(token.type != ')') {
+ call_argument_t *last_argument = NULL;
+
+ while(true) {
+ call_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
+
+ argument->expression = parse_assignment_expression();
+ if(last_argument == NULL) {
+ call->arguments = argument;
+ } else {
+ last_argument->next = argument;
+ }
+ last_argument = argument;
+
+ if(token.type != ',')
+ break;
+ next_token();
+ }
+ }
+ expect(')');
+
+ if(function_type != NULL) {
+ function_parameter_t *parameter = function_type->parameters;
+ call_argument_t *argument = call->arguments;
+ for( ; parameter != NULL && argument != NULL;
+ parameter = parameter->next, argument = argument->next) {
+ type_t *expected_type = parameter->type;
+ /* TODO report context in error messages */
+ argument->expression = create_implicit_cast(argument->expression,
+ expected_type);
+ }
+ /* too few parameters */
+ if(parameter != NULL) {
+ parser_print_error_prefix();
+ fprintf(stderr, "too few arguments to function '");
+ print_expression(expression);
+ fprintf(stderr, "'\n");
+ } else if(argument != NULL) {
+ /* too many parameters */
+ if(!function_type->variadic
+ && !function_type->unspecified_parameters) {
+ parser_print_error_prefix();
+ fprintf(stderr, "too many arguments to function '");
+ print_expression(expression);
+ fprintf(stderr, "'\n");
+ } else {
+ /* do default promotion */
+ for( ; argument != NULL; argument = argument->next) {
+ type_t *type = argument->expression->datatype;
+
+ if(type == NULL)
+ continue;
+
+ if(is_type_integer(type)) {
+ type = promote_integer(type);
+ } else if(type == type_float) {
+ type = type_double;
+ }
+ argument->expression
+ = create_implicit_cast(argument->expression, type);
+ }
+ }
+ }
+ }
+
+ return (expression_t*) call;
+}
+
+static type_t *get_type_after_conversion(const type_t *type1,
+ const type_t *type2)
+{
+ /* TODO... */
+ (void) type2;
+ return (type_t*) type1;
+}
+
+static expression_t *parse_conditional_expression(unsigned precedence,
+ expression_t *expression)
+{
+ eat('?');
+
+ conditional_expression_t *conditional
+ = allocate_ast_zero(sizeof(conditional[0]));
+ conditional->expression.type = EXPR_CONDITIONAL;
+ conditional->condition = expression;
+
+ /* 6.5.15.2 */
+ type_t *condition_type = conditional->condition->datatype;
+ if(condition_type != NULL) {
+ if(!is_type_scalar(skip_typeref(condition_type))) {
+ type_error("expected a scalar type", expression->source_position,
+ condition_type);
+ }
+ }
+
+ conditional->true_expression = parse_expression();
+ expect(':');
+ conditional->false_expression = parse_sub_expression(precedence);
+
+ type_t *true_type = conditional->true_expression->datatype;
+ if(true_type == NULL)
+ return (expression_t*) conditional;
+ type_t *false_type = conditional->false_expression->datatype;
+ if(false_type == NULL)
+ return (expression_t*) conditional;
+
+ type_t *const skipped_true_type = skip_typeref(true_type);
+ type_t *const skipped_false_type = skip_typeref(false_type);
+
+ /* 6.5.15.3 */
+ if (skipped_true_type == skipped_false_type) {
+ conditional->expression.datatype = skipped_true_type;
+ } else if (is_type_arithmetic(skipped_true_type) &&
+ is_type_arithmetic(skipped_false_type)) {
+ type_t *const result = get_type_after_conversion(skipped_true_type,
+ skipped_false_type);
+ /* TODO: create implicit convs if necessary */
+ conditional->expression.datatype = result;
+ } else if (skipped_true_type->type == TYPE_POINTER &&
+ skipped_false_type->type == TYPE_POINTER &&
+ true /* TODO compatible points_to types */) {
+ /* TODO */
+ } else if(/* (is_null_ptr_const(skipped_true_type) &&
+ skipped_false_type->type == TYPE_POINTER)
+ || (is_null_ptr_const(skipped_false_type) &&
+ skipped_true_type->type == TYPE_POINTER) TODO*/ false) {
+ /* TODO */
+ } else if(/* 1 is pointer to object type, other is void* */ false) {
+ /* TODO */
+ } else {
+ type_error_incompatible("while parsing conditional",
+ expression->source_position, true_type,
+ skipped_false_type);
+ }
+
+ return (expression_t*) conditional;
+}
+
+static expression_t *parse_extension(unsigned precedence)
+{
+ eat(T___extension__);
+
+ /* TODO enable extensions */
+
+ return parse_sub_expression(precedence);
+}
+
+static void semantic_incdec(unary_expression_t *expression)
+{
+ type_t *orig_type = expression->value->datatype;
+ if(orig_type == NULL)
+ return;
+
+ type_t *type = skip_typeref(orig_type);
+ if(!is_type_arithmetic(type) && type->type != TYPE_POINTER) {
+ /* TODO: improve error message */
+ parser_print_error_prefix();
+ fprintf(stderr, "operation needs an arithmetic or pointer type\n");
+ return;
+ }
+
+ expression->expression.datatype = orig_type;
+}
+
+static void semantic_unexpr_arithmetic(unary_expression_t *expression)
+{
+ type_t *orig_type = expression->value->datatype;
+ if(orig_type == NULL)
+ return;
+
+ type_t *type = skip_typeref(orig_type);
+ if(!is_type_arithmetic(type)) {
+ /* TODO: improve error message */
+ parser_print_error_prefix();
+ fprintf(stderr, "operation needs an arithmetic type\n");
+ return;
+ }
+
+ expression->expression.datatype = orig_type;
+}
+
+static void semantic_dereference(unary_expression_t *expression)
+{
+ type_t *orig_type = expression->value->datatype;
+ if(orig_type == NULL)
+ return;
+
+ type_t *type = skip_typeref(orig_type);
+ if(type->type != TYPE_POINTER) {
+ /* TODO: improve error message */
+ parser_print_error_prefix();
+ fprintf(stderr, "operation needs a pointer type\n");
+ return;
+ }
+
+ pointer_type_t *pointer_type = (pointer_type_t*) type;
+ expression->expression.datatype = pointer_type->points_to;
+}
+
+static void semantic_take_addr(unary_expression_t *expression)
+{
+ type_t *orig_type = expression->value->datatype;
+ if(orig_type == NULL)
+ return;
+
+ expression_t *value = expression->value;
+ if(value->type == EXPR_REFERENCE) {
+ reference_expression_t *reference = (reference_expression_t*) value;
+ declaration_t *declaration = reference->declaration;
+ if(declaration != NULL) {
+ declaration->address_taken = 1;
+ }
+ }
+
+ expression->expression.datatype = make_pointer_type(orig_type, 0);
+}
+
+#define CREATE_UNARY_EXPRESSION_PARSER(token_type, unexpression_type, sfunc) \
+static expression_t *parse_##unexpression_type(unsigned precedence) \
+{ \
+ eat(token_type); \
+ \
+ unary_expression_t *unary_expression \
+ = allocate_ast_zero(sizeof(unary_expression[0])); \
+ unary_expression->expression.type = EXPR_UNARY; \
+ unary_expression->type = unexpression_type; \
+ unary_expression->value = parse_sub_expression(precedence); \
+ \
+ sfunc(unary_expression); \
+ \
+ return (expression_t*) unary_expression; \
+}
+
+CREATE_UNARY_EXPRESSION_PARSER('-', UNEXPR_NEGATE, semantic_unexpr_arithmetic)
+CREATE_UNARY_EXPRESSION_PARSER('+', UNEXPR_PLUS, semantic_unexpr_arithmetic)
+CREATE_UNARY_EXPRESSION_PARSER('!', UNEXPR_NOT, semantic_unexpr_arithmetic)
+CREATE_UNARY_EXPRESSION_PARSER('*', UNEXPR_DEREFERENCE, semantic_dereference)
+CREATE_UNARY_EXPRESSION_PARSER('&', UNEXPR_TAKE_ADDRESS, semantic_take_addr)
+CREATE_UNARY_EXPRESSION_PARSER('~', UNEXPR_BITWISE_NEGATE,
+ semantic_unexpr_arithmetic)
+CREATE_UNARY_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_PREFIX_INCREMENT,
+ semantic_incdec)
+CREATE_UNARY_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_PREFIX_DECREMENT,
+ semantic_incdec)
+
+#define CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(token_type, unexpression_type, \
+ sfunc) \
+static expression_t *parse_##unexpression_type(unsigned precedence, \
+ expression_t *left) \
+{ \
+ (void) precedence; \
+ eat(token_type); \
+ \
+ unary_expression_t *unary_expression \
+ = allocate_ast_zero(sizeof(unary_expression[0])); \
+ unary_expression->expression.type = EXPR_UNARY; \
+ unary_expression->type = unexpression_type; \
+ unary_expression->value = left; \
+ \
+ sfunc(unary_expression); \
+ \
+ return (expression_t*) unary_expression; \
+}
+
+CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_PLUSPLUS, UNEXPR_POSTFIX_INCREMENT,
+ semantic_incdec)
+CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(T_MINUSMINUS, UNEXPR_POSTFIX_DECREMENT,
+ semantic_incdec)
+
+static type_t *semantic_arithmetic(type_t *type_left, type_t *type_right)
+{
+ /* TODO: handle complex + imaginary types */
+
+ /* § 6.3.1.8 Usual arithmetic conversions */
+ if(type_left == type_long_double || type_right == type_long_double) {
+ return type_long_double;
+ } else if(type_left == type_double || type_right == type_double) {
+ return type_double;
+ } else if(type_left == type_float || type_right == type_float) {
+ return type_float;
+ }
+
+ type_right = promote_integer(type_right);
+ type_left = promote_integer(type_left);
+
+ if(type_left == type_right)
+ return type_left;
+
+ bool signed_left = is_type_signed(type_left);
+ bool signed_right = is_type_signed(type_right);
+ if(get_rank(type_left) < get_rank(type_right)) {
+ if(signed_left == signed_right || !signed_right) {
+ return type_right;
+ } else {
+ return type_left;
+ }
+ } else {
+ if(signed_left == signed_right || !signed_left) {
+ return type_left;
+ } else {
+ return type_right;
+ }
+ }
+}
+
+static void semantic_binexpr_arithmetic(binary_expression_t *expression)
+{
+ expression_t *left = expression->left;
+ expression_t *right = expression->right;
+ type_t *orig_type_left = left->datatype;
+ type_t *orig_type_right = right->datatype;
+
+ if(orig_type_left == NULL || orig_type_right == NULL)
+ return;
+
+ type_t *type_left = skip_typeref(orig_type_left);
+ type_t *type_right = skip_typeref(orig_type_right);
+
+ if(!is_type_arithmetic(type_left) || !is_type_arithmetic(type_right)) {
+ /* TODO: improve error message */
+ parser_print_error_prefix();
+ fprintf(stderr, "operation needs arithmetic types\n");
+ return;
+ }
+
+ type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
+ expression->left = create_implicit_cast(left, arithmetic_type);
+ expression->right = create_implicit_cast(right, arithmetic_type);
+ expression->expression.datatype = arithmetic_type;