| /* |
| * sparse/expression.c |
| * |
| * Copyright (C) 2003 Transmeta Corp. |
| * 2003-2004 Linus Torvalds |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| * |
| * This is the expression parsing part of parsing C. |
| */ |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <limits.h> |
| |
| #include "lib.h" |
| #include "allocate.h" |
| #include "token.h" |
| #include "parse.h" |
| #include "symbol.h" |
| #include "scope.h" |
| #include "expression.h" |
| #include "target.h" |
| #include "char.h" |
| |
| static int match_oplist(int op, ...) |
| { |
| va_list args; |
| int nextop; |
| |
| va_start(args, op); |
| do { |
| nextop = va_arg(args, int); |
| } while (nextop != 0 && nextop != op); |
| va_end(args); |
| |
| return nextop != 0; |
| } |
| |
| static struct token *comma_expression(struct token *, struct expression **); |
| |
| struct token *parens_expression(struct token *token, struct expression **expr, const char *where) |
| { |
| token = expect(token, '(', where); |
| if (match_op(token, '{')) { |
| struct expression *e = alloc_expression(token->pos, EXPR_STATEMENT); |
| struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND); |
| *expr = e; |
| e->statement = stmt; |
| start_symbol_scope(); |
| token = compound_statement(token->next, stmt); |
| end_symbol_scope(); |
| token = expect(token, '}', "at end of statement expression"); |
| } else |
| token = parse_expression(token, expr); |
| return expect(token, ')', where); |
| } |
| |
| /* |
| * Handle __func__, __FUNCTION__ and __PRETTY_FUNCTION__ token |
| * conversion |
| */ |
| static struct symbol *handle_func(struct token *token) |
| { |
| struct ident *ident = token->ident; |
| struct symbol *decl, *array; |
| struct string *string; |
| int len; |
| |
| if (ident != &__func___ident && |
| ident != &__FUNCTION___ident && |
| ident != &__PRETTY_FUNCTION___ident) |
| return NULL; |
| |
| if (!current_fn || !current_fn->ident) |
| return NULL; |
| |
| /* OK, it's one of ours */ |
| array = alloc_symbol(token->pos, SYM_ARRAY); |
| array->ctype.base_type = &char_ctype; |
| array->ctype.alignment = 1; |
| array->endpos = token->pos; |
| decl = alloc_symbol(token->pos, SYM_NODE); |
| decl->ctype.base_type = array; |
| decl->ctype.alignment = 1; |
| decl->ctype.modifiers = MOD_STATIC; |
| decl->endpos = token->pos; |
| |
| /* function-scope, but in NS_SYMBOL */ |
| bind_symbol(decl, ident, NS_LABEL); |
| decl->namespace = NS_SYMBOL; |
| |
| len = current_fn->ident->len; |
| string = __alloc_string(len + 1); |
| memcpy(string->data, current_fn->ident->name, len); |
| string->data[len] = 0; |
| string->length = len + 1; |
| |
| decl->initializer = alloc_expression(token->pos, EXPR_STRING); |
| decl->initializer->string = string; |
| decl->initializer->ctype = decl; |
| decl->array_size = alloc_const_expression(token->pos, len + 1); |
| array->array_size = decl->array_size; |
| decl->bit_size = array->bit_size = bytes_to_bits(len + 1); |
| |
| return decl; |
| } |
| |
| static struct token *parse_type(struct token *token, struct expression **tree) |
| { |
| struct symbol *sym; |
| *tree = alloc_expression(token->pos, EXPR_TYPE); |
| (*tree)->flags = Int_const_expr; /* sic */ |
| token = typename(token, &sym, NULL); |
| if (sym->ident) |
| sparse_error(token->pos, |
| "type expression should not include identifier " |
| "\"%s\"", sym->ident->name); |
| (*tree)->symbol = sym; |
| return token; |
| } |
| |
| static struct token *builtin_types_compatible_p_expr(struct token *token, |
| struct expression **tree) |
| { |
| struct expression *expr = alloc_expression( |
| token->pos, EXPR_COMPARE); |
| expr->flags = Int_const_expr; |
| expr->op = SPECIAL_EQUAL; |
| token = token->next; |
| if (!match_op(token, '(')) |
| return expect(token, '(', |
| "after __builtin_types_compatible_p"); |
| token = token->next; |
| token = parse_type(token, &expr->left); |
| if (!match_op(token, ',')) |
| return expect(token, ',', |
| "in __builtin_types_compatible_p"); |
| token = token->next; |
| token = parse_type(token, &expr->right); |
| if (!match_op(token, ')')) |
| return expect(token, ')', |
| "at end of __builtin_types_compatible_p"); |
| token = token->next; |
| |
| *tree = expr; |
| return token; |
| } |
| |
| static struct token *builtin_offsetof_expr(struct token *token, |
| struct expression **tree) |
| { |
| struct expression *expr = NULL; |
| struct expression **p = &expr; |
| struct symbol *sym; |
| int op = '.'; |
| |
| token = token->next; |
| if (!match_op(token, '(')) |
| return expect(token, '(', "after __builtin_offset"); |
| |
| token = token->next; |
| token = typename(token, &sym, NULL); |
| if (sym->ident) |
| sparse_error(token->pos, |
| "type expression should not include identifier " |
| "\"%s\"", sym->ident->name); |
| |
| if (!match_op(token, ',')) |
| return expect(token, ',', "in __builtin_offset"); |
| |
| while (1) { |
| struct expression *e; |
| switch (op) { |
| case ')': |
| expr->in = sym; |
| *tree = expr; |
| default: |
| return expect(token, ')', "at end of __builtin_offset"); |
| case SPECIAL_DEREFERENCE: |
| e = alloc_expression(token->pos, EXPR_OFFSETOF); |
| e->flags = Int_const_expr; |
| e->op = '['; |
| *p = e; |
| p = &e->down; |
| /* fall through */ |
| case '.': |
| token = token->next; |
| e = alloc_expression(token->pos, EXPR_OFFSETOF); |
| e->flags = Int_const_expr; |
| e->op = '.'; |
| if (token_type(token) != TOKEN_IDENT) { |
| sparse_error(token->pos, "Expected member name"); |
| return token; |
| } |
| e->ident = token->ident; |
| token = token->next; |
| break; |
| case '[': |
| token = token->next; |
| e = alloc_expression(token->pos, EXPR_OFFSETOF); |
| e->flags = Int_const_expr; |
| e->op = '['; |
| token = parse_expression(token, &e->index); |
| token = expect(token, ']', |
| "at end of array dereference"); |
| if (!e->index) |
| return token; |
| } |
| *p = e; |
| p = &e->down; |
| op = token_type(token) == TOKEN_SPECIAL ? token->special : 0; |
| } |
| } |
| |
| #ifndef ULLONG_MAX |
| #define ULLONG_MAX (~0ULL) |
| #endif |
| |
| static unsigned long long parse_num(const char *nptr, char **end) |
| { |
| if (nptr[0] == '0' && tolower((unsigned char)nptr[1]) == 'b') |
| return strtoull(&nptr[2], end, 2); |
| return strtoull(nptr, end, 0); |
| } |
| |
| static void get_number_value(struct expression *expr, struct token *token) |
| { |
| const char *str = token->number; |
| unsigned long long value; |
| char *end; |
| int size = 0, want_unsigned = 0; |
| int overflow = 0, do_warn = 0; |
| int try_unsigned = 1; |
| int bits; |
| |
| errno = 0; |
| value = parse_num(str, &end); |
| if (end == str) |
| goto Float; |
| if (value == ULLONG_MAX && errno == ERANGE) |
| overflow = 1; |
| while (1) { |
| char c = *end++; |
| if (!c) { |
| break; |
| } else if (c == 'u' || c == 'U') { |
| if (want_unsigned) |
| goto Enoint; |
| want_unsigned = 1; |
| } else if (c == 'l' || c == 'L') { |
| if (size) |
| goto Enoint; |
| size = 1; |
| if (*end == c) { |
| size = 2; |
| end++; |
| } |
| } else |
| goto Float; |
| } |
| if (overflow) |
| goto Eoverflow; |
| /* OK, it's a valid integer */ |
| /* decimals can be unsigned only if directly specified as such */ |
| if (str[0] != '0' && !want_unsigned) |
| try_unsigned = 0; |
| if (!size) { |
| bits = bits_in_int - 1; |
| if (!(value & (~1ULL << bits))) { |
| if (!(value & (1ULL << bits))) { |
| goto got_it; |
| } else if (try_unsigned) { |
| want_unsigned = 1; |
| goto got_it; |
| } |
| } |
| size = 1; |
| do_warn = 1; |
| } |
| if (size < 2) { |
| bits = bits_in_long - 1; |
| if (!(value & (~1ULL << bits))) { |
| if (!(value & (1ULL << bits))) { |
| goto got_it; |
| } else if (try_unsigned) { |
| want_unsigned = 1; |
| goto got_it; |
| } |
| do_warn |= 2; |
| } |
| size = 2; |
| do_warn |= 1; |
| } |
| bits = bits_in_longlong - 1; |
| if (value & (~1ULL << bits)) |
| goto Eoverflow; |
| if (!(value & (1ULL << bits))) |
| goto got_it; |
| if (!try_unsigned) |
| warning(expr->pos, "decimal constant %s is too big for long long", |
| show_token(token)); |
| want_unsigned = 1; |
| got_it: |
| if (do_warn) |
| warning(expr->pos, "constant %s is so big it is%s%s%s", |
| show_token(token), |
| want_unsigned ? " unsigned":"", |
| size > 0 ? " long":"", |
| size > 1 ? " long":""); |
| if (do_warn & 2) |
| warning(expr->pos, |
| "decimal constant %s is between LONG_MAX and ULONG_MAX." |
| " For C99 that means long long, C90 compilers are very " |
| "likely to produce unsigned long (and a warning) here", |
| show_token(token)); |
| expr->type = EXPR_VALUE; |
| expr->flags = Int_const_expr; |
| expr->ctype = ctype_integer(size, want_unsigned); |
| expr->value = value; |
| return; |
| Eoverflow: |
| error_die(expr->pos, "constant %s is too big even for unsigned long long", |
| show_token(token)); |
| return; |
| Float: |
| expr->fvalue = string_to_ld(str, &end); |
| if (str == end) |
| goto Enoint; |
| |
| if (*end && end[1]) |
| goto Enoint; |
| |
| if (*end == 'f' || *end == 'F') |
| expr->ctype = &float_ctype; |
| else if (*end == 'l' || *end == 'L') |
| expr->ctype = &ldouble_ctype; |
| else if (!*end) |
| expr->ctype = &double_ctype; |
| else |
| goto Enoint; |
| |
| expr->flags = Float_literal; |
| expr->type = EXPR_FVALUE; |
| return; |
| |
| Enoint: |
| error_die(expr->pos, "constant %s is not a valid number", show_token(token)); |
| } |
| |
| struct token *primary_expression(struct token *token, struct expression **tree) |
| { |
| struct expression *expr = NULL; |
| |
| switch (token_type(token)) { |
| case TOKEN_CHAR ... TOKEN_WIDE_CHAR_EMBEDDED_3: |
| expr = alloc_expression(token->pos, EXPR_VALUE); |
| expr->flags = Int_const_expr; |
| expr->ctype = token_type(token) < TOKEN_WIDE_CHAR ? &int_ctype : &long_ctype; |
| get_char_constant(token, &expr->value); |
| token = token->next; |
| break; |
| |
| case TOKEN_NUMBER: |
| expr = alloc_expression(token->pos, EXPR_VALUE); |
| get_number_value(expr, token); /* will see if it's an integer */ |
| token = token->next; |
| break; |
| |
| case TOKEN_ZERO_IDENT: { |
| expr = alloc_expression(token->pos, EXPR_SYMBOL); |
| expr->flags = Int_const_expr; |
| expr->ctype = &int_ctype; |
| expr->symbol = &zero_int; |
| expr->symbol_name = token->ident; |
| token = token->next; |
| break; |
| } |
| |
| case TOKEN_IDENT: { |
| struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF); |
| struct token *next = token->next; |
| |
| if (!sym) { |
| sym = handle_func(token); |
| if (token->ident == &__builtin_types_compatible_p_ident) { |
| token = builtin_types_compatible_p_expr(token, &expr); |
| break; |
| } |
| if (token->ident == &__builtin_offsetof_ident) { |
| token = builtin_offsetof_expr(token, &expr); |
| break; |
| } |
| } else if (sym->enum_member) { |
| expr = alloc_expression(token->pos, EXPR_VALUE); |
| *expr = *sym->initializer; |
| /* we want the right position reported, thus the copy */ |
| expr->pos = token->pos; |
| expr->flags = Int_const_expr; |
| token = next; |
| break; |
| } |
| |
| expr = alloc_expression(token->pos, EXPR_SYMBOL); |
| |
| /* |
| * We support types as real first-class citizens, with type |
| * comparisons etc: |
| * |
| * if (typeof(a) == int) .. |
| */ |
| if (sym && sym->namespace == NS_TYPEDEF) { |
| sparse_error(token->pos, "typename in expression"); |
| sym = NULL; |
| } |
| expr->symbol_name = token->ident; |
| expr->symbol = sym; |
| token = next; |
| break; |
| } |
| |
| case TOKEN_STRING: |
| case TOKEN_WIDE_STRING: |
| expr = alloc_expression(token->pos, EXPR_STRING); |
| token = get_string_constant(token, expr); |
| break; |
| |
| case TOKEN_SPECIAL: |
| if (token->special == '(') { |
| expr = alloc_expression(token->pos, EXPR_PREOP); |
| expr->op = '('; |
| token = parens_expression(token, &expr->unop, "in expression"); |
| if (expr->unop) |
| expr->flags = expr->unop->flags; |
| break; |
| } |
| if (token->special == '[' && lookup_type(token->next)) { |
| expr = alloc_expression(token->pos, EXPR_TYPE); |
| expr->flags = Int_const_expr; /* sic */ |
| token = typename(token->next, &expr->symbol, NULL); |
| token = expect(token, ']', "in type expression"); |
| break; |
| } |
| |
| default: |
| ; |
| } |
| *tree = expr; |
| return token; |
| } |
| |
| static struct token *expression_list(struct token *token, struct expression_list **list) |
| { |
| while (!match_op(token, ')')) { |
| struct expression *expr = NULL; |
| token = assignment_expression(token, &expr); |
| if (!expr) |
| break; |
| add_expression(list, expr); |
| if (!match_op(token, ',')) |
| break; |
| token = token->next; |
| } |
| return token; |
| } |
| |
| /* |
| * extend to deal with the ambiguous C grammar for parsing |
| * a cast expressions followed by an initializer. |
| */ |
| static struct token *postfix_expression(struct token *token, struct expression **tree, struct expression *cast_init_expr) |
| { |
| struct expression *expr = cast_init_expr; |
| |
| if (!expr) |
| token = primary_expression(token, &expr); |
| |
| while (expr && token_type(token) == TOKEN_SPECIAL) { |
| switch (token->special) { |
| case '[': { /* Array dereference */ |
| struct expression *deref = alloc_expression(token->pos, EXPR_PREOP); |
| struct expression *add = alloc_expression(token->pos, EXPR_BINOP); |
| |
| deref->op = '*'; |
| deref->unop = add; |
| |
| add->op = '+'; |
| add->left = expr; |
| token = parse_expression(token->next, &add->right); |
| token = expect(token, ']', "at end of array dereference"); |
| expr = deref; |
| continue; |
| } |
| case SPECIAL_INCREMENT: /* Post-increment */ |
| case SPECIAL_DECREMENT: { /* Post-decrement */ |
| struct expression *post = alloc_expression(token->pos, EXPR_POSTOP); |
| post->op = token->special; |
| post->unop = expr; |
| expr = post; |
| token = token->next; |
| continue; |
| } |
| case SPECIAL_DEREFERENCE: { /* Structure pointer member dereference */ |
| /* "x->y" is just shorthand for "(*x).y" */ |
| struct expression *inner = alloc_expression(token->pos, EXPR_PREOP); |
| inner->op = '*'; |
| inner->unop = expr; |
| expr = inner; |
| } |
| /* Fall through!! */ |
| case '.': { /* Structure member dereference */ |
| struct expression *deref = alloc_expression(token->pos, EXPR_DEREF); |
| deref->op = '.'; |
| deref->deref = expr; |
| token = token->next; |
| if (token_type(token) != TOKEN_IDENT) { |
| sparse_error(token->pos, "Expected member name"); |
| break; |
| } |
| deref->member = token->ident; |
| token = token->next; |
| expr = deref; |
| continue; |
| } |
| |
| case '(': { /* Function call */ |
| struct expression *call = alloc_expression(token->pos, EXPR_CALL); |
| call->op = '('; |
| call->fn = expr; |
| token = expression_list(token->next, &call->args); |
| token = expect(token, ')', "in function call"); |
| expr = call; |
| continue; |
| } |
| |
| default: |
| break; |
| } |
| break; |
| } |
| *tree = expr; |
| return token; |
| } |
| |
| static struct token *cast_expression(struct token *token, struct expression **tree); |
| static struct token *unary_expression(struct token *token, struct expression **tree); |
| |
| static struct token *type_info_expression(struct token *token, |
| struct expression **tree, int type) |
| { |
| struct expression *expr = alloc_expression(token->pos, type); |
| struct token *p; |
| |
| *tree = expr; |
| expr->flags = Int_const_expr; /* XXX: VLA support will need that changed */ |
| token = token->next; |
| if (!match_op(token, '(') || !lookup_type(token->next)) |
| return unary_expression(token, &expr->cast_expression); |
| p = token; |
| token = typename(token->next, &expr->cast_type, NULL); |
| |
| if (!match_op(token, ')')) { |
| static const char * error[] = { |
| [EXPR_SIZEOF] = "at end of sizeof", |
| [EXPR_ALIGNOF] = "at end of __alignof__", |
| [EXPR_PTRSIZEOF] = "at end of __sizeof_ptr__" |
| }; |
| return expect(token, ')', error[type]); |
| } |
| |
| token = token->next; |
| /* |
| * C99 ambiguity: the typename might have been the beginning |
| * of a typed initializer expression.. |
| */ |
| if (match_op(token, '{')) { |
| struct expression *cast = alloc_expression(p->pos, EXPR_CAST); |
| cast->cast_type = expr->cast_type; |
| expr->cast_type = NULL; |
| expr->cast_expression = cast; |
| token = initializer(&cast->cast_expression, token); |
| token = postfix_expression(token, &expr->cast_expression, cast); |
| } |
| return token; |
| } |
| |
| static struct token *unary_expression(struct token *token, struct expression **tree) |
| { |
| if (token_type(token) == TOKEN_IDENT) { |
| struct ident *ident = token->ident; |
| if (ident->reserved) { |
| static const struct { |
| struct ident *id; |
| int type; |
| } type_information[] = { |
| { &sizeof_ident, EXPR_SIZEOF }, |
| { &__alignof___ident, EXPR_ALIGNOF }, |
| { &__alignof_ident, EXPR_ALIGNOF }, |
| { &_Alignof_ident, EXPR_ALIGNOF }, |
| { &__sizeof_ptr___ident, EXPR_PTRSIZEOF }, |
| }; |
| int i; |
| for (i = 0; i < ARRAY_SIZE(type_information); i++) { |
| if (ident == type_information[i].id) |
| return type_info_expression(token, tree, type_information[i].type); |
| } |
| } |
| } |
| |
| if (token_type(token) == TOKEN_SPECIAL) { |
| if (match_oplist(token->special, |
| SPECIAL_INCREMENT, SPECIAL_DECREMENT, |
| '&', '*', 0)) { |
| struct expression *unop; |
| struct expression *unary; |
| struct token *next; |
| |
| next = cast_expression(token->next, &unop); |
| if (!unop) { |
| sparse_error(token->pos, "Syntax error in unary expression"); |
| *tree = NULL; |
| return next; |
| } |
| unary = alloc_expression(token->pos, EXPR_PREOP); |
| unary->op = token->special; |
| unary->unop = unop; |
| *tree = unary; |
| return next; |
| } |
| /* possibly constant ones */ |
| if (match_oplist(token->special, '+', '-', '~', '!', 0)) { |
| struct expression *unop; |
| struct expression *unary; |
| struct token *next; |
| |
| next = cast_expression(token->next, &unop); |
| if (!unop) { |
| sparse_error(token->pos, "Syntax error in unary expression"); |
| *tree = NULL; |
| return next; |
| } |
| unary = alloc_expression(token->pos, EXPR_PREOP); |
| unary->op = token->special; |
| unary->unop = unop; |
| unary->flags = unop->flags & Int_const_expr; |
| *tree = unary; |
| return next; |
| } |
| /* Gcc extension: &&label gives the address of a label */ |
| if (match_op(token, SPECIAL_LOGICAL_AND) && |
| token_type(token->next) == TOKEN_IDENT) { |
| struct expression *label = alloc_expression(token->pos, EXPR_LABEL); |
| struct symbol *sym = label_symbol(token->next); |
| if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) { |
| sym->ctype.modifiers |= MOD_ADDRESSABLE; |
| add_symbol(&function_computed_target_list, sym); |
| } |
| label->label_symbol = sym; |
| *tree = label; |
| return token->next->next; |
| } |
| |
| } |
| |
| return postfix_expression(token, tree, NULL); |
| } |
| |
| /* |
| * Ambiguity: a '(' can be either a cast-expression or |
| * a primary-expression depending on whether it is followed |
| * by a type or not. |
| * |
| * additional ambiguity: a "cast expression" followed by |
| * an initializer is really a postfix-expression. |
| */ |
| static struct token *cast_expression(struct token *token, struct expression **tree) |
| { |
| if (match_op(token, '(')) { |
| struct token *next = token->next; |
| if (lookup_type(next)) { |
| struct expression *cast = alloc_expression(next->pos, EXPR_CAST); |
| struct expression *v; |
| struct symbol *sym; |
| int is_force; |
| |
| token = typename(next, &sym, &is_force); |
| cast->cast_type = sym; |
| token = expect(token, ')', "at end of cast operator"); |
| if (match_op(token, '{')) { |
| if (is_force) |
| warning(sym->pos, |
| "[force] in compound literal"); |
| token = initializer(&cast->cast_expression, token); |
| return postfix_expression(token, tree, cast); |
| } |
| *tree = cast; |
| if (is_force) |
| cast->type = EXPR_FORCE_CAST; |
| token = cast_expression(token, &v); |
| if (!v) |
| return token; |
| cast->cast_expression = v; |
| if (v->flags & Int_const_expr) |
| cast->flags = Int_const_expr; |
| else if (v->flags & Float_literal) /* and _not_ int */ |
| cast->flags = Int_const_expr | Float_literal; |
| return token; |
| } |
| } |
| return unary_expression(token, tree); |
| } |
| |
| /* |
| * Generic left-to-right binop parsing |
| * |
| * This _really_ needs to be inlined, because that makes the inner |
| * function call statically deterministic rather than a totally |
| * unpredictable indirect call. But gcc-3 is so "clever" that it |
| * doesn't do so by default even when you tell it to inline it. |
| * |
| * Making it a macro avoids the inlining problem, and also means |
| * that we can pass in the op-comparison as an expression rather |
| * than create a data structure for it. |
| */ |
| |
| #define LR_BINOP_EXPRESSION(__token, tree, type, inner, compare) \ |
| struct expression *left = NULL; \ |
| struct token * next = inner(__token, &left); \ |
| \ |
| if (left) { \ |
| while (token_type(next) == TOKEN_SPECIAL) { \ |
| struct expression *top, *right = NULL; \ |
| int op = next->special; \ |
| \ |
| if (!(compare)) \ |
| goto out; \ |
| top = alloc_expression(next->pos, type); \ |
| next = inner(next->next, &right); \ |
| if (!right) { \ |
| sparse_error(next->pos, "No right hand side of '%s'-expression", show_special(op)); \ |
| break; \ |
| } \ |
| top->flags = left->flags & right->flags \ |
| & Int_const_expr; \ |
| top->op = op; \ |
| top->left = left; \ |
| top->right = right; \ |
| left = top; \ |
| } \ |
| } \ |
| out: \ |
| *tree = left; \ |
| return next; \ |
| |
| static struct token *multiplicative_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, cast_expression, |
| (op == '*') || (op == '/') || (op == '%') |
| ); |
| } |
| |
| static struct token *additive_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, multiplicative_expression, |
| (op == '+') || (op == '-') |
| ); |
| } |
| |
| static struct token *shift_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, additive_expression, |
| (op == SPECIAL_LEFTSHIFT) || (op == SPECIAL_RIGHTSHIFT) |
| ); |
| } |
| |
| static struct token *relational_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_COMPARE, shift_expression, |
| (op == '<') || (op == '>') || |
| (op == SPECIAL_LTE) || (op == SPECIAL_GTE) |
| ); |
| } |
| |
| static struct token *equality_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_COMPARE, relational_expression, |
| (op == SPECIAL_EQUAL) || (op == SPECIAL_NOTEQUAL) |
| ); |
| } |
| |
| static struct token *bitwise_and_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, equality_expression, |
| (op == '&') |
| ); |
| } |
| |
| static struct token *bitwise_xor_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, bitwise_and_expression, |
| (op == '^') |
| ); |
| } |
| |
| static struct token *bitwise_or_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_BINOP, bitwise_xor_expression, |
| (op == '|') |
| ); |
| } |
| |
| static struct token *logical_and_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_LOGICAL, bitwise_or_expression, |
| (op == SPECIAL_LOGICAL_AND) |
| ); |
| } |
| |
| static struct token *logical_or_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_LOGICAL, logical_and_expression, |
| (op == SPECIAL_LOGICAL_OR) |
| ); |
| } |
| |
| struct token *conditional_expression(struct token *token, struct expression **tree) |
| { |
| token = logical_or_expression(token, tree); |
| if (*tree && match_op(token, '?')) { |
| struct expression *expr = alloc_expression(token->pos, EXPR_CONDITIONAL); |
| expr->op = token->special; |
| expr->left = *tree; |
| *tree = expr; |
| token = parse_expression(token->next, &expr->cond_true); |
| token = expect(token, ':', "in conditional expression"); |
| token = conditional_expression(token, &expr->cond_false); |
| if (expr->left && expr->cond_false) { |
| int is_const = expr->left->flags & |
| expr->cond_false->flags & |
| Int_const_expr; |
| if (expr->cond_true) |
| is_const &= expr->cond_true->flags; |
| expr->flags = is_const; |
| } |
| } |
| return token; |
| } |
| |
| struct token *assignment_expression(struct token *token, struct expression **tree) |
| { |
| token = conditional_expression(token, tree); |
| if (*tree && token_type(token) == TOKEN_SPECIAL) { |
| static const int assignments[] = { |
| '=', |
| SPECIAL_ADD_ASSIGN, SPECIAL_SUB_ASSIGN, |
| SPECIAL_MUL_ASSIGN, SPECIAL_DIV_ASSIGN, |
| SPECIAL_MOD_ASSIGN, SPECIAL_SHL_ASSIGN, |
| SPECIAL_SHR_ASSIGN, SPECIAL_AND_ASSIGN, |
| SPECIAL_OR_ASSIGN, SPECIAL_XOR_ASSIGN }; |
| int i, op = token->special; |
| for (i = 0; i < ARRAY_SIZE(assignments); i++) |
| if (assignments[i] == op) { |
| struct expression * expr = alloc_expression(token->pos, EXPR_ASSIGNMENT); |
| expr->left = *tree; |
| expr->op = op; |
| *tree = expr; |
| return assignment_expression(token->next, &expr->right); |
| } |
| } |
| return token; |
| } |
| |
| static struct token *comma_expression(struct token *token, struct expression **tree) |
| { |
| LR_BINOP_EXPRESSION( |
| token, tree, EXPR_COMMA, assignment_expression, |
| (op == ',') |
| ); |
| } |
| |
| struct token *parse_expression(struct token *token, struct expression **tree) |
| { |
| return comma_expression(token,tree); |
| } |
| |
| |