diff --git a/.run/a.ark.run.xml b/.run/a.ark.run.xml
index 2b60e9a3b..0a85746a5 100644
--- a/.run/a.ark.run.xml
+++ b/.run/a.ark.run.xml
@@ -1,15 +1,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92d5dfe52..18c36832a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,12 +7,49 @@
### Added
- new builtin to extract the bytes of a string: builtin__string:bytes
-- math:fromBase to convert a number from a given base to base 10
-- string:surrogate? and string:privateUse? to check if an unicode character is in one of those planes
+- new builtins `builtin__time:asUTCDate` and `builtin__time:parseDate` to convert a timestamp to a date, and parse a string date as a timestamp
+- standard library
+ - math:fromBase to convert a number from a given base to base 10
+ - string:surrogate? and string:privateUse? to check if a Unicode character is in one of those planes
+ - list:findAfter to search for an element in a list after a given index
+ - list:findIf to search for an element in a list using a predicate
+ - list:search to search for a set of contiguous elements in a list
+ - list:minMax to get the minimum and maximum values of a list of numbers in a single call
+ - list:sorted? to check if a list is sorted
+ - list:lowerBound, list:upperBound
+ - list:binarySearch
+ - list:shiftLeft, list:shiftRight
+ - datetime library
+ - timezoneOffsets
+ - utcOffsetMinutes
+ - makeUTCTimestamp
+ - timestamps: year0, year1970, year200
+ - toUTCTimestamp
+ - asUTCDate
+ - plusSeconds, minusSeconds
+ - plusMinutes, minusMinutes
+ - plusHours, minusHours
+ - plusDays, minusDays
+ - plusWeeks, minusWeeks
+ - plusMonths, minusMonths
+ - plusYears, minusYears
+ - atStartOfDay, atEndOfDay
+ - today, yesterday, tomorrow
+ - nextDay, previousDay
+ - delta, asDelta
+ - asSeconds
+ - plusDelta, minusDelta
+ - year, month, day, hour, minute, second, millisecond, dayOfWeek, dayOfYear
+ - leapYear?
+ - monthLength, yearLength
+ - utcOffsetRepr
+ - asISO8601
+ - parse, parseAs
### Changed
- math:toBase handles 0 correctly
- math:countDigits returns 1 for 0
+- builtin__list:find can take an optional third argument, to specify at which index to start looking from
### Removed
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e670bc31c..c0ceb416f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
-project(ark CXX)
+project(ark CXX C)
# ArkScript version (have to manually update it)
set(ARK_VERSION_MAJOR 4)
@@ -170,6 +170,11 @@ endif ()
add_subdirectory("${ark_SOURCE_DIR}/thirdparties/ankerl_unordered_dense" EXCLUDE_FROM_ALL)
target_link_libraries(ArkReactor PUBLIC unordered_dense::unordered_dense)
+add_library(newlib STATIC ${ark_SOURCE_DIR}/thirdparties/newlib/src/gmtime_r.c)
+target_include_directories(newlib SYSTEM PUBLIC ${ark_SOURCE_DIR}/thirdparties/newlib/include)
+target_include_directories(ArkReactor SYSTEM PUBLIC ${ark_SOURCE_DIR}/thirdparties/newlib/include)
+target_link_libraries(ArkReactor PRIVATE newlib)
+
if (UNIX OR LINUX)
find_package(Threads)
target_link_libraries(ArkReactor PRIVATE ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp
index 15a998ba0..955e43774 100644
--- a/include/Ark/Builtins/Builtins.hpp
+++ b/include/Ark/Builtins/Builtins.hpp
@@ -62,6 +62,8 @@ namespace Ark::internal::Builtins
namespace Time
{
ARK_BUILTIN(timeSinceEpoch);
+ ARK_BUILTIN(timestampToDate);
+ ARK_BUILTIN(parseDate);
}
namespace System
diff --git a/lib/std b/lib/std
index 8b5af97d9..275187817 160000
--- a/lib/std
+++ b/lib/std
@@ -1 +1 @@
-Subproject commit 8b5af97d9f46202c724f4b8a1aab9eb196d01680
+Subproject commit 2751878171e8c9effb8214c763768bba4932c35d
diff --git a/src/arkreactor/Builtins/Builtins.cpp b/src/arkreactor/Builtins/Builtins.cpp
index 0e7e73f1c..d0084b2bb 100644
--- a/src/arkreactor/Builtins/Builtins.cpp
+++ b/src/arkreactor/Builtins/Builtins.cpp
@@ -51,6 +51,8 @@ namespace Ark::internal::Builtins
// Time
{ "time", Value(Time::timeSinceEpoch) },
+ { "builtin__time:asUTCDate", Value(Time::timestampToDate) },
+ { "builtin__time:parseDate", Value(Time::parseDate) },
// System
{ "builtin__sys:platform", platform },
diff --git a/src/arkreactor/Builtins/List.cpp b/src/arkreactor/Builtins/List.cpp
index bc84143e0..f52b58f62 100644
--- a/src/arkreactor/Builtins/List.cpp
+++ b/src/arkreactor/Builtins/List.cpp
@@ -5,6 +5,7 @@
#include
#include
+#include
namespace Ark::internal::Builtins::List
{
@@ -22,14 +23,25 @@ namespace Ark::internal::Builtins::List
Value findInList(std::vector& n, VM* vm [[maybe_unused]])
{
- if (!types::check(n, ValueType::List, ValueType::Any))
+ if (!types::check(n, ValueType::List, ValueType::Any) &&
+ !types::check(n, ValueType::List, ValueType::Any, ValueType::Number))
throw types::TypeCheckingError(
"list:find",
- { { types::Contract { { types::Typedef("list", ValueType::List), types::Typedef("value", ValueType::Any) } } } },
+ { { types::Contract {
+ { types::Typedef("list", ValueType::List),
+ types::Typedef("value", ValueType::Any) } },
+ types::Contract {
+ { types::Typedef("list", ValueType::List),
+ types::Typedef("value", ValueType::Any),
+ types::Typedef("index", ValueType::Number) } } } },
n);
- if (const auto it = std::ranges::find(n[0].list(), n[1]); it != n[0].list().end())
- return Value(static_cast(std::distance(n[0].list().begin(), it)));
+ const long offset = n.size() == 3 ? static_cast(n[2].number()) : 0L;
+ if (std::cmp_less(offset, n[0].list().size()))
+ {
+ if (const auto it = std::ranges::find(n[0].list().begin() + offset, n[0].list().end(), n[1]); it != n[0].list().end())
+ return Value(static_cast(std::distance(n[0].list().begin() + offset, it) + offset));
+ }
return Value(-1);
}
@@ -50,8 +62,9 @@ namespace Ark::internal::Builtins::List
if (!types::check(n, ValueType::Number, ValueType::Any))
throw types::TypeCheckingError(
"list:fill",
- { { types::Contract { { types::Typedef("size", ValueType::Number),
- types::Typedef("value", ValueType::Any) } } } },
+ { { types::Contract {
+ { types::Typedef("size", ValueType::Number),
+ types::Typedef("value", ValueType::Any) } } } },
n);
const auto c = static_cast(n[0].number());
@@ -68,9 +81,10 @@ namespace Ark::internal::Builtins::List
if (!types::check(n, ValueType::List, ValueType::Number, ValueType::Any))
throw types::TypeCheckingError(
"list:setAt",
- { { types::Contract { { types::Typedef("list", ValueType::List),
- types::Typedef("index", ValueType::Number),
- types::Typedef("value", ValueType::Any) } } } },
+ { { types::Contract {
+ { types::Typedef("list", ValueType::List),
+ types::Typedef("index", ValueType::Number),
+ types::Typedef("value", ValueType::Any) } } } },
n);
auto& list = n[0].list();
diff --git a/src/arkreactor/Builtins/Time.cpp b/src/arkreactor/Builtins/Time.cpp
index b5d907e0a..d5eef01ae 100644
--- a/src/arkreactor/Builtins/Time.cpp
+++ b/src/arkreactor/Builtins/Time.cpp
@@ -1,9 +1,13 @@
#include
+#include
+#include
+#include
+
#undef abs
#include
-#include
+#include
namespace Ark::internal::Builtins::Time
{
@@ -15,6 +19,7 @@ namespace Ark::internal::Builtins::Time
* =end
* @author https://github.com/SuperFola
*/
+ // cppcheck-suppress constParameterReference
Value timeSinceEpoch(std::vector& n [[maybe_unused]], VM* vm [[maybe_unused]])
{
const auto now = std::chrono::system_clock::now();
@@ -22,4 +27,84 @@ namespace Ark::internal::Builtins::Time
const auto microseconds = std::chrono::duration_cast(epoch);
return Value(static_cast(microseconds.count()) / 1000000);
}
+
+ Value timestampToDate(std::vector& n, VM* vm [[maybe_unused]])
+ {
+ if (!types::check(n, ValueType::Number))
+ throw types::TypeCheckingError(
+ "asUTCDate",
+ { { types::Contract {
+ { types::Typedef("timestamp", ValueType::Number) } } } },
+ n);
+
+ nl_tm calendar_time {};
+ nl_gmtime_r(static_cast(n[0].number()), &calendar_time);
+
+ int week = calendar_time.tm_wday;
+ if (week == 0) // Sunday
+ week = 6;
+ else
+ --week; // 0-5: Monday-Saturday
+
+ internal::Dict dict;
+ const double ms = n[0].number() - static_cast(static_cast(n[0].number()));
+
+ dict.set(Value("millisecond"), Value(static_cast(1000.0 * ms)));
+ dict.set(Value("second"), Value(calendar_time.tm_sec));
+ dict.set(Value("minute"), Value(calendar_time.tm_min));
+ dict.set(Value("hour"), Value(calendar_time.tm_hour));
+ dict.set(Value("day"), Value(calendar_time.tm_mday));
+ dict.set(Value("month"), Value(calendar_time.tm_mon + 1));
+ dict.set(Value("year"), Value(calendar_time.tm_year + 1900));
+ dict.set(Value("week_day"), Value(week));
+ dict.set(Value("year_day"), Value(calendar_time.tm_yday));
+ dict.set(Value("is_dst"), calendar_time.tm_isdst ? True : False);
+
+ return Value(std::move(dict));
+ }
+
+ int64_t makeTimestamp(const int tm_sec, const int tm_min, const int tm_hour, const int tm_mday, const int tm_mon, const int tm_year)
+ {
+ constexpr int MonthsPerYear = 12;
+ static const std::array cumulative_days = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+ const long year = 1900 + tm_year + tm_mon / MonthsPerYear;
+ int64_t result = (year - 1970) * 365 + cumulative_days[static_cast(tm_mon % MonthsPerYear)];
+ result += (year - 1968) / 4;
+ result -= (year - 1900) / 100;
+ result += (year - 1600) / 400;
+ if ((year % 4) == 0 &&
+ ((year % 100) != 0 || (year % 400) == 0) &&
+ (tm_mon % MonthsPerYear) < 2)
+ result--;
+ result += tm_mday - 1;
+ result *= 24;
+ result += tm_hour;
+ result *= 60;
+ result += tm_min;
+ result *= 60;
+ result += tm_sec;
+ return result;
+ }
+
+ Value parseDate(std::vector& n, VM* vm [[maybe_unused]])
+ {
+ if (!types::check(n, ValueType::String) && !types::check(n, ValueType::String, ValueType::String))
+ throw types::TypeCheckingError(
+ "parseDate",
+ { { types::Contract {
+ { types::Typedef("date", ValueType::String) } },
+ types::Contract {
+ { types::Typedef("date", ValueType::String),
+ types::Typedef("format", ValueType::String) } } } },
+ n);
+
+ std::tm t = {};
+ std::istringstream ss(n[0].string());
+ ss >> std::get_time(&t, n.size() == 1 ? "%Y-%m-%dT%H:%M:%S" : n[1].string().c_str());
+
+ if (ss.fail())
+ return Nil;
+ return Value(makeTimestamp(t.tm_sec, t.tm_min, t.tm_hour, t.tm_mday, t.tm_mon, t.tm_year));
+ }
}
diff --git a/tests/unittests/TestsHelper.cpp b/tests/unittests/TestsHelper.cpp
index 89c01a17b..261445de4 100644
--- a/tests/unittests/TestsHelper.cpp
+++ b/tests/unittests/TestsHelper.cpp
@@ -28,7 +28,7 @@ void updateExpectedFile(const TestData& data, const std::string& actual)
f.close();
}
}
-
+
void iterTestFiles(const std::string& folder, std::function&& test, IterTestFilesParam&& params)
{
boost::ut::test(folder) = [&] {
diff --git a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected
index feea5ee20..0aa2c278f 100644
--- a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected
+++ b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected
@@ -37,7 +37,7 @@ page_0
LOAD_CONST 3
LOAD_FAST 4
LOAD_FAST 4
- CALL_BUILTIN 27, 3
+ CALL_BUILTIN 29, 3
.L7:
CALL_BUILTIN 9, 1
.L6:
@@ -50,7 +50,7 @@ page_0
PUSH_RETURN_ADDRESS L9
LOAD_CONST 4
LOAD_FAST 4
- CALL_BUILTIN 27, 2
+ CALL_BUILTIN 29, 2
.L9:
CALL_BUILTIN 9, 1
.L8:
diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected
index 8999a7f5d..d4e04adcf 100644
--- a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected
+++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected
@@ -62,7 +62,7 @@ page_1
LOAD_FAST_BY_INDEX 0
LOAD_FAST_BY_INDEX 1
LOAD_FAST_BY_INDEX 2
- CALL_BUILTIN 27, 4
+ CALL_BUILTIN 29, 4
.L1:
CALL_BUILTIN 9, 1
.L0:
diff --git a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected
index 908810d01..af666e403 100644
--- a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected
+++ b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected
@@ -1,7 +1,5 @@
page_0
PUSH_RETURN_ADDRESS L0
- BUILTIN 87
- BUILTIN 88
BUILTIN 89
BUILTIN 90
BUILTIN 91
@@ -25,6 +23,8 @@ page_0
BUILTIN 109
BUILTIN 110
BUILTIN 111
+ BUILTIN 112
+ BUILTIN 113
CALL_BUILTIN 9, 25
.L0:
POP 0
diff --git a/tests/unittests/resources/CompilerSuite/ir/plugin.expected b/tests/unittests/resources/CompilerSuite/ir/plugin.expected
index 77c9c2126..1ee1867de 100644
--- a/tests/unittests/resources/CompilerSuite/ir/plugin.expected
+++ b/tests/unittests/resources/CompilerSuite/ir/plugin.expected
@@ -8,7 +8,7 @@ page_0
.L1:
EQ 0
LOAD_CONST 3
- CALL_BUILTIN 26, 2
+ CALL_BUILTIN 28, 2
.L0:
POP 0
HALT 0
diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected
index 2131676dc..2227e444b 100644
--- a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected
+++ b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected
@@ -2,7 +2,7 @@ page_0
LOAD_CONST_STORE 0, 0
LOAD_CONST_STORE 1, 2
LOAD_CONST_STORE 2, 4
- BUILTIN 22
+ BUILTIN 24
STORE 6
STORE_FROM 8, 7
STORE_FROM 10, 9
@@ -37,7 +37,7 @@ page_0
LOAD_CONST 6
LOAD_FAST 13
LOAD_FAST 13
- CALL_BUILTIN 27, 3
+ CALL_BUILTIN 29, 3
.L10:
CALL_BUILTIN 9, 1
.L9:
@@ -47,7 +47,7 @@ page_0
PUSH_RETURN_ADDRESS L12
LOAD_CONST 7
LOAD_FAST 13
- CALL_BUILTIN 27, 2
+ CALL_BUILTIN 29, 2
.L12:
CALL_BUILTIN 9, 1
.L11:
@@ -58,19 +58,19 @@ page_0
HALT 0
page_1
- CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 23, 1
+ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 25, 1
.L0:
RET 0
HALT 0
page_2
- CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 24, 1
+ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 26, 1
.L1:
RET 0
HALT 0
page_3
- CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 25, 1
+ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 27, 1
.L2:
RET 0
HALT 0
diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected
index 8565951cf..3aa746ef3 100644
--- a/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected
+++ b/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected
@@ -5,7 +5,7 @@ page_0
HALT 0
page_1
- CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 58, 1
+ CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 60, 1
.L0:
RET 0
HALT 0
diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/backtrace_builtin.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/backtrace_builtin.expected
index a0322a85f..31ff04fd6 100644
--- a/tests/unittests/resources/DiagnosticsSuite/runtime/backtrace_builtin.expected
+++ b/tests/unittests/resources/DiagnosticsSuite/runtime/backtrace_builtin.expected
@@ -6,16 +6,16 @@ Signature
Arguments
→ `list' (expected List), got "live" (String)
-In file /lib/std/List.ark:63
- 60 | # (list:sort [4 2 3]) # [1 2 4]
- 61 | # =end
- 62 | # @author https://github.com/SuperFola
- 63 | (let sort (fun (_L) (builtin__list:sort _L)))
+In file /lib/std/List.ark:206
+ 203 | # (list:sort [4 2 3]) # [1 2 4]
+ 204 | # =end
+ 205 | # @author https://github.com/SuperFola
+ 206 | (let sort (fun (_L) (builtin__list:sort _L)))
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 64 |
- 65 | # @brief Generate a List of n copies of an element
+ 207 |
+ 208 | # @brief Check if a List is sorted
-[ 2] In function `list:sort' (/lib/std/List.ark:63)
+[ 2] In function `list:sort' (/lib/std/List.ark:206)
[ 1] In global scope (tests/unittests/resources/DiagnosticsSuite/runtime/backtrace_builtin.ark:3)
Current scope variables values:
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.ark
new file mode 100644
index 000000000..ead7929bc
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.ark
@@ -0,0 +1 @@
+(builtin__time:parseDate 1)
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.expected
new file mode 100644
index 000000000..a8b447094
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.expected
@@ -0,0 +1,19 @@
+Function parseDate expected between 1 argument and 2 arguments
+Call
+ ↳ (parseDate 1)
+Signature
+ ↳ (parseDate date)
+Arguments
+ → `date' (expected String), got 1 (Number)
+
+Alternative 2:
+Signature
+ ↳ (parseDate date format)
+Arguments
+ → `date' (expected String), got 1 (Number)
+ → `format' (expected String) was not provided
+
+In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_num.ark:1
+ 1 | (builtin__time:parseDate 1)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ 2 |
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.ark
new file mode 100644
index 000000000..0d9a15b85
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.ark
@@ -0,0 +1 @@
+(builtin__time:parseDate "1" 1)
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.expected
new file mode 100644
index 000000000..8a5f2c9f2
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.expected
@@ -0,0 +1,20 @@
+Function parseDate expected between 1 argument and 2 arguments
+Call
+ ↳ (parseDate "1" 1)
+Signature
+ ↳ (parseDate date)
+Arguments
+ → `date' (expected String) ✓
+ → unexpected additional args: 1 (Number)
+
+Alternative 2:
+Signature
+ ↳ (parseDate date format)
+Arguments
+ → `date' (expected String) ✓
+ → `format' (expected String), got 1 (Number)
+
+In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timeparsedate_str_num.ark:1
+ 1 | (builtin__time:parseDate "1" 1)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 2 |
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.ark
new file mode 100644
index 000000000..c3adabbcc
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.ark
@@ -0,0 +1 @@
+(builtin__time:asUTCDate "1" true)
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.expected
new file mode 100644
index 000000000..9cb7bf462
--- /dev/null
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.expected
@@ -0,0 +1,13 @@
+Function asUTCDate expected 1 argument but got 2
+Call
+ ↳ (asUTCDate "1" true)
+Signature
+ ↳ (asUTCDate timestamp)
+Arguments
+ → `timestamp' (expected Number), got "1" (String)
+ → unexpected additional args: true (Bool)
+
+In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_timetodate.ark:1
+ 1 | (builtin__time:asUTCDate "1" true)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 2 |
diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/listfind_str_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/listfind_str_num.expected
index a557a293c..e631a6332 100644
--- a/tests/unittests/resources/DiagnosticsSuite/typeChecking/listfind_str_num.expected
+++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/listfind_str_num.expected
@@ -1,4 +1,4 @@
-Function list:find expected 2 arguments
+Function list:find expected between 2 arguments and 3 arguments
Call
↳ (list:find "hello" 1)
Signature
@@ -7,6 +7,14 @@ Arguments
→ `list' (expected List), got "hello" (String)
→ `value' (expected any) ✓
+Alternative 2:
+Signature
+ ↳ (list:find list value index)
+Arguments
+ → `list' (expected List), got "hello" (String)
+ → `value' (expected any) ✓
+ → `index' (expected Number) was not provided
+
In file tests/unittests/resources/DiagnosticsSuite/typeChecking/listfind_str_num.ark:1
1 | (builtin__list:find "hello" 1)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/thirdparties/README.md b/thirdparties/README.md
index d5b7ec046..5454bdd3d 100644
--- a/thirdparties/README.md
+++ b/thirdparties/README.md
@@ -9,5 +9,6 @@ Includes
* [picosha2](https://github.com/okdshin/PicoSHA2), MIT License
* [replxx](https://github.com/AmokHuginnsson/replxx/blob/master/LICENSE.md), MIT License + specifities
* [ut](https://github.com/boost-ext/ut), BSL 1.0
+* newlib, BSD & MIT License ([COPYING.NEWLIB](https://sourceware.org/git/?p=newlib-cygwin.git;a=blob_plain;f=COPYING.NEWLIB;h=2bf6f0bed40635a9398b5a4d9b0828d59bf1213f;hb=HEAD))
All used by [Ark](https://github.com/ArkScript-lang/Ark)
diff --git a/thirdparties/newlib/include/newlib/gmtime_r.h b/thirdparties/newlib/include/newlib/gmtime_r.h
new file mode 100644
index 000000000..c597fb56e
--- /dev/null
+++ b/thirdparties/newlib/include/newlib/gmtime_r.h
@@ -0,0 +1,14 @@
+#ifndef NEWLIB_GMTIME_R_H
+#define NEWLIB_GMTIME_R_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct nl_tm* nl_gmtime_r(const long long tim_p, struct nl_tm* __restrict res);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NEWLIB_GMTIME_R_H
diff --git a/thirdparties/newlib/include/newlib/local.h b/thirdparties/newlib/include/newlib/local.h
new file mode 100644
index 000000000..706efe63d
--- /dev/null
+++ b/thirdparties/newlib/include/newlib/local.h
@@ -0,0 +1,37 @@
+#ifndef NEWLIB_LOCAL_H
+#define NEWLIB_LOCAL_H
+
+/* local header used by libc/time routines */
+#include
+
+#define SECSPERMIN 60L
+#define MINSPERHOUR 60L
+#define HOURSPERDAY 24L
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
+#define DAYSPERWEEK 7
+#define MONSPERYEAR 12
+
+#define YEAR_BASE 1900
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY 4
+#define EPOCH_YEARS_SINCE_LEAP 2
+#define EPOCH_YEARS_SINCE_CENTURY 70
+#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
+
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+struct nl_tm
+{
+ int tm_hour;
+ int tm_min;
+ int tm_sec;
+ int tm_wday;
+ int tm_yday;
+ int tm_year;
+ int tm_mon;
+ int tm_mday;
+ int tm_isdst;
+};
+
+#endif // NEWLIB_LOCAL_H
diff --git a/thirdparties/newlib/src/gmtime_r.c b/thirdparties/newlib/src/gmtime_r.c
new file mode 100644
index 000000000..f94b94194
--- /dev/null
+++ b/thirdparties/newlib/src/gmtime_r.c
@@ -0,0 +1,98 @@
+/*
+ * gmtime_r.c
+ * Original Author: Adapted from tzcode maintained by Arthur David Olson.
+ * Modifications:
+ * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
+ * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov
+ * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov
+ * - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin
+ * - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
+ * solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
+ *
+ * - Use faster algorithm from civil_from_days() by Howard Hinnant - 12/06/2014,
+ * Freddie Chopin
+ *
+ * Converts the calendar time pointed to by tim_p into a broken-down time
+ * expressed as local time. Returns a pointer to a structure containing the
+ * broken-down time.
+ */
+
+#include
+
+/* Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
+ * day of a 400-year long "era", right after additional day of leap year.
+ * This adjustment is required only for date calculation, so instead of
+ * modifying time_t value (which would require 64-bit operations to work
+ * correctly) it's enough to adjust the calculated number of days since epoch.
+ */
+#define EPOCH_ADJUSTMENT_DAYS 719468L
+/* year to which the adjustment was made */
+#define ADJUSTED_EPOCH_YEAR 0
+/* 1st March of year 0 is Wednesday */
+#define ADJUSTED_EPOCH_WDAY 3
+/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
+#define DAYS_PER_ERA 146097L
+/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
+#define DAYS_PER_CENTURY 36524L
+/* there is one leap year every 4 years */
+#define DAYS_PER_4_YEARS (3 * 365 + 366)
+/* number of days in a non-leap year */
+#define DAYS_PER_YEAR 365
+/* number of days in January */
+#define DAYS_IN_JANUARY 31
+/* number of days in non-leap February */
+#define DAYS_IN_FEBRUARY 28
+/* number of years per era */
+#define YEARS_PER_ERA 400
+
+struct nl_tm*
+nl_gmtime_r(const long long lcltime,
+ struct nl_tm* __restrict res)
+{
+ long long days, rem;
+ long long era, weekday, year;
+ unsigned erayear, yearday, month, day;
+ unsigned long eraday;
+
+ days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
+ rem = lcltime % SECSPERDAY;
+ if (rem < 0)
+ {
+ rem += SECSPERDAY;
+ --days;
+ }
+
+ /* compute hour, min, and sec */
+ res->tm_hour = (int)(rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ res->tm_min = (int)(rem / SECSPERMIN);
+ res->tm_sec = (int)(rem % SECSPERMIN);
+
+ /* compute day of week */
+ if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
+ weekday += DAYSPERWEEK;
+ res->tm_wday = weekday;
+
+ /* compute year, month, day & day of year */
+ /* for description of this algorithm see
+ * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
+ era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
+ eraday = days - era * DAYS_PER_ERA; /* [0, 146096] */
+ erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
+ eraday / (DAYS_PER_ERA - 1)) /
+ 365; /* [0, 399] */
+ yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0, 365] */
+ month = (5 * yearday + 2) / 153; /* [0, 11] */
+ day = yearday - (153 * month + 2) / 5 + 1; /* [1, 31] */
+ month += month < 10 ? 2 : -10;
+ year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
+
+ res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ? yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) : yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + isleap(erayear);
+ res->tm_year = year - YEAR_BASE;
+ res->tm_mon = month;
+ res->tm_mday = day;
+
+ res->tm_isdst = 0;
+
+ return (res);
+}