Skip to content

Commit

Permalink
Add support for list_to_integer/2
Browse files Browse the repository at this point in the history
  • Loading branch information
jakub-gonet committed Oct 11, 2024
1 parent 1bedd1c commit 736aa51
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 14 deletions.
14 changes: 14 additions & 0 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
list_to_existing_atom/1,
list_to_binary/1,
list_to_integer/1,
list_to_integer/2,
list_to_tuple/1,
iolist_to_binary/1,
binary_to_atom/1,
Expand Down Expand Up @@ -600,6 +601,19 @@ list_to_binary(_IOList) ->
list_to_integer(_String) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param String string to convert to integer
%% @param Base string to convert to integer
%% @returns an integer value from its string representation
%% @doc Convert a string (list of characters) to integer in specified base.
%% Errors with `badarg' if the string is not a representation of an integer or
%% the base is out of bounds.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_integer(String :: string(), Base :: 2..36) -> integer().
list_to_integer(_String, _Base) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param List list to convert to tuple
%% @returns a tuple with elements of the list
Expand Down
38 changes: 29 additions & 9 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ static term nif_erlang_link(Context *ctx, int argc, term argv[]);
static term nif_erlang_float_to_binary(Context *ctx, int argc, term argv[]);
static term nif_erlang_float_to_list(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_integer_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_integer(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_float_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_atom_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_list_to_existing_atom_1(Context *ctx, int argc, term argv[]);
Expand Down Expand Up @@ -386,7 +386,7 @@ static const struct Nif list_to_binary_nif =
static const struct Nif list_to_integer_nif =
{
.base.type = NIFFunctionType,
.nif_ptr = nif_erlang_list_to_integer_1
.nif_ptr = nif_erlang_list_to_integer
};

static const struct Nif list_to_float_nif =
Expand Down Expand Up @@ -2544,9 +2544,30 @@ static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[])
return bin_res;
}

static term nif_erlang_list_to_integer_1(Context *ctx, int argc, term argv[])
static avm_int_t to_digit_index(avm_int_t character)
{
UNUSED(argc);
if (character >= '0' && character <= '9') {
return character - '0';
} else if (character >= 'a' && character <= 'z') {
return character - 'a' + 10;
} else if (character >= 'A' && character <= 'Z') {
return character - 'A' + 10;
} else {
return -1;
}
}

static term nif_erlang_list_to_integer(Context *ctx, int argc, term argv[])
{
avm_int_t base = 10;
if(argc == 2) {
term t = argv[1];
VALIDATE_VALUE(t, term_is_integer);
base = term_to_int(t);
if (UNLIKELY(base < 2 || base > 36)) {
RAISE_ERROR(BADARG_ATOM);
}
}

term t = argv[0];
int64_t acc = 0;
Expand All @@ -2565,22 +2586,21 @@ static term nif_erlang_list_to_integer_1(Context *ctx, int argc, term argv[])

while (term_is_nonempty_list(t)) {
term head = term_get_list_head(t);

VALIDATE_VALUE(head, term_is_integer);

avm_int_t c = term_to_int(head);

if (UNLIKELY((c < '0') || (c > '9'))) {
avm_int_t digit = to_digit_index(c);
if(UNLIKELY(digit == -1 || digit >= base)) {
RAISE_ERROR(BADARG_ATOM);
}

//TODO: fix this
if (acc > INT64_MAX / 10) {
if (acc > INT64_MAX / base) {
// overflow error is not standard, but we need it since we are running on an embedded device
RAISE_ERROR(OVERFLOW_ATOM);
}

acc = (acc * 10) + (c - '0');
acc = (acc * base) + digit;
digits++;
t = term_get_list_tail(t);
if (!term_is_list(t)) {
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ erlang:integer_to_list/2, &integer_to_list_nif
erlang:link/1, &link_nif
erlang:list_to_binary/1, &list_to_binary_nif
erlang:list_to_integer/1, &list_to_integer_nif
erlang:list_to_integer/2, &list_to_integer_nif
erlang:list_to_float/1, &list_to_float_nif
erlang:list_to_tuple/1, &list_to_tuple_nif
erlang:iolist_size/1, &iolist_size_nif
Expand Down
18 changes: 13 additions & 5 deletions tests/erlang_tests/test_list_to_integer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,16 @@
-export([start/0, sum_integers/2, append_0/1]).

start() ->
sum_integers(append_0("10"), "-1") + safe_list_to_integer("--") - safe_list_to_integer(nan) +
safe_list_to_integer("+10") - 10 + safe_list_to_integer("-") - 5 + safe_list_to_integer("+") -
5 +
safe_list_to_integer("") - 5.
sum_integers(append_0("10"), "-1") +
safe_list_to_integer("--") - 5 +
safe_list_to_integer(nan) - 5 +
safe_list_to_integer("+10") - 10 +
safe_list_to_integer("-") - 5 +
safe_list_to_integer("+") - 5 +
safe_list_to_integer("") - 5 +
safe_list_to_integer("0a", 16) - 10 +
safe_list_to_integer("-0a", 16) + 10 +
safe_list_to_integer("1010", 2) - 10.

append_0(L) ->
L ++ "0".
Expand All @@ -35,7 +41,9 @@ sum_integers(A, B) ->
list_to_integer(A) + list_to_integer(B).

safe_list_to_integer(A) ->
try list_to_integer(A) of
safe_list_to_integer(A, 10).
safe_list_to_integer(A, Base) ->
try list_to_integer(A, Base) of
AnyValue -> AnyValue
catch
error:badarg ->
Expand Down

0 comments on commit 736aa51

Please sign in to comment.