Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigInt support #1461

Open
viluon opened this issue Jun 21, 2023 · 6 comments
Open

BigInt support #1461

viluon opened this issue Jun 21, 2023 · 6 comments

Comments

@viluon
Copy link

viluon commented Jun 21, 2023

Currently, TSTL doesn't support JavaScript big integers, available since at least ECMAScript 2020. TypeScript supports the bigint type, big integer literals, and related features since version 3.2.

Supporting big integers in TSTL would require implementing them in the lualib, using metatables to support arithmetic operations in generic code*. The compiler would need to turn big integer literals into invocations of the appropriate constructor.


*: Mixing numbers and bigints in arithmetic is an error, but a function which works both on bigints and numbers, like the one below, could not be compiled to a single Lua function without big integer metatables (or costly dispatch via type()).

function arith<A extends bigint | number, B extends A>(a: A, b: B): bigint | number {
    return a * b
}
@Cheatoid
Copy link
Contributor

For LuaJIT target (big int overflow example):

print( 9223372036854775807LL + 1LL )
-9223372036854775808LL

print( 18446744073709551615ULL + 1ULL )
0ULL

For Lua 5.0/5.1/5.2 this could only be supported by using/requireing native extension, basically you're left alone to declare API provided by your host environment (if available).

It is a thing for 5.3+, just make sure to not mix things:

print( 9223372036854775807.0 == 9223372036854775807 )
false

@probable-basilisk
Copy link

Lua 5.3+ lets you compare an int and a number (double), the issue with your example is that the literal "9223372036854775807.0" does not correspond to a representable double, the nearest double is 9223372036854775808.0:

Lua 5.4.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio
> ("%f"):format(9223372036854775807.0)
9223372036854775808.000000

> 9223372036854775807.0 == 9223372036854775808
true

But if you try with a integer that is exactly representable in a double (e.g., 2^52) then the comparison returns true:

Lua 5.4.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio
> 4503599627370496 == 4503599627370496.0
true

@viluon
Copy link
Author

viluon commented Jun 21, 2023

@Cheatoid any Lua target would require a software implementation. Lua 5.3 introduced 64-bit integers, but not big integers, which are the subject of this proposal. Big integers are of an arbitrary size, operations with big integers do not overflow.

@Cheatoid
Copy link
Contributor

@Cheatoid any Lua target would require a software implementation. Lua 5.3 introduced 64-bit integers, but not big integers, which are the subject of this proposal. Big integers are of an arbitrary size, operations with big integers do not overflow.

That's what I thought you are proposing at first, but that didn't sound right since Lua by-standard doesn't have bigint support, except LuaJIT where you could theoretically implement that using a script. So for that reason, I thought this was about 64-bit integers.
Not sure how you expect to do this in Lua without native extension or other means.

@viluon
Copy link
Author

viluon commented Jul 11, 2023

@Cheatoid

That's what I thought you are proposing at first, but that didn't sound right since Lua by-standard doesn't have bigint support, except LuaJIT where you could theoretically implement that using a script.

That is not a problem – Lua also­ lacks support for other TypeScript features, such as classes. The purpose of the aforementioned lualib is to polyfill this functionality with a software implementation that doesn't rely on a straightforward mapping between TypeScript and Lua language features. See the src/lualib directory for the full implementation, or src/lualib/Class.kt for classes in particular.

I'm not sure what you mean by "except LuaJIT where you could theoretically implement that using a script." A software implementation of big integers is possible to implement for all Lua dialects TSTL currently supports, including PUC Lua 5.0-5.4.

Not sure how you expect to do this in Lua without native extension or other means.

I'm not sure what you mean by "native extension." As I suggested in the proposal above, you can implement arithmetic on big integer objects via metatables.

Similar arithmetic overloading is a well-known Lua feature, see e.g. Programming in Lua: Arithmetic Metamethods:

(…)
Finally, we add to the metatable the so-called metamethod, a field __add that describes how to perform the union:

Set.mt.__add = Set.union

Whenever Lua tries to add two sets, it will call this function, with the two operands as arguments.

With the metamethod in place, we can use the addition operator to do set unions:

s3 = s1 + s2
Set.print(s3)  --> {1, 10, 20, 30, 50}

Similarly, we may use the multiplication operator to perform set intersection:

Set.mt.__mul = Set.intersection

Set.print((s1 + s2)*s1)     --> {10, 20, 30, 50}

@Cheatoid
Copy link
Contributor

@viluon Sure... But scripted/software implementation is going to be slow.
I recommend implementing it natively within your Lua environment via native extension (using Lua C API), if possible.
With that being said, here goes my personal scripted implementation (it includes and passes all unit tests):
https://gist.github.com/Cheatoid/831cd0703c474d50e17de20766ae898f

I could port it over to TSTL and publish it to npm so everyone could use it easier, but kinda busy right now, if you want this so badly, just drop the bigint.lua Lua file into your TSTL source folder, and then make a file called bigint.d.ts and add the following:

declare interface BigInt {
  clone(this: BigInt): BigInt;
  equals(this: BigInt, other: object): boolean;
  toString(this: BigInt): string;
  negate(this: BigInt): BigInt;
  add(this: BigInt, other: BigInt): BigInt;
  subtract(this: BigInt, other: BigInt): BigInt;
  multiply(this: BigInt, other: BigInt): BigInt;
  divide(this: BigInt, other: BigInt): BigInt;
}
export = {
  (this: void, value: string): BigInt;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants