From fc7d9ad573c9298797f5326fdf7dc728efd2c895 Mon Sep 17 00:00:00 2001 From: Rangi Date: Tue, 9 Jun 2026 18:44:03 -0400 Subject: [PATCH] Handle more numeric literal syntax errors in linker scripts --- src/link/lexer.cpp | 67 +++++++++++++++++-------------- test/link/script-lone-dollar.out | 2 +- test/link/script-lone-percent.out | 2 +- test/link/script-num-fmt.link | 12 +++--- test/link/script-num-fmt.out | 24 ++++++++++- 5 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/link/lexer.cpp b/src/link/lexer.cpp index 1dffd3aa..47f16b49 100644 --- a/src/link/lexer.cpp +++ b/src/link/lexer.cpp @@ -97,27 +97,39 @@ static std::string readKeyword(int initial) { template requires ValidBaseV -static yy::parser::symbol_type readNumber(int initial, char const *prefix, char const *name) { +static yy::parser::symbol_type readNumber(int initial, char const *prefix) { LexerStackEntry &context = lexerStack.back(); + uint32_t number; + bool empty; if constexpr (Base == 10) { - assume(prefix == nullptr && name == nullptr); + assume(prefix == nullptr); number = parseDigit(initial); + empty = false; } else { - assume(initial == 0 && prefix != nullptr && name != nullptr); - int c = context.file.sgetc(); - if (!isDigit(c)) { - scriptError("No %s digits found after %s", name, prefix); - return yy::parser::make_number(0); - } - number = parseDigit(c); - context.file.sbumpc(); + assume(initial == 0 && prefix != nullptr); + number = 0; + empty = true; } - for (int c = context.file.sgetc(); isDigit(c) || c == '_'; c = context.file.snextc()) { + + bool prevWasSeparator = false; + + for (int c = context.file.sgetc();; c = context.file.snextc()) { if (c == '_') { + if (prevWasSeparator) { + scriptError("Invalid integer constant, '_' after another '_'"); + } + prevWasSeparator = true; continue; } + + if (!isDigit(c)) { + break; + } uint32_t digit = parseDigit(c); + empty = false; + prevWasSeparator = false; + if (number > (UINT32_MAX - digit) / Base) { scriptWarning(WARNING_LARGE_CONSTANT, "Integer constant is too large"); // Discard any additional digits @@ -127,21 +139,16 @@ static yy::parser::symbol_type readNumber(int initial, char const *prefix, char } number = number * Base + digit; } + + if (empty) { + scriptError("Invalid integer constant, no digits after %s", prefix); + } + if (prevWasSeparator) { + scriptError("Invalid integer constant, trailing '_'"); + } return yy::parser::make_number(number); } -static yy::parser::symbol_type parseBinNumber(char const *prefix) { - return readNumber<2>(0, prefix, "binary"); -} - -static yy::parser::symbol_type parseOctNumber(char const *prefix) { - return readNumber<8>(0, prefix, "octal"); -} - -static yy::parser::symbol_type parseHexNumber(char const *prefix) { - return readNumber<16>(0, prefix, "hexadecimal"); -} - static yy::parser::symbol_type parseAnyNumber(int initial) { LexerStackEntry &context = lexerStack.back(); if (initial == '0') { @@ -149,18 +156,18 @@ static yy::parser::symbol_type parseAnyNumber(int initial) { case 'x': case 'X': context.file.sbumpc(); - return parseHexNumber("\"0x\""); + return readNumber<16>(0, "\"0x\""); case 'o': case 'O': context.file.sbumpc(); - return parseOctNumber("\"0o\""); + return readNumber<8>(0, "\"0o\""); case 'b': case 'B': context.file.sbumpc(); - return parseBinNumber("\"0b\""); + return readNumber<2>(0, "\"0b\""); } } - return readNumber<10>(initial, nullptr, nullptr); + return readNumber<10>(initial, nullptr); } static yy::parser::symbol_type parseString() { @@ -226,11 +233,11 @@ yy::parser::symbol_type yylex() { } else if (c == '"') { return parseString(); } else if (c == '$') { - return parseHexNumber("'$'"); + return readNumber<16>(0, "'$'"); } else if (c == '%') { - return parseBinNumber("'%'"); + return readNumber<2>(0, "'%'"); } else if (c == '&') { - return parseOctNumber("'&'"); + return readNumber<8>(0, "'&'"); } else if (isDigit<10>(c)) { return parseAnyNumber(c); } else if (isLetter(c)) { diff --git a/test/link/script-lone-dollar.out b/test/link/script-lone-dollar.out index f2f6eaf9..8f34c422 100644 --- a/test/link/script-lone-dollar.out +++ b/test/link/script-lone-dollar.out @@ -1,3 +1,3 @@ -error: No hexadecimal digits found after '$' +error: Invalid integer constant, no digits after '$' at script-lone-dollar.link(1) Linking failed with 1 error diff --git a/test/link/script-lone-percent.out b/test/link/script-lone-percent.out index a4635a4d..c5adc98b 100644 --- a/test/link/script-lone-percent.out +++ b/test/link/script-lone-percent.out @@ -1,3 +1,3 @@ -error: No binary digits found after '%' +error: Invalid integer constant, no digits after '%' at script-lone-percent.link(1) Linking failed with 1 error diff --git a/test/link/script-num-fmt.link b/test/link/script-num-fmt.link index 09386e9f..1477a09a 100644 --- a/test/link/script-num-fmt.link +++ b/test/link/script-num-fmt.link @@ -1,9 +1,11 @@ ROM0 org 4_2 org %10_10_10 - org &52_ - org $2A_ - org 0b101_010 - org 0o5_2 + org &52_ ; trailing '_' + org $2A_ ; trailing '_' + org 0b101__010 ; '_' after another '_' + org 0o__5_2 ; '_' after another '_' org 0x2_A - org 41 ; Error! + org 41 ; different value + org %_ ; no digits + org 0x__ ; no digits diff --git a/test/link/script-num-fmt.out b/test/link/script-num-fmt.out index b8650793..f901dd65 100644 --- a/test/link/script-num-fmt.out +++ b/test/link/script-num-fmt.out @@ -1,3 +1,25 @@ +error: Invalid integer constant, trailing '_' + at script-num-fmt.link(4) +error: Invalid integer constant, trailing '_' + at script-num-fmt.link(5) +error: Invalid integer constant, '_' after another '_' + at script-num-fmt.link(6) +error: Invalid integer constant, '_' after another '_' + at script-num-fmt.link(7) error: Cannot decrease the current address (from $002a to $0029) at script-num-fmt.link(9) -Linking failed with 1 error +error: Invalid integer constant, no digits after '%' + at script-num-fmt.link(10) +error: Invalid integer constant, trailing '_' + at script-num-fmt.link(10) +error: Cannot decrease the current address (from $002a to $0000) + at script-num-fmt.link(10) +error: Invalid integer constant, '_' after another '_' + at script-num-fmt.link(11) +error: Invalid integer constant, no digits after "0x" + at script-num-fmt.link(11) +error: Invalid integer constant, trailing '_' + at script-num-fmt.link(11) +error: Cannot decrease the current address (from $002a to $0000) + at script-num-fmt.link(11) +Linking failed with 12 errors