mirror of
https://github.com/Museum-of-Art-and-Digital-Entertainment/macross.git
synced 2024-11-26 22:51:04 +00:00
489 lines
12 KiB
C
489 lines
12 KiB
C
/*
|
|
* Copyright (c) 1987 Fujitsu
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
/*
|
|
lookups.c -- Routines to lookup things in tables.
|
|
|
|
Chip Morningstar -- Lucasfilm Ltd.
|
|
|
|
5-November-1984
|
|
*/
|
|
|
|
#include "macrossTypes.h"
|
|
#include "macrossGlobals.h"
|
|
#include "buildStuff.h"
|
|
#include "errorStuff.h"
|
|
#include "garbage.h"
|
|
#include "listing.h"
|
|
#include "lookups.h"
|
|
#include "operandStuff.h"
|
|
#include "parserMisc.h"
|
|
#include "semanticMisc.h"
|
|
|
|
/*
|
|
These routines all do basically the same thing. Various kinds of keywords
|
|
and so forth are stored in hash tables. The hash buckets contain linked
|
|
lists that are sorted alphabetically.
|
|
*/
|
|
|
|
conditionType
|
|
lookupConditionCode(char *s, int hashValue)
|
|
{
|
|
conditionTableEntryType *result;
|
|
|
|
result = (conditionTableEntryType *) prehashedStringLookup(s,
|
|
conditionTable, hashValue);
|
|
if (result != NULL)
|
|
return(result->code);
|
|
else
|
|
return(NOT_FOUND_COND);
|
|
}
|
|
|
|
int
|
|
lookupKeyword(char *s, int hashValue)
|
|
{
|
|
keywordTableEntryType *result;
|
|
|
|
result = (keywordTableEntryType *) prehashedStringLookup(s,
|
|
keywordTable, hashValue);
|
|
if (result != NULL)
|
|
return(result->token);
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
macroTableEntryType *
|
|
lookupMacroName(char *s, int hashValue)
|
|
{
|
|
return((macroTableEntryType *) prehashedStringLookup(s, macroTable,
|
|
hashValue));
|
|
}
|
|
|
|
opcodeTableEntryType *
|
|
lookupOpcode(char *s, int hashValue)
|
|
{
|
|
return((opcodeTableEntryType *) prehashedStringLookup(s,
|
|
opcodeTable, hashValue));
|
|
}
|
|
|
|
/* lookupOrEnterSymbol -- if there is an entry in the symbol table for the
|
|
given symbol, return that entry, otherwise create a new entry for it of
|
|
the given kind and return *that* */
|
|
|
|
symbolTableEntryType *
|
|
lookupOrEnterSymbol(stringType *s, symbolUsageKindType kind)
|
|
{
|
|
symbolTableEntryType *result;
|
|
|
|
if (result = (symbolTableEntryType *)hashStringLookup(s,symbolTable)){
|
|
/* result->referenceCount++;*/
|
|
return(result);
|
|
} else
|
|
return((symbolTableEntryType *)
|
|
hashStringEnter(buildSymbolTableEntry(s, kind),
|
|
symbolTable));
|
|
}
|
|
|
|
void
|
|
pushSymbol(symbolTableEntryType *symbol)
|
|
{
|
|
symbolInContextType *newContext;
|
|
|
|
newContext = typeAlloc(symbolInContextType);
|
|
newContext->pushedContexts = symbol->context;
|
|
symbol->context = newContext;
|
|
}
|
|
|
|
void
|
|
popSymbol(symbolTableEntryType *symbol)
|
|
{
|
|
symbolInContextType *deadContext;
|
|
|
|
deadContext = symbol->context;
|
|
if (deadContext == NULL)
|
|
botch("symbol pop underflow\n");
|
|
else {
|
|
symbol->context = deadContext->pushedContexts;
|
|
if (freeFlag) {
|
|
if (deadContext->value != NULL)
|
|
freeValue(deadContext->value);
|
|
free(deadContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
macroTableEntryType *
|
|
createMacro(stringType *macroName)
|
|
{
|
|
macroTableEntryType *result;
|
|
symbolTableEntryType *testSymbol;
|
|
|
|
testSymbol = lookupOrEnterSymbol(macroName, MACRO_SYMBOL);
|
|
if (testSymbol->context->usage != MACRO_SYMBOL) {
|
|
error(SYMBOL_ALREADY_THERE_ERROR, symbName(testSymbol));
|
|
return(NULL);
|
|
} else if (hashStringLookup(macroName, macroTable) != NULL) {
|
|
error(SYMBOL_ALREADY_THERE_ERROR, symbName(testSymbol));
|
|
return(NULL);
|
|
} else {
|
|
result = (macroTableEntryType *)
|
|
hashStringEnter(buildMacroTableEntry(macroName),
|
|
macroTable);
|
|
result->body = NULL;
|
|
return(result);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Generic table lookup utility routines
|
|
*/
|
|
|
|
genericTableEntryType *
|
|
prehashedStringLookup(char *s, genericTableEntryType **table, int hashValue)
|
|
{
|
|
genericTableEntryType *result;
|
|
int test;
|
|
|
|
result = table[hashValue];
|
|
while (result != NULL) {
|
|
if ((test = strcmplc(s, result->string)) == 0)
|
|
break;
|
|
else if (test > 0) {
|
|
result = NULL;
|
|
break;
|
|
} else {
|
|
result = result->next;
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
genericTableEntryType *
|
|
hashStringLookup(char *s, genericTableEntryType **table)
|
|
{
|
|
return(prehashedStringLookup(s, table, hashString(s)));
|
|
}
|
|
|
|
genericTableEntryType *
|
|
hashStringEnter(genericTableEntryType *entry, genericTableEntryType **table)
|
|
{
|
|
genericTableEntryType *result;
|
|
genericTableEntryType *oldResult;
|
|
int test;
|
|
int hashValue;
|
|
|
|
hashValue = hashString(entry->string);
|
|
result = table[hashValue];
|
|
if (result == NULL) {
|
|
table[hashValue] = entry;
|
|
entry->next = NULL;
|
|
return(entry);
|
|
}
|
|
oldResult = NULL;
|
|
while (result != NULL) {
|
|
if ((test = strcmplc(entry->string, result->string)) == 0) {
|
|
botch("symbol table entry %s already there\n",
|
|
entry->string);
|
|
} else if (test > 0) {
|
|
entry->next = result;
|
|
if (oldResult == NULL)
|
|
table[hashValue] = entry;
|
|
else
|
|
oldResult->next = entry;
|
|
return(entry);
|
|
} else {
|
|
oldResult = result;
|
|
result = result->next;
|
|
}
|
|
}
|
|
if (oldResult == NULL)
|
|
table[hashValue] = entry;
|
|
else
|
|
oldResult->next = entry;
|
|
entry->next = NULL;
|
|
return(entry);
|
|
}
|
|
|
|
int
|
|
hashString(char *s)
|
|
{
|
|
unsigned result;
|
|
|
|
result = 0;
|
|
while (*s != '\0')
|
|
result = (result << 1) + toLowerCase(*s++);
|
|
return(result & HASH_TABLE_MASK);
|
|
}
|
|
|
|
bool
|
|
strcmplc(char *s1, char *s2) /* string compare in lower case */
|
|
/* heavily optimized version */
|
|
|
|
{
|
|
char c1;
|
|
int result;
|
|
|
|
do {
|
|
c1 = toLowerCase(*s1++);
|
|
|
|
/* if result != 0, they differ */
|
|
if (result = c1 - toLowerCase(*s2++)) {
|
|
return(result); /* c1<c2==neg, c1>c2==pos */
|
|
} else if (!c1) { /* if they're null, we're done */
|
|
return(0);
|
|
}
|
|
} while (TRUE);
|
|
}
|
|
|
|
bool
|
|
strcmplct(char *s1, char *s2) /* For tables: s2 is already lower case */
|
|
/* heavily optimized version. */
|
|
|
|
{
|
|
char c1;
|
|
int result;
|
|
|
|
while (TRUE) {
|
|
c1 = toLowerCase(*s1++);
|
|
if (result = c1 - (*s2++)) { /* if result != 0, they differ */
|
|
return(result); /* c1<c2==neg, c1>c2==pos */
|
|
} else if (!c1) { /* if they're null, we're done */
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
purgeSymbol(symbolTableEntryType *symbol)
|
|
{
|
|
symbolInContextType *context;
|
|
|
|
if ((context = getWorkingContext(symbol)) != NULL)
|
|
context->usage = DEAD_SYMBOL;
|
|
}
|
|
|
|
void
|
|
reincarnateSymbol(symbolInContextType *context, symbolUsageKindType newUsage)
|
|
{
|
|
context->attributes = 0;
|
|
dupValue(context->value, UndefinedValue);
|
|
context->usage = newUsage;
|
|
}
|
|
|
|
|
|
/*
|
|
Routines to handle assembly-time binding of symbols to contexts.
|
|
*/
|
|
|
|
void
|
|
pushBinding(symbolTableEntryType *symbol, valueType *newBinding, symbolUsageKindType newUsage)
|
|
{
|
|
pushSymbol(symbol);
|
|
if (newBinding == NULL)
|
|
newBinding = newValue(FAIL, 0, EXPRESSION_OPND);
|
|
symbol->context->value = newBinding;
|
|
symbol->context->usage = newUsage;
|
|
symbol->context->environmentNumber = currentEnvironment->
|
|
environmentNumber;
|
|
symbol->context->attributes = DEFINED_VARIABLE_ATT;
|
|
}
|
|
|
|
void
|
|
popBinding(symbolTableEntryType *symbol)
|
|
{
|
|
popSymbol(symbol);
|
|
}
|
|
|
|
int /* returns number of bindings completed, negative this if failure */
|
|
bindMacroArguments(argumentDefinitionListType *argumentList, operandListType *parameterList, stringType *macroName)
|
|
{
|
|
int numberBound;
|
|
bool arrayTag;
|
|
int arrayLength;
|
|
valueType **arrayContents;
|
|
int i;
|
|
|
|
if (argumentList == NULL)
|
|
arrayTag = FALSE;
|
|
else
|
|
arrayTag = ((argumentListHeadType *)argumentList)->arrayTag;
|
|
numberBound = 1;
|
|
while (argumentList!=NULL && (!arrayTag || argumentList->
|
|
nextArgument!=NULL) && parameterList!=NULL) {
|
|
pushBinding(argumentList->theArgument, newValue(OPERAND_VALUE,
|
|
parameterList, EXPRESSION_OPND), ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
parameterList = parameterList->nextOperand;
|
|
numberBound++;
|
|
}
|
|
if (!arrayTag) {
|
|
if (parameterList != NULL) {
|
|
error(TOO_MANY_ARGUMENTS_TO_MACRO_ERROR, macroName,
|
|
numberBound-1);
|
|
return(-numberBound);
|
|
}
|
|
while (argumentList != NULL) {
|
|
pushBinding(argumentList->theArgument, newValue(FAIL,
|
|
0, EXPRESSION_OPND), ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
numberBound++;
|
|
}
|
|
} else {
|
|
if (parameterList == NULL) {
|
|
while (argumentList->nextArgument != NULL) {
|
|
pushBinding(argumentList->theArgument,
|
|
newValue(FAIL, 0, EXPRESSION_OPND),
|
|
ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
numberBound++;
|
|
}
|
|
}
|
|
arrayLength = countParameters(parameterList);
|
|
pushBinding(argumentList->theArgument, newValue(ARRAY_VALUE,
|
|
allocArray(arrayLength, &arrayContents),
|
|
EXPRESSION_OPND), ARGUMENT_SYMBOL);
|
|
for (i=0; i<arrayLength; ++i) {
|
|
arrayContents[i] = newValue(OPERAND_VALUE,
|
|
parameterList, EXPRESSION_OPND);
|
|
parameterList = parameterList->nextOperand;
|
|
}
|
|
numberBound++;
|
|
}
|
|
return(numberBound);
|
|
}
|
|
|
|
int /* returns number of bindings completed, negative this if failure */
|
|
bindFunctionArguments(argumentDefinitionListType *argumentList, operandListType *parameterList, stringType *functionName)
|
|
{
|
|
valueType *argument;
|
|
bool arrayTag;
|
|
int arrayLength;
|
|
valueType **arrayContents;
|
|
int i;
|
|
int numberBound;
|
|
valueType *firstArgument;
|
|
environmentType *saveEnvironment;
|
|
|
|
if (argumentList == NULL)
|
|
arrayTag = FALSE;
|
|
else
|
|
arrayTag = ((argumentListHeadType *)argumentList)->arrayTag;
|
|
numberBound = 1;
|
|
firstArgument = NULL;
|
|
while (argumentList!=NULL && (!arrayTag || argumentList->
|
|
nextArgument!=NULL) && parameterList!=NULL) {
|
|
saveEnvironment = currentEnvironment;
|
|
currentEnvironment = currentEnvironment->previousEnvironment;
|
|
argument = evaluateOperand(parameterList);
|
|
currentEnvironment = saveEnvironment;
|
|
if (firstArgument == NULL)
|
|
firstArgument = argument;
|
|
if (isUsable(argument)) {
|
|
pushBinding(argumentList->theArgument, argument,
|
|
ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
parameterList = parameterList->nextOperand;
|
|
expand((argumentList!=NULL && parameterList!=NULL) ?
|
|
moreExpression(", ") : 0);
|
|
numberBound++;
|
|
} else {
|
|
if (isUndefined(argument))
|
|
resultOfLastFunctionCall =
|
|
newValue(UNDEFINED_VALUE, 0,
|
|
firstArgument->addressMode);
|
|
return(-numberBound);
|
|
}
|
|
}
|
|
if (!arrayTag) {
|
|
if (parameterList != NULL) {
|
|
error(TOO_MANY_ARGUMENTS_TO_FUNCTION_ERROR,
|
|
functionName, numberBound - 1);
|
|
return(-numberBound);
|
|
}
|
|
while (argumentList != NULL) {
|
|
pushBinding(argumentList->theArgument, newValue(FAIL,
|
|
0, EXPRESSION_OPND), ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
numberBound++;
|
|
}
|
|
} else {
|
|
if (parameterList == NULL) {
|
|
while (argumentList->nextArgument != NULL) {
|
|
pushBinding(argumentList->theArgument,
|
|
newValue(FAIL, 0, EXPRESSION_OPND),
|
|
ARGUMENT_SYMBOL);
|
|
argumentList = argumentList->nextArgument;
|
|
numberBound++;
|
|
}
|
|
}
|
|
arrayLength = countParameters(parameterList);
|
|
pushBinding(argumentList->theArgument, newValue(ARRAY_VALUE,
|
|
allocArray(arrayLength, &arrayContents),
|
|
EXPRESSION_OPND), ARGUMENT_SYMBOL);
|
|
numberBound++;
|
|
for (i=0; i<arrayLength; ++i) {
|
|
saveEnvironment = currentEnvironment;
|
|
currentEnvironment = currentEnvironment->
|
|
previousEnvironment;
|
|
argument = evaluateOperand(parameterList);
|
|
currentEnvironment = saveEnvironment;
|
|
if (firstArgument == NULL)
|
|
firstArgument = argument;
|
|
if (isUsable(argument)) {
|
|
arrayContents[i] = argument;
|
|
parameterList = parameterList->nextOperand;
|
|
} else {
|
|
if (isUndefined(argument))
|
|
resultOfLastFunctionCall =
|
|
newValue(UNDEFINED_VALUE, 0,
|
|
firstArgument->addressMode);
|
|
return(-numberBound);
|
|
}
|
|
}
|
|
}
|
|
return(numberBound);
|
|
}
|
|
|
|
void
|
|
unbindArguments(argumentDefinitionListType *argumentList, int numberToUnbind)
|
|
{
|
|
while (argumentList != NULL && numberToUnbind-- > 0) {
|
|
popBinding(argumentList->theArgument);
|
|
argumentList = argumentList->nextArgument;
|
|
}
|
|
if (numberToUnbind > 0)
|
|
botch("binding count larger than number of bindings\n");
|
|
}
|
|
|
|
void
|
|
unbindLocalVariables(identifierListType *identifierList)
|
|
{
|
|
identifierListType *deadEntry;
|
|
|
|
while (identifierList != NULL) {
|
|
popBinding(identifierList->theSymbol);
|
|
deadEntry = identifierList;
|
|
identifierList = identifierList->nextIdentifier;
|
|
qfree(deadEntry);
|
|
}
|
|
}
|