Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ char toUpper(char c);
bool startsIdentifier(int c);
bool continuesIdentifier(int c);

uint8_t parseDigit(int c);
uint8_t parseHexDigit(int c);
std::optional<uint64_t> parseNumber(char const *&str, NumberBase base = BASE_AUTO);
std::optional<uint64_t> parseWholeNumber(char const *str, NumberBase base = BASE_AUTO);
Expand Down
132 changes: 33 additions & 99 deletions src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1051,10 +1051,15 @@ static bool isValidDigit(char c) {
return isAlphanumeric(c) || c == '.' || c == '#' || c == '@';
}

static bool isCustomBinDigit(int c) {
static bool isAsmBinDigit(int c) {
return isBinDigit(c) || c == options.binDigits[0] || c == options.binDigits[1];
}

static uint8_t parseAsmBinDigit(int c) {
assume(isAsmBinDigit(c));
return c == '1' || c == options.binDigits[1]; // Returns 0 or 1
}

static bool checkDigitErrors(char const *digits, size_t n, char const *type) {
for (size_t i = 0; i < n; ++i) {
char c = digits[i];
Expand Down Expand Up @@ -1092,45 +1097,21 @@ void lexer_SetGfxDigits(char const digits[4]) {
}
}

static uint32_t readBinaryNumber(char const *prefix) {
uint32_t number = 0;
bool empty = true;
bool prevWasSeparator = false;

for (int c = peek();; c = nextChar()) {
if (c == '_') {
checkDigitSeparator(prevWasSeparator, "integer");
prevWasSeparator = true;
continue;
}

int bit;
if (c == '0' || c == options.binDigits[0]) {
bit = 0;
} else if (c == '1' || c == options.binDigits[1]) {
bit = 1;
} else {
break;
}
template<uint32_t Base, typename IsDigitFnT, typename ParseSomeDigitFnT>
static uint32_t readSomeNumber(
char const *prefix, int initial, IsDigitFnT isSomeDigit, ParseSomeDigitFnT parseSomeDigit
) {
uint32_t number;
bool empty;
if (prefix) {
Comment thread
Rangi42 marked this conversation as resolved.
Outdated
assume(initial == 0);
number = 0;
empty = true;
} else {
number = parseSomeDigit(initial);
empty = false;
prevWasSeparator = false;

if (number > (UINT32_MAX - bit) / 2) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
// Discard any additional digits
skipChars([](int d) { return isCustomBinDigit(d) || d == '_'; });
return 0;
}
number = number * 2 + bit;
}

checkDigitsEnding(empty, prefix, prevWasSeparator, "integer");
return number;
}

static uint32_t readOctalNumber(char const *prefix) {
uint32_t number = 0;
bool empty = true;
bool prevWasSeparator = false;

for (int c = peek();; c = nextChar()) {
Expand All @@ -1140,87 +1121,40 @@ static uint32_t readOctalNumber(char const *prefix) {
continue;
}

if (!isOctDigit(c)) {
if (!isSomeDigit(c)) {
break;
}
int digit = c - '0';
int digit = parseSomeDigit(c);
empty = false;
prevWasSeparator = false;

if (number > (UINT32_MAX - digit) / 8) {
if (number > (UINT32_MAX - digit) / Base) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
// Discard any additional digits
skipChars([](int d) { return isOctDigit(d) || d == '_'; });
skipChars([&isSomeDigit](int d) { return isSomeDigit(d) || d == '_'; });
return 0;
}
number = number * 8 + digit;
number = number * Base + digit;
}

checkDigitsEnding(empty, prefix, prevWasSeparator, "integer");
return number;
}

static uint32_t readDecimalNumber(int initial) {
assume(isDigit(initial));
uint32_t number = initial - '0';
bool prevWasSeparator = false;

for (int c = peek();; c = nextChar()) {
if (c == '_') {
checkDigitSeparator(prevWasSeparator, "integer");
prevWasSeparator = true;
continue;
}

if (!isDigit(c)) {
break;
}
int digit = c - '0';
prevWasSeparator = false;
static uint32_t readBinaryNumber(char const *prefix) {
return readSomeNumber<2>(prefix, 0, isAsmBinDigit, parseAsmBinDigit);
}

if (number > (UINT32_MAX - digit) / 10) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
// Discard any additional digits
skipChars([](int d) { return isDigit(d) || d == '_'; });
return 0;
}
number = number * 10 + digit;
}
static uint32_t readOctalNumber(char const *prefix) {
return readSomeNumber<8>(prefix, 0, isOctDigit, parseDigit);
Comment thread
Rangi42 marked this conversation as resolved.
Outdated
}

checkDigitsEnding(false, nullptr, prevWasSeparator, "integer");
return number;
static uint32_t readDecimalNumber(int initial) {
return readSomeNumber<10>(nullptr, initial, isDigit, parseDigit);
}

static uint32_t readHexNumber(char const *prefix) {
uint32_t number = 0;
bool empty = true;
bool prevWasSeparator = false;

for (int c = peek();; c = nextChar()) {
if (c == '_') {
checkDigitSeparator(prevWasSeparator, "integer");
prevWasSeparator = true;
continue;
}

if (!isHexDigit(c)) {
break;
}
int digit = parseHexDigit(c);
empty = false;
prevWasSeparator = false;

if (number > (UINT32_MAX - digit) / 16) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
// Discard any additional digits
skipChars([](int d) { return isHexDigit(d) || d == '_'; });
return 0;
}
number = number * 16 + digit;
}

checkDigitsEnding(empty, prefix, prevWasSeparator, "integer");
return number;
return readSomeNumber<16>(prefix, 0, isHexDigit, parseHexDigit);
}

static uint32_t readGfxConstant() {
Expand Down Expand Up @@ -1871,7 +1805,7 @@ static Token yylex_NORMAL() {

case '%': // Either %=, MOD, or a binary constant
c = peek();
if (isCustomBinDigit(c) || c == '_') {
if (isAsmBinDigit(c) || c == '_') {
return Token(T_(NUMBER), readBinaryNumber("'%'"));
}
return oneOrTwo('=', T_(POP_MODEQ), T_(OP_MOD));
Expand Down
77 changes: 31 additions & 46 deletions src/link/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,66 +94,51 @@ static std::string readKeyword(int initial) {
return keyword;
}

static yy::parser::symbol_type parseDecNumber(int initial) {
template<uint32_t Base, typename IsDigitFnT, typename ParseSomeDigitFnT>
static yy::parser::symbol_type parseSomeNumber(
char const *prefix,
int initial,
char const *name,
IsDigitFnT isSomeDigit,
ParseSomeDigitFnT parseSomeDigit
) {
LexerStackEntry &context = lexerStack.back();
uint32_t number = initial - '0';
for (int c = context.file.sgetc(); isDigit(c) || c == '_'; c = context.file.snextc()) {
uint32_t number;
if (prefix) {
assume(initial == 0 && name != nullptr);
int c = context.file.sgetc();
if (!isSomeDigit(c)) {
scriptError("No %s digits found after %s", name, prefix);
return yy::parser::make_number(0);
}
number = parseSomeDigit(c);
context.file.sbumpc();
} else {
assume(name == nullptr);
number = parseSomeDigit(initial);
}
for (int c = context.file.sgetc(); isSomeDigit(c) || c == '_'; c = context.file.snextc()) {
if (c != '_') {
number = number * 10 + (c - '0');
number = number * Base + parseSomeDigit(c);
}
}
return yy::parser::make_number(number);
}

static yy::parser::symbol_type parseBinNumber(char const *prefix) {
LexerStackEntry &context = lexerStack.back();
int c = context.file.sgetc();
if (!isBinDigit(c)) {
scriptError("No binary digits found after %s", prefix);
return yy::parser::make_number(0);
}
static yy::parser::symbol_type parseDecNumber(int initial) {
return parseSomeNumber<10>(nullptr, initial, nullptr, isDigit, parseDigit);
}

uint32_t number = c - '0';
for (c = context.file.snextc(); isBinDigit(c) || c == '_'; c = context.file.snextc()) {
if (c != '_') {
number = number * 2 + (c - '0');
}
}
return yy::parser::make_number(number);
static yy::parser::symbol_type parseBinNumber(char const *prefix) {
return parseSomeNumber<2>(prefix, 0, "binary", isBinDigit, parseDigit);
}

static yy::parser::symbol_type parseOctNumber(char const *prefix) {
LexerStackEntry &context = lexerStack.back();
int c = context.file.sgetc();
if (!isOctDigit(c)) {
scriptError("No octal digits found after %s", prefix);
return yy::parser::make_number(0);
}

uint32_t number = c - '0';
for (c = context.file.snextc(); isOctDigit(c) || c == '_'; c = context.file.snextc()) {
if (c != '_') {
number = number * 8 + (c - '0');
}
}
return yy::parser::make_number(number);
return parseSomeNumber<8>(prefix, 0, "octal", isOctDigit, parseDigit);
}

static yy::parser::symbol_type parseHexNumber(char const *prefix) {
LexerStackEntry &context = lexerStack.back();
int c = context.file.sgetc();
if (!isHexDigit(c)) {
scriptError("No hexadecimal digits found after %s", prefix);
return yy::parser::make_number(0);
}

uint32_t number = parseHexDigit(c);
for (c = context.file.snextc(); isHexDigit(c) || c == '_'; c = context.file.snextc()) {
if (c != '_') {
number = number * 16 + parseHexDigit(c);
}
}
return yy::parser::make_number(number);
return parseSomeNumber<16>(prefix, 0, "hexadecimal", isHexDigit, parseHexDigit);
}

static yy::parser::symbol_type parseAnyNumber(int initial) {
Expand Down
9 changes: 7 additions & 2 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,19 @@ bool continuesIdentifier(int c) {
return startsIdentifier(c) || isDigit(c) || c == '#' || c == '$' || c == '@';
}

uint8_t parseDigit(int c) {
assume(isDigit(c));
return c - '0';
}

uint8_t parseHexDigit(int c) {
assume(isHexDigit(c));
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
} else {
assume(isDigit(c));
return c - '0';
return parseDigit(c);
}
}

Expand Down
Loading