//
-// RMAC - Reboot's Macro Assembler for all Atari computers
+// RMAC - Renamed Macro Assembler for all Atari computers
// MACRO.C - Macro Definition and Invocation
-// Copyright (C) 199x Landon Dyer, 2011-2017 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
// Source utilised with the kind permission of Landon Dyer
//
LONG curuniq; // Current macro's unique number
int macnum; // Unique number for macro definition
-TOKEN * argPtrs[128]; // 128 arguments ought to be enough for anyone
-static int argp;
static LONG macuniq; // Unique-per-macro number
static SYM * curmac; // Macro currently being defined
-static VALUE argno; // Formal argument count
+static uint32_t argno; // Formal argument count
+LONG reptuniq; // Unique-per-rept number
static LLIST * firstrpt; // First .rept line
static LLIST * nextrpt; // Last .rept line
-static int rptlevel; // .rept nesting level
+int rptlevel; // .rept nesting level
// Function prototypes
static int KWMatch(char *, char *);
{
macuniq = 0;
macnum = 1;
- argp = 0;
+ reptuniq = 0;
}
This is a problem. Currently, the argument logic just keeps the current
arguments and doesn't save anything if a new macro is called in the middle
of another (nested macros). Need to fix that somehow.
+
+Is this still true, now that we have IMACROs with TOKENSTREAMs in them? Need to
+check it out for sure...!
*/
// Pop intervening include files and .rept blocks
while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
IMACRO * imacro = cur_inobj->inobj.imacro;
curuniq = imacro->im_olduniq;
-// /*TOKEN ** p = */argp--;
-// argp = (TOKEN **)*argp;
- DEBUG { printf("ExitMacro: argp: %d -> ", argp); }
- argp -= imacro->im_nargs;
- DEBUG { printf("%d (nargs = %d)\n", argp, imacro->im_nargs); }
+ DEBUG { printf("ExitMacro: nargs = %d\n", imacro->im_nargs); }
return fpop();
}
{
if (list_flag)
{
- listeol(); // Flush previous source line
- lstout('.'); // Mark macro definition with period
+ listeol(); // Flush previous source line
+ lstout('.'); // Mark macro definition with period
}
- // This is just wrong, wrong, wrong. It makes up a weird kind of string with
- // a pointer on front, and then uses a ** to manage them: This is a recipe
- // for disaster.
- // How to manage it then?
- // Could use a linked list, like Landon uses everywhere else.
-/*
-How it works:
-Allocate a space big enough for the string + NULL + pointer.
-Set the pointer to NULL.
-Copy the string to the space after the pointer.
-If this is the 1st time through, set the SYM * "svalue" to the pointer.
-If this is the 2nd time through, derefence the ** to point to the memory you
-just allocated. Then, set the ** to the location of the memory you allocated
-for the next pass through.
-
-This is a really low level way to do a linked list, and by bypassing all the
-safety features of the language. Seems like we can do better here.
-*/
if (notEndFlag)
{
-#if 0
- len = strlen(ln) + 1 + sizeof(LONG);
- p.cp = malloc(len);
- *p.lp = 0;
- strcpy(p.cp + sizeof(LONG), ln);
-
- // Link line of text onto end of list
- if (curmln == NULL)
- curmac->svalue = p.cp;
- else
- *curmln = p.cp;
-
- curmln = (char **)p.cp;
- return 1; // Keep looking
-#else
if (curmac->lineList == NULL)
{
curmac->lineList = malloc(sizeof(LLIST));
curmac->lineList->next = NULL;
curmac->lineList->line = strdup(ln);
+ curmac->lineList->lineno = curlineno;
curmac->last = curmac->lineList;
}
else
curmac->last->next = malloc(sizeof(LLIST));
curmac->last->next->next = NULL;
curmac->last->next->line = strdup(ln);
+ curmac->lineList->lineno = curlineno;
curmac->last = curmac->last->next;
}
- return 1; // Keep looking
-#endif
+ return 1; // Keep looking
}
- return 0; // Stop looking at the end
+ return 0; // Stop looking; at the end
}
{
argno = 0;
symlist(defmac2);
- at_eol();
+ ErrorIfNotAtEOL();
}
// Suck in the macro definition; we're looking for an ENDM symbol on a line
firstrpt = malloc(sizeof(LLIST));
firstrpt->next = NULL;
firstrpt->line = strdup(line);
+ firstrpt->lineno = curlineno;
nextrpt = firstrpt;
}
else
nextrpt->next = malloc(sizeof(LLIST));
nextrpt->next->next = NULL;
nextrpt->next->line = strdup(line);
+ nextrpt->next->lineno = curlineno;
nextrpt = nextrpt->next;
}
#endif
//
int HandleRept(void)
{
- VALUE eval;
+ uint64_t eval;
// Evaluate repeat expression
if (abs_expr(&eval) != OK)
IREPT * irept = inobj->inobj.irept;
irept->ir_firstln = firstrpt;
irept->ir_nextln = NULL;
- irept->ir_count = eval;
+ irept->ir_count = (uint32_t)eval;
}
return 0;
fatal("cannot continue");
}
+ DEBUG { DumpTokenBuffer(); }
+
// Test for end condition. Two cases to handle:
// <directive>
// symbol: <directive>
//
-// Invoke a macro
-// o parse, count and copy arguments
-// o push macro's string-stream
+// Invoke a macro by creating a new IMACRO object & chopping up the arguments
//
int InvokeMacro(SYM * mac, WORD siz)
{
- TOKEN * p = NULL;
- int dry_run;
- WORD arg_siz = 0;
-// TOKEN ** argptr = NULL;
-//Doesn't need to be global! (or does it???--it does)
-// argp = 0;
- DEBUG printf("InvokeMacro: argp: %d -> ", argp);
-
- INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
+ DEBUG { printf("InvokeMacro: arguments="); DumpTokens(tok); }
+
+ INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
IMACRO * imacro = inobj->inobj.imacro;
- imacro->im_siz = siz;
- WORD nargs = 0;
- TOKEN * beg_tok = tok; // 'tok' comes from token.c
- TOKEN * startOfArg;
- TOKEN * dest;
- int stringNum = 0;
- int argumentNum = 0;
-
- for(dry_run=1; ; dry_run--)
+ uint16_t nargs = 0;
+
+ // Chop up the arguments, if any (tok comes from token.c, which at this
+ // point points at the macro argument token stream)
+ if (*tok != EOL)
{
- for(tok=beg_tok; *tok!=EOL;)
+ // Parse out the arguments and set them up correctly
+ TOKEN * p = imacro->argument[nargs].token;
+ int stringNum = 0;
+ int numTokens = 0;
+
+ while (*tok != EOL)
{
- if (dry_run)
- nargs++;
- else
+ if (*tok == ACONST)
{
-#if 0
- *argptr++ = p;
-#else
- argPtrs[argp++] = p;
- startOfArg = p;
-#endif
- }
+ // Sanity checking (it's numTokens + 1 because we need an EOL
+ // if we successfully parse this argument)
+ if ((numTokens + 3) >= TS_MAXTOKENS)
+ return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
+
+ for(int i=0; i<3; i++)
+ *p++ = *tok++;
- // Keep going while tok isn't pointing at a comma or EOL
- while (*tok != ',' && *tok != EOL)
+ numTokens += 3;
+ }
+ else if (*tok == CONST) // Constants are 64-bits
{
- // Skip over backslash character, unless it's followed by an EOL
- if (*tok == '\\' && tok[1] != EOL)
- tok++;
-
- switch (*tok)
- {
- case CONST:
- case SYMBOL:
-//Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
- case ACONST:
- if (dry_run)
- {
- arg_siz += sizeof(TOKEN);
- tok++;
- }
- else
- {
- *p++ = *tok++;
- }
- // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
- default:
- if (dry_run)
- {
- arg_siz += sizeof(TOKEN);
- tok++;
- }
- else
- {
- *p++ = *tok++;
- }
-
- break;
- }
+ // Sanity checking (it's numTokens + 1 because we need an EOL
+ // if we successfully parse this argument)
+ if ((numTokens + 3) >= TS_MAXTOKENS)
+ return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
+
+ *p++ = *tok++; // Token
+ uint64_t *p64 = (uint64_t *)p;
+ uint64_t *tok64 = (uint64_t *)tok;
+ *p64++ = *tok64++;
+ tok = (TOKEN *)tok64;
+ p = (uint32_t *)p64;
+ numTokens += 3;
}
+ else if ((*tok == STRING) || (*tok == SYMBOL))
+ {
+ // Sanity checking (it's numTokens + 1 because we need an EOL
+ // if we successfully parse this argument)
+ if (stringNum >= TS_MAXSTRINGS)
+ return error("Too many strings in argument #%d in MACRO invocation", nargs + 1);
+
+ if ((numTokens + 2) >= TS_MAXTOKENS)
+ return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
+
+ *p++ = *tok++;
+ imacro->argument[nargs].string[stringNum] = strdup(string[*tok++]);
+ *p++ = stringNum++;
+ numTokens += 2;
+ }
+ else if (*tok == ',')
+ {
+ // Sanity checking
+ if ((nargs + 1) >= TS_MAXARGS)
+ return error("Too many arguments in MACRO invocation");
- // We hit the comma or EOL, so count/stuff it
- if (dry_run)
- arg_siz += sizeof(TOKEN);
- else
+ // Comma delimiter was found, so set up for next argument
*p++ = EOL;
-
- // If we hit the comma instead of an EOL, skip over it
- if (*tok == ',')
tok++;
-
- // Do our QnD token grabbing (this will be redone once we get all
- // the data structures fixed as this is a really dirty hack)
- if (!dry_run)
- {
- dest = imacro->argument[argumentNum].token;
stringNum = 0;
+ numTokens = 0;
+ nargs++;
+ p = imacro->argument[nargs].token;
+ }
+ else
+ {
+ // Sanity checking (it's numTokens + 1 because we need an EOL
+ // if we successfully parse this argument)
+ if ((numTokens + 1) >= TS_MAXTOKENS)
+ return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
- do
- {
- // Remap strings to point the IMACRO internal token storage
- if (*startOfArg == SYMBOL || *startOfArg == STRING)
- {
- *dest++ = *startOfArg++;
- imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
- *dest++ = stringNum++;
- }
- else
- *dest++ = *startOfArg++;
- }
- while (*startOfArg != EOL);
-
- *dest = *startOfArg; // Copy EOL...
- argumentNum++;
+ *p++ = *tok++;
+ numTokens++;
}
}
- // Allocate space for argument ptrs and so on and then go back and
- // construct the arg frame
- if (dry_run)
- {
- if (nargs != 0)
- p = (TOKEN *)malloc(arg_siz);
-// p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
-
-/*
-Shamus:
-This construct is meant to deal with nested macros, so the simple minded way
-we deal with them now won't work. :-/ Have to think about how to fix.
-What we could do is simply move the argp with each call, and move it back by
-the number of arguments in the macro that's ending. That would solve the
-problem nicely.
-[Which we do now. But that uncovered another problem: the token strings are all
-stale by the time a nested macro gets to the end. But they're supposed to be
-symbols, which means if we put symbol references into the argument token
-streams, we can alleviate this problem.]
-*/
-#if 0
- argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
- *argptr++ = (TOKEN *)argp;
- argp = argptr;
-#else
- // We don't need to do anything here since we already advance argp
- // when parsing the arguments.
-// argp += nargs;
-#endif
- }
- else
- break;
+ // Make sure to stuff the final EOL (otherwise, it will be skipped)
+ *p++ = EOL;
+ nargs++;
}
- DEBUG { printf("%d\n", argp); }
-
- // Setup imacro:
+ // Setup IMACRO:
// o # arguments;
// o -> macro symbol;
// o -> macro definition string list;
// o bump `macuniq' counter and set 'curuniq' to it;
imacro->im_nargs = nargs;
imacro->im_macro = mac;
+ imacro->im_siz = siz;
imacro->im_nextln = mac->lineList;
imacro->im_olduniq = curuniq;
curuniq = macuniq++;
- imacro->argBase = argp - nargs; // Shamus: keep track of argument base
DEBUG
{
- printf("nargs=%d\n", nargs);
+ printf("# args = %d\n", nargs);
- for(nargs=0; nargs<imacro->im_nargs; nargs++)
+ for(uint16_t i=0; i<nargs; i++)
{
- printf("arg%d=", nargs);
- dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);
+ printf("arg%d=", i);
+ DumpTokens(imacro->argument[i].token);
}
}