diff options
Diffstat (limited to 'gas/expr.c')
-rw-r--r-- | gas/expr.c | 1859 |
1 files changed, 1859 insertions, 0 deletions
diff --git a/gas/expr.c b/gas/expr.c new file mode 100644 index 0000000..d5d55fd --- /dev/null +++ b/gas/expr.c @@ -0,0 +1,1859 @@ +/* expr.c -operands, expressions- + Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 96, 97, 1998 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GAS 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* + * This is really a branch office of as-read.c. I split it out to clearly + * distinguish the world of expressions from the world of statements. + * (It also gives smaller files to re-compile.) + * Here, "operand"s are of expressions, not instructions. + */ + +#include <ctype.h> +#include <string.h> +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#include "as.h" +#include "obstack.h" + +static void floating_constant PARAMS ((expressionS * expressionP)); +static void integer_constant PARAMS ((int radix, expressionS * expressionP)); +static void mri_char_constant PARAMS ((expressionS *)); +static void current_location PARAMS ((expressionS *)); +static void clean_up_expression PARAMS ((expressionS * expressionP)); +static segT operand PARAMS ((expressionS *)); +static operatorT operator PARAMS ((void)); + +extern const char EXP_CHARS[], FLT_CHARS[]; + +/* We keep a mapping of expression symbols to file positions, so that + we can provide better error messages. */ + +struct expr_symbol_line +{ + struct expr_symbol_line *next; + symbolS *sym; + char *file; + unsigned int line; +}; + +static struct expr_symbol_line *expr_symbol_lines; + +/* Build a dummy symbol to hold a complex expression. This is how we + build expressions up out of other expressions. The symbol is put + into the fake section expr_section. */ + +symbolS * +make_expr_symbol (expressionP) + expressionS *expressionP; +{ + expressionS zero; + const char *fake; + symbolS *symbolP; + struct expr_symbol_line *n; + + if (expressionP->X_op == O_symbol + && expressionP->X_add_number == 0) + return expressionP->X_add_symbol; + + if (expressionP->X_op == O_big) + { + /* This won't work, because the actual value is stored in + generic_floating_point_number or generic_bignum, and we are + going to lose it if we haven't already. */ + if (expressionP->X_add_number > 0) + as_bad (_("bignum invalid; zero assumed")); + else + as_bad (_("floating point number invalid; zero assumed")); + zero.X_op = O_constant; + zero.X_add_number = 0; + zero.X_unsigned = 0; + clean_up_expression (&zero); + expressionP = &zero; + } + + fake = FAKE_LABEL_NAME; + + /* Putting constant symbols in absolute_section rather than + expr_section is convenient for the old a.out code, for which + S_GET_SEGMENT does not always retrieve the value put in by + S_SET_SEGMENT. */ + symbolP = symbol_create (fake, + (expressionP->X_op == O_constant + ? absolute_section + : expr_section), + 0, &zero_address_frag); + symbolP->sy_value = *expressionP; + + if (expressionP->X_op == O_constant) + resolve_symbol_value (symbolP, 1); + + n = (struct expr_symbol_line *) xmalloc (sizeof *n); + n->sym = symbolP; + as_where (&n->file, &n->line); + n->next = expr_symbol_lines; + expr_symbol_lines = n; + + return symbolP; +} + +/* Return the file and line number for an expr symbol. Return + non-zero if something was found, 0 if no information is known for + the symbol. */ + +int +expr_symbol_where (sym, pfile, pline) + symbolS *sym; + char **pfile; + unsigned int *pline; +{ + register struct expr_symbol_line *l; + + for (l = expr_symbol_lines; l != NULL; l = l->next) + { + if (l->sym == sym) + { + *pfile = l->file; + *pline = l->line; + return 1; + } + } + + return 0; +} + +/* Utilities for building expressions. + Since complex expressions are recorded as symbols for use in other + expressions these return a symbolS * and not an expressionS *. + These explicitly do not take an "add_number" argument. */ +/* ??? For completeness' sake one might want expr_build_symbol. + It would just return its argument. */ + +/* Build an expression for an unsigned constant. + The corresponding one for signed constants is missing because + there's currently no need for it. One could add an unsigned_p flag + but that seems more clumsy. */ + +symbolS * +expr_build_uconstant (value) + offsetT value; +{ + expressionS e; + + e.X_op = O_constant; + e.X_add_number = value; + e.X_unsigned = 1; + return make_expr_symbol (&e); +} + +/* Build an expression for OP s1. */ + +symbolS * +expr_build_unary (op, s1) + operatorT op; + symbolS *s1; +{ + expressionS e; + + e.X_op = op; + e.X_add_symbol = s1; + e.X_add_number = 0; + return make_expr_symbol (&e); +} + +/* Build an expression for s1 OP s2. */ + +symbolS * +expr_build_binary (op, s1, s2) + operatorT op; + symbolS *s1; + symbolS *s2; +{ + expressionS e; + + e.X_op = op; + e.X_add_symbol = s1; + e.X_op_symbol = s2; + e.X_add_number = 0; + return make_expr_symbol (&e); +} + +/* Build an expression for the current location ('.'). */ + +symbolS * +expr_build_dot () +{ + expressionS e; + + current_location (&e); + return make_expr_symbol (&e); +} + +/* + * Build any floating-point literal here. + * Also build any bignum literal here. + */ + +/* Seems atof_machine can backscan through generic_bignum and hit whatever + happens to be loaded before it in memory. And its way too complicated + for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, + and never write into the early words, thus they'll always be zero. + I hate Dean's floating-point code. Bleh. */ +LITTLENUM_TYPE generic_bignum[SIZE_OF_LARGE_NUMBER + 6]; +FLONUM_TYPE generic_floating_point_number = +{ + &generic_bignum[6], /* low (JF: Was 0) */ + &generic_bignum[SIZE_OF_LARGE_NUMBER + 6 - 1], /* high JF: (added +6) */ + 0, /* leader */ + 0, /* exponent */ + 0 /* sign */ +}; +/* If nonzero, we've been asked to assemble nan, +inf or -inf */ +int generic_floating_point_magic; + +static void +floating_constant (expressionP) + expressionS *expressionP; +{ + /* input_line_pointer->*/ + /* floating-point constant. */ + int error_code; + + error_code = atof_generic (&input_line_pointer, ".", EXP_CHARS, + &generic_floating_point_number); + + if (error_code) + { + if (error_code == ERROR_EXPONENT_OVERFLOW) + { + as_bad (_("bad floating-point constant: exponent overflow, probably assembling junk")); + } + else + { + as_bad (_("bad floating-point constant: unknown error code=%d."), error_code); + } + } + expressionP->X_op = O_big; + /* input_line_pointer->just after constant, */ + /* which may point to whitespace. */ + expressionP->X_add_number = -1; +} + +static valueT +generic_bignum_to_int32 () +{ + valueT number = + ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK); + number &= 0xffffffff; + return number; +} + +#ifdef BFD64 +static valueT +generic_bignum_to_int64 () +{ + valueT number = + ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[2] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[1] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[0] & LITTLENUM_MASK)); + return number; +} +#endif + +static void +integer_constant (radix, expressionP) + int radix; + expressionS *expressionP; +{ + char *start; /* start of number. */ + char *suffix = NULL; + char c; + valueT number; /* offset or (absolute) value */ + short int digit; /* value of next digit in current radix */ + short int maxdig = 0;/* highest permitted digit value. */ + int too_many_digits = 0; /* if we see >= this number of */ + char *name; /* points to name of symbol */ + symbolS *symbolP; /* points to symbol */ + + int small; /* true if fits in 32 bits. */ + + /* May be bignum, or may fit in 32 bits. */ + /* Most numbers fit into 32 bits, and we want this case to be fast. + so we pretend it will fit into 32 bits. If, after making up a 32 + bit number, we realise that we have scanned more digits than + comfortably fit into 32 bits, we re-scan the digits coding them + into a bignum. For decimal and octal numbers we are + conservative: Some numbers may be assumed bignums when in fact + they do fit into 32 bits. Numbers of any radix can have excess + leading zeros: We strive to recognise this and cast them back + into 32 bits. We must check that the bignum really is more than + 32 bits, and change it back to a 32-bit number if it fits. The + number we are looking for is expected to be positive, but if it + fits into 32 bits as an unsigned number, we let it be a 32-bit + number. The cavalier approach is for speed in ordinary cases. */ + /* This has been extended for 64 bits. We blindly assume that if + you're compiling in 64-bit mode, the target is a 64-bit machine. + This should be cleaned up. */ + +#ifdef BFD64 +#define valuesize 64 +#else /* includes non-bfd case, mostly */ +#define valuesize 32 +#endif + + if (flag_m68k_mri && radix == 0) + { + int flt = 0; + + /* In MRI mode, the number may have a suffix indicating the + radix. For that matter, it might actually be a floating + point constant. */ + for (suffix = input_line_pointer; + isalnum ((unsigned char) *suffix); + suffix++) + { + if (*suffix == 'e' || *suffix == 'E') + flt = 1; + } + + if (suffix == input_line_pointer) + { + radix = 10; + suffix = NULL; + } + else + { + c = *--suffix; + if (islower ((unsigned char) c)) + c = toupper (c); + if (c == 'B') + radix = 2; + else if (c == 'D') + radix = 10; + else if (c == 'O' || c == 'Q') + radix = 8; + else if (c == 'H') + radix = 16; + else if (suffix[1] == '.' || c == 'E' || flt) + { + floating_constant (expressionP); + return; + } + else + { + radix = 10; + suffix = NULL; + } + } + } + + switch (radix) + { + case 2: + maxdig = 2; + too_many_digits = valuesize + 1; + break; + case 8: + maxdig = radix = 8; + too_many_digits = (valuesize + 2) / 3 + 1; + break; + case 16: + maxdig = radix = 16; + too_many_digits = (valuesize + 3) / 4 + 1; + break; + case 10: + maxdig = radix = 10; + too_many_digits = (valuesize + 12) / 4; /* very rough */ + } +#undef valuesize + start = input_line_pointer; + c = *input_line_pointer++; + for (number = 0; + (digit = hex_value (c)) < maxdig; + c = *input_line_pointer++) + { + number = number * radix + digit; + } + /* c contains character after number. */ + /* input_line_pointer->char after c. */ + small = (input_line_pointer - start - 1) < too_many_digits; + + if (radix == 16 && c == '_') + { + /* This is literal of the form 0x333_0_12345678_1. + This example is equivalent to 0x00000333000000001234567800000001. */ + + int num_little_digits = 0; + int i; + input_line_pointer = start; /*->1st digit. */ + + know (LITTLENUM_NUMBER_OF_BITS == 16); + + for (c = '_'; c == '_'; num_little_digits+=2) + { + + /* Convert one 64-bit word. */ + int ndigit = 0; + number = 0; + for (c = *input_line_pointer++; + (digit = hex_value (c)) < maxdig; + c = *(input_line_pointer++)) + { + number = number * radix + digit; + ndigit++; + } + + /* Check for 8 digit per word max. */ + if (ndigit > 8) + as_bad (_("A bignum with underscores may not have more than 8 hex digits in any word.")); + + /* Add this chunk to the bignum. Shift things down 2 little digits.*/ + know (LITTLENUM_NUMBER_OF_BITS == 16); + for (i = min (num_little_digits + 1, SIZE_OF_LARGE_NUMBER - 1); i >= 2; i--) + generic_bignum[i] = generic_bignum[i-2]; + + /* Add the new digits as the least significant new ones. */ + generic_bignum[0] = number & 0xffffffff; + generic_bignum[1] = number >> 16; + } + + /* Again, c is char after number, input_line_pointer->after c. */ + + if (num_little_digits > SIZE_OF_LARGE_NUMBER - 1) + num_little_digits = SIZE_OF_LARGE_NUMBER - 1; + + assert (num_little_digits >= 4); + + if (num_little_digits != 8) + as_bad (_("A bignum with underscores must have exactly 4 words.")); + + /* We might have some leading zeros. These can be trimmed to give + * us a change to fit this constant into a small number. + */ + while (generic_bignum[num_little_digits-1] == 0 && num_little_digits > 1) + num_little_digits--; + + if (num_little_digits <= 2) + { + /* will fit into 32 bits. */ + number = generic_bignum_to_int32 (); + small = 1; + } +#ifdef BFD64 + else if (num_little_digits <= 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif + else + { + small = 0; + number = num_little_digits; /* number of littlenums in the bignum. */ + } + } + else if (!small) + { + /* + * we saw a lot of digits. manufacture a bignum the hard way. + */ + LITTLENUM_TYPE *leader; /*->high order littlenum of the bignum. */ + LITTLENUM_TYPE *pointer; /*->littlenum we are frobbing now. */ + long carry; + + leader = generic_bignum; + generic_bignum[0] = 0; + generic_bignum[1] = 0; + generic_bignum[2] = 0; + generic_bignum[3] = 0; + input_line_pointer = start; /*->1st digit. */ + c = *input_line_pointer++; + for (; + (carry = hex_value (c)) < maxdig; + c = *input_line_pointer++) + { + for (pointer = generic_bignum; + pointer <= leader; + pointer++) + { + long work; + + work = carry + radix * *pointer; + *pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry) + { + if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) + { + /* room to grow a longer bignum. */ + *++leader = carry; + } + } + } + /* again, c is char after number, */ + /* input_line_pointer->after c. */ + know (LITTLENUM_NUMBER_OF_BITS == 16); + if (leader < generic_bignum + 2) + { + /* will fit into 32 bits. */ + number = generic_bignum_to_int32 (); + small = 1; + } +#ifdef BFD64 + else if (leader < generic_bignum + 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif + else + { + number = leader - generic_bignum + 1; /* number of littlenums in the bignum. */ + } + } + + if (flag_m68k_mri && suffix != NULL && input_line_pointer - 1 == suffix) + c = *input_line_pointer++; + + if (small) + { + /* + * here with number, in correct radix. c is the next char. + * note that unlike un*x, we allow "011f" "0x9f" to + * both mean the same as the (conventional) "9f". this is simply easier + * than checking for strict canonical form. syntax sux! + */ + + if (LOCAL_LABELS_FB && c == 'b') + { + /* + * backward ref to local label. + * because it is backward, expect it to be defined. + */ + /* Construct a local label. */ + name = fb_label_name ((int) number, 0); + + /* seen before, or symbol is defined: ok */ + symbolP = symbol_find (name); + if ((symbolP != NULL) && (S_IS_DEFINED (symbolP))) + { + /* local labels are never absolute. don't waste time + checking absoluteness. */ + know (SEG_NORMAL (S_GET_SEGMENT (symbolP))); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + } + else + { + /* either not seen or not defined. */ + /* @@ Should print out the original string instead of + the parsed number. */ + as_bad (_("backw. ref to unknown label \"%d:\", 0 assumed."), + (int) number); + expressionP->X_op = O_constant; + } + + expressionP->X_add_number = 0; + } /* case 'b' */ + else if (LOCAL_LABELS_FB && c == 'f') + { + /* + * forward reference. expect symbol to be undefined or + * unknown. undefined: seen it before. unknown: never seen + * it before. + * construct a local label name, then an undefined symbol. + * don't create a xseg frag for it: caller may do that. + * just return it as never seen before. + */ + name = fb_label_name ((int) number, 1); + symbolP = symbol_find_or_make (name); + /* we have no need to check symbol properties. */ +#ifndef many_segments + /* since "know" puts its arg into a "string", we + can't have newlines in the argument. */ + know (S_GET_SEGMENT (symbolP) == undefined_section || S_GET_SEGMENT (symbolP) == text_section || S_GET_SEGMENT (symbolP) == data_section); +#endif + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case 'f' */ + else if (LOCAL_LABELS_DOLLAR && c == '$') + { + /* If the dollar label is *currently* defined, then this is just + another reference to it. If it is not *currently* defined, + then this is a fresh instantiation of that number, so create + it. */ + + if (dollar_label_defined ((long) number)) + { + name = dollar_label_name ((long) number, 0); + symbolP = symbol_find (name); + know (symbolP != NULL); + } + else + { + name = dollar_label_name ((long) number, 1); + symbolP = symbol_find_or_make (name); + } + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case '$' */ + else + { + expressionP->X_op = O_constant; +#ifdef TARGET_WORD_SIZE + /* Sign extend NUMBER. */ + number |= (-(number >> (TARGET_WORD_SIZE - 1))) << (TARGET_WORD_SIZE - 1); +#endif + expressionP->X_add_number = number; + input_line_pointer--; /* restore following character. */ + } /* really just a number */ + } + else + { + /* not a small number */ + expressionP->X_op = O_big; + expressionP->X_add_number = number; /* number of littlenums */ + input_line_pointer--; /*->char following number. */ + } +} + +/* Parse an MRI multi character constant. */ + +static void +mri_char_constant (expressionP) + expressionS *expressionP; +{ + int i; + + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + { + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + return; + } + + /* In order to get the correct byte ordering, we must build the + number in reverse. */ + for (i = SIZE_OF_LARGE_NUMBER - 1; i >= 0; i--) + { + int j; + + generic_bignum[i] = 0; + for (j = 0; j < CHARS_PER_LITTLENUM; j++) + { + if (*input_line_pointer == '\'') + { + if (input_line_pointer[1] != '\'') + break; + ++input_line_pointer; + } + generic_bignum[i] <<= 8; + generic_bignum[i] += *input_line_pointer; + ++input_line_pointer; + } + + if (i < SIZE_OF_LARGE_NUMBER - 1) + { + /* If there is more than one littlenum, left justify the + last one to make it match the earlier ones. If there is + only one, we can just use the value directly. */ + for (; j < CHARS_PER_LITTLENUM; j++) + generic_bignum[i] <<= 8; + } + + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + break; + } + + if (i < 0) + { + as_bad (_("Character constant too large")); + i = 0; + } + + if (i > 0) + { + int c; + int j; + + c = SIZE_OF_LARGE_NUMBER - i; + for (j = 0; j < c; j++) + generic_bignum[j] = generic_bignum[i + j]; + i = c; + } + + know (LITTLENUM_NUMBER_OF_BITS == 16); + if (i > 2) + { + expressionP->X_op = O_big; + expressionP->X_add_number = i; + } + else + { + expressionP->X_op = O_constant; + if (i < 2) + expressionP->X_add_number = generic_bignum[0] & LITTLENUM_MASK; + else + expressionP->X_add_number = + (((generic_bignum[1] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK)); + } + + /* Skip the final closing quote. */ + ++input_line_pointer; +} + +/* Return an expression representing the current location. This + handles the magic symbol `.'. */ + +static void +current_location (expressionp) + expressionS *expressionp; +{ + if (now_seg == absolute_section) + { + expressionp->X_op = O_constant; + expressionp->X_add_number = abs_section_offset; + } + else + { + symbolS *symbolp; + + symbolp = symbol_new (FAKE_LABEL_NAME, now_seg, + (valueT) frag_now_fix (), + frag_now); + expressionp->X_op = O_symbol; + expressionp->X_add_symbol = symbolp; + expressionp->X_add_number = 0; + } +} + +/* + * Summary of operand(). + * + * in: Input_line_pointer points to 1st char of operand, which may + * be a space. + * + * out: A expressionS. + * The operand may have been empty: in this case X_op == O_absent. + * Input_line_pointer->(next non-blank) char after operand. + */ + +static segT +operand (expressionP) + expressionS *expressionP; +{ + char c; + symbolS *symbolP; /* points to symbol */ + char *name; /* points to name of symbol */ + segT segment; + + /* All integers are regarded as unsigned unless they are negated. + This is because the only thing which cares whether a number is + unsigned is the code in emit_expr which extends constants into + bignums. It should only sign extend negative numbers, so that + something like ``.quad 0x80000000'' is not sign extended even + though it appears negative if valueT is 32 bits. */ + expressionP->X_unsigned = 1; + + /* digits, assume it is a bignum. */ + + SKIP_WHITESPACE (); /* leading whitespace is part of operand. */ + c = *input_line_pointer++; /* input_line_pointer->past char in c. */ + + switch (c) + { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + input_line_pointer--; + + integer_constant (flag_m68k_mri ? 0 : 10, expressionP); + break; + + case '0': + /* non-decimal radix */ + + if (flag_m68k_mri) + { + char *s; + + /* Check for a hex constant. */ + for (s = input_line_pointer; hex_p (*s); s++) + ; + if (*s == 'h' || *s == 'H') + { + --input_line_pointer; + integer_constant (0, expressionP); + break; + } + } + + c = *input_line_pointer; + switch (c) + { + case 'o': + case 'O': + case 'q': + case 'Q': + case '8': + case '9': + if (flag_m68k_mri) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ + default: + default_case: + if (c && strchr (FLT_CHARS, c)) + { + input_line_pointer++; + floating_constant (expressionP); + expressionP->X_add_number = + - (isupper ((unsigned char) c) ? tolower (c) : c); + } + else + { + /* The string was only zero */ + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + } + + break; + + case 'x': + case 'X': + if (flag_m68k_mri) + goto default_case; + input_line_pointer++; + integer_constant (16, expressionP); + break; + + case 'b': + if (LOCAL_LABELS_FB && ! flag_m68k_mri) + { + /* This code used to check for '+' and '-' here, and, in + some conditions, fall through to call + integer_constant. However, that didn't make sense, + as integer_constant only accepts digits. */ + /* Some of our code elsewhere does permit digits greater + than the expected base; for consistency, do the same + here. */ + if (input_line_pointer[1] < '0' + || input_line_pointer[1] > '9') + { + /* Parse this as a back reference to label 0. */ + input_line_pointer--; + integer_constant (10, expressionP); + break; + } + /* Otherwise, parse this as a binary number. */ + } + /* Fall through. */ + case 'B': + input_line_pointer++; + if (flag_m68k_mri) + goto default_case; + integer_constant (2, expressionP); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + integer_constant (flag_m68k_mri ? 0 : 8, expressionP); + break; + + case 'f': + if (LOCAL_LABELS_FB) + { + /* If it says "0f" and it could possibly be a floating point + number, make it one. Otherwise, make it a local label, + and try to deal with parsing the rest later. */ + if (!input_line_pointer[1] + || (is_end_of_line[0xff & input_line_pointer[1]])) + goto is_0f_label; + { + char *cp = input_line_pointer + 1; + int r = atof_generic (&cp, ".", EXP_CHARS, + &generic_floating_point_number); + switch (r) + { + case 0: + case ERROR_EXPONENT_OVERFLOW: + if (*cp == 'f' || *cp == 'b') + /* looks like a difference expression */ + goto is_0f_label; + else if (cp == input_line_pointer + 1) + /* No characters has been accepted -- looks like + end of operand. */ + goto is_0f_label; + else + goto is_0f_float; + default: + as_fatal (_("expr.c(operand): bad atof_generic return val %d"), + r); + } + } + + /* Okay, now we've sorted it out. We resume at one of these + two labels, depending on what we've decided we're probably + looking at. */ + is_0f_label: + input_line_pointer--; + integer_constant (10, expressionP); + break; + + is_0f_float: + /* fall through */ + ; + } + + case 'd': + case 'D': + if (flag_m68k_mri) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ + case 'F': + case 'r': + case 'e': + case 'E': + case 'g': + case 'G': + input_line_pointer++; + floating_constant (expressionP); + expressionP->X_add_number = + - (isupper ((unsigned char) c) ? tolower (c) : c); + break; + + case '$': + if (LOCAL_LABELS_DOLLAR) + { + integer_constant (10, expressionP); + break; + } + else + goto default_case; + } + + break; + + case '(': + case '[': + /* didn't begin with digit & not a name */ + segment = expression (expressionP); + /* Expression() will pass trailing whitespace */ + if ((c == '(' && *input_line_pointer++ != ')') + || (c == '[' && *input_line_pointer++ != ']')) + { + as_bad (_("Missing ')' assumed")); + input_line_pointer--; + } + SKIP_WHITESPACE (); + /* here with input_line_pointer->char after "(...)" */ + return segment; + + case 'E': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + as_bad (_("EBCDIC constants are not supported")); + /* Fall through. */ + case 'A': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + ++input_line_pointer; + /* Fall through. */ + case '\'': + if (! flag_m68k_mri) + { + /* Warning: to conform to other people's assemblers NO + ESCAPEMENT is permitted for a single quote. The next + character, parity errors and all, is taken as the value + of the operand. VERY KINKY. */ + expressionP->X_op = O_constant; + expressionP->X_add_number = *input_line_pointer++; + break; + } + + mri_char_constant (expressionP); + break; + + case '+': + (void) operand (expressionP); + break; + + case '"': + /* Double quote is the bitwise not operator in MRI mode. */ + if (! flag_m68k_mri) + goto de_fault; + /* Fall through. */ + case '~': + /* ~ is permitted to start a label on the Delta. */ + if (is_name_beginner (c)) + goto isname; + case '!': + case '-': + { + operand (expressionP); + if (expressionP->X_op == O_constant) + { + /* input_line_pointer -> char after operand */ + if (c == '-') + { + expressionP->X_add_number = - expressionP->X_add_number; + /* Notice: '-' may overflow: no warning is given. This is + compatible with other people's assemblers. Sigh. */ + expressionP->X_unsigned = 0; + } + else if (c == '~' || c == '"') + expressionP->X_add_number = ~ expressionP->X_add_number; + else + expressionP->X_add_number = ! expressionP->X_add_number; + } + else if (expressionP->X_op != O_illegal + && expressionP->X_op != O_absent) + { + expressionP->X_add_symbol = make_expr_symbol (expressionP); + if (c == '-') + expressionP->X_op = O_uminus; + else if (c == '~' || c == '"') + expressionP->X_op = O_bit_not; + else + expressionP->X_op = O_logical_not; + expressionP->X_add_number = 0; + } + else + as_warn (_("Unary operator %c ignored because bad operand follows"), + c); + } + break; + + case '$': + /* $ is the program counter when in MRI mode, or when DOLLAR_DOT + is defined. */ +#ifndef DOLLAR_DOT + if (! flag_m68k_mri) + goto de_fault; +#endif + if (flag_m68k_mri && hex_p (*input_line_pointer)) + { + /* In MRI mode, $ is also used as the prefix for a + hexadecimal constant. */ + integer_constant (16, expressionP); + break; + } + + if (is_part_of_name (*input_line_pointer)) + goto isname; + + current_location (expressionP); + break; + + case '.': + if (!is_part_of_name (*input_line_pointer)) + { + current_location (expressionP); + break; + } + else if ((strncasecmp (input_line_pointer, "startof.", 8) == 0 + && ! is_part_of_name (input_line_pointer[8])) + || (strncasecmp (input_line_pointer, "sizeof.", 7) == 0 + && ! is_part_of_name (input_line_pointer[7]))) + { + int start; + + start = (input_line_pointer[1] == 't' + || input_line_pointer[1] == 'T'); + input_line_pointer += start ? 8 : 7; + SKIP_WHITESPACE (); + if (*input_line_pointer != '(') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + { + char *buf; + + ++input_line_pointer; + SKIP_WHITESPACE (); + name = input_line_pointer; + c = get_symbol_end (); + + buf = (char *) xmalloc (strlen (name) + 10); + if (start) + sprintf (buf, ".startof.%s", name); + else + sprintf (buf, ".sizeof.%s", name); + symbolP = symbol_make (buf); + free (buf); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ')') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + ++input_line_pointer; + } + break; + } + else + { + goto isname; + } + case ',': + case '\n': + case '\0': + eol: + /* can't imagine any other kind of operand */ + expressionP->X_op = O_absent; + input_line_pointer--; + break; + + case '%': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (2, expressionP); + break; + + case '@': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (8, expressionP); + break; + + case ':': + if (! flag_m68k_mri) + goto de_fault; + + /* In MRI mode, this is a floating point constant represented + using hexadecimal digits. */ + + ++input_line_pointer; + integer_constant (16, expressionP); + break; + + case '*': + if (! flag_m68k_mri || is_part_of_name (*input_line_pointer)) + goto de_fault; + + current_location (expressionP); + break; + + default: + de_fault: + if (is_end_of_line[(unsigned char) c]) + goto eol; + if (is_name_beginner (c)) /* here if did not begin with a digit */ + { + /* + * Identifier begins here. + * This is kludged for speed, so code is repeated. + */ + isname: + name = --input_line_pointer; + c = get_symbol_end (); + +#ifdef md_parse_name + /* This is a hook for the backend to parse certain names + specially in certain contexts. If a name always has a + specific value, it can often be handled by simply + entering it in the symbol table. */ + if (md_parse_name (name, expressionP)) + { + *input_line_pointer = c; + break; + } +#endif + +#ifdef TC_I960 + /* The MRI i960 assembler permits + lda sizeof code,g13 + FIXME: This should use md_parse_name. */ + if (flag_mri + && (strcasecmp (name, "sizeof") == 0 + || strcasecmp (name, "startof") == 0)) + { + int start; + char *buf; + + start = (name[1] == 't' + || name[1] == 'T'); + + *input_line_pointer = c; + SKIP_WHITESPACE (); + + name = input_line_pointer; + c = get_symbol_end (); + + buf = (char *) xmalloc (strlen (name) + 10); + if (start) + sprintf (buf, ".startof.%s", name); + else + sprintf (buf, ".sizeof.%s", name); + symbolP = symbol_make (buf); + free (buf); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE (); + + break; + } +#endif + + symbolP = symbol_find_or_make (name); + + /* If we have an absolute symbol or a reg, then we know its + value now. */ + segment = S_GET_SEGMENT (symbolP); + if (segment == absolute_section) + { + expressionP->X_op = O_constant; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else if (segment == reg_section) + { + expressionP->X_op = O_register; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else + { + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } + *input_line_pointer = c; + } + else + { + /* Let the target try to parse it. Success is indicated by changing + the X_op field to something other than O_absent and pointing + input_line_pointer passed the expression. If it can't parse the + expression, X_op and input_line_pointer should be unchanged. */ + expressionP->X_op = O_absent; + --input_line_pointer; + md_operand (expressionP); + if (expressionP->X_op == O_absent) + { + ++input_line_pointer; + as_bad (_("Bad expression")); + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + } + } + break; + } + + /* + * It is more 'efficient' to clean up the expressionS when they are created. + * Doing it here saves lines of code. + */ + clean_up_expression (expressionP); + SKIP_WHITESPACE (); /*->1st char after operand. */ + know (*input_line_pointer != ' '); + + /* The PA port needs this information. */ + if (expressionP->X_add_symbol) + expressionP->X_add_symbol->sy_used = 1; + + switch (expressionP->X_op) + { + default: + return absolute_section; + case O_symbol: + return S_GET_SEGMENT (expressionP->X_add_symbol); + case O_register: + return reg_section; + } +} /* operand() */ + +/* Internal. Simplify a struct expression for use by expr() */ + +/* + * In: address of a expressionS. + * The X_op field of the expressionS may only take certain values. + * Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT. + * Out: expressionS may have been modified: + * 'foo-foo' symbol references cancelled to 0, + * which changes X_op from O_subtract to O_constant. + * Unused fields zeroed to help expr(). + */ + +static void +clean_up_expression (expressionP) + expressionS *expressionP; +{ + switch (expressionP->X_op) + { + case O_illegal: + case O_absent: + expressionP->X_add_number = 0; + /* Fall through. */ + case O_big: + case O_constant: + case O_register: + expressionP->X_add_symbol = NULL; + /* Fall through. */ + case O_symbol: + case O_uminus: + case O_bit_not: + expressionP->X_op_symbol = NULL; + break; + case O_subtract: + if (expressionP->X_op_symbol == expressionP->X_add_symbol + || ((expressionP->X_op_symbol->sy_frag + == expressionP->X_add_symbol->sy_frag) + && SEG_NORMAL (S_GET_SEGMENT (expressionP->X_add_symbol)) + && (S_GET_VALUE (expressionP->X_op_symbol) + == S_GET_VALUE (expressionP->X_add_symbol)))) + { + addressT diff = (S_GET_VALUE (expressionP->X_add_symbol) + - S_GET_VALUE (expressionP->X_op_symbol)); + + expressionP->X_op = O_constant; + expressionP->X_add_symbol = NULL; + expressionP->X_op_symbol = NULL; + expressionP->X_add_number += diff; + } + break; + default: + break; + } +} + +/* Expression parser. */ + +/* + * We allow an empty expression, and just assume (absolute,0) silently. + * Unary operators and parenthetical expressions are treated as operands. + * As usual, Q==quantity==operand, O==operator, X==expression mnemonics. + * + * We used to do a aho/ullman shift-reduce parser, but the logic got so + * warped that I flushed it and wrote a recursive-descent parser instead. + * Now things are stable, would anybody like to write a fast parser? + * Most expressions are either register (which does not even reach here) + * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. + * So I guess it doesn't really matter how inefficient more complex expressions + * are parsed. + * + * After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. + * Also, we have consumed any leading or trailing spaces (operand does that) + * and done all intervening operators. + * + * This returns the segment of the result, which will be + * absolute_section or the segment of a symbol. + */ + +#undef __ +#define __ O_illegal + +static const operatorT op_encoding[256] = +{ /* maps ASCII->operators */ + + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + + __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, + __, __, O_multiply, O_add, __, O_subtract, __, O_divide, + __, __, __, __, __, __, __, __, + __, __, __, __, O_lt, __, O_gt, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, O_bit_exclusive_or, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, O_bit_inclusive_or, __, __, __, + + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ +}; + + +/* + * Rank Examples + * 0 operand, (expression) + * 1 || + * 2 && + * 3 = <> < <= >= > + * 4 + - + * 5 used for * / % in MRI mode + * 6 & ^ ! | + * 7 * / % << >> + * 8 unary - unary ~ + */ +static operator_rankT op_rank[] = +{ + 0, /* O_illegal */ + 0, /* O_absent */ + 0, /* O_constant */ + 0, /* O_symbol */ + 0, /* O_symbol_rva */ + 0, /* O_register */ + 0, /* O_bit */ + 8, /* O_uminus */ + 8, /* O_bit_not */ + 8, /* O_logical_not */ + 7, /* O_multiply */ + 7, /* O_divide */ + 7, /* O_modulus */ + 7, /* O_left_shift */ + 7, /* O_right_shift */ + 6, /* O_bit_inclusive_or */ + 6, /* O_bit_or_not */ + 6, /* O_bit_exclusive_or */ + 6, /* O_bit_and */ + 4, /* O_add */ + 4, /* O_subtract */ + 3, /* O_eq */ + 3, /* O_ne */ + 3, /* O_lt */ + 3, /* O_le */ + 3, /* O_ge */ + 3, /* O_gt */ + 2, /* O_logical_and */ + 1 /* O_logical_or */ +}; + +/* Unfortunately, in MRI mode for the m68k, multiplication and + division have lower precedence than the bit wise operators. This + function sets the operator precedences correctly for the current + mode. Also, MRI uses a different bit_not operator, and this fixes + that as well. */ + +#define STANDARD_MUL_PRECEDENCE (7) +#define MRI_MUL_PRECEDENCE (5) + +void +expr_set_precedence () +{ + if (flag_m68k_mri) + { + op_rank[O_multiply] = MRI_MUL_PRECEDENCE; + op_rank[O_divide] = MRI_MUL_PRECEDENCE; + op_rank[O_modulus] = MRI_MUL_PRECEDENCE; + } + else + { + op_rank[O_multiply] = STANDARD_MUL_PRECEDENCE; + op_rank[O_divide] = STANDARD_MUL_PRECEDENCE; + op_rank[O_modulus] = STANDARD_MUL_PRECEDENCE; + } +} + +/* Initialize the expression parser. */ + +void +expr_begin () +{ + expr_set_precedence (); + + /* Verify that X_op field is wide enough. */ + { + expressionS e; + e.X_op = O_max; + assert (e.X_op == O_max); + } +} + +/* Return the encoding for the operator at INPUT_LINE_POINTER. + Advance INPUT_LINE_POINTER to the last character in the operator + (i.e., don't change it for a single character operator). */ + +static inline operatorT +operator () +{ + int c; + operatorT ret; + + c = *input_line_pointer & 0xff; + + switch (c) + { + default: + return op_encoding[c]; + + case '<': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '<': + ret = O_left_shift; + break; + case '>': + ret = O_ne; + break; + case '=': + ret = O_le; + break; + } + ++input_line_pointer; + return ret; + + case '=': + if (input_line_pointer[1] != '=') + return op_encoding[c]; + + ++input_line_pointer; + return O_eq; + + case '>': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '>': + ret = O_right_shift; + break; + case '=': + ret = O_ge; + break; + } + ++input_line_pointer; + return ret; + + case '!': + /* We accept !! as equivalent to ^ for MRI compatibility. */ + if (input_line_pointer[1] != '!') + { + if (flag_m68k_mri) + return O_bit_inclusive_or; + return op_encoding[c]; + } + ++input_line_pointer; + return O_bit_exclusive_or; + + case '|': + if (input_line_pointer[1] != '|') + return op_encoding[c]; + + ++input_line_pointer; + return O_logical_or; + + case '&': + if (input_line_pointer[1] != '&') + return op_encoding[c]; + + ++input_line_pointer; + return O_logical_and; + } + + /*NOTREACHED*/ +} + +/* Parse an expression. */ + +segT +expr (rank, resultP) + operator_rankT rank; /* Larger # is higher rank. */ + expressionS *resultP; /* Deliver result here. */ +{ + segT retval; + expressionS right; + operatorT op_left; + operatorT op_right; + + know (rank >= 0); + + retval = operand (resultP); + + know (*input_line_pointer != ' '); /* Operand() gobbles spaces. */ + + op_left = operator (); + while (op_left != O_illegal && op_rank[(int) op_left] > rank) + { + segT rightseg; + + input_line_pointer++; /*->after 1st character of operator. */ + + rightseg = expr (op_rank[(int) op_left], &right); + if (right.X_op == O_absent) + { + as_warn (_("missing operand; zero assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + know (*input_line_pointer != ' '); + + if (retval == undefined_section) + { + if (SEG_NORMAL (rightseg)) + retval = rightseg; + } + else if (! SEG_NORMAL (retval)) + retval = rightseg; + else if (SEG_NORMAL (rightseg) + && retval != rightseg +#ifdef DIFF_EXPR_OK + && op_left != O_subtract +#endif + ) + as_bad (_("operation combines symbols in different segments")); + + op_right = operator (); + + know (op_right == O_illegal || op_rank[(int) op_right] <= op_rank[(int) op_left]); + know ((int) op_left >= (int) O_multiply + && (int) op_left <= (int) O_logical_or); + + /* input_line_pointer->after right-hand quantity. */ + /* left-hand quantity in resultP */ + /* right-hand quantity in right. */ + /* operator in op_left. */ + + if (resultP->X_op == O_big) + { + if (resultP->X_add_number > 0) + as_warn (_("left operand is a bignum; integer 0 assumed")); + else + as_warn (_("left operand is a float; integer 0 assumed")); + resultP->X_op = O_constant; + resultP->X_add_number = 0; + resultP->X_add_symbol = NULL; + resultP->X_op_symbol = NULL; + } + if (right.X_op == O_big) + { + if (right.X_add_number > 0) + as_warn (_("right operand is a bignum; integer 0 assumed")); + else + as_warn (_("right operand is a float; integer 0 assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + /* Optimize common cases. */ + if (op_left == O_add && right.X_op == O_constant) + { + /* X + constant. */ + resultP->X_add_number += right.X_add_number; + } + /* This case comes up in PIC code. */ + else if (op_left == O_subtract + && right.X_op == O_symbol + && resultP->X_op == O_symbol + && (right.X_add_symbol->sy_frag + == resultP->X_add_symbol->sy_frag) + && SEG_NORMAL (S_GET_SEGMENT (right.X_add_symbol))) + + { + resultP->X_add_number -= right.X_add_number; + resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol) + - S_GET_VALUE (right.X_add_symbol)); + resultP->X_op = O_constant; + resultP->X_add_symbol = 0; + } + else if (op_left == O_subtract && right.X_op == O_constant) + { + /* X - constant. */ + resultP->X_add_number -= right.X_add_number; + } + else if (op_left == O_add && resultP->X_op == O_constant) + { + /* Constant + X. */ + resultP->X_op = right.X_op; + resultP->X_add_symbol = right.X_add_symbol; + resultP->X_op_symbol = right.X_op_symbol; + resultP->X_add_number += right.X_add_number; + retval = rightseg; + } + else if (resultP->X_op == O_constant && right.X_op == O_constant) + { + /* Constant OP constant. */ + offsetT v = right.X_add_number; + if (v == 0 && (op_left == O_divide || op_left == O_modulus)) + { + as_warn (_("division by zero")); + v = 1; + } + switch (op_left) + { + default: abort (); + case O_multiply: resultP->X_add_number *= v; break; + case O_divide: resultP->X_add_number /= v; break; + case O_modulus: resultP->X_add_number %= v; break; + case O_left_shift: resultP->X_add_number <<= v; break; + case O_right_shift: + /* We always use unsigned shifts, to avoid relying on + characteristics of the compiler used to compile gas. */ + resultP->X_add_number = + (offsetT) ((valueT) resultP->X_add_number >> (valueT) v); + break; + case O_bit_inclusive_or: resultP->X_add_number |= v; break; + case O_bit_or_not: resultP->X_add_number |= ~v; break; + case O_bit_exclusive_or: resultP->X_add_number ^= v; break; + case O_bit_and: resultP->X_add_number &= v; break; + case O_add: resultP->X_add_number += v; break; + case O_subtract: resultP->X_add_number -= v; break; + case O_eq: + resultP->X_add_number = + resultP->X_add_number == v ? ~ (offsetT) 0 : 0; + break; + case O_ne: + resultP->X_add_number = + resultP->X_add_number != v ? ~ (offsetT) 0 : 0; + break; + case O_lt: + resultP->X_add_number = + resultP->X_add_number < v ? ~ (offsetT) 0 : 0; + break; + case O_le: + resultP->X_add_number = + resultP->X_add_number <= v ? ~ (offsetT) 0 : 0; + break; + case O_ge: + resultP->X_add_number = + resultP->X_add_number >= v ? ~ (offsetT) 0 : 0; + break; + case O_gt: + resultP->X_add_number = + resultP->X_add_number > v ? ~ (offsetT) 0 : 0; + break; + case O_logical_and: + resultP->X_add_number = resultP->X_add_number && v; + break; + case O_logical_or: + resultP->X_add_number = resultP->X_add_number || v; + break; + } + } + else if (resultP->X_op == O_symbol + && right.X_op == O_symbol + && (op_left == O_add + || op_left == O_subtract + || (resultP->X_add_number == 0 + && right.X_add_number == 0))) + { + /* Symbol OP symbol. */ + resultP->X_op = op_left; + resultP->X_op_symbol = right.X_add_symbol; + if (op_left == O_add) + resultP->X_add_number += right.X_add_number; + else if (op_left == O_subtract) + resultP->X_add_number -= right.X_add_number; + } + else + { + /* The general case. */ + resultP->X_add_symbol = make_expr_symbol (resultP); + resultP->X_op_symbol = make_expr_symbol (&right); + resultP->X_op = op_left; + resultP->X_add_number = 0; + resultP->X_unsigned = 1; + } + + op_left = op_right; + } /* While next operator is >= this rank. */ + + /* The PA port needs this information. */ + if (resultP->X_add_symbol) + resultP->X_add_symbol->sy_used = 1; + + return resultP->X_op == O_constant ? absolute_section : retval; +} + +/* + * get_symbol_end() + * + * This lives here because it belongs equally in expr.c & read.c. + * Expr.c is just a branch office read.c anyway, and putting it + * here lessens the crowd at read.c. + * + * Assume input_line_pointer is at start of symbol name. + * Advance input_line_pointer past symbol name. + * Turn that character into a '\0', returning its former value. + * This allows a string compare (RMS wants symbol names to be strings) + * of the symbol name. + * There will always be a char following symbol name, because all good + * lines end in end-of-line. + */ +char +get_symbol_end () +{ + char c; + + /* We accept \001 in a name in case this is being called with a + constructed string. */ + if (is_name_beginner (c = *input_line_pointer++) || c == '\001') + while (is_part_of_name (c = *input_line_pointer++) + || c == '\001') + ; + *--input_line_pointer = 0; + return (c); +} + + +unsigned int +get_single_number () +{ + expressionS exp; + operand (&exp); + return exp.X_add_number; + +} + +/* end of expr.c */ |