| /* |
| * YAPP preprocessor (mimics NASM's preprocessor) |
| * |
| * Copyright (C) 2001 Michael Urman |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <util.h> |
| /*@unused@*/ RCSID("$Id$"); |
| |
| #include <libyasm.h> |
| |
| #include "yapp-preproc.h" |
| #include "yapp-token.h" |
| |
| #define ydebug(x) /* printf x */ |
| |
| typedef struct yasm_preproc_yapp { |
| yasm_preproc_base preproc; /* base structure */ |
| } yasm_preproc_yapp; |
| |
| yasm_preproc_module yasm_yapp_LTX_preproc; |
| |
| static int saved_length; |
| |
| static HAMT *macro_table; |
| |
| static YAPP_Output current_output; |
| YYSTYPE yapp_preproc_lval; |
| |
| /*@dependent@*/ yasm_linemap *yapp_preproc_linemap; |
| |
| /* Build source and macro representations */ |
| static SLIST_HEAD(source_head, source_s) source_head, macro_head, param_head; |
| static struct source_s { |
| SLIST_ENTRY(source_s) next; |
| YAPP_Token token; |
| } *src, *source_tail, *macro_tail, *param_tail; |
| typedef struct source_s source; |
| |
| /* don't forget what the nesting level says */ |
| static SLIST_HEAD(output_head, output_s) output_head; |
| static struct output_s { |
| SLIST_ENTRY(output_s) next; |
| YAPP_Output out; |
| } output, *out; |
| |
| /*****************************************************************************/ |
| /* macro support - to be moved to a separate file later (?) */ |
| /*****************************************************************************/ |
| typedef struct YAPP_Macro_s { |
| struct source_head macro_head; |
| struct source_head param_head; |
| enum { |
| YAPP_MACRO = 0, |
| YAPP_DEFINE |
| } type; |
| int args; |
| int fillargs; |
| int expanding; |
| } YAPP_Macro; |
| |
| YAPP_Macro * |
| yapp_macro_insert (char *name, int argc, int fillargs); |
| |
| void |
| yapp_macro_error_exists (YAPP_Macro *v); |
| |
| void |
| yapp_macro_error_sameargname (YAPP_Macro *v); |
| |
| YAPP_Macro * |
| yapp_define_insert (char *name, int argc, int fillargs); |
| |
| void |
| yapp_macro_delete (YAPP_Macro *ym); |
| |
| static YAPP_Macro * |
| yapp_macro_get (const char *key); |
| |
| struct source_head * |
| yapp_define_param_get (HAMT *hamt, const char *key); |
| |
| static void |
| replay_saved_tokens(char *ident, |
| struct source_head *from_head, |
| struct source_head *to_head, |
| source **to_tail); |
| |
| YAPP_Macro * |
| yapp_macro_insert (char *name, int argc, int fillargs) |
| { |
| YAPP_Macro *ym = yasm_xmalloc(sizeof(YAPP_Macro)); |
| ym->type = YAPP_MACRO; |
| ym->args = argc; |
| ym->fillargs = fillargs; |
| |
| memcpy(&ym->macro_head, ¯o_head, sizeof(macro_head)); |
| |
| SLIST_INIT(¯o_head); |
| macro_tail = SLIST_FIRST(¯o_head); |
| return ym; |
| } |
| |
| void |
| yapp_macro_error_exists (YAPP_Macro *v) |
| { |
| if (v) yasm_error_set(YASM_ERROR_VALUE, N_("Redefining macro of the same name %d:%d"), v->type, v->args); |
| } |
| |
| void |
| yapp_macro_error_sameargname (YAPP_Macro *v) |
| { |
| if (v) yasm_error_set(YASM_ERROR_VALUE, N_("Duplicate argument names in macro")); |
| } |
| |
| YAPP_Macro * |
| yapp_define_insert (char *name, int argc, int fillargs) |
| { |
| int zero = 0; |
| char *mungename = name; |
| YAPP_Macro *ym; |
| |
| ym = yapp_macro_get(name); |
| if (ym) { |
| if ((argc >= 0 && ym->args < 0) |
| || (argc < 0 && ym->args >= 0)) |
| { |
| yasm_warn_set(YASM_WARN_PREPROC, N_("Attempted %%define both with and without parameters")); |
| return NULL; |
| } |
| } |
| else if (argc >= 0) |
| { |
| /* insert placeholder for paramlisted defines */ |
| ym = yasm_xmalloc(sizeof(YAPP_Macro)); |
| ym->type = YAPP_DEFINE; |
| ym->args = argc; |
| ym->fillargs = fillargs; |
| ym->expanding = 0; |
| HAMT_insert(macro_table, name, (void *)ym, &zero, (void (*)(void *))yapp_macro_error_exists); |
| } |
| |
| /* now for the real one */ |
| ym = yasm_xmalloc(sizeof(YAPP_Macro)); |
| ym->type = YAPP_DEFINE; |
| ym->args = argc; |
| ym->fillargs = fillargs; |
| ym->expanding = 0; |
| |
| if (argc>=0) { |
| mungename = yasm_xmalloc(strlen(name)+8); |
| sprintf(mungename, "%s(%d)", name, argc); |
| } |
| |
| ydebug(("YAPP: +Inserting macro %s\n", mungename)); |
| |
| memcpy(&ym->macro_head, ¯o_head, sizeof(macro_head)); |
| memcpy(&ym->param_head, ¶m_head, sizeof(param_head)); |
| |
| HAMT_insert(macro_table, mungename, (void *)ym, &zero, (void (*)(void *))yapp_macro_error_exists); |
| |
| SLIST_INIT(¯o_head); |
| SLIST_INIT(¶m_head); |
| |
| macro_tail = SLIST_FIRST(¯o_head); |
| param_tail = SLIST_FIRST(¶m_head); |
| |
| return ym; |
| } |
| |
| void |
| yapp_macro_delete (YAPP_Macro *ym) |
| { |
| while (!SLIST_EMPTY(&ym->macro_head)) { |
| source *s = SLIST_FIRST(&ym->macro_head); |
| yasm_xfree(s); |
| SLIST_REMOVE_HEAD(&ym->macro_head, next); |
| } |
| yasm_xfree(ym); |
| } |
| |
| static YAPP_Macro * |
| yapp_macro_get (const char *key) |
| { |
| return (YAPP_Macro *)HAMT_search(macro_table, key); |
| } |
| |
| struct source_head * |
| yapp_define_param_get (HAMT *hamt, const char *key) |
| { |
| return (struct source_head *)HAMT_search(hamt, key); |
| } |
| |
| /*****************************************************************************/ |
| |
| void |
| append_processed_token(source *tok, struct source_head *to_head, source **to_tail); |
| |
| void |
| append_token(int token, struct source_head *to_head, source **to_tail); |
| |
| int |
| append_to_return(struct source_head *to_head, source **to_tail); |
| |
| int |
| eat_through_return(struct source_head *to_head, source **to_tail); |
| |
| int |
| yapp_get_ident(const char *synlvl); |
| |
| void |
| copy_token(YAPP_Token *tok, struct source_head *to_head, source **to_tail); |
| |
| void |
| expand_macro(char *name, |
| struct source_head *from_head, |
| struct source_head *to_head, |
| source **to_tail); |
| |
| void |
| expand_token_list(struct source_head *paramexp, struct source_head *to_head, source **to_tail); |
| |
| static yasm_preproc * |
| yapp_preproc_create(const char *in_filename, yasm_linemap *lm, |
| yasm_errwarns *errwarns) |
| { |
| FILE *f; |
| yasm_preproc_yapp *preproc_yapp = yasm_xmalloc(sizeof(yasm_preproc_yapp)); |
| |
| if (strcmp(in_filename, "-") != 0) { |
| f = fopen(in_filename, "r"); |
| if (!f) |
| yasm__fatal( N_("Could not open input file") ); |
| } |
| else |
| f = stdin; |
| |
| preproc_yapp->preproc.module = &yasm_yapp_LTX_preproc; |
| |
| yapp_preproc_linemap = lm; |
| yapp_preproc_current_file = yasm__xstrdup(in_filename); |
| yapp_preproc_line_number = 1; |
| yapp_lex_initialize(f); |
| SLIST_INIT(&output_head); |
| SLIST_INIT(&source_head); |
| SLIST_INIT(¯o_head); |
| SLIST_INIT(¶m_head); |
| out = yasm_xmalloc(sizeof(output)); |
| out->out = current_output = YAPP_OUTPUT; |
| SLIST_INSERT_HEAD(&output_head, out, next); |
| |
| macro_table = HAMT_create(yasm_internal_error_); |
| |
| source_tail = SLIST_FIRST(&source_head); |
| macro_tail = SLIST_FIRST(¯o_head); |
| param_tail = SLIST_FIRST(¶m_head); |
| |
| append_token(LINE, &source_head, &source_tail); |
| |
| return (yasm_preproc *)preproc_yapp; |
| } |
| |
| static void |
| yapp_preproc_destroy(yasm_preproc *preproc) |
| { |
| /* TODO: clean up */ |
| yasm_xfree(preproc); |
| } |
| |
| /* Generate a new level of if* context |
| * if val is true, this module of the current level will be output IFF the |
| * surrounding one is. |
| */ |
| static void |
| push_if(int val) |
| { |
| out = yasm_xmalloc(sizeof(output)); |
| out->out = current_output; |
| SLIST_INSERT_HEAD(&output_head, out, next); |
| |
| switch (current_output) |
| { |
| case YAPP_OUTPUT: |
| current_output = val ? YAPP_OUTPUT : YAPP_NO_OUTPUT; |
| break; |
| |
| case YAPP_NO_OUTPUT: |
| case YAPP_OLD_OUTPUT: |
| case YAPP_BLOCKED_OUTPUT: |
| current_output = YAPP_BLOCKED_OUTPUT; |
| break; |
| } |
| if (current_output != YAPP_OUTPUT) set_inhibit(); |
| |
| } |
| |
| /* Generate a new module in the current if* context |
| * if val is true and this level hasn't had a true, it will be output if the |
| * surrounding level is. |
| */ |
| static void |
| push_else(int val) |
| { |
| switch (current_output) |
| { |
| /* if it was NO, turn to output IFF val is true */ |
| case YAPP_NO_OUTPUT: |
| current_output = val ? YAPP_OUTPUT : YAPP_NO_OUTPUT; |
| break; |
| |
| /* if it was yes, make it OLD */ |
| case YAPP_OUTPUT: |
| current_output = YAPP_OLD_OUTPUT; |
| break; |
| |
| /* leave OLD as OLD, BLOCKED as BLOCKED */ |
| case YAPP_OLD_OUTPUT: |
| case YAPP_BLOCKED_OUTPUT: |
| break; |
| } |
| if (current_output != YAPP_OUTPUT) set_inhibit(); |
| } |
| |
| /* Clear the curent if* context level */ |
| static void |
| pop_if(void) |
| { |
| out = SLIST_FIRST(&output_head); |
| current_output = out->out; |
| SLIST_REMOVE_HEAD(&output_head, next); |
| yasm_xfree(out); |
| if (current_output != YAPP_OUTPUT) set_inhibit(); |
| } |
| |
| /* undefine a symbol */ |
| static void |
| yapp_undef(const char *key) |
| { |
| int zero = 0; |
| HAMT_insert(macro_table, key, NULL, &zero, (void (*)(void *))yapp_macro_delete); |
| } |
| |
| /* Is a symbol known to YAPP? */ |
| static int |
| yapp_defined(const char *key) |
| { |
| return yapp_macro_get(key) != NULL; |
| } |
| |
| void |
| append_processed_token(source *tok, struct source_head *to_head, source **to_tail) |
| { |
| ydebug(("YAPP: appended token \"%s\"\n", tok->token.str)); |
| if (*to_tail) { |
| SLIST_INSERT_AFTER(*to_tail, tok, next); |
| } |
| else { |
| SLIST_INSERT_HEAD(to_head, tok, next); |
| } |
| *to_tail = tok; |
| if (to_head == &source_head) |
| saved_length += strlen(tok->token.str); |
| } |
| |
| void |
| append_token(int token, struct source_head *to_head, source **to_tail) |
| { |
| if (current_output != YAPP_OUTPUT) { |
| ydebug(("YAPP: append_token while not YAPP_OUTPUT\n")); |
| return; |
| } |
| |
| /* attempt to condense LINES together or newlines onto LINES */ |
| if ((*to_tail) && (*to_tail)->token.type == LINE |
| && (token == '\n' || token == LINE)) |
| { |
| yasm_xfree ((*to_tail)->token.str); |
| (*to_tail)->token.str = yasm_xmalloc(23+strlen(yapp_preproc_current_file)); |
| sprintf((*to_tail)->token.str, "%%line %d+1 %s\n", yapp_preproc_line_number, yapp_preproc_current_file); |
| } |
| else { |
| src = yasm_xmalloc(sizeof(source)); |
| src->token.type = token; |
| switch (token) |
| { |
| case INTNUM: |
| src->token.str = yasm__xstrdup(yapp_preproc_lval.int_str_val.str); |
| src->token.val.int_val = yapp_preproc_lval.int_str_val.val; |
| break; |
| |
| case FLTNUM: |
| src->token.str = yasm__xstrdup(yapp_preproc_lval.double_str_val.str); |
| src->token.val.double_val = yapp_preproc_lval.double_str_val.val; |
| break; |
| |
| case STRING: |
| case WHITESPACE: |
| src->token.str = yasm__xstrdup(yapp_preproc_lval.str_val); |
| break; |
| |
| case IDENT: |
| src->token.str = yasm__xstrdup(yapp_preproc_lval.str_val); |
| break; |
| |
| case '+': case '-': case '*': case '/': case '%': case ',': case '\n': |
| case '[': case ']': case '(': case ')': |
| src->token.str = yasm_xmalloc(2); |
| src->token.str[0] = (char)token; |
| src->token.str[1] = '\0'; |
| break; |
| |
| case LINE: |
| /* TODO: consider removing any trailing newline or LINE tokens */ |
| src->token.str = yasm_xmalloc(23+strlen(yapp_preproc_current_file)); |
| sprintf(src->token.str, "%%line %d+1 %s\n", yapp_preproc_line_number, yapp_preproc_current_file); |
| break; |
| |
| default: |
| yasm_xfree(src); |
| return; |
| } |
| append_processed_token(src, to_head, to_tail); |
| } |
| } |
| |
| static void |
| replay_saved_tokens(char *ident, |
| struct source_head *from_head, |
| struct source_head *to_head, |
| source **to_tail) |
| { |
| source *item, *after; |
| ydebug(("-No, %s didn't match any macro we have\n", ident)); |
| /* this means a macro expansion failed. stick its name and all the saved |
| * tokens into the output stream */ |
| yapp_preproc_lval.str_val = ident; |
| append_token(IDENT, to_head, to_tail); |
| |
| item = SLIST_FIRST(from_head); |
| while (item) { |
| after = SLIST_NEXT(item, next); |
| SLIST_INSERT_AFTER(*to_tail, item, next); |
| *to_tail = item; |
| saved_length += strlen(item->token.str); |
| item = after; |
| } |
| SLIST_INIT(from_head); |
| } |
| |
| int |
| append_to_return(struct source_head *to_head, source **to_tail) |
| { |
| int token = yapp_preproc_lex(); |
| while (token != '\n') { |
| ydebug(("YAPP: ATR: '%c' \"%s\"\n", token, yapp_preproc_lval.str_val)); |
| if (token == 0) |
| return 0; |
| append_token(token, to_head, to_tail); |
| token = yapp_preproc_lex(); |
| } |
| return '\n'; |
| } |
| |
| int |
| eat_through_return(struct source_head *to_head, source **to_tail) |
| { |
| int token; |
| while ((token = yapp_preproc_lex()) != '\n') { |
| if (token == 0) |
| return 0; |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("Skipping possibly valid %%define stuff")); |
| } |
| append_token('\n', to_head, to_tail); |
| return '\n'; |
| } |
| |
| int |
| yapp_get_ident(const char *synlvl) |
| { |
| int token = yapp_preproc_lex(); |
| if (token == WHITESPACE) |
| token = yapp_preproc_lex(); |
| if (token != IDENT) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("Identifier expected after %%%s"), |
| synlvl); |
| } |
| return token; |
| } |
| |
| void |
| copy_token(YAPP_Token *tok, struct source_head *to_head, source **to_tail) |
| { |
| src = yasm_xmalloc(sizeof(source)); |
| src->token.type = tok->type; |
| src->token.str = yasm__xstrdup(tok->str); |
| |
| append_processed_token(src, to_head, to_tail); |
| } |
| |
| void |
| expand_macro(char *name, |
| struct source_head *from_head, |
| struct source_head *to_head, |
| source **to_tail) |
| { |
| struct source_head replay_head, arg_head; |
| source *replay_tail, *arg_tail; |
| |
| YAPP_Macro *ym = yapp_macro_get(name); |
| |
| ydebug(("YAPP: +Expand macro %s...\n", name)); |
| |
| if (ym->expanding) yasm_internal_error(N_("Recursively expanding a macro!")); |
| |
| if (ym->type == YAPP_DEFINE) { |
| if (ym->args == -1) { |
| /* no parens to deal with */ |
| ym->expanding = 1; |
| expand_token_list(&ym->macro_head, to_head, to_tail); |
| ym->expanding = 0; |
| } |
| else |
| { |
| char *mungename; |
| int token; |
| int argc=0; |
| int parennest=0; |
| HAMT *param_table; |
| source *replay, *param; |
| |
| ydebug(("YAPP: +Expanding multi-arg macro %s...\n", name)); |
| |
| /* Build up both a parameter reference list and a token buffer, |
| * because we won't know until we've reached the closing paren if |
| * we can actuall expand the macro or not. bleah. */ |
| /* worse, we can't build the parameter list until we know the |
| * parameter names, which we can't look up until we know the |
| * number of arguments. sigh */ |
| /* HMM */ |
| SLIST_INIT(&replay_head); |
| replay_tail = SLIST_FIRST(&replay_head); |
| |
| /* find out what we got */ |
| if (from_head) { |
| yasm_internal_error(N_("Expanding macro with non-null from_head ugh\n")); |
| } |
| token = yapp_preproc_lex(); |
| append_token(token, &replay_head, &replay_tail); |
| /* allow one whitespace */ |
| if (token == WHITESPACE) { |
| ydebug(("Ignoring WS between macro and paren\n")); |
| token = yapp_preproc_lex(); |
| append_token(token, &replay_head, &replay_tail); |
| } |
| if (token != '(') { |
| ydebug(("YAPP: -Didn't get to left paren; instead got '%c' \"%s\"\n", token, yapp_preproc_lval.str_val)); |
| replay_saved_tokens(name, &replay_head, to_head, to_tail); |
| return; |
| } |
| |
| /* at this point, we've got the left paren. time to get annoyed */ |
| while (token != ')') { |
| token = yapp_preproc_lex(); |
| append_token(token, &replay_head, &replay_tail); |
| /* TODO: handle { } for commas? or is that just macros? */ |
| switch (token) { |
| case '(': |
| parennest++; |
| break; |
| |
| case ')': |
| if (parennest) { |
| parennest--; |
| token = ','; |
| } |
| else { |
| argc++; |
| } |
| break; |
| |
| case ',': |
| if (!parennest) |
| argc++; |
| break; |
| |
| case WHITESPACE: case IDENT: case INTNUM: case FLTNUM: case STRING: |
| case '+': case '-': case '*': case '/': |
| break; |
| |
| default: |
| if (token < 256) |
| yasm_internal_error(N_("Unexpected character token in parameter expansion")); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("Cannot handle preprocessor items inside possible macro invocation")); |
| } |
| } |
| |
| /* Now we have the argument count; let's see if it exists */ |
| mungename = yasm_xmalloc(strlen(name)+8); |
| sprintf(mungename, "%s(%d)", name, argc); |
| ym = yapp_macro_get(mungename); |
| if (!ym) |
| { |
| ydebug(("YAPP: -Didn't find macro %s\n", mungename)); |
| replay_saved_tokens(name, &replay_head, to_head, to_tail); |
| yasm_xfree(mungename); |
| return; |
| } |
| ydebug(("YAPP: +Found macro %s\n", mungename)); |
| |
| ym->expanding = 1; |
| |
| /* so the macro exists. build a HAMT parameter table */ |
| param_table = HAMT_create(yasm_internal_error_); |
| /* fill the entries by walking the replay buffer and create |
| * "macros". coincidentally, clear the replay buffer. */ |
| |
| /* get to the opening paren */ |
| replay = SLIST_FIRST(&replay_head); |
| while (replay->token.type != '(') { |
| ydebug(("YAPP: Ignoring replay token '%c' \"%s\"\n", replay->token.type, replay->token.str)); |
| SLIST_REMOVE_HEAD(&replay_head, next); |
| yasm_xfree(replay->token.str); |
| yasm_xfree(replay); |
| replay = SLIST_FIRST(&replay_head); |
| } |
| ydebug(("YAPP: Ignoring replay token '%c' \"%s\"\n", replay->token.type, replay->token.str)); |
| |
| /* free the open paren */ |
| SLIST_REMOVE_HEAD(&replay_head, next); |
| yasm_xfree(replay->token.str); |
| yasm_xfree(replay); |
| |
| param = SLIST_FIRST(&ym->param_head); |
| |
| SLIST_INIT(&arg_head); |
| arg_tail = SLIST_FIRST(&arg_head); |
| |
| replay = SLIST_FIRST(&replay_head); |
| SLIST_REMOVE_HEAD(&replay_head, next); |
| while (parennest || replay) { |
| if (replay->token.type == '(') { |
| parennest++; |
| append_processed_token(replay, &arg_head, &arg_tail); |
| ydebug(("YAPP: +add arg token '%c'\n", replay->token.type)); |
| } |
| else if (parennest && replay->token.type == ')') { |
| parennest--; |
| append_processed_token(replay, &arg_head, &arg_tail); |
| ydebug(("YAPP: +add arg token '%c'\n", replay->token.type)); |
| } |
| else if ((!parennest) && (replay->token.type == ',' |
| || replay->token.type == ')')) |
| { |
| int zero=0; |
| struct source_head *argmacro = yasm_xmalloc(sizeof(struct source_head)); |
| memcpy(argmacro, &arg_head, sizeof(struct source_head)); |
| SLIST_INIT(&arg_head); |
| arg_tail = SLIST_FIRST(&arg_head); |
| |
| /* don't save the comma */ |
| yasm_xfree(replay->token.str); |
| yasm_xfree(replay); |
| |
| HAMT_insert(param_table, |
| param->token.str, |
| (void *)argmacro, |
| &zero, |
| (void (*)(void *))yapp_macro_error_sameargname); |
| param = SLIST_NEXT(param, next); |
| } |
| else if (replay->token.type == IDENT |
| && yapp_defined(replay->token.str)) |
| { |
| ydebug(("YAPP: +add arg macro '%c' \"%s\"\n", replay->token.type, replay->token.str)); |
| expand_macro(replay->token.str, &replay_head, &arg_head, &arg_tail); |
| } |
| else if (arg_tail || replay->token.type != WHITESPACE) { |
| ydebug(("YAPP: +else add arg token '%c' \"%s\"\n", replay->token.type, replay->token.str)); |
| append_processed_token(replay, &arg_head, &arg_tail); |
| } |
| replay = SLIST_FIRST(&replay_head); |
| if (replay) SLIST_REMOVE_HEAD(&replay_head, next); |
| } |
| if (replay) { |
| yasm_xfree(replay->token.str); |
| yasm_xfree(replay); |
| } |
| else if (param) { |
| yasm_internal_error(N_("Got to end of arglist before end of replay!")); |
| } |
| replay = SLIST_FIRST(&replay_head); |
| if (replay || param) |
| yasm_internal_error(N_("Count and distribution of define args mismatched!")); |
| |
| /* the param_table is set up without errors, so expansion is ready |
| * to go */ |
| SLIST_FOREACH (replay, &ym->macro_head, next) { |
| if (replay->token.type == IDENT) { |
| |
| /* check local args first */ |
| struct source_head *paramexp = |
| yapp_define_param_get(param_table, replay->token.str); |
| |
| if (paramexp) { |
| expand_token_list(paramexp, to_head, to_tail); |
| } |
| else { |
| /* otherwise, check macros */ |
| YAPP_Macro *imacro = yapp_macro_get(replay->token.str); |
| if (imacro != NULL && !imacro->expanding) { |
| expand_macro(replay->token.str, NULL, to_head, to_tail); |
| } |
| else { |
| /* otherwise it's just a vanilla ident */ |
| copy_token(&replay->token, to_head, to_tail); |
| } |
| } |
| } |
| else { |
| copy_token(&replay->token, to_head, to_tail); |
| } |
| } |
| ym->expanding = 0; |
| } |
| } |
| else |
| yasm_internal_error(N_("Invoking Macros not yet supported")); |
| |
| ym->expanding = 0; |
| } |
| |
| void |
| expand_token_list(struct source_head *paramexp, struct source_head *to_head, source **to_tail) |
| { |
| source *item; |
| SLIST_FOREACH (item, paramexp, next) { |
| if (item->token.type == IDENT) { |
| YAPP_Macro *imacro = yapp_macro_get(item->token.str); |
| if (imacro != NULL && !imacro->expanding) { |
| expand_macro(item->token.str, NULL, to_head, to_tail); |
| } |
| else { |
| copy_token(&item->token, to_head, to_tail); |
| } |
| } |
| else { |
| copy_token(&item->token, to_head, to_tail); |
| } |
| } |
| } |
| |
| static size_t |
| yapp_preproc_input(yasm_preproc *preproc, char *buf, size_t max_size) |
| { |
| static YAPP_State state = YAPP_STATE_INITIAL; |
| size_t n = 0; |
| int token; |
| int need_line_directive = 0; |
| |
| while ((size_t)saved_length < max_size && state != YAPP_STATE_EOF) |
| { |
| token = yapp_preproc_lex(); |
| |
| switch (state) { |
| case YAPP_STATE_INITIAL: |
| switch (token) |
| { |
| char *s; |
| default: |
| append_token(token, &source_head, &source_tail); |
| /*if (append_to_return()==0) state=YAPP_STATE_EOF;*/ |
| ydebug(("YAPP: default: '%c' \"%s\"\n", token, yapp_preproc_lval.str_val)); |
| /*yasm_error_set(YASM_ERROR_SYNTAX, N_("YAPP got an unhandled token."));*/ |
| break; |
| |
| case IDENT: |
| ydebug(("YAPP: ident: \"%s\"\n", yapp_preproc_lval.str_val)); |
| if (yapp_defined(yapp_preproc_lval.str_val)) { |
| expand_macro(yapp_preproc_lval.str_val, NULL, &source_head, &source_tail); |
| } |
| else { |
| append_token(token, &source_head, &source_tail); |
| } |
| break; |
| |
| case 0: |
| state = YAPP_STATE_EOF; |
| break; |
| |
| case '\n': |
| append_token(token, &source_head, &source_tail); |
| break; |
| |
| case CLEAR: |
| HAMT_destroy(macro_table, (void (*)(void *))yapp_macro_delete); |
| macro_table = HAMT_create(yasm_internal_error_); |
| break; |
| |
| case DEFINE: |
| ydebug(("YAPP: define: ")); |
| token = yapp_get_ident("define"); |
| ydebug((" \"%s\"\n", yapp_preproc_lval.str_val)); |
| s = yasm__xstrdup(yapp_preproc_lval.str_val); |
| |
| /* three cases: newline or stuff or left paren */ |
| token = yapp_preproc_lex(); |
| if (token == '\n') { |
| /* no args or content - just insert it */ |
| yapp_define_insert(s, -1, 0); |
| append_token('\n', &source_head, &source_tail); |
| } |
| else if (token == WHITESPACE) { |
| /* no parens */ |
| if(append_to_return(¯o_head, ¯o_tail)==0) state=YAPP_STATE_EOF; |
| else { |
| yapp_define_insert(s, -1, 0); |
| } |
| append_token('\n', &source_head, &source_tail); |
| } |
| else if (token == '(') { |
| /* get all params of the parameter list */ |
| /* ensure they alternate IDENT and ',' */ |
| int param_count = 0; |
| int last_token = ','; |
| |
| ydebug((" *Getting arglist for define %s\n", s)); |
| |
| while ((token = yapp_preproc_lex())!=')') { |
| ydebug(("YAPP: +read token '%c' \"%s\" for macro %s\n", token, yapp_preproc_lval.str_val, s)); |
| if (token == WHITESPACE) { |
| token = last_token; |
| } |
| else if (last_token == ',' && token == IDENT) { |
| append_token(token, ¶m_head, ¶m_tail); |
| param_count++; |
| } |
| else if (token == 0) { |
| state = YAPP_STATE_EOF; |
| break; |
| } |
| else if (last_token == ',' || token != ',') |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("Unexpected token in %%define parameters")); |
| last_token = token; |
| } |
| if (token == ')') { |
| /* after paramlist and ')' */ |
| /* everything is what it's defined to be */ |
| token = yapp_preproc_lex(); |
| if (token != WHITESPACE) append_token(token, ¯o_head, ¯o_tail); |
| if (append_to_return(¯o_head, ¯o_tail)==0) state=YAPP_STATE_EOF; |
| else { |
| ydebug(("YAPP: Inserting define macro %s (%d)\n", s, param_count)); |
| yapp_define_insert(s, param_count, 0); |
| } |
| } |
| append_token('\n', &source_head, &source_tail); |
| } |
| else { |
| yasm_internal_error(N_("%%define ... failed miserably - neither \\n, WS, or ( followed ident")); |
| } |
| break; |
| |
| case UNDEF: |
| token = yapp_get_ident("undef"); |
| yapp_undef(yapp_preproc_lval.str_val); |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case IFDEF: |
| token = yapp_get_ident("ifdef"); |
| push_if(yapp_defined(yapp_preproc_lval.str_val)); |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case IFNDEF: |
| token = yapp_get_ident("ifndef"); |
| push_if(!yapp_defined(yapp_preproc_lval.str_val)); |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case ELSE: |
| push_else(1); |
| if (current_output == YAPP_OUTPUT) need_line_directive = 1; |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case ELIFDEF: |
| token = yapp_get_ident("elifdef"); |
| push_else(yapp_defined(yapp_preproc_lval.str_val)); |
| if (current_output == YAPP_OUTPUT) need_line_directive = 1; |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case ELIFNDEF: |
| token = yapp_get_ident("elifndef"); |
| push_else(!yapp_defined(yapp_preproc_lval.str_val)); |
| if (current_output == YAPP_OUTPUT) need_line_directive = 1; |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case ENDIF: |
| /* there's got to be another way to do this: */ |
| /* only set if going from non-output to output */ |
| if (current_output != YAPP_OUTPUT) need_line_directive = 1; |
| pop_if(); |
| if (current_output != YAPP_OUTPUT) need_line_directive = 0; |
| state = YAPP_STATE_NEED_EOL; |
| break; |
| |
| case INCLUDE: |
| case LINE: |
| need_line_directive = 1; |
| break; |
| } |
| if (state == YAPP_STATE_NEED_EOL) |
| { |
| if (eat_through_return(&source_head, &source_tail)==0) state=YAPP_STATE_EOF; |
| else state=YAPP_STATE_INITIAL; |
| } |
| break; |
| default: |
| yasm_error_set(YASM_ERROR_PARSE, N_("YAPP got into a bad state")); |
| } |
| if (need_line_directive) { |
| append_token(LINE, &source_head, &source_tail); |
| need_line_directive = 0; |
| } |
| } |
| |
| /* convert saved stuff into output. we either have enough, or are EOF */ |
| while (n < max_size && saved_length) |
| { |
| src = SLIST_FIRST(&source_head); |
| if (max_size - n >= strlen(src->token.str)) { |
| strcpy(buf+n, src->token.str); |
| n += strlen(src->token.str); |
| |
| saved_length -= strlen(src->token.str); |
| SLIST_REMOVE_HEAD(&source_head, next); |
| yasm_xfree(src->token.str); |
| yasm_xfree(src); |
| } |
| } |
| |
| return n; |
| } |
| |
| static size_t |
| yapp_preproc_get_included_file(yasm_preproc *preproc, /*@out@*/ char *buf, |
| size_t max_size) |
| { |
| /* TODO */ |
| return 0; |
| } |
| |
| static void |
| yapp_preproc_add_include_file(yasm_preproc *preproc, const char *filename) |
| { |
| /* TODO */ |
| } |
| |
| static void |
| yapp_preproc_predefine_macro(yasm_preproc *preproc, const char *macronameval) |
| { |
| /* TODO */ |
| } |
| |
| static void |
| yapp_preproc_undefine_macro(yasm_preproc *preproc, const char *macroname) |
| { |
| /* TODO */ |
| } |
| |
| static void |
| yapp_preproc_define_builtin(yasm_preproc *preproc, const char *macronameval) |
| { |
| /* TODO */ |
| } |
| |
| /* Define preproc structure -- see preproc.h for details */ |
| yasm_preproc_module yasm_yapp_LTX_preproc = { |
| "YAPP preprocessing (NASM style)", |
| "yapp", |
| yapp_preproc_create, |
| yapp_preproc_destroy, |
| yapp_preproc_input, |
| yapp_preproc_get_included_file, |
| yapp_preproc_add_include_file, |
| yapp_preproc_predefine_macro, |
| yapp_preproc_undefine_macro, |
| yapp_preproc_define_builtin |
| }; |