From 739e157750bd366764925849f722d3b47290e225 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Fri, 28 Feb 2025 14:17:08 +0100 Subject: [PATCH] feat(builtins): turning random into a builtin --- CHANGELOG.md | 1 + README.md | 5 +-- examples/games/more-or-less.ark | 5 +-- include/Ark/Builtins/Builtins.hpp | 2 ++ lib/modules | 2 +- src/arkreactor/Builtins/Builtins.cpp | 1 + src/arkreactor/Builtins/Mathematics.cpp | 36 +++++++++++++++++++ tests/fuzzing/arkscript.dict | 1 + .../examples_games_more-or-less.ark | 5 +-- .../examples_games_more-or-less.ark | 5 +-- .../corpus/examples_games_more-or-less.ark | 5 +-- .../resources/LangSuite/builtins-tests.ark | 10 ++++++ 12 files changed, 57 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5cc2036a..c238efde7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - compile time argument count check for `and` and `or` - basic dead code elimination in the AST optimizer - new operator `@@` to get elements in list of lists / list of strings +- new builtin `random`, returning a random number between INT_MIN and INT_MAX, or in a custom range ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/README.md b/README.md index f9b8e48f3..af43fda63 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,7 @@ Also, it has: ## More or less game ```clojure -(import std.random) -(import std.Math) - -(let number (mod (math:abs (random)) 10000)) +(let number (random 0 10000)) (let game (fun () { (let impl (fun (tries) { diff --git a/examples/games/more-or-less.ark b/examples/games/more-or-less.ark index c06f792bc..70c9ae070 100644 --- a/examples/games/more-or-less.ark +++ b/examples/games/more-or-less.ark @@ -1,7 +1,4 @@ -(import random) -(import std.Math) - -(let number (mod (math:abs (random)) 10000)) +(let number (random 0 10000)) (let game (fun () { (let impl (fun (tries) { diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp index 24f228c49..12555a702 100644 --- a/include/Ark/Builtins/Builtins.hpp +++ b/include/Ark/Builtins/Builtins.hpp @@ -108,6 +108,8 @@ namespace Ark::internal::Builtins Value acosh_(std::vector& n, VM* vm); // math:acosh, 1 argument Value asinh_(std::vector& n, VM* vm); // math:asinh, 1 argument Value atanh_(std::vector& n, VM* vm); // math:atanh, 1 argument + + Value random(std::vector& n, VM* vm); // random, 0-2 args } namespace Async diff --git a/lib/modules b/lib/modules index 81a698c85..8d6d36bef 160000 --- a/lib/modules +++ b/lib/modules @@ -1 +1 @@ -Subproject commit 81a698c8570588485879915f6facd86e5a299d4d +Subproject commit 8d6d36bef18cc972b2f5a3b2e00e59cdec62c768 diff --git a/src/arkreactor/Builtins/Builtins.cpp b/src/arkreactor/Builtins/Builtins.cpp index 27602e5a1..4166787f2 100644 --- a/src/arkreactor/Builtins/Builtins.cpp +++ b/src/arkreactor/Builtins/Builtins.cpp @@ -87,6 +87,7 @@ namespace Ark::internal::Builtins { "math:acosh", Value(Mathematics::acosh_) }, { "math:asinh", Value(Mathematics::asinh_) }, { "math:atanh", Value(Mathematics::atanh_) }, + { "random", Value(Mathematics::random) }, // Async { "async", Value(Async::async) }, diff --git a/src/arkreactor/Builtins/Mathematics.cpp b/src/arkreactor/Builtins/Mathematics.cpp index e9539019f..25a7c9853 100644 --- a/src/arkreactor/Builtins/Mathematics.cpp +++ b/src/arkreactor/Builtins/Mathematics.cpp @@ -1,6 +1,7 @@ #define _USE_MATH_DEFINES #include #include +#include #include @@ -385,4 +386,39 @@ namespace Ark::internal::Builtins::Mathematics return Value(std::atanh(n[0].number())); } + + /** + * @name random + * @brief Compute a random number in [-2147483648, 2147483647] or in a custom range passed to the function + * @param min optional inclusive lower bound + * @param max optional inclusive upper bound. Must be present if `min` is passed + * =begin + * (print (random)) # a number in [-2147483648, 2147483647] + * (print (random 0 10)) # a number between 0 and 10 + * =end + * @author https://github.com/SuperFola + */ + Value random(std::vector& n, VM* vm [[maybe_unused]]) + { + static std::mt19937 gen { std::random_device()() }; + + if (n.size() == 2 && !types::check(n, ValueType::Number, ValueType::Number)) + types::generateError( + "random", + { { types::Contract { + { types::Typedef("min", ValueType::Number), types::Typedef("max", ValueType::Number) } } } }, + n); + + if (n.size() == 2) + { + const auto inclusive_min = static_cast(n[0].number()), + inclusive_max = static_cast(n[1].number()); + + std::uniform_int_distribution<> distrib(inclusive_min, inclusive_max); + return Value(distrib(gen)); + } + + const auto x = static_cast(gen()); + return Value(x); + } } diff --git a/tests/fuzzing/arkscript.dict b/tests/fuzzing/arkscript.dict index 29e3bc3ac..b4fadb914 100644 --- a/tests/fuzzing/arkscript.dict +++ b/tests/fuzzing/arkscript.dict @@ -80,3 +80,4 @@ "math:arccos" "math:arcsin" "math:arctan" +"random" diff --git a/tests/fuzzing/corpus-cmin-tmin/examples_games_more-or-less.ark b/tests/fuzzing/corpus-cmin-tmin/examples_games_more-or-less.ark index b152ce98f..1fedda75e 100644 --- a/tests/fuzzing/corpus-cmin-tmin/examples_games_more-or-less.ark +++ b/tests/fuzzing/corpus-cmin-tmin/examples_games_more-or-less.ark @@ -1,7 +1,4 @@ -(import random) -(import std.Math) - -(let number (mod (math:abs (random)) 10000)) +(let number (random 0 10000)) (let game (fun () { (let impl (fun (tries) { diff --git a/tests/fuzzing/corpus-cmin/examples_games_more-or-less.ark b/tests/fuzzing/corpus-cmin/examples_games_more-or-less.ark index c06f792bc..70c9ae070 100644 --- a/tests/fuzzing/corpus-cmin/examples_games_more-or-less.ark +++ b/tests/fuzzing/corpus-cmin/examples_games_more-or-less.ark @@ -1,7 +1,4 @@ -(import random) -(import std.Math) - -(let number (mod (math:abs (random)) 10000)) +(let number (random 0 10000)) (let game (fun () { (let impl (fun (tries) { diff --git a/tests/fuzzing/corpus/examples_games_more-or-less.ark b/tests/fuzzing/corpus/examples_games_more-or-less.ark index c06f792bc..70c9ae070 100644 --- a/tests/fuzzing/corpus/examples_games_more-or-less.ark +++ b/tests/fuzzing/corpus/examples_games_more-or-less.ark @@ -1,7 +1,4 @@ -(import random) -(import std.Math) - -(let number (mod (math:abs (random)) 10000)) +(let number (random 0 10000)) (let game (fun () { (let impl (fun (tries) { diff --git a/tests/unittests/resources/LangSuite/builtins-tests.ark b/tests/unittests/resources/LangSuite/builtins-tests.ark index 46947cb8f..53fac9a88 100644 --- a/tests/unittests/resources/LangSuite/builtins-tests.ark +++ b/tests/unittests/resources/LangSuite/builtins-tests.ark @@ -1,4 +1,5 @@ (import std.Testing) +(import std.List) (let foo (fun () ())) (let closure (fun (&foo) ())) @@ -30,6 +31,15 @@ (sys:sleep 1) (test:expect (< old (time))) + (mut rands []) + (mut i 0) + (while (< i 100) { + (append! rands (random 0 10)) + (set i (+ 1 i)) }) + (test:expect + (not (list:any rands (fun (e) (or (< e 0) (> e 10))))) + "should not find any number outside the given range") + # no need to test the math functions since they're 1:1 binding of C++ functions and were carefully checked # before writing this comment, to ensure we aren't binding math:sin to the C++ tan function