/*****************************************************************************/
/*                                                                           */
/*				   ulabel.c				     */
/*                                                                           */
/*		  Unnamed labels for the ca65 macroassembler		     */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 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.                                                          */
/*                                                                           */
/*****************************************************************************/



/* common */
#include "check.h"
#include "filepos.h"
#include "xmalloc.h"

/* ca65 */
#include "error.h"
#include "expr.h"
#include "scanner.h"
#include "ulabel.h"



/*****************************************************************************/
/*  		    	      	     Data				     */
/*****************************************************************************/



/* Struct that describes an unnamed label */
typedef struct ULabel ULabel;
struct ULabel {
    ULabel*    	Prev;         		/* Pointer to previous node in list */
    ULabel*    	Next;  	       		/* Pointer to next node in list */
    FilePos	Pos;			/* Position of the label in the source */
    ExprNode*	Val;          	       	/* The label value - may be NULL */
};

/* List management */
static ULabel*  ULabRoot       	= 0;	/* Root of the list */
static ULabel*  ULabLast	= 0;    /* Last ULabel */
static ULabel*  ULabLastDef	= 0;    /* Last defined ULabel */
static unsigned	ULabCount	= 0; 	/* Number of labels */
static unsigned ULabDefCount	= 0;	/* Number of defined labels */
static ULabel** ULabList	= 0;	/* Array with pointers to all labels */



/*****************************************************************************/
/*     	      	     	  	     Code		     	     	     */
/*****************************************************************************/



static ULabel* NewULabel (ExprNode* Val)
/* Create a new ULabel and insert it into the list. The function will move
 * ULabelLast, but not ULabelLastDef. The created label structure is returned.
 */
{
    /* Allocate memory for the ULabel structure */
    ULabel* L = xmalloc (sizeof (ULabel));

    /* Initialize the fields */
    L->Pos = CurPos;
    L->Val = Val;

    /* Insert the label into the list */
    L->Next = 0;
    if (ULabRoot == 0) {
	/* First label */
	L->Prev = 0;
	ULabRoot = L;
    } else {
    	ULabLast->Next = L;
    	L->Prev = ULabLast;
    }
    ULabLast = L;

    /* One label more */
    ++ULabCount;

    /* Return the created label */
    return L;
}



ExprNode* ULabRef (int Which)
/* Get an unnamed label. If Which is negative, it is a backreference (a
 * reference to an already defined label), and the function will return a
 * segment relative expression. If Which is positive, it is a forward ref,
 * and the function will return a expression node for an unnamed label that
 * must be resolved later.
 */
{
    ULabel* L;

    /* Which can never be 0 */
    PRECONDITION (Which != 0);

    /* Which is never really big (usually -3..+3), so a linear search is
     * the best we can do here.
     */
    L = ULabLastDef;
    if (Which < 0) {
      	/* Backward reference */
      	while (Which < -1 && L != 0) {
      	    L = L->Prev;
      	    ++Which;
      	}
      	if (L == 0) {
      	    /* Label does not exist */
      	    Error (ERR_UNDEFINED_LABEL);
      	    /* We must return something valid */
      	    return CurrentPC();
      	} else {
      	    /* Return a copy of the label value */
      	    return CloneExpr (L->Val);
      	}
    } else {
      	/* Forward reference. Create labels as needed */
	unsigned LabelNum = ULabDefCount + Which - 1;
	while (LabelNum < ULabCount) {
       	    NewULabel (0);
	}

	/* Return an unnamed label expression */
       	return ULabelExpr (LabelNum);
    }
}



void ULabDef (void)
/* Define an unnamed label at the current PC */
{
    /* Create a new label if needed, or use an existing one */
    if (ULabLastDef == 0 || ULabLastDef->Next == 0) {
      	/* The last label is also the last defined label, we need a new one */
      	ULabLastDef = NewULabel (CurrentPC ());
    } else {
      	/* We do already have the label, but it's undefined until now */
      	ULabLastDef = ULabLastDef->Next;
      	ULabLastDef->Val = CurrentPC ();
      	ULabLastDef->Pos = CurPos;
    }
    ++ULabDefCount;
}



int ULabCanResolve (void)
/* Return true if we can resolve arbitrary ULabels. */
{
    /* We can resolve labels if we have built the necessary access array */
    return (ULabList != 0);
}



ExprNode* ULabResolve (unsigned Index)
/* Return a valid expression for the unnamed label with the given index. This
 * is used to resolve unnamed labels when assembly is done, so it is an error
 * if a label is still undefined in this phase.
 */
{
    ULabel* L;

    /* Must be in resolve phase and the index must be valid */
    CHECK (ULabList != 0 && Index < ULabCount);

    /* Get the label */
    L = ULabList [Index];

    /* If the label is open (not defined), return some valid value */
    if (L->Val == 0) {
	return LiteralExpr (0);
    } else {
	return CloneExpr (L->Val);
    }
}



void ULabCheck (void)
/* Run through all unnamed labels and check for anomalies and errors */
{
    ULabel* L;

    /* Check if there are undefined labels */
    if (ULabLastDef) {
	L = ULabLastDef->Next;
	while (L) {
	    PError (&L->Pos, ERR_UNDEFINED_LABEL);
	    L = L->Next;
	}
    }

    /* Create an array that holds pointers to all labels. This allows us to
     * access the labels quickly by index in the resolver phase at the end of
     * the assembly.
     */
    if (ULabCount) {
	unsigned I = 0;
	ULabList = xmalloc (ULabCount * sizeof (ULabel*));
	L = ULabRoot;
	while (L) {
	    ULabList[I] = L;
	    ++I;
	    L = L->Next;
	}
	CHECK (I == ULabCount);
    }
}



