mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Use std::shared_ptr<std::string> for lexed/parsed strings
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#define RGBDS_ASM_LEXER_H
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
@@ -31,13 +32,10 @@ enum LexerMode {
|
||||
|
||||
struct Expansion {
|
||||
std::optional<std::string> name;
|
||||
union {
|
||||
char const *unowned;
|
||||
char *owned; // Non-`const` only so it can be `delete []`d
|
||||
} contents;
|
||||
size_t size; // Length of the contents
|
||||
std::shared_ptr<std::string> contents;
|
||||
size_t offset; // Cursor into the contents
|
||||
bool owned; // Whether or not to free contents when this expansion is freed
|
||||
|
||||
size_t size() const { return contents->size(); }
|
||||
};
|
||||
|
||||
struct IfStackEntry {
|
||||
@@ -140,10 +138,6 @@ struct CaptureBody {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct String {
|
||||
char string[MAXSTRLEN + 1];
|
||||
};
|
||||
|
||||
void lexer_CheckRecursionDepth();
|
||||
uint32_t lexer_GetLineNo();
|
||||
uint32_t lexer_GetColNo();
|
||||
|
||||
@@ -3,26 +3,22 @@
|
||||
#ifndef RGBDS_MACRO_H
|
||||
#define RGBDS_MACRO_H
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
struct MacroArgs {
|
||||
unsigned int shift;
|
||||
std::vector<std::string> args;
|
||||
std::vector<std::shared_ptr<std::string>> args;
|
||||
|
||||
void append(std::string s);
|
||||
void append(std::shared_ptr<std::string> arg);
|
||||
};
|
||||
|
||||
MacroArgs *macro_GetCurrentArgs();
|
||||
void macro_UseNewArgs(MacroArgs *args);
|
||||
char const *macro_GetArg(uint32_t i);
|
||||
char const *macro_GetAllArgs();
|
||||
std::shared_ptr<std::string> macro_GetArg(uint32_t i);
|
||||
std::shared_ptr<std::string> macro_GetAllArgs();
|
||||
|
||||
void macro_ShiftCurrentArgs(int32_t count);
|
||||
uint32_t macro_NbArgs();
|
||||
|
||||
@@ -36,10 +36,10 @@ struct Symbol {
|
||||
uint32_t fileLine; // Line where the symbol was defined
|
||||
|
||||
std::variant<
|
||||
int32_t, // If isNumeric()
|
||||
int32_t (*)(), // If isNumeric() and has a callback
|
||||
std::string_view *, // For SYM_MACRO
|
||||
std::string * // For SYM_EQUS
|
||||
int32_t, // If isNumeric()
|
||||
int32_t (*)(), // If isNumeric() and has a callback
|
||||
std::string_view *, // For SYM_MACRO
|
||||
std::shared_ptr<std::string> // For SYM_EQUS
|
||||
>
|
||||
data;
|
||||
|
||||
@@ -62,7 +62,7 @@ struct Symbol {
|
||||
int32_t getValue() const;
|
||||
int32_t getOutputValue() const;
|
||||
std::string_view *getMacro() const;
|
||||
std::string *getEqus() const;
|
||||
std::shared_ptr<std::string> getEqus() const;
|
||||
uint32_t getConstantValue() const;
|
||||
};
|
||||
|
||||
@@ -90,8 +90,8 @@ Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
||||
Symbol const *sym_GetPC();
|
||||
Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, char const *body, size_t size);
|
||||
Symbol *sym_Ref(std::string const &symName);
|
||||
Symbol *sym_AddString(std::string const &symName, char const *value);
|
||||
Symbol *sym_RedefString(std::string const &symName, char const *value);
|
||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||
void sym_Purge(std::string const &symName);
|
||||
void sym_Init(time_t now);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -95,13 +96,13 @@ static void mapFile(void *&mappingAddr, int fd, std::string const &path, size_t
|
||||
|
||||
struct Token {
|
||||
int type;
|
||||
std::variant<std::monostate, uint32_t, String, std::string> value;
|
||||
std::variant<std::monostate, uint32_t, std::string> value;
|
||||
|
||||
Token() : type(T_(NUMBER)), value(std::monostate{}) {}
|
||||
Token(int type_) : type(type_), value(std::monostate{}) {}
|
||||
Token(int type_, uint32_t value_) : type(type_), value(value_) {}
|
||||
Token(int type_, String &value_) : type(type_), value(value_) {}
|
||||
Token(int type_, std::string &value_) : type(type_), value(value_) {}
|
||||
Token(int type_, std::string const &value_) : type(type_), value(value_) {}
|
||||
Token(int type_, std::string &&value_) : type(type_), value(value_) {}
|
||||
};
|
||||
|
||||
struct CaseInsensitive {
|
||||
@@ -490,23 +491,15 @@ void lexer_ToggleStringExpansion(bool enable) {
|
||||
|
||||
// Functions for the actual lexer to obtain characters
|
||||
|
||||
static void beginExpansion(char const *str, bool owned, char const *name) {
|
||||
size_t size = strlen(str);
|
||||
|
||||
// Do not expand empty strings
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
static void beginExpansion(std::shared_ptr<std::string> str, std::optional<std::string> name) {
|
||||
if (name)
|
||||
lexer_CheckRecursionDepth();
|
||||
|
||||
lexerState->expansions.push_front({
|
||||
.name = name ? std::optional<std::string>(name) : std::nullopt,
|
||||
.contents = {.unowned = str},
|
||||
.size = size,
|
||||
.offset = 0,
|
||||
.owned = owned,
|
||||
});
|
||||
// Do not expand empty strings
|
||||
if (str->empty())
|
||||
return;
|
||||
|
||||
lexerState->expansions.push_front({.name = name, .contents = str, .offset = 0});
|
||||
}
|
||||
|
||||
void lexer_CheckRecursionDepth() {
|
||||
@@ -514,11 +507,6 @@ void lexer_CheckRecursionDepth() {
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
}
|
||||
|
||||
static void freeExpansion(Expansion &expansion) {
|
||||
if (expansion.owned)
|
||||
delete[] expansion.contents.owned;
|
||||
}
|
||||
|
||||
static bool isMacroChar(char c) {
|
||||
return c == '@' || c == '#' || c == '<' || (c >= '0' && c <= '9');
|
||||
}
|
||||
@@ -587,34 +575,42 @@ static uint32_t readBracketedMacroArgNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
static char const *readMacroArg(char name) {
|
||||
char const *str = nullptr;
|
||||
|
||||
static std::shared_ptr<std::string> readMacroArg(char name) {
|
||||
if (name == '@') {
|
||||
auto maybeStr = fstk_GetUniqueIDStr();
|
||||
str = maybeStr ? maybeStr->c_str() : nullptr;
|
||||
auto str = fstk_GetUniqueIDStr();
|
||||
if (!str) {
|
||||
error("'\\@' cannot be used outside of a macro or REPT/FOR block\n");
|
||||
}
|
||||
return str;
|
||||
} else if (name == '#') {
|
||||
str = macro_GetAllArgs();
|
||||
auto str = macro_GetAllArgs();
|
||||
if (!str) {
|
||||
error("'\\#' cannot be used outside of a macro");
|
||||
}
|
||||
return str;
|
||||
} else if (name == '<') {
|
||||
uint32_t num = readBracketedMacroArgNum();
|
||||
|
||||
if (num == 0)
|
||||
if (num == 0) {
|
||||
// The error was already reported by `readBracketedMacroArgNum`.
|
||||
return nullptr;
|
||||
str = macro_GetArg(num);
|
||||
if (!str)
|
||||
}
|
||||
|
||||
auto str = macro_GetArg(num);
|
||||
if (!str) {
|
||||
error("Macro argument '\\<%" PRIu32 ">' not defined\n", num);
|
||||
}
|
||||
return str;
|
||||
} else if (name == '0') {
|
||||
error("Invalid macro argument '\\0'\n");
|
||||
return nullptr;
|
||||
} else {
|
||||
assert(name > '0' && name <= '9');
|
||||
str = macro_GetArg(name - '0');
|
||||
auto str = macro_GetArg(name - '0');
|
||||
if (!str) {
|
||||
error("Macro argument '\\%c' not defined\n", name);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
if (!str)
|
||||
error("Macro argument '\\%c' not defined\n", name);
|
||||
return str;
|
||||
}
|
||||
|
||||
static size_t readInternal(BufferedLexerState &cbuf, size_t bufIndex, size_t nbChars) {
|
||||
@@ -632,12 +628,12 @@ static size_t readInternal(BufferedLexerState &cbuf, size_t bufIndex, size_t nbC
|
||||
// We only need one character of lookahead, for macro arguments
|
||||
static int peekInternal(uint8_t distance) {
|
||||
for (Expansion &exp : lexerState->expansions) {
|
||||
// An expansion that has reached its end will have `exp->offset` == `exp->size`,
|
||||
// An expansion that has reached its end will have `exp->offset` == `exp->size()`,
|
||||
// and `peekInternal` will continue with its parent
|
||||
assert(exp.offset <= exp.size);
|
||||
if (distance < exp.size - exp.offset)
|
||||
return exp.contents.unowned[exp.offset + distance];
|
||||
distance -= exp.size - exp.offset;
|
||||
assert(exp.offset <= exp.size());
|
||||
if (distance < exp.size() - exp.offset)
|
||||
return (*exp.contents)[exp.offset + distance];
|
||||
distance -= exp.size() - exp.offset;
|
||||
}
|
||||
|
||||
if (distance >= LEXER_BUF_SIZE)
|
||||
@@ -697,7 +693,7 @@ static int peekInternal(uint8_t distance) {
|
||||
|
||||
// forward declarations for peek
|
||||
static void shiftChar();
|
||||
static char const *readInterpolation(size_t depth);
|
||||
static std::shared_ptr<std::string> readInterpolation(size_t depth);
|
||||
|
||||
static int peek() {
|
||||
int c = peekInternal(0);
|
||||
@@ -714,30 +710,32 @@ static int peek() {
|
||||
if (isMacroChar(c)) {
|
||||
shiftChar();
|
||||
shiftChar();
|
||||
char const *str = readMacroArg(c);
|
||||
|
||||
// If the macro arg is invalid or an empty string, it cannot be
|
||||
// expanded, so skip it and keep peeking.
|
||||
if (!str || !str[0])
|
||||
std::shared_ptr<std::string> str = readMacroArg(c);
|
||||
// If the macro arg is invalid or an empty string, it cannot be expanded,
|
||||
// so skip it and keep peeking.
|
||||
if (!str || str->empty()) {
|
||||
return peek();
|
||||
}
|
||||
|
||||
beginExpansion(str, c == '#', nullptr);
|
||||
beginExpansion(str, std::nullopt);
|
||||
|
||||
// Assuming macro args can't be recursive (I'll be damned if a way
|
||||
// is found...), then we mark the entire macro arg as scanned.
|
||||
lexerState->macroArgScanDistance += strlen(str);
|
||||
lexerState->macroArgScanDistance += str->length();
|
||||
|
||||
c = str[0];
|
||||
c = str->front();
|
||||
} else {
|
||||
c = '\\';
|
||||
}
|
||||
} else if (c == '{' && !lexerState->disableInterpolation) {
|
||||
// If character is an open brace, do symbol interpolation
|
||||
shiftChar();
|
||||
char const *str = readInterpolation(0);
|
||||
|
||||
if (str && str[0])
|
||||
beginExpansion(str, false, str);
|
||||
if (auto str = readInterpolation(0); str) {
|
||||
beginExpansion(str, *str);
|
||||
}
|
||||
|
||||
return peek();
|
||||
}
|
||||
|
||||
@@ -758,12 +756,11 @@ restart:
|
||||
// Advance within the current expansion
|
||||
Expansion &expansion = lexerState->expansions.front();
|
||||
|
||||
assert(expansion.offset <= expansion.size);
|
||||
assert(expansion.offset <= expansion.size());
|
||||
expansion.offset++;
|
||||
if (expansion.offset > expansion.size) {
|
||||
// When advancing would go past an expansion's end, free it,
|
||||
// move up to its parent, and try again to advance
|
||||
freeExpansion(expansion);
|
||||
if (expansion.offset > expansion.size()) {
|
||||
// When advancing would go past an expansion's end,
|
||||
// move up to its parent and try again to advance
|
||||
lexerState->expansions.pop_front();
|
||||
goto restart;
|
||||
}
|
||||
@@ -1124,7 +1121,7 @@ static Token readIdentifier(char firstChar) {
|
||||
|
||||
// Functions to read strings
|
||||
|
||||
static char const *readInterpolation(size_t depth) {
|
||||
static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
||||
if (depth > maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
|
||||
@@ -1142,10 +1139,9 @@ static char const *readInterpolation(size_t depth) {
|
||||
|
||||
if (c == '{') { // Nested interpolation
|
||||
shiftChar();
|
||||
char const *str = readInterpolation(depth + 1);
|
||||
auto str = readInterpolation(depth + 1);
|
||||
|
||||
if (str && str[0])
|
||||
beginExpansion(str, false, str);
|
||||
beginExpansion(str, *str);
|
||||
continue; // Restart, reading from the new buffer
|
||||
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
|
||||
error("Missing }\n");
|
||||
@@ -1175,67 +1171,50 @@ static char const *readInterpolation(size_t depth) {
|
||||
if (!sym) {
|
||||
error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str());
|
||||
} else if (sym->type == SYM_EQUS) {
|
||||
static std::string buf;
|
||||
buf.clear();
|
||||
fmt.appendString(buf, *sym->getEqus());
|
||||
return buf.c_str();
|
||||
auto buf = std::make_shared<std::string>();
|
||||
fmt.appendString(*buf, *sym->getEqus());
|
||||
return buf;
|
||||
} else if (sym->isNumeric()) {
|
||||
static std::string buf;
|
||||
buf.clear();
|
||||
fmt.appendNumber(buf, sym->getConstantValue());
|
||||
return buf.c_str();
|
||||
auto buf = std::make_shared<std::string>();
|
||||
fmt.appendNumber(*buf, sym->getConstantValue());
|
||||
return buf;
|
||||
} else {
|
||||
error("Only numerical and string symbols can be interpolated\n");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define append_yylval_string(c) \
|
||||
do { \
|
||||
/* Evaluate c exactly once in case it has side effects */ \
|
||||
if (char v = (c); i < sizeof(yylval.string)) \
|
||||
yylval.string[i++] = v; \
|
||||
} while (0)
|
||||
|
||||
static size_t appendEscapedSubstring(String &yylval, char const *str, size_t i) {
|
||||
// Copy one extra to flag overflow
|
||||
while (*str) {
|
||||
char c = *str++;
|
||||
|
||||
static void appendEscapedSubstring(std::string &yylval, std::string const &str) {
|
||||
for (char c : str) {
|
||||
// Escape characters that need escaping
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
case '{':
|
||||
append_yylval_string('\\');
|
||||
yylval += '\\';
|
||||
// fallthrough
|
||||
default:
|
||||
yylval += c;
|
||||
break;
|
||||
case '\n':
|
||||
append_yylval_string('\\');
|
||||
c = 'n';
|
||||
yylval += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
append_yylval_string('\\');
|
||||
c = 'r';
|
||||
yylval += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
append_yylval_string('\\');
|
||||
c = 't';
|
||||
yylval += "\\t";
|
||||
break;
|
||||
}
|
||||
|
||||
append_yylval_string(c);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void readString(String &yylval, bool raw) {
|
||||
static std::string readString(bool raw) {
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
|
||||
size_t i = 0;
|
||||
std::string yylval;
|
||||
bool multiline = false;
|
||||
char const *str;
|
||||
|
||||
// We reach this function after reading a single quote, but we also support triple quotes
|
||||
if (peek() == '"') {
|
||||
@@ -1278,7 +1257,7 @@ static void readString(String &yylval, bool raw) {
|
||||
break;
|
||||
shiftChar();
|
||||
if (peek() != '"') {
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
break;
|
||||
}
|
||||
shiftChar();
|
||||
@@ -1332,10 +1311,8 @@ static void readString(String &yylval, bool raw) {
|
||||
case '9':
|
||||
case '<':
|
||||
shiftChar();
|
||||
str = readMacroArg(c);
|
||||
if (str) {
|
||||
while (*str)
|
||||
append_yylval_string(*str++);
|
||||
if (auto str = readMacroArg(c); str) {
|
||||
yylval.append(*str);
|
||||
}
|
||||
continue; // Do not copy an additional character
|
||||
|
||||
@@ -1357,10 +1334,8 @@ static void readString(String &yylval, bool raw) {
|
||||
// We'll be exiting the string scope, so re-enable expansions
|
||||
// (Not interpolations, since they're handled by the function itself...)
|
||||
lexerState->disableMacroArgs = false;
|
||||
str = readInterpolation(0);
|
||||
if (str) {
|
||||
while (*str)
|
||||
append_yylval_string(*str++);
|
||||
if (auto interpolation = readInterpolation(0); interpolation) {
|
||||
yylval.append(*interpolation);
|
||||
}
|
||||
lexerState->disableMacroArgs = true;
|
||||
continue; // Do not copy an additional character
|
||||
@@ -1368,35 +1343,35 @@ static void readString(String &yylval, bool raw) {
|
||||
// Regular characters will just get copied
|
||||
}
|
||||
|
||||
append_yylval_string(c);
|
||||
yylval += c;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (i == sizeof(yylval.string)) {
|
||||
i--;
|
||||
if (yylval.length() > MAXSTRLEN) {
|
||||
warning(WARNING_LONG_STR, "String constant too long\n");
|
||||
yylval.resize(MAXSTRLEN);
|
||||
}
|
||||
yylval.string[i] = '\0';
|
||||
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
|
||||
return yylval;
|
||||
}
|
||||
|
||||
static size_t appendStringLiteral(String &yylval, size_t i, bool raw) {
|
||||
static void appendStringLiteral(std::string &yylval, bool raw) {
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
|
||||
bool multiline = false;
|
||||
char const *str;
|
||||
|
||||
// We reach this function after reading a single quote, but we also support triple quotes
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
if (peek() == '"') {
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
shiftChar();
|
||||
if (peek() == '"') {
|
||||
// """ begins a multi-line string
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
shiftChar();
|
||||
multiline = true;
|
||||
} else {
|
||||
@@ -1431,14 +1406,14 @@ static size_t appendStringLiteral(String &yylval, size_t i, bool raw) {
|
||||
// Only """ ends a multi-line string
|
||||
if (peek() != '"')
|
||||
break;
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
shiftChar();
|
||||
if (peek() != '"')
|
||||
break;
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
shiftChar();
|
||||
}
|
||||
append_yylval_string('"');
|
||||
yylval += '"';
|
||||
goto finish;
|
||||
|
||||
case '\\': // Character escape or macro arg
|
||||
@@ -1455,7 +1430,7 @@ static size_t appendStringLiteral(String &yylval, size_t i, bool raw) {
|
||||
case 'r':
|
||||
case 't':
|
||||
// Return that character unchanged
|
||||
append_yylval_string('\\');
|
||||
yylval += '\\';
|
||||
shiftChar();
|
||||
break;
|
||||
|
||||
@@ -1479,12 +1454,14 @@ static size_t appendStringLiteral(String &yylval, size_t i, bool raw) {
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '<':
|
||||
case '<': {
|
||||
shiftChar();
|
||||
str = readMacroArg(c);
|
||||
if (str && str[0])
|
||||
i = appendEscapedSubstring(yylval, str, i);
|
||||
auto str = readMacroArg(c);
|
||||
if (str) {
|
||||
appendEscapedSubstring(yylval, *str);
|
||||
}
|
||||
continue; // Do not copy an additional character
|
||||
}
|
||||
|
||||
case EOF: // Can't really print that one
|
||||
error("Illegal character escape at end of input\n");
|
||||
@@ -1504,29 +1481,27 @@ static size_t appendStringLiteral(String &yylval, size_t i, bool raw) {
|
||||
// We'll be exiting the string scope, so re-enable expansions
|
||||
// (Not interpolations, since they're handled by the function itself...)
|
||||
lexerState->disableMacroArgs = false;
|
||||
str = readInterpolation(0);
|
||||
if (str && str[0])
|
||||
i = appendEscapedSubstring(yylval, str, i);
|
||||
auto str = readInterpolation(0);
|
||||
if (str) {
|
||||
appendEscapedSubstring(yylval, *str);
|
||||
}
|
||||
lexerState->disableMacroArgs = true;
|
||||
continue; // Do not copy an additional character
|
||||
|
||||
// Regular characters will just get copied
|
||||
}
|
||||
|
||||
append_yylval_string(c);
|
||||
yylval += c;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (i == sizeof(yylval.string)) {
|
||||
i--;
|
||||
if (yylval.length() > MAXSTRLEN) {
|
||||
warning(WARNING_LONG_STR, "String constant too long\n");
|
||||
yylval.resize(MAXSTRLEN);
|
||||
}
|
||||
yylval.string[i] = '\0';
|
||||
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// Lexer core
|
||||
@@ -1749,11 +1724,8 @@ static Token yylex_NORMAL() {
|
||||
|
||||
// Handle strings
|
||||
|
||||
case '"': {
|
||||
String yylval;
|
||||
readString(yylval, false);
|
||||
return Token(T_(STRING), yylval);
|
||||
}
|
||||
case '"':
|
||||
return Token(T_(STRING), readString(false));
|
||||
|
||||
// Handle newlines and EOF
|
||||
|
||||
@@ -1779,9 +1751,7 @@ static Token yylex_NORMAL() {
|
||||
case '#':
|
||||
if (peek() == '"') {
|
||||
shiftChar();
|
||||
String yylval;
|
||||
readString(yylval, true);
|
||||
return Token(T_(STRING), yylval);
|
||||
return Token(T_(STRING), readString(true));
|
||||
}
|
||||
// fallthrough
|
||||
|
||||
@@ -1809,11 +1779,10 @@ static Token yylex_NORMAL() {
|
||||
Symbol const *sym = sym_FindExactSymbol(std::get<std::string>(token.value));
|
||||
|
||||
if (sym && sym->type == SYM_EQUS) {
|
||||
char const *str = sym->getEqus()->c_str();
|
||||
std::shared_ptr<std::string> str = sym->getEqus();
|
||||
|
||||
assert(str);
|
||||
if (str[0])
|
||||
beginExpansion(str, false, sym->name.c_str());
|
||||
beginExpansion(str, sym->name);
|
||||
continue; // Restart, reading from the new buffer
|
||||
}
|
||||
}
|
||||
@@ -1836,9 +1805,8 @@ static Token yylex_NORMAL() {
|
||||
|
||||
static Token yylex_RAW() {
|
||||
// This is essentially a modified `appendStringLiteral`
|
||||
String yylval;
|
||||
std::string yylval;
|
||||
size_t parenDepth = 0;
|
||||
size_t i = 0;
|
||||
int c;
|
||||
|
||||
// Trim left whitespace (stops at a block comment)
|
||||
@@ -1865,15 +1833,15 @@ static Token yylex_RAW() {
|
||||
switch (c) {
|
||||
case '"': // String literals inside macro args
|
||||
shiftChar();
|
||||
i = appendStringLiteral(yylval, i, false);
|
||||
appendStringLiteral(yylval, false);
|
||||
break;
|
||||
|
||||
case '#': // Raw string literals inside macro args
|
||||
append_yylval_string(c);
|
||||
yylval += c;
|
||||
shiftChar();
|
||||
if (peek() == '"') {
|
||||
shiftChar();
|
||||
i = appendStringLiteral(yylval, i, true);
|
||||
appendStringLiteral(yylval, true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1893,7 +1861,7 @@ static Token yylex_RAW() {
|
||||
discardBlockComment();
|
||||
continue;
|
||||
}
|
||||
append_yylval_string(c); // Append the slash
|
||||
yylval += c; // Append the slash
|
||||
break;
|
||||
|
||||
case ',': // End of macro arg
|
||||
@@ -1958,21 +1926,20 @@ backslash:
|
||||
|
||||
default: // Regular characters will just get copied
|
||||
append:
|
||||
append_yylval_string(c);
|
||||
yylval += c;
|
||||
shiftChar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (i == sizeof(yylval.string)) {
|
||||
i--;
|
||||
if (yylval.length() > MAXSTRLEN) {
|
||||
warning(WARNING_LONG_STR, "Macro argument too long\n");
|
||||
yylval.resize(MAXSTRLEN);
|
||||
}
|
||||
// Trim right whitespace
|
||||
while (i && isWhitespace(yylval.string[i - 1]))
|
||||
i--;
|
||||
yylval.string[i] = '\0';
|
||||
auto rightPos = std::find_if_not(yylval.rbegin(), yylval.rend(), isWhitespace);
|
||||
yylval.resize(rightPos.base() - yylval.begin());
|
||||
|
||||
// Returning COMMAs to the parser would mean that two consecutive commas
|
||||
// (i.e. an empty argument) need to return two different tokens (STRING
|
||||
@@ -1989,7 +1956,7 @@ finish:
|
||||
// an empty raw string before it). This will not be treated as a
|
||||
// macro argument. To pass an empty last argument, use a second
|
||||
// trailing comma.
|
||||
if (i > 0)
|
||||
if (!yylval.empty())
|
||||
return Token(T_(STRING), yylval);
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
|
||||
@@ -2002,8 +1969,6 @@ finish:
|
||||
return Token(T_(YYEOF));
|
||||
}
|
||||
|
||||
#undef append_yylval_string
|
||||
|
||||
// This function uses the fact that `if`, etc. constructs are only valid when
|
||||
// there's nothing before them on their lines. This enables filtering
|
||||
// "meaningful" (= at line start) vs. "meaningless" (everything else) tokens.
|
||||
@@ -2213,8 +2178,6 @@ yy::parser::symbol_type yylex() {
|
||||
|
||||
if (auto *numValue = std::get_if<uint32_t>(&token.value); numValue) {
|
||||
return yy::parser::symbol_type(token.type, *numValue);
|
||||
} else if (auto *stringValue = std::get_if<String>(&token.value); stringValue) {
|
||||
return yy::parser::symbol_type(token.type, *stringValue);
|
||||
} else if (auto *strValue = std::get_if<std::string>(&token.value); strValue) {
|
||||
return yy::parser::symbol_type(token.type, *strValue);
|
||||
} else {
|
||||
|
||||
@@ -2,22 +2,24 @@
|
||||
|
||||
#include "asm/macro.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#define MAXMACROARGS 99999
|
||||
|
||||
static MacroArgs *macroArgs = nullptr;
|
||||
|
||||
void MacroArgs::append(std::string s) {
|
||||
if (s.empty())
|
||||
void MacroArgs::append(std::shared_ptr<std::string> arg) {
|
||||
if (arg->empty())
|
||||
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
||||
if (args.size() == MAXMACROARGS)
|
||||
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
|
||||
args.push_back(s);
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
MacroArgs *macro_GetCurrentArgs() {
|
||||
@@ -28,47 +30,41 @@ void macro_UseNewArgs(MacroArgs *args) {
|
||||
macroArgs = args;
|
||||
}
|
||||
|
||||
char const *macro_GetArg(uint32_t i) {
|
||||
std::shared_ptr<std::string> macro_GetArg(uint32_t i) {
|
||||
if (!macroArgs)
|
||||
return nullptr;
|
||||
|
||||
uint32_t realIndex = i + macroArgs->shift - 1;
|
||||
|
||||
return realIndex >= macroArgs->args.size() ? nullptr : macroArgs->args[realIndex].c_str();
|
||||
return realIndex >= macroArgs->args.size() ? nullptr : macroArgs->args[realIndex];
|
||||
}
|
||||
|
||||
char const *macro_GetAllArgs() {
|
||||
std::shared_ptr<std::string> macro_GetAllArgs() {
|
||||
if (!macroArgs)
|
||||
return nullptr;
|
||||
|
||||
size_t nbArgs = macroArgs->args.size();
|
||||
|
||||
if (macroArgs->shift >= nbArgs)
|
||||
return "";
|
||||
return std::make_shared<std::string>("");
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
for (uint32_t i = macroArgs->shift; i < nbArgs; i++)
|
||||
len += macroArgs->args[i].length() + 1; // 1 for comma
|
||||
len += macroArgs->args[i]->length() + 1; // 1 for comma
|
||||
|
||||
char *str = new (std::nothrow) char[len + 1]; // 1 for '\0'
|
||||
char *ptr = str;
|
||||
|
||||
if (!str)
|
||||
fatalerror("Failed to allocate memory for expanding '\\#': %s\n", strerror(errno));
|
||||
auto str = std::make_shared<std::string>();
|
||||
str->reserve(len + 1); // 1 for comma
|
||||
|
||||
for (uint32_t i = macroArgs->shift; i < nbArgs; i++) {
|
||||
std::string const &arg = macroArgs->args[i];
|
||||
size_t n = arg.length();
|
||||
auto const &arg = macroArgs->args[i];
|
||||
|
||||
memcpy(ptr, arg.c_str(), n);
|
||||
ptr += n;
|
||||
str->append(*arg);
|
||||
|
||||
// Commas go between args and after a last empty arg
|
||||
if (i < nbArgs - 1 || n == 0)
|
||||
*ptr++ = ','; // no space after comma
|
||||
if (i < nbArgs - 1 || arg->empty())
|
||||
str->push_back(','); // no space after comma
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,8 @@
|
||||
|
||||
#include "asm/main.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
@@ -19,13 +14,9 @@
|
||||
#include "version.hpp"
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/opt.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
@@ -186,9 +177,9 @@ int main(int argc, char *argv[]) {
|
||||
equals = strchr(musl_optarg, '=');
|
||||
if (equals) {
|
||||
*equals = '\0';
|
||||
sym_AddString(musl_optarg, equals + 1);
|
||||
sym_AddString(musl_optarg, std::make_shared<std::string>(equals + 1));
|
||||
} else {
|
||||
sym_AddString(musl_optarg, "1");
|
||||
sym_AddString(musl_optarg, std::make_shared<std::string>("1"));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -6,16 +6,13 @@
|
||||
|
||||
%code requires {
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/section.hpp"
|
||||
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
@@ -48,6 +45,7 @@
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -56,12 +54,14 @@
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/opt.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/section.hpp"
|
||||
#include "util.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "extern/utf8decoder.hpp"
|
||||
@@ -129,7 +129,7 @@
|
||||
%type <SectionSpec> sect_attrs
|
||||
|
||||
%token <int32_t> NUMBER "number"
|
||||
%token <String> STRING "string"
|
||||
%token <std::string> STRING "string"
|
||||
|
||||
%token PERIOD "."
|
||||
%token COMMA ","
|
||||
@@ -524,7 +524,7 @@ macro_args:
|
||||
}
|
||||
| macro_args STRING {
|
||||
$$ = $1;
|
||||
$$->append($2.string);
|
||||
$$->append(std::make_shared<std::string>($2));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -663,7 +663,7 @@ equs:
|
||||
$1.c_str(),
|
||||
$1.c_str()
|
||||
);
|
||||
sym_AddString($1, $3.c_str());
|
||||
sym_AddString($1, std::make_shared<std::string>($3));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -757,7 +757,7 @@ opt_list:
|
||||
|
||||
opt_list_entry:
|
||||
STRING {
|
||||
opt_Parse($1.string);
|
||||
opt_Parse($1.c_str());
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1077,13 +1077,13 @@ def_rl:
|
||||
|
||||
def_equs:
|
||||
def_id POP_EQUS string {
|
||||
sym_AddString($1, $3.c_str());
|
||||
sym_AddString($1, std::make_shared<std::string>($3));
|
||||
}
|
||||
;
|
||||
|
||||
redef_equs:
|
||||
redef_id POP_EQUS string {
|
||||
sym_RedefString($1, $3.c_str());
|
||||
sym_RedefString($1, std::make_shared<std::string>($3));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1553,7 +1553,7 @@ opt_q_arg:
|
||||
|
||||
string:
|
||||
STRING {
|
||||
$$ = $1.string;
|
||||
$$ = std::move($1);
|
||||
}
|
||||
| OP_STRSUB LPAREN string COMMA const COMMA uconst RPAREN {
|
||||
size_t len = strlenUTF8($3);
|
||||
@@ -2655,19 +2655,16 @@ static std::string strfmt(
|
||||
) {
|
||||
std::string str;
|
||||
size_t argIndex = 0;
|
||||
char const *ptr = spec.c_str();
|
||||
|
||||
while (str.length() <= MAXSTRLEN) {
|
||||
int c = *ptr++;
|
||||
for (size_t i = 0; spec[i] != '\0' && str.length() <= MAXSTRLEN; ++i) {
|
||||
int c = spec[i];
|
||||
|
||||
if (c == '\0') {
|
||||
break;
|
||||
} else if (c != '%') {
|
||||
if (c != '%') {
|
||||
str += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = *ptr++;
|
||||
c = spec[++i];
|
||||
|
||||
if (c == '%') {
|
||||
str += c;
|
||||
@@ -2680,7 +2677,7 @@ static std::string strfmt(
|
||||
fmt.useCharacter(c);
|
||||
if (fmt.isFinished())
|
||||
break;
|
||||
c = *ptr++;
|
||||
c = spec[++i];
|
||||
}
|
||||
|
||||
if (fmt.isEmpty()) {
|
||||
|
||||
@@ -79,9 +79,9 @@ std::string_view *Symbol::getMacro() const {
|
||||
return std::get<std::string_view *>(data);
|
||||
}
|
||||
|
||||
std::string *Symbol::getEqus() const {
|
||||
assert(std::holds_alternative<std::string *>(data));
|
||||
return std::get<std::string *>(data);
|
||||
std::shared_ptr<std::string> Symbol::getEqus() const {
|
||||
assert(std::holds_alternative<std::shared_ptr<std::string>>(data));
|
||||
return std::get<std::shared_ptr<std::string>>(data);
|
||||
}
|
||||
|
||||
static void dumpFilename(Symbol const &sym) {
|
||||
@@ -121,14 +121,6 @@ static Symbol &createSymbol(std::string const &symName) {
|
||||
return sym;
|
||||
}
|
||||
|
||||
static void assignStringSymbol(Symbol &sym, char const *value) {
|
||||
std::string *equs = new (std::nothrow) std::string(value);
|
||||
if (!equs)
|
||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||
sym.type = SYM_EQUS;
|
||||
sym.data = equs;
|
||||
}
|
||||
|
||||
Symbol *sym_FindExactSymbol(std::string const &symName) {
|
||||
auto search = symbols.find(symName);
|
||||
return search != symbols.end() ? &search->second : nullptr;
|
||||
@@ -310,21 +302,22 @@ Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
||||
* of the string are enough: sym_AddString("M_PI"s, "3.1415"). This is the same
|
||||
* as ``` M_PI EQUS "3.1415" ```
|
||||
*/
|
||||
Symbol *sym_AddString(std::string const &symName, char const *value) {
|
||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||
|
||||
if (!sym)
|
||||
return nullptr;
|
||||
|
||||
assignStringSymbol(*sym, value);
|
||||
sym->type = SYM_EQUS;
|
||||
sym->data = str;
|
||||
return sym;
|
||||
}
|
||||
|
||||
Symbol *sym_RedefString(std::string const &symName, char const *value) {
|
||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (!sym)
|
||||
return sym_AddString(symName, value);
|
||||
return sym_AddString(symName, str);
|
||||
|
||||
if (sym->type != SYM_EQUS) {
|
||||
if (sym->isDefined())
|
||||
@@ -339,9 +332,7 @@ Symbol *sym_RedefString(std::string const &symName, char const *value) {
|
||||
}
|
||||
|
||||
updateSymbolFilename(*sym);
|
||||
// FIXME: this leaks the previous `sym->getEqus()`, but this can't delete it because the
|
||||
// expansion may be redefining itself.
|
||||
assignStringSymbol(*sym, value);
|
||||
sym->data = str;
|
||||
|
||||
return sym;
|
||||
}
|
||||
@@ -564,7 +555,8 @@ void sym_Init(time_t now) {
|
||||
_RSSymbol = sym_AddVar("_RS"s, 0);
|
||||
_RSSymbol->isBuiltin = true;
|
||||
|
||||
sym_AddString("__RGBDS_VERSION__"s, get_package_version_string())->isBuiltin = true;
|
||||
sym_AddString("__RGBDS_VERSION__"s, std::make_shared<std::string>(get_package_version_string()))
|
||||
->isBuiltin = true;
|
||||
sym_AddEqu("__RGBDS_MAJOR__"s, PACKAGE_VERSION_MAJOR)->isBuiltin = true;
|
||||
sym_AddEqu("__RGBDS_MINOR__"s, PACKAGE_VERSION_MINOR)->isBuiltin = true;
|
||||
sym_AddEqu("__RGBDS_PATCH__"s, PACKAGE_VERSION_PATCH)->isBuiltin = true;
|
||||
@@ -598,10 +590,16 @@ void sym_Init(time_t now) {
|
||||
time_utc
|
||||
);
|
||||
|
||||
sym_AddString("__TIME__"s, savedTIME)->isBuiltin = true;
|
||||
sym_AddString("__DATE__"s, savedDATE)->isBuiltin = true;
|
||||
sym_AddString("__ISO_8601_LOCAL__"s, savedTIMESTAMP_ISO8601_LOCAL)->isBuiltin = true;
|
||||
sym_AddString("__ISO_8601_UTC__"s, savedTIMESTAMP_ISO8601_UTC)->isBuiltin = true;
|
||||
sym_AddString("__TIME__"s, std::make_shared<std::string>(savedTIME))->isBuiltin = true;
|
||||
sym_AddString("__DATE__"s, std::make_shared<std::string>(savedDATE))->isBuiltin = true;
|
||||
sym_AddString(
|
||||
"__ISO_8601_LOCAL__"s,
|
||||
std::make_shared<std::string>(savedTIMESTAMP_ISO8601_LOCAL)
|
||||
)->isBuiltin = true;
|
||||
sym_AddString(
|
||||
"__ISO_8601_UTC__"s,
|
||||
std::make_shared<std::string>(savedTIMESTAMP_ISO8601_UTC)
|
||||
)->isBuiltin = true;
|
||||
|
||||
sym_AddEqu("__UTC_YEAR__"s, time_utc->tm_year + 1900)->isBuiltin = true;
|
||||
sym_AddEqu("__UTC_MONTH__"s, time_utc->tm_mon + 1)->isBuiltin = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser]
|
||||
_u4!
|
||||
while expanding symbol "warn_unique"
|
||||
error: unique-id.asm(15):
|
||||
Macro argument '\@' not defined
|
||||
'\@' cannot be used outside of a macro or REPT/FOR block
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(15): [-Wuser]
|
||||
!
|
||||
|
||||
Reference in New Issue
Block a user