diff --git a/Makefile b/Makefile index ee2605f7..cecb0a6e 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ rgbasm_obj := \ src/hashmap.o \ src/linkdefs.o -src/asm/lexer.o: src/asm/asmy.h +src/asm/lexer.o src/asm/main.o: src/asm/asmy.h rgblink_obj := \ src/link/assign.o \ diff --git a/include/asm/fstack.h b/include/asm/fstack.h index d365b954..bbf321c3 100644 --- a/include/asm/fstack.h +++ b/include/asm/fstack.h @@ -41,13 +41,7 @@ struct sContext { extern unsigned int nMaxRecursionDepth; -void fstk_RunInclude(char *tzFileName); -void fstk_Init(char *s); -void fstk_Dump(void); -void fstk_DumpToStr(char *buf, size_t len); -void fstk_AddIncludePath(char *s); -void fstk_RunMacro(char *s, struct MacroArgs *args); -void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char const *body, size_t size); +void fstk_AddIncludePath(char const *s); /** * @param path The user-provided file name * @param fullPath The address of a pointer, which will be made to point at the full path @@ -56,6 +50,16 @@ void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char const *body, size_t * @return True if the file was found, false if no path worked */ bool fstk_FindFile(char const *path, char **fullPath, size_t *size); -int32_t fstk_GetLine(void); + +bool yywrap(void); +void fstk_RunInclude(char const *path); +void fstk_RunMacro(char *macroName, struct MacroArgs *args); +void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size); + +void fstk_Dump(void); +char *fstk_DumpToStr(void); +uint32_t fstk_GetLine(void); + +void fstk_Init(char *mainPath, uint32_t maxRecursionDepth); #endif /* RGBDS_ASM_FSTACK_H */ diff --git a/include/asm/lexer.h b/include/asm/lexer.h index 842a91f3..d1cfd08b 100644 --- a/include/asm/lexer.h +++ b/include/asm/lexer.h @@ -31,7 +31,8 @@ static inline void lexer_SetStateAtEOL(struct LexerState *state) } struct LexerState *lexer_OpenFile(char const *path); -struct LexerState *lexer_OpenFileView(void); +struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo); +void lexer_RestartRept(uint32_t lineNo); void lexer_DeleteState(struct LexerState *state); void lexer_Init(void); @@ -50,7 +51,7 @@ uint32_t lexer_GetLineNo(void); uint32_t lexer_GetColNo(void); void lexer_DumpStringExpansions(void); int yylex(void); -void lexer_CaptureBlock(int blockStartToken, int blockEndToken, char const **capture, size_t *size, +void lexer_CaptureBlock(int blockStartToken, int blockEndToken, char **capture, size_t *size, char const *name); #endif /* RGBDS_ASM_LEXER_H */ diff --git a/include/asm/macro.h b/include/asm/macro.h index 2142ea71..855133f8 100644 --- a/include/asm/macro.h +++ b/include/asm/macro.h @@ -28,6 +28,7 @@ char const *macro_GetArg(uint32_t i); uint32_t macro_GetUniqueID(void); char const *macro_GetUniqueIDStr(void); void macro_SetUniqueID(uint32_t id); +uint32_t macro_UseNewUniqueID(void); void macro_ShiftCurrentArgs(void); uint32_t macro_NbArgs(void); diff --git a/include/asm/symbol.h b/include/asm/symbol.h index 8503490b..22ae95ab 100644 --- a/include/asm/symbol.h +++ b/include/asm/symbol.h @@ -45,7 +45,7 @@ struct Symbol { }; struct { /* For SYM_MACRO */ size_t macroSize; - char const *macro; + char *macro; }; }; @@ -117,7 +117,7 @@ uint32_t sym_GetPCValue(void); uint32_t sym_GetConstantSymValue(struct Symbol const *sym); uint32_t sym_GetConstantValue(char const *s); struct Symbol *sym_FindSymbol(char const *symName); -struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char const *body, size_t size); +struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size); struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_AddString(char const *symName, char const *value); uint32_t sym_GetDefinedValue(char const *s); diff --git a/src/asm/asmy.y b/src/asm/asmy.y index d10681f3..ac08843d 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -597,21 +597,21 @@ load : T_POP_LOAD string ',' sectiontype sectorg sectattrs { rept : T_POP_REPT uconst { uint32_t nDefinitionLineNo = lexer_GetLineNo(); - char const *body; + char *body; size_t size; lexer_CaptureBlock(T_POP_REPT, T_POP_ENDR, &body, &size, "REPT block"); - fstk_RunRept($2, nDefinitionLineNo, body, size); + fstk_RunRept($2, nDefinitionLineNo, body, size - strlen("ENDR")); } ; macrodef : T_LABEL ':' T_POP_MACRO { int32_t nDefinitionLineNo = lexer_GetLineNo(); - char const *body; + char *body; size_t size; lexer_CaptureBlock(T_POP_MACRO, T_POP_ENDM, &body, &size, "macro definition"); - sym_AddMacro($1, nDefinitionLineNo, body, size); + sym_AddMacro($1, nDefinitionLineNo, body, size - strlen("ENDM")); } ; diff --git a/src/asm/fstack.c b/src/asm/fstack.c index 2e3dc1bd..3e02c936 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -6,318 +6,82 @@ * SPDX-License-Identifier: MIT */ -/* - * FileStack routines - */ - +#include +#include #include #include -#include #include #include #include -#include -#include -#include #include "asm/fstack.h" -#include "asm/lexer.h" #include "asm/macro.h" #include "asm/main.h" -#include "asm/output.h" +#include "asm/symbol.h" #include "asm/warning.h" +#include "platform.h" /* S_ISDIR (stat macro) */ -#include "extern/err.h" +struct Context { + struct Context *parent; + struct Context *child; + struct LexerState *lexerState; + uint32_t uniqueID; + char *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[]; +}; -#include "platform.h" // S_ISDIR (stat macro) -#include "types.h" - -static struct sContext *pFileStack; -static unsigned int nFileStackDepth; +static struct Context *contextStack; +static struct Context *topLevelContext; +static unsigned int contextDepth = 0; unsigned int nMaxRecursionDepth; -static struct Symbol const *pCurrentMacro; -static uint32_t nCurrentStatus; -static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1]; -static int32_t NextIncPath; -static uint32_t nMacroCount; -static char const *pCurrentREPTBlock; -static uint32_t nCurrentREPTBlockSize; -static uint32_t nCurrentREPTBlockCount; -static int32_t nCurrentREPTBodyFirstLine; -static int32_t nCurrentREPTBodyLastLine; +static unsigned int nbIncPaths = 0; +static char const *includePaths[MAXINCPATHS]; -uint32_t ulMacroReturnValue; - -/* - * defines for nCurrentStatus - */ -#define STAT_isInclude 0 /* 'Normal' state as well */ -#define STAT_isMacro 1 -#define STAT_isMacroArg 2 -#define STAT_isREPTBlock 3 - -/* Max context stack size */ - -/* - * Context push and pop - */ -static void pushcontext(void) +void fstk_AddIncludePath(char const *path) { - struct sContext **ppFileStack; - - if (++nFileStackDepth > nMaxRecursionDepth) - fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth); - - ppFileStack = &pFileStack; - while (*ppFileStack) - ppFileStack = &((*ppFileStack)->next); - - *ppFileStack = malloc(sizeof(struct sContext)); - - if (*ppFileStack == NULL) - fatalerror("No memory for context\n"); - - (*ppFileStack)->next = NULL; - (*ppFileStack)->nLine = lexer_GetLineNo(); - - switch ((*ppFileStack)->nStatus = nCurrentStatus) { - case STAT_isMacroArg: - case STAT_isMacro: - (*ppFileStack)->macroArgs = macro_GetCurrentArgs(); - (*ppFileStack)->pMacro = pCurrentMacro; - break; - case STAT_isInclude: - break; - case STAT_isREPTBlock: - (*ppFileStack)->macroArgs = macro_GetCurrentArgs(); - (*ppFileStack)->pREPTBlock = pCurrentREPTBlock; - (*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize; - (*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount; - (*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine; - (*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine; - break; - default: - fatalerror("%s: Internal error.\n", __func__); + if (path[0] == '\0') + return; + if (nbIncPaths >= MAXINCPATHS) { + error("Too many include directories passed from command line\n"); + return; } - (*ppFileStack)->uniqueID = macro_GetUniqueID(); + 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 int32_t popcontext(void) -{ - struct sContext *pLastFile, **ppLastFile; - - if (nCurrentStatus == STAT_isREPTBlock) { - if (--nCurrentREPTBlockCount) { - char *pREPTIterationWritePtr; - unsigned long nREPTIterationNo; - int nNbCharsWritten; - int nNbCharsLeft; - - macro_SetUniqueID(nMacroCount++); - - /* Increment REPT count in file path */ - pREPTIterationWritePtr = - strrchr(lexer_GetFileName(), '~') + 1; - nREPTIterationNo = - strtoul(pREPTIterationWritePtr, NULL, 10); - nNbCharsLeft = sizeof(lexer_GetFileName()) - - (pREPTIterationWritePtr - lexer_GetFileName()); - nNbCharsWritten = snprintf(pREPTIterationWritePtr, - nNbCharsLeft, "%lu", - nREPTIterationNo + 1); - if (nNbCharsWritten >= nNbCharsLeft) { - /* - * The string is probably corrupted somehow, - * revert the change to avoid a bad error - * output. - */ - sprintf(pREPTIterationWritePtr, "%lu", - nREPTIterationNo); - fatalerror("Cannot write REPT count to file path\n"); - } - - return 0; - } - } - - pLastFile = pFileStack; - if (pLastFile == NULL) - return 1; - - ppLastFile = &pFileStack; - while (pLastFile->next) { - ppLastFile = &(pLastFile->next); - pLastFile = *ppLastFile; - } - - lexer_DeleteState(lexer_GetState()); - lexer_SetState(pLastFile->lexerState); - - switch (pLastFile->nStatus) { - struct MacroArgs *args; - - case STAT_isMacroArg: - case STAT_isMacro: - args = macro_GetCurrentArgs(); - if (nCurrentStatus == STAT_isMacro) { - macro_FreeArgs(args); - free(args); - } - macro_UseNewArgs(pLastFile->macroArgs); - pCurrentMacro = pLastFile->pMacro; - break; - case STAT_isInclude: - break; - case STAT_isREPTBlock: - args = macro_GetCurrentArgs(); - if (nCurrentStatus == STAT_isMacro) { - macro_FreeArgs(args); - free(args); - } - macro_UseNewArgs(pLastFile->macroArgs); - pCurrentREPTBlock = pLastFile->pREPTBlock; - nCurrentREPTBlockSize = pLastFile->nREPTBlockSize; - nCurrentREPTBlockCount = pLastFile->nREPTBlockCount; - nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine; - break; - default: - fatalerror("%s: Internal error.\n", __func__); - } - macro_SetUniqueID(pLastFile->uniqueID); - - nCurrentStatus = pLastFile->nStatus; - - nFileStackDepth--; - - free(*ppLastFile); - *ppLastFile = NULL; - return 0; -} - -int32_t fstk_GetLine(void) -{ - struct sContext *pLastFile, **ppLastFile; - - switch (nCurrentStatus) { - case STAT_isInclude: - /* This is the normal mode, also used when including a file. */ - return lexer_GetLineNo(); - case STAT_isMacro: - break; /* Peek top file of the stack */ - case STAT_isMacroArg: - return lexer_GetLineNo(); /* ??? */ - case STAT_isREPTBlock: - break; /* Peek top file of the stack */ - default: - fatalerror("%s: Internal error.\n", __func__); - } - - pLastFile = pFileStack; - - if (pLastFile != NULL) { - while (pLastFile->next) { - ppLastFile = &(pLastFile->next); - pLastFile = *ppLastFile; - } - return pLastFile->nLine; - } - - /* - * This is only reached if the lexer is in REPT or MACRO mode but there - * are no saved contexts with the origin of said REPT or MACRO. - */ - fatalerror("%s: Internal error.\n", __func__); -} - -int yywrap(void) -{ - return popcontext(); -} - -/* - * Dump the context stack to stderr - */ -void fstk_Dump(void) -{ - const struct sContext *pLastFile; - - pLastFile = pFileStack; - - while (pLastFile) { - fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName, - pLastFile->nLine); - pLastFile = pLastFile->next; - } - char const *fileName = lexer_GetFileName(); - - if (fileName) - fprintf(stderr, "%s(%" PRId32 ",%" PRId32 "): ", - fileName, lexer_GetLineNo(), lexer_GetColNo()); -} - -void fstk_DumpToStr(char *buf, size_t buflen) -{ - const struct sContext *pLastFile = pFileStack; - int retcode; - size_t len = buflen; - - while (pLastFile) { - retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ", - pLastFile->tzFileName, pLastFile->nLine); - if (retcode < 0) - fatalerror("Failed to dump file stack to string: %s\n", strerror(errno)); - else if (retcode >= len) - len = 0; - else - len -= retcode; - pLastFile = pLastFile->next; - } - - retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")", - lexer_GetFileName(), lexer_GetLineNo()); - if (retcode < 0) - fatalerror("Failed to dump file stack to string: %s\n", strerror(errno)); - else if (retcode >= len) - len = 0; - else - len -= retcode; - - if (!len) - warning(WARNING_LONG_STR, "File stack dump too long, got truncated\n"); -} - -/* - * Extra includepath stuff - */ -void fstk_AddIncludePath(char *s) -{ - if (NextIncPath == MAXINCPATHS) - fatalerror("Too many include directories passed from command line\n"); - - // Find last occurrence of slash; is it at the end of the string? - char const *lastSlash = strrchr(s, '/'); - char const *pattern = lastSlash && *(lastSlash + 1) == 0 ? "%s" : "%s/"; - - if (snprintf(IncludePaths[NextIncPath++], _MAX_PATH, pattern, - s) >= _MAX_PATH) - fatalerror("Include path too long '%s'\n", s); -} - -static void printdep(const char *fileName) +static void printDep(char const *path) { if (dependfile) { - fprintf(dependfile, "%s: %s\n", tzTargetFileName, fileName); + fprintf(dependfile, "%s: %s\n", tzTargetFileName, path); if (oGeneratePhonyDeps) - fprintf(dependfile, "%s:\n", fileName); + fprintf(dependfile, "%s:\n", path); } } -static bool isPathValid(char const *pathname) +static bool isPathValid(char const *path) { struct stat statbuf; - if (stat(pathname, &statbuf) != 0) + if (stat(path, &statbuf) != 0) return false; /* Reject directories */ @@ -335,8 +99,8 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size) } if (*fullPath) { - for (size_t i = 0; i <= NextIncPath; ++i) { - char *incPath = i ? IncludePaths[i - 1] : ""; + 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... */ @@ -355,7 +119,7 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size) error("snprintf error during include path search: %s\n", strerror(errno)); } else if (isPathValid(*fullPath)) { - printdep(*fullPath); + printDep(*fullPath); return true; } } @@ -363,114 +127,210 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size) errno = ENOENT; if (oGeneratedMissingIncludes) - printdep(path); + printDep(path); return false; } -/* - * Set up an include file for parsing - */ -void fstk_RunInclude(char *tzFileName) +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; + } + contextStack = contextStack->parent; + contextDepth--; + + lexer_DeleteState(contextStack->child->lexerState); + /* If at top level (= not in macro or in REPT), free the file name */ + if (!contextStack->macro && contextStack->reptIters == 0) + free(contextStack->child->fileName); + /* Free the entry and make its parent the current entry */ + free(contextStack->child); + + contextStack->child = NULL; + lexer_SetState(contextStack->lexerState); + 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) { char *fullPath = NULL; size_t size = 0; - if (!fstk_FindFile(tzFileName, &fullPath, &size)) { - if (oGeneratedMissingIncludes) - oFailedOnMissingInclude = true; - else - error("Unable to open included file '%s': %s\n", - tzFileName, strerror(errno)); + if (!fstk_FindFile(path, &fullPath, &size)) { free(fullPath); + error("Unable to open included file '%s': %s\n", path, strerror(errno)); return; } - pushcontext(); - nCurrentStatus = STAT_isInclude; - if (verbose) - printf("Assembling %s\n", fullPath); - - struct LexerState *state = lexer_OpenFile(fullPath); - - if (!state) - /* If lexer had an error, it already reported it */ - fatalerror("Failed to open file for INCLUDE\n"); /* TODO: make this non-fatal? */ - lexer_SetStateAtEOL(state); - free(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 = fullPath; + contextStack->macro = NULL; + contextStack->nbReptIters = 0; } -/* - * Set up a macro for parsing - */ -void fstk_RunMacro(char *s, struct MacroArgs *args) +void fstk_RunMacro(char *macroName, struct MacroArgs *args) { - struct Symbol const *sym = sym_FindSymbol(s); + struct Symbol *macro = sym_FindSymbol(macroName); - if (sym == NULL) { - error("Macro \"%s\" not defined\n", s); + if (!macro) { + error("Macro \"%s\" not defined\n", macroName); return; } - if (sym->type != SYM_MACRO) { - error("\"%s\" is not a macro\n", s); + if (macro->type != SYM_MACRO) { + error("\"%s\" is not a macro\n", macroName); return; } - - pushcontext(); - macro_SetUniqueID(nMacroCount++); - /* Minus 1 because there is a newline at the beginning of the buffer */ macro_UseNewArgs(args); - nCurrentStatus = STAT_isMacro; - pCurrentMacro = sym; + newContext(0); + contextStack->lexerState = lexer_OpenFileView(macro->macro, + macro->macroSize, macro->fileLine); + 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; } -/* - * Set up a repeat block for parsing - */ -void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char const *body, size_t size) +void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size) { - if (count) { - pushcontext(); - macro_SetUniqueID(nMacroCount++); - nCurrentREPTBlockCount = count; - nCurrentStatus = STAT_isREPTBlock; - nCurrentREPTBlockSize = size; - pCurrentREPTBlock = body; - nCurrentREPTBodyFirstLine = nReptLineNo + 1; + 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; +} + +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 = mainPath; + 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; } } - -/* - * Initialize the filestack routines - */ -void fstk_Init(char *pFileName) -{ - char tzSymFileName[_MAX_PATH + 1 + 2]; - - char *c = pFileName; - int fileNameIndex = 0; - - tzSymFileName[fileNameIndex++] = '"'; - - // minus 2 to account for trailing "\"\0" - // minus 1 to avoid a buffer overflow in extreme cases - while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) { - if (*c == '"') { - tzSymFileName[fileNameIndex++] = '\\'; - } - - tzSymFileName[fileNameIndex++] = *c; - ++c; - } - - tzSymFileName[fileNameIndex++] = '"'; - tzSymFileName[fileNameIndex] = '\0'; - - sym_AddString("__FILE__", tzSymFileName); - - pFileStack = NULL; - nFileStackDepth = 0; - - nMacroCount = 0; - nCurrentStatus = STAT_isInclude; -} diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 71c4a95c..6bfc1187 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -258,6 +258,8 @@ struct LexerState { }; /* Common state */ + bool isFile; + enum LexerMode mode; bool atLineStart; uint32_t lineNo; @@ -278,6 +280,21 @@ struct LexerState { struct LexerState *lexerState = NULL; struct LexerState *lexerStateEOL = NULL; +static void initState(struct LexerState *state) +{ + state->mode = LEXER_NORMAL; + state->atLineStart = true; /* yylex() will init colNo due to this */ + state->lastToken = 0; + + state->capturing = false; + state->captureBuf = NULL; + + state->nbChars = 0; + state->expandStrings = true; + state->expansions = NULL; + state->expansionOfs = 0; +} + struct LexerState *lexer_OpenFile(char const *path) { bool isStdin = !strcmp(path, "-"); @@ -292,6 +309,7 @@ struct LexerState *lexer_OpenFile(char const *path) } state->path = path; + state->isFile = true; state->fd = isStdin ? STDIN_FILENO : open(path, O_RDONLY); if (state->fd == -1) { error("Failed to open file \"%s\": %s\n", path, strerror(errno)); @@ -345,32 +363,45 @@ struct LexerState *lexer_OpenFile(char const *path) state->index = 0; } - state->mode = LEXER_NORMAL; - state->atLineStart = true; /* yylex() will init colNo due to this */ - state->lineNo = 0; - state->lastToken = 0; - - state->capturing = false; - state->captureBuf = NULL; - - state->nbChars = 0; - state->expandStrings = true; - state->expansions = NULL; - state->expansionOfs = 0; + initState(state); + state->lineNo = 0; /* Will be incremented at first line start */ return state; } -struct LexerState *lexer_OpenFileView(void) +struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo) { - return NULL; + struct LexerState *state = malloc(sizeof(*state)); + + if (!state) { + error("Failed to allocate memory for lexer state: %s", strerror(errno)); + return NULL; + } + // TODO: init `path` + + state->isFile = false; + state->isMmapped = true; /* It's not *really* mmap()ed, but it behaves the same */ + state->ptr = buf; + state->size = size; + state->offset = 0; + + initState(state); + state->lineNo = lineNo; /* Will be incremented at first line start */ + return state; +} + +void lexer_RestartRept(uint32_t lineNo) +{ + lexerState->offset = 0; + initState(lexerState); + lexerState->lineNo = lineNo; } void lexer_DeleteState(struct LexerState *state) { - if (state->isMmapped) - munmap(state->ptr, state->size); - else + if (!state->isMmapped) close(state->fd); + else if (state->isFile) + munmap(state->ptr, state->size); free(state); } @@ -523,7 +554,7 @@ static void beginExpansion(size_t distance, uint8_t skip, #define LOOKUP_PRE_NEST(exp) (exp)->totalLen += size #define LOOKUP_POST_NEST(exp) do { \ if (++depth >= nMaxRecursionDepth) \ - fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth); \ + fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth); \ } while (0) lookupExpansion(parent, distance); #undef LOOKUP_PRE_NEST @@ -536,7 +567,7 @@ static void beginExpansion(size_t distance, uint8_t skip, *insertPoint = malloc(sizeof(**insertPoint)); if (!*insertPoint) - fatalerror("Unable to allocate new expansion: %s", strerror(errno)); + fatalerror("Unable to allocate new expansion: %s\n", strerror(errno)); (*insertPoint)->firstChild = NULL; (*insertPoint)->next = NULL; /* Expansions are always performed left to right */ (*insertPoint)->name = strdup(name); @@ -1417,10 +1448,6 @@ static int yylex_NORMAL(void) return '\n'; case EOF: - /* Captures end at their buffer's boundary no matter what */ - if (!lexerState->capturing) { - /* TODO: use `yywrap()` */ - } return 0; /* Handle identifiers... or error out */ @@ -1520,6 +1547,7 @@ static int yylex_SKIP_TO_ENDC(void) int yylex(void) { +restart: if (lexerState->atLineStart /* Newlines read within an expansion should not increase the line count */ && (!lexerState->expansions || lexerState->expansions->distance)) { @@ -1536,8 +1564,17 @@ int yylex(void) int token = lexerModeFuncs[lexerState->mode](); /* Make sure to terminate files with a line feed */ - if (token == 0 && lexerState->lastToken != '\n') - token = '\n'; + if (token == 0) { + if (lexerState->lastToken != '\n') { + token = '\n'; + } else { /* Try to switch to new buffer; if it succeeds, scan again */ + /* Captures end at their buffer's boundary no matter what */ + if (!lexerState->capturing) { + if (!yywrap()) + goto restart; + } + } + } lexerState->lastToken = token; lexerState->atLineStart = false; @@ -1547,16 +1584,18 @@ int yylex(void) return token; } -void lexer_CaptureBlock(int blockStartToken, int blockEndToken, char const **capture, size_t *size, +void lexer_CaptureBlock(int blockStartToken, int blockEndToken, char **capture, size_t *size, char const *name) { + assert(!lexerState->expansions); + lexerState->capturing = true; lexerState->captureSize = 0; unsigned int level = 0; char *captureStart; if (lexerState->isMmapped) { - captureStart = lexerState->ptr; + captureStart = &lexerState->ptr[lexerState->offset]; } else { lexerState->captureCapacity = 128; /* The initial size will be twice that */ reallocCaptureBuf(); diff --git a/src/asm/macro.c b/src/asm/macro.c index 71364943..ed0bdc64 100644 --- a/src/asm/macro.c +++ b/src/asm/macro.c @@ -29,7 +29,8 @@ struct MacroArgs { sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs)) static struct MacroArgs *macroArgs = NULL; -static uint32_t uniqueID = -1; +static uint32_t uniqueID = 0; +static uint32_t maxUniqueID = 0; /* * The initialization is somewhat harmful, since it is never used, but it * guarantees the size of the buffer will be correct. I was unable to find a @@ -107,15 +108,23 @@ char const *macro_GetUniqueIDStr(void) void macro_SetUniqueID(uint32_t id) { uniqueID = id; - if (id == -1) { + if (id == 0) { uniqueIDPtr = NULL; } else { + if (uniqueID > maxUniqueID) + maxUniqueID = uniqueID; /* The buffer is guaranteed to be the correct size */ sprintf(uniqueIDBuf, "_%" PRIu32, id); uniqueIDPtr = uniqueIDBuf; } } +uint32_t macro_UseNewUniqueID(void) +{ + macro_SetUniqueID(++maxUniqueID); + return maxUniqueID; +} + void macro_ShiftCurrentArgs(void) { if (macroArgs->shift != macroArgs->nbArgs) diff --git a/src/asm/main.c b/src/asm/main.c index 9c559e7a..4f264d36 100644 --- a/src/asm/main.c +++ b/src/asm/main.c @@ -23,8 +23,10 @@ #include "asm/lexer.h" #include "asm/main.h" #include "asm/output.h" +#include "asm/rpn.h" #include "asm/symbol.h" #include "asm/warning.h" +#include "asmy.h" #include "extern/err.h" #include "extern/getopt.h" @@ -32,8 +34,6 @@ #include "helpers.h" #include "version.h" -extern int yyparse(void); - size_t cldefines_index; size_t cldefines_numindices; size_t cldefines_bufsize; @@ -307,11 +307,11 @@ int main(int argc, char *argv[]) yydebug = 1; #endif - nMaxRecursionDepth = 64; oGeneratePhonyDeps = false; oGeneratedMissingIncludes = false; oFailedOnMissingInclude = false; tzTargetFileName = NULL; + uint32_t maxRecursionDepth = 64; size_t nTargetFileNameLen = 0; DefaultOptions.gbgfx[0] = '0'; @@ -390,7 +390,7 @@ int main(int argc, char *argv[]) break; case 'r': - nMaxRecursionDepth = strtoul(optarg, &ep, 0); + maxRecursionDepth = strtoul(optarg, &ep, 0); if (optarg[0] == '\0' || *ep != '\0') errx(1, "Invalid argument for option 'r'"); @@ -483,13 +483,9 @@ int main(int argc, char *argv[]) fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile); } - /* Init lexer; important to do first, since that's what provides the file name, line, etc */ - struct LexerState *state = lexer_OpenFile(tzMainfile); - - if (!state) - fatalerror("Failed to open main file!\n"); + /* Init file stack; important to do first, since it provides the file name, line, etc */ lexer_Init(); - lexer_SetState(state); + fstk_Init(tzMainfile, maxRecursionDepth); nStartClock = clock(); @@ -497,7 +493,6 @@ int main(int argc, char *argv[]) nIFDepth = 0; sym_Init(); sym_SetExportAll(exportall); - fstk_Init(tzMainfile); opt_ParseDefines(); charmap_New("main", NULL); diff --git a/src/asm/output.c b/src/asm/output.c index 45f2f1e1..b8919edd 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -33,7 +33,7 @@ #include "platform.h" // strdup struct Patch { - char tzFilename[_MAX_PATH + 1]; + char *tzFilename; uint32_t nOffset; struct Section *pcSection; uint32_t pcOffset; @@ -318,7 +318,7 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno)); patch->type = type; - fstk_DumpToStr(patch->tzFilename, sizeof(patch->tzFilename)); + patch->tzFilename = fstk_DumpToStr(); patch->nOffset = ofs; patch->pcSection = sect_GetSymbolSection(); patch->pcOffset = sect_GetSymbolOffset(); diff --git a/src/asm/symbol.c b/src/asm/symbol.c index 8253ffac..bd790fe9 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -477,7 +477,7 @@ void sym_Export(char const *symName) /* * Add a macro definition */ -struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char const *body, size_t size) +struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size) { struct Symbol *sym = createNonrelocSymbol(symName); diff --git a/test/asm/file-sym.asm b/test/asm/file-sym.asm new file mode 100644 index 00000000..25782859 --- /dev/null +++ b/test/asm/file-sym.asm @@ -0,0 +1 @@ +PRINTT "{__FILE__}\n" diff --git a/test/asm/file-sym.err b/test/asm/file-sym.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/file-sym.out b/test/asm/file-sym.out new file mode 100644 index 00000000..61fc13a2 --- /dev/null +++ b/test/asm/file-sym.out @@ -0,0 +1 @@ +"test/asm/file-sym.asm"