Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
repository: pcsx-redux/nugget
path: nugget

- name: Number primitives
run: cc -Wall -I src -o /tmp/numbers tests/numbers.c && /tmp/numbers

- name: Library
run: make -C src psx -j2

Expand Down
84 changes: 38 additions & 46 deletions src/llibc.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,65 +122,57 @@ static const inline int strisspace(char c) {
c == '\v';
}

static int digitval(unsigned char c) {
if ((c >= '0') && (c <= '9')) return c - '0';
if ((c >= 'a') && (c <= 'z')) return c - 'a' + 10;
if ((c >= 'A') && (c <= 'Z')) return c - 'A' + 10;
return 36;
}

/*
** Same contract as strtol: *endptr is set on every return, and points at
** nptr when no digit was read.
*/
long int luaA_strtol(const char *nptr, char **endptr, int base) {
const char *s = nptr;
int negative = 0;
int digits = 0;
unsigned long int r = 0;

if (endptr) {
*endptr = (char *)nptr;
}
if (base < 0 || base == 1 || base >= 37) {
return 0;
}

if ((*nptr == '0') && ((*(nptr + 1) == 'x') || (*(nptr + 1) == 'X'))) {
if ((base != 0) && (base != 16)) {
return 0;
}
while (strisspace(*s)) {
s++;
}
if ((*s == '+') || (*s == '-')) {
negative = *s == '-';
s++;
}

if (((base == 0) || (base == 16)) && (s[0] == '0') &&
((s[1] == 'x') || (s[1] == 'X')) && (digitval(s[2]) < 16)) {
base = 16;
nptr += 2;
} else if (*nptr == '0') {
if ((base != 0) && (base != 8) && (*(nptr + 1) != 0)) {
return 0;
}
base = 8;
nptr++;
s += 2;
} else if (base == 0) {
base = 10;
base = (*s == '0') ? 8 : 10;
}

int got_something = 0;
int negative = 0;
unsigned long int r = 0;

char c;
char maxLC = 'a' + (base - 10) - 1;
char maxUC = 'A' + (base - 10) - 1;

while ((c = *nptr++)) {
if (strisspace(c) && !got_something) {
continue;
}
if (((c == '+') || (c == '-')) && !got_something) {
got_something = 1;
if (c == '-') {
negative = 1;
}
continue;
}
if ((c >= '0') && (c <= '9')) {
c -= '0';
} else if ((c >= 'a') && (c <= maxLC)) {
c -= 'a' - 10;
} else if ((c >= 'A') && (c <= maxUC)) {
c -= 'A' - 10;
} else {
for (;; s++) {
int d = digitval(*s);
if (d >= base) {
break;
}
if (c >= base) {
break;
}
got_something = 1;
r *= base;
r += c;
r = r * base + d;
digits = 1;
}

if (endptr) {
*endptr = (char *)nptr - 1;
if (endptr && digits) {
*endptr = (char *)s;
}
return negative ? -r : r;
}
Expand Down
19 changes: 15 additions & 4 deletions src/luaconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,12 +454,23 @@ long int luaA_strtol(const char *nptr, char **endptr, int base);
/* the following operations need the math library */
#if defined(lobject_c) || defined(lvm_c)
#define luai_nummod(L,a,b) ((a) % (b))
/*
** Integer power. A negative exponent gives the truncated quotient
** 1 / a^-b, which is 0 unless |a| is 1, and 0 for 0^-b. Overflow wraps.
*/
static inline long luai_numpowimpl(long a, long b) {
long r = a;
for (unsigned i = 0; i < b; i++) {
r *= a;
unsigned long r = 1, x = (unsigned long)a;
if (b < 0) {
if (a == 1) return 1;
if (a == -1) return (b & 1) ? -1 : 1;
return 0;
}
while (b) {
if (b & 1) r *= x;
x *= x;
b >>= 1;
}
return r;
return (long)r;
}
#define luai_numpow(L,a,b) (luai_numpowimpl((a),(b)))
#endif
Expand Down
74 changes: 74 additions & 0 deletions tests/numbers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Host checks for the integer number primitives: luai_numpow (constant
* folding and OP_POW) and luaA_strtol (lexer numerals and tonumber).
*
* cc -I src -o numbers tests/numbers.c && ./numbers
*/

#define lobject_c
#include "luaconf.h"
#include "llibc.c"

#include <stdio.h>
#include <string.h>

static int failures = 0;

static void pow_case(long a, long b, long expected) {
long got = luai_numpowimpl(a, b);
if (got != expected) {
printf("FAIL %ld^%ld = %ld, expected %ld\n", a, b, got, expected);
failures++;
}
}

static void strtol_case(const char *s, int base, long expected, int consumed) {
char *end = (char *)0x1; /* anything but s, so an unset endptr shows */
long got = luaA_strtol(s, &end, base);
if ((got != expected) || (end != s + consumed)) {
printf("FAIL strtol(\"%s\", %d) = %ld, end +%ld, expected %ld, end +%d\n",
s, base, got, (long)(end - s), expected, consumed);
failures++;
}
}

int main(void) {
pow_case(2, 10, 1024);
pow_case(3, 0, 1);
pow_case(0, 0, 1);
pow_case(-3, 3, -27);
pow_case(10, 9, 1000000000);
pow_case(2, -1, 0);
pow_case(1, -5, 1);
pow_case(-1, -3, -1);
pow_case(-1, -4, 1);
pow_case(0, -1, 0);

strtol_case("10", 10, 10, 2);
strtol_case("010", 10, 10, 3);
strtol_case("00", 10, 0, 2);
strtol_case("0", 10, 0, 1);
strtol_case("0.5", 10, 0, 1);
strtol_case("-010", 10, -10, 4);
strtol_case(" 42 ", 10, 42, 4);
strtol_case("", 10, 0, 0);
strtol_case(" ", 10, 0, 0);
strtol_case("-", 10, 0, 0);
strtol_case("abc", 10, 0, 0);
strtol_case("0x1F", 16, 31, 4);
strtol_case("-0x10", 16, -16, 5);
strtol_case("0x", 16, 0, 1);
strtol_case("0x1F", 10, 0, 1);
strtol_case("017", 0, 15, 3);
strtol_case("0x1f", 0, 31, 4);
strtol_case("17", 0, 17, 2);
strtol_case("z", 36, 35, 1);
strtol_case("1", 1, 0, 0);

if (failures) {
printf("%d failure(s)\n", failures);
return 1;
}
printf("numbers: all checks passed\n");
return 0;
}
2 changes: 2 additions & 0 deletions tests/sample.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local squares = {}
for i = 1, 10 do squares[i] = i * i end
-- Folded at compile time on the console: 1024 + 10.
squares.folded = 2^10 + 010
return squares
Loading