diff --git a/include/asm/actions.hpp b/include/asm/actions.hpp index a429f319..17470997 100644 --- a/include/asm/actions.hpp +++ b/include/asm/actions.hpp @@ -13,30 +13,46 @@ #include "asm/output.hpp" // AssertionType #include "asm/rpn.hpp" // RPNCommand +struct AlignmentSpec { + uint8_t alignment; + uint16_t alignOfs; +}; + +void act_If(int32_t condition); +void act_Elif(int32_t condition); +void act_Else(); +void act_Endc(); + +AlignmentSpec act_Alignment(int32_t alignment, int32_t alignOfs); + +void act_Assert(AssertionType type, Expression const &expr, std::string const &message); +void act_StaticAssert(AssertionType type, int32_t condition, std::string const &message); + std::optional act_ReadFile(std::string const &name, uint32_t maxLen); +uint32_t act_CharToNum(std::string const &str); uint32_t act_StringToNum(std::vector const &str); +int32_t act_CharVal(std::string const &str, int32_t negIdx); +uint8_t act_StringByte(std::string const &str, int32_t negIdx); + size_t act_StringLen(std::string const &str, bool printErrors); -std::string act_StringSlice(std::string const &str, uint32_t start, uint32_t stop); -std::string act_StringSub(std::string const &str, uint32_t pos, uint32_t len); +std::string + act_StringSlice(std::string const &str, int32_t negStart, std::optional negStop); +std::string act_StringSub(std::string const &str, int32_t negPos, std::optional optLen); size_t act_CharLen(std::string const &str); -std::string act_StringChar(std::string const &str, uint32_t idx); -std::string act_CharSub(std::string const &str, uint32_t pos); +std::string act_StringChar(std::string const &str, int32_t negIdx); +std::string act_CharSub(std::string const &str, int32_t negPos); int32_t act_CharCmp(std::string_view str1, std::string_view str2); -uint32_t act_AdjustNegativeIndex(int32_t idx, size_t len, char const *functionName); -uint32_t act_AdjustNegativePos(int32_t pos, size_t len, char const *functionName); - std::string act_StringReplace(std::string_view str, std::string const &old, std::string const &rep); std::string act_StringFormat( std::string const &spec, std::vector> const &args ); +std::string act_SectionName(std::string const &symName); + void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue); -void act_FailAssert(AssertionType type); -void act_FailAssertMsg(AssertionType type, std::string const &message); - #endif // RGBDS_ASM_ACTIONS_HPP diff --git a/src/asm/actions.cpp b/src/asm/actions.cpp index b67871fb..e6119b77 100644 --- a/src/asm/actions.cpp +++ b/src/asm/actions.cpp @@ -15,6 +15,107 @@ #include "asm/symbol.hpp" #include "asm/warning.hpp" +void act_If(int32_t condition) { + lexer_IncIFDepth(); + + if (condition) { + lexer_RunIFBlock(); + } else { + lexer_SetMode(LEXER_SKIP_TO_ELIF); + } +} + +void act_Elif(int32_t condition) { + if (lexer_GetIFDepth() == 0) { + fatal("Found ELIF outside of an IF construct"); + } + if (lexer_RanIFBlock()) { + if (lexer_ReachedELSEBlock()) { + fatal("Found ELIF after an ELSE block"); + } + lexer_SetMode(LEXER_SKIP_TO_ENDC); + } else if (condition) { + lexer_RunIFBlock(); + } else { + lexer_SetMode(LEXER_SKIP_TO_ELIF); + } +} + +void act_Else() { + if (lexer_GetIFDepth() == 0) { + fatal("Found ELSE outside of an IF construct"); + } + if (lexer_RanIFBlock()) { + if (lexer_ReachedELSEBlock()) { + fatal("Found ELSE after an ELSE block"); + } + lexer_SetMode(LEXER_SKIP_TO_ENDC); + } else { + lexer_RunIFBlock(); + lexer_ReachELSEBlock(); + } +} + +void act_Endc() { + lexer_DecIFDepth(); +} + +AlignmentSpec act_Alignment(int32_t alignment, int32_t alignOfs) { + AlignmentSpec spec = {0, 0}; + if (alignment > 16) { + error("Alignment must be between 0 and 16, not %u", alignment); + } else if (alignOfs <= -(1 << alignment) || alignOfs >= 1 << alignment) { + error( + "The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)", + static_cast(alignOfs < 0 ? -alignOfs : alignOfs), + 1 << alignment + ); + } else { + spec.alignment = alignment; + spec.alignOfs = alignOfs < 0 ? (1 << alignment) + alignOfs : alignOfs; + } + return spec; +} + +static void failAssert(AssertionType type, std::string const &message) { + switch (type) { + case ASSERT_FATAL: + if (message.empty()) { + fatal("Assertion failed"); + } else { + fatal("Assertion failed: %s", message.c_str()); + } + case ASSERT_ERROR: + if (message.empty()) { + error("Assertion failed"); + } else { + error("Assertion failed: %s", message.c_str()); + } + break; + case ASSERT_WARN: + if (message.empty()) { + warning(WARNING_ASSERT, "Assertion failed"); + } else { + warning(WARNING_ASSERT, "Assertion failed: %s", message.c_str()); + } + break; + } +} + +void act_Assert(AssertionType type, Expression const &expr, std::string const &message) { + if (!expr.isKnown()) { + out_CreateAssert(type, expr, message, sect_GetOutputOffset()); + } else if (expr.value() == 0) { + failAssert(type, message); + } +} + +void act_StaticAssert(AssertionType type, int32_t condition, std::string const &message) { + if (!condition) { + failAssert(type, message); + } +} + std::optional act_ReadFile(std::string const &name, uint32_t maxLen) { FILE *file = nullptr; if (std::optional fullPath = fstk_FindFile(name); fullPath) { @@ -53,6 +154,15 @@ std::optional act_ReadFile(std::string const &name, uint32_t maxLen return contents; } +uint32_t act_CharToNum(std::string const &str) { + if (std::vector output = charmap_Convert(str); output.size() == 1) { + return static_cast(output[0]); + } else { + error("Character literals must be a single charmap unit"); + return 0; + } +} + uint32_t act_StringToNum(std::vector const &str) { uint32_t length = str.size(); @@ -80,6 +190,63 @@ uint32_t act_StringToNum(std::vector const &str) { return r; } +static uint32_t adjustNegativeIndex(int32_t idx, size_t len, char const *functionName) { + // String functions adjust negative index arguments the same way, + // such that position -1 is the last character of a string. + if (idx < 0) { + idx += len; + } + if (idx < 0) { + warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0", functionName); + idx = 0; + } + return static_cast(idx); +} + +static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionName) { + // STRSUB and CHARSUB adjust negative position arguments the same way, + // such that position -1 is the last character of a string. + if (pos < 0) { + pos += len + 1; + } + if (pos < 1) { + warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1", functionName); + pos = 1; + } + return static_cast(pos); +} + +int32_t act_CharVal(std::string const &str, int32_t negIdx) { + if (size_t len = charmap_CharSize(str); len != 0) { + uint32_t idx = adjustNegativeIndex(negIdx, len, "CHARVAL"); + if (std::optional val = charmap_CharValue(str, idx); val.has_value()) { + return *val; + } else { + warning( + WARNING_BUILTIN_ARG, + "CHARVAL: Index %" PRIu32 " is past the end of the character mapping", + idx + ); + return 0; + } + } else { + error("CHARVAL: No character mapping for \"%s\"", str.c_str()); + return 0; + } +} + +uint8_t act_StringByte(std::string const &str, int32_t negIdx) { + size_t len = str.length(); + if (uint32_t idx = adjustNegativeIndex(negIdx, len, "STRBYTE"); idx < len) { + return static_cast(str[idx]); + } else { + warning( + WARNING_BUILTIN_ARG, "STRBYTE: Index %" PRIu32 " is past the end of the string", idx + ); + return 0; + } +} + static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) { error("%s: Invalid UTF-8 byte 0x%02hhX", functionName, byte); } @@ -116,7 +283,12 @@ size_t act_StringLen(std::string const &str, bool printErrors) { return len; } -std::string act_StringSlice(std::string const &str, uint32_t start, uint32_t stop) { +std::string + act_StringSlice(std::string const &str, int32_t negStart, std::optional negStop) { + size_t adjustLen = act_StringLen(str, false); + uint32_t start = adjustNegativeIndex(negStart, adjustLen, "STRSLICE"); + uint32_t stop = negStop ? adjustNegativeIndex(*negStop, adjustLen, "STRSLICE") : adjustLen; + size_t strLen = str.length(); size_t index = 0; uint32_t state = UTF8_ACCEPT; @@ -180,7 +352,11 @@ std::string act_StringSlice(std::string const &str, uint32_t start, uint32_t sto return str.substr(startIndex, index - startIndex); } -std::string act_StringSub(std::string const &str, uint32_t pos, uint32_t len) { +std::string act_StringSub(std::string const &str, int32_t negPos, std::optional optLen) { + size_t adjustLen = act_StringLen(str, false); + uint32_t pos = adjustNegativePos(negPos, adjustLen, "STRSUB"); + uint32_t len = optLen ? *optLen : pos > adjustLen ? 0 : adjustLen + 1 - pos; + size_t strLen = str.length(); size_t index = 0; uint32_t state = UTF8_ACCEPT; @@ -248,7 +424,10 @@ size_t act_CharLen(std::string const &str) { return len; } -std::string act_StringChar(std::string const &str, uint32_t idx) { +std::string act_StringChar(std::string const &str, int32_t negIdx) { + size_t adjustLen = act_CharLen(str); + uint32_t idx = adjustNegativeIndex(negIdx, adjustLen, "STRCHAR"); + std::string_view view = str; size_t charLen = 1; @@ -269,7 +448,10 @@ std::string act_StringChar(std::string const &str, uint32_t idx) { return std::string(start); } -std::string act_CharSub(std::string const &str, uint32_t pos) { +std::string act_CharSub(std::string const &str, int32_t negPos) { + size_t adjustLen = act_CharLen(str); + uint32_t pos = adjustNegativePos(negPos, adjustLen, "CHARSUB"); + std::string_view view = str; size_t charLen = 1; @@ -317,32 +499,6 @@ int32_t act_CharCmp(std::string_view str1, std::string_view str2) { } } -uint32_t act_AdjustNegativeIndex(int32_t idx, size_t len, char const *functionName) { - // String functions adjust negative index arguments the same way, - // such that position -1 is the last character of a string. - if (idx < 0) { - idx += len; - } - if (idx < 0) { - warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0", functionName); - idx = 0; - } - return static_cast(idx); -} - -uint32_t act_AdjustNegativePos(int32_t pos, size_t len, char const *functionName) { - // STRSUB and CHARSUB adjust negative position arguments the same way, - // such that position -1 is the last character of a string. - if (pos < 0) { - pos += len + 1; - } - if (pos < 1) { - warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1", functionName); - pos = 1; - } - return static_cast(pos); -} - std::string act_StringReplace(std::string_view str, std::string const &old, std::string const &rep) { if (old.empty()) { @@ -431,39 +587,30 @@ std::string act_StringFormat( return str; } +std::string act_SectionName(std::string const &symName) { + Symbol *sym = sym_FindScopedValidSymbol(symName); + if (!sym) { + if (sym_IsPurgedScoped(symName)) { + fatal("Unknown symbol \"%s\"; it was purged", symName.c_str()); + } else { + fatal("Unknown symbol \"%s\"", symName.c_str()); + } + } + + Section const *section = sym->getSection(); + if (!section) { + fatal("\"%s\" does not belong to any section", sym->name.c_str()); + } + + return section->name; +} + void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue) { Expression oldExpr, constExpr, newExpr; - int32_t newValue; - oldExpr.makeSymbol(symName); constExpr.makeNumber(constValue); newExpr.makeBinaryOp(op, std::move(oldExpr), constExpr); - newValue = newExpr.getConstVal(); + + int32_t newValue = newExpr.getConstVal(); sym_AddVar(symName, newValue); } - -void act_FailAssert(AssertionType type) { - switch (type) { - case ASSERT_FATAL: - fatal("Assertion failed"); - case ASSERT_ERROR: - error("Assertion failed"); - break; - case ASSERT_WARN: - warning(WARNING_ASSERT, "Assertion failed"); - break; - } -} - -void act_FailAssertMsg(AssertionType type, std::string const &message) { - switch (type) { - case ASSERT_FATAL: - fatal("Assertion failed: %s", message.c_str()); - case ASSERT_ERROR: - error("Assertion failed: %s", message.c_str()); - break; - case ASSERT_WARN: - warning(WARNING_ASSERT, "Assertion failed: %s", message.c_str()); - break; - } -} diff --git a/src/asm/parser.y b/src/asm/parser.y index d22f1557..bd9e756f 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -12,16 +12,12 @@ #include "linkdefs.hpp" + #include "asm/actions.hpp" #include "asm/lexer.hpp" #include "asm/macro.hpp" #include "asm/rpn.hpp" #include "asm/section.hpp" - struct AlignmentSpec { - uint8_t alignment; - uint16_t alignOfs; - }; - struct ForArgs { int32_t start; int32_t stop; @@ -47,7 +43,6 @@ #include "extern/utf8decoder.hpp" #include "helpers.hpp" - #include "asm/actions.hpp" #include "asm/charmap.hpp" #include "asm/fixpoint.hpp" #include "asm/fstack.hpp" @@ -465,48 +460,19 @@ line_directive: if: POP_IF iconst NEWLINE { - lexer_IncIFDepth(); - - if ($2) { - lexer_RunIFBlock(); - } else { - lexer_SetMode(LEXER_SKIP_TO_ELIF); - } + act_If($2); } ; elif: POP_ELIF iconst NEWLINE { - if (lexer_GetIFDepth() == 0) { - fatal("Found ELIF outside of an IF construct"); - } - if (lexer_RanIFBlock()) { - if (lexer_ReachedELSEBlock()) { - fatal("Found ELIF after an ELSE block"); - } - lexer_SetMode(LEXER_SKIP_TO_ENDC); - } else if ($2) { - lexer_RunIFBlock(); - } else { - lexer_SetMode(LEXER_SKIP_TO_ELIF); - } + act_Elif($2); } ; else: POP_ELSE NEWLINE { - if (lexer_GetIFDepth() == 0) { - fatal("Found ELSE outside of an IF construct"); - } - if (lexer_RanIFBlock()) { - if (lexer_ReachedELSEBlock()) { - fatal("Found ELSE after an ELSE block"); - } - lexer_SetMode(LEXER_SKIP_TO_ENDC); - } else { - lexer_RunIFBlock(); - lexer_ReachELSEBlock(); - } + act_Else(); } ; @@ -521,7 +487,7 @@ plain_directive: endc: POP_ENDC { - lexer_DecIFDepth(); + act_Endc(); } ; @@ -684,29 +650,10 @@ align: align_spec: uconst { - if ($1 > 16) { - ::error("Alignment must be between 0 and 16, not %u", $1); - $$.alignment = $$.alignOfs = 0; - } else { - $$.alignment = $1; - $$.alignOfs = 0; - } + $$ = act_Alignment($1, 0); } | uconst COMMA iconst { - if ($1 > 16) { - ::error("Alignment must be between 0 and 16, not %u", $1); - $$.alignment = $$.alignOfs = 0; - } else if ($3 <= -(1 << $1) || $3 >= 1 << $1) { - ::error( - "The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)", - static_cast($3 < 0 ? -$3 : $3), - 1 << $1 - ); - $$.alignment = $$.alignOfs = 0; - } else { - $$.alignment = $1; - $$.alignOfs = $3 < 0 ? (1 << $1) + $3 : $3; - } + $$ = act_Alignment($1, $3); } ; @@ -796,28 +743,16 @@ assert_type: assert: POP_ASSERT assert_type relocexpr { - if (!$3.isKnown()) { - out_CreateAssert($2, $3, "", sect_GetOutputOffset()); - } else if ($3.value() == 0) { - act_FailAssert($2); - } + act_Assert($2, $3, ""); } | POP_ASSERT assert_type relocexpr COMMA string { - if (!$3.isKnown()) { - out_CreateAssert($2, $3, $5, sect_GetOutputOffset()); - } else if ($3.value() == 0) { - act_FailAssertMsg($2, $5); - } + act_Assert($2, $3, $5); } | POP_STATIC_ASSERT assert_type iconst { - if ($3 == 0) { - act_FailAssert($2); - } + act_StaticAssert($2, $3, ""); } | POP_STATIC_ASSERT assert_type iconst COMMA string { - if ($3 == 0) { - act_FailAssertMsg($2, $5); - } + act_StaticAssert($2, $3, $5); } ; @@ -963,13 +898,11 @@ ds: } | POP_DS POP_ALIGN LBRACK align_spec RBRACK trailing_comma { uint32_t n = sect_GetAlignBytes($4.alignment, $4.alignOfs); - sect_Skip(n, true); sect_AlignPC($4.alignment, $4.alignOfs); } | POP_DS POP_ALIGN LBRACK align_spec RBRACK COMMA ds_args trailing_comma { uint32_t n = sect_GetAlignBytes($4.alignment, $4.alignOfs); - sect_RelBytes(n, $7); sect_AlignPC($4.alignment, $4.alignOfs); } @@ -1385,13 +1318,7 @@ relocexpr_no_str: $$.makeNumber($1); } | CHARACTER { - std::vector output = charmap_Convert($1); - if (output.size() == 1) { - $$.makeNumber(static_cast(output[0])); - } else { - ::error("Character literals must be a single charmap unit"); - $$.makeNumber(0); - } + $$.makeNumber(act_CharToNum($1)); } | OP_LOGICNOT relocexpr %prec NEG { $$.makeUnaryOp(RPN_LOGNOT, std::move($2)); @@ -1598,36 +1525,10 @@ relocexpr_no_str: $$.makeNumber(charSize); } | OP_CHARVAL LPAREN string COMMA iconst RPAREN { - if (size_t len = charmap_CharSize($3); len != 0) { - uint32_t idx = act_AdjustNegativeIndex($5, len, "CHARVAL"); - if (std::optional val = charmap_CharValue($3, idx); val.has_value()) { - $$.makeNumber(*val); - } else { - warning( - WARNING_BUILTIN_ARG, - "CHARVAL: Index %" PRIu32 " is past the end of the character mapping", - idx - ); - $$.makeNumber(0); - } - } else { - ::error("CHARVAL: No character mapping for \"%s\"", $3.c_str()); - $$.makeNumber(0); - } + $$.makeNumber(act_CharVal($3, $5)); } | OP_STRBYTE LPAREN string COMMA iconst RPAREN { - size_t len = $3.length(); - uint32_t idx = act_AdjustNegativeIndex($5, len, "STRBYTE"); - if (idx < len) { - $$.makeNumber(static_cast($3[idx])); - } else { - warning( - WARNING_BUILTIN_ARG, - "STRBYTE: Index %" PRIu32 " is past the end of the string", - idx - ); - $$.makeNumber(0); - } + $$.makeNumber(act_StringByte($3, $5)); } | LPAREN relocexpr RPAREN { $$ = std::move($2); @@ -1685,35 +1586,22 @@ string_literal: } } | OP_STRSLICE LPAREN string COMMA iconst COMMA iconst RPAREN { - size_t len = act_StringLen($3, false); - uint32_t start = act_AdjustNegativeIndex($5, len, "STRSLICE"); - uint32_t stop = act_AdjustNegativeIndex($7, len, "STRSLICE"); - $$ = act_StringSlice($3, start, stop); + $$ = act_StringSlice($3, $5, $7); } | OP_STRSLICE LPAREN string COMMA iconst RPAREN { - size_t len = act_StringLen($3, false); - uint32_t start = act_AdjustNegativeIndex($5, len, "STRSLICE"); - $$ = act_StringSlice($3, start, len); + $$ = act_StringSlice($3, $5, std::nullopt); } | OP_STRSUB LPAREN string COMMA iconst COMMA uconst RPAREN { - size_t len = act_StringLen($3, false); - uint32_t pos = act_AdjustNegativePos($5, len, "STRSUB"); - $$ = act_StringSub($3, pos, $7); + $$ = act_StringSub($3, $5, $7); } | OP_STRSUB LPAREN string COMMA iconst RPAREN { - size_t len = act_StringLen($3, false); - uint32_t pos = act_AdjustNegativePos($5, len, "STRSUB"); - $$ = act_StringSub($3, pos, pos > len ? 0 : len + 1 - pos); + $$ = act_StringSub($3, $5, std::nullopt); } | OP_STRCHAR LPAREN string COMMA iconst RPAREN { - size_t len = act_CharLen($3); - uint32_t idx = act_AdjustNegativeIndex($5, len, "STRCHAR"); - $$ = act_StringChar($3, idx); + $$ = act_StringChar($3, $5); } | OP_CHARSUB LPAREN string COMMA iconst RPAREN { - size_t len = act_CharLen($3); - uint32_t pos = act_AdjustNegativePos($5, len, "CHARSUB"); - $$ = act_CharSub($3, pos); + $$ = act_CharSub($3, $5); } | OP_REVCHAR LPAREN charmap_args RPAREN { bool unique; @@ -1745,23 +1633,7 @@ string_literal: $$ = act_StringFormat($3.format, $3.args); } | POP_SECTION LPAREN scoped_sym RPAREN { - Symbol *sym = sym_FindScopedValidSymbol($3); - - if (!sym) { - if (sym_IsPurgedScoped($3)) { - fatal("Unknown symbol \"%s\"; it was purged", $3.c_str()); - } else { - fatal("Unknown symbol \"%s\"", $3.c_str()); - } - } - Section const *section = sym->getSection(); - - if (!section) { - fatal("\"%s\" does not belong to any section", sym->name.c_str()); - } - // Section names are capped by rgbasm's maximum string length, - // so this currently can't overflow. - $$ = section->name; + $$ = act_SectionName($3); } ;