aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-09 21:53:51 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-09 21:53:51 +0100
commit3546644ebcd8112b089555e69dcd7a5dad5b434b (patch)
tree0525c7ac4274c3ef1a91907b75f3f1f30897d25e
parentbb88a32d105da31e5cdd2ad713016c88b96ac0ce (diff)
downloadgleam_stdlib-3546644ebcd8112b089555e69dcd7a5dad5b434b.tar.gz
gleam_stdlib-3546644ebcd8112b089555e69dcd7a5dad5b434b.zip
Negative indexes in Erlang
-rw-r--r--src/gleam_stdlib.erl53
-rw-r--r--src/gleam_stdlib.js2
-rw-r--r--test/gleam/dynamic_test.gleam92
3 files changed, 98 insertions, 49 deletions
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 92cae8e..2f234e7 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -59,24 +59,31 @@ classify(X) when is_integer(X) -> <<"Int">>;
classify(X) when is_float(X) -> <<"Float">>;
classify(X) when is_list(X) -> <<"List">>;
classify(X) when is_boolean(X) -> <<"Bool">>;
-classify(X) when is_function(X, 0) -> <<"zero arity function">>;
-classify(X) when is_tuple(X) -> iolist_to_binary([integer_to_list(tuple_size(X)), " element tuple"]);
-classify(_) -> "some other type".
+classify(X) when is_map(X) -> <<"Map">>;
+classify(X) when is_tuple(X) ->
+ iolist_to_binary(["Tuple of ", integer_to_list(tuple_size(X)), " elements"]);
+classify(X) when
+ is_function(X, 0) orelse is_function(X, 1) orelse is_function(X, 2) orelse
+ is_function(X, 3) orelse is_function(X, 4) orelse is_function(X, 5) orelse
+ is_function(X, 6) orelse is_function(X, 7) orelse is_function(X, 8) orelse
+ is_function(X, 9) orelse is_function(X, 10) orelse is_function(X, 11) orelse
+ is_function(X, 12) -> <<"Function">>;
+classify(_) -> "Some other type".
decode_tuple2({_, _} = T) -> {ok, T};
-decode_tuple2(Data) -> decode_error_msg(<<"2 element tuple">>, Data).
+decode_tuple2(Data) -> decode_error_msg(<<"Tuple of 2 elements">>, Data).
decode_tuple3({_, _, _} = T) -> {ok, T};
-decode_tuple3(Data) -> decode_error_msg(<<"3 element tuple">>, Data).
+decode_tuple3(Data) -> decode_error_msg(<<"Tuple of 3 elements">>, Data).
decode_tuple4({_, _, _, _} = T) -> {ok, T};
-decode_tuple4(Data) -> decode_error_msg(<<"4 element tuple">>, Data).
+decode_tuple4(Data) -> decode_error_msg(<<"Tuple of 4 elements">>, Data).
decode_tuple5({_, _, _, _, _} = T) -> {ok, T};
-decode_tuple5(Data) -> decode_error_msg(<<"5 element tuple">>, Data).
+decode_tuple5(Data) -> decode_error_msg(<<"Tuple of 5 elements">>, Data).
decode_tuple6({_, _, _, _, _, _} = T) -> {ok, T};
-decode_tuple6(Data) -> decode_error_msg(<<"6 element tuple">>, Data).
+decode_tuple6(Data) -> decode_error_msg(<<"Tuple of 6 elements">>, Data).
decode_map(Data) when is_map(Data) -> {ok, Data};
decode_map(Data) -> decode_error_msg(<<"Map">>, Data).
@@ -108,16 +115,28 @@ decode_field(Data, Key) ->
decode_error_msg(io_lib:format("a map with key `~p`", [Key]), Data)
end.
-decode_element(Data, Position) when is_tuple(Data) ->
- case catch element(Position + 1, Data) of
- {'EXIT', _Reason} ->
- Msg = ["Tuple of at least ", integer_to_list(Position + 1), " elements"],
- decode_error_msg(list_to_binary(Msg), Data);
-
- Value ->
- {ok, Value}
+decode_element(Data, Index) when is_tuple(Data) ->
+ Error = fun(Size) ->
+ S = case Size of
+ 1 -> "s";
+ _ -> ""
+ end,
+ Msg = list_to_binary(["Tuple of at least ", integer_to_list(Size), S, " elements"]),
+ decode_error_msg(Msg, Data)
+ end,
+ Size = tuple_size(Data),
+ case Index >= 0 of
+ true -> case Index < Size of
+ true -> {ok, element(Index + 1, Data)};
+ false -> Error(Index + 1)
+ end;
+ false -> case abs(Index) < Size of
+ true -> {ok, element(Size + Index + 1, Data)};
+ false -> Error(abs(Index))
+ end
end;
-decode_element(Data, _Position) -> decode_error_msg(<<"Tuple">>, Data).
+decode_element(Data, _Position) ->
+ decode_error_msg(<<"Tuple of at least 1 element">>, Data).
decode_optional(Term, F) ->
Decode = fun(Inner) ->
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index 9a80a6d..7ecbe11 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -521,7 +521,7 @@ export function decode_bit_string(data) {
export function decode_element(data, index) {
let error = (size) =>
decoder_error(
- `Tuple of at least ${size} element${size > 1 ? "s" : ""}`,
+ `Tuple of at least ${size} element${size == 1 ? "" : "s"}`,
data
);
if (!Array.isArray(data)) return error(index < 0 ? 1 : index + 1);
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index 4ab3d65..1bf0eee 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -324,14 +324,17 @@ if erlang {
|> dynamic.from
|> dynamic.tuple2
|> should.equal(Error(DecodeError(
- expected: "2 element tuple",
- found: "3 element tuple",
+ expected: "Tuple of 2 elements",
+ found: "Tuple of 3 elements",
)))
1
|> dynamic.from
|> dynamic.tuple2
- |> should.equal(Error(DecodeError(expected: "2 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 2 elements",
+ found: "Int",
+ )))
}
pub fn typed_tuple2_test() {
@@ -354,14 +357,17 @@ if erlang {
|> dynamic.from
|> dynamic.typed_tuple2(dynamic.int, dynamic.int)
|> should.equal(Error(DecodeError(
- expected: "2 element tuple",
- found: "3 element tuple",
+ expected: "Tuple of 2 elements",
+ found: "Tuple of 3 elements",
)))
1
|> dynamic.from
|> dynamic.typed_tuple2(dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "2 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 2 elements",
+ found: "Int",
+ )))
}
pub fn tuple3_test() {
@@ -379,14 +385,17 @@ if erlang {
|> dynamic.from
|> dynamic.tuple3
|> should.equal(Error(DecodeError(
- expected: "3 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 3 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.tuple3
- |> should.equal(Error(DecodeError(expected: "3 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 3 elements",
+ found: "Int",
+ )))
}
pub fn typed_tuple3_test() {
@@ -409,14 +418,17 @@ if erlang {
|> dynamic.from
|> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int)
|> should.equal(Error(DecodeError(
- expected: "3 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 3 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "3 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 3 elements",
+ found: "Int",
+ )))
}
pub fn tuple4_test() {
@@ -444,14 +456,17 @@ if erlang {
|> dynamic.from
|> dynamic.tuple4
|> should.equal(Error(DecodeError(
- expected: "4 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 4 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.tuple4
- |> should.equal(Error(DecodeError(expected: "4 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 4 elements",
+ found: "Int",
+ )))
}
pub fn typed_tuple4_test() {
@@ -479,14 +494,17 @@ if erlang {
|> dynamic.from
|> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int)
|> should.equal(Error(DecodeError(
- expected: "4 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 4 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "4 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 4 elements",
+ found: "Int",
+ )))
}
pub fn tuple5_test() {
@@ -516,14 +534,17 @@ if erlang {
|> dynamic.from
|> dynamic.tuple5
|> should.equal(Error(DecodeError(
- expected: "5 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 5 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.tuple5
- |> should.equal(Error(DecodeError(expected: "5 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 5 elements",
+ found: "Int",
+ )))
}
pub fn typed_tuple5_test() {
@@ -570,8 +591,8 @@ if erlang {
dynamic.int,
)
|> should.equal(Error(DecodeError(
- expected: "5 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 5 elements",
+ found: "Tuple of 2 elements",
)))
1
@@ -583,7 +604,10 @@ if erlang {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(expected: "5 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 5 elements",
+ found: "Int",
+ )))
}
pub fn tuple6_test() {
@@ -615,14 +639,17 @@ if erlang {
|> dynamic.from
|> dynamic.tuple6
|> should.equal(Error(DecodeError(
- expected: "6 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 6 elements",
+ found: "Tuple of 2 elements",
)))
1
|> dynamic.from
|> dynamic.tuple6
- |> should.equal(Error(DecodeError(expected: "6 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 6 elements",
+ found: "Int",
+ )))
}
pub fn typed_tuple6_test() {
@@ -673,8 +700,8 @@ if erlang {
dynamic.int,
)
|> should.equal(Error(DecodeError(
- expected: "6 element tuple",
- found: "2 element tuple",
+ expected: "Tuple of 6 elements",
+ found: "Tuple of 2 elements",
)))
1
@@ -687,7 +714,10 @@ if erlang {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(expected: "6 element tuple", found: "Int")))
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of 6 elements",
+ found: "Int",
+ )))
}
pub fn map_test() {
@@ -745,7 +775,7 @@ if erlang {
|> dynamic.result
|> should.equal(Error(DecodeError(
expected: "result tuple",
- found: "2 element tuple",
+ found: "Tuple of 2 elements",
)))
}