mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Also clean up built-in symbol creation This is not great, but currently okay. Should be fixed later, like the rest...
356 lines
9.6 KiB
C
356 lines
9.6 KiB
C
/*
|
|
* This file is part of RGBDS.
|
|
*
|
|
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "asm/fstack.h"
|
|
#include "asm/macro.h"
|
|
#include "asm/main.h"
|
|
#include "asm/symbol.h"
|
|
#include "asm/warning.h"
|
|
#include "platform.h" /* S_ISDIR (stat macro) */
|
|
|
|
#ifdef LEXER_DEBUG
|
|
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
|
|
#else
|
|
#define dbgPrint(...)
|
|
#endif
|
|
|
|
struct Context {
|
|
struct Context *parent;
|
|
struct Context *child;
|
|
struct LexerState *lexerState;
|
|
uint32_t uniqueID;
|
|
char const *fileName;
|
|
uint32_t lineNo; /* Line number at which the context was EXITED */
|
|
struct Symbol const *macro;
|
|
uint32_t nbReptIters; /* If zero, this isn't a REPT block */
|
|
size_t reptDepth;
|
|
uint32_t reptIters[];
|
|
};
|
|
|
|
static struct Context *contextStack;
|
|
static struct Context *topLevelContext;
|
|
static unsigned int contextDepth = 0;
|
|
unsigned int nMaxRecursionDepth;
|
|
|
|
static unsigned int nbIncPaths = 0;
|
|
static char const *includePaths[MAXINCPATHS];
|
|
|
|
void fstk_AddIncludePath(char const *path)
|
|
{
|
|
if (path[0] == '\0')
|
|
return;
|
|
if (nbIncPaths >= MAXINCPATHS) {
|
|
error("Too many include directories passed from command line\n");
|
|
return;
|
|
}
|
|
size_t len = strlen(path);
|
|
size_t allocSize = len + (path[len - 1] != '/') + 1;
|
|
char *str = malloc(allocSize);
|
|
|
|
if (!str) {
|
|
/* Attempt to continue without that path */
|
|
error("Failed to allocate new include path: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
memcpy(str, path, len);
|
|
char *end = str + len - 1;
|
|
|
|
if (*end++ != '/')
|
|
*end++ = '/';
|
|
*end = '\0';
|
|
includePaths[nbIncPaths++] = str;
|
|
}
|
|
|
|
static void printDep(char const *path)
|
|
{
|
|
if (dependfile) {
|
|
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
|
|
if (oGeneratePhonyDeps)
|
|
fprintf(dependfile, "%s:\n", path);
|
|
}
|
|
}
|
|
|
|
static bool isPathValid(char const *path)
|
|
{
|
|
struct stat statbuf;
|
|
|
|
if (stat(path, &statbuf) != 0)
|
|
return false;
|
|
|
|
/* Reject directories */
|
|
return !S_ISDIR(statbuf.st_mode);
|
|
}
|
|
|
|
bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|
{
|
|
if (!*size) {
|
|
*size = 64; /* This is arbitrary, really */
|
|
*fullPath = realloc(*fullPath, *size);
|
|
if (!*fullPath)
|
|
error("realloc error during include path search: %s\n",
|
|
strerror(errno));
|
|
}
|
|
|
|
if (*fullPath) {
|
|
for (size_t i = 0; i <= nbIncPaths; ++i) {
|
|
char const *incPath = i ? includePaths[i - 1] : "";
|
|
int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
|
|
|
|
/* Oh how I wish `asnprintf` was standard... */
|
|
if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
|
|
*size = len + 1;
|
|
*fullPath = realloc(*fullPath, *size);
|
|
if (!*fullPath) {
|
|
error("realloc error during include path search: %s\n",
|
|
strerror(errno));
|
|
break;
|
|
}
|
|
len = sprintf(*fullPath, "%s%s", incPath, path);
|
|
}
|
|
|
|
if (len < 0) {
|
|
error("snprintf error during include path search: %s\n",
|
|
strerror(errno));
|
|
} else if (isPathValid(*fullPath)) {
|
|
printDep(*fullPath);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
errno = ENOENT;
|
|
if (oGeneratedMissingIncludes)
|
|
printDep(path);
|
|
return false;
|
|
}
|
|
|
|
bool yywrap(void)
|
|
{
|
|
if (contextStack->nbReptIters) { /* The context is a REPT block, which may loop */
|
|
contextStack->reptIters[contextStack->reptDepth - 1]++;
|
|
/* If this wasn't the last iteration, wrap instead of popping */
|
|
if (contextStack->reptIters[contextStack->reptDepth - 1]
|
|
<= contextStack->nbReptIters) {
|
|
lexer_RestartRept(contextStack->parent->lineNo);
|
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
|
return false;
|
|
}
|
|
} else if (!contextStack->parent) {
|
|
return true;
|
|
}
|
|
dbgPrint("Popping context\n");
|
|
|
|
contextStack = contextStack->parent;
|
|
contextDepth--;
|
|
|
|
lexer_DeleteState(contextStack->child->lexerState);
|
|
/* Free the entry and make its parent the current entry */
|
|
free(contextStack->child);
|
|
|
|
contextStack->child = NULL;
|
|
lexer_SetState(contextStack->lexerState);
|
|
macro_SetUniqueID(contextStack->uniqueID);
|
|
return false;
|
|
}
|
|
|
|
static void newContext(uint32_t reptDepth)
|
|
{
|
|
if (++contextDepth >= nMaxRecursionDepth)
|
|
fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth);
|
|
contextStack->child = malloc(sizeof(*contextStack->child)
|
|
+ reptDepth * sizeof(contextStack->reptIters[0]));
|
|
if (!contextStack->child)
|
|
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
|
|
|
contextStack->lineNo = lexer_GetLineNo();
|
|
/* Link new entry to its parent so it's reachable later */
|
|
contextStack->child->parent = contextStack;
|
|
contextStack = contextStack->child;
|
|
|
|
contextStack->child = NULL;
|
|
contextStack->reptDepth = reptDepth;
|
|
}
|
|
|
|
void fstk_RunInclude(char const *path)
|
|
{
|
|
dbgPrint("Including path \"%s\"\n", path);
|
|
|
|
char *fullPath = NULL;
|
|
size_t size = 0;
|
|
|
|
if (!fstk_FindFile(path, &fullPath, &size)) {
|
|
free(fullPath);
|
|
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
|
return;
|
|
}
|
|
dbgPrint("Full path: \"%s\"\n", fullPath);
|
|
|
|
newContext(0);
|
|
contextStack->lexerState = lexer_OpenFile(fullPath);
|
|
if (!contextStack->lexerState)
|
|
fatalerror("Failed to set up lexer for file include\n");
|
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
|
/* We're back at top-level, so most things are reset */
|
|
contextStack->uniqueID = 0;
|
|
macro_SetUniqueID(0);
|
|
contextStack->fileName = lexer_GetFileName();
|
|
contextStack->macro = NULL;
|
|
contextStack->nbReptIters = 0;
|
|
}
|
|
|
|
void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
|
{
|
|
dbgPrint("Running macro \"%s\"\n", macroName);
|
|
|
|
struct Symbol *macro = sym_FindSymbol(macroName);
|
|
|
|
if (!macro) {
|
|
error("Macro \"%s\" not defined\n", macroName);
|
|
return;
|
|
}
|
|
if (macro->type != SYM_MACRO) {
|
|
error("\"%s\" is not a macro\n", macroName);
|
|
return;
|
|
}
|
|
macro_UseNewArgs(args);
|
|
|
|
newContext(0);
|
|
/* Line minus 1 because buffer begins with a newline */
|
|
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
|
macro->fileLine - 1);
|
|
if (!contextStack->lexerState)
|
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
|
contextStack->fileName = macro->fileName;
|
|
contextStack->macro = macro;
|
|
contextStack->nbReptIters = 0;
|
|
}
|
|
|
|
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size)
|
|
{
|
|
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
|
|
|
uint32_t reptDepth = contextStack->reptDepth;
|
|
|
|
newContext(reptDepth + 1);
|
|
contextStack->lexerState = lexer_OpenFileView(body, size, nReptLineNo);
|
|
if (!contextStack->lexerState)
|
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
|
contextStack->fileName = contextStack->parent->fileName;
|
|
contextStack->macro = contextStack->parent->macro; /* Inherit */
|
|
contextStack->nbReptIters = count;
|
|
/* Copy all of parent's iters, and add ours */
|
|
if (reptDepth)
|
|
memcpy(contextStack->reptIters, contextStack->parent->reptIters,
|
|
sizeof(contextStack->reptIters[0]) * reptDepth);
|
|
contextStack->reptIters[reptDepth] = 1;
|
|
|
|
/* Correct our parent's line number, which currently points to the `ENDR` line */
|
|
contextStack->parent->lineNo = nReptLineNo;
|
|
}
|
|
|
|
static void printContext(FILE *stream, struct Context const *context)
|
|
{
|
|
fprintf(stream, "%s", context->fileName);
|
|
if (context->macro)
|
|
fprintf(stream, "::%s", context->macro->name);
|
|
for (size_t i = 0; i < context->reptDepth; i++)
|
|
fprintf(stream, "::REPT~%" PRIu32, context->reptIters[i]);
|
|
fprintf(stream, "(%" PRId32 ")", context->lineNo);
|
|
}
|
|
|
|
static void dumpToStream(FILE *stream)
|
|
{
|
|
struct Context *context = topLevelContext;
|
|
|
|
while (context != contextStack) {
|
|
printContext(stream, context);
|
|
fprintf(stream, " -> ");
|
|
context = context->child;
|
|
}
|
|
contextStack->lineNo = lexer_GetLineNo();
|
|
printContext(stream, contextStack);
|
|
}
|
|
|
|
void fstk_Dump(void)
|
|
{
|
|
dumpToStream(stderr);
|
|
}
|
|
|
|
char *fstk_DumpToStr(void)
|
|
{
|
|
char *str;
|
|
size_t size;
|
|
/* `open_memstream` is specified to always include a '\0' at the end of the buffer! */
|
|
FILE *stream = open_memstream(&str, &size);
|
|
|
|
if (!stream)
|
|
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
|
|
dumpToStream(stream);
|
|
fclose(stream);
|
|
return str;
|
|
}
|
|
|
|
char const *fstk_GetFileName(void)
|
|
{
|
|
return contextStack->fileName;
|
|
}
|
|
|
|
uint32_t fstk_GetLine(void)
|
|
{
|
|
return lexer_GetLineNo();
|
|
}
|
|
|
|
void fstk_Init(char *mainPath, uint32_t maxRecursionDepth)
|
|
{
|
|
topLevelContext = malloc(sizeof(*topLevelContext));
|
|
if (!topLevelContext)
|
|
fatalerror("Failed to allocate memory for initial context: %s\n", strerror(errno));
|
|
topLevelContext->parent = NULL;
|
|
topLevelContext->child = NULL;
|
|
topLevelContext->lexerState = lexer_OpenFile(mainPath);
|
|
if (!topLevelContext->lexerState)
|
|
fatalerror("Failed to open main file!\n");
|
|
lexer_SetState(topLevelContext->lexerState);
|
|
topLevelContext->uniqueID = 0;
|
|
macro_SetUniqueID(0);
|
|
topLevelContext->fileName = lexer_GetFileName();
|
|
topLevelContext->macro = NULL;
|
|
topLevelContext->nbReptIters = 0;
|
|
topLevelContext->reptDepth = 0;
|
|
|
|
contextStack = topLevelContext;
|
|
|
|
#if 0
|
|
if (maxRecursionDepth
|
|
> (SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0])) {
|
|
#else
|
|
/* If this holds, then GCC raises a warning about the `if` above being dead code */
|
|
static_assert(UINT32_MAX
|
|
<= (SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0]));
|
|
if (0) {
|
|
#endif
|
|
error("Recursion depth may not be higher than %zu, defaulting to 64\n",
|
|
(SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0]));
|
|
nMaxRecursionDepth = 64;
|
|
} else {
|
|
nMaxRecursionDepth = maxRecursionDepth;
|
|
}
|
|
}
|