]>
Commit | Line | Data |
---|---|---|
14c7c974 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
4f6e3300 A |
6 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.1 (the "License"). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
14c7c974 A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
4f6e3300 | 15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
14c7c974 A |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
4f6e3300 A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License. | |
14c7c974 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* preproc.c macro preprocessor for the Netwide Assembler | |
25 | * | |
26 | * The Netwide Assembler is copyright (C) 1996 Simon Tatham and | |
27 | * Julian Hall. All rights reserved. The software is | |
28 | * redistributable under the licence given in the file "Licence" | |
29 | * distributed in the NASM archive. | |
30 | * | |
31 | * initial version 18/iii/97 by Simon Tatham | |
32 | */ | |
33 | ||
34 | #include <stdio.h> | |
35 | #include <stdlib.h> | |
36 | #include <stddef.h> | |
37 | #include <string.h> | |
38 | #include <ctype.h> | |
39 | #include <limits.h> | |
40 | ||
41 | #include "nasm.h" | |
42 | #include "nasmlib.h" | |
43 | ||
44 | typedef struct SMacro SMacro; | |
45 | typedef struct MMacro MMacro; | |
46 | typedef struct Context Context; | |
47 | typedef struct Token Token; | |
48 | typedef struct Line Line; | |
49 | typedef struct Include Include; | |
50 | typedef struct Cond Cond; | |
51 | typedef struct IncPath IncPath; | |
52 | ||
53 | /* | |
54 | * Store the definition of a single-line macro. | |
55 | */ | |
56 | struct SMacro { | |
57 | SMacro *next; | |
58 | char *name; | |
59 | int casesense; | |
60 | int nparam; | |
61 | int in_progress; | |
62 | Token *expansion; | |
63 | }; | |
64 | ||
65 | /* | |
66 | * Store the definition of a multi-line macro. This is also used to | |
67 | * store the interiors of `%rep...%endrep' blocks, which are | |
68 | * effectively self-re-invoking multi-line macros which simply | |
69 | * don't have a name or bother to appear in the hash tables. %rep | |
70 | * blocks are signified by having a NULL `name' field. | |
71 | * | |
72 | * In a MMacro describing a `%rep' block, the `in_progress' field | |
73 | * isn't merely boolean, but gives the number of repeats left to | |
74 | * run. | |
75 | * | |
76 | * The `next' field is used for storing MMacros in hash tables; the | |
77 | * `next_active' field is for stacking them on istk entries. | |
78 | * | |
79 | * When a MMacro is being expanded, `params', `iline', `nparam', | |
80 | * `paramlen', `rotate' and `unique' are local to the invocation. | |
81 | */ | |
82 | struct MMacro { | |
83 | MMacro *next; | |
84 | char *name; | |
85 | int casesense; | |
86 | int nparam_min, nparam_max; | |
87 | int plus; /* is the last parameter greedy? */ | |
88 | int nolist; /* is this macro listing-inhibited? */ | |
89 | int in_progress; | |
90 | Token **defaults, *dlist; | |
91 | int ndefs; /* number of default parameters */ | |
92 | Line *expansion; | |
93 | ||
94 | MMacro *next_active; | |
95 | Token **params, *iline; | |
96 | int nparam, rotate, *paramlen; | |
97 | unsigned long unique; | |
98 | }; | |
99 | ||
100 | /* | |
101 | * The context stack is composed of a linked list of these. | |
102 | */ | |
103 | struct Context { | |
104 | Context *next; | |
105 | SMacro *localmac; | |
106 | char *name; | |
107 | unsigned long number; | |
108 | }; | |
109 | ||
110 | /* | |
111 | * This is the internal form which we break input lines up into. | |
112 | * Typically stored in linked lists. | |
113 | * | |
114 | * TOK_PS_OTHER is a token type used internally within | |
115 | * expand_smacro(), to denote a token which has already been | |
116 | * checked for being a potential macro, but may still be a context- | |
117 | * local label. | |
118 | * | |
119 | * Note that `type' serves a double meaning: TOK_SMAC_PARAM is not | |
120 | * necessarily used as-is, but is intended to denote the number of | |
121 | * the substituted parameter. So in the definition | |
122 | * | |
123 | * %define a(x,y) ( (x) & ~(y) ) | |
124 | * | |
125 | * the token representing `x' will have its type changed to | |
126 | * TOK_SMAC_PARAM, but the one representing `y' will be | |
127 | * TOK_SMAC_PARAM+1. | |
128 | * | |
129 | * TOK_INTERNAL_STRING is a dirty hack: it's a single string token | |
130 | * which doesn't need quotes around it. Used in the pre-include | |
131 | * mechanism as an alternative to trying to find a sensible type of | |
132 | * quote to use on the filename we were passed. | |
133 | */ | |
134 | struct Token { | |
135 | Token *next; | |
136 | char *text; | |
137 | SMacro *mac; /* associated macro for TOK_SMAC_END */ | |
138 | int type; | |
139 | }; | |
140 | enum { | |
141 | TOK_WHITESPACE = 1, TOK_COMMENT, TOK_ID, TOK_PREPROC_ID, TOK_STRING, | |
142 | TOK_NUMBER, TOK_SMAC_END, TOK_OTHER, TOK_PS_OTHER, TOK_SMAC_PARAM, | |
143 | TOK_INTERNAL_STRING | |
144 | }; | |
145 | ||
146 | /* | |
147 | * Multi-line macro definitions are stored as a linked list of | |
148 | * these, which is essentially a container to allow several linked | |
149 | * lists of Tokens. | |
150 | * | |
151 | * Note that in this module, linked lists are treated as stacks | |
152 | * wherever possible. For this reason, Lines are _pushed_ on to the | |
153 | * `expansion' field in MMacro structures, so that the linked list, | |
154 | * if walked, would give the macro lines in reverse order; this | |
155 | * means that we can walk the list when expanding a macro, and thus | |
156 | * push the lines on to the `expansion' field in _istk_ in reverse | |
157 | * order (so that when popped back off they are in the right | |
158 | * order). It may seem cockeyed, and it relies on my design having | |
159 | * an even number of steps in, but it works... | |
160 | * | |
161 | * Some of these structures, rather than being actual lines, are | |
162 | * markers delimiting the end of the expansion of a given macro. | |
163 | * This is for use in the cycle-tracking and %rep-handling code. | |
164 | * Such structures have `finishes' non-NULL, and `first' NULL. All | |
165 | * others have `finishes' NULL, but `first' may still be NULL if | |
166 | * the line is blank. | |
167 | */ | |
168 | struct Line { | |
169 | Line *next; | |
170 | MMacro *finishes; | |
171 | Token *first; | |
172 | }; | |
173 | ||
174 | /* | |
175 | * To handle an arbitrary level of file inclusion, we maintain a | |
176 | * stack (ie linked list) of these things. | |
177 | */ | |
178 | struct Include { | |
179 | Include *next; | |
180 | FILE *fp; | |
181 | Cond *conds; | |
182 | Line *expansion; | |
183 | char *fname; | |
184 | int lineno, lineinc; | |
185 | MMacro *mstk; /* stack of active macros/reps */ | |
186 | }; | |
187 | ||
188 | /* | |
189 | * Include search path. This is simply a list of strings which get | |
190 | * prepended, in turn, to the name of an include file, in an | |
191 | * attempt to find the file if it's not in the current directory. | |
192 | */ | |
193 | struct IncPath { | |
194 | IncPath *next; | |
195 | char *path; | |
196 | }; | |
197 | ||
198 | /* | |
199 | * Conditional assembly: we maintain a separate stack of these for | |
200 | * each level of file inclusion. (The only reason we keep the | |
201 | * stacks separate is to ensure that a stray `%endif' in a file | |
202 | * included from within the true branch of a `%if' won't terminate | |
203 | * it and cause confusion: instead, rightly, it'll cause an error.) | |
204 | */ | |
205 | struct Cond { | |
206 | Cond *next; | |
207 | int state; | |
208 | }; | |
209 | enum { | |
210 | /* | |
211 | * These states are for use just after %if or %elif: IF_TRUE | |
212 | * means the condition has evaluated to truth so we are | |
213 | * currently emitting, whereas IF_FALSE means we are not | |
214 | * currently emitting but will start doing so if a %else comes | |
215 | * up. In these states, all directives are admissible: %elif, | |
216 | * %else and %endif. (And of course %if.) | |
217 | */ | |
218 | COND_IF_TRUE, COND_IF_FALSE, | |
219 | /* | |
220 | * These states come up after a %else: ELSE_TRUE means we're | |
221 | * emitting, and ELSE_FALSE means we're not. In ELSE_* states, | |
222 | * any %elif or %else will cause an error. | |
223 | */ | |
224 | COND_ELSE_TRUE, COND_ELSE_FALSE, | |
225 | /* | |
226 | * This state means that we're not emitting now, and also that | |
227 | * nothing until %endif will be emitted at all. It's for use in | |
228 | * two circumstances: (i) when we've had our moment of emission | |
229 | * and have now started seeing %elifs, and (ii) when the | |
230 | * condition construct in question is contained within a | |
231 | * non-emitting branch of a larger condition construct. | |
232 | */ | |
233 | COND_NEVER | |
234 | }; | |
235 | #define emitting(x) ( (x) == COND_IF_TRUE || (x) == COND_ELSE_TRUE ) | |
236 | ||
237 | /* | |
238 | * Condition codes. Note that we use c_ prefix not C_ because C_ is | |
239 | * used in nasm.h for the "real" condition codes. At _this_ level, | |
240 | * we treat CXZ and ECXZ as condition codes, albeit non-invertible | |
241 | * ones, so we need a different enum... | |
242 | */ | |
243 | static char *conditions[] = { | |
244 | "a", "ae", "b", "be", "c", "cxz", "e", "ecxz", "g", "ge", "l", "le", | |
245 | "na", "nae", "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no", | |
246 | "np", "ns", "nz", "o", "p", "pe", "po", "s", "z" | |
247 | }; | |
248 | enum { | |
249 | c_A, c_AE, c_B, c_BE, c_C, c_CXZ, c_E, c_ECXZ, c_G, c_GE, c_L, c_LE, | |
250 | c_NA, c_NAE, c_NB, c_NBE, c_NC, c_NE, c_NG, c_NGE, c_NL, c_NLE, c_NO, | |
251 | c_NP, c_NS, c_NZ, c_O, c_P, c_PE, c_PO, c_S, c_Z | |
252 | }; | |
253 | static int inverse_ccs[] = { | |
254 | c_NA, c_NAE, c_NB, c_NBE, c_NC, -1, c_NE, -1, c_NG, c_NGE, c_NL, c_NLE, | |
255 | c_A, c_AE, c_B, c_BE, c_C, c_E, c_G, c_GE, c_L, c_LE, c_O, c_P, c_S, | |
256 | c_Z, c_NO, c_NP, c_PO, c_PE, c_NS, c_NZ | |
257 | }; | |
258 | ||
259 | /* | |
260 | * Directive names. | |
261 | */ | |
262 | static char *directives[] = { | |
263 | "%assign", "%clear", "%define", "%elif", "%elifctx", "%elifdef", | |
264 | "%elifid", "%elifidn", "%elifidni", "%elifnctx", "%elifndef", | |
265 | "%elifnid", "%elifnidn", "%elifnidni", "%elifnnum", "%elifnstr", | |
266 | "%elifnum", "%elifstr", "%else", "%endif", "%endm", "%endmacro", | |
267 | "%endrep", "%error", "%exitrep", "%iassign", "%idefine", "%if", | |
268 | "%ifctx", "%ifdef", "%ifid", "%ifidn", "%ifidni", "%ifnctx", | |
269 | "%ifndef", "%ifnid", "%ifnidn", "%ifnidni", "%ifnnum", | |
270 | "%ifnstr", "%ifnum", "%ifstr", "%imacro", "%include", "%line", | |
271 | "%macro", "%pop", "%push", "%rep", "%repl", "%rotate" | |
272 | }; | |
273 | enum { | |
274 | PP_ASSIGN, PP_CLEAR, PP_DEFINE, PP_ELIF, PP_ELIFCTX, PP_ELIFDEF, | |
275 | PP_ELIFID, PP_ELIFIDN, PP_ELIFIDNI, PP_ELIFNCTX, PP_ELIFNDEF, | |
276 | PP_ELIFNID, PP_ELIFNIDN, PP_ELIFNIDNI, PP_ELIFNNUM, PP_ELIFNSTR, | |
277 | PP_ELIFNUM, PP_ELIFSTR, PP_ELSE, PP_ENDIF, PP_ENDM, PP_ENDMACRO, | |
278 | PP_ENDREP, PP_ERROR, PP_EXITREP, PP_IASSIGN, PP_IDEFINE, PP_IF, | |
279 | PP_IFCTX, PP_IFDEF, PP_IFID, PP_IFIDN, PP_IFIDNI, PP_IFNCTX, | |
280 | PP_IFNDEF, PP_IFNID, PP_IFNIDN, PP_IFNIDNI, PP_IFNNUM, | |
281 | PP_IFNSTR, PP_IFNUM, PP_IFSTR, PP_IMACRO, PP_INCLUDE, PP_LINE, | |
282 | PP_MACRO, PP_POP, PP_PUSH, PP_REP, PP_REPL, PP_ROTATE | |
283 | }; | |
284 | ||
285 | ||
286 | static Context *cstk; | |
287 | static Include *istk; | |
288 | static IncPath *ipath = NULL; | |
289 | ||
290 | static efunc error; | |
291 | static evalfunc evaluate; | |
292 | ||
293 | static int pass; | |
294 | ||
295 | static unsigned long unique; /* unique identifier numbers */ | |
296 | ||
297 | static char *linesync, *outline; | |
298 | ||
299 | static Line *predef = NULL; | |
300 | ||
301 | static ListGen *list; | |
302 | ||
303 | /* | |
304 | * The number of hash values we use for the macro lookup tables. | |
305 | */ | |
306 | #define NHASH 31 | |
307 | ||
308 | /* | |
309 | * The current set of multi-line macros we have defined. | |
310 | */ | |
311 | static MMacro *mmacros[NHASH]; | |
312 | ||
313 | /* | |
314 | * The current set of single-line macros we have defined. | |
315 | */ | |
316 | static SMacro *smacros[NHASH]; | |
317 | ||
318 | /* | |
319 | * The multi-line macro we are currently defining, or the %rep | |
320 | * block we are currently reading, if any. | |
321 | */ | |
322 | static MMacro *defining; | |
323 | ||
324 | /* | |
325 | * The number of macro parameters to allocate space for at a time. | |
326 | */ | |
327 | #define PARAM_DELTA 16 | |
328 | ||
329 | /* | |
330 | * The standard macro set: defined as `static char *stdmac[]'. Also | |
331 | * gives our position in the macro set, when we're processing it. | |
332 | */ | |
333 | #include "macros.c" | |
334 | static char **stdmacpos; | |
335 | ||
336 | /* | |
337 | * The extra standard macros that come from the object format, if | |
338 | * any. | |
339 | */ | |
340 | static char **extrastdmac = NULL; | |
341 | int any_extrastdmac; | |
342 | ||
343 | /* | |
344 | * Forward declarations. | |
345 | */ | |
346 | static Token *expand_smacro (Token *tline); | |
347 | static void update_fileline (int which); | |
348 | ||
349 | /* | |
350 | * The pre-preprocessing stage... This function translates line | |
351 | * number indications as they emerge from GNU cpp (`# lineno "file" | |
352 | * flags') into NASM preprocessor line number indications (`%line | |
353 | * lineno file'). | |
354 | */ | |
355 | static char *prepreproc(char *line) { | |
356 | int lineno, fnlen; | |
357 | char *fname, *oldline; | |
358 | ||
359 | if (line[0] == '#' && line[1] == ' ') { | |
360 | oldline = line; | |
361 | fname = oldline+2; | |
362 | lineno = atoi(fname); | |
363 | fname += strspn(fname, "0123456789 "); | |
364 | if (*fname == '"') | |
365 | fname++; | |
366 | fnlen = strcspn(fname, "\""); | |
367 | line = nasm_malloc(20+fnlen); | |
368 | sprintf(line, "%%line %d %.*s", lineno, fnlen, fname); | |
369 | nasm_free (oldline); | |
370 | } | |
371 | return line; | |
372 | } | |
373 | ||
374 | /* | |
375 | * The hash function for macro lookups. Note that due to some | |
376 | * macros having case-insensitive names, the hash function must be | |
377 | * invariant under case changes. We implement this by applying a | |
378 | * perfectly normal hash function to the uppercase of the string. | |
379 | */ | |
380 | static int hash(char *s) { | |
381 | /* | |
382 | * Powers of three, mod 31. | |
383 | */ | |
384 | static const int multipliers[] = { | |
385 | 1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, | |
386 | 30, 28, 22, 4, 12, 5, 15, 14, 11, 2, 6, 18, 23, 7, 21 | |
387 | }; | |
388 | int h = 0; | |
389 | int i = 0; | |
390 | ||
391 | while (*s) { | |
392 | h += multipliers[i] * (unsigned char) (toupper(*s)); | |
393 | s++; | |
f083c6c3 | 394 | if (++i >= (int)sizeof(multipliers)/(int)sizeof(*multipliers)) |
14c7c974 A |
395 | i = 0; |
396 | } | |
397 | h %= NHASH; | |
398 | return h; | |
399 | } | |
400 | ||
401 | /* | |
402 | * Free a linked list of tokens. | |
403 | */ | |
404 | static void free_tlist (Token *list) { | |
405 | Token *t; | |
406 | while (list) { | |
407 | t = list; | |
408 | list = list->next; | |
409 | nasm_free (t->text); | |
410 | nasm_free (t); | |
411 | } | |
412 | } | |
413 | ||
414 | /* | |
415 | * Free a linked list of lines. | |
416 | */ | |
417 | static void free_llist (Line *list) { | |
418 | Line *l; | |
419 | while (list) { | |
420 | l = list; | |
421 | list = list->next; | |
422 | free_tlist (l->first); | |
423 | nasm_free (l); | |
424 | } | |
425 | } | |
426 | ||
427 | /* | |
428 | * Pop the context stack. | |
429 | */ | |
430 | static void ctx_pop (void) { | |
431 | Context *c = cstk; | |
432 | SMacro *smac, *s; | |
433 | ||
434 | cstk = cstk->next; | |
435 | smac = c->localmac; | |
436 | while (smac) { | |
437 | s = smac; | |
438 | smac = smac->next; | |
439 | nasm_free (s->name); | |
440 | free_tlist (s->expansion); | |
441 | nasm_free (s); | |
442 | } | |
443 | nasm_free (c->name); | |
444 | nasm_free (c); | |
445 | } | |
446 | ||
447 | /* | |
448 | * Generate a line synchronisation comment, to ensure the assembler | |
449 | * knows which source file the current output has really come from. | |
450 | */ | |
451 | static void line_sync (void) { | |
452 | char text[30+FILENAME_MAX]; | |
453 | sprintf(text, "%%line %d+%d %s", | |
454 | (istk->expansion ? istk->lineno - istk->lineinc : istk->lineno), | |
455 | (istk->expansion ? 0 : istk->lineinc), istk->fname); | |
456 | nasm_free (linesync); | |
457 | linesync = nasm_strdup(text); | |
458 | } | |
459 | ||
460 | #define BUF_DELTA 512 | |
461 | /* | |
462 | * Read a line from the top file in istk, handling multiple CR/LFs | |
463 | * at the end of the line read, and handling spurious ^Zs. Will | |
464 | * return lines from the standard macro set if this has not already | |
465 | * been done. | |
466 | */ | |
467 | static char *read_line (void) { | |
468 | char *buffer, *p, *q; | |
469 | int bufsize; | |
470 | ||
471 | if (stdmacpos) { | |
472 | if (*stdmacpos) { | |
473 | char *ret = nasm_strdup(*stdmacpos++); | |
474 | if (!*stdmacpos && any_extrastdmac) { | |
475 | stdmacpos = extrastdmac; | |
476 | any_extrastdmac = FALSE; | |
477 | return ret; | |
478 | } | |
479 | /* | |
480 | * Nasty hack: here we push the contents of `predef' on | |
481 | * to the top-level expansion stack, since this is the | |
482 | * most convenient way to implement the pre-include and | |
483 | * pre-define features. | |
484 | */ | |
485 | if (!*stdmacpos) { | |
486 | Line *pd, *l; | |
487 | Token *head, **tail, *t, *tt; | |
488 | ||
489 | for (pd = predef; pd; pd = pd->next) { | |
490 | head = NULL; | |
491 | tail = &head; | |
492 | for (t = pd->first; t; t = t->next) { | |
493 | tt = *tail = nasm_malloc(sizeof(Token)); | |
494 | tt->next = NULL; | |
495 | tail = &tt->next; | |
496 | tt->type = t->type; | |
497 | tt->text = nasm_strdup(t->text); | |
498 | tt->mac = t->mac; /* always NULL here, in fact */ | |
499 | } | |
500 | l = nasm_malloc(sizeof(Line)); | |
501 | l->next = istk->expansion; | |
502 | l->first = head; | |
503 | l->finishes = FALSE; | |
504 | istk->expansion = l; | |
505 | } | |
506 | } | |
507 | return ret; | |
508 | } else { | |
509 | stdmacpos = NULL; | |
510 | line_sync(); | |
511 | update_fileline(3); /* update __FILE__ and __LINE__ */ | |
512 | } | |
513 | } | |
514 | ||
515 | bufsize = BUF_DELTA; | |
516 | buffer = nasm_malloc(BUF_DELTA); | |
517 | p = buffer; | |
518 | while (1) { | |
519 | q = fgets(p, bufsize-(p-buffer), istk->fp); | |
520 | if (!q) | |
521 | break; | |
522 | p += strlen(p); | |
523 | if (p > buffer && p[-1] == '\n') { | |
524 | istk->lineno += istk->lineinc; | |
525 | update_fileline(1); /* update __LINE__ only */ | |
526 | break; | |
527 | } | |
528 | if (p-buffer > bufsize-10) { | |
529 | bufsize += BUF_DELTA; | |
530 | buffer = nasm_realloc(buffer, bufsize); | |
531 | } | |
532 | } | |
533 | ||
534 | if (!q && p == buffer) { | |
535 | nasm_free (buffer); | |
536 | return NULL; | |
537 | } | |
538 | ||
539 | /* | |
540 | * Play safe: remove CRs as well as LFs, if any of either are | |
541 | * present at the end of the line. | |
542 | */ | |
543 | while (p > buffer && (p[-1] == '\n' || p[-1] == '\r')) | |
544 | *--p = '\0'; | |
545 | ||
546 | /* | |
547 | * Handle spurious ^Z, which may be inserted into source files | |
548 | * by some file transfer utilities. | |
549 | */ | |
550 | buffer[strcspn(buffer, "\032")] = '\0'; | |
551 | ||
552 | list->line (LIST_READ, buffer); | |
553 | ||
554 | return buffer; | |
555 | } | |
556 | ||
557 | /* | |
558 | * Tokenise a line of text. This is a very simple process since we | |
559 | * don't need to parse the value out of e.g. numeric tokens: we | |
560 | * simply split one string into many. | |
561 | */ | |
562 | static Token *tokenise (char *line) { | |
563 | char *p = line; | |
564 | int type; | |
565 | Token *list = NULL; | |
566 | Token *t, **tail = &list; | |
567 | ||
568 | while (*line) { | |
569 | p = line; | |
570 | if (*p == '%' && | |
571 | (p[1] == '{' || p[1] == '!' || (p[1] == '%' && isidchar(p[2])) || | |
572 | p[1] == '$' || p[1] == '+' || p[1] == '-' || isidchar(p[1]))) { | |
573 | type = TOK_PREPROC_ID; | |
574 | p++; | |
575 | if (*p == '{') { | |
576 | p++; | |
577 | while (*p && *p != '}') { | |
578 | p[-1] = *p; | |
579 | p++; | |
580 | } | |
581 | p[-1] = '\0'; | |
582 | if (*p) p++; | |
583 | } else { | |
584 | if (*p == '!' || *p == '%' || *p == '$' || | |
585 | *p == '+' || *p == '-') p++; | |
586 | while (*p && isidchar(*p)) | |
587 | p++; | |
588 | } | |
589 | } else if (isidstart(*p) || (*p == '$' && isidstart(p[1]))) { | |
590 | type = TOK_ID; | |
591 | p++; | |
592 | while (*p && isidchar(*p)) | |
593 | p++; | |
594 | } else if (*p == '\'' || *p == '"') { | |
595 | /* | |
596 | * A string token. | |
597 | */ | |
598 | char c = *p; | |
599 | p++; | |
600 | type = TOK_STRING; | |
601 | while (*p && *p != c) | |
602 | p++; | |
603 | if (*p) p++; | |
604 | } else if (isnumstart(*p)) { | |
605 | /* | |
606 | * A number token. | |
607 | */ | |
608 | type = TOK_NUMBER; | |
609 | p++; | |
610 | while (*p && isnumchar(*p)) | |
611 | p++; | |
612 | } else if (isspace(*p)) { | |
613 | type = TOK_WHITESPACE; | |
614 | p++; | |
615 | while (*p && isspace(*p)) | |
616 | p++; | |
617 | /* | |
618 | * Whitespace just before end-of-line is discarded by | |
619 | * pretending it's a comment; whitespace just before a | |
620 | * comment gets lumped into the comment. | |
621 | */ | |
622 | if (!*p || *p == ';') { | |
623 | type = TOK_COMMENT; | |
624 | while (*p) p++; | |
625 | } | |
626 | } else if (*p == ';') { | |
627 | type = TOK_COMMENT; | |
628 | while (*p) p++; | |
629 | } else { | |
630 | /* | |
631 | * Anything else is an operator of some kind. We check | |
632 | * for all the double-character operators (>>, <<, //, | |
633 | * %%, <=, >=, ==, !=, <>, &&, ||, ^^), but anything | |
634 | * else is a single-character operator. | |
635 | */ | |
636 | type = TOK_OTHER; | |
637 | if ((p[0] == '>' && p[1] == '>') || | |
638 | (p[0] == '<' && p[1] == '<') || | |
639 | (p[0] == '/' && p[1] == '/') || | |
640 | (p[0] == '%' && p[1] == '%') || | |
641 | (p[0] == '<' && p[1] == '=') || | |
642 | (p[0] == '>' && p[1] == '=') || | |
643 | (p[0] == '=' && p[1] == '=') || | |
644 | (p[0] == '!' && p[1] == '=') || | |
645 | (p[0] == '<' && p[1] == '>') || | |
646 | (p[0] == '&' && p[1] == '&') || | |
647 | (p[0] == '|' && p[1] == '|') || | |
648 | (p[0] == '^' && p[1] == '^')) | |
649 | p++; | |
650 | p++; | |
651 | } | |
652 | if (type != TOK_COMMENT) { | |
653 | *tail = t = nasm_malloc (sizeof(Token)); | |
654 | tail = &t->next; | |
655 | t->next = NULL; | |
656 | t->type = type; | |
657 | t->text = nasm_malloc(1+p-line); | |
658 | strncpy(t->text, line, p-line); | |
659 | t->text[p-line] = '\0'; | |
660 | } | |
661 | line = p; | |
662 | } | |
663 | ||
664 | return list; | |
665 | } | |
666 | ||
667 | /* | |
668 | * Convert a line of tokens back into text. | |
669 | */ | |
670 | static char *detoken (Token *tlist) { | |
671 | Token *t; | |
672 | int len; | |
673 | char *line, *p; | |
674 | ||
675 | len = 0; | |
676 | for (t = tlist; t; t = t->next) { | |
677 | if (t->type == TOK_PREPROC_ID && t->text[1] == '!') { | |
678 | char *p = getenv(t->text+2); | |
679 | nasm_free (t->text); | |
680 | if (p) | |
681 | t->text = nasm_strdup(p); | |
682 | else | |
683 | t->text = NULL; | |
684 | } | |
685 | if (t->text) | |
686 | len += strlen(t->text); | |
687 | } | |
688 | p = line = nasm_malloc(len+1); | |
689 | for (t = tlist; t; t = t->next) { | |
690 | if (t->text) { | |
691 | strcpy (p, t->text); | |
692 | p += strlen(p); | |
693 | } | |
694 | } | |
695 | *p = '\0'; | |
696 | return line; | |
697 | } | |
698 | ||
699 | /* | |
700 | * A scanner, suitable for use by the expression evaluator, which | |
701 | * operates on a line of Tokens. Expects a pointer to a pointer to | |
702 | * the first token in the line to be passed in as its private_data | |
703 | * field. | |
704 | */ | |
705 | static int ppscan(void *private_data, struct tokenval *tokval) { | |
706 | Token **tlineptr = private_data; | |
707 | Token *tline; | |
708 | ||
709 | do { | |
710 | tline = *tlineptr; | |
711 | *tlineptr = tline ? tline->next : NULL; | |
712 | } while (tline && (tline->type == TOK_WHITESPACE || | |
713 | tline->type == TOK_COMMENT)); | |
714 | ||
715 | if (!tline) | |
716 | return tokval->t_type = TOKEN_EOS; | |
717 | ||
718 | if (tline->text[0] == '$' && !tline->text[1]) | |
719 | return tokval->t_type = TOKEN_HERE; | |
720 | if (tline->text[0] == '$' && tline->text[1] == '$' && !tline->text[1]) | |
721 | return tokval->t_type = TOKEN_BASE; | |
722 | ||
723 | if (tline->type == TOK_ID) { | |
724 | tokval->t_charptr = tline->text; | |
725 | if (tline->text[0] == '$') { | |
726 | tokval->t_charptr++; | |
727 | return tokval->t_type = TOKEN_ID; | |
728 | } | |
729 | ||
730 | /* | |
731 | * This is the only special case we actually need to worry | |
732 | * about in this restricted context. | |
733 | */ | |
734 | if (!nasm_stricmp(tline->text, "seg")) | |
735 | return tokval->t_type = TOKEN_SEG; | |
736 | ||
737 | return tokval->t_type = TOKEN_ID; | |
738 | } | |
739 | ||
740 | if (tline->type == TOK_NUMBER) { | |
741 | int rn_error; | |
742 | ||
743 | tokval->t_integer = readnum(tline->text, &rn_error); | |
744 | if (rn_error) | |
745 | return tokval->t_type = TOKEN_ERRNUM; | |
746 | tokval->t_charptr = NULL; | |
747 | return tokval->t_type = TOKEN_NUM; | |
748 | } | |
749 | ||
750 | if (tline->type == TOK_OTHER) { | |
751 | if (!strcmp(tline->text, "<<")) return tokval->t_type = TOKEN_SHL; | |
752 | if (!strcmp(tline->text, ">>")) return tokval->t_type = TOKEN_SHR; | |
753 | if (!strcmp(tline->text, "//")) return tokval->t_type = TOKEN_SDIV; | |
754 | if (!strcmp(tline->text, "%%")) return tokval->t_type = TOKEN_SMOD; | |
755 | if (!strcmp(tline->text, "==")) return tokval->t_type = TOKEN_EQ; | |
756 | if (!strcmp(tline->text, "<>")) return tokval->t_type = TOKEN_NE; | |
757 | if (!strcmp(tline->text, "!=")) return tokval->t_type = TOKEN_NE; | |
758 | if (!strcmp(tline->text, "<=")) return tokval->t_type = TOKEN_LE; | |
759 | if (!strcmp(tline->text, ">=")) return tokval->t_type = TOKEN_GE; | |
760 | if (!strcmp(tline->text, "&&")) return tokval->t_type = TOKEN_DBL_AND; | |
761 | if (!strcmp(tline->text, "^^")) return tokval->t_type = TOKEN_DBL_XOR; | |
762 | if (!strcmp(tline->text, "||")) return tokval->t_type = TOKEN_DBL_OR; | |
763 | } | |
764 | ||
765 | /* | |
766 | * We have no other options: just return the first character of | |
767 | * the token text. | |
768 | */ | |
769 | return tokval->t_type = tline->text[0]; | |
770 | } | |
771 | ||
772 | /* | |
773 | * Return the Context structure associated with a %$ token. Return | |
774 | * NULL, having _already_ reported an error condition, if the | |
775 | * context stack isn't deep enough for the supplied number of $ | |
776 | * signs. | |
777 | */ | |
778 | static Context *get_ctx (char *name) { | |
779 | Context *ctx; | |
780 | int i; | |
781 | ||
782 | if (!cstk) { | |
783 | error (ERR_NONFATAL|ERR_OFFBY1, "`%s': context stack is empty", name); | |
784 | return NULL; | |
785 | } | |
786 | ||
787 | i = 1; | |
788 | ctx = cstk; | |
789 | while (name[i+1] == '$') { | |
790 | i++; | |
791 | ctx = ctx->next; | |
792 | if (!ctx) { | |
793 | error (ERR_NONFATAL|ERR_OFFBY1, "`%s': context stack is only" | |
794 | " %d level%s deep", name, i-1, (i==2 ? "" : "s")); | |
795 | return NULL; | |
796 | } | |
797 | } | |
798 | return ctx; | |
799 | } | |
800 | ||
801 | /* | |
802 | * Compare a string to the name of an existing macro; this is a | |
803 | * simple wrapper which calls either strcmp or nasm_stricmp | |
804 | * depending on the value of the `casesense' parameter. | |
805 | */ | |
806 | static int mstrcmp(char *p, char *q, int casesense) { | |
807 | return casesense ? strcmp(p,q) : nasm_stricmp(p,q); | |
808 | } | |
809 | ||
810 | /* | |
811 | * Open an include file. This routine must always return a valid | |
812 | * file pointer if it returns - it's responsible for throwing an | |
813 | * ERR_FATAL and bombing out completely if not. It should also try | |
814 | * the include path one by one until it finds the file or reaches | |
815 | * the end of the path. | |
816 | */ | |
817 | static FILE *inc_fopen(char *file) { | |
818 | FILE *fp; | |
819 | char *prefix = "", *combine; | |
820 | IncPath *ip = ipath; | |
821 | int len = strlen(file); | |
822 | ||
823 | do { | |
824 | combine = nasm_malloc(strlen(prefix)+len+1); | |
825 | strcpy(combine, prefix); | |
826 | strcat(combine, file); | |
827 | fp = fopen(combine, "r"); | |
828 | nasm_free (combine); | |
829 | if (fp) | |
830 | return fp; | |
831 | prefix = ip ? ip->path : NULL; | |
832 | if (ip) | |
833 | ip = ip->next; | |
834 | } while (prefix); | |
835 | ||
836 | error (ERR_FATAL|ERR_OFFBY1, | |
837 | "unable to open include file `%s'", file); | |
838 | return NULL; /* never reached - placate compilers */ | |
839 | } | |
840 | ||
841 | /* | |
842 | * Determine if we should warn on defining a single-line macro of | |
843 | * name `name', with `nparam' parameters. If nparam is 0, will | |
844 | * return TRUE if _any_ single-line macro of that name is defined. | |
845 | * Otherwise, will return TRUE if a single-line macro with either | |
846 | * `nparam' or no parameters is defined. | |
847 | * | |
848 | * If a macro with precisely the right number of parameters is | |
849 | * defined, the address of the definition structure will be | |
850 | * returned in `defn'; otherwise NULL will be returned. If `defn' | |
851 | * is NULL, no action will be taken regarding its contents, and no | |
852 | * error will occur. | |
853 | * | |
854 | * Note that this is also called with nparam zero to resolve | |
855 | * `ifdef'. | |
856 | */ | |
857 | static int smacro_defined (char *name, int nparam, SMacro **defn) { | |
858 | SMacro *m; | |
859 | Context *ctx; | |
860 | char *p; | |
861 | ||
862 | if (name[0] == '%' && name[1] == '$') { | |
863 | ctx = get_ctx (name); | |
864 | if (!ctx) | |
865 | return FALSE; /* got to return _something_ */ | |
866 | m = ctx->localmac; | |
867 | p = name+1; | |
868 | p += strspn(p, "$"); | |
869 | } else { | |
870 | m = smacros[hash(name)]; | |
871 | p = name; | |
872 | } | |
873 | ||
874 | while (m) { | |
875 | if (!mstrcmp(m->name, p, m->casesense) && | |
876 | (nparam == 0 || m->nparam == 0 || nparam == m->nparam)) { | |
877 | if (defn) { | |
878 | if (nparam == m->nparam) | |
879 | *defn = m; | |
880 | else | |
881 | *defn = NULL; | |
882 | } | |
883 | return TRUE; | |
884 | } | |
885 | m = m->next; | |
886 | } | |
887 | return FALSE; | |
888 | } | |
889 | ||
890 | /* | |
891 | * Update the __FILE__ and __LINE__ macros. Specifically, update | |
892 | * __FILE__ if bit 1 of our argument is set, and update __LINE__ if | |
893 | * bit 0 is set. | |
894 | * | |
895 | * If the macros don't exist, a `%clear' must have happened, in | |
896 | * which case we should exit quite happily and carry on going. It's | |
897 | * not an error condition. | |
898 | */ | |
899 | static void update_fileline(int which) { | |
900 | SMacro *sm; | |
901 | char num[20]; | |
902 | ||
903 | if ((which & 3) && smacro_defined ("__FILE__", 0, &sm) && sm) { | |
904 | free_tlist(sm->expansion); | |
905 | sm->expansion = nasm_malloc(sizeof(Token)); | |
906 | sm->expansion->next = NULL; | |
907 | sm->expansion->mac = NULL; | |
908 | sm->expansion->type = TOK_STRING; | |
909 | sm->expansion->text = nasm_malloc(3+strlen(istk->fname)); | |
910 | /* FIXME: throw an error if both sorts of quote are present */ | |
911 | /* Better still, invent a way for us to cope with that case */ | |
912 | sprintf(sm->expansion->text, "\"%s\"", istk->fname); | |
913 | } | |
914 | ||
915 | if ((which & 1) && smacro_defined ("__LINE__", 0, &sm) && sm) { | |
916 | free_tlist(sm->expansion); | |
917 | sm->expansion = nasm_malloc(sizeof(Token)); | |
918 | sm->expansion->next = NULL; | |
919 | sm->expansion->mac = NULL; | |
920 | sm->expansion->type = TOK_NUMBER; | |
921 | sprintf(num, "%d", istk->lineno - istk->lineinc); | |
922 | sm->expansion->text = nasm_strdup(num); | |
923 | } | |
924 | } | |
925 | ||
926 | /* | |
927 | * Count and mark off the parameters in a multi-line macro call. | |
928 | * This is called both from within the multi-line macro expansion | |
929 | * code, and also to mark off the default parameters when provided | |
930 | * in a %macro definition line. | |
931 | */ | |
932 | static void count_mmac_params (Token *t, int *nparam, Token ***params) { | |
933 | int paramsize, brace; | |
934 | ||
935 | *nparam = paramsize = 0; | |
936 | *params = NULL; | |
937 | while (t) { | |
938 | if (*nparam >= paramsize) { | |
939 | paramsize += PARAM_DELTA; | |
940 | *params = nasm_realloc(*params, sizeof(**params) * paramsize); | |
941 | } | |
942 | if (t && t->type == TOK_WHITESPACE) | |
943 | t = t->next; | |
944 | brace = FALSE; | |
945 | if (t && t->type == TOK_OTHER && !strcmp(t->text, "{")) | |
946 | brace = TRUE; | |
947 | (*params)[(*nparam)++] = t; | |
948 | while (t && (t->type != TOK_OTHER || | |
949 | strcmp(t->text, brace ? "}" : ","))) | |
950 | t = t->next; | |
951 | if (t) { /* got a comma/brace */ | |
952 | t = t->next; | |
953 | if (brace) { | |
954 | /* | |
955 | * Now we've found the closing brace, look further | |
956 | * for the comma. | |
957 | */ | |
958 | if (t && t->type == TOK_WHITESPACE) | |
959 | t = t->next; | |
960 | if (t && (t->type != TOK_OTHER || strcmp(t->text, ","))) { | |
961 | error (ERR_NONFATAL|ERR_OFFBY1, | |
962 | "braces do not enclose all of macro parameter"); | |
963 | while (t && (t->type != TOK_OTHER || | |
964 | strcmp(t->text, ","))) | |
965 | t = t->next; | |
966 | } | |
967 | if (t) | |
968 | t = t->next; /* eat the comma */ | |
969 | } | |
970 | } | |
971 | else /* got EOL */ | |
972 | break; | |
973 | } | |
974 | } | |
975 | ||
976 | /* | |
977 | * Determine whether one of the various `if' conditions is true or | |
978 | * not. | |
979 | * | |
980 | * We must free the tline we get passed. | |
981 | */ | |
982 | static int if_condition (Token *tline, int i) { | |
983 | int j, casesense; | |
984 | Token *t, *tt, **tptr, *origline; | |
985 | struct tokenval tokval; | |
986 | expr *evalresult; | |
987 | ||
988 | origline = tline; | |
989 | ||
990 | switch (i) { | |
991 | case PP_IFCTX: case PP_ELIFCTX: | |
992 | case PP_IFNCTX: case PP_ELIFNCTX: | |
993 | j = FALSE; /* have we matched yet? */ | |
994 | if (!cstk) | |
995 | error(ERR_FATAL|ERR_OFFBY1, | |
996 | "`%s': context stack is empty", directives[i]); | |
997 | else while (tline) { | |
998 | if (tline->type == TOK_WHITESPACE) | |
999 | tline = tline->next; | |
1000 | if (!tline || tline->type != TOK_ID) { | |
1001 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1002 | "`%s' expects context identifiers", directives[i]); | |
1003 | free_tlist (origline); | |
1004 | return -1; | |
1005 | } | |
1006 | if (!nasm_stricmp(tline->text, cstk->name)) | |
1007 | j = TRUE; | |
1008 | tline = tline->next; | |
1009 | } | |
1010 | if (i == PP_IFNCTX || i == PP_ELIFNCTX) | |
1011 | j = !j; | |
1012 | free_tlist (origline); | |
1013 | return j; | |
1014 | ||
1015 | case PP_IFDEF: case PP_ELIFDEF: | |
1016 | case PP_IFNDEF: case PP_ELIFNDEF: | |
1017 | j = FALSE; /* have we matched yet? */ | |
1018 | while (tline) { | |
1019 | if (tline->type == TOK_WHITESPACE) | |
1020 | tline = tline->next; | |
1021 | if (!tline || (tline->type != TOK_ID && | |
1022 | (tline->type != TOK_PREPROC_ID || | |
1023 | tline->text[1] != '$'))) { | |
1024 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1025 | "`%%if%sdef' expects macro identifiers", | |
1026 | (i==PP_ELIFNDEF ? "n" : "")); | |
1027 | free_tlist (origline); | |
1028 | return -1; | |
1029 | } | |
1030 | if (smacro_defined(tline->text, 0, NULL)) | |
1031 | j = TRUE; | |
1032 | tline = tline->next; | |
1033 | } | |
1034 | if (i == PP_IFNDEF || i == PP_ELIFNDEF) | |
1035 | j = !j; | |
1036 | free_tlist (origline); | |
1037 | return j; | |
1038 | ||
1039 | case PP_IFIDN: case PP_ELIFIDN: case PP_IFNIDN: case PP_ELIFNIDN: | |
1040 | case PP_IFIDNI: case PP_ELIFIDNI: case PP_IFNIDNI: case PP_ELIFNIDNI: | |
1041 | tline = expand_smacro(tline); | |
1042 | t = tt = tline; | |
1043 | while (tt && (tt->type != TOK_OTHER || strcmp(tt->text, ","))) | |
1044 | tt = tt->next; | |
1045 | if (!tt) { | |
1046 | error(ERR_NONFATAL, "`%s' expects two comma-separated arguments"); | |
1047 | free_tlist (tline); | |
1048 | return -1; | |
1049 | } | |
1050 | tt = tt->next; | |
1051 | casesense = (i == PP_IFIDN || i == PP_ELIFIDN || | |
1052 | i == PP_IFNIDN || i == PP_ELIFNIDN); | |
1053 | j = TRUE; /* assume equality unless proved not */ | |
1054 | while ((t->type != TOK_OTHER || strcmp(t->text, ",")) && tt) { | |
1055 | if (tt->type == TOK_OTHER && !strcmp(tt->text, ",")) { | |
1056 | error(ERR_NONFATAL, "`%s': more than one comma on line", | |
1057 | directives[i]); | |
1058 | free_tlist (tline); | |
1059 | return -1; | |
1060 | } | |
1061 | if (t->type == TOK_WHITESPACE) { | |
1062 | t = t->next; | |
1063 | continue; | |
1064 | } else if (tt->type == TOK_WHITESPACE) { | |
1065 | tt = tt->next; | |
1066 | continue; | |
1067 | } else if (tt->type != t->type || | |
1068 | (casesense ? strcmp(tt->text, t->text) : | |
1069 | nasm_stricmp(tt->text, t->text))) { | |
1070 | j = FALSE; /* found mismatching tokens */ | |
1071 | break; | |
1072 | } else { | |
1073 | t = t->next; | |
1074 | tt = tt->next; | |
1075 | continue; | |
1076 | } | |
1077 | } | |
1078 | if ((t->type != TOK_OTHER || strcmp(t->text, ",")) || tt) | |
1079 | j = FALSE; /* trailing gunk on one end or other */ | |
1080 | if (i == PP_IFNIDN || i == PP_ELIFNIDN) | |
1081 | j = !j; | |
1082 | free_tlist (tline); | |
1083 | return j; | |
1084 | ||
1085 | case PP_IFID: case PP_ELIFID: case PP_IFNID: case PP_ELIFNID: | |
1086 | case PP_IFNUM: case PP_ELIFNUM: case PP_IFNNUM: case PP_ELIFNNUM: | |
1087 | case PP_IFSTR: case PP_ELIFSTR: case PP_IFNSTR: case PP_ELIFNSTR: | |
1088 | tline = expand_smacro(tline); | |
1089 | t = tline; | |
1090 | while (t && t->type == TOK_WHITESPACE) | |
1091 | t = t->next; | |
1092 | j = FALSE; /* placate optimiser */ | |
1093 | switch (i) { | |
1094 | case PP_IFID: case PP_ELIFID: case PP_IFNID: case PP_ELIFNID: | |
1095 | j = (t->type == TOK_ID); | |
1096 | break; | |
1097 | case PP_IFNUM: case PP_ELIFNUM: case PP_IFNNUM: case PP_ELIFNNUM: | |
1098 | j = (t->type == TOK_NUMBER); | |
1099 | break; | |
1100 | case PP_IFSTR: case PP_ELIFSTR: case PP_IFNSTR: case PP_ELIFNSTR: | |
1101 | j = (t->type == TOK_STRING); | |
1102 | break; | |
1103 | } | |
1104 | if (i == PP_IFNID || i == PP_ELIFNID || | |
1105 | i == PP_IFNNUM || i == PP_ELIFNNUM || | |
1106 | i == PP_IFNSTR || i == PP_ELIFNSTR) | |
1107 | j = !j; | |
1108 | free_tlist (tline); | |
1109 | return j; | |
1110 | ||
1111 | case PP_IF: case PP_ELIF: | |
1112 | t = tline = expand_smacro(tline); | |
1113 | tptr = &t; | |
1114 | tokval.t_type = TOKEN_INVALID; | |
1115 | evalresult = evaluate (ppscan, tptr, &tokval, | |
1116 | NULL, pass | 0x10, error, NULL); | |
1117 | free_tlist (tline); | |
1118 | if (!evalresult) | |
1119 | return -1; | |
1120 | if (tokval.t_type) | |
1121 | error(ERR_WARNING|ERR_OFFBY1, | |
1122 | "trailing garbage after expression ignored"); | |
1123 | if (!is_simple(evalresult)) { | |
1124 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1125 | "non-constant value given to `%s'", directives[i]); | |
1126 | return -1; | |
1127 | } | |
1128 | return reloc_value(evalresult) != 0; | |
1129 | ||
1130 | default: | |
1131 | error(ERR_FATAL|ERR_OFFBY1, | |
1132 | "preprocessor directive `%s' not yet implemented", | |
1133 | directives[i]); | |
1134 | free_tlist (origline); | |
1135 | return -1; /* yeah, right */ | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | /* | |
1140 | * Find out if a line contains a preprocessor directive, and deal | |
1141 | * with it if so. | |
1142 | * | |
1143 | * If a directive _is_ found, we are expected to free_tlist() the | |
1144 | * line. | |
1145 | * | |
1146 | * Return values go like this: | |
1147 | * | |
1148 | * bit 0 is set if a directive was found (so the line gets freed) | |
1149 | * bit 1 is set if a blank line should be emitted | |
1150 | * bit 2 is set if a re-sync line number comment should be emitted | |
1151 | * | |
1152 | * (bits 1 and 2 are mutually exclusive in that the rest of the | |
1153 | * preprocessor doesn't guarantee to be able to handle the case in | |
1154 | * which both are set) | |
1155 | */ | |
1156 | static int do_directive (Token *tline) { | |
1157 | int i, j, k, m, nparam, nolist; | |
1158 | char *p, *mname; | |
1159 | Include *inc; | |
1160 | Context *ctx; | |
1161 | Cond *cond; | |
1162 | SMacro *smac, **smhead; | |
1163 | MMacro *mmac; | |
1164 | Token *t, *tt, *param_start, *macro_start, *last, **tptr, *origline; | |
1165 | Line *l; | |
1166 | struct tokenval tokval; | |
1167 | expr *evalresult; | |
1168 | ||
1169 | origline = tline; | |
1170 | ||
1171 | if (tline && tline->type == TOK_WHITESPACE) | |
1172 | tline = tline->next; | |
1173 | if (!tline || tline->type != TOK_PREPROC_ID || | |
1174 | (tline->text[1]=='%' || tline->text[1]=='$' || tline->text[1]=='!')) | |
1175 | return 0; | |
1176 | ||
1177 | i = -1; | |
1178 | j = sizeof(directives)/sizeof(*directives); | |
1179 | while (j-i > 1) { | |
1180 | k = (j+i) / 2; | |
1181 | m = nasm_stricmp(tline->text, directives[k]); | |
1182 | if (m == 0) { | |
1183 | i = k; | |
1184 | j = -2; | |
1185 | break; | |
1186 | } else if (m < 0) { | |
1187 | j = k; | |
1188 | } else | |
1189 | i = k; | |
1190 | } | |
1191 | ||
1192 | /* | |
1193 | * If we're in a non-emitting branch of a condition construct, | |
1194 | * or walking to the end of an already terminated %rep block, | |
1195 | * we should ignore all directives except for condition | |
1196 | * directives. | |
1197 | */ | |
1198 | if (((istk->conds && !emitting(istk->conds->state)) || | |
1199 | (istk->mstk && !istk->mstk->in_progress)) && | |
1200 | i != PP_IF && i != PP_ELIF && | |
1201 | i != PP_IFCTX && i != PP_ELIFCTX && | |
1202 | i != PP_IFDEF && i != PP_ELIFDEF && | |
1203 | i != PP_IFID && i != PP_ELIFID && | |
1204 | i != PP_IFIDN && i != PP_ELIFIDN && | |
1205 | i != PP_IFIDNI && i != PP_ELIFIDNI && | |
1206 | i != PP_IFNCTX && i != PP_ELIFNCTX && | |
1207 | i != PP_IFNDEF && i != PP_ELIFNDEF && | |
1208 | i != PP_IFNID && i != PP_ELIFNID && | |
1209 | i != PP_IFNIDN && i != PP_ELIFNIDN && | |
1210 | i != PP_IFNIDNI && i != PP_ELIFNIDNI && | |
1211 | i != PP_IFNNUM && i != PP_ELIFNNUM && | |
1212 | i != PP_IFNSTR && i != PP_ELIFNSTR && | |
1213 | i != PP_IFNUM && i != PP_ELIFNUM && | |
1214 | i != PP_IFSTR && i != PP_ELIFSTR && | |
1215 | i != PP_ELSE && i != PP_ENDIF) | |
1216 | return 0; | |
1217 | ||
1218 | /* | |
1219 | * If we're defining a macro or reading a %rep block, we should | |
1220 | * ignore all directives except for %macro/%imacro (which | |
1221 | * generate an error), %endm/%endmacro, and (only if we're in a | |
1222 | * %rep block) %endrep. | |
1223 | */ | |
1224 | if (defining && i != PP_MACRO && i != PP_IMACRO && | |
1225 | i != PP_ENDMACRO && i != PP_ENDM && | |
1226 | (defining->name || i != PP_ENDREP)) | |
1227 | return 0; | |
1228 | ||
1229 | if (j != -2) { | |
1230 | error(ERR_NONFATAL|ERR_OFFBY1, "unknown preprocessor directive `%s'", | |
1231 | tline->text); | |
1232 | return 0; /* didn't get it */ | |
1233 | } | |
1234 | ||
1235 | switch (i) { | |
1236 | ||
1237 | case PP_CLEAR: | |
1238 | if (tline->next) | |
1239 | error(ERR_WARNING|ERR_OFFBY1, | |
1240 | "trailing garbage after `%%clear' ignored"); | |
1241 | for (j=0; j<NHASH; j++) { | |
1242 | while (mmacros[j]) { | |
1243 | MMacro *m = mmacros[j]; | |
1244 | mmacros[j] = mmacros[j]->next; | |
1245 | nasm_free (m->name); | |
1246 | free_tlist (m->dlist); | |
1247 | free_llist (m->expansion); | |
1248 | nasm_free (m); | |
1249 | } | |
1250 | while (smacros[j]) { | |
1251 | SMacro *s = smacros[j]; | |
1252 | smacros[j] = smacros[j]->next; | |
1253 | nasm_free (s->name); | |
1254 | free_tlist (s->expansion); | |
1255 | nasm_free (s); | |
1256 | } | |
1257 | } | |
1258 | free_tlist (origline); | |
1259 | return 3; | |
1260 | ||
1261 | case PP_INCLUDE: | |
1262 | tline = tline->next; | |
1263 | if (tline && tline->type == TOK_WHITESPACE) | |
1264 | tline = tline->next; | |
1265 | if (!tline || (tline->type != TOK_STRING && | |
1266 | tline->type != TOK_INTERNAL_STRING)) { | |
1267 | error(ERR_NONFATAL|ERR_OFFBY1, "`%%include' expects a file name"); | |
1268 | free_tlist (origline); | |
1269 | return 3; /* but we did _something_ */ | |
1270 | } | |
1271 | if (tline->next) | |
1272 | error(ERR_WARNING|ERR_OFFBY1, | |
1273 | "trailing garbage after `%%include' ignored"); | |
1274 | if (tline->type != TOK_INTERNAL_STRING) { | |
1275 | p = tline->text+1; /* point past the quote to the name */ | |
1276 | p[strlen(p)-1] = '\0'; /* remove the trailing quote */ | |
1277 | } else | |
1278 | p = tline->text; /* internal_string is easier */ | |
1279 | inc = nasm_malloc(sizeof(Include)); | |
1280 | inc->next = istk; | |
1281 | inc->conds = NULL; | |
1282 | inc->fp = inc_fopen(p); | |
1283 | inc->fname = nasm_strdup(p); | |
1284 | inc->lineno = inc->lineinc = 1; | |
1285 | inc->expansion = NULL; | |
1286 | inc->mstk = NULL; | |
1287 | istk = inc; | |
1288 | list->uplevel (LIST_INCLUDE); | |
1289 | update_fileline(3); /* update __FILE__ and __LINE__ */ | |
1290 | free_tlist (origline); | |
1291 | return 5; | |
1292 | ||
1293 | case PP_PUSH: | |
1294 | tline = tline->next; | |
1295 | if (tline && tline->type == TOK_WHITESPACE) | |
1296 | tline = tline->next; | |
1297 | if (!tline || tline->type != TOK_ID) { | |
1298 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1299 | "`%%push' expects a context identifier"); | |
1300 | free_tlist (origline); | |
1301 | return 3; /* but we did _something_ */ | |
1302 | } | |
1303 | if (tline->next) | |
1304 | error(ERR_WARNING|ERR_OFFBY1, | |
1305 | "trailing garbage after `%%push' ignored"); | |
1306 | ctx = nasm_malloc(sizeof(Context)); | |
1307 | ctx->next = cstk; | |
1308 | ctx->localmac = NULL; | |
1309 | ctx->name = nasm_strdup(tline->text); | |
1310 | ctx->number = unique++; | |
1311 | cstk = ctx; | |
1312 | free_tlist (origline); | |
1313 | break; | |
1314 | ||
1315 | case PP_REPL: | |
1316 | tline = tline->next; | |
1317 | if (tline && tline->type == TOK_WHITESPACE) | |
1318 | tline = tline->next; | |
1319 | if (!tline || tline->type != TOK_ID) { | |
1320 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1321 | "`%%repl' expects a context identifier"); | |
1322 | free_tlist (origline); | |
1323 | return 3; /* but we did _something_ */ | |
1324 | } | |
1325 | if (tline->next) | |
1326 | error(ERR_WARNING|ERR_OFFBY1, | |
1327 | "trailing garbage after `%%repl' ignored"); | |
1328 | if (!cstk) | |
1329 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1330 | "`%%repl': context stack is empty"); | |
1331 | else { | |
1332 | nasm_free (cstk->name); | |
1333 | cstk->name = nasm_strdup(tline->text); | |
1334 | } | |
1335 | free_tlist (origline); | |
1336 | break; | |
1337 | ||
1338 | case PP_POP: | |
1339 | if (tline->next) | |
1340 | error(ERR_WARNING|ERR_OFFBY1, | |
1341 | "trailing garbage after `%%pop' ignored"); | |
1342 | if (!cstk) | |
1343 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1344 | "`%%pop': context stack is already empty"); | |
1345 | else | |
1346 | ctx_pop(); | |
1347 | free_tlist (origline); | |
1348 | break; | |
1349 | ||
1350 | case PP_ERROR: | |
1351 | tline = tline->next; | |
1352 | if (tline && tline->type == TOK_WHITESPACE) | |
1353 | tline = tline->next; | |
1354 | if (!tline || tline->type != TOK_STRING) { | |
1355 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1356 | "`%%error' expects an error string"); | |
1357 | free_tlist (origline); | |
1358 | return 3; /* but we did _something_ */ | |
1359 | } | |
1360 | if (tline->next) | |
1361 | error(ERR_WARNING|ERR_OFFBY1, | |
1362 | "trailing garbage after `%%error' ignored"); | |
1363 | p = tline->text+1; /* point past the quote to the name */ | |
1364 | p[strlen(p)-1] = '\0'; /* remove the trailing quote */ | |
1365 | error(ERR_NONFATAL|ERR_OFFBY1, "user error: %s", p); | |
1366 | free_tlist (origline); | |
1367 | break; | |
1368 | ||
1369 | case PP_IF: | |
1370 | case PP_IFCTX: | |
1371 | case PP_IFDEF: | |
1372 | case PP_IFID: | |
1373 | case PP_IFIDN: | |
1374 | case PP_IFIDNI: | |
1375 | case PP_IFNCTX: | |
1376 | case PP_IFNDEF: | |
1377 | case PP_IFNID: | |
1378 | case PP_IFNIDN: | |
1379 | case PP_IFNIDNI: | |
1380 | case PP_IFNNUM: | |
1381 | case PP_IFNSTR: | |
1382 | case PP_IFNUM: | |
1383 | case PP_IFSTR: | |
1384 | if (istk->conds && !emitting(istk->conds->state)) | |
1385 | j = COND_NEVER; | |
1386 | else { | |
1387 | j = if_condition(tline->next, i); | |
1388 | tline->next = NULL; /* it got freed */ | |
1389 | free_tlist (origline); | |
1390 | if (j < 0) | |
1391 | return 3; | |
1392 | else | |
1393 | j = j ? COND_IF_TRUE : COND_IF_FALSE; | |
1394 | } | |
1395 | cond = nasm_malloc(sizeof(Cond)); | |
1396 | cond->next = istk->conds; | |
1397 | cond->state = j; | |
1398 | istk->conds = cond; | |
1399 | return (j == COND_IF_TRUE ? 3 : 1); | |
1400 | ||
1401 | case PP_ELIF: | |
1402 | case PP_ELIFCTX: | |
1403 | case PP_ELIFDEF: | |
1404 | case PP_ELIFID: | |
1405 | case PP_ELIFIDN: | |
1406 | case PP_ELIFIDNI: | |
1407 | case PP_ELIFNCTX: | |
1408 | case PP_ELIFNDEF: | |
1409 | case PP_ELIFNID: | |
1410 | case PP_ELIFNIDN: | |
1411 | case PP_ELIFNIDNI: | |
1412 | case PP_ELIFNNUM: | |
1413 | case PP_ELIFNSTR: | |
1414 | case PP_ELIFNUM: | |
1415 | case PP_ELIFSTR: | |
1416 | if (!istk->conds) | |
1417 | error(ERR_FATAL|ERR_OFFBY1, "`%s': no matching `%%if'", | |
1418 | directives[i]); | |
1419 | if (emitting(istk->conds->state) || istk->conds->state == COND_NEVER) | |
1420 | istk->conds->state = COND_NEVER; | |
1421 | else { | |
1422 | j = if_condition(tline->next, i); | |
1423 | tline->next = NULL; /* it got freed */ | |
1424 | free_tlist (origline); | |
1425 | if (j < 0) | |
1426 | return 3; | |
1427 | else | |
1428 | istk->conds->state = j ? COND_IF_TRUE : COND_IF_FALSE; | |
1429 | } | |
1430 | return (istk->conds->state == COND_IF_TRUE ? 5 : 1); | |
1431 | ||
1432 | case PP_ELSE: | |
1433 | if (tline->next) | |
1434 | error(ERR_WARNING|ERR_OFFBY1, | |
1435 | "trailing garbage after `%%else' ignored"); | |
1436 | if (!istk->conds) | |
1437 | error(ERR_FATAL|ERR_OFFBY1, | |
1438 | "`%%else': no matching `%%if'"); | |
1439 | if (emitting(istk->conds->state) || istk->conds->state == COND_NEVER) | |
1440 | istk->conds->state = COND_ELSE_FALSE; | |
1441 | else | |
1442 | istk->conds->state = COND_ELSE_TRUE; | |
1443 | free_tlist (origline); | |
1444 | return 5; | |
1445 | ||
1446 | case PP_ENDIF: | |
1447 | if (tline->next) | |
1448 | error(ERR_WARNING|ERR_OFFBY1, | |
1449 | "trailing garbage after `%%endif' ignored"); | |
1450 | if (!istk->conds) | |
1451 | error(ERR_FATAL|ERR_OFFBY1, | |
1452 | "`%%endif': no matching `%%if'"); | |
1453 | cond = istk->conds; | |
1454 | istk->conds = cond->next; | |
1455 | nasm_free (cond); | |
1456 | free_tlist (origline); | |
1457 | return 5; | |
1458 | ||
1459 | case PP_MACRO: | |
1460 | case PP_IMACRO: | |
1461 | if (defining) | |
1462 | error (ERR_FATAL|ERR_OFFBY1, | |
1463 | "`%%%smacro': already defining a macro", | |
1464 | (i == PP_IMACRO ? "i" : "")); | |
1465 | tline = tline->next; | |
1466 | if (tline && tline->type == TOK_WHITESPACE) | |
1467 | tline = tline->next; | |
1468 | if (!tline || tline->type != TOK_ID) { | |
1469 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1470 | "`%%%smacro' expects a macro name", | |
1471 | (i == PP_IMACRO ? "i" : "")); | |
1472 | return 3; | |
1473 | } | |
1474 | defining = nasm_malloc(sizeof(MMacro)); | |
1475 | defining->name = nasm_strdup(tline->text); | |
1476 | defining->casesense = (i == PP_MACRO); | |
1477 | defining->plus = FALSE; | |
1478 | defining->nolist = FALSE; | |
1479 | defining->in_progress = FALSE; | |
1480 | tline = tline->next; | |
1481 | if (tline && tline->type == TOK_WHITESPACE) | |
1482 | tline = tline->next; | |
1483 | if (!tline || tline->type != TOK_NUMBER) { | |
1484 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1485 | "`%%%smacro' expects a parameter count", | |
1486 | (i == PP_IMACRO ? "i" : "")); | |
1487 | defining->nparam_min = defining->nparam_max = 0; | |
1488 | } else { | |
1489 | defining->nparam_min = defining->nparam_max = | |
1490 | readnum(tline->text, &j); | |
1491 | if (j) | |
1492 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1493 | "unable to parse parameter count `%s'", tline->text); | |
1494 | } | |
1495 | if (tline && tline->next && tline->next->type == TOK_OTHER && | |
1496 | !strcmp(tline->next->text, "-")) { | |
1497 | tline = tline->next->next; | |
1498 | if (tline && tline->type == TOK_OTHER && | |
1499 | !strcmp(tline->text, "*")) | |
1500 | defining->nparam_max = INT_MAX; | |
1501 | else if (!tline || tline->type != TOK_NUMBER) | |
1502 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1503 | "`%%%smacro' expects a parameter count after `-'", | |
1504 | (i == PP_IMACRO ? "i" : "")); | |
1505 | else { | |
1506 | defining->nparam_max = readnum(tline->text, &j); | |
1507 | if (j) | |
1508 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1509 | "unable to parse parameter count `%s'", | |
1510 | tline->text); | |
1511 | if (defining->nparam_min > defining->nparam_max) | |
1512 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1513 | "minimum parameter count exceeds maximum"); | |
1514 | } | |
1515 | } | |
1516 | if (tline && tline->next && tline->next->type == TOK_OTHER && | |
1517 | !strcmp(tline->next->text, "+")) { | |
1518 | tline = tline->next; | |
1519 | defining->plus = TRUE; | |
1520 | } | |
1521 | if (tline && tline->next && tline->next->type == TOK_ID && | |
1522 | !nasm_stricmp(tline->next->text, ".nolist")) { | |
1523 | tline = tline->next; | |
1524 | defining->nolist = TRUE; | |
1525 | } | |
1526 | mmac = mmacros[hash(defining->name)]; | |
1527 | while (mmac) { | |
1528 | if (!strcmp(mmac->name, defining->name) && | |
1529 | (mmac->nparam_min<=defining->nparam_max || defining->plus) && | |
1530 | (defining->nparam_min<=mmac->nparam_max || mmac->plus)) { | |
1531 | error (ERR_WARNING|ERR_OFFBY1, | |
1532 | "redefining multi-line macro `%s'", defining->name); | |
1533 | break; | |
1534 | } | |
1535 | mmac = mmac->next; | |
1536 | } | |
1537 | /* | |
1538 | * Handle default parameters. | |
1539 | */ | |
1540 | if (tline && tline->next) { | |
1541 | defining->dlist = tline->next; | |
1542 | tline->next = NULL; | |
1543 | count_mmac_params (defining->dlist, &defining->ndefs, | |
1544 | &defining->defaults); | |
1545 | } else { | |
1546 | defining->dlist = NULL; | |
1547 | defining->defaults = NULL; | |
1548 | } | |
1549 | defining->expansion = NULL; | |
1550 | free_tlist (origline); | |
1551 | return 1; | |
1552 | ||
1553 | case PP_ENDM: | |
1554 | case PP_ENDMACRO: | |
1555 | if (!defining) { | |
1556 | error (ERR_NONFATAL|ERR_OFFBY1, "`%s': not defining a macro", | |
1557 | tline->text); | |
1558 | return 3; | |
1559 | } | |
1560 | k = hash(defining->name); | |
1561 | defining->next = mmacros[k]; | |
1562 | mmacros[k] = defining; | |
1563 | defining = NULL; | |
1564 | free_tlist (origline); | |
1565 | return 5; | |
1566 | ||
1567 | case PP_ROTATE: | |
1568 | if (tline->next && tline->next->type == TOK_WHITESPACE) | |
1569 | tline = tline->next; | |
1570 | t = expand_smacro(tline->next); | |
1571 | tline->next = NULL; | |
1572 | free_tlist (origline); | |
1573 | tline = t; | |
1574 | tptr = &t; | |
1575 | tokval.t_type = TOKEN_INVALID; | |
1576 | evalresult = evaluate (ppscan, tptr, &tokval, NULL, pass, error, NULL); | |
1577 | free_tlist (tline); | |
1578 | if (!evalresult) | |
1579 | return 3; | |
1580 | if (tokval.t_type) | |
1581 | error(ERR_WARNING|ERR_OFFBY1, | |
1582 | "trailing garbage after expression ignored"); | |
1583 | if (!is_simple(evalresult)) { | |
1584 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1585 | "non-constant value given to `%%rotate'"); | |
1586 | return 3; | |
1587 | } | |
1588 | mmac = istk->mstk; | |
1589 | while (mmac && !mmac->name) /* avoid mistaking %reps for macros */ | |
1590 | mmac = mmac->next_active; | |
1591 | if (!mmac) | |
1592 | error(ERR_NONFATAL, "`%rotate' invoked outside a macro call"); | |
1593 | mmac->rotate = mmac->rotate + reloc_value(evalresult); | |
1594 | if (mmac->rotate < 0) | |
1595 | mmac->rotate = mmac->nparam - (-mmac->rotate) % mmac->nparam; | |
1596 | mmac->rotate %= mmac->nparam; | |
1597 | return 1; | |
1598 | ||
1599 | case PP_REP: | |
1600 | nolist = FALSE; | |
1601 | tline = tline->next; | |
1602 | if (tline->next && tline->next->type == TOK_WHITESPACE) | |
1603 | tline = tline->next; | |
1604 | if (tline->next && tline->next->type == TOK_ID && | |
1605 | !nasm_stricmp(tline->next->text, ".nolist")) { | |
1606 | tline = tline->next; | |
1607 | nolist = TRUE; | |
1608 | } | |
1609 | t = expand_smacro(tline->next); | |
1610 | tline->next = NULL; | |
1611 | free_tlist (origline); | |
1612 | tline = t; | |
1613 | tptr = &t; | |
1614 | tokval.t_type = TOKEN_INVALID; | |
1615 | evalresult = evaluate (ppscan, tptr, &tokval, NULL, pass, error, NULL); | |
1616 | free_tlist (tline); | |
1617 | if (!evalresult) | |
1618 | return 3; | |
1619 | if (tokval.t_type) | |
1620 | error(ERR_WARNING|ERR_OFFBY1, | |
1621 | "trailing garbage after expression ignored"); | |
1622 | if (!is_simple(evalresult)) { | |
1623 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1624 | "non-constant value given to `%%rep'"); | |
1625 | return 3; | |
1626 | } | |
1627 | defining = nasm_malloc(sizeof(MMacro)); | |
1628 | defining->name = NULL; /* flags this macro as a %rep block */ | |
1629 | defining->casesense = 0; | |
1630 | defining->plus = FALSE; | |
1631 | defining->nolist = nolist; | |
1632 | defining->in_progress = reloc_value(evalresult) + 1; | |
1633 | defining->nparam_min = defining->nparam_max = 0; | |
1634 | defining->expansion = NULL; | |
1635 | defining->next_active = istk->mstk; | |
1636 | return 1; | |
1637 | ||
1638 | case PP_ENDREP: | |
1639 | if (!defining) { | |
1640 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1641 | "`%%endrep': no matching `%%rep'"); | |
1642 | return 3; | |
1643 | } | |
1644 | ||
1645 | /* | |
1646 | * Now we have a "macro" defined - although it has no name | |
1647 | * and we won't be entering it in the hash tables - we must | |
1648 | * push a macro-end marker for it on to istk->expansion. | |
1649 | * After that, it will take care of propagating itself (a | |
1650 | * macro-end marker line for a macro which is really a %rep | |
1651 | * block will cause the macro to be re-expanded, complete | |
1652 | * with another macro-end marker to ensure the process | |
1653 | * continues) until the whole expansion is forcibly removed | |
1654 | * from istk->expansion by a %exitrep. | |
1655 | */ | |
1656 | l = nasm_malloc(sizeof(Line)); | |
1657 | l->next = istk->expansion; | |
1658 | l->finishes = defining; | |
1659 | l->first = NULL; | |
1660 | istk->expansion = l; | |
1661 | ||
1662 | istk->mstk = defining; | |
1663 | ||
1664 | list->uplevel (defining->nolist ? LIST_MACRO_NOLIST : LIST_MACRO); | |
1665 | defining = NULL; | |
1666 | free_tlist (origline); | |
1667 | return 1; /* the expansion will line-sync */ | |
1668 | ||
1669 | case PP_EXITREP: | |
1670 | /* | |
1671 | * We must search along istk->expansion until we hit a | |
1672 | * macro-end marker for a macro with no name. Then we set | |
1673 | * its `in_progress' flag to 0. | |
1674 | */ | |
1675 | for (l = istk->expansion; l; l = l->next) | |
1676 | if (l->finishes && !l->finishes->name) | |
1677 | break; | |
1678 | ||
1679 | if (l->finishes && !l->finishes->name) | |
1680 | l->finishes->in_progress = 0; | |
1681 | else | |
1682 | error (ERR_NONFATAL, "`%%exitrep' not within `%%rep' block"); | |
1683 | free_tlist (origline); | |
1684 | return 1; /* the end marker will line-sync */ | |
1685 | ||
1686 | case PP_DEFINE: | |
1687 | case PP_IDEFINE: | |
1688 | tline = tline->next; | |
1689 | if (tline && tline->type == TOK_WHITESPACE) | |
1690 | tline = tline->next; | |
1691 | if (!tline || (tline->type != TOK_ID && | |
1692 | (tline->type != TOK_PREPROC_ID || | |
1693 | tline->text[1] != '$'))) { | |
1694 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1695 | "`%%%sdefine' expects a macro identifier", | |
1696 | (i == PP_IDEFINE ? "i" : "")); | |
1697 | free_tlist (origline); | |
1698 | return 3; | |
1699 | } | |
1700 | mname = tline->text; | |
1701 | if (tline->type == TOK_ID) { | |
1702 | p = tline->text; | |
1703 | smhead = &smacros[hash(mname)]; | |
1704 | } else { | |
1705 | ctx = get_ctx (tline->text); | |
1706 | if (ctx == NULL) | |
1707 | return 3; | |
1708 | else { | |
1709 | p = tline->text+1; | |
1710 | p += strspn(p, "$"); | |
1711 | smhead = &ctx->localmac; | |
1712 | } | |
1713 | } | |
1714 | last = tline; | |
1715 | param_start = tline = tline->next; | |
1716 | nparam = 0; | |
1717 | if (tline && tline->type == TOK_OTHER && !strcmp(tline->text, "(")) { | |
1718 | /* | |
1719 | * This macro has parameters. | |
1720 | */ | |
1721 | ||
1722 | tline = tline->next; | |
1723 | while (1) { | |
1724 | if (tline && tline->type == TOK_WHITESPACE) | |
1725 | tline = tline->next; | |
1726 | if (!tline) { | |
1727 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1728 | "parameter identifier expected"); | |
1729 | free_tlist (origline); | |
1730 | return 3; | |
1731 | } | |
1732 | if (tline->type != TOK_ID) { | |
1733 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1734 | "`%s': parameter identifier expected", | |
1735 | tline->text); | |
1736 | free_tlist (origline); | |
1737 | return 3; | |
1738 | } | |
1739 | tline->type = TOK_SMAC_PARAM + nparam++; | |
1740 | tline = tline->next; | |
1741 | if (tline && tline->type == TOK_WHITESPACE) | |
1742 | tline = tline->next; | |
1743 | if (tline && tline->type == TOK_OTHER && | |
1744 | !strcmp(tline->text, ",")) { | |
1745 | tline = tline->next; | |
1746 | continue; | |
1747 | } | |
1748 | if (!tline || tline->type != TOK_OTHER || | |
1749 | strcmp(tline->text, ")")) { | |
1750 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1751 | "`)' expected to terminate macro template"); | |
1752 | free_tlist (origline); | |
1753 | return 3; | |
1754 | } | |
1755 | break; | |
1756 | } | |
1757 | last = tline; | |
1758 | tline = tline->next; | |
1759 | } | |
1760 | if (tline && tline->type == TOK_WHITESPACE) | |
1761 | last = tline, tline = tline->next; | |
1762 | macro_start = NULL; | |
1763 | last->next = NULL; | |
1764 | t = tline; | |
1765 | while (t) { | |
1766 | if (t->type == TOK_ID) { | |
1767 | for (tt = param_start; tt; tt = tt->next) | |
1768 | if (tt->type >= TOK_SMAC_PARAM && | |
1769 | !strcmp(tt->text, t->text)) | |
1770 | t->type = tt->type; | |
1771 | } | |
1772 | tt = t->next; | |
1773 | t->next = macro_start; | |
1774 | macro_start = t; | |
1775 | t = tt; | |
1776 | } | |
1777 | /* | |
1778 | * Good. We now have a macro name, a parameter count, and a | |
1779 | * token list (in reverse order) for an expansion. We ought | |
1780 | * to be OK just to create an SMacro, store it, and let | |
1781 | * free_tlist have the rest of the line (which we have | |
1782 | * carefully re-terminated after chopping off the expansion | |
1783 | * from the end). | |
1784 | */ | |
1785 | if (smacro_defined (mname, nparam, &smac)) { | |
1786 | if (!smac) | |
1787 | error (ERR_WARNING|ERR_OFFBY1, | |
1788 | "single-line macro `%s' defined both with and" | |
1789 | " without parameters", mname); | |
1790 | else { | |
1791 | /* | |
1792 | * We're redefining, so we have to take over an | |
1793 | * existing SMacro structure. This means freeing | |
1794 | * what was already in it. | |
1795 | */ | |
1796 | nasm_free (smac->name); | |
1797 | free_tlist (smac->expansion); | |
1798 | } | |
1799 | } else { | |
1800 | smac = nasm_malloc(sizeof(SMacro)); | |
1801 | smac->next = *smhead; | |
1802 | *smhead = smac; | |
1803 | } | |
1804 | smac->name = nasm_strdup(p); | |
1805 | smac->casesense = (i == PP_DEFINE); | |
1806 | smac->nparam = nparam; | |
1807 | smac->expansion = macro_start; | |
1808 | smac->in_progress = FALSE; | |
1809 | free_tlist (origline); | |
1810 | return 3; | |
1811 | ||
1812 | case PP_ASSIGN: | |
1813 | case PP_IASSIGN: | |
1814 | tline = tline->next; | |
1815 | if (tline && tline->type == TOK_WHITESPACE) | |
1816 | tline = tline->next; | |
1817 | if (!tline || (tline->type != TOK_ID && | |
1818 | (tline->type != TOK_PREPROC_ID || | |
1819 | tline->text[1] != '$'))) { | |
1820 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1821 | "`%%%sassign' expects a macro identifier", | |
1822 | (i == PP_IASSIGN ? "i" : "")); | |
1823 | free_tlist (origline); | |
1824 | return 3; | |
1825 | } | |
1826 | mname = tline->text; | |
1827 | if (tline->type == TOK_ID) { | |
1828 | p = tline->text; | |
1829 | smhead = &smacros[hash(mname)]; | |
1830 | } else { | |
1831 | ctx = get_ctx (tline->text); | |
1832 | if (ctx == NULL) { | |
1833 | free_tlist (origline); | |
1834 | return 3; | |
1835 | } else { | |
1836 | p = tline->text+1; | |
1837 | p += strspn(p, "$"); | |
1838 | smhead = &ctx->localmac; | |
1839 | } | |
1840 | } | |
1841 | last = tline; | |
1842 | tline = tline->next; | |
1843 | last->next = NULL; | |
1844 | ||
1845 | tline = expand_smacro (tline); | |
1846 | t = tline; | |
1847 | tptr = &t; | |
1848 | tokval.t_type = TOKEN_INVALID; | |
1849 | evalresult = evaluate (ppscan, tptr, &tokval, NULL, pass, error, NULL); | |
1850 | free_tlist (tline); | |
1851 | if (!evalresult) { | |
1852 | free_tlist (origline); | |
1853 | return 3; | |
1854 | } | |
1855 | ||
1856 | if (tokval.t_type) | |
1857 | error(ERR_WARNING|ERR_OFFBY1, | |
1858 | "trailing garbage after expression ignored"); | |
1859 | ||
1860 | if (!is_simple(evalresult)) { | |
1861 | error(ERR_NONFATAL|ERR_OFFBY1, | |
1862 | "non-constant value given to `%%%sassign'", | |
1863 | (i == PP_IASSIGN ? "i" : "")); | |
1864 | free_tlist (origline); | |
1865 | return 3; | |
1866 | } | |
1867 | ||
1868 | macro_start = nasm_malloc(sizeof(*macro_start)); | |
1869 | macro_start->next = NULL; | |
1870 | { | |
1871 | char numbuf[20]; | |
1872 | sprintf(numbuf, "%ld", reloc_value(evalresult)); | |
1873 | macro_start->text = nasm_strdup(numbuf); | |
1874 | } | |
1875 | macro_start->mac = NULL; | |
1876 | macro_start->type = TOK_NUMBER; | |
1877 | ||
1878 | /* | |
1879 | * We now have a macro name, an implicit parameter count of | |
1880 | * zero, and a numeric token to use as an expansion. Create | |
1881 | * and store an SMacro. | |
1882 | */ | |
1883 | if (smacro_defined (mname, 0, &smac)) { | |
1884 | if (!smac) | |
1885 | error (ERR_WARNING|ERR_OFFBY1, | |
1886 | "single-line macro `%s' defined both with and" | |
1887 | " without parameters", mname); | |
1888 | else { | |
1889 | /* | |
1890 | * We're redefining, so we have to take over an | |
1891 | * existing SMacro structure. This means freeing | |
1892 | * what was already in it. | |
1893 | */ | |
1894 | nasm_free (smac->name); | |
1895 | free_tlist (smac->expansion); | |
1896 | } | |
1897 | } else { | |
1898 | smac = nasm_malloc(sizeof(SMacro)); | |
1899 | smac->next = *smhead; | |
1900 | *smhead = smac; | |
1901 | } | |
1902 | smac->name = nasm_strdup(p); | |
1903 | smac->casesense = (i == PP_ASSIGN); | |
1904 | smac->nparam = 0; | |
1905 | smac->expansion = macro_start; | |
1906 | smac->in_progress = FALSE; | |
1907 | free_tlist (origline); | |
1908 | return 3; | |
1909 | ||
1910 | case PP_LINE: | |
1911 | /* | |
1912 | * Syntax is `%line nnn[+mmm] [filename]' | |
1913 | */ | |
1914 | tline = tline->next; | |
1915 | if (tline && tline->type == TOK_WHITESPACE) | |
1916 | tline = tline->next; | |
1917 | if (!tline || tline->type != TOK_NUMBER) { | |
1918 | error (ERR_NONFATAL|ERR_OFFBY1, "`%%line' expects line number"); | |
1919 | free_tlist (origline); | |
1920 | return 3; | |
1921 | } | |
1922 | k = readnum(tline->text, &j); | |
1923 | m = 1; | |
1924 | tline = tline->next; | |
1925 | if (tline && tline->type == TOK_OTHER && !strcmp(tline->text, "+")) { | |
1926 | tline = tline->next; | |
1927 | if (!tline || tline->type != TOK_NUMBER) { | |
1928 | error (ERR_NONFATAL|ERR_OFFBY1, | |
1929 | "`%%line' expects line increment"); | |
1930 | free_tlist (origline); | |
1931 | return 3; | |
1932 | } | |
1933 | m = readnum(tline->text, &j); | |
1934 | tline = tline->next; | |
1935 | } | |
1936 | if (tline && tline->type == TOK_WHITESPACE) | |
1937 | tline = tline->next; | |
1938 | istk->lineno = k; | |
1939 | istk->lineinc = m; | |
1940 | update_fileline(3); /* update __FILE__ and __LINE__ */ | |
1941 | if (tline) { | |
1942 | char *s = detoken(tline); | |
1943 | nasm_free (istk->fname); | |
1944 | istk->fname = s; | |
1945 | } | |
1946 | free_tlist (origline); | |
1947 | return 5; | |
1948 | ||
1949 | default: | |
1950 | error(ERR_FATAL|ERR_OFFBY1, | |
1951 | "preprocessor directive `%s' not yet implemented", | |
1952 | directives[i]); | |
1953 | break; | |
1954 | } | |
1955 | return 3; | |
1956 | } | |
1957 | ||
1958 | /* | |
1959 | * Ensure that a macro parameter contains a condition code and | |
1960 | * nothing else. Return the condition code index if so, or -1 | |
1961 | * otherwise. | |
1962 | */ | |
1963 | static int find_cc (Token *t) { | |
1964 | Token *tt; | |
1965 | int i, j, k, m; | |
1966 | ||
1967 | if (t && t->type == TOK_WHITESPACE) | |
1968 | t = t->next; | |
1969 | if (t->type != TOK_ID) | |
1970 | return -1; | |
1971 | tt = t->next; | |
1972 | if (tt && tt->type == TOK_WHITESPACE) | |
1973 | tt = tt->next; | |
1974 | if (tt && (tt->type != TOK_OTHER || strcmp(tt->text, ","))) | |
1975 | return -1; | |
1976 | ||
1977 | i = -1; | |
1978 | j = sizeof(conditions)/sizeof(*conditions); | |
1979 | while (j-i > 1) { | |
1980 | k = (j+i) / 2; | |
1981 | m = nasm_stricmp(t->text, conditions[k]); | |
1982 | if (m == 0) { | |
1983 | i = k; | |
1984 | j = -2; | |
1985 | break; | |
1986 | } else if (m < 0) { | |
1987 | j = k; | |
1988 | } else | |
1989 | i = k; | |
1990 | } | |
1991 | if (j != -2) | |
1992 | return -1; | |
1993 | return i; | |
1994 | } | |
1995 | ||
1996 | /* | |
1997 | * Expand MMacro-local things: parameter references (%0, %n, %+n, | |
1998 | * %-n) and MMacro-local identifiers (%%foo). | |
1999 | */ | |
2000 | static Token *expand_mmac_params (Token *tline) { | |
2001 | Token *t, *tt, *ttt, **tail, *thead; | |
2002 | ||
2003 | tail = &thead; | |
2004 | thead = NULL; | |
2005 | ||
2006 | while (tline) { | |
2007 | if (tline->type == TOK_PREPROC_ID && | |
2008 | (tline->text[1] == '+' || tline->text[1] == '-' || | |
2009 | tline->text[1] == '%' || | |
2010 | (tline->text[1] >= '0' && tline->text[1] <= '9'))) { | |
2011 | char *text = NULL; | |
2012 | int type = 0, cc; /* type = 0 to placate optimisers */ | |
2013 | char tmpbuf[30]; | |
2014 | int n, i; | |
2015 | MMacro *mac; | |
2016 | ||
2017 | t = tline; | |
2018 | tline = tline->next; | |
2019 | ||
2020 | mac = istk->mstk; | |
2021 | while (mac && !mac->name) /* avoid mistaking %reps for macros */ | |
2022 | mac = mac->next_active; | |
2023 | if (!mac) | |
2024 | error(ERR_NONFATAL, "`%s': not in a macro call", t->text); | |
2025 | else switch (t->text[1]) { | |
2026 | /* | |
2027 | * We have to make a substitution of one of the | |
2028 | * forms %1, %-1, %+1, %%foo, %0. | |
2029 | */ | |
2030 | case '0': | |
2031 | type = TOK_NUMBER; | |
2032 | sprintf(tmpbuf, "%d", mac->nparam); | |
2033 | text = nasm_strdup(tmpbuf); | |
2034 | break; | |
2035 | case '%': | |
2036 | type = TOK_ID; | |
2037 | sprintf(tmpbuf, "..@%lu.", mac->unique); | |
2038 | text = nasm_malloc(strlen(tmpbuf)+strlen(t->text+2)+1); | |
2039 | strcpy(text, tmpbuf); | |
2040 | strcat(text, t->text+2); | |
2041 | break; | |
2042 | case '-': | |
2043 | n = atoi(t->text+2)-1; | |
2044 | if (n >= mac->nparam) | |
2045 | tt = NULL; | |
2046 | else { | |
2047 | if (mac->nparam > 1) | |
2048 | n = (n + mac->rotate) % mac->nparam; | |
2049 | tt = mac->params[n]; | |
2050 | } | |
2051 | cc = find_cc (tt); | |
2052 | if (cc == -1) { | |
2053 | error (ERR_NONFATAL|ERR_OFFBY1, | |
2054 | "macro parameter %d is not a condition code", | |
2055 | n+1); | |
2056 | text = NULL; | |
2057 | } else { | |
2058 | type = TOK_ID; | |
2059 | if (inverse_ccs[cc] == -1) { | |
2060 | error (ERR_NONFATAL|ERR_OFFBY1, | |
2061 | "condition code `%s' is not invertible", | |
2062 | conditions[cc]); | |
2063 | text = NULL; | |
2064 | } else | |
2065 | text = nasm_strdup(conditions[inverse_ccs[cc]]); | |
2066 | } | |
2067 | break; | |
2068 | case '+': | |
2069 | n = atoi(t->text+2)-1; | |
2070 | if (n >= mac->nparam) | |
2071 | tt = NULL; | |
2072 | else { | |
2073 | if (mac->nparam > 1) | |
2074 | n = (n + mac->rotate) % mac->nparam; | |
2075 | tt = mac->params[n]; | |
2076 | } | |
2077 | cc = find_cc (tt); | |
2078 | if (cc == -1) { | |
2079 | error (ERR_NONFATAL|ERR_OFFBY1, | |
2080 | "macro parameter %d is not a condition code", | |
2081 | n+1); | |
2082 | text = NULL; | |
2083 | } else { | |
2084 | type = TOK_ID; | |
2085 | text = nasm_strdup(conditions[cc]); | |
2086 | } | |
2087 | break; | |
2088 | default: | |
2089 | n = atoi(t->text+1)-1; | |
2090 | if (n >= mac->nparam) | |
2091 | tt = NULL; | |
2092 | else { | |
2093 | if (mac->nparam > 1) | |
2094 | n = (n + mac->rotate) % mac->nparam; | |
2095 | tt = mac->params[n]; | |
2096 | } | |
2097 | if (tt) { | |
2098 | for (i=0; i<mac->paramlen[n]; i++) { | |
2099 | ttt = *tail = nasm_malloc(sizeof(Token)); | |
2100 | ttt->next = NULL; | |
2101 | tail = &ttt->next; | |
2102 | ttt->type = tt->type; | |
2103 | ttt->text = nasm_strdup(tt->text); | |
2104 | ttt->mac = NULL; | |
2105 | tt = tt->next; | |
2106 | } | |
2107 | } | |
2108 | text = NULL; /* we've done it here */ | |
2109 | break; | |
2110 | } | |
2111 | nasm_free (t->text); | |
2112 | nasm_free (t); | |
2113 | if (text) { | |
2114 | t = *tail = nasm_malloc(sizeof(Token)); | |
2115 | t->next = NULL; | |
2116 | tail = &t->next; | |
2117 | t->type = type; | |
2118 | t->text = text; | |
2119 | t->mac = NULL; | |
2120 | } | |
2121 | continue; | |
2122 | } else { | |
2123 | t = *tail = tline; | |
2124 | tline = tline->next; | |
2125 | t->mac = NULL; | |
2126 | t->next = NULL; | |
2127 | tail = &t->next; | |
2128 | } | |
2129 | } | |
2130 | return thead; | |
2131 | } | |
2132 | ||
2133 | /* | |
2134 | * Expand all single-line macro calls made in the given line. | |
2135 | * Return the expanded version of the line. The original is deemed | |
2136 | * to be destroyed in the process. (In reality we'll just move | |
2137 | * Tokens from input to output a lot of the time, rather than | |
2138 | * actually bothering to destroy and replicate.) | |
2139 | */ | |
2140 | static Token *expand_smacro (Token *tline) { | |
2141 | Token *t, *tt, *mstart, **tail, *thead; | |
2142 | SMacro *head, *m; | |
2143 | Token **params; | |
2144 | int *paramsize; | |
2145 | int nparam, sparam, brackets; | |
2146 | char *p; | |
2147 | ||
2148 | tail = &thead; | |
2149 | thead = NULL; | |
2150 | ||
2151 | while (tline) { | |
2152 | while (tline && tline->type != TOK_ID && | |
2153 | (tline->type != TOK_PREPROC_ID || tline->text[1] != '$')) { | |
2154 | if (tline->type == TOK_SMAC_END) { | |
2155 | tline->mac->in_progress = FALSE; | |
2156 | t = tline; | |
2157 | tline = tline->next; | |
2158 | nasm_free (t); | |
2159 | } else { | |
2160 | t = *tail = tline; | |
2161 | tline = tline->next; | |
2162 | t->mac = NULL; | |
2163 | t->next = NULL; | |
2164 | tail = &t->next; | |
2165 | if (t->type == TOK_PS_OTHER) { | |
2166 | /* | |
2167 | * If we see a PS_OTHER, we must at the very | |
2168 | * least restore its correct token type. We | |
2169 | * should also check for a %$ token, since this | |
2170 | * is the point at which we expand context- | |
2171 | * local labels. | |
2172 | */ | |
2173 | t->type = TOK_ID; | |
2174 | if (t->text[0] == '%' && t->text[1] == '$') { | |
2175 | Context *c = get_ctx (t->text); | |
2176 | char *p, *q, buffer[40]; | |
2177 | ||
2178 | if (c) { | |
2179 | q = t->text+1; | |
2180 | q += strspn(q, "$"); | |
2181 | sprintf(buffer, "..@%lu.", c->number); | |
2182 | p = nasm_malloc (strlen(buffer)+strlen(q)+1); | |
2183 | strcpy (p, buffer); | |
2184 | strcat (p, q); | |
2185 | nasm_free (t->text); | |
2186 | t->text = p; | |
2187 | } | |
2188 | } | |
2189 | } | |
2190 | } | |
2191 | } | |
2192 | ||
2193 | if (!tline) | |
2194 | break; | |
2195 | /* | |
2196 | * We've hit an identifier. As in is_mmacro below, we first | |
2197 | * check whether the identifier is a single-line macro at | |
2198 | * all, then think about checking for parameters if | |
2199 | * necessary. | |
2200 | */ | |
2201 | if (tline->type == TOK_ID) { | |
2202 | head = smacros[hash(tline->text)]; | |
2203 | p = tline->text; | |
2204 | } else { | |
2205 | Context *ctx = get_ctx (tline->text); | |
2206 | if (ctx) { | |
2207 | p = tline->text+1; | |
2208 | p += strspn(p, "$"); | |
2209 | head = ctx->localmac; | |
2210 | } else { | |
2211 | tline->type = TOK_OTHER; /* so it will get copied above */ | |
2212 | continue; | |
2213 | } | |
2214 | } | |
2215 | for (m = head; m; m = m->next) | |
2216 | if (!mstrcmp(m->name, p, m->casesense)) | |
2217 | break; | |
2218 | if (!m || m->in_progress) { | |
2219 | /* | |
2220 | * Either we didn't find a macro, so this can't be a | |
2221 | * macro call, or we found a macro which was already in | |
2222 | * progress, in which case we don't _treat_ this as a | |
2223 | * macro call. Copy it through and ignore it. | |
2224 | */ | |
2225 | tline->type = TOK_PS_OTHER; /* so it will get copied above */ | |
2226 | continue; | |
2227 | } | |
2228 | mstart = tline; | |
2229 | if (m->nparam == 0) { | |
2230 | /* | |
2231 | * Simple case: the macro is parameterless. Discard the | |
2232 | * one token that the macro call took, and push the | |
2233 | * expansion back on the to-do stack. | |
2234 | */ | |
2235 | params = NULL; | |
2236 | paramsize = NULL; | |
2237 | } else { | |
2238 | /* | |
2239 | * Complicated case: at least one macro with this name | |
2240 | * exists and takes parameters. We must find the | |
2241 | * parameters in the call, count them, find the SMacro | |
2242 | * that corresponds to that form of the macro call, and | |
2243 | * substitute for the parameters when we expand. What a | |
2244 | * pain. | |
2245 | */ | |
2246 | nparam = sparam = 0; | |
2247 | params = NULL; | |
2248 | paramsize = NULL; | |
2249 | tline = tline->next; | |
2250 | if (tline && tline->type == TOK_WHITESPACE) | |
2251 | tline = tline->next; | |
2252 | if (!tline || tline->type != TOK_OTHER || | |
2253 | strcmp(tline->text, "(")) { | |
2254 | /* | |
2255 | * This macro wasn't called with parameters: ignore | |
2256 | * the call. (Behaviour borrowed from gnu cpp.) | |
2257 | */ | |
2258 | tline = mstart; | |
2259 | tline->type = TOK_PS_OTHER; | |
2260 | continue; | |
2261 | } | |
2262 | tline = tline->next; | |
2263 | while (1) { | |
2264 | if (tline && tline->type == TOK_WHITESPACE) | |
2265 | tline = tline->next; | |
2266 | if (!tline) { | |
2267 | error(ERR_NONFATAL|ERR_OFFBY1, | |
2268 | "macro call expects terminating `)'"); | |
2269 | break; | |
2270 | } | |
2271 | if (nparam >= sparam) { | |
2272 | sparam += PARAM_DELTA; | |
2273 | params = nasm_realloc (params, sparam*sizeof(Token *)); | |
2274 | paramsize = nasm_realloc (paramsize, sparam*sizeof(int)); | |
2275 | } | |
2276 | params[nparam] = tline; | |
2277 | paramsize[nparam] = 0; | |
2278 | brackets = 0; | |
2279 | if (tline && tline->type == TOK_OTHER && | |
2280 | !strcmp(tline->text, "{")) { | |
2281 | params[nparam] = tline = tline->next; | |
2282 | while (tline && (brackets > 0 || | |
2283 | tline->type != TOK_OTHER || | |
2284 | strcmp(tline->text, "}"))) { | |
2285 | tline = tline->next; | |
2286 | paramsize[nparam]++; | |
2287 | } | |
2288 | tline = tline->next; | |
2289 | if (tline && tline->type == TOK_WHITESPACE) | |
2290 | tline = tline->next; | |
2291 | if (tline && (tline->type != TOK_OTHER || | |
2292 | (strcmp(tline->text, ")") && | |
2293 | strcmp(tline->text, ",")))) { | |
2294 | error (ERR_NONFATAL|ERR_OFFBY1, "braces do not " | |
2295 | "enclose all of macro parameter"); | |
2296 | } | |
2297 | if (tline && tline->type == TOK_OTHER && | |
2298 | !strcmp(tline->text, ",")) | |
2299 | tline = tline->next; | |
2300 | } else { | |
2301 | while (tline && (brackets > 0 || | |
2302 | tline->type != TOK_OTHER || | |
2303 | (strcmp(tline->text, ",") && | |
2304 | strcmp(tline->text, ")")))) { | |
2305 | if (tline->type == TOK_OTHER && !tline->text[1]) | |
2306 | brackets += (tline->text[0] == '(' ? 1 : | |
2307 | tline->text[0] == ')' ? -1 : 0); | |
2308 | tline = tline->next; | |
2309 | paramsize[nparam]++; | |
2310 | } | |
2311 | } | |
2312 | nparam++; | |
2313 | if (tline && !strcmp(tline->text, ")")) | |
2314 | break; | |
2315 | if (tline && !strcmp(tline->text, ",")) | |
2316 | tline = tline->next; | |
2317 | } | |
2318 | while (m && m->nparam != nparam) { | |
2319 | while ( (m = m->next) ) | |
2320 | if (!strcmp(m->name, mstart->text)) | |
2321 | break; | |
2322 | } | |
2323 | if (!m) { | |
2324 | error (ERR_WARNING|ERR_OFFBY1|ERR_WARN_MNP, | |
2325 | "macro `%s' exists, but not taking %d parameters", | |
2326 | mstart->text, nparam); | |
2327 | nasm_free (params); | |
2328 | nasm_free (paramsize); | |
2329 | tline = mstart; | |
2330 | tline->type = TOK_PS_OTHER; | |
2331 | continue; | |
2332 | } | |
2333 | } | |
2334 | /* | |
2335 | * Expand the macro: we are placed on the last token of the | |
2336 | * call, so that we can easily split the call from the | |
2337 | * following tokens. We also start by pushing an SMAC_END | |
2338 | * token for the cycle removal. | |
2339 | */ | |
2340 | t = tline; | |
2341 | tline = tline->next; | |
2342 | t->next = NULL; | |
2343 | tt = nasm_malloc(sizeof(Token)); | |
2344 | tt->type = TOK_SMAC_END; | |
2345 | tt->text = NULL; | |
2346 | tt->mac = m; | |
2347 | m->in_progress = TRUE; | |
2348 | tt->next = tline; | |
2349 | tline = tt; | |
2350 | for (t = m->expansion; t; t = t->next) { | |
2351 | if (t->type >= TOK_SMAC_PARAM) { | |
2352 | Token *pcopy = tline, **ptail = &pcopy; | |
2353 | Token *ttt, *pt; | |
2354 | int i; | |
2355 | ||
2356 | ttt = params[t->type - TOK_SMAC_PARAM]; | |
2357 | for (i=0; i<paramsize[t->type-TOK_SMAC_PARAM]; i++) { | |
2358 | pt = *ptail = nasm_malloc(sizeof(Token)); | |
2359 | pt->next = tline; | |
2360 | ptail = &pt->next; | |
2361 | pt->text = nasm_strdup(ttt->text); | |
2362 | pt->type = ttt->type; | |
2363 | pt->mac = NULL; | |
2364 | ttt = ttt->next; | |
2365 | } | |
2366 | tline = pcopy; | |
2367 | } else { | |
2368 | tt = nasm_malloc(sizeof(Token)); | |
2369 | tt->type = t->type; | |
2370 | tt->text = nasm_strdup(t->text); | |
2371 | tt->mac = NULL; | |
2372 | tt->next = tline; | |
2373 | tline = tt; | |
2374 | } | |
2375 | } | |
2376 | ||
2377 | /* | |
2378 | * Having done that, get rid of the macro call, and clean | |
2379 | * up the parameters. | |
2380 | */ | |
2381 | nasm_free (params); | |
2382 | nasm_free (paramsize); | |
2383 | free_tlist (mstart); | |
2384 | } | |
2385 | ||
2386 | return thead; | |
2387 | } | |
2388 | ||
2389 | /* | |
2390 | * Determine whether the given line constitutes a multi-line macro | |
2391 | * call, and return the MMacro structure called if so. Doesn't have | |
2392 | * to check for an initial label - that's taken care of in | |
2393 | * expand_mmacro - but must check numbers of parameters. Guaranteed | |
2394 | * to be called with tline->type == TOK_ID, so the putative macro | |
2395 | * name is easy to find. | |
2396 | */ | |
2397 | static MMacro *is_mmacro (Token *tline, Token ***params_array) { | |
2398 | MMacro *head, *m; | |
2399 | Token **params; | |
2400 | int nparam; | |
2401 | ||
2402 | head = mmacros[hash(tline->text)]; | |
2403 | ||
2404 | /* | |
2405 | * Efficiency: first we see if any macro exists with the given | |
2406 | * name. If not, we can return NULL immediately. _Then_ we | |
2407 | * count the parameters, and then we look further along the | |
2408 | * list if necessary to find the proper MMacro. | |
2409 | */ | |
2410 | for (m = head; m; m = m->next) | |
2411 | if (!mstrcmp(m->name, tline->text, m->casesense)) | |
2412 | break; | |
2413 | if (!m) | |
2414 | return NULL; | |
2415 | ||
2416 | /* | |
2417 | * OK, we have a potential macro. Count and demarcate the | |
2418 | * parameters. | |
2419 | */ | |
2420 | count_mmac_params (tline->next, &nparam, ¶ms); | |
2421 | ||
2422 | /* | |
2423 | * So we know how many parameters we've got. Find the MMacro | |
2424 | * structure that handles this number. | |
2425 | */ | |
2426 | while (m) { | |
2427 | if (m->nparam_min <= nparam && (m->plus || nparam <= m->nparam_max)) { | |
2428 | /* | |
2429 | * This one is right. Just check if cycle removal | |
2430 | * prohibits us using it before we actually celebrate... | |
2431 | */ | |
2432 | if (m->in_progress) { | |
2433 | error (ERR_NONFATAL|ERR_OFFBY1, | |
2434 | "self-reference in multi-line macro `%s'", | |
2435 | m->name); | |
2436 | nasm_free (params); | |
2437 | return NULL; | |
2438 | } | |
2439 | /* | |
2440 | * It's right, and we can use it. Add its default | |
2441 | * parameters to the end of our list if necessary. | |
2442 | */ | |
2443 | if (m->defaults && nparam < m->nparam_min + m->ndefs) { | |
2444 | params = nasm_realloc (params, ((m->nparam_min+m->ndefs+1) * | |
2445 | sizeof(*params))); | |
2446 | while (nparam < m->nparam_min + m->ndefs) { | |
2447 | params[nparam] = m->defaults[nparam - m->nparam_min]; | |
2448 | nparam++; | |
2449 | } | |
2450 | } | |
2451 | /* | |
2452 | * If we've gone over the maximum parameter count (and | |
2453 | * we're in Plus mode), ignore parameters beyond | |
2454 | * nparam_max. | |
2455 | */ | |
2456 | if (m->plus && nparam > m->nparam_max) | |
2457 | nparam = m->nparam_max; | |
2458 | /* | |
2459 | * Then terminate the parameter list, and leave. | |
2460 | */ | |
2461 | if (!params) { /* need this special case */ | |
2462 | params = nasm_malloc(sizeof(*params)); | |
2463 | nparam = 0; | |
2464 | } | |
2465 | params[nparam] = NULL; | |
2466 | *params_array = params; | |
2467 | return m; | |
2468 | } | |
2469 | /* | |
2470 | * This one wasn't right: look for the next one with the | |
2471 | * same name. | |
2472 | */ | |
2473 | for (m = m->next; m; m = m->next) | |
2474 | if (!mstrcmp(m->name, tline->text, m->casesense)) | |
2475 | break; | |
2476 | } | |
2477 | ||
2478 | /* | |
2479 | * After all that, we didn't find one with the right number of | |
2480 | * parameters. Issue a warning, and fail to expand the macro. | |
2481 | */ | |
2482 | error (ERR_WARNING|ERR_OFFBY1|ERR_WARN_MNP, | |
2483 | "macro `%s' exists, but not taking %d parameters", | |
2484 | tline->text, nparam); | |
2485 | nasm_free (params); | |
2486 | return NULL; | |
2487 | } | |
2488 | ||
2489 | /* | |
2490 | * Expand the multi-line macro call made by the given line, if | |
2491 | * there is one to be expanded. If there is, push the expansion on | |
2492 | * istk->expansion and return 1 or 2, as according to whether a | |
2493 | * line sync is needed (2 if it is). Otherwise return 0. | |
2494 | */ | |
2495 | static int expand_mmacro (Token *tline) { | |
2496 | Token *label = NULL, **params, *t, *tt, *last = NULL; | |
2497 | MMacro *m = NULL; | |
2498 | Line *l, *ll; | |
2499 | int i, nparam, *paramlen; | |
2500 | int need_sync = FALSE; | |
2501 | ||
2502 | t = tline; | |
2503 | if (t && t->type == TOK_WHITESPACE) | |
2504 | t = t->next; | |
2505 | if (t && t->type == TOK_ID) { | |
2506 | m = is_mmacro (t, ¶ms); | |
2507 | if (!m) { | |
2508 | /* | |
2509 | * We have an id which isn't a macro call. We'll assume | |
2510 | * it might be a label; we'll also check to see if a | |
2511 | * colon follows it. Then, if there's another id after | |
2512 | * that lot, we'll check it again for macro-hood. | |
2513 | */ | |
2514 | last = t, t = t->next; | |
2515 | if (t && t->type == TOK_WHITESPACE) | |
2516 | last = t, t = t->next; | |
2517 | if (t && t->type == TOK_OTHER && !strcmp(t->text, ":")) | |
2518 | last = t, t = t->next; | |
2519 | if (t && t->type == TOK_WHITESPACE) | |
2520 | last = t, t = t->next; | |
2521 | if (t && t->type == TOK_ID) { | |
2522 | m = is_mmacro(t, ¶ms); | |
2523 | if (m) { | |
2524 | last->next = NULL; | |
2525 | label = tline; | |
2526 | tline = t; | |
2527 | } | |
2528 | } | |
2529 | } | |
2530 | } | |
2531 | if (!m) | |
2532 | return 0; | |
2533 | ||
2534 | /* | |
2535 | * If we're not already inside another macro expansion, we'd | |
2536 | * better push a line synchronisation to ensure we stay put on | |
2537 | * line numbering. | |
2538 | */ | |
2539 | if (!istk->expansion) | |
2540 | need_sync = TRUE; | |
2541 | ||
2542 | /* | |
2543 | * Fix up the parameters: this involves stripping leading and | |
2544 | * trailing whitespace, then stripping braces if they are | |
2545 | * present. | |
2546 | */ | |
2547 | for (nparam = 0; params[nparam]; nparam++); | |
2548 | paramlen = nparam ? nasm_malloc(nparam*sizeof(*paramlen)) : NULL; | |
2549 | ||
2550 | for (i = 0; params[i]; i++) { | |
2551 | int brace = FALSE; | |
2552 | int comma = (!m->plus || i < nparam-1); | |
2553 | ||
2554 | t = params[i]; | |
2555 | if (t && t->type == TOK_WHITESPACE) | |
2556 | t = t->next; | |
2557 | if (t && t->type == TOK_OTHER && !strcmp(t->text, "{")) | |
2558 | t = t->next, brace = TRUE, comma = FALSE; | |
2559 | params[i] = t; | |
2560 | paramlen[i] = 0; | |
2561 | while (t) { | |
2562 | if (!t) /* end of param because EOL */ | |
2563 | break; | |
2564 | if (comma && t->type == TOK_OTHER && !strcmp(t->text, ",")) | |
2565 | break; /* ... because we have hit a comma */ | |
2566 | if (comma && t->type == TOK_WHITESPACE && | |
2567 | t->next->type == TOK_OTHER && !strcmp(t->next->text, ",")) | |
2568 | break; /* ... or a space then a comma */ | |
2569 | if (brace && t->type == TOK_OTHER && !strcmp(t->text, "}")) | |
2570 | break; /* ... or a brace */ | |
2571 | t = t->next; | |
2572 | paramlen[i]++; | |
2573 | } | |
2574 | } | |
2575 | ||
2576 | /* | |
2577 | * OK, we have a MMacro structure together with a set of | |
2578 | * parameters. We must now go through the expansion and push | |
2579 | * copies of each Line on to istk->expansion. Substitution of | |
2580 | * parameter tokens and macro-local tokens doesn't get done | |
2581 | * until the single-line macro substitution process; this is | |
2582 | * because delaying them allows us to change the semantics | |
2583 | * later through %rotate. | |
2584 | * | |
2585 | * First, push an end marker on to istk->expansion, mark this | |
2586 | * macro as in progress, and set up its invocation-specific | |
2587 | * variables. | |
2588 | */ | |
2589 | ll = nasm_malloc(sizeof(Line)); | |
2590 | ll->next = istk->expansion; | |
2591 | ll->finishes = m; | |
2592 | ll->first = NULL; | |
2593 | istk->expansion = ll; | |
2594 | ||
2595 | m->in_progress = TRUE; | |
2596 | m->params = params; | |
2597 | m->iline = tline; | |
2598 | m->nparam = nparam; | |
2599 | m->rotate = 0; | |
2600 | m->paramlen = paramlen; | |
2601 | m->unique = unique++; | |
2602 | ||
2603 | m->next_active = istk->mstk; | |
2604 | istk->mstk = m; | |
2605 | ||
2606 | for (l = m->expansion; l; l = l->next) { | |
2607 | Token **tail; | |
2608 | ||
2609 | ll = nasm_malloc(sizeof(Line)); | |
2610 | ll->next = istk->expansion; | |
2611 | ll->finishes = NULL; | |
2612 | ll->first = NULL; | |
2613 | tail = &ll->first; | |
2614 | ||
2615 | for (t = l->first; t; t = t->next) { | |
2616 | tt = *tail = nasm_malloc(sizeof(Token)); | |
2617 | tt->next = NULL; | |
2618 | tail = &tt->next; | |
2619 | tt->type = t->type; | |
2620 | tt->text = nasm_strdup(t->text); | |
2621 | tt->mac = NULL; | |
2622 | } | |
2623 | ||
2624 | istk->expansion = ll; | |
2625 | ||
2626 | } | |
2627 | ||
2628 | /* | |
2629 | * If we had a label, push it on the front of the first line of | |
2630 | * the macro expansion. We must check that this doesn't give | |
2631 | * two consecutive TOK_WHITESPACE. | |
2632 | */ | |
2633 | if (label) { | |
2634 | if (last->type == TOK_WHITESPACE && | |
2635 | istk->expansion->first->type == TOK_WHITESPACE) { | |
2636 | Token *victim = istk->expansion->first; /* kill this whitespace */ | |
2637 | istk->expansion->first = victim->next; | |
2638 | nasm_free (victim->text); | |
2639 | nasm_free (victim); | |
2640 | } | |
2641 | last->next = istk->expansion->first; | |
2642 | istk->expansion->first = label; | |
2643 | } | |
2644 | ||
2645 | list->uplevel (m->nolist ? LIST_MACRO_NOLIST : LIST_MACRO); | |
2646 | ||
2647 | return need_sync ? 2 : 1; | |
2648 | } | |
2649 | ||
2650 | static void pp_reset (char *file, int apass, efunc errfunc, evalfunc eval, | |
2651 | ListGen *listgen) { | |
2652 | int h; | |
2653 | ||
2654 | error = errfunc; | |
2655 | cstk = NULL; | |
2656 | linesync = outline = NULL; | |
2657 | istk = nasm_malloc(sizeof(Include)); | |
2658 | istk->next = NULL; | |
2659 | istk->conds = NULL; | |
2660 | istk->expansion = NULL; | |
2661 | istk->mstk = NULL; | |
2662 | istk->fp = fopen(file, "r"); | |
2663 | istk->fname = nasm_strdup(file); | |
2664 | istk->lineno = istk->lineinc = 1; | |
2665 | if (!istk->fp) | |
2666 | error (ERR_FATAL|ERR_NOFILE, "unable to open input file `%s'", file); | |
2667 | defining = NULL; | |
2668 | for (h=0; h<NHASH; h++) { | |
2669 | mmacros[h] = NULL; | |
2670 | smacros[h] = NULL; | |
2671 | } | |
2672 | unique = 0; | |
2673 | stdmacpos = stdmac; | |
2674 | any_extrastdmac = (extrastdmac != NULL); | |
2675 | list = listgen; | |
2676 | evaluate = eval; | |
2677 | pass = apass; | |
2678 | } | |
2679 | ||
2680 | static char *pp_getline (void) { | |
2681 | char *line; | |
2682 | Token *tline; | |
2683 | int ret; | |
2684 | ||
2685 | if (outline) { | |
2686 | line = outline; | |
2687 | outline = NULL; | |
2688 | return line; | |
2689 | } | |
2690 | ||
2691 | while (1) { | |
2692 | /* | |
2693 | * Fetch a tokenised line, either from the macro-expansion | |
2694 | * buffer or from the input file. | |
2695 | */ | |
2696 | tline = NULL; | |
2697 | while (istk->expansion && istk->expansion->finishes) { | |
2698 | Line *l = istk->expansion; | |
2699 | if (!l->finishes->name && l->finishes->in_progress > 1) { | |
2700 | Line *ll; | |
2701 | ||
2702 | /* | |
2703 | * This is a macro-end marker for a macro with no | |
2704 | * name, which means it's not really a macro at all | |
2705 | * but a %rep block, and the `in_progress' field is | |
2706 | * more than 1, meaning that we still need to | |
2707 | * repeat. (1 means the natural last repetition; 0 | |
2708 | * means termination by %exitrep.) We have | |
2709 | * therefore expanded up to the %endrep, and must | |
2710 | * push the whole block on to the expansion buffer | |
2711 | * again. We don't bother to remove the macro-end | |
2712 | * marker: we'd only have to generate another one | |
2713 | * if we did. | |
2714 | */ | |
2715 | l->finishes->in_progress--; | |
2716 | for (l = l->finishes->expansion; l; l = l->next) { | |
2717 | Token *t, *tt, **tail; | |
2718 | ||
2719 | ll = nasm_malloc(sizeof(Line)); | |
2720 | ll->next = istk->expansion; | |
2721 | ll->finishes = NULL; | |
2722 | ll->first = NULL; | |
2723 | tail = &ll->first; | |
2724 | ||
2725 | for (t = l->first; t; t = t->next) { | |
2726 | if (t->text) { | |
2727 | tt = *tail = nasm_malloc(sizeof(Token)); | |
2728 | tt->next = NULL; | |
2729 | tail = &tt->next; | |
2730 | tt->type = t->type; | |
2731 | tt->text = nasm_strdup(t->text); | |
2732 | tt->mac = NULL; | |
2733 | } | |
2734 | } | |
2735 | ||
2736 | istk->expansion = ll; | |
2737 | } | |
2738 | line_sync(); | |
2739 | } else { | |
2740 | /* | |
2741 | * Check whether a `%rep' was started and not ended | |
2742 | * within this macro expansion. This can happen and | |
2743 | * should be detected. It's a fatal error because | |
2744 | * I'm too confused to work out how to recover | |
2745 | * sensibly from it. | |
2746 | */ | |
2747 | if (defining) { | |
2748 | if (defining->name) | |
2749 | error (ERR_PANIC, | |
2750 | "defining with name in expansion"); | |
2751 | else if (!istk->mstk->name) | |
2752 | error (ERR_PANIC, "istk->mstk has no name but" | |
2753 | " defining is set at end of expansion"); | |
2754 | else | |
2755 | error (ERR_FATAL, "`%%rep' without `%%endrep' within" | |
2756 | " expansion of macro `%s'", istk->mstk->name); | |
2757 | } | |
2758 | ||
2759 | if (istk->mstk->name) { | |
2760 | /* | |
2761 | * This was a real macro call, not a %rep, and | |
2762 | * therefore the parameter information needs to | |
2763 | * be freed. | |
2764 | */ | |
2765 | nasm_free(istk->mstk->params); | |
2766 | free_tlist(istk->mstk->iline); | |
2767 | nasm_free(istk->mstk->paramlen); | |
2768 | } | |
2769 | istk->mstk = istk->mstk->next_active; | |
2770 | l->finishes->in_progress = FALSE; | |
2771 | istk->expansion = l->next; | |
2772 | nasm_free (l); | |
2773 | list->downlevel (LIST_MACRO); | |
2774 | if (!istk->expansion) | |
2775 | line_sync(); | |
2776 | } | |
2777 | } | |
2778 | if (istk->expansion) { | |
2779 | char *p; | |
2780 | Line *l = istk->expansion; | |
2781 | tline = l->first; | |
2782 | istk->expansion = l->next; | |
2783 | nasm_free (l); | |
2784 | p = detoken(tline); | |
2785 | list->line (LIST_MACRO, p); | |
2786 | nasm_free(p); | |
2787 | if (!istk->expansion) | |
2788 | line_sync(); | |
2789 | } else { | |
2790 | line = read_line(); | |
2791 | while (!line) { | |
2792 | /* | |
2793 | * The current file has ended; work down the istk | |
2794 | * until we find a file we can read from. | |
2795 | */ | |
2796 | Include *i; | |
2797 | fclose(istk->fp); | |
2798 | if (istk->conds) | |
2799 | error(ERR_FATAL, "expected `%%endif' before end of file"); | |
2800 | i = istk; | |
2801 | istk = istk->next; | |
2802 | list->downlevel (LIST_INCLUDE); | |
2803 | nasm_free (i->fname); | |
2804 | nasm_free (i); | |
2805 | if (!istk) | |
2806 | return NULL; | |
2807 | else | |
2808 | line_sync(); | |
2809 | update_fileline(3); /* update __FILE__ and __LINE__ */ | |
2810 | line = read_line(); | |
2811 | } | |
2812 | line = prepreproc(line); | |
2813 | tline = tokenise(line); | |
2814 | nasm_free (line); | |
2815 | } | |
2816 | ||
2817 | /* | |
2818 | * We must expand MMacro parameters and MMacro-local labels | |
2819 | * _before_ we plunge into directive processing, to cope | |
2820 | * with things like `%define something %1' such as STRUC | |
2821 | * uses. Unless we're _defining_ a MMacro, in which case | |
2822 | * those tokens should be left alone to go into the | |
2823 | * definition. | |
2824 | */ | |
2825 | if (!defining) | |
2826 | tline = expand_mmac_params(tline); | |
2827 | ||
2828 | /* | |
2829 | * Check the line to see if it's a preprocessor directive. | |
2830 | */ | |
2831 | ret = do_directive(tline); | |
2832 | if (ret & 1) { | |
2833 | if (ret & 4) | |
2834 | line_sync(); | |
2835 | if ((ret & 2) && !stdmacpos) {/* give a blank line to the output */ | |
2836 | outline = nasm_strdup(""); | |
2837 | break; | |
2838 | } | |
2839 | else | |
2840 | continue; | |
2841 | } else if (defining) { | |
2842 | /* | |
2843 | * We're defining a multi-line macro. We emit nothing | |
2844 | * at all, not even a blank line (when we finish | |
2845 | * defining the macro, we'll emit a line-number | |
2846 | * directive so that we keep sync properly), and just | |
2847 | * shove the tokenised line on to the macro definition. | |
2848 | */ | |
2849 | Line *l = nasm_malloc(sizeof(Line)); | |
2850 | l->next = defining->expansion; | |
2851 | l->first = tline; | |
2852 | l->finishes = FALSE; | |
2853 | defining->expansion = l; | |
2854 | continue; | |
2855 | } else if (istk->conds && !emitting(istk->conds->state)) { | |
2856 | /* | |
2857 | * We're in a non-emitting branch of a condition block. | |
2858 | * Emit nothing at all, not even a blank line: when we | |
2859 | * emerge from the condition we'll give a line-number | |
2860 | * directive so we keep our place correctly. | |
2861 | */ | |
2862 | free_tlist(tline); | |
2863 | continue; | |
2864 | } else if (istk->mstk && !istk->mstk->in_progress) { | |
2865 | /* | |
2866 | * We're in a %rep block which has been terminated, so | |
2867 | * we're walking through to the %endrep without | |
2868 | * emitting anything. Emit nothing at all, not even a | |
2869 | * blank line: when we emerge from the %rep block we'll | |
2870 | * give a line-number directive so we keep our place | |
2871 | * correctly. | |
2872 | */ | |
2873 | free_tlist(tline); | |
2874 | continue; | |
2875 | } else { | |
2876 | tline = expand_smacro(tline); | |
2877 | ret = expand_mmacro(tline); | |
2878 | if (!ret) { | |
2879 | /* | |
2880 | * De-tokenise the line again, and emit it. | |
2881 | */ | |
2882 | line = detoken(tline); | |
2883 | free_tlist (tline); | |
2884 | outline = line; | |
2885 | break; | |
2886 | } else { | |
2887 | if (ret == 2) | |
2888 | line_sync(); | |
2889 | continue; /* expand_mmacro calls free_tlist */ | |
2890 | } | |
2891 | } | |
2892 | } | |
2893 | ||
2894 | /* | |
2895 | * Once we're out of this loop, outline _must_ be non-NULL. The | |
2896 | * only question is whether linesync is NULL or not. | |
2897 | */ | |
2898 | if (linesync) { | |
2899 | line = linesync; | |
2900 | linesync = NULL; | |
2901 | } else { | |
2902 | line = outline; | |
2903 | outline = NULL; | |
2904 | } | |
2905 | return line; | |
2906 | } | |
2907 | ||
2908 | static void pp_cleanup (void) { | |
2909 | int h; | |
2910 | ||
2911 | if (defining) { | |
2912 | error (ERR_NONFATAL, "end of file while still defining macro `%s'", | |
2913 | defining->name); | |
2914 | nasm_free (defining->name); | |
2915 | free_tlist (defining->dlist); | |
2916 | free_llist (defining->expansion); | |
2917 | nasm_free (defining); | |
2918 | } | |
2919 | nasm_free (linesync); /* might just be necessary */ | |
2920 | nasm_free (outline); /* really shouldn't be necessary */ | |
2921 | while (cstk) | |
2922 | ctx_pop(); | |
2923 | for (h=0; h<NHASH; h++) { | |
2924 | while (mmacros[h]) { | |
2925 | MMacro *m = mmacros[h]; | |
2926 | mmacros[h] = mmacros[h]->next; | |
2927 | nasm_free (m->name); | |
2928 | free_tlist (m->dlist); | |
2929 | nasm_free (m->defaults); | |
2930 | free_llist (m->expansion); | |
2931 | nasm_free (m); | |
2932 | } | |
2933 | while (smacros[h]) { | |
2934 | SMacro *s = smacros[h]; | |
2935 | smacros[h] = smacros[h]->next; | |
2936 | nasm_free (s->name); | |
2937 | free_tlist (s->expansion); | |
2938 | nasm_free (s); | |
2939 | } | |
2940 | } | |
2941 | while (istk) { | |
2942 | Include *i = istk; | |
2943 | istk = istk->next; | |
2944 | fclose(i->fp); | |
2945 | nasm_free (i->fname); | |
2946 | nasm_free (i); | |
2947 | } | |
2948 | while (cstk) | |
2949 | ctx_pop(); | |
2950 | } | |
2951 | ||
2952 | void pp_include_path (char *path) { | |
2953 | IncPath *i; | |
2954 | ||
2955 | i = nasm_malloc(sizeof(IncPath)); | |
2956 | i->path = nasm_strdup(path); | |
2957 | i->next = ipath; | |
2958 | ||
2959 | ipath = i; | |
2960 | } | |
2961 | ||
2962 | void pp_pre_include (char *fname) { | |
2963 | Token *inc, *space, *name; | |
2964 | Line *l; | |
2965 | ||
2966 | inc = nasm_malloc(sizeof(Token)); | |
2967 | inc->next = space = nasm_malloc(sizeof(Token)); | |
2968 | space->next = name = nasm_malloc(sizeof(Token)); | |
2969 | name->next = NULL; | |
2970 | ||
2971 | inc->type = TOK_PREPROC_ID; | |
2972 | inc->text = nasm_strdup("%include"); | |
2973 | space->type = TOK_WHITESPACE; | |
2974 | space->text = nasm_strdup(" "); | |
2975 | name->type = TOK_INTERNAL_STRING; | |
2976 | name->text = nasm_strdup(fname); | |
2977 | ||
2978 | inc->mac = space->mac = name->mac = NULL; | |
2979 | ||
2980 | l = nasm_malloc(sizeof(Line)); | |
2981 | l->next = predef; | |
2982 | l->first = inc; | |
2983 | l->finishes = FALSE; | |
2984 | predef = l; | |
2985 | } | |
2986 | ||
2987 | void pp_pre_define (char *definition) { | |
2988 | Token *def, *space, *name; | |
2989 | Line *l; | |
2990 | char *equals; | |
2991 | ||
2992 | equals = strchr(definition, '='); | |
2993 | ||
2994 | def = nasm_malloc(sizeof(Token)); | |
2995 | def->next = space = nasm_malloc(sizeof(Token)); | |
2996 | if (equals) | |
2997 | *equals = ' '; | |
2998 | space->next = name = tokenise(definition); | |
2999 | if (equals) | |
3000 | *equals = '='; | |
3001 | ||
3002 | def->type = TOK_PREPROC_ID; | |
3003 | def->text = nasm_strdup("%define"); | |
3004 | space->type = TOK_WHITESPACE; | |
3005 | space->text = nasm_strdup(" "); | |
3006 | ||
3007 | def->mac = space->mac = NULL; | |
3008 | ||
3009 | l = nasm_malloc(sizeof(Line)); | |
3010 | l->next = predef; | |
3011 | l->first = def; | |
3012 | l->finishes = FALSE; | |
3013 | predef = l; | |
3014 | } | |
3015 | ||
3016 | void pp_extra_stdmac (char **macros) { | |
3017 | extrastdmac = macros; | |
3018 | } | |
3019 | ||
3020 | Preproc nasmpp = { | |
3021 | pp_reset, | |
3022 | pp_getline, | |
3023 | pp_cleanup | |
3024 | }; |