Files
cc65/src/ca65/macro.c
cuz 9464c7dd29 Change the .LOCAL command so that the cheap local symbol prefix is kept.
This allows to create macros with local labels that expand to cheap local
symbols when the macro is expanded.
Tip from Dagan Galarneau <dagan@msd.com>


git-svn-id: svn://svn.cc65.org/cc65/trunk@1405 b7a2c559-68d2-44c3-8de9-860c34a00d81
2002-09-25 21:35:00 +00:00

793 lines
20 KiB
C

/*****************************************************************************/
/* */
/* macro.c */
/* */
/* Macros for the ca65 macroassembler */
/* */
/* */
/* */
/* (C) 1998-2000 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <string.h>
/* common */
#include "check.h"
#include "hashstr.h"
#include "xmalloc.h"
/* ca65 */
#include "condasm.h"
#include "error.h"
#include "global.h"
#include "istack.h"
#include "nexttok.h"
#include "pseudo.h"
#include "toklist.h"
#include "macro.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Struct that describes an identifer (macro param, local list) */
typedef struct IdDesc_ IdDesc;
struct IdDesc_ {
IdDesc* Next; /* Linked list */
char Id [1]; /* Identifier, dynamically allocated */
};
/* Struct that describes a macro definition */
typedef struct Macro_ Macro;
struct Macro_ {
Macro* Next; /* Next macro with same hash */
Macro* List; /* List of all macros */
unsigned LocalCount; /* Count of local symbols */
IdDesc* Locals; /* List of local symbols */
unsigned ParamCount; /* Parameter count of macro */
IdDesc* Params; /* Identifiers of macro parameters */
unsigned TokCount; /* Number of tokens for this macro */
TokNode* TokRoot; /* Root of token list */
TokNode* TokLast; /* Pointer to last token in list */
unsigned char Style; /* Macro style */
char Name [1]; /* Macro name, dynamically allocated */
};
/* Macro hash table */
#define HASHTAB_SIZE 117
static Macro* MacroTab [HASHTAB_SIZE];
/* Global macro data */
static Macro* MacroRoot = 0; /* List of all macros */
/* Structs that holds data for a macro expansion */
typedef struct MacExp_ MacExp;
struct MacExp_ {
MacExp* Next; /* Pointer to next expansion */
Macro* M; /* Which macro do we expand? */
unsigned IfSP; /* .IF stack pointer at start of expansion */
TokNode* Exp; /* Pointer to current token */
TokNode* Final; /* Pointer to final token */
unsigned LocalStart; /* Start of counter for local symbol names */
unsigned ParamCount; /* Number of actual parameters */
TokNode** Params; /* List of actual parameters */
TokNode* ParamExp; /* Node for expanding parameters */
};
/* Number of active macro expansions */
static unsigned MacExpansions = 0;
/* Flag if a macro expansion should get aborted */
static int DoMacAbort = 0;
/* Counter to create local names for symbols */
static unsigned LocalName = 0;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static IdDesc* NewIdDesc (const char* Id)
/* Create a new IdDesc, initialize and return it */
{
/* Allocate memory */
unsigned Len = strlen (Id);
IdDesc* I = xmalloc (sizeof (IdDesc) + Len);
/* Initialize the struct */
I->Next = 0;
memcpy (I->Id, Id, Len);
I->Id [Len] = '\0';
/* Return the new struct */
return I;
}
static Macro* NewMacro (const char* Name, unsigned HashVal, unsigned char Style)
/* Generate a new macro entry, initialize and return it */
{
/* Allocate memory */
unsigned Len = strlen (Name);
Macro* M = xmalloc (sizeof (Macro) + Len);
/* Initialize the macro struct */
M->LocalCount = 0;
M->ParamCount = 0;
M->Params = 0;
M->TokCount = 0;
M->TokRoot = 0;
M->TokLast = 0;
M->Style = Style;
memcpy (M->Name, Name, Len);
M->Name [Len] = '\0';
/* Insert the macro into the global macro list */
M->List = MacroRoot;
MacroRoot = M;
/* Insert the macro into the hash table */
M->Next = MacroTab [HashVal];
MacroTab [HashVal] = M;
/* Return the new macro struct */
return M;
}
static MacExp* NewMacExp (Macro* M)
/* Create a new expansion structure for the given macro */
{
unsigned I;
/* Allocate memory */
MacExp* E = xmalloc (sizeof (MacExp));
/* Initialize the data */
E->M = M;
E->IfSP = GetIfStack ();
E->Exp = M->TokRoot;
E->Final = 0;
E->LocalStart = LocalName;
LocalName += M->LocalCount;
E->ParamCount = 0;
E->Params = xmalloc (M->ParamCount * sizeof (TokNode*));
E->ParamExp = 0;
for (I = 0; I < M->ParamCount; ++I) {
E->Params [I] = 0;
}
/* One macro expansion more */
++MacExpansions;
/* Return the new macro expansion */
return E;
}
static void FreeMacExp (MacExp* E)
/* Remove and free the current macro expansion */
{
unsigned I;
/* One macro expansion less */
--MacExpansions;
/* Free the parameter list */
for (I = 0; I < E->ParamCount; ++I) {
xfree (E->Params [I]);
}
xfree (E->Params);
/* Free the final token if we have one */
if (E->Final) {
FreeTokNode (E->Final);
}
/* Free the structure itself */
xfree (E);
}
static void MacSkipDef (unsigned Style)
/* Skip a macro definition */
{
if (Style == MAC_STYLE_CLASSIC) {
/* Skip tokens until we reach the final .endmacro */
while (Tok != TOK_ENDMACRO && Tok != TOK_EOF) {
NextTok ();
}
if (Tok != TOK_EOF) {
SkipUntilSep ();
} else {
Error (ERR_ENDMACRO_EXPECTED);
}
} else {
/* Skip until end of line */
SkipUntilSep ();
}
}
static Macro* MacFind (const char* Name, unsigned HashVal)
/* Search for a macro in the hash table */
{
/* Search for the identifier */
Macro* M = MacroTab [HashVal];
while (M) {
if (strcmp (Name, M->Name) == 0) {
return M;
}
M = M->Next;
}
return 0;
}
void MacDef (unsigned Style)
/* Parse a macro definition */
{
Macro* M;
TokNode* T;
unsigned HashVal;
int HaveParams;
/* We expect a macro name here */
if (Tok != TOK_IDENT) {
Error (ERR_IDENT_EXPECTED);
MacSkipDef (Style);
return;
}
/* Generate the hash value */
HashVal = HashStr (SVal) % HASHTAB_SIZE;
/* Did we already define that macro? */
if (MacFind (SVal, HashVal) != 0) {
/* Macro is already defined */
Error (ERR_SYM_ALREADY_DEFINED, SVal);
/* Skip tokens until we reach the final .endmacro */
MacSkipDef (Style);
return;
}
/* Define the macro */
M = NewMacro (SVal, HashVal, Style);
/* Switch to raw token mode and skip the macro name */
EnterRawTokenMode ();
NextTok ();
/* If we have a DEFINE style macro, we may have parameters in braces,
* otherwise we may have parameters without braces.
*/
if (Style == MAC_STYLE_CLASSIC) {
HaveParams = 1;
} else {
if (Tok == TOK_LPAREN) {
HaveParams = 1;
NextTok ();
} else {
HaveParams = 0;
}
}
/* Parse the parameter list */
if (HaveParams) {
while (Tok == TOK_IDENT) {
/* Create a struct holding the identifier */
IdDesc* I = NewIdDesc (SVal);
/* Insert the struct into the list, checking for duplicate idents */
if (M->ParamCount == 0) {
M->Params = I;
} else {
IdDesc* List = M->Params;
while (1) {
if (strcmp (List->Id, SVal) == 0) {
Error (ERR_SYM_ALREADY_DEFINED, SVal);
}
if (List->Next == 0) {
break;
} else {
List = List->Next;
}
}
List->Next = I;
}
++M->ParamCount;
/* Skip the name */
NextTok ();
/* Maybe there are more params... */
if (Tok == TOK_COMMA) {
NextTok ();
} else {
break;
}
}
}
/* For class macros, we expect a separator token, for define style macros,
* we expect the closing paren.
*/
if (Style == MAC_STYLE_CLASSIC) {
ConsumeSep ();
} else if (HaveParams) {
ConsumeRParen ();
}
/* Preparse the macro body. We will read the tokens until we reach end of
* file, or a .endmacro (or end of line for DEFINE style macros) and store
* them into an token list internal to the macro. For classic macros, there
* the .LOCAL command is detected and removed at this time.
*/
while (1) {
/* Check for end of macro */
if (Style == MAC_STYLE_CLASSIC) {
/* In classic macros, only .endmacro is allowed */
if (Tok == TOK_ENDMACRO) {
/* Done */
break;
}
/* May not have end of file in a macro definition */
if (Tok == TOK_EOF) {
Error (ERR_ENDMACRO_EXPECTED);
goto Done;
}
} else {
/* Accept a newline or end of file for new style macros */
if (Tok == TOK_SEP || Tok == TOK_EOF) {
break;
}
}
/* Check for a .LOCAL declaration */
if (Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) {
while (1) {
IdDesc* I;
/* Skip .local or comma */
NextTok ();
/* Need an identifer */
if (Tok != TOK_IDENT) {
Error (ERR_IDENT_EXPECTED);
SkipUntilSep ();
break;
}
/* Put the identifier into the locals list and skip it */
I = NewIdDesc (SVal);
I->Next = M->Locals;
M->Locals = I;
++M->LocalCount;
NextTok ();
/* Check for end of list */
if (Tok != TOK_COMMA) {
break;
}
}
/* We need end of line after the locals */
ConsumeSep ();
continue;
}
/* Create a token node for the current token */
T = NewTokNode ();
/* If the token is an ident, check if it is a local parameter */
if (Tok == TOK_IDENT) {
unsigned Count = 0;
IdDesc* I = M->Params;
while (I) {
if (strcmp (I->Id, SVal) == 0) {
/* Local param name, replace it */
T->Tok = TOK_MACPARAM;
T->IVal = Count;
break;
}
++Count;
I = I->Next;
}
}
/* Insert the new token in the list */
if (M->TokCount == 0) {
/* First token */
M->TokRoot = M->TokLast = T;
} else {
/* We have already tokens */
M->TokLast->Next = T;
M->TokLast = T;
}
++M->TokCount;
/* Read the next token */
NextTok ();
}
/* Skip the .endmacro for a classic macro */
if (Style == MAC_STYLE_CLASSIC) {
NextTok ();
}
Done:
/* Switch out of raw token mode */
LeaveRawTokenMode ();
}
static int MacExpand (void* Data)
/* If we're currently expanding a macro, set the the scanner token and
* attribute to the next value and return true. If we are not expanding
* a macro, return false.
*/
{
/* Cast the Data pointer to the actual data structure */
MacExp* Mac = (MacExp*) Data;
/* Check if we should abort this macro */
if (DoMacAbort) {
/* Reset the flag */
DoMacAbort = 0;
/* Abort any open .IF statements in this macro expansion */
CleanupIfStack (Mac->IfSP);
/* Terminate macro expansion */
goto MacEnd;
}
/* We're expanding a macro. Check if we are expanding one of the
* macro parameters.
*/
if (Mac->ParamExp) {
/* Ok, use token from parameter list */
TokSet (Mac->ParamExp);
/* Set pointer to next token */
Mac->ParamExp = Mac->ParamExp->Next;
/* Done */
return 1;
}
/* We're not expanding macro parameters. Check if we have tokens left from
* the macro itself.
*/
if (Mac->Exp) {
/* Use next macro token */
TokSet (Mac->Exp);
/* Set pointer to next token */
Mac->Exp = Mac->Exp->Next;
/* Is it a request for actual parameter count? */
if (Tok == TOK_PARAMCOUNT) {
Tok = TOK_INTCON;
IVal = Mac->ParamCount;
return 1;
}
/* Is it the name of a macro parameter? */
if (Tok == TOK_MACPARAM) {
/* Start to expand the parameter token list */
Mac->ParamExp = Mac->Params [IVal];
/* Recursive call to expand the parameter */
return MacExpand (Mac);
}
/* If it's an identifier, it may in fact be a local symbol */
if (Tok == TOK_IDENT && Mac->M->LocalCount) {
/* Search for the local symbol in the list */
unsigned Index = 0;
IdDesc* I = Mac->M->Locals;
while (I) {
if (strcmp (SVal, I->Id) == 0) {
/* This is in fact a local symbol, change the name. Be sure
* to generate a local label name if the original name was
* a local label, and also generate a name that cannot be
* generated by a user.
*/
unsigned PrefixLen = (I->Id[0] == LocalStart);
sprintf (SVal, "%.*sLOCAL-MACRO-SYMBOL-%04X", PrefixLen,
I->Id, Mac->LocalStart + Index);
break;
}
/* Next symbol */
++Index;
I = I->Next;
}
/* Done */
return 1;
}
/* The token was successfully set */
return 1;
}
/* No more macro tokens. Do we have a final token? */
if (Mac->Final) {
/* Set the final token and remove it */
TokSet (Mac->Final);
FreeTokNode (Mac->Final);
Mac->Final = 0;
/* The token was successfully set */
return 1;
}
MacEnd:
/* End of macro expansion */
FreeMacExp (Mac);
/* Pop the input function */
PopInput ();
/* No token available */
return 0;
}
static void StartExpClassic (Macro* M)
/* Start expanding the classic macro M */
{
MacExp* E;
/* Skip the macro name */
NextTok ();
/* Create a structure holding expansion data */
E = NewMacExp (M);
/* Read the actual parameters */
while (Tok != TOK_SEP && Tok != TOK_EOF) {
TokNode* Last;
/* Check for maximum parameter count */
if (E->ParamCount >= M->ParamCount) {
Error (ERR_TOO_MANY_PARAMS);
SkipUntilSep ();
break;
}
/* Read tokens for one parameter, accept empty params */
Last = 0;
while (Tok != TOK_COMMA && Tok != TOK_SEP) {
TokNode* T;
/* Check for end of file */
if (Tok == TOK_EOF) {
Error (ERR_UNEXPECTED_EOF);
return;
}
/* Get the next token in a node */
T = NewTokNode ();
/* Insert it into the list */
if (Last == 0) {
E->Params [E->ParamCount] = T;
} else {
Last->Next = T;
}
Last = T;
/* And skip it... */
NextTok ();
}
/* One parameter more */
++E->ParamCount;
/* Check for a comma */
if (Tok == TOK_COMMA) {
NextTok ();
} else {
break;
}
}
/* Insert a new token input function */
PushInput (MacExpand, E, ".MACRO");
}
static void StartExpDefine (Macro* M)
/* Start expanding a DEFINE style macro */
{
/* Create a structure holding expansion data */
MacExp* E = NewMacExp (M);
/* A define style macro must be called with as many actual parameters
* as there are formal ones. Get the parameter count.
*/
unsigned Count = M->ParamCount;
/* Skip the current token */
NextTok ();
/* Read the actual parameters */
while (Count--) {
TokNode* Last;
/* Check if there is really a parameter */
if (Tok == TOK_SEP || Tok == TOK_EOF || Tok == TOK_COMMA) {
Error (ERR_MACRO_PARAM_EXPECTED);
SkipUntilSep ();
return;
}
/* Read tokens for one parameter */
Last = 0;
do {
TokNode* T;
/* Get the next token in a node */
T = NewTokNode ();
/* Insert it into the list */
if (Last == 0) {
E->Params [E->ParamCount] = T;
} else {
Last->Next = T;
}
Last = T;
/* And skip it... */
NextTok ();
} while (Tok != TOK_COMMA && Tok != TOK_SEP && Tok != TOK_EOF);
/* One parameter more */
++E->ParamCount;
/* Check for a comma */
if (Count > 0) {
if (Tok == TOK_COMMA) {
NextTok ();
} else {
Error (ERR_COMMA_EXPECTED);
}
}
}
/* Macro expansion will overwrite the current token. This is a problem
* for define style macros since these are called from the scanner level.
* To avoid it, remember the current token and re-insert it if macro
* expansion is done.
*/
E->Final = NewTokNode ();
/* Insert a new token input function */
PushInput (MacExpand, E, ".DEFINE");
}
void MacExpandStart (void)
/* Start expanding the macro in SVal */
{
/* Search for the macro */
Macro* M = MacFind (SVal, HashStr (SVal) % HASHTAB_SIZE);
CHECK (M != 0);
/* Call the apropriate subroutine */
switch (M->Style) {
case MAC_STYLE_CLASSIC: StartExpClassic (M); break;
case MAC_STYLE_DEFINE: StartExpDefine (M); break;
default: Internal ("Invalid macro style: %d", M->Style);
}
}
void MacAbort (void)
/* Abort the current macro expansion */
{
/* Must have an expansion */
CHECK (MacExpansions > 0);
/* Set a flag so macro expansion will terminate on the next call */
DoMacAbort = 1;
}
int IsMacro (const char* Name)
/* Return true if the given name is the name of a macro */
{
return MacFind (Name, HashStr (Name) % HASHTAB_SIZE) != 0;
}
int IsDefine (const char* Name)
/* Return true if the given name is the name of a define style macro */
{
Macro* M = MacFind (Name, HashStr (Name) % HASHTAB_SIZE);
return (M != 0 && M->Style == MAC_STYLE_DEFINE);
}
int InMacExpansion (void)
/* Return true if we're currently expanding a macro */
{
return (MacExpansions > 0);
}