]>
Commit | Line | Data |
---|---|---|
fecd2382 | 1 | /* expr.c -operands, expressions- |
f2f7d044 | 2 | Copyright (C) 1987, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. |
2ed83a59 | 3 | |
a39116f1 | 4 | This file is part of GAS, the GNU Assembler. |
2ed83a59 | 5 | |
a39116f1 RP |
6 | GAS is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
2ed83a59 | 10 | |
a39116f1 RP |
11 | GAS is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
2ed83a59 | 15 | |
a39116f1 RP |
16 | You should have received a copy of the GNU General Public License |
17 | along with GAS; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
fecd2382 RP |
19 | |
20 | /* | |
21 | * This is really a branch office of as-read.c. I split it out to clearly | |
22 | * distinguish the world of expressions from the world of statements. | |
23 | * (It also gives smaller files to re-compile.) | |
24 | * Here, "operand"s are of expressions, not instructions. | |
25 | */ | |
26 | ||
27 | #include <ctype.h> | |
28 | #include <string.h> | |
29 | ||
30 | #include "as.h" | |
31 | ||
32 | #include "obstack.h" | |
33 | ||
d4c8cbd8 JL |
34 | static void floating_constant PARAMS ((expressionS * expressionP)); |
35 | static void integer_constant PARAMS ((int radix, expressionS * expressionP)); | |
f2f7d044 | 36 | static void clean_up_expression PARAMS ((expressionS * expressionP)); |
5ac34ac3 ILT |
37 | static symbolS *make_expr_symbol PARAMS ((expressionS * expressionP)); |
38 | ||
f2f7d044 | 39 | extern const char EXP_CHARS[], FLT_CHARS[]; |
5ac34ac3 ILT |
40 | \f |
41 | /* Build a dummy symbol to hold a complex expression. This is how we | |
42 | build expressions up out of other expressions. The symbol is put | |
43 | into the fake section expr_section. */ | |
44 | ||
45 | static symbolS * | |
46 | make_expr_symbol (expressionP) | |
47 | expressionS *expressionP; | |
48 | { | |
49 | const char *fake; | |
50 | symbolS *symbolP; | |
fecd2382 | 51 | |
5ac34ac3 ILT |
52 | /* FIXME: This should be something which decode_local_label_name |
53 | will handle. */ | |
d4c8cbd8 JL |
54 | fake = FAKE_LABEL_NAME; |
55 | ||
5ac34ac3 ILT |
56 | /* Putting constant symbols in absolute_section rather than |
57 | expr_section is convenient for the old a.out code, for which | |
58 | S_GET_SEGMENT does not always retrieve the value put in by | |
59 | S_SET_SEGMENT. */ | |
60 | symbolP = symbol_new (fake, | |
61 | (expressionP->X_op == O_constant | |
62 | ? absolute_section | |
63 | : expr_section), | |
64 | 0, &zero_address_frag); | |
65 | symbolP->sy_value = *expressionP; | |
66 | return symbolP; | |
67 | } | |
68 | \f | |
fecd2382 RP |
69 | /* |
70 | * Build any floating-point literal here. | |
71 | * Also build any bignum literal here. | |
72 | */ | |
73 | ||
fecd2382 RP |
74 | /* Seems atof_machine can backscan through generic_bignum and hit whatever |
75 | happens to be loaded before it in memory. And its way too complicated | |
76 | for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, | |
77 | and never write into the early words, thus they'll always be zero. | |
f2f7d044 | 78 | I hate Dean's floating-point code. Bleh. */ |
2ed83a59 KR |
79 | LITTLENUM_TYPE generic_bignum[SIZE_OF_LARGE_NUMBER + 6]; |
80 | FLONUM_TYPE generic_floating_point_number = | |
fecd2382 | 81 | { |
2ed83a59 KR |
82 | &generic_bignum[6], /* low (JF: Was 0) */ |
83 | &generic_bignum[SIZE_OF_LARGE_NUMBER + 6 - 1], /* high JF: (added +6) */ | |
84 | 0, /* leader */ | |
85 | 0, /* exponent */ | |
86 | 0 /* sign */ | |
87 | }; | |
fecd2382 RP |
88 | /* If nonzero, we've been asked to assemble nan, +inf or -inf */ |
89 | int generic_floating_point_magic; | |
90 | \f | |
d4c8cbd8 | 91 | static void |
2ed83a59 KR |
92 | floating_constant (expressionP) |
93 | expressionS *expressionP; | |
c593cf41 SC |
94 | { |
95 | /* input_line_pointer->*/ | |
96 | /* floating-point constant. */ | |
97 | int error_code; | |
98 | ||
99 | error_code = atof_generic | |
2ed83a59 KR |
100 | (&input_line_pointer, ".", EXP_CHARS, |
101 | &generic_floating_point_number); | |
c593cf41 SC |
102 | |
103 | if (error_code) | |
c593cf41 | 104 | { |
2ed83a59 KR |
105 | if (error_code == ERROR_EXPONENT_OVERFLOW) |
106 | { | |
107 | as_bad ("bad floating-point constant: exponent overflow, probably assembling junk"); | |
108 | } | |
109 | else | |
110 | { | |
111 | as_bad ("bad floating-point constant: unknown error code=%d.", error_code); | |
112 | } | |
c593cf41 | 113 | } |
5ac34ac3 | 114 | expressionP->X_op = O_big; |
c593cf41 SC |
115 | /* input_line_pointer->just after constant, */ |
116 | /* which may point to whitespace. */ | |
2ed83a59 | 117 | expressionP->X_add_number = -1; |
c593cf41 SC |
118 | } |
119 | ||
d4c8cbd8 | 120 | static void |
2ed83a59 KR |
121 | integer_constant (radix, expressionP) |
122 | int radix; | |
123 | expressionS *expressionP; | |
c593cf41 | 124 | { |
dae92eab | 125 | char *digit_2; /*->2nd digit of number. */ |
c593cf41 | 126 | char c; |
2ed83a59 | 127 | |
dae92eab KR |
128 | valueT number; /* offset or (absolute) value */ |
129 | short int digit; /* value of next digit in current radix */ | |
130 | short int maxdig = 0;/* highest permitted digit value. */ | |
131 | int too_many_digits = 0; /* if we see >= this number of */ | |
132 | char *name; /* points to name of symbol */ | |
133 | symbolS *symbolP; /* points to symbol */ | |
2ed83a59 KR |
134 | |
135 | int small; /* true if fits in 32 bits. */ | |
f2f7d044 | 136 | extern const char hex_value[]; /* in hex_value.c */ |
2ed83a59 | 137 | |
dae92eab KR |
138 | /* May be bignum, or may fit in 32 bits. */ |
139 | /* Most numbers fit into 32 bits, and we want this case to be fast. | |
140 | so we pretend it will fit into 32 bits. If, after making up a 32 | |
141 | bit number, we realise that we have scanned more digits than | |
142 | comfortably fit into 32 bits, we re-scan the digits coding them | |
143 | into a bignum. For decimal and octal numbers we are | |
144 | conservative: Some numbers may be assumed bignums when in fact | |
145 | they do fit into 32 bits. Numbers of any radix can have excess | |
146 | leading zeros: We strive to recognise this and cast them back | |
147 | into 32 bits. We must check that the bignum really is more than | |
148 | 32 bits, and change it back to a 32-bit number if it fits. The | |
149 | number we are looking for is expected to be positive, but if it | |
150 | fits into 32 bits as an unsigned number, we let it be a 32-bit | |
151 | number. The cavalier approach is for speed in ordinary cases. */ | |
58d4951d ILT |
152 | /* This has been extended for 64 bits. We blindly assume that if |
153 | you're compiling in 64-bit mode, the target is a 64-bit machine. | |
154 | This should be cleaned up. */ | |
155 | ||
156 | #ifdef BFD64 | |
157 | #define valuesize 64 | |
158 | #else /* includes non-bfd case, mostly */ | |
159 | #define valuesize 32 | |
160 | #endif | |
2ed83a59 KR |
161 | |
162 | switch (radix) | |
f8701a3f | 163 | { |
f8701a3f SC |
164 | case 2: |
165 | maxdig = 2; | |
58d4951d | 166 | too_many_digits = valuesize + 1; |
f8701a3f SC |
167 | break; |
168 | case 8: | |
169 | maxdig = radix = 8; | |
58d4951d | 170 | too_many_digits = (valuesize + 2) / 3; |
f8701a3f SC |
171 | break; |
172 | case 16: | |
f8701a3f | 173 | maxdig = radix = 16; |
58d4951d | 174 | too_many_digits = (valuesize + 3) / 4; |
f8701a3f SC |
175 | break; |
176 | case 10: | |
177 | maxdig = radix = 10; | |
58d4951d | 178 | too_many_digits = (valuesize + 12) / 4; /* very rough */ |
f8701a3f | 179 | } |
58d4951d | 180 | #undef valuesize |
c593cf41 SC |
181 | c = *input_line_pointer; |
182 | input_line_pointer++; | |
183 | digit_2 = input_line_pointer; | |
58d4951d ILT |
184 | for (number = 0; |
185 | (digit = hex_value[(unsigned char) c]) < maxdig; | |
186 | c = *input_line_pointer++) | |
f8701a3f SC |
187 | { |
188 | number = number * radix + digit; | |
189 | } | |
c593cf41 SC |
190 | /* c contains character after number. */ |
191 | /* input_line_pointer->char after c. */ | |
192 | small = input_line_pointer - digit_2 < too_many_digits; | |
2ed83a59 | 193 | if (!small) |
c593cf41 | 194 | { |
f8701a3f SC |
195 | /* |
196 | * we saw a lot of digits. manufacture a bignum the hard way. | |
197 | */ | |
2ed83a59 KR |
198 | LITTLENUM_TYPE *leader; /*->high order littlenum of the bignum. */ |
199 | LITTLENUM_TYPE *pointer; /*->littlenum we are frobbing now. */ | |
f8701a3f | 200 | long carry; |
2ed83a59 | 201 | |
f8701a3f | 202 | leader = generic_bignum; |
2ed83a59 KR |
203 | generic_bignum[0] = 0; |
204 | generic_bignum[1] = 0; | |
f8701a3f | 205 | /* we could just use digit_2, but lets be mnemonic. */ |
2ed83a59 | 206 | input_line_pointer = --digit_2; /*->1st digit. */ |
f8701a3f | 207 | c = *input_line_pointer++; |
58d4951d ILT |
208 | for (; |
209 | (carry = hex_value[(unsigned char) c]) < maxdig; | |
210 | c = *input_line_pointer++) | |
f8701a3f SC |
211 | { |
212 | for (pointer = generic_bignum; | |
213 | pointer <= leader; | |
214 | pointer++) | |
215 | { | |
216 | long work; | |
2ed83a59 KR |
217 | |
218 | work = carry + radix * *pointer; | |
f8701a3f SC |
219 | *pointer = work & LITTLENUM_MASK; |
220 | carry = work >> LITTLENUM_NUMBER_OF_BITS; | |
221 | } | |
222 | if (carry) | |
223 | { | |
224 | if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) | |
2ed83a59 | 225 | { /* room to grow a longer bignum. */ |
f8701a3f SC |
226 | *++leader = carry; |
227 | } | |
228 | } | |
229 | } | |
230 | /* again, c is char after number, */ | |
231 | /* input_line_pointer->after c. */ | |
2ed83a59 | 232 | know (LITTLENUM_NUMBER_OF_BITS == 16); |
d4c8cbd8 | 233 | if (leader < generic_bignum + sizeof (valueT) / 2) |
2ed83a59 | 234 | { /* will fit into 32 bits. */ |
f8701a3f | 235 | number = |
2ed83a59 KR |
236 | ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) |
237 | | (generic_bignum[0] & LITTLENUM_MASK); | |
f8701a3f SC |
238 | small = 1; |
239 | } | |
240 | else | |
241 | { | |
2ed83a59 | 242 | number = leader - generic_bignum + 1; /* number of littlenums in the bignum. */ |
c593cf41 | 243 | } |
c593cf41 | 244 | } |
2ed83a59 KR |
245 | if (small) |
246 | { | |
f8701a3f | 247 | /* |
2ed83a59 KR |
248 | * here with number, in correct radix. c is the next char. |
249 | * note that unlike un*x, we allow "011f" "0x9f" to | |
250 | * both mean the same as the (conventional) "9f". this is simply easier | |
251 | * than checking for strict canonical form. syntax sux! | |
f8701a3f | 252 | */ |
2ed83a59 KR |
253 | |
254 | switch (c) | |
255 | { | |
256 | ||
257 | #ifdef LOCAL_LABELS_FB | |
258 | case 'b': | |
259 | { | |
260 | /* | |
261 | * backward ref to local label. | |
262 | * because it is backward, expect it to be defined. | |
263 | */ | |
264 | /* Construct a local label. */ | |
265 | name = fb_label_name ((int) number, 0); | |
266 | ||
267 | /* seen before, or symbol is defined: ok */ | |
268 | symbolP = symbol_find (name); | |
269 | if ((symbolP != NULL) && (S_IS_DEFINED (symbolP))) | |
270 | { | |
271 | ||
272 | /* local labels are never absolute. don't waste time | |
273 | checking absoluteness. */ | |
274 | know (SEG_NORMAL (S_GET_SEGMENT (symbolP))); | |
275 | ||
5ac34ac3 | 276 | expressionP->X_op = O_symbol; |
2ed83a59 | 277 | expressionP->X_add_symbol = symbolP; |
2ed83a59 KR |
278 | |
279 | } | |
280 | else | |
dae92eab KR |
281 | { |
282 | /* either not seen or not defined. */ | |
283 | /* @@ Should print out the original string instead of | |
284 | the parsed number. */ | |
285 | as_bad ("backw. ref to unknown label \"%d:\", 0 assumed.", | |
286 | (int) number); | |
5ac34ac3 | 287 | expressionP->X_op = O_constant; |
2ed83a59 KR |
288 | } |
289 | ||
290 | expressionP->X_add_number = 0; | |
291 | break; | |
292 | } /* case 'b' */ | |
293 | ||
294 | case 'f': | |
295 | { | |
296 | /* | |
297 | * forward reference. expect symbol to be undefined or | |
298 | * unknown. undefined: seen it before. unknown: never seen | |
299 | * it before. | |
300 | * construct a local label name, then an undefined symbol. | |
301 | * don't create a xseg frag for it: caller may do that. | |
302 | * just return it as never seen before. | |
303 | */ | |
304 | name = fb_label_name ((int) number, 1); | |
305 | symbolP = symbol_find_or_make (name); | |
306 | /* we have no need to check symbol properties. */ | |
c593cf41 | 307 | #ifndef many_segments |
2ed83a59 KR |
308 | /* since "know" puts its arg into a "string", we |
309 | can't have newlines in the argument. */ | |
f2f7d044 | 310 | know (S_GET_SEGMENT (symbolP) == undefined_section || S_GET_SEGMENT (symbolP) == text_section || S_GET_SEGMENT (symbolP) == data_section); |
c593cf41 | 311 | #endif |
5ac34ac3 | 312 | expressionP->X_op = O_symbol; |
2ed83a59 | 313 | expressionP->X_add_symbol = symbolP; |
2ed83a59 KR |
314 | expressionP->X_add_number = 0; |
315 | ||
316 | break; | |
317 | } /* case 'f' */ | |
318 | ||
f8701a3f | 319 | #endif /* LOCAL_LABELS_FB */ |
2ed83a59 | 320 | |
f8701a3f | 321 | #ifdef LOCAL_LABELS_DOLLAR |
f8701a3f | 322 | |
2ed83a59 KR |
323 | case '$': |
324 | { | |
325 | ||
326 | /* If the dollar label is *currently* defined, then this is just | |
327 | another reference to it. If it is not *currently* defined, | |
328 | then this is a fresh instantiation of that number, so create | |
329 | it. */ | |
330 | ||
d4c8cbd8 | 331 | if (dollar_label_defined ((long) number)) |
2ed83a59 | 332 | { |
d4c8cbd8 | 333 | name = dollar_label_name ((long) number, 0); |
2ed83a59 KR |
334 | symbolP = symbol_find (name); |
335 | know (symbolP != NULL); | |
336 | } | |
337 | else | |
338 | { | |
d4c8cbd8 | 339 | name = dollar_label_name ((long) number, 1); |
2ed83a59 KR |
340 | symbolP = symbol_find_or_make (name); |
341 | } | |
342 | ||
5ac34ac3 | 343 | expressionP->X_op = O_symbol; |
2ed83a59 KR |
344 | expressionP->X_add_symbol = symbolP; |
345 | expressionP->X_add_number = 0; | |
2ed83a59 KR |
346 | |
347 | break; | |
348 | } /* case '$' */ | |
349 | ||
f8701a3f | 350 | #endif /* LOCAL_LABELS_DOLLAR */ |
2ed83a59 KR |
351 | |
352 | default: | |
353 | { | |
5ac34ac3 | 354 | expressionP->X_op = O_constant; |
2ed83a59 | 355 | expressionP->X_add_number = number; |
2ed83a59 KR |
356 | input_line_pointer--; /* restore following character. */ |
357 | break; | |
358 | } /* really just a number */ | |
359 | ||
360 | } /* switch on char following the number */ | |
361 | ||
362 | ||
363 | } | |
364 | else | |
dae92eab KR |
365 | { |
366 | /* not a small number */ | |
5ac34ac3 | 367 | expressionP->X_op = O_big; |
c593cf41 | 368 | expressionP->X_add_number = number; |
2ed83a59 | 369 | input_line_pointer--; /*->char following number. */ |
dae92eab | 370 | } |
d4c8cbd8 | 371 | } |
c593cf41 SC |
372 | |
373 | ||
fecd2382 RP |
374 | /* |
375 | * Summary of operand(). | |
376 | * | |
377 | * in: Input_line_pointer points to 1st char of operand, which may | |
378 | * be a space. | |
379 | * | |
5ac34ac3 ILT |
380 | * out: A expressionS. |
381 | * The operand may have been empty: in this case X_op == O_absent. | |
fecd2382 | 382 | * Input_line_pointer->(next non-blank) char after operand. |
fecd2382 | 383 | */ |
c593cf41 | 384 | |
fecd2382 | 385 | static segT |
c593cf41 | 386 | operand (expressionP) |
dae92eab | 387 | expressionS *expressionP; |
fecd2382 | 388 | { |
dae92eab KR |
389 | char c; |
390 | symbolS *symbolP; /* points to symbol */ | |
391 | char *name; /* points to name of symbol */ | |
58d4951d | 392 | segT segment; |
c593cf41 | 393 | |
d4c8cbd8 JL |
394 | /* All integers are regarded as unsigned unless they are negated. |
395 | This is because the only thing which cares whether a number is | |
396 | unsigned is the code in emit_expr which extends constants into | |
397 | bignums. It should only sign extend negative numbers, so that | |
398 | something like ``.quad 0x80000000'' is not sign extended even | |
399 | though it appears negative if valueT is 32 bits. */ | |
400 | expressionP->X_unsigned = 1; | |
401 | ||
c593cf41 SC |
402 | /* digits, assume it is a bignum. */ |
403 | ||
2ed83a59 KR |
404 | SKIP_WHITESPACE (); /* leading whitespace is part of operand. */ |
405 | c = *input_line_pointer++; /* input_line_pointer->past char in c. */ | |
c593cf41 SC |
406 | |
407 | switch (c) | |
fecd2382 | 408 | { |
2ed83a59 KR |
409 | #ifdef MRI |
410 | case '%': | |
411 | integer_constant (2, expressionP); | |
c593cf41 | 412 | break; |
2ed83a59 KR |
413 | case '@': |
414 | integer_constant (8, expressionP); | |
c593cf41 | 415 | break; |
2ed83a59 KR |
416 | case '$': |
417 | integer_constant (16, expressionP); | |
c593cf41 | 418 | break; |
2ed83a59 | 419 | #endif |
c593cf41 SC |
420 | case '1': |
421 | case '2': | |
422 | case '3': | |
423 | case '4': | |
424 | case '5': | |
425 | case '6': | |
426 | case '7': | |
2ed83a59 KR |
427 | case '8': |
428 | case '9': | |
429 | input_line_pointer--; | |
430 | ||
431 | integer_constant (10, expressionP); | |
c593cf41 SC |
432 | break; |
433 | ||
2ed83a59 KR |
434 | case '0': |
435 | /* non-decimal radix */ | |
436 | ||
2ed83a59 KR |
437 | c = *input_line_pointer; |
438 | switch (c) | |
439 | { | |
440 | ||
441 | default: | |
442 | if (c && strchr (FLT_CHARS, c)) | |
443 | { | |
444 | input_line_pointer++; | |
445 | floating_constant (expressionP); | |
446 | } | |
447 | else | |
448 | { | |
449 | /* The string was only zero */ | |
5ac34ac3 | 450 | expressionP->X_op = O_constant; |
2ed83a59 | 451 | expressionP->X_add_number = 0; |
2ed83a59 KR |
452 | } |
453 | ||
454 | break; | |
455 | ||
456 | case 'x': | |
457 | case 'X': | |
458 | input_line_pointer++; | |
459 | integer_constant (16, expressionP); | |
460 | break; | |
461 | ||
462 | case 'b': | |
463 | #ifdef LOCAL_LABELS_FB | |
5ac34ac3 ILT |
464 | /* FIXME: This seems to be nonsense. At this point we know |
465 | for sure that *input_line_pointer is 'b'. So why are we | |
466 | checking it? What is this code supposed to do? */ | |
2ed83a59 KR |
467 | if (!*input_line_pointer |
468 | || (!strchr ("+-.0123456789", *input_line_pointer) | |
469 | && !strchr (EXP_CHARS, *input_line_pointer))) | |
470 | { | |
471 | input_line_pointer--; | |
472 | integer_constant (10, expressionP); | |
473 | break; | |
474 | } | |
475 | #endif | |
476 | case 'B': | |
477 | input_line_pointer++; | |
478 | integer_constant (2, expressionP); | |
479 | break; | |
480 | ||
481 | case '0': | |
482 | case '1': | |
483 | case '2': | |
484 | case '3': | |
485 | case '4': | |
486 | case '5': | |
487 | case '6': | |
488 | case '7': | |
489 | integer_constant (8, expressionP); | |
490 | break; | |
491 | ||
492 | case 'f': | |
493 | #ifdef LOCAL_LABELS_FB | |
494 | /* if it says '0f' and the line ends or it doesn't look like | |
d841bc49 | 495 | a floating point #, its a local label ref. dtrt */ |
2ed83a59 | 496 | /* likewise for the b's. xoxorich. */ |
5ac34ac3 ILT |
497 | /* FIXME: As in the 'b' case, we know that the |
498 | *input_line_pointer is 'f'. What is this code really | |
499 | trying to do? */ | |
2ed83a59 KR |
500 | if (c == 'f' |
501 | && (!*input_line_pointer || | |
502 | (!strchr ("+-.0123456789", *input_line_pointer) && | |
503 | !strchr (EXP_CHARS, *input_line_pointer)))) | |
504 | { | |
505 | input_line_pointer -= 1; | |
506 | integer_constant (10, expressionP); | |
507 | break; | |
508 | } | |
509 | #endif | |
510 | ||
511 | case 'd': | |
512 | case 'D': | |
513 | case 'F': | |
514 | case 'r': | |
515 | case 'e': | |
516 | case 'E': | |
517 | case 'g': | |
518 | case 'G': | |
519 | ||
520 | input_line_pointer++; | |
521 | floating_constant (expressionP); | |
f2f7d044 | 522 | expressionP->X_add_number = -(isupper (c) ? tolower (c) : c); |
2ed83a59 KR |
523 | break; |
524 | ||
525 | #ifdef LOCAL_LABELS_DOLLAR | |
526 | case '$': | |
527 | integer_constant (10, expressionP); | |
528 | break; | |
529 | #endif | |
530 | } | |
531 | ||
c593cf41 | 532 | break; |
5ac34ac3 | 533 | |
2ed83a59 KR |
534 | case '(': |
535 | /* didn't begin with digit & not a name */ | |
58d4951d | 536 | segment = expression (expressionP); |
5ac34ac3 ILT |
537 | /* Expression() will pass trailing whitespace */ |
538 | if (*input_line_pointer++ != ')') | |
539 | { | |
540 | as_bad ("Missing ')' assumed"); | |
541 | input_line_pointer--; | |
542 | } | |
543 | /* here with input_line_pointer->char after "(...)" */ | |
58d4951d | 544 | return segment; |
c593cf41 | 545 | |
2ed83a59 | 546 | case '\'': |
d841bc49 KR |
547 | /* Warning: to conform to other people's assemblers NO ESCAPEMENT is |
548 | permitted for a single quote. The next character, parity errors and | |
549 | all, is taken as the value of the operand. VERY KINKY. */ | |
5ac34ac3 | 550 | expressionP->X_op = O_constant; |
2ed83a59 | 551 | expressionP->X_add_number = *input_line_pointer++; |
2ed83a59 KR |
552 | break; |
553 | ||
49864cfa | 554 | case '+': |
58d4951d | 555 | (void) operand (expressionP); |
49864cfa KR |
556 | break; |
557 | ||
2ed83a59 KR |
558 | case '~': |
559 | case '-': | |
2ed83a59 | 560 | { |
5ac34ac3 ILT |
561 | operand (expressionP); |
562 | if (expressionP->X_op == O_constant) | |
2ed83a59 | 563 | { |
2ed83a59 KR |
564 | /* input_line_pointer -> char after operand */ |
565 | if (c == '-') | |
566 | { | |
5ac34ac3 | 567 | expressionP->X_add_number = - expressionP->X_add_number; |
d841bc49 KR |
568 | /* Notice: '-' may overflow: no warning is given. This is |
569 | compatible with other people's assemblers. Sigh. */ | |
d4c8cbd8 | 570 | expressionP->X_unsigned = 0; |
2ed83a59 KR |
571 | } |
572 | else | |
5ac34ac3 | 573 | expressionP->X_add_number = ~ expressionP->X_add_number; |
f2f7d044 | 574 | } |
5ac34ac3 ILT |
575 | else if (expressionP->X_op != O_illegal |
576 | && expressionP->X_op != O_absent) | |
f2f7d044 | 577 | { |
5ac34ac3 | 578 | expressionP->X_add_symbol = make_expr_symbol (expressionP); |
2ed83a59 | 579 | if (c == '-') |
5ac34ac3 | 580 | expressionP->X_op = O_uminus; |
f2f7d044 | 581 | else |
5ac34ac3 ILT |
582 | expressionP->X_op = O_bit_not; |
583 | expressionP->X_add_number = 0; | |
c593cf41 | 584 | } |
f2f7d044 | 585 | else |
5ac34ac3 ILT |
586 | as_warn ("Unary operator %c ignored because bad operand follows", |
587 | c); | |
c593cf41 | 588 | } |
2ed83a59 KR |
589 | break; |
590 | ||
591 | case '.': | |
592 | if (!is_part_of_name (*input_line_pointer)) | |
593 | { | |
5ac34ac3 | 594 | const char *fake; |
2ed83a59 | 595 | |
85825401 ILT |
596 | /* JF: '.' is pseudo symbol with value of current location |
597 | in current segment. */ | |
d4c8cbd8 | 598 | fake = FAKE_LABEL_NAME; |
85825401 | 599 | symbolP = symbol_new (fake, |
2ed83a59 | 600 | now_seg, |
5ac34ac3 | 601 | (valueT) frag_now_fix (), |
2ed83a59 | 602 | frag_now); |
c593cf41 | 603 | |
5ac34ac3 | 604 | expressionP->X_op = O_symbol; |
2ed83a59 | 605 | expressionP->X_add_symbol = symbolP; |
5ac34ac3 | 606 | expressionP->X_add_number = 0; |
2ed83a59 | 607 | break; |
2ed83a59 KR |
608 | } |
609 | else | |
610 | { | |
611 | goto isname; | |
2ed83a59 KR |
612 | } |
613 | case ',': | |
614 | case '\n': | |
f2f7d044 | 615 | case '\0': |
0bd77bc4 | 616 | eol: |
2ed83a59 | 617 | /* can't imagine any other kind of operand */ |
5ac34ac3 | 618 | expressionP->X_op = O_absent; |
2ed83a59 KR |
619 | input_line_pointer--; |
620 | md_operand (expressionP); | |
621 | break; | |
0bd77bc4 | 622 | |
2ed83a59 | 623 | default: |
58d4951d | 624 | if (is_end_of_line[(unsigned char) c]) |
0bd77bc4 | 625 | goto eol; |
2ed83a59 KR |
626 | if (is_name_beginner (c)) /* here if did not begin with a digit */ |
627 | { | |
628 | /* | |
d841bc49 KR |
629 | * Identifier begins here. |
630 | * This is kludged for speed, so code is repeated. | |
631 | */ | |
2ed83a59 KR |
632 | isname: |
633 | name = --input_line_pointer; | |
634 | c = get_symbol_end (); | |
635 | symbolP = symbol_find_or_make (name); | |
5ac34ac3 ILT |
636 | |
637 | /* If we have an absolute symbol or a reg, then we know its | |
638 | value now. */ | |
58d4951d ILT |
639 | segment = S_GET_SEGMENT (symbolP); |
640 | if (segment == absolute_section) | |
5ac34ac3 ILT |
641 | { |
642 | expressionP->X_op = O_constant; | |
643 | expressionP->X_add_number = S_GET_VALUE (symbolP); | |
644 | } | |
58d4951d | 645 | else if (segment == reg_section) |
5ac34ac3 ILT |
646 | { |
647 | expressionP->X_op = O_register; | |
648 | expressionP->X_add_number = S_GET_VALUE (symbolP); | |
649 | } | |
f2f7d044 | 650 | else |
2ed83a59 | 651 | { |
5ac34ac3 | 652 | expressionP->X_op = O_symbol; |
2ed83a59 | 653 | expressionP->X_add_symbol = symbolP; |
5ac34ac3 | 654 | expressionP->X_add_number = 0; |
2ed83a59 KR |
655 | } |
656 | *input_line_pointer = c; | |
2ed83a59 KR |
657 | } |
658 | else | |
659 | { | |
660 | as_bad ("Bad expression"); | |
5ac34ac3 | 661 | expressionP->X_op = O_constant; |
2ed83a59 | 662 | expressionP->X_add_number = 0; |
2ed83a59 | 663 | } |
c593cf41 | 664 | } |
c593cf41 | 665 | |
c593cf41 SC |
666 | /* |
667 | * It is more 'efficient' to clean up the expressionS when they are created. | |
668 | * Doing it here saves lines of code. | |
669 | */ | |
670 | clean_up_expression (expressionP); | |
2ed83a59 KR |
671 | SKIP_WHITESPACE (); /*->1st char after operand. */ |
672 | know (*input_line_pointer != ' '); | |
58d4951d | 673 | |
009dc5e1 JL |
674 | /* The PA port needs this information. */ |
675 | if (expressionP->X_add_symbol) | |
676 | expressionP->X_add_symbol->sy_used = 1; | |
677 | ||
58d4951d ILT |
678 | switch (expressionP->X_op) |
679 | { | |
680 | default: | |
681 | return absolute_section; | |
682 | case O_symbol: | |
683 | return S_GET_SEGMENT (expressionP->X_add_symbol); | |
684 | case O_register: | |
685 | return reg_section; | |
686 | } | |
2ed83a59 | 687 | } /* operand() */ |
fecd2382 RP |
688 | \f |
689 | /* Internal. Simplify a struct expression for use by expr() */ | |
690 | ||
691 | /* | |
692 | * In: address of a expressionS. | |
5ac34ac3 | 693 | * The X_op field of the expressionS may only take certain values. |
fecd2382 RP |
694 | * Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT. |
695 | * Out: expressionS may have been modified: | |
696 | * 'foo-foo' symbol references cancelled to 0, | |
5ac34ac3 | 697 | * which changes X_op from O_subtract to O_constant. |
fecd2382 RP |
698 | * Unused fields zeroed to help expr(). |
699 | */ | |
700 | ||
701 | static void | |
c593cf41 | 702 | clean_up_expression (expressionP) |
dae92eab | 703 | expressionS *expressionP; |
fecd2382 | 704 | { |
5ac34ac3 | 705 | switch (expressionP->X_op) |
2ed83a59 | 706 | { |
5ac34ac3 ILT |
707 | case O_illegal: |
708 | case O_absent: | |
2ed83a59 | 709 | expressionP->X_add_number = 0; |
5ac34ac3 ILT |
710 | /* Fall through. */ |
711 | case O_big: | |
712 | case O_constant: | |
713 | case O_register: | |
2ed83a59 | 714 | expressionP->X_add_symbol = NULL; |
5ac34ac3 ILT |
715 | /* Fall through. */ |
716 | case O_symbol: | |
717 | case O_uminus: | |
718 | case O_bit_not: | |
719 | expressionP->X_op_symbol = NULL; | |
720 | break; | |
721 | case O_subtract: | |
722 | if (expressionP->X_op_symbol == expressionP->X_add_symbol | |
723 | || ((expressionP->X_op_symbol->sy_frag | |
724 | == expressionP->X_add_symbol->sy_frag) | |
ffffc8fb | 725 | && SEG_NORMAL (S_GET_SEGMENT (expressionP->X_add_symbol)) |
5ac34ac3 | 726 | && (S_GET_VALUE (expressionP->X_op_symbol) |
49864cfa | 727 | == S_GET_VALUE (expressionP->X_add_symbol)))) |
2ed83a59 | 728 | { |
5ac34ac3 | 729 | expressionP->X_op = O_constant; |
2ed83a59 | 730 | expressionP->X_add_symbol = NULL; |
5ac34ac3 | 731 | expressionP->X_op_symbol = NULL; |
fecd2382 | 732 | } |
5ac34ac3 ILT |
733 | break; |
734 | default: | |
735 | break; | |
fecd2382 | 736 | } |
f2f7d044 | 737 | } |
fecd2382 RP |
738 | \f |
739 | /* Expression parser. */ | |
740 | ||
741 | /* | |
742 | * We allow an empty expression, and just assume (absolute,0) silently. | |
743 | * Unary operators and parenthetical expressions are treated as operands. | |
744 | * As usual, Q==quantity==operand, O==operator, X==expression mnemonics. | |
745 | * | |
746 | * We used to do a aho/ullman shift-reduce parser, but the logic got so | |
747 | * warped that I flushed it and wrote a recursive-descent parser instead. | |
748 | * Now things are stable, would anybody like to write a fast parser? | |
749 | * Most expressions are either register (which does not even reach here) | |
750 | * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. | |
751 | * So I guess it doesn't really matter how inefficient more complex expressions | |
752 | * are parsed. | |
753 | * | |
754 | * After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. | |
755 | * Also, we have consumed any leading or trailing spaces (operand does that) | |
756 | * and done all intervening operators. | |
5ac34ac3 ILT |
757 | * |
758 | * This returns the segment of the result, which will be | |
759 | * absolute_section or the segment of a symbol. | |
fecd2382 RP |
760 | */ |
761 | ||
49864cfa | 762 | #undef __ |
fecd2382 RP |
763 | #define __ O_illegal |
764 | ||
2ed83a59 KR |
765 | static const operatorT op_encoding[256] = |
766 | { /* maps ASCII->operators */ | |
767 | ||
768 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
769 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
770 | ||
771 | __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, | |
772 | __, __, O_multiply, O_add, __, O_subtract, __, O_divide, | |
773 | __, __, __, __, __, __, __, __, | |
774 | __, __, __, __, O_left_shift, __, O_right_shift, __, | |
775 | __, __, __, __, __, __, __, __, | |
776 | __, __, __, __, __, __, __, __, | |
777 | __, __, __, __, __, __, __, __, | |
778 | __, __, __, __, __, __, O_bit_exclusive_or, __, | |
779 | __, __, __, __, __, __, __, __, | |
780 | __, __, __, __, __, __, __, __, | |
781 | __, __, __, __, __, __, __, __, | |
782 | __, __, __, __, O_bit_inclusive_or, __, __, __, | |
783 | ||
784 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
785 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
786 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
787 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
788 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
789 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
790 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, | |
791 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ | |
792 | }; | |
fecd2382 RP |
793 | |
794 | ||
795 | /* | |
796 | * Rank Examples | |
797 | * 0 operand, (expression) | |
798 | * 1 + - | |
799 | * 2 & ^ ! | | |
800 | * 3 * / % << >> | |
5ac34ac3 | 801 | * 4 unary - unary ~ |
fecd2382 | 802 | */ |
5ac34ac3 ILT |
803 | static const operator_rankT op_rank[] = |
804 | { | |
805 | 0, /* O_illegal */ | |
806 | 0, /* O_absent */ | |
807 | 0, /* O_constant */ | |
808 | 0, /* O_symbol */ | |
809 | 0, /* O_register */ | |
810 | 0, /* O_bit */ | |
811 | 4, /* O_uminus */ | |
812 | 4, /* O_bit_now */ | |
813 | 3, /* O_multiply */ | |
814 | 3, /* O_divide */ | |
815 | 3, /* O_modulus */ | |
816 | 3, /* O_left_shift */ | |
817 | 3, /* O_right_shift */ | |
818 | 2, /* O_bit_inclusive_or */ | |
819 | 2, /* O_bit_or_not */ | |
820 | 2, /* O_bit_exclusive_or */ | |
821 | 2, /* O_bit_and */ | |
822 | 1, /* O_add */ | |
823 | 1, /* O_subtract */ | |
824 | }; | |
fecd2382 | 825 | \f |
5ac34ac3 | 826 | segT |
2ed83a59 | 827 | expr (rank, resultP) |
dae92eab KR |
828 | operator_rankT rank; /* Larger # is higher rank. */ |
829 | expressionS *resultP; /* Deliver result here. */ | |
fecd2382 | 830 | { |
5ac34ac3 | 831 | segT retval; |
2ed83a59 | 832 | expressionS right; |
dae92eab KR |
833 | operatorT op_left; |
834 | char c_left; /* 1st operator character. */ | |
835 | operatorT op_right; | |
836 | char c_right; | |
c593cf41 | 837 | |
2ed83a59 | 838 | know (rank >= 0); |
5ac34ac3 ILT |
839 | |
840 | retval = operand (resultP); | |
841 | ||
2ed83a59 | 842 | know (*input_line_pointer != ' '); /* Operand() gobbles spaces. */ |
5ac34ac3 | 843 | |
2ed83a59 | 844 | c_left = *input_line_pointer; /* Potential operator character. */ |
58d4951d | 845 | op_left = op_encoding[(unsigned char) c_left]; |
2ed83a59 | 846 | while (op_left != O_illegal && op_rank[(int) op_left] > rank) |
fecd2382 | 847 | { |
5ac34ac3 ILT |
848 | segT rightseg; |
849 | ||
2ed83a59 KR |
850 | input_line_pointer++; /*->after 1st character of operator. */ |
851 | /* Operators "<<" and ">>" have 2 characters. */ | |
852 | if (*input_line_pointer == c_left && (c_left == '<' || c_left == '>')) | |
5ac34ac3 ILT |
853 | ++input_line_pointer; |
854 | ||
855 | rightseg = expr (op_rank[(int) op_left], &right); | |
856 | if (right.X_op == O_absent) | |
fecd2382 | 857 | { |
5ac34ac3 ILT |
858 | as_warn ("missing operand; zero assumed"); |
859 | right.X_op = O_constant; | |
860 | right.X_add_number = 0; | |
d4c8cbd8 JL |
861 | right.X_add_symbol = NULL; |
862 | right.X_op_symbol = NULL; | |
fecd2382 | 863 | } |
5ac34ac3 | 864 | |
2ed83a59 | 865 | know (*input_line_pointer != ' '); |
5ac34ac3 | 866 | |
58d4951d ILT |
867 | if (retval == undefined_section) |
868 | { | |
869 | if (SEG_NORMAL (rightseg)) | |
870 | retval = rightseg; | |
871 | } | |
872 | else if (! SEG_NORMAL (retval)) | |
5ac34ac3 ILT |
873 | retval = rightseg; |
874 | else if (SEG_NORMAL (rightseg) | |
d4c8cbd8 JL |
875 | && retval != rightseg |
876 | #ifdef DIFF_EXPR_OK | |
877 | && op_left != O_subtract | |
878 | #endif | |
879 | ) | |
5ac34ac3 ILT |
880 | as_bad ("operation combines symbols in different segments"); |
881 | ||
2ed83a59 | 882 | c_right = *input_line_pointer; |
58d4951d | 883 | op_right = op_encoding[(unsigned char) c_right]; |
2ed83a59 | 884 | if (*input_line_pointer == c_right && (c_right == '<' || c_right == '>')) |
5ac34ac3 ILT |
885 | ++input_line_pointer; |
886 | ||
887 | know (op_right == O_illegal || op_rank[(int) op_right] <= op_rank[(int) op_left]); | |
888 | know ((int) op_left >= (int) O_multiply && (int) op_left <= (int) O_subtract); | |
889 | ||
c593cf41 SC |
890 | /* input_line_pointer->after right-hand quantity. */ |
891 | /* left-hand quantity in resultP */ | |
892 | /* right-hand quantity in right. */ | |
893 | /* operator in op_left. */ | |
5ac34ac3 ILT |
894 | |
895 | if (resultP->X_op == O_big) | |
fecd2382 | 896 | { |
5ac34ac3 ILT |
897 | as_warn ("left operand of %c is a %s; integer 0 assumed", |
898 | c_left, resultP->X_add_number > 0 ? "bignum" : "float"); | |
899 | resultP->X_op = O_constant; | |
900 | resultP->X_add_number = 0; | |
901 | resultP->X_add_symbol = NULL; | |
902 | resultP->X_op_symbol = NULL; | |
fecd2382 | 903 | } |
5ac34ac3 | 904 | if (right.X_op == O_big) |
fecd2382 | 905 | { |
5ac34ac3 ILT |
906 | as_warn ("right operand of %c is a %s; integer 0 assumed", |
907 | c_left, right.X_add_number > 0 ? "bignum" : "float"); | |
908 | right.X_op = O_constant; | |
909 | right.X_add_number = 0; | |
910 | right.X_add_symbol = NULL; | |
911 | right.X_op_symbol = NULL; | |
912 | } | |
913 | ||
914 | /* Optimize common cases. */ | |
915 | if (op_left == O_add && right.X_op == O_constant) | |
916 | { | |
917 | /* X + constant. */ | |
918 | resultP->X_add_number += right.X_add_number; | |
919 | } | |
920 | else if (op_left == O_subtract && right.X_op == O_constant) | |
921 | { | |
922 | /* X - constant. */ | |
923 | resultP->X_add_number -= right.X_add_number; | |
924 | } | |
925 | else if (op_left == O_add && resultP->X_op == O_constant) | |
926 | { | |
927 | /* Constant + X. */ | |
928 | resultP->X_op = right.X_op; | |
929 | resultP->X_add_symbol = right.X_add_symbol; | |
930 | resultP->X_op_symbol = right.X_op_symbol; | |
931 | resultP->X_add_number += right.X_add_number; | |
932 | retval = rightseg; | |
933 | } | |
934 | else if (resultP->X_op == O_constant && right.X_op == O_constant) | |
935 | { | |
936 | /* Constant OP constant. */ | |
937 | offsetT v = right.X_add_number; | |
938 | if (v == 0 && (op_left == O_divide || op_left == O_modulus)) | |
fecd2382 | 939 | { |
5ac34ac3 ILT |
940 | as_warn ("division by zero"); |
941 | v = 1; | |
fecd2382 | 942 | } |
5ac34ac3 | 943 | switch (op_left) |
fecd2382 | 944 | { |
5ac34ac3 ILT |
945 | case O_multiply: resultP->X_add_number *= v; break; |
946 | case O_divide: resultP->X_add_number /= v; break; | |
947 | case O_modulus: resultP->X_add_number %= v; break; | |
948 | case O_left_shift: resultP->X_add_number <<= v; break; | |
949 | case O_right_shift: resultP->X_add_number >>= v; break; | |
950 | case O_bit_inclusive_or: resultP->X_add_number |= v; break; | |
951 | case O_bit_or_not: resultP->X_add_number |= ~v; break; | |
952 | case O_bit_exclusive_or: resultP->X_add_number ^= v; break; | |
953 | case O_bit_and: resultP->X_add_number &= v; break; | |
954 | case O_add: resultP->X_add_number += v; break; | |
955 | case O_subtract: resultP->X_add_number -= v; break; | |
956 | default: abort (); | |
fecd2382 | 957 | } |
5ac34ac3 ILT |
958 | } |
959 | else if (resultP->X_op == O_symbol | |
960 | && right.X_op == O_symbol | |
961 | && (op_left == O_add | |
962 | || op_left == O_subtract | |
963 | || (resultP->X_add_number == 0 | |
964 | && right.X_add_number == 0))) | |
965 | { | |
966 | /* Symbol OP symbol. */ | |
967 | resultP->X_op = op_left; | |
968 | resultP->X_op_symbol = right.X_add_symbol; | |
c593cf41 | 969 | if (op_left == O_add) |
5ac34ac3 ILT |
970 | resultP->X_add_number += right.X_add_number; |
971 | else if (op_left == O_subtract) | |
972 | resultP->X_add_number -= right.X_add_number; | |
973 | } | |
974 | else | |
975 | { | |
976 | /* The general case. */ | |
977 | resultP->X_add_symbol = make_expr_symbol (resultP); | |
978 | resultP->X_op_symbol = make_expr_symbol (&right); | |
979 | resultP->X_op = op_left; | |
980 | resultP->X_add_number = 0; | |
d4c8cbd8 | 981 | resultP->X_unsigned = 1; |
5ac34ac3 ILT |
982 | } |
983 | ||
2ed83a59 | 984 | op_left = op_right; |
fecd2382 | 985 | } /* While next operator is >= this rank. */ |
5ac34ac3 | 986 | |
009dc5e1 JL |
987 | /* The PA port needs this information. */ |
988 | if (resultP->X_add_symbol) | |
989 | resultP->X_add_symbol->sy_used = 1; | |
990 | ||
5ac34ac3 | 991 | return resultP->X_op == O_constant ? absolute_section : retval; |
fecd2382 RP |
992 | } |
993 | \f | |
994 | /* | |
995 | * get_symbol_end() | |
996 | * | |
997 | * This lives here because it belongs equally in expr.c & read.c. | |
998 | * Expr.c is just a branch office read.c anyway, and putting it | |
999 | * here lessens the crowd at read.c. | |
1000 | * | |
1001 | * Assume input_line_pointer is at start of symbol name. | |
1002 | * Advance input_line_pointer past symbol name. | |
1003 | * Turn that character into a '\0', returning its former value. | |
1004 | * This allows a string compare (RMS wants symbol names to be strings) | |
1005 | * of the symbol name. | |
1006 | * There will always be a char following symbol name, because all good | |
1007 | * lines end in end-of-line. | |
1008 | */ | |
1009 | char | |
2ed83a59 | 1010 | get_symbol_end () |
fecd2382 | 1011 | { |
dae92eab | 1012 | char c; |
2ed83a59 KR |
1013 | |
1014 | while (is_part_of_name (c = *input_line_pointer++)) | |
1015 | ; | |
1016 | *--input_line_pointer = 0; | |
1017 | return (c); | |
fecd2382 RP |
1018 | } |
1019 | ||
a39116f1 | 1020 | |
2ed83a59 KR |
1021 | unsigned int |
1022 | get_single_number () | |
a39116f1 | 1023 | { |
2ed83a59 KR |
1024 | expressionS exp; |
1025 | operand (&exp); | |
1026 | return exp.X_add_number; | |
1027 | ||
a39116f1 | 1028 | } |
2ed83a59 | 1029 | |
8b228fe9 | 1030 | /* end of expr.c */ |