/* Implements macros
Copyright 2002-2005 Craig Franklin
*/
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "directive.h"
#include "evaluate.h"
#include "gpmsg.h"
#include "macro.h"
#include "parse.h"
#include "preprocess.h"
#include "lst.h"
#include <vector>
/* The symbol table is pushed at each macro call. This makes local symbols
possible. Each symbol table is created once on pass 1. On pass two the
old symbol table is reloaded so forward references to the local symbols
are possible. */
struct macro_table_t {
symbol_table_t *table;
int line_number; /* sanity check, better not change */
macro_table_t(symbol_table_t*t,int n):table(t),line_number(n) {}
};
static std::vector<macro_table_t> macro_table_list;
static int macro_table_ptr; // tracer for second pass
/*------------------------------------------------------------------------------------------------*/
static void _add_macro_table(symbol_table_t *Table) {
macro_table_t m(Table,state.src_list.last->line_number);
macro_table_list.push_back(m);
}
/*------------------------------------------------------------------------------------------------*/
/* Create a new defines table and place the macro parms in it. */
void macro_setup(macro_head_t *Head, int Arity, const pnode_t *Parms) {
const pnode_t *pFrom;
const pnode_t *pFromH;
const pnode_t *pTo;
const pnode_t *pToH;
symbol_t *sym;
if (eval_enforce_arity(Arity, eval_list_length(Head->parms))) {
/* push table for the marco parms */
state.stMacroParams = gp_sym_push_table(state.stMacroParams, state.case_insensitive);
/* Now add the macro's declared parameter list to the new defines table. */
if (Arity > 0) {
pTo = Parms;
for (pFrom = Head->parms; pFrom != NULL; pFrom = PnListTail(pFrom)) {
pToH = PnListHead(pTo);
pFromH = PnListHead(pFrom);
assert(PnIsSymbol(pFromH));
assert(PnIsSymbol(pToH));
sym = gp_sym_add_symbol(state.stMacroParams, PnSymbol(pFromH));
gp_sym_annotate_symbol(sym, mk_list(mk_string(GP_Strdup(PnSymbol(pToH))), NULL));
pTo = PnListTail(pTo);
}
}
state.next_state = STATE_MACRO;
state.next_buffer.macro = Head;
state.lst.line.linetype = gpasm_state::Lst::Line::LTY_NONE;
}
}
/*------------------------------------------------------------------------------------------------*/
symbol_table_t* macro_push_symbol_table(symbol_table_t *Table) {
symbol_table_t *pnew = NULL;
if (state.pass == 1) {
pnew = gp_sym_push_table(Table, state.case_insensitive);
_add_macro_table(pnew);
}else{
if (macro_table_ptr==macro_table_list.size()) {
gpmsg(GPE_UNKNOWN, "An error occurred during a macro execution on pass %i.", state.pass);
exit(1);
}
macro_table_t &el=macro_table_list[macro_table_ptr];
if (el.line_number != (int)state.src_list.last->line_number) {
/* The user must have conditionally assembled a macro using a forward
reference to a label. This is a very bad practice. It means that
a macro wasn't executed on the first pass, but it was on the second.
Probably errors will be generated. Forward references to local
symbols probably won't be correct. */
pnew = gp_sym_push_table(Table, state.case_insensitive);
gpmsg0(GPW_UNKNOWN, "Macro not executed on pass 1.");
}else{
assert(macro_table_ptr);
pnew = el.table;
gp_sym_set_guest_table(pnew, Table);
macro_table_ptr++; /* setup for next macro */
}
}
return pnew;
}
/*------------------------------------------------------------------------------------------------*/
void macro_list(macro_body_t *Body) {
source_context_t *ctx;
unsigned old_line_number;
ctx = state.src_list.last;
old_line_number = ctx->line_number;
/* Never executed: List the macro body. */
state.lst.line.linetype = gpasm_state::Lst::Line::LTY_DIR;
ctx->line_number = state.while_head->line_number;
while (Body) {
++(ctx->line_number);
lst_format_line(Body->src_line, 0);
Body = Body->next;
}
ctx->line_number = old_line_number;
}
Detected encoding: ASCII (7 bit) | 2
|