1
0
mirror of https://github.com/cc65/cc65.git synced 2024-11-15 11:05:56 +00:00
cc65/src/ca65/macro.c

793 lines
20 KiB
C
Raw Normal View History

/*****************************************************************************/
/* */
/* macro.c */
/* */
/* Macros for the ca65 macroassembler */
/* */
/* */
/* */
/* (C) 1998-2003 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 (TokIsSep (Tok)) {
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 (!TokIsSep (Tok)) {
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 (TokIsSep (Tok) || 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 && !TokIsSep (Tok));
/* 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);
}