From fe4364f06efb824b19108b91ecdc3bdcc74c9293 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 19 Sep 2022 23:13:45 +0200 Subject: [PATCH 01/42] chore: updating lib/std version Former-commit-id: 35e9ab78b972aafec4426ddc831308afde661e12 --- lib/std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std b/lib/std index acf7d32ec..fc8e2e9a7 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit acf7d32ecc8befe245f6757574e917315264b708 +Subproject commit fc8e2e9a74f78b19666b3fb5032d902b7ce6cc2c From 2b53b3a661d8f483d2a45e8fc9ade55d43505beb Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Wed, 21 Sep 2022 21:41:11 +0200 Subject: [PATCH 02/42] ci: launch all the std lib tests one after another Former-commit-id: 26e031b67eb8cba1bd984d74a78b1ef3dd452269 --- .github/launch-tests | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/launch-tests b/.github/launch-tests index 3dc16694d..b6845d94c 100755 --- a/.github/launch-tests +++ b/.github/launch-tests @@ -15,7 +15,9 @@ else fi $ark tests/arkscript/unittests.ark --lib lib/ || exit 1 -$ark lib/std/tests/all.ark --lib lib/ || exit 1 +for file in lib/std/tests/*.ark; do + $ark $file --lib lib/ +done ######################################### # Integration tests From f8f73c7c5b13af8826f7b994e16deb70bd4be98d Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Thu, 13 Oct 2022 21:16:04 +0200 Subject: [PATCH 03/42] refacto: moving Ark/Module.hpp inside Ark repository Former-commit-id: 6d1532403ab4f9959d4662c531cdd39b238defad --- include/Ark/Module.hpp | 51 ++++++++++++++++++++++++++++++++++++++++++ lib/modules | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 include/Ark/Module.hpp diff --git a/include/Ark/Module.hpp b/include/Ark/Module.hpp new file mode 100644 index 000000000..9667f2251 --- /dev/null +++ b/include/Ark/Module.hpp @@ -0,0 +1,51 @@ +#ifndef INCLUDE_ARK_MODULE_HPP +#define INCLUDE_ARK_MODULE_HPP + +#include +#include +#include + +namespace Ark +{ + struct mapping + { + char* name; + Value (*value)(std::vector&, Ark::VM*); + }; + + inline mapping* mapping_create(std::size_t len) + { + mapping* map = new mapping[len + 1]; + map[len].name = nullptr; + map[len].value = nullptr; + return map; + } + + inline void mapping_add(mapping& map, const std::string& name, Value (*val)(std::vector&, Ark::VM*)) + { + map.name = new char[1 + name.size()]; + for (std::size_t i = 0; i < name.size(); map.name[i] = name[i], ++i) + ; + map.name[name.size()] = 0; + + map.value = val; + } +} + +#undef ARK_API + +#ifdef ARK_OS_WINDOWS +// Windows compilers need specific (and different) keywords for export and import +# define ARK_API extern "C" __declspec(dllexport) +#else // Linux, FreeBSD, Mac OS X +# if __GNUC__ >= 4 +// GCC 4 has special keywords for showing/hidding symbols, +// the same keyword is used for both importing and exporting +# define ARK_API extern "C" __attribute__((__visibility__("default"))) +# else +// GCC < 4 has no mechanism to explicitely hide symbols, everything's exported +# define ARK_API extern "C" +# endif +#endif + +#endif diff --git a/lib/modules b/lib/modules index ead871d48..55723b7c9 160000 --- a/lib/modules +++ b/lib/modules @@ -1 +1 @@ -Subproject commit ead871d48641edb0e4d33252b82bf6c21ce94d58 +Subproject commit 55723b7c982d0e7d1291007bd65d411bd750d028 From a4dfab2201d3e1554d6a1f75ff732805ff97d9f8 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 16 Oct 2022 14:56:38 +0200 Subject: [PATCH 04/42] docs: updating the module creation tutorial Former-commit-id: 1eb368cdb5a1be368fa6eacf8eeb8d0f209098dc --- CHANGELOG.md | 14 ++++++++++++++ docs/tutorial_modules.md | 34 ++++++++++++++-------------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21afeb710..b83903483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## [Unreleased version] +### Added + + +### Changed +- plugins can be constructed from outside ArkScript lib/modules folder, easing the development process +- plugins loading now works as intended: look alongside the given file/bytecode file, then in the std lib folder + +### Removed + + +### Deprecated + + ## [3.4.0] - 2022-09-12 ### Added - added new `async` and `await` builtins diff --git a/docs/tutorial_modules.md b/docs/tutorial_modules.md index 7d382b8d7..ffec96bc7 100644 --- a/docs/tutorial_modules.md +++ b/docs/tutorial_modules.md @@ -18,15 +18,15 @@ Create a `Main.cpp` file in `module_name/src/` with the following content: ~~~~{.cpp} #include -Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) +Ark::Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) { - return Value(1); + return Ark::Value(1); } -ARK_API mapping* getFunctionsMapping() +ARK_API Ark::mapping* getFunctionsMapping() { - mapping* map = mapping_create(1); - mapping_add(map[0], "test:foo", foo); + Ark::mapping* map = Ark::mapping_create(1); + Ark::apping_add(map[0], "test:foo", foo); return map; } @@ -34,27 +34,21 @@ ARK_API mapping* getFunctionsMapping() Let's walk through this line by line: - `#include ` includes basic files from ArkScript to be able to use the VM, instanciate values, and generate the entry point of the module -- `Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) {...}` defines a function for our module, taking an argument list from the VM, and a non-owning pointer to the VM -- `ARK_API mapping* getFunctionsMapping()` declares the entrypoint of our module -- `mapping* map = mapping_create(1);` creates a mapping of a single element to hold the name -> function pointer association, defining the module -- `mapping_add(map[0], "test:foo", foo);` adds an element at position 0 in our mapping, using the previously defining function - - note that the given name is `"test:foo"`: this is a convention in ArkScript, every module function must be prefixed by the module name's +- `Ark::Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) {...}` defines a function for our module, taking an argument list from the VM, and a non-owning pointer to the VM +- `ARK_API Ark::mapping* getFunctionsMapping()` declares the entrypoint of our module +- `Ark::mapping* map = Ark::mapping_create(1);` creates a mapping of a single element to hold the name -> function pointer association, defining the module +- `Ark::mapping_add(map[0], "test:foo", foo);` adds an element at position 0 in our mapping, using the previously defining function + - note that the given name is `"test:foo"`: this is a convention in ArkScript, every module's function must be prefixed by the module name ## Building your module -Clone ArkScript, then copy your modules fork to lib/modules. This is required for CMake to be able to find ArkScript headers. - -You will need to update `lib/modules/CMakeLists.txt` to add the following code: +Clone ArkScript wherever you like. Then, you will need to update your CMakeLists.txt to add the following code: ~~~~{cmake} -set(ARK_MOD_MODULE_NAME Off CACHE BOOL "Build the module_name module") - -if (ARK_MOD_MODULE_NAME OR ARK_MOD_ALL) - add_subdirectory(${PROJECT_SOURCE_DIR}/module_name) -endif() +add_subdirectory(path/to/arkscript/ Ark) ~~~~ -Then, run `cmake . -Build -DARK_BUILD_MODULES=On -DARK_MOD_MODULE_NAME=On`, and build only your module with `cmake --build build --target module_name`. +Then, run `cmake . -Build`, and build your module with `cmake --build build`. It should output a `.arkm` file in the current working directory. ## Common problems @@ -78,7 +72,7 @@ Handle& get_me_a_window_handle() Ark::UserType::ControlFuncs* get_cfs_window() { static Ark::UserType::ControlFuncs cfs; - cfs.ostream_func = [](std::ostream& os, const UserType& a) -> std::ostream& { + cfs.ostream_func = [](std::ostream& os, const Ark::UserType& a) -> std::ostream& { // do stuff return os; }; From 752b56e94ee40de799ffc7327adc2d8534507d2a Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 16 Oct 2022 14:57:22 +0200 Subject: [PATCH 05/42] fix: changing the lookup algorithm for modules Former-commit-id: 9e358d4070301880be8eee2e73342566524e9e48 --- .vscode/launch.json | 18 +++++++++--------- src/arkreactor/VM/State.cpp | 2 +- src/arkreactor/VM/VM.cpp | 36 ++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2d879a2b4..a6258b14d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,8 +5,8 @@ "version": "0.2.0", "configurations": [ { - "name": "(Win) REPL", - "type": "cppvsdbg", + "name": "REPL", + "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", "args": [], @@ -15,11 +15,11 @@ "environment": [ {"name": "ARKSCRIPT_PATH", "value": "${workspaceFolder}/lib"} ], - "console": "integratedTerminal" + "MIMode": "lldb" }, { - "name": "(Win) Unit Tests", - "type": "cppvsdbg", + "name": "Unit Tests", + "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", "args": ["tests/arkscript/unittests.ark"], @@ -28,11 +28,11 @@ "environment": [ {"name": "ARKSCRIPT_PATH", "value": "${workspaceFolder}/lib"} ], - "console": "integratedTerminal" + "MIMode": "lldb" }, { - "name": "(Win) Lib tests", - "type": "cppvsdbg", + "name": "Lib tests", + "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", "args": ["lib/std/tests/all.ark"], @@ -41,7 +41,7 @@ "environment": [ {"name": "ARKSCRIPT_PATH", "value": "${workspaceFolder}/lib"} ], - "console": "integratedTerminal" + "MIMode": "lldb" } ] } \ No newline at end of file diff --git a/src/arkreactor/VM/State.cpp b/src/arkreactor/VM/State.cpp index a6477a0e5..b27ca513c 100644 --- a/src/arkreactor/VM/State.cpp +++ b/src/arkreactor/VM/State.cpp @@ -48,7 +48,6 @@ namespace Ark bcr.feed(bytecode_filename); m_bytecode = bcr.bytecode(); - m_filename = bytecode_filename; configure(); } catch (const std::exception& e) @@ -115,6 +114,7 @@ namespace Ark << termcolor::reset; return false; } + m_filename = file; // check if it's a bytecode file or a source code file BytecodeReader bcr; diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 3bf8ff79b..230463ae7 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -97,27 +97,27 @@ namespace Ark path = (fs::path(m_state.m_filename).parent_path() / fs::path(file)).relative_path().string(); std::shared_ptr lib; - for (auto const& v : m_state.m_libenv) + // if it exists alongside the .arkc file + if (Utils::fileExists(path)) + lib = std::make_shared(path); + else { - std::string lib_path = (fs::path(v) / fs::path(file)).string(); + for (auto const& v : m_state.m_libenv) + { + std::string lib_path = (fs::path(v) / fs::path(file)).string(); - // if it's already loaded don't do anything - if (std::find_if(m_shared_lib_objects.begin(), m_shared_lib_objects.end(), [&](const auto& val) { - return (val->path() == path || val->path() == lib_path); - }) != m_shared_lib_objects.end()) - return; + // if it's already loaded don't do anything + if (std::find_if(m_shared_lib_objects.begin(), m_shared_lib_objects.end(), [&](const auto& val) { + return (val->path() == path || val->path() == lib_path); + }) != m_shared_lib_objects.end()) + return; - // if it exists alongside the .arkc file - if (Utils::fileExists(path)) - { - lib = std::make_shared(path); - break; - } - // check in lib_path otherwise - else if (Utils::fileExists(lib_path)) - { - lib = std::make_shared(lib_path); - break; + // check in lib_path + if (Utils::fileExists(lib_path)) + { + lib = std::make_shared(lib_path); + break; + } } } From 0155aebf0f69b311ee8e732d3596464a0cebd15d Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 17 Oct 2022 21:57:39 +0200 Subject: [PATCH 06/42] modules: new way of declaring modules, easier to use and to understand Former-commit-id: 3bf0eedafb5d45340c4b0a6265a03c4cc8d840ac --- CHANGELOG.md | 1 + docs/tutorial_modules.md | 13 +++++++++---- include/Ark/Module.hpp | 20 +------------------- lib/modules | 2 +- src/arkreactor/VM/VM.cpp | 6 ------ 5 files changed, 12 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b83903483..aa349a840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Changed - plugins can be constructed from outside ArkScript lib/modules folder, easing the development process - plugins loading now works as intended: look alongside the given file/bytecode file, then in the std lib folder +- new way to create modules, easier to use ### Removed diff --git a/docs/tutorial_modules.md b/docs/tutorial_modules.md index ffec96bc7..7821dfc96 100644 --- a/docs/tutorial_modules.md +++ b/docs/tutorial_modules.md @@ -25,8 +25,12 @@ Ark::Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_ ARK_API Ark::mapping* getFunctionsMapping() { - Ark::mapping* map = Ark::mapping_create(1); - Ark::apping_add(map[0], "test:foo", foo); + static Ark::mapping map[] = { + { "test:foo", foo }, + + // sentinel + { nullptr, nullptr } + }; return map; } @@ -36,9 +40,10 @@ Let's walk through this line by line: - `#include ` includes basic files from ArkScript to be able to use the VM, instanciate values, and generate the entry point of the module - `Ark::Value foo(std::vector& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) {...}` defines a function for our module, taking an argument list from the VM, and a non-owning pointer to the VM - `ARK_API Ark::mapping* getFunctionsMapping()` declares the entrypoint of our module -- `Ark::mapping* map = Ark::mapping_create(1);` creates a mapping of a single element to hold the name -> function pointer association, defining the module -- `Ark::mapping_add(map[0], "test:foo", foo);` adds an element at position 0 in our mapping, using the previously defining function +- `static Ark::mapping map[] = {...};` creates a mapping of elements to hold the name -> function pointer association, defining the module +- `{ "test:foo", foo }` adds an element at position 0 in our mapping, using the previously defining function - note that the given name is `"test:foo"`: this is a convention in ArkScript, every module's function must be prefixed by the module name +- `{ nullptr, nullptr }`: a sentinel so that the virtual machine where the end of mapping is ## Building your module diff --git a/include/Ark/Module.hpp b/include/Ark/Module.hpp index 9667f2251..8f511e33c 100644 --- a/include/Ark/Module.hpp +++ b/include/Ark/Module.hpp @@ -9,27 +9,9 @@ namespace Ark { struct mapping { - char* name; + const char* name; Value (*value)(std::vector&, Ark::VM*); }; - - inline mapping* mapping_create(std::size_t len) - { - mapping* map = new mapping[len + 1]; - map[len].name = nullptr; - map[len].value = nullptr; - return map; - } - - inline void mapping_add(mapping& map, const std::string& name, Value (*val)(std::vector&, Ark::VM*)) - { - map.name = new char[1 + name.size()]; - for (std::size_t i = 0; i < name.size(); map.name[i] = name[i], ++i) - ; - map.name[name.size()] = 0; - - map.value = val; - } } #undef ARK_API diff --git a/lib/modules b/lib/modules index 55723b7c9..6e27609e2 160000 --- a/lib/modules +++ b/lib/modules @@ -1 +1 @@ -Subproject commit 55723b7c982d0e7d1291007bd65d411bd750d028 +Subproject commit 6e27609e29f2725c806481793878fc979be9d317 diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 230463ae7..6bf7014f9 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -157,14 +157,8 @@ namespace Ark if (it != m_state.m_symbols.end()) (*context.locals[0]).push_back(static_cast(std::distance(m_state.m_symbols.begin(), it)), Value(map[i].value)); - // free memory because we have used it and don't need it anymore - // no need to free map[i].value since it's a pointer to a function in the DLL - delete[] map[i].name; ++i; } - - // free memory - delete[] map; } void VM::exit(int code) noexcept From 63ddd82059fc6ed5576ec830f1b3baed03581524 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 17 Oct 2022 22:22:43 +0200 Subject: [PATCH 07/42] chore: update lib/modules Former-commit-id: a1582021dcc941ac27d79cb318576e8ca08d6d23 --- lib/modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules b/lib/modules index 6e27609e2..0c99e073c 160000 --- a/lib/modules +++ b/lib/modules @@ -1 +1 @@ -Subproject commit 6e27609e29f2725c806481793878fc979be9d317 +Subproject commit 0c99e073c53e9b3d73589ebeb1d7052b728c45b4 From b63c0b62249da7fbdbfaa6b30ba6cc1ad96815e6 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 23 Oct 2022 17:52:57 +0200 Subject: [PATCH 08/42] feat: adding specific AFL blocks to make fuzzing easier Former-commit-id: 2047245b4d1e1f356b3530cbe5d12c400872b41a --- src/arkreactor/VM/State.cpp | 2 ++ src/arkreactor/VM/VM.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/arkreactor/VM/State.cpp b/src/arkreactor/VM/State.cpp index b27ca513c..97c963ec1 100644 --- a/src/arkreactor/VM/State.cpp +++ b/src/arkreactor/VM/State.cpp @@ -251,8 +251,10 @@ namespace Ark // checking integrity for (std::size_t j = 0; j < picosha2::k_digest_size; ++j) { +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (hash[j] != m_bytecode[i]) throwStateError("Integrity check failed"); +#endif ++i; } diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 6bf7014f9..80c19acfc 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -1157,12 +1157,20 @@ namespace Ark std::printf("%s\n", e.what()); backtrace(context); m_exit_code = 1; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + throw; +#endif } catch (...) { std::printf("Unknown error\n"); backtrace(context); m_exit_code = 1; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + throw; +#endif } if (m_state.m_debug_level > 0) From c0fe5368b74227c697087c3260e4381f12451ccd Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 23 Oct 2022 17:53:27 +0200 Subject: [PATCH 09/42] feat: adding a fuzzing corpus and scripts to make fuzzing easier Former-commit-id: 83c26e01885c4e57656c7dab371763aac8bf1305 --- .gitignore | 1 + fuzzing/build.sh | 12 +++ fuzzing/corpus/99bottles.ark | 22 ++++++ fuzzing/corpus/ackermann.ark | 24 ++++++ fuzzing/corpus/callbacks.ark | 30 ++++++++ fuzzing/corpus/closures.ark | 49 ++++++++++++ fuzzing/corpus/collatz.ark | 23 ++++++ fuzzing/corpus/counter.ark | 9 +++ fuzzing/corpus/error.ark | 25 +++++++ fuzzing/corpus/events-tests.arkc | Bin 0 -> 8067 bytes fuzzing/corpus/exceptions-tests.arkc | Bin 0 -> 2344 bytes fuzzing/corpus/factorial.ark | 18 +++++ fuzzing/corpus/fibo.ark | 11 +++ fuzzing/corpus/functional-tests.arkc | Bin 0 -> 2490 bytes fuzzing/corpus/lazy-tests.arkc | Bin 0 -> 2290 bytes fuzzing/corpus/list-tests.arkc | Bin 0 -> 9862 bytes fuzzing/corpus/macros-tests.arkc | Bin 0 -> 2450 bytes fuzzing/corpus/macros.ark | 107 +++++++++++++++++++++++++++ fuzzing/corpus/math-tests.arkc | Bin 0 -> 6710 bytes fuzzing/corpus/quicksort.ark | 59 +++++++++++++++ fuzzing/corpus/quote.ark | 5 ++ fuzzing/corpus/range-tests.arkc | Bin 0 -> 5581 bytes fuzzing/corpus/string-tests.arkc | Bin 0 -> 4960 bytes fuzzing/corpus/sum_digits.ark | 26 +++++++ fuzzing/corpus/switch-tests.arkc | Bin 0 -> 2292 bytes fuzzing/corpus/tests-tools.arkc | Bin 0 -> 1520 bytes fuzzing/fuzz.sh | 10 +++ fuzzing/gen_corpus.sh | 18 +++++ 28 files changed, 449 insertions(+) create mode 100644 fuzzing/build.sh create mode 100644 fuzzing/corpus/99bottles.ark create mode 100644 fuzzing/corpus/ackermann.ark create mode 100644 fuzzing/corpus/callbacks.ark create mode 100644 fuzzing/corpus/closures.ark create mode 100644 fuzzing/corpus/collatz.ark create mode 100644 fuzzing/corpus/counter.ark create mode 100644 fuzzing/corpus/error.ark create mode 100644 fuzzing/corpus/events-tests.arkc create mode 100644 fuzzing/corpus/exceptions-tests.arkc create mode 100644 fuzzing/corpus/factorial.ark create mode 100644 fuzzing/corpus/fibo.ark create mode 100644 fuzzing/corpus/functional-tests.arkc create mode 100644 fuzzing/corpus/lazy-tests.arkc create mode 100644 fuzzing/corpus/list-tests.arkc create mode 100644 fuzzing/corpus/macros-tests.arkc create mode 100644 fuzzing/corpus/macros.ark create mode 100644 fuzzing/corpus/math-tests.arkc create mode 100644 fuzzing/corpus/quicksort.ark create mode 100644 fuzzing/corpus/quote.ark create mode 100644 fuzzing/corpus/range-tests.arkc create mode 100644 fuzzing/corpus/string-tests.arkc create mode 100644 fuzzing/corpus/sum_digits.ark create mode 100644 fuzzing/corpus/switch-tests.arkc create mode 100644 fuzzing/corpus/tests-tools.arkc create mode 100644 fuzzing/fuzz.sh create mode 100644 fuzzing/gen_corpus.sh diff --git a/.gitignore b/.gitignore index e73765ffb..7cc6ff44b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ cformat.ps1 include/Ark/Constants.hpp __arkscript__/ *.arkc +!fuzzing/**/*.arkc *.arkm /Installer.iss diff --git a/fuzzing/build.sh b/fuzzing/build.sh new file mode 100644 index 000000000..16365116b --- /dev/null +++ b/fuzzing/build.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash + +if [[ $(basename $(pwd)) != "Ark" ]]; then + echo "This script should be launched from ArkScript source folder" && exit 1 +fi + +buildFolder=afl + +cmake -B${buildFolder} \ + -DCMAKE_C_COMPILER=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ -DCMAKE_BUILD_TYPE=Release \ + -DARK_BUILD_EXE=On -DARK_BUILD_MODULE=On +cmake --build ${buildFolder} --config Release diff --git a/fuzzing/corpus/99bottles.ark b/fuzzing/corpus/99bottles.ark new file mode 100644 index 000000000..10f729852 --- /dev/null +++ b/fuzzing/corpus/99bottles.ark @@ -0,0 +1,22 @@ +# Lyrics from the song: +# +# 99 bottles of beer on the wall +# 99 bottles of beer +# Take one down, pass it around +# 98 bottles of beer on the wall +# +# 98 bottles of beer on the wall +# 98 bottles of beer +# Take one down, pass it around +# 97 bottles of beer on the wall + + +(let arg (if (>= (len sys:args) 1) (toNumber (@ sys:args 0)) nil)) +(let i (if (nil? arg) 100 arg)) + +(mut n i) +(while (> n 1) { + (print (str:format "%% Bottles of beer on the wall\n%% bottles of beer\nTake one down, pass it around" n n)) + (set n (- n 1)) + (print (str:format "%% Bottles of beer on the wall." n)) +}) diff --git a/fuzzing/corpus/ackermann.ark b/fuzzing/corpus/ackermann.ark new file mode 100644 index 000000000..b855f2858 --- /dev/null +++ b/fuzzing/corpus/ackermann.ark @@ -0,0 +1,24 @@ +# the Ackermann Peter function (see https://en.wikipedia.org/wiki/Ackermann_function) +# One of the simplest and earliest-discovered examples of a total computable function, +# that is not primitive. All primitive recursive functions are total and computable, +# but the Ackermann function illustrates that not all total computable functions +# are primitive recursive. +# Due to its definitions in terms of extremely deep recursion, it can be used as a +# benchmark of a compiler's ability to optimize recursion, which is the reason why +# we are using this function to benchmark the language. + +(let ackermann (fun (m n) { + (if (> m 0) + # then + (if (= 0 n) + # then + (ackermann (- m 1) 1) + # else + (ackermann (- m 1) (ackermann m (- n 1))) + ) + # else + (+ 1 n) + ) +})) + +(print "Ackermann-Péter function, m=3, n=6: " (ackermann 3 6)) diff --git a/fuzzing/corpus/callbacks.ark b/fuzzing/corpus/callbacks.ark new file mode 100644 index 000000000..d4383a187 --- /dev/null +++ b/fuzzing/corpus/callbacks.ark @@ -0,0 +1,30 @@ +# a function which just prints its argument +(let egg (fun (bar) (print bar))) +# the data we're going to give to this function +(let data ["Iron Man" "is" "Tony Stark"]) +# a list of function call which should be executed later on +(mut callbacks []) + +(print "Data: " data) +(print "Generating callbacks") +(mut acc 0) +# here we are filling the list callbacks +(while (!= acc (len data)) { + (mut d (@ data acc)) + # by putting in it closures that captured d, an element of `data` + # and call the function egg on it + (set callbacks (append callbacks (fun (&d) (egg d)))) + (set acc (+ 1 acc)) +}) + +# then we reset the accumulator +(set acc 0) +(while (!= acc (len callbacks)) { + # we print what was stored in the closure using dot notation + (print "stored: " (@ callbacks acc) .d) + # and then we call the closure itself (be careful: (@ callbacks acc) only returns the callback, + # thus we need to put it in another pair of parens to call the callback) + (puts "Calling callback number " acc ": ") + ((@ callbacks acc)) + (set acc (+ 1 acc)) +}) diff --git a/fuzzing/corpus/closures.ark b/fuzzing/corpus/closures.ark new file mode 100644 index 000000000..82f9032aa --- /dev/null +++ b/fuzzing/corpus/closures.ark @@ -0,0 +1,49 @@ +# Inspired by +# Closures and object are equivalent: http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent + +# this will construct a closure capturing the 3 arguments, plus a function to set the age +(let create-human (fun (name age weight) { + # functions can be invoked in the closure scope + (let set-age (fun (new-age) (set age new-age))) + + # the return value, our closure + # the &name notation is used in the argument list to explicitly capture + # a variable (using deep copy) + (fun (&set-age &name &age &weight) ()) +})) + +# we create 2 humans using such construction, just a nice function call +(let bob (create-human "Bob" 0 144)) +(let john (create-human "John" 12 15)) + +# using the dot notation on a closure object, we can have a **read only** access to its fields +(print "Bob's age: " bob.age) +# and even call the captured functions, which will enter the closure, and be given a **read write** access +# meaning that, even if the fields are read only (eg, we can not do (set bob.age 14)), the "object" can be modified +(print "Setting Bob's age to 10") +(bob.set-age 10) +# the age changed +(print "New age: " bob.age) + +# but john age didn't change, because we created 2 separated closures +(print "John's age, didn't change: " john.age) + + + +# Another example to simulate a python range(x, y) + +# this function will return a closure capturing the number given +# and modifying its value each time we'll call the closure, returning +# the new number +(let countdown-from (fun (number) + (fun (&number) { + (set number (- number 1)) + number + })) +) + +(let countdown-from-3 (countdown-from 3)) + +(print "Countdown " (countdown-from-3)) # 2 +(print "Countdown " (countdown-from-3)) # 1 +(print "Countdown " (countdown-from-3)) # 0 diff --git a/fuzzing/corpus/collatz.ark b/fuzzing/corpus/collatz.ark new file mode 100644 index 000000000..2828e7ab1 --- /dev/null +++ b/fuzzing/corpus/collatz.ark @@ -0,0 +1,23 @@ +# If the number is even, divide it by two. +# If the number is odd, triple it and add one. + +(let get? (fun (seq idx default) + (if (> (len seq) idx) + (@ seq idx) + default))) + +(let n (toNumber (get? sys:args 0 "10"))) + +(let collatz (fun (num) + (if (= 0 (mod num 2)) + (math:floor (/ num 2)) + (math:floor (+ 1 (* 3 num)))))) + +(mut a_i n) +(mut iterations 0) +(while (!= 1 a_i) { + (print a_i) + (set a_i (collatz a_i)) + (set iterations (+ 1 iterations))}) + +(print "Reached 1 in " iterations " iteration(s)") diff --git a/fuzzing/corpus/counter.ark b/fuzzing/corpus/counter.ark new file mode 100644 index 000000000..e1bf09230 --- /dev/null +++ b/fuzzing/corpus/counter.ark @@ -0,0 +1,9 @@ +(puts "\t\tHello world\nHere is a counter: ") +(mut i 0) +(while (<= i 100) { + # puts doesn't put a \n at the end of the string + (puts "\rHere is a counter: " i "/100") + (set i (+ 1 i)) + # sleep for 50ms + (sys:sleep 50) +}) diff --git a/fuzzing/corpus/error.ark b/fuzzing/corpus/error.ark new file mode 100644 index 000000000..a5197735e --- /dev/null +++ b/fuzzing/corpus/error.ark @@ -0,0 +1,25 @@ +# this is how we can import files in ArkScript +# very often, and this is a convention, +# if an imported file starts with a capital letter, +# it shall be a file in the standard library. +(import "Exceptions.ark") + +# the function which should do a "safe number invertion" +(let invert (fun (x) { + (if (= x 0) + # then + (throw "cannot divide by zero") # the value we should return in case of an error + # else + (return (/ 1 x)) # the value returned if everything is ok (if (!= x 0)) + ) +})) + +# this function (try) is implemented in Exceptions.ark (in lib/std/) +# and will check the return value of (invert 0) +# if it's an error (seen by the use of throw), it will call the second function, +# if it's a result, it will call the first +# it works the same way as a try: { function then } catch { do_something } +(try (invert 0) + (fun (inverted) (print inverted)) + (fun (err) (print err)) +) diff --git a/fuzzing/corpus/events-tests.arkc b/fuzzing/corpus/events-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..671fe7d24903236b226b8216d34b6fdf3babdc76 GIT binary patch literal 8067 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 literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/exceptions-tests.arkc b/fuzzing/corpus/exceptions-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..b90bf5c3f282100620a64172b89eab552f32e218 GIT binary patch literal 2344 zcmb_cOH&g;5bm914M}*1_j3_~AcmL4K|G zdi9@p^W@zh;LVd?Po5hMlvV6h@62|8{Y_7I@0ji>QJAVv{^7HcFWDFL`R9+FneV?3 zKfa&+u(YwcUB3U|>$gvHiFd!A#xG>X25e^{L z<*{F1$=b@U>+HG?WK6h9uFRWfiZ11tMG#v>nWKEk@k+Mb%$MwvOa6&ldPBJ~q2f6e5j8GHEO%lZ!^m!0KQpbGZA&c$7BM&kwhD zjcf*5($up_j(+39u z_FLCM|00jH^~mlYW_Mm9mtZ5>XOSh$}tmlUN{eWTTv7F={!$ z9=lRH$Sbm1_b7TXkVYXq$041NE=V_Nby$dcNaRh&Dfvdhy08k;IJz|6O1A`HFNt)m zQ4H=A==u;=ZI2N3Gu1||n29F5ryM+^(l>CNge#%m8tG~dV@As97T(Awj;|B4glKh6 z1)|jvy+E`&bOOQX& zpXKRArQ=Q2j)SPDi6)LV;tGeUZG|7hoktkb4RC}Mbzs@_H0!y+gFsu)H{=~;JI z*$0TD4`CZYgH=q$H$vmk4Fz-pxFG*Xi+wa;=VqMj2&bgiu&zj`$KImpi;WKd5Y+vGafM zKd`W}cE)i=D=Qm2dmAb({O!AsBw%Em;coW!-R}N=Z{NOs&vdtmk`zAWYj;OJy|ee; zjK6=8dHwmvul%doy|2Qai&U4MXI3To z((^qEBW#3(!>S*TWHhp3n-$wY#)R|a%Cfnm=<>E%2XU<~OH_7j&#~myvST?e{^~o9 zC#h7SQk6=(WXXz8rRP+t*^eysb;+$&ANo|WYCBXiskB9vCwAGdIksu#gKRzD#Bz87 zsyGfkCt0o1E^V1E?Qr@J@(l`KdWo{=yN2x*J+QK3YNL<-_cPx>ULNo-k9 zB#D_+%|>k1mC{CCQPmhx_l|=!1!0|rbU{u)x*)(6 z1NRAGp#QxybkCJjqQFb0BH|=&BL_JM3X|xmXaFW{Y@D0HEO+vZ>$7un@aePNr597SRBXCl>VC8eh zcDBtb{~)?izhFfd`ajpC#`~0IVes0!a-YB2__{={4sTRCY=cF4a zh`V(>j1}S98eDP1cVc0Ekbcy)57G}*)&&_Z6?e%19Ywx&Glf(KseK;!@Y^0b4ZCxJOCG8 zCF8J7Lr*}@Li25K0h$Y#2gL*g*EX&vrs8A*yP?MH4{USr@cl#hPpUVH`!_z|5%f3w zMes#=jDk0spL?5gJFRSj=G)B8SmtQP=0n@RunUSWs*#2oO#-E g7C|$rfN^iIYZ5NTpI2*%UA&dq(9P&c6%@Mv0I);!P5=M^ literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/lazy-tests.arkc b/fuzzing/corpus/lazy-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..d08e7c0e15a0b89f1b31b38b526622ccf7e9b056 GIT binary patch literal 2290 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 literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/macros-tests.arkc b/fuzzing/corpus/macros-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..34a90c5e27d8f44f75dd1e82127e9c3d2bf4550a GIT binary patch literal 2450 zcmb_dOLG%P5bhmGc4XPcZ-mWb42akUIg(7}5OTl?DqMxZV9N(i)Oa-#DptGB?iz|e zLUG6?m8u-L_T!tL?w*zMUl3&}{@8aO6bqjp z|EK3@G++;S!7OFqL#7IjPP`bpT_)3i;YrhX$_fsxE*#UHyz~&ka0B|6+TN=TleN zz893EJ%dmoDR!e)m6|G4)T&Wy!(cpX9l16sIw1S_xk?J(+OeWWhn#M+908M{+alg^ zUHLD%lp%{msU%lQrBzv$koLBA_P6iJ-|DjdWPfv8Hg=#TtGZq>A4G_u5s!8neNh?q z9)f;m~#(+ntx%l{_zD2h$b@0gF*G zGy$3fokQ|TCe}tO{hUbRF~+f@oOlZxJBHXn$-`K0E5}g>j!w5CvFW#M@5l|M*D|Rj zu1PX7ca#mu4L0u{({sc8xv1*Xg3Q$U%5mLOk-7vSemE(;_~*(NZvmRW*Uq2p!2%j(gO zBPHJKCu5ClXg@Zm(;zW2Sh=%=ylx;z&l2)S1BszMyMvbuWZ^6!Zx~2!-gmZV3Y{+W zbQ{-@uSyD2z_@USv%@?5JHmNoL6ZodM))~|SaiY6=nNztsaZgcVP%Y-b!)~xN1Q#+ zwxIdgCyE`VCFmCVbOL_;_{9PHg+4nEYqW2Y&tUxGfPJYiUeHB6PtsLnBIr`=E8?>q zW2FQC0B%=IImU71=z2{)_}hN`%Umg@LS-Qz{{X@L@pwpkHj~(g6F|K*^jAuf{p(rnQAB4HjFMg zyM=LBhNoo;m$W{6{C*fgsb*oGh0`*M)br}-HC-ESD)pPj0C*l$1o0?vi;AXwJOJFr lv{iYyEP*aU$@hjk&)r@?Y53nSeua%1H?BtH5>+lXe+3w2B**{& literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/macros.ark b/fuzzing/corpus/macros.ark new file mode 100644 index 000000000..f122ad426 --- /dev/null +++ b/fuzzing/corpus/macros.ark @@ -0,0 +1,107 @@ +!{suffix-dup (sym x) { + !{if (> x 1) + (suffix-dup sym (- x 1)) + } + (symcat sym x) +}} + +!{partial (func ...defargs) { + !{bloc (suffix-dup a (- (argcount func) (len defargs)))} + (fun (bloc) (func ...defargs bloc)) + !{undef bloc} +}} + +(let test_func (fun (a b c) (* a b c))) +(let test_func1 (partial test_func 1)) + +(print "Generated partial functions for test_func (a b c) => (* a b c)") +(print "Expected arguments for test_func: " (argcount test_func) ", expected " 3) +(print "Expected arguments for test_func1: " (argcount test_func1) ", expected " 2) +(print "Calling them: " (test_func 1 2 3) " " (test_func1 2 3)) + +!{foo (a b) (+ a b)} +(print "Using macro foo (a b) => (+ a b): " (foo 1 2)) + +!{var 12} +(print "Using macro constant var=12: " var) + +!{if (= var 12) + (print "This was executed in a if macro, testing var == 12") + (print "You shouldn't see this") +} + +!{if (and true true) + (print "This was executed in a if macro, testing (and true true)") + (print "You shouldn't see this (bis)") +} + +!{defun (name args body) (let name (fun args body))} +(defun a_func (a b) (+ a b)) +(print "Generated a function with a macro, a_func (a b) => (+ a b)") +(print "Calling (a_func 1 2): " (a_func 1 2)) + +!{one (...args) (print "Macro 'one', returns the 2nd argument given in " args " => " (@ args 1))} +(one 1 2) +(one 1 3 4) +(one 1 5 6 7 8) + +!{last (...args) (print "Macro 'last', returns the last argument given in " args " => " (@ args -1))} +(last 1 2) +(last 1 3 4) +(last 1 5 6 7 8) + +{ + (print "Testing macros in scopes and macro shadowing") + + !{test (+ 1 2 3)} + (print "(global) Reading macro 'test', expected 6, " test) + + ((fun () { + !{test (- 1 2 3)} + (print "(sub scope) Reading macro 'test', expected -4, " test) + })) + + (print "(global) Reading macro 'test', expected 6, " test) + + { + !{test 555} + (print "(subscope) Reading macro 'test', expected 555, " test) + !{undef test} + (print "(subscope, undef test) Reading macro 'test', expected 6, " test) + !{undef a} + } +} + +(print "Demonstrating a threading macro") + +!{-> (arg fn1 ...fn) { + !{if (> (len fn) 0) + (-> (fn1 arg) ...fn) + (fn1 arg) + } +}} + +(let filename "hello.json") + +(let f1 (fun (data) { + (print ">>f1 " data) + (+ data "-f1") +})) + +(let f2 (fun (data) { + (print ">>f2 " data) + (+ data "-f2") +})) + +(let f3 (fun (data) { + (print ">>f3 " data) + (+ data "-f3") +})) + +(let f4 (fun (data) { + (print ">>f4 " data) + (+ data "-f4") +})) + +(print "We expected calls to go like this: f1, f2, f3, f4") +(print (-> filename f1 f2 f3 f4)) # (f4 (f3 (f2 (f1 filename)))) \ No newline at end of file diff --git a/fuzzing/corpus/math-tests.arkc b/fuzzing/corpus/math-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..657139b414c4b67ebf244ebb78bcf218a12a4539 GIT binary patch literal 6710 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 literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/quicksort.ark b/fuzzing/corpus/quicksort.ark new file mode 100644 index 000000000..937bf2255 --- /dev/null +++ b/fuzzing/corpus/quicksort.ark @@ -0,0 +1,59 @@ +(import "List.ark") + +(let filter (fun (lst cond) { + (mut output []) + (mut i 0) + (while (< i (len lst)) { + (if (cond (@ lst i)) + (append! output (@ lst i))) + (set i (+ 1 i)) + }) + output +})) + +# a quicksort function in ArkScript, a lot smaller than its C++ version! +# and according to me, a lot simpler to understand +(let quicksort (fun (array) { + (if (empty? array) + # if the given list is empty, return it + [] + # otherwise, sort it + { + # the pivot will be the first element + (let pivot (head array)) + # call quicksort on a smaller array containing all the elements less than the pivot + (mut less (quicksort (filter (tail array) (fun (e) (< e pivot))))) + # and after that, call quicksort on a smaller array containing all the elements greater or equal to the pivot + (let more (quicksort (filter (tail array) (fun (e) (>= e pivot))))) + + (concat! less [pivot] more) + # return a concatenation of arrays + less + }) +})) + +# an unsorted list to sort +(let a [3 6 1 5 1 65 324 765 1 6 3 0 6 9 6 5 3 2 5 6 7 64 645 7 345 432 432 4 324 23]) + +# a benchmarking function, to see the difference between C++ sort and ArkScript quicksort +# obviously ArkScript will be a bit slower +(let bench (fun (name code) { + (mut start (time)) + (let rep 1000) + + (mut i 0) + (while (< i rep) { + (code) + (set i (+ 1 i)) + }) + + (let t (/ (* 1000 (- (time) start)) rep)) + (print name " average: " t "ms") + t +})) + +(print a) +# use a quoted argument to defer evaluation and be able to call it multiple times in a fresh context +(let ark (bench "ark" '(quicksort a))) +(let cpp (bench "cpp" '(list:sort a))) +(print "ratio ark/cpp: " (/ ark cpp)) diff --git a/fuzzing/corpus/quote.ark b/fuzzing/corpus/quote.ark new file mode 100644 index 000000000..357a06843 --- /dev/null +++ b/fuzzing/corpus/quote.ark @@ -0,0 +1,5 @@ +# quoting a block of code will create an anonymous function taking no arguments +# allowing us to defer its evaluation +(let test '{(let a 12) (print a)}) +# and call the quoted code block later on, using the function call syntax +(test) diff --git a/fuzzing/corpus/range-tests.arkc b/fuzzing/corpus/range-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..3d2053f9bbb50bad0bae970841b5bfc92f481937 GIT binary patch literal 5581 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 literal 0 HcmV?d00001 diff --git a/fuzzing/corpus/string-tests.arkc b/fuzzing/corpus/string-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..80c0589aab7f1d6157d0c53d3ec2c4ef1b15fb08 GIT binary patch literal 4960 zcmcgv*>V#{6ur}utgsl2F`EfwY!ItuEVBhWn+*mV3yN&F@XL@TB_|BsAS$}ALM zS0XozLWw0B5Q!hxqV&m4p1hRjr96NUa7+TX;xsi}vEkGKT&uffsrZf1_uPq!@A-j* zQ7{qt^ZvRUK=Mr_6S5#;C0C7#<@i=N}MMpYw(O|J$MLbvGzP6YGHO@P+? zR;ZZ}k1uL^4&jwIc8J=66}X`ri8>={)oZz;&hDr+ zAiEa0NWN7s!-znCW%ZZG2Ej_a?jwjrV?1JEKv94WBSXODg zVn+)1h|=|I>q@b8huHO27}={XcD8O0`{~#S1Xx$Lsq3e>WUr{Dq?&F2nXN0_t~d?5 z;cIob-i+RM3K`uL-id(b;HnbP`mpw4^^0;=D^>T|+Vk3rmtMW$H(v!|)Ox-C=IuLC zQ`6UP%-p;+J9qoe{M~yC_a7`SEw4O$^!Ulwa3r=+cYLyI>0VaPnH+oF54@_Vsu%d{ zw(oiF^HeM8aOH>*7Ap;Pf@M0LnMz`-06=81E~PBkjtp1e$H;1BMDIq@Ys?r>PI0oh zTEUk-22H#cHHkk#Z?^DH6hfKm2CkV6WG1fgkt;UZHh#vt$%~_Iq_-v4O-CkRCzHTawc>3yQ9j9(<#v&;dC?qo7E0>;}|e4h5`a z59m(NKG5Bu$3go*3!q0p&w?HWJr8;eva3wPt6(P2cxAMBw81r+=qlbsUhE z)_6ajSW^1|wG*F==970$pAQJF&)^y*2Vxnco7c5`a05?4ZX^ETP3K28oU>TXy@@@W z^#?bdAKjp@^FEk2*y?(pX%^T^m)DS%&1-Aywp%x1x7b#^Y8aWOxue=#?#*z>Xx9|D zbi-UZjC>zPg(neb-JO8(0?Glb31iJ35bpgN1bp>$Dc_)#c9? zIF-WoBKO=gJUM)`$Sm-TiyR_+qKlMETss}RK`ipmrF-IZ@;cozS)|TJ8?wO2!%Fhg zLM?zXf`wmS0B0s>hAB1{&xkhJ%(`LhnJJoUy1xW7Or+bgNng>S3_?R(z7j0lVNBy4 z%v^uGXaDqI-x1ZnVl(&_*pEarYAhvo$0dmN=~1cVGM@Q!N3hT8kCD09Mdp&m;=6@! zJSA7&gF_kKwVljW$b1SJGrE;rdk+qsX0vQ3GXWVE`gSt-St`7bv*a_#nkrH9`Frl9 zTq65L7uhd6WSN%jaKDOW`5#|4#&)vPkhzXEfkadO2G$I8h7fZOjhlGhoV(3Bw;(s$ zMQ0B9%;6l??Jlykg|dwI_O@^^F>l@EOq?0(J6Q8TFTg+gdl%1}{ksP~Wtq!vvI~&8 z|1V`9KyDGeLf@9OPCxEevJB4oINmGov4?g{#xdz8YeSAPdDsQ>k-jJCTMK= o 48) (<= o 57)) + (- o 48) + (if (and (>= o 97) (<= o 122)) + (- o 87) + (if (and (>= o 65) (<= 90)) + (- o 55) + o)))) + (mod v base) +})) + +(let sum-digits (fun (n base) { + (let number (if (not (= "String" (type n))) (toString n) n)) + (list:reduce + (list:map number (fun (e) (to-base e base))) + (fun (a b) (+ a b))) +})) + +(print (sum-digits 1 10)) # 1 +(print (sum-digits 1234 10)) # 10 +(print (sum-digits "fe" 16)) # 29 +(print (sum-digits "f0e" 16)) # 29 diff --git a/fuzzing/corpus/switch-tests.arkc b/fuzzing/corpus/switch-tests.arkc new file mode 100644 index 0000000000000000000000000000000000000000..967892e1c04a31e10981d285f9fdf766760e4edd GIT binary patch literal 2292 zcmc&!OHUI~6h8M(hf)ec9|8rDp;{@(AarA*8&MlE5*phQ!>Sp|1RMImnIf_>E;0TR z|AR|6uKf%C2Dg6a&U9#Nn#QDwy}7;Tp2v5dcP!_WC`A6R`uJ7qWBSck^V??R>hkla zA0G~X8HeY~OE34Iq<^QrfBAX!P|$+qy0+6c?RWIvs&DgOjvBV>S|>KOZMW@`uhE2x zPoA^`%I;A0fL9NA0As){a_p*grr^w`)c|msLT#@YkWUlj8@<$Td7@9ylg z2MsM6rdufX!RrG}_VT4t5Bk_J)xW^Q11-wMgYxbnLLMi1Y1iwm3zCL`O(M6|DU^(m zj9@#qYqv>eNgiFtM#4`#qI!efU3X+@mkL{U3r=wlnjsj@9 z3nq%O;UNhP?(j31^9o^OwlS#!5O@oiDxTRmC?2T6cGR_3ddUgQ;Xbnse<;>{`abAoMaSV@;LF3`B5x`-aKH91;Gew2`pO60CaCRT|oX=G}Z$Q_MT&HK;x>z=b-eDRp85RibN*r|U*HD+v$(}d1a1bgBG}tsR&e3}0L`+aHUIzs literal 0 HcmV?d00001 diff --git a/fuzzing/fuzz.sh b/fuzzing/fuzz.sh new file mode 100644 index 000000000..5f97ba036 --- /dev/null +++ b/fuzzing/fuzz.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +if [[ $(basename $(pwd)) != "Ark" ]]; then + echo "This script should be launched from ArkScript source folder" && exit 1 +fi + +buildFolder=afl + +sudo afl-system-config +afl-fuzz -i fuzzing/input -o fuzzing/output -s 0 -- ${buildFolder}/arkscript @@ -L ./lib diff --git a/fuzzing/gen_corpus.sh b/fuzzing/gen_corpus.sh new file mode 100644 index 000000000..41366228e --- /dev/null +++ b/fuzzing/gen_corpus.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +if [[ $(basename $(pwd)) != "Ark" ]]; then + echo "This script should be launched from ArkScript source folder" && exit 1 +fi + +buildFolder=afl + +afl-cmin -i fuzzing/corpus -o fuzzing/unique -- ${buildFolder}/arkscript @@ -L ./lib + +mkdir -p fuzzing/input +cd fuzzing/unique + +for i in *; do + afl-tmin -i "$i" -o "../input/$i" -- ../../${buildFolder}/arkscript @@ -L ../../lib +done + +cd ../.. From c58c8d9bf8f57924f3090c32662dc18074f3c185 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 23 Oct 2022 18:05:43 +0200 Subject: [PATCH 10/42] fix: setting shell scripts permissions Former-commit-id: 7694fd7e5223fbc73bc125b594dd3a7174888e38 --- fuzzing/build.sh | 2 +- fuzzing/fuzz.sh | 0 fuzzing/gen_corpus.sh | 0 3 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 fuzzing/build.sh mode change 100644 => 100755 fuzzing/fuzz.sh mode change 100644 => 100755 fuzzing/gen_corpus.sh diff --git a/fuzzing/build.sh b/fuzzing/build.sh old mode 100644 new mode 100755 index 16365116b..5abd537b5 --- a/fuzzing/build.sh +++ b/fuzzing/build.sh @@ -8,5 +8,5 @@ buildFolder=afl cmake -B${buildFolder} \ -DCMAKE_C_COMPILER=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ -DCMAKE_BUILD_TYPE=Release \ - -DARK_BUILD_EXE=On -DARK_BUILD_MODULE=On + -DARK_BUILD_EXE=On -DARK_BUILD_MODULES=On cmake --build ${buildFolder} --config Release diff --git a/fuzzing/fuzz.sh b/fuzzing/fuzz.sh old mode 100644 new mode 100755 diff --git a/fuzzing/gen_corpus.sh b/fuzzing/gen_corpus.sh old mode 100644 new mode 100755 From 5ae415a325e864556c68f1db27c7ef4d5a7a35d0 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 23 Oct 2022 18:15:04 +0200 Subject: [PATCH 11/42] chore: adding the corpus generated by AFL Former-commit-id: 908f14c011337a4636ecf2fcd8997ee2b0676c84 --- fuzzing/input/99bottles.ark | 1 + fuzzing/input/ackermann.ark | 1 + fuzzing/input/callbacks.ark | 1 + fuzzing/input/closures.ark | 1 + fuzzing/input/collatz.ark | 1 + fuzzing/input/error.ark | 1 + fuzzing/input/factorial.ark | 1 + fuzzing/input/macros.ark | 1 + fuzzing/input/quote.ark | 1 + fuzzing/input/sum_digits.ark | 1 + fuzzing/input/tests-tools.arkc | Bin 0 -> 5 bytes fuzzing/unique/99bottles.ark | 22 +++++++ fuzzing/unique/ackermann.ark | 24 +++++++ fuzzing/unique/callbacks.ark | 30 +++++++++ fuzzing/unique/closures.ark | 49 +++++++++++++++ fuzzing/unique/collatz.ark | 23 +++++++ fuzzing/unique/error.ark | 25 ++++++++ fuzzing/unique/factorial.ark | 18 ++++++ fuzzing/unique/macros.ark | 107 ++++++++++++++++++++++++++++++++ fuzzing/unique/quote.ark | 5 ++ fuzzing/unique/sum_digits.ark | 26 ++++++++ fuzzing/unique/tests-tools.arkc | Bin 0 -> 1520 bytes 22 files changed, 339 insertions(+) create mode 100644 fuzzing/input/99bottles.ark create mode 100644 fuzzing/input/ackermann.ark create mode 100644 fuzzing/input/callbacks.ark create mode 100644 fuzzing/input/closures.ark create mode 100644 fuzzing/input/collatz.ark create mode 100644 fuzzing/input/error.ark create mode 100644 fuzzing/input/factorial.ark create mode 100644 fuzzing/input/macros.ark create mode 100644 fuzzing/input/quote.ark create mode 100644 fuzzing/input/sum_digits.ark create mode 100644 fuzzing/input/tests-tools.arkc create mode 100644 fuzzing/unique/99bottles.ark create mode 100644 fuzzing/unique/ackermann.ark create mode 100644 fuzzing/unique/callbacks.ark create mode 100644 fuzzing/unique/closures.ark create mode 100644 fuzzing/unique/collatz.ark create mode 100644 fuzzing/unique/error.ark create mode 100644 fuzzing/unique/factorial.ark create mode 100644 fuzzing/unique/macros.ark create mode 100644 fuzzing/unique/quote.ark create mode 100644 fuzzing/unique/sum_digits.ark create mode 100644 fuzzing/unique/tests-tools.arkc diff --git a/fuzzing/input/99bottles.ark b/fuzzing/input/99bottles.ark new file mode 100644 index 000000000..883a99466 --- /dev/null +++ b/fuzzing/input/99bottles.ark @@ -0,0 +1 @@ +(let arg(if(>=(len sys:args)1)(toNumber(@ sys:args 0))nil))(let i(if(nil? arg)100 arg))(mut n i)(while(> n 1){(print(str:format"%%0000000000000%%000000000000000000000000000000000000000000000"n n))(set n(- n 1))(print(str:format"%%00000000000000000000000000000"n))}) \ No newline at end of file diff --git a/fuzzing/input/ackermann.ark b/fuzzing/input/ackermann.ark new file mode 100644 index 000000000..29459d432 --- /dev/null +++ b/fuzzing/input/ackermann.ark @@ -0,0 +1 @@ +(let a0kermann(fun(m n){(if(> m 0)(if(= 0 n)(a0kermann(- m 1)1)(a0kermann(- m 1)(a0kermann m(- n 1))))(+ 1 n))}))(print"0000000000000000000000000000000000"(a0kermann 3 6)) \ No newline at end of file diff --git a/fuzzing/input/callbacks.ark b/fuzzing/input/callbacks.ark new file mode 100644 index 000000000..5a45fec5d --- /dev/null +++ b/fuzzing/input/callbacks.ark @@ -0,0 +1 @@ +(let e00(fun(bar)(print bar)))(let data["00000000""00""0000000000"])(mut callbacks[])(print"000000"data)(print"00000000000000000000")(mut acc 0)(while(!= acc(len data)){(mut d(@ data acc))(set callbacks(append callbacks(fun(&d)(e00 d))))(set acc(+ 1 acc))})(set acc 0)(while(!= acc(len callbacks)){(print"0000000 "(@ callbacks acc).d)(puts"000000000000000000000000"acc"0 ")((@ callbacks acc))(set acc(+ 1 acc))}) \ No newline at end of file diff --git a/fuzzing/input/closures.ark b/fuzzing/input/closures.ark new file mode 100644 index 000000000..09817b983 --- /dev/null +++ b/fuzzing/input/closures.ark @@ -0,0 +1 @@ +(let create-human(fun(name a0e wei0ht){(let set-a0e(fun(new-a0e)(set a0e new-a0e)))(fun(&set-a0e&name&a0e&wei0ht)())}))(let bob(create-human"00"0 100))(let john(create-human"0n"12 15))(print"0000000"bob.a0e)(print"00000000000000000000000")(bob.set-a0e 10)(print"000000000"bob.a0e)(print"000000000000000000000000000"john.a0e)(let countdown-from(fun(number)(fun(&number){(set number(- number 1))number})))(let countdown-from-3(countdown-from 3))(print"0ountdown "(countdown-from-3))(print"0ountdown "(countdown-from-3))(print"0ountdown "(countdown-from-3)) \ No newline at end of file diff --git a/fuzzing/input/collatz.ark b/fuzzing/input/collatz.ark new file mode 100644 index 000000000..bba8632de --- /dev/null +++ b/fuzzing/input/collatz.ark @@ -0,0 +1 @@ +(let get0(fun(se0 idx default)(if(>(len se0)idx)(@ se0 idx)default)))(let n(toNumber(get0 sys:args 0"10")))(let collatz(fun(num)(if(= 0(mod num 2))(math:floor(/ num 2))(math:floor(+ 1(* 3 num))))))(mut a0i n)(mut iterations 0)(while(!= 1 a0i){(print a0i)(set a0i(collatz a0i))(set iterations(+ 1 iterations))})(print"0000"iterations"0000000000000") \ No newline at end of file diff --git a/fuzzing/input/error.ark b/fuzzing/input/error.ark new file mode 100644 index 000000000..cce0721cd --- /dev/null +++ b/fuzzing/input/error.ark @@ -0,0 +1 @@ +(import"Exceptions.ark")(let invert(fun(x){(if(= x 0)(throw"000000000000000000000")(return(/ 1 x)))}))(try(invert 0)(fun(inverte0)(print inverte0))(fun(err)(print err))) \ No newline at end of file diff --git a/fuzzing/input/factorial.ark b/fuzzing/input/factorial.ark new file mode 100644 index 000000000..58299544f --- /dev/null +++ b/fuzzing/input/factorial.ark @@ -0,0 +1 @@ +(let fa0t(fun(n){(mut a 1)(mut a00 2)(while(<= a00 n){(set a(* a a00))(set a00(+ 1 a00))})a}))(print"000000000000000000000000000000000"(fa0t 6)) \ No newline at end of file diff --git a/fuzzing/input/macros.ark b/fuzzing/input/macros.ark new file mode 100644 index 000000000..4c9b699fa --- /dev/null +++ b/fuzzing/input/macros.ark @@ -0,0 +1 @@ +!{suffix-dup(sym x){!{if(> x 1)(suffix-dup sym(- x 1))}(symcat sym x)}}!{partial(func...defargs){!{bloc(suffix-dup a(-(argcount func)(len defargs)))}(fun(bloc)(func...defargs bloc))!{undef c}}}(let test_func(fun(a b c)(* a b c)))(let test_func1(partial test_func 1))(print"0000000000000000000000000000000000000000000000000000")(print"00000000000000000000000000000000000"(argcount test_func)"0 expected "3)(print"0000000000000000000000000000000000 "(argcount test_func1)"0 expected "2)(print"00000000000000"(test_func 1 2 3)"0"(test_func1 2 3))!{foo(a b)(+ a b)}(print"0000000000000000000000000000000000"(foo 1 2))!{0ar 12}(print"00000000000000000000000000000"0ar)!{if(= 0ar 12)(print"00000000000000000000000000000000000000000000000000")(r"00000000000000")}!{if(and true true)(print"00000000000000000000000000000000000000000000000000000000")(r"00000000000000")}!{defun(name args body)(let name(fun args body))}(defun a_func(a b)(+ a b))(print"0000000000000000000000000000000000000000000000000000000000")(print"0000000000000000000000"(a_func 1 2))!{one(...args)(print"00000000000000000000000000000000000000000000000"args" => "(@ args 1))}(one 1 2)(one 1 3 4)(one 1 5 6 7 8)!{last(...args)(print"0000000000000000000000000000000000000000000000000"args" => "(@ args -1))}(last 1 2)(last 1 3 4)(last 1 5 6 7 8){(print"00000000000000000000000000000000000000000000")!{test(+ 1 2 3)}(print"(global) 0eading macro 0test00 expected 60 "test)((fun(){!{test(- 1 2 3)}(print"0000000000000000000000000000000000000000000000 "test)}))(print"(global) 0eading macro 0test00 expected 60 "test){!{test 500}(print"00000000000000000000000000000000000000000000500"test)!{undef test}(print"000000000000000000000000000000000000000000000000000000000"test)!{undef a}}}(print"0000000000000000000000000000000")!{->(arg fn1...fn){!{if(>(len fn)0)(->(fn1 arg)...fn)(fn1 arg)}}}(let filename"0000000000")(let f1(fun(data){(print"00000"data)(+ data"000")}))(let f2(fun(data){(print"0000 "data)(+ data"002")}))(let f3(fun(data){(print"00030"data)(+ data"003")}))(let f4(fun(data){(print"00040"data)(+ data"004")}))(print"0000000000000000000000000000000000000000000000004")(print(-> filename f1 f2 f3 f4)) \ No newline at end of file diff --git a/fuzzing/input/quote.ark b/fuzzing/input/quote.ark new file mode 100644 index 000000000..6761a665d --- /dev/null +++ b/fuzzing/input/quote.ark @@ -0,0 +1 @@ +(let te0t '{(let a 10)(print a)})(te0t) \ No newline at end of file diff --git a/fuzzing/input/sum_digits.ark b/fuzzing/input/sum_digits.ark new file mode 100644 index 000000000..5deb5f295 --- /dev/null +++ b/fuzzing/input/sum_digits.ark @@ -0,0 +1 @@ +(import"List.ark")(import"String.ark")(let to-base(fun(n base){(let o(str:ord n))(let v(if(and(>= o 40)(<= o 50))(- o 40)(if(and(>= o 0)(<= o 122))(- o 7)(if(and(= o 6)(= 9))(- o 5)o))))(mod v base)}))(let sum-digits(fun(n base){(let number(if(not(="String"(type n)))(toString n)n))(list:reduce(list:map number(fun(e)(to-base e base)))(fun(a b)(+ a b)))}))(print(sum-digits 1 10))(print(sum-digits 1000 10))(print(sum-digits"fe"6))(print(sum-digits"f0e"6)) \ No newline at end of file diff --git a/fuzzing/input/tests-tools.arkc b/fuzzing/input/tests-tools.arkc new file mode 100644 index 0000000000000000000000000000000000000000..cc7f725b4109a775399a7d0461d6a436d38732d0 GIT binary patch literal 5 McmYc+%4T2y00i>^KL7v# literal 0 HcmV?d00001 diff --git a/fuzzing/unique/99bottles.ark b/fuzzing/unique/99bottles.ark new file mode 100644 index 000000000..10f729852 --- /dev/null +++ b/fuzzing/unique/99bottles.ark @@ -0,0 +1,22 @@ +# Lyrics from the song: +# +# 99 bottles of beer on the wall +# 99 bottles of beer +# Take one down, pass it around +# 98 bottles of beer on the wall +# +# 98 bottles of beer on the wall +# 98 bottles of beer +# Take one down, pass it around +# 97 bottles of beer on the wall + + +(let arg (if (>= (len sys:args) 1) (toNumber (@ sys:args 0)) nil)) +(let i (if (nil? arg) 100 arg)) + +(mut n i) +(while (> n 1) { + (print (str:format "%% Bottles of beer on the wall\n%% bottles of beer\nTake one down, pass it around" n n)) + (set n (- n 1)) + (print (str:format "%% Bottles of beer on the wall." n)) +}) diff --git a/fuzzing/unique/ackermann.ark b/fuzzing/unique/ackermann.ark new file mode 100644 index 000000000..b855f2858 --- /dev/null +++ b/fuzzing/unique/ackermann.ark @@ -0,0 +1,24 @@ +# the Ackermann Peter function (see https://en.wikipedia.org/wiki/Ackermann_function) +# One of the simplest and earliest-discovered examples of a total computable function, +# that is not primitive. All primitive recursive functions are total and computable, +# but the Ackermann function illustrates that not all total computable functions +# are primitive recursive. +# Due to its definitions in terms of extremely deep recursion, it can be used as a +# benchmark of a compiler's ability to optimize recursion, which is the reason why +# we are using this function to benchmark the language. + +(let ackermann (fun (m n) { + (if (> m 0) + # then + (if (= 0 n) + # then + (ackermann (- m 1) 1) + # else + (ackermann (- m 1) (ackermann m (- n 1))) + ) + # else + (+ 1 n) + ) +})) + +(print "Ackermann-Péter function, m=3, n=6: " (ackermann 3 6)) diff --git a/fuzzing/unique/callbacks.ark b/fuzzing/unique/callbacks.ark new file mode 100644 index 000000000..d4383a187 --- /dev/null +++ b/fuzzing/unique/callbacks.ark @@ -0,0 +1,30 @@ +# a function which just prints its argument +(let egg (fun (bar) (print bar))) +# the data we're going to give to this function +(let data ["Iron Man" "is" "Tony Stark"]) +# a list of function call which should be executed later on +(mut callbacks []) + +(print "Data: " data) +(print "Generating callbacks") +(mut acc 0) +# here we are filling the list callbacks +(while (!= acc (len data)) { + (mut d (@ data acc)) + # by putting in it closures that captured d, an element of `data` + # and call the function egg on it + (set callbacks (append callbacks (fun (&d) (egg d)))) + (set acc (+ 1 acc)) +}) + +# then we reset the accumulator +(set acc 0) +(while (!= acc (len callbacks)) { + # we print what was stored in the closure using dot notation + (print "stored: " (@ callbacks acc) .d) + # and then we call the closure itself (be careful: (@ callbacks acc) only returns the callback, + # thus we need to put it in another pair of parens to call the callback) + (puts "Calling callback number " acc ": ") + ((@ callbacks acc)) + (set acc (+ 1 acc)) +}) diff --git a/fuzzing/unique/closures.ark b/fuzzing/unique/closures.ark new file mode 100644 index 000000000..82f9032aa --- /dev/null +++ b/fuzzing/unique/closures.ark @@ -0,0 +1,49 @@ +# Inspired by +# Closures and object are equivalent: http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent + +# this will construct a closure capturing the 3 arguments, plus a function to set the age +(let create-human (fun (name age weight) { + # functions can be invoked in the closure scope + (let set-age (fun (new-age) (set age new-age))) + + # the return value, our closure + # the &name notation is used in the argument list to explicitly capture + # a variable (using deep copy) + (fun (&set-age &name &age &weight) ()) +})) + +# we create 2 humans using such construction, just a nice function call +(let bob (create-human "Bob" 0 144)) +(let john (create-human "John" 12 15)) + +# using the dot notation on a closure object, we can have a **read only** access to its fields +(print "Bob's age: " bob.age) +# and even call the captured functions, which will enter the closure, and be given a **read write** access +# meaning that, even if the fields are read only (eg, we can not do (set bob.age 14)), the "object" can be modified +(print "Setting Bob's age to 10") +(bob.set-age 10) +# the age changed +(print "New age: " bob.age) + +# but john age didn't change, because we created 2 separated closures +(print "John's age, didn't change: " john.age) + + + +# Another example to simulate a python range(x, y) + +# this function will return a closure capturing the number given +# and modifying its value each time we'll call the closure, returning +# the new number +(let countdown-from (fun (number) + (fun (&number) { + (set number (- number 1)) + number + })) +) + +(let countdown-from-3 (countdown-from 3)) + +(print "Countdown " (countdown-from-3)) # 2 +(print "Countdown " (countdown-from-3)) # 1 +(print "Countdown " (countdown-from-3)) # 0 diff --git a/fuzzing/unique/collatz.ark b/fuzzing/unique/collatz.ark new file mode 100644 index 000000000..2828e7ab1 --- /dev/null +++ b/fuzzing/unique/collatz.ark @@ -0,0 +1,23 @@ +# If the number is even, divide it by two. +# If the number is odd, triple it and add one. + +(let get? (fun (seq idx default) + (if (> (len seq) idx) + (@ seq idx) + default))) + +(let n (toNumber (get? sys:args 0 "10"))) + +(let collatz (fun (num) + (if (= 0 (mod num 2)) + (math:floor (/ num 2)) + (math:floor (+ 1 (* 3 num)))))) + +(mut a_i n) +(mut iterations 0) +(while (!= 1 a_i) { + (print a_i) + (set a_i (collatz a_i)) + (set iterations (+ 1 iterations))}) + +(print "Reached 1 in " iterations " iteration(s)") diff --git a/fuzzing/unique/error.ark b/fuzzing/unique/error.ark new file mode 100644 index 000000000..a5197735e --- /dev/null +++ b/fuzzing/unique/error.ark @@ -0,0 +1,25 @@ +# this is how we can import files in ArkScript +# very often, and this is a convention, +# if an imported file starts with a capital letter, +# it shall be a file in the standard library. +(import "Exceptions.ark") + +# the function which should do a "safe number invertion" +(let invert (fun (x) { + (if (= x 0) + # then + (throw "cannot divide by zero") # the value we should return in case of an error + # else + (return (/ 1 x)) # the value returned if everything is ok (if (!= x 0)) + ) +})) + +# this function (try) is implemented in Exceptions.ark (in lib/std/) +# and will check the return value of (invert 0) +# if it's an error (seen by the use of throw), it will call the second function, +# if it's a result, it will call the first +# it works the same way as a try: { function then } catch { do_something } +(try (invert 0) + (fun (inverted) (print inverted)) + (fun (err) (print err)) +) diff --git a/fuzzing/unique/factorial.ark b/fuzzing/unique/factorial.ark new file mode 100644 index 000000000..c74e12de5 --- /dev/null +++ b/fuzzing/unique/factorial.ark @@ -0,0 +1,18 @@ +# demonstration of the creation of a function +# we create a constant named fact, and put a function in it +# taking a single argument, n +(let fact (fun (n) { + (mut a 1) + (mut acc 2) + # then we use a loop (for loops doesn't exist in ArkScript) + (while (<= acc n) { + (set a (* a acc)) + # thus we need to increment the accumulator ourselves + (set acc (+ 1 acc)) + }) + # the return value + a +})) + +# then we call the function we just created +(print "Factorial 6 (with loop and acc): " (fact 6)) diff --git a/fuzzing/unique/macros.ark b/fuzzing/unique/macros.ark new file mode 100644 index 000000000..f122ad426 --- /dev/null +++ b/fuzzing/unique/macros.ark @@ -0,0 +1,107 @@ +!{suffix-dup (sym x) { + !{if (> x 1) + (suffix-dup sym (- x 1)) + } + (symcat sym x) +}} + +!{partial (func ...defargs) { + !{bloc (suffix-dup a (- (argcount func) (len defargs)))} + (fun (bloc) (func ...defargs bloc)) + !{undef bloc} +}} + +(let test_func (fun (a b c) (* a b c))) +(let test_func1 (partial test_func 1)) + +(print "Generated partial functions for test_func (a b c) => (* a b c)") +(print "Expected arguments for test_func: " (argcount test_func) ", expected " 3) +(print "Expected arguments for test_func1: " (argcount test_func1) ", expected " 2) +(print "Calling them: " (test_func 1 2 3) " " (test_func1 2 3)) + +!{foo (a b) (+ a b)} +(print "Using macro foo (a b) => (+ a b): " (foo 1 2)) + +!{var 12} +(print "Using macro constant var=12: " var) + +!{if (= var 12) + (print "This was executed in a if macro, testing var == 12") + (print "You shouldn't see this") +} + +!{if (and true true) + (print "This was executed in a if macro, testing (and true true)") + (print "You shouldn't see this (bis)") +} + +!{defun (name args body) (let name (fun args body))} +(defun a_func (a b) (+ a b)) +(print "Generated a function with a macro, a_func (a b) => (+ a b)") +(print "Calling (a_func 1 2): " (a_func 1 2)) + +!{one (...args) (print "Macro 'one', returns the 2nd argument given in " args " => " (@ args 1))} +(one 1 2) +(one 1 3 4) +(one 1 5 6 7 8) + +!{last (...args) (print "Macro 'last', returns the last argument given in " args " => " (@ args -1))} +(last 1 2) +(last 1 3 4) +(last 1 5 6 7 8) + +{ + (print "Testing macros in scopes and macro shadowing") + + !{test (+ 1 2 3)} + (print "(global) Reading macro 'test', expected 6, " test) + + ((fun () { + !{test (- 1 2 3)} + (print "(sub scope) Reading macro 'test', expected -4, " test) + })) + + (print "(global) Reading macro 'test', expected 6, " test) + + { + !{test 555} + (print "(subscope) Reading macro 'test', expected 555, " test) + !{undef test} + (print "(subscope, undef test) Reading macro 'test', expected 6, " test) + !{undef a} + } +} + +(print "Demonstrating a threading macro") + +!{-> (arg fn1 ...fn) { + !{if (> (len fn) 0) + (-> (fn1 arg) ...fn) + (fn1 arg) + } +}} + +(let filename "hello.json") + +(let f1 (fun (data) { + (print ">>f1 " data) + (+ data "-f1") +})) + +(let f2 (fun (data) { + (print ">>f2 " data) + (+ data "-f2") +})) + +(let f3 (fun (data) { + (print ">>f3 " data) + (+ data "-f3") +})) + +(let f4 (fun (data) { + (print ">>f4 " data) + (+ data "-f4") +})) + +(print "We expected calls to go like this: f1, f2, f3, f4") +(print (-> filename f1 f2 f3 f4)) # (f4 (f3 (f2 (f1 filename)))) \ No newline at end of file diff --git a/fuzzing/unique/quote.ark b/fuzzing/unique/quote.ark new file mode 100644 index 000000000..357a06843 --- /dev/null +++ b/fuzzing/unique/quote.ark @@ -0,0 +1,5 @@ +# quoting a block of code will create an anonymous function taking no arguments +# allowing us to defer its evaluation +(let test '{(let a 12) (print a)}) +# and call the quoted code block later on, using the function call syntax +(test) diff --git a/fuzzing/unique/sum_digits.ark b/fuzzing/unique/sum_digits.ark new file mode 100644 index 000000000..c3b07c399 --- /dev/null +++ b/fuzzing/unique/sum_digits.ark @@ -0,0 +1,26 @@ +(import "List.ark") +(import "String.ark") + +(let to-base (fun (n base) { + (let o (str:ord n)) + (let v (if (and (>= o 48) (<= o 57)) + (- o 48) + (if (and (>= o 97) (<= o 122)) + (- o 87) + (if (and (>= o 65) (<= 90)) + (- o 55) + o)))) + (mod v base) +})) + +(let sum-digits (fun (n base) { + (let number (if (not (= "String" (type n))) (toString n) n)) + (list:reduce + (list:map number (fun (e) (to-base e base))) + (fun (a b) (+ a b))) +})) + +(print (sum-digits 1 10)) # 1 +(print (sum-digits 1234 10)) # 10 +(print (sum-digits "fe" 16)) # 29 +(print (sum-digits "f0e" 16)) # 29 diff --git a/fuzzing/unique/tests-tools.arkc b/fuzzing/unique/tests-tools.arkc new file mode 100644 index 0000000000000000000000000000000000000000..820d4b104cda74a275f09cef151ed7595a2b86c1 GIT binary patch literal 1520 zcmb`F&u$V?6vn@~K#{+Nw*Eo1nHXt{2`%kXSExjyq(m4Nu9}hDkft+GXHet9hw%-3 z3SYpzi3<}K?u?0xYW(hHKn6pI33D@Z?tJH*-#z!9YkOxv6v5NG_oVRt#Q632!hM(j z-1}Yp^!?ob_;vfkk4;Z}d3*8m!2D()XZybNMiu!2uWaW~eU9Kt-?#e`Bk7NP1Q7#< z1k?U#I+K%`Gu51_MwkLt;mMwTu5p!t?Gk+I$`ka41ApkqyS2`pO60CaCRT|oX=G}Z$Q_MT&HK;x>z=b-eDRp85RibN*r|U*HD+v$(}d1a1bgBG}tsR&e3}0L`+aHUIzs literal 0 HcmV?d00001 From 38448906f5e6f73475f7239f982fe36d2d29b4c2 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 23 Oct 2022 18:34:32 +0200 Subject: [PATCH 12/42] crashing the VM only on unknown errors Former-commit-id: 99dab5e5736672c16af4df130f64c4b83575a78e --- .gitignore | 6 +++++- src/arkreactor/VM/VM.cpp | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7cc6ff44b..7c484ac05 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ cformat.ps1 include/Ark/Constants.hpp __arkscript__/ *.arkc -!fuzzing/**/*.arkc *.arkm /Installer.iss @@ -18,6 +17,11 @@ __arkscript__/ tests/cpp/out/ tests/cpp/ark/ +# Fuzzing +!fuzzing/**/*.arkc +fuzzing/output +afl/ + # Folders .vs/ .idea/ diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 80c19acfc..24260eeb6 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -1157,10 +1157,6 @@ namespace Ark std::printf("%s\n", e.what()); backtrace(context); m_exit_code = 1; - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - throw; -#endif } catch (...) { From 3238706e10ff7f7d03f43a777cb2ecb33e562edf Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Thu, 20 Oct 2022 22:15:20 +0200 Subject: [PATCH 13/42] fix: coding style Former-commit-id: 1faa2d0c743edf4216c9f388a3a58d80b54c2f8f --- README.md | 27 +++++------ examples/99bottles.ark | 3 +- examples/ackermann.ark | 7 +-- examples/blockchain.ark | 70 +++++++++++----------------- examples/callbacks.ark | 6 +-- examples/closures.ark | 7 +-- examples/counter.ark | 3 +- examples/error.ark | 7 +-- examples/factorial.ark | 6 +-- examples/fibo.ark | 4 +- examples/http.ark | 13 ++---- examples/macros.ark | 38 +++++---------- examples/more-or-less.ark | 26 +++++------ examples/quicksort.ark | 16 ++----- examples/sum_digits.ark | 8 ++-- lib/std | 2 +- tests/arkscript/async-tests.ark | 12 ++--- tests/arkscript/builtins-tests.ark | 6 +-- tests/arkscript/list-tests.ark | 3 +- tests/arkscript/macro-tests.ark | 74 ++++++++++++++---------------- tests/arkscript/string-tests.ark | 3 +- tests/arkscript/tests-tools.ark | 28 +++++------ tests/arkscript/unittests.ark | 3 +- tests/arkscript/utf8-tests.ark | 9 ++-- tests/arkscript/vm-tests.ark | 3 +- 25 files changed, 148 insertions(+), 236 deletions(-) diff --git a/README.md b/README.md index 5aa54417d..f9c5443dc 100644 --- a/README.md +++ b/README.md @@ -59,25 +59,22 @@ Also it has: (let impl (fun (tries) { (let guess (toNumber (input "Input a numeric value: "))) - (if (< guess number) { - (print "It's more than " guess) - (impl (+ tries 1)) - } - (if (= guess number) { - (print "You found it!") - tries - } { - (print "It's less than " guess) - (impl (+ tries 1)) - })) - })) + (if (< guess number) + { + (print "It's more than " guess) + (impl (+ tries 1))} + (if (= guess number) + { + (print "You found it!") + tries } + { + (print "It's less than " guess) + (impl (+ tries 1))}))})) (let tries (impl 0)) - (print "You won in " tries " tries.") -})) + (print "You won in " tries " tries.")})) (game) - ``` More examples are available inside `examples/`. diff --git a/examples/99bottles.ark b/examples/99bottles.ark index 10f729852..bfb83c3eb 100644 --- a/examples/99bottles.ark +++ b/examples/99bottles.ark @@ -18,5 +18,4 @@ (while (> n 1) { (print (str:format "%% Bottles of beer on the wall\n%% bottles of beer\nTake one down, pass it around" n n)) (set n (- n 1)) - (print (str:format "%% Bottles of beer on the wall." n)) -}) + (print (str:format "%% Bottles of beer on the wall." n))}) diff --git a/examples/ackermann.ark b/examples/ackermann.ark index b855f2858..879145214 100644 --- a/examples/ackermann.ark +++ b/examples/ackermann.ark @@ -14,11 +14,8 @@ # then (ackermann (- m 1) 1) # else - (ackermann (- m 1) (ackermann m (- n 1))) - ) + (ackermann (- m 1) (ackermann m (- n 1)))) # else - (+ 1 n) - ) -})) + (+ 1 n))})) (print "Ackermann-Péter function, m=3, n=6: " (ackermann 3 6)) diff --git a/examples/blockchain.ark b/examples/blockchain.ark index d2605cb9a..4ca29c813 100644 --- a/examples/blockchain.ark +++ b/examples/blockchain.ark @@ -9,8 +9,7 @@ (let make:block (fun (index timestamp data previous_hash) { (let hash (hash:sha256 (+ (toString index) (toString (math:floor timestamp)) (json:toString data) previous_hash))) (print "made block " hash) - (fun (&index ×tamp &data &previous_hash &hash) ()) -})) + (fun (&index ×tamp &data &previous_hash &hash) ())})) (let make:block:fromJSON (fun (data) (make:block (json:get data "index") @@ -50,12 +49,9 @@ (if (not (nil? tmp)) { (let content (make:block:fromJSON (json:fromString (@ tmp 1)))) - (set other_chains (append other_chains content)) - }) - (del cli) - })) - other_chains -})) + (set other_chains (append other_chains content))}) + (del cli)})) + other_chains })) (let verify_proof_of_work (fun (proof last_proof) (and (> proof last_proof) (= 0 (mod proof magic)) (= 0 (mod proof last_proof))))) @@ -68,12 +64,9 @@ (list:forEach chain (fun (block) { # no need to continue checking the blocks if a block wasn't ok (if (and ok? (not (nil? previous))) - (set ok? (verify_proof_of_work (json:get block.data "proof-of-work") (json:get previous.data "proof-of-work"))) - ()) - (set previous block) - })) - ok? -})) + (set ok? (verify_proof_of_work (json:get block.data "proof-of-work") (json:get previous.data "proof-of-work")))) + (set previous block)})) + ok? })) (let consensus (fun () { (print "consensus running") @@ -82,10 +75,7 @@ # if our chain isn't longest, then we store the longest (list:forEach other_chains (fun (chain) { (if (and (< (len blockchain) (len chain)) (verify_chain chain)) - (set blockchain chain) - ()) - })) -})) + (set blockchain chain))}))})) (let proof_of_work (fun (last_proof) { (print "proof of work being generated") @@ -95,8 +85,7 @@ # the proof of work of the previous block in the chain (while (not (and (= 0 (mod inc magic))) (= 0 (mod inc last_proof))) (set inc (+ 1 inc))) - inc -})) + inc })) (let srv (http:server:create)) (http:server:post srv "/transaction" (fun (request) { @@ -111,8 +100,7 @@ (print (str:format "AMOUNT: %%" (json:get new "amount"))) # return value - [200 "transaction submission successful" "text/plain"] -})) + [200 "transaction submission successful" "text/plain"]})) (http:server:get srv "/blocks" (fun (_) { (print "fetching blocks") @@ -124,16 +112,13 @@ "index" data.index "timestamp" data.timestamp "data" data.data - "hash" data.hash - ]))) - })) + "hash" data.hash])))})) (mut str (toString (@ to_send 0))) (list:forEach (tail to_send) (fun (e) (set str (+ str ", " (toString e))))) - [200 (+ "{\"chain\": [" str "]}") "application/json"] -})) + [200 (+ "{\"chain\": [" str "]}") "application/json"]})) (http:server:get srv "/mine" (fun (data) { (print "mining block") @@ -148,18 +133,22 @@ # the program will hang here until a new proof of work is found (let proof (proof_of_work last_proof)) # once we have the proof of work, we can mine a block so we reward the miner by adding a transaction - (set nodes_transactions (append nodes_transactions (json:fromList [ - "from" "network" - "to" miner_address - "amount" 1 - ]))) + (set nodes_transactions (append nodes_transactions (json:fromList + [ + "from" "network" + "to" miner_address + "amount" 1]))) (print "make block") # gather the data needed to create a new block - (mut new_block (make:block (+ 1 last_block.index) (time) (json:fromList [ - "proof-of-work" proof - "transactions" nodes_transactions - "content" data - ]) last_block.hash)) + (mut new_block (make:block + (+ 1 last_block.index) + (time) + (json:fromList + [ + "proof-of-work" proof + "transactions" nodes_transactions + "content" data ]) + last_block.hash)) (set blockchain (append blockchain new_block)) # empty transactions list @@ -172,11 +161,8 @@ "\"timestamp\": " (toString new_block.timestamp) "," "\"data\": " (toString new_block.data) "," "\"hash\": \"" (toString new_block.hash) "\"" - "}" - ) - "application/json" - ] -})) + "}") + "application/json"]})) (print "Listening on localhost:80 for miner " miner_address) (http:server:listen srv "localhost" 80) diff --git a/examples/callbacks.ark b/examples/callbacks.ark index d4383a187..e6ee543b0 100644 --- a/examples/callbacks.ark +++ b/examples/callbacks.ark @@ -14,8 +14,7 @@ # by putting in it closures that captured d, an element of `data` # and call the function egg on it (set callbacks (append callbacks (fun (&d) (egg d)))) - (set acc (+ 1 acc)) -}) + (set acc (+ 1 acc))}) # then we reset the accumulator (set acc 0) @@ -26,5 +25,4 @@ # thus we need to put it in another pair of parens to call the callback) (puts "Calling callback number " acc ": ") ((@ callbacks acc)) - (set acc (+ 1 acc)) -}) + (set acc (+ 1 acc))}) diff --git a/examples/closures.ark b/examples/closures.ark index 82f9032aa..f818e5632 100644 --- a/examples/closures.ark +++ b/examples/closures.ark @@ -9,8 +9,7 @@ # the return value, our closure # the &name notation is used in the argument list to explicitly capture # a variable (using deep copy) - (fun (&set-age &name &age &weight) ()) -})) + (fun (&set-age &name &age &weight) ())})) # we create 2 humans using such construction, just a nice function call (let bob (create-human "Bob" 0 144)) @@ -38,9 +37,7 @@ (let countdown-from (fun (number) (fun (&number) { (set number (- number 1)) - number - })) -) + number }))) (let countdown-from-3 (countdown-from 3)) diff --git a/examples/counter.ark b/examples/counter.ark index e1bf09230..351c3f4d6 100644 --- a/examples/counter.ark +++ b/examples/counter.ark @@ -5,5 +5,4 @@ (puts "\rHere is a counter: " i "/100") (set i (+ 1 i)) # sleep for 50ms - (sys:sleep 50) -}) + (sys:sleep 50)}) diff --git a/examples/error.ark b/examples/error.ark index a5197735e..d54d0b41d 100644 --- a/examples/error.ark +++ b/examples/error.ark @@ -10,9 +10,7 @@ # then (throw "cannot divide by zero") # the value we should return in case of an error # else - (return (/ 1 x)) # the value returned if everything is ok (if (!= x 0)) - ) -})) + (return (/ 1 x)))})) # the value returned if everything is ok (if (!= x 0)) # this function (try) is implemented in Exceptions.ark (in lib/std/) # and will check the return value of (invert 0) @@ -21,5 +19,4 @@ # it works the same way as a try: { function then } catch { do_something } (try (invert 0) (fun (inverted) (print inverted)) - (fun (err) (print err)) -) + (fun (err) (print err))) diff --git a/examples/factorial.ark b/examples/factorial.ark index c74e12de5..160e2b808 100644 --- a/examples/factorial.ark +++ b/examples/factorial.ark @@ -8,11 +8,9 @@ (while (<= acc n) { (set a (* a acc)) # thus we need to increment the accumulator ourselves - (set acc (+ 1 acc)) - }) + (set acc (+ 1 acc))}) # the return value - a -})) + a })) # then we call the function we just created (print "Factorial 6 (with loop and acc): " (fact 6)) diff --git a/examples/fibo.ark b/examples/fibo.ark index a3d4c3941..ed3dbd708 100644 --- a/examples/fibo.ark +++ b/examples/fibo.ark @@ -4,8 +4,6 @@ # then, its the last value evaluated in this case, thus it's the return value n # else, the last value evaluated in this branch as well - (+ (fibo (- n 1)) (fibo (- n 2))) - )) -) + (+ (fibo (- n 1)) (fibo (- n 2)))))) (print "Fibonacci 28: " (fibo 28)) diff --git a/examples/http.ark b/examples/http.ark index 4c68feeba..93ea3683e 100644 --- a/examples/http.ark +++ b/examples/http.ark @@ -17,14 +17,12 @@ "hello world" (+ "hello, " (toString (http:params:toList data)))) "text/plain" - ] - })) + ]})) # configure the route and the handler, we can also give a string instead of a function (http:server:get srv "/hi" f) (print "starting on localhost:80") # make the server listen forever on the port 80 - (http:server:listen srv "localhost" 80) - } + (http:server:listen srv "localhost" 80)} # else, client { # we give the website and the port @@ -35,8 +33,7 @@ # if we got nil, then we couldn't reach the destination (if (nil? output) (print "couldn't reach the server") - (print output) - ) + (print output)) # we can create multiple clients at the same time (let cli2 (http:client:create "yahoo.com" 80)) @@ -52,6 +49,4 @@ # it should work now (if (nil? output) (print "error") - (print (@ output 0)) # status: 200 - ) - }) + (print (@ output 0)))}) # status: 200 diff --git a/examples/macros.ark b/examples/macros.ark index f122ad426..b7ee159f1 100644 --- a/examples/macros.ark +++ b/examples/macros.ark @@ -1,15 +1,12 @@ !{suffix-dup (sym x) { !{if (> x 1) - (suffix-dup sym (- x 1)) - } - (symcat sym x) -}} + (suffix-dup sym (- x 1))} + (symcat sym x)}} !{partial (func ...defargs) { !{bloc (suffix-dup a (- (argcount func) (len defargs)))} (fun (bloc) (func ...defargs bloc)) - !{undef bloc} -}} + !{undef bloc}}} (let test_func (fun (a b c) (* a b c))) (let test_func1 (partial test_func 1)) @@ -27,13 +24,11 @@ !{if (= var 12) (print "This was executed in a if macro, testing var == 12") - (print "You shouldn't see this") -} + (print "You shouldn't see this")} !{if (and true true) (print "This was executed in a if macro, testing (and true true)") - (print "You shouldn't see this (bis)") -} + (print "You shouldn't see this (bis)")} !{defun (name args body) (let name (fun args body))} (defun a_func (a b) (+ a b)) @@ -58,8 +53,7 @@ ((fun () { !{test (- 1 2 3)} - (print "(sub scope) Reading macro 'test', expected -4, " test) - })) + (print "(sub scope) Reading macro 'test', expected -4, " test)})) (print "(global) Reading macro 'test', expected 6, " test) @@ -68,40 +62,32 @@ (print "(subscope) Reading macro 'test', expected 555, " test) !{undef test} (print "(subscope, undef test) Reading macro 'test', expected 6, " test) - !{undef a} - } -} + !{undef a}}} (print "Demonstrating a threading macro") !{-> (arg fn1 ...fn) { !{if (> (len fn) 0) (-> (fn1 arg) ...fn) - (fn1 arg) - } -}} + (fn1 arg)}}} (let filename "hello.json") (let f1 (fun (data) { (print ">>f1 " data) - (+ data "-f1") -})) + (+ data "-f1")})) (let f2 (fun (data) { (print ">>f2 " data) - (+ data "-f2") -})) + (+ data "-f2")})) (let f3 (fun (data) { (print ">>f3 " data) - (+ data "-f3") -})) + (+ data "-f3")})) (let f4 (fun (data) { (print ">>f4 " data) - (+ data "-f4") -})) + (+ data "-f4")})) (print "We expected calls to go like this: f1, f2, f3, f4") (print (-> filename f1 f2 f3 f4)) # (f4 (f3 (f2 (f1 filename)))) \ No newline at end of file diff --git a/examples/more-or-less.ark b/examples/more-or-less.ark index 5e5641223..dc252772e 100644 --- a/examples/more-or-less.ark +++ b/examples/more-or-less.ark @@ -7,21 +7,19 @@ (let impl (fun (tries) { (let guess (toNumber (input "Input a numeric value: "))) - (if (< guess number) { - (print "It's more than " guess) - (impl (+ tries 1)) - } - (if (= guess number) { - (print "You found it!") - tries - } { - (print "It's less than " guess) - (impl (+ tries 1)) - })) - })) + (if (< guess number) + { + (print "It's more than " guess) + (impl (+ tries 1))} + (if (= guess number) + { + (print "You found it!") + tries } + { + (print "It's less than " guess) + (impl (+ tries 1))}))})) (let tries (impl 0)) - (print "You won in " tries " tries.") -})) + (print "You won in " tries " tries.")})) (game) diff --git a/examples/quicksort.ark b/examples/quicksort.ark index 937bf2255..cba02afe1 100644 --- a/examples/quicksort.ark +++ b/examples/quicksort.ark @@ -6,10 +6,8 @@ (while (< i (len lst)) { (if (cond (@ lst i)) (append! output (@ lst i))) - (set i (+ 1 i)) - }) - output -})) + (set i (+ 1 i))}) + output })) # a quicksort function in ArkScript, a lot smaller than its C++ version! # and according to me, a lot simpler to understand @@ -28,9 +26,7 @@ (concat! less [pivot] more) # return a concatenation of arrays - less - }) -})) + less })})) # an unsorted list to sort (let a [3 6 1 5 1 65 324 765 1 6 3 0 6 9 6 5 3 2 5 6 7 64 645 7 345 432 432 4 324 23]) @@ -44,13 +40,11 @@ (mut i 0) (while (< i rep) { (code) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (let t (/ (* 1000 (- (time) start)) rep)) (print name " average: " t "ms") - t -})) + t })) (print a) # use a quoted argument to defer evaluation and be able to call it multiple times in a fresh context diff --git a/examples/sum_digits.ark b/examples/sum_digits.ark index c3b07c399..571db4903 100644 --- a/examples/sum_digits.ark +++ b/examples/sum_digits.ark @@ -9,16 +9,14 @@ (- o 87) (if (and (>= o 65) (<= 90)) (- o 55) - o)))) - (mod v base) -})) + o )))) + (mod v base)})) (let sum-digits (fun (n base) { (let number (if (not (= "String" (type n))) (toString n) n)) (list:reduce (list:map number (fun (e) (to-base e base))) - (fun (a b) (+ a b))) -})) + (fun (a b) (+ a b)))})) (print (sum-digits 1 10)) # 1 (print (sum-digits 1234 10)) # 10 diff --git a/lib/std b/lib/std index fc8e2e9a7..cb8a2039b 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit fc8e2e9a74f78b19666b3fb5032d902b7ce6cc2c +Subproject commit cb8a2039b29b19f01aeb7a1fd5cdad2b47f40c45 diff --git a/tests/arkscript/async-tests.ark b/tests/arkscript/async-tests.ark index c3d2dfb6e..1568906a6 100644 --- a/tests/arkscript/async-tests.ark +++ b/tests/arkscript/async-tests.ark @@ -17,10 +17,8 @@ (mut acc 0) (while (< a b) { (set acc (+ acc (@ src a))) - (set a (+ 1 a)) - }) - acc - })) + (set a (+ 1 a))}) + acc })) (let start-non-async (time)) (let res-non-async (sum 0 size data)) @@ -31,8 +29,7 @@ (async sum 0 (/ size 4) data) (async sum (/ size 4) (/ size 2) data) (async sum (/ size 2) (- size (/ size 4)) data) - (async sum (- size (/ size 4)) size data) - ]) + (async sum (- size (/ size 4)) size data)]) (let res-async (list:reduce (list:map workers (fun (w) (await w))) (fun (a b) (+ a b)))) (let time-async (- (time) start-async)) @@ -41,7 +38,6 @@ (recap "Async/await tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-async (async-tests)) diff --git a/tests/arkscript/builtins-tests.ark b/tests/arkscript/builtins-tests.ark index dc19a98cb..175d7cc3e 100644 --- a/tests/arkscript/builtins-tests.ark +++ b/tests/arkscript/builtins-tests.ark @@ -29,8 +29,7 @@ (mut i 0) (while (< i 12) { (set tests (assert-eq (@ lst i) nil "list:fill @" tests)) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (del i) (set tests (assert-eq (@ (list:setAt lst 5 "a") 5) "a" "list:setAt" tests)) @@ -70,7 +69,6 @@ # clean up (io:removeFiles "test.txt" "temp/") - tests -})) + tests })) (let passed-builtins (builtin-tests)) diff --git a/tests/arkscript/list-tests.ark b/tests/arkscript/list-tests.ark index 1aca26519..c7332488f 100644 --- a/tests/arkscript/list-tests.ark +++ b/tests/arkscript/list-tests.ark @@ -64,7 +64,6 @@ (recap "List tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-list (list-tests)) diff --git a/tests/arkscript/macro-tests.ark b/tests/arkscript/macro-tests.ark index 235839718..9f897a5f3 100644 --- a/tests/arkscript/macro-tests.ark +++ b/tests/arkscript/macro-tests.ark @@ -11,60 +11,60 @@ (set tests (assert-eq (add_two nice_value 2) 14 "macro add_two and macro value" tests)) !{if (and true true) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (= nice_value 12) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (and true (= nice_value 12)) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (and false (= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (or false (= nice_value 12)) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (or false (!= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (not (= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (< nice_value 14) (set tests (assert-val true "macro comparison <" tests)) - (set tests (assert-val false "macro comparison <" tests)) - } + (set tests (assert-val false "macro comparison <" tests))} + !{if (> nice_value 14) (set tests (assert-val false "macro comparison >" tests)) - (set tests (assert-val true "macro comparison >" tests)) - } + (set tests (assert-val true "macro comparison >" tests))} + !{if (<= nice_value 12) (set tests (assert-val true "macro comparison <=" tests)) - (set tests (assert-val false "macro comparison <=" tests)) - } + (set tests (assert-val false "macro comparison <=" tests))} + !{if (>= nice_value 12) (set tests (assert-val true "macro comparison >=" tests)) - (set tests (assert-val false "macro comparison >=" tests)) - } + (set tests (assert-val false "macro comparison >=" tests))} + !{if (@ [true false] 0) (set tests (assert-val true "macro if @" tests)) - (set tests (assert-val false "macro if @" tests)) - } + (set tests (assert-val false "macro if @" tests))} + !{if (@ [true false] -2) (set tests (assert-val true "macro if @" tests)) - (set tests (assert-val false "macro if @" tests)) - } + (set tests (assert-val false "macro if @" tests))} + !{if true { !{in_if_1 true} - !{in_if_2 true} - }} + !{in_if_2 true}}} + (set tests (assert-val (and in_if_1 in_if_2) "macro if multiple definitions" tests)) !{undef in_if_1} !{undef in_if_2} @@ -78,11 +78,9 @@ (set tests (assert-eq val 0 "macro value scoping" tests)) !{undef val} (set tests (assert-eq val 6 "macro value undefine" tests)) - !{undef a} # shouldn't yield an error on unknown macros - } + !{undef a}} # shouldn't yield an error on unknown macros - (set tests (assert-eq val 6 "macro value scoping" tests)) - } + (set tests (assert-eq val 6 "macro value scoping" tests))} !{bar (a ...args) (+ a (len args))} (set tests (assert-eq (bar 1) 1 "macro bar spreading" tests)) @@ -138,18 +136,15 @@ !{suffix-dup (sym x) { !{if (> x 1) - (suffix-dup sym (- x 1)) - } - (symcat sym x) - }} + (suffix-dup sym (- x 1))} + (symcat sym x)}} (let magic_func (fun ((suffix-dup a 3)) (- a1 a2 a3))) (set tests (assert-eq (magic_func 1 2 3) (- 1 2 3) "macro symdup" tests)) !{partial (func ...defargs) { !{bloc (suffix-dup a (- (argcount func) (len defargs)))} (fun (bloc) (func ...defargs bloc)) - !{undef bloc} - }} + !{undef bloc}}} (let test_func (fun (a b c) (* a b c))) (let test_func1 (partial test_func 1)) @@ -165,7 +160,6 @@ (recap "Macro tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-macro (macro-tests)) diff --git a/tests/arkscript/string-tests.ark b/tests/arkscript/string-tests.ark index 08f3ce4bf..7cc9ce927 100644 --- a/tests/arkscript/string-tests.ark +++ b/tests/arkscript/string-tests.ark @@ -17,7 +17,6 @@ (recap "String tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-string (string-tests)) diff --git a/tests/arkscript/tests-tools.ark b/tests/arkscript/tests-tools.ark index 724d5da09..145be0ff9 100644 --- a/tests/arkscript/tests-tools.ark +++ b/tests/arkscript/tests-tools.ark @@ -2,43 +2,38 @@ (let assert-eq (fun (val1 val2 message tests) { (assert (= val1 val2) (str:format "%% (%%) - %% SHOULD BE EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-neq (fun (val1 val2 message tests) { (assert (!= val1 val2) (str:format "%% (%%) - %% SHOULD BE NOT EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-gt (fun (val1 val2 message tests) { (assert (> val1 val2) (str:format "%% (%%) - %% SHOULD BE GREATER THAN %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-ge (fun (val1 val2 message tests) { (assert (>= val1 val2) (str:format "%% (%%) - %% SHOULD BE GREATER OR EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-lt (fun (val1 val2 message tests) { (assert (< val1 val2) (str:format "%% (%%) - %% SHOULD BE LESSER THAN %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-le (fun (val1 val2 message tests) { (assert (<= val1 val2) (str:format "%% (%%) - %% SHOULD BE LESSER OR EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-val (fun (val0 message tests) { (assert val0 (str:format "%% (%%) - %% SHOULD BE TRUTHY" message tests val0)) - (+ 1 tests) -})) + (+ 1 tests)})) (let recap (fun (test-name tests time_) { (console:color "yellow") (puts " " test-name " ") - (if (<= (len test-name) 20) (puts "\t\t") (puts "\t")) + (if (<= (len test-name) 20) + (puts "\t\t") + (puts "\t")) (console:color "reset") (puts "(") @@ -52,5 +47,4 @@ (console:color "green") (puts (* 1000 time_) "ms\n") - (console:color "reset") -})) + (console:color "reset")})) diff --git a/tests/arkscript/unittests.ark b/tests/arkscript/unittests.ark index 871e660e2..4735f60b9 100644 --- a/tests/arkscript/unittests.ark +++ b/tests/arkscript/unittests.ark @@ -28,7 +28,6 @@ ArkScript language passed-macro passed-list passed-string - passed-async -)) + passed-async )) (print "Completed in " (* 1000 (- (time) start_time)) "ms") diff --git a/tests/arkscript/utf8-tests.ark b/tests/arkscript/utf8-tests.ark index 302b3b65e..c7363defb 100644 --- a/tests/arkscript/utf8-tests.ark +++ b/tests/arkscript/utf8-tests.ark @@ -34,13 +34,11 @@ (let emotes [ "🥳" "😅" "😥" "👿" "🟢" "🙊" "💡" "💻" "🌟" "🔹" "🌐" "🤖" - "🖐" "🤔" "🤩" "🤠" "😊" - ]) + "🖐" "🤔" "🤩" "🤠" "😊"]) (mut i 0) (while (< i (len emotes)) { (set tests (assert-eq (len (@ emotes i)) 4 "checking emotes length" tests)) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (set tests (assert-eq "\U0001f47f" "👿" "checking conversion pattern \\U and its emote" tests)) (set tests (assert-eq "\U0001F47F" "👿" "checking conversion pattern \\U and its emote" tests)) @@ -55,7 +53,6 @@ (recap "UTF8 tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-utf8 (utf8-tests)) diff --git a/tests/arkscript/vm-tests.ark b/tests/arkscript/vm-tests.ark index 9f85c2aa8..5f7f01f68 100644 --- a/tests/arkscript/vm-tests.ark +++ b/tests/arkscript/vm-tests.ark @@ -90,7 +90,6 @@ (recap "VM operations passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-vm (vm-tests)) From ef1d3b4c9291b53324bbf6ed38ecb694c7432c07 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 24 Oct 2022 21:37:14 +0200 Subject: [PATCH 14/42] update lib/std Former-commit-id: dc98d87fef4b72c830f813a9483461c2d86a251c --- lib/std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std b/lib/std index cb8a2039b..4fa96f980 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit cb8a2039b29b19f01aeb7a1fd5cdad2b47f40c45 +Subproject commit 4fa96f98073b7b57051451189ae00e2b0cf24909 From 4066e3fbe9a287323a5d362d1307b0b2832623d2 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 24 Oct 2022 21:28:07 +0200 Subject: [PATCH 15/42] adding script to run all the crashes and sort them Former-commit-id: 9bdfbe10207e268d879d00ea2c45674324a2ccda --- .gitignore | 2 +- CHANGELOG.md | 2 +- fuzzing/run_crashes.py | 63 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 fuzzing/run_crashes.py diff --git a/.gitignore b/.gitignore index 7c484ac05..3c6b0a55e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ tests/cpp/ark/ # Fuzzing !fuzzing/**/*.arkc -fuzzing/output +fuzzing/output* afl/ # Folders diff --git a/CHANGELOG.md b/CHANGELOG.md index aa349a840..800b5f193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased version] ### Added - +- added fuzzing tools and corpus for [AFL](https://github.com/AFLplusplus/AFLplusplus) ### Changed - plugins can be constructed from outside ArkScript lib/modules folder, easing the development process diff --git a/fuzzing/run_crashes.py b/fuzzing/run_crashes.py new file mode 100644 index 000000000..6bb580d0d --- /dev/null +++ b/fuzzing/run_crashes.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import sys +import glob +import subprocess + + +def main(): + errors = { + "bad_variant_access": [], + "std::bad_alloc": [], + "unknown instruction": [], + "warning unused quote expression": [], + "warning statement has no effect": [], + "warning ignoring return value of function": [], + "unbound variable:": [], + "unrecognized macro form": [], + "can not modify a constant list": [], + "was of type": [], + "was not provided": [], + "but it received": [], + "parseerror:": [], + "compilationerror": [], + "timeout": [], + " should be ": [], + } + unclassified = [] + + for i, file in enumerate(glob.glob("fuzzing/output/*/crashes/id*")): + try: + cmd = subprocess.run( + ["build/arkscript", file, "-L", "./lib"], + capture_output=True, + check=False, # do not check return code + timeout=2 # 2 seconds timeout + ) + output = cmd.stderr.decode(errors="ignore") + cmd.stdout.decode(errors="ignore") + except subprocess.TimeoutExpired: + output = "timeout" + + for name, values in errors.items(): + if name in output.lower(): + values.append(cmd) + break + else: + unclassified.append(cmd) + + errors["unclassified"] = unclassified + + for name, values in errors.items(): + print(f"{name}: {len(values)}") + + for name in ["std::bad_alloc", "unknown instruction", "parseerror:", "compilationerror", "timeout", "unclassified"]: + with open(f"fuzzing/output/{name.replace(':', '').replace(' ', '_')}.list", "w", encoding="utf-8") as f: + for line in errors[name]: + path = line.args[1] + f.write(f"{path}\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 485cbd120c826b9855775c5fb7a8f708a17d8295 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Thu, 20 Oct 2022 22:15:20 +0200 Subject: [PATCH 16/42] fix: coding style Former-commit-id: c6521d3a1e17030d50d7f930c389e91e431e2142 --- README.md | 27 +++++------ examples/99bottles.ark | 3 +- examples/ackermann.ark | 7 +-- examples/blockchain.ark | 70 +++++++++++----------------- examples/callbacks.ark | 6 +-- examples/closures.ark | 7 +-- examples/counter.ark | 3 +- examples/error.ark | 7 +-- examples/factorial.ark | 6 +-- examples/fibo.ark | 4 +- examples/http.ark | 13 ++---- examples/macros.ark | 38 +++++---------- examples/more-or-less.ark | 26 +++++------ examples/quicksort.ark | 16 ++----- examples/sum_digits.ark | 8 ++-- lib/std | 2 +- tests/arkscript/async-tests.ark | 12 ++--- tests/arkscript/builtins-tests.ark | 6 +-- tests/arkscript/list-tests.ark | 3 +- tests/arkscript/macro-tests.ark | 74 ++++++++++++++---------------- tests/arkscript/string-tests.ark | 3 +- tests/arkscript/tests-tools.ark | 28 +++++------ tests/arkscript/unittests.ark | 3 +- tests/arkscript/utf8-tests.ark | 9 ++-- tests/arkscript/vm-tests.ark | 3 +- 25 files changed, 148 insertions(+), 236 deletions(-) diff --git a/README.md b/README.md index 5aa54417d..f9c5443dc 100644 --- a/README.md +++ b/README.md @@ -59,25 +59,22 @@ Also it has: (let impl (fun (tries) { (let guess (toNumber (input "Input a numeric value: "))) - (if (< guess number) { - (print "It's more than " guess) - (impl (+ tries 1)) - } - (if (= guess number) { - (print "You found it!") - tries - } { - (print "It's less than " guess) - (impl (+ tries 1)) - })) - })) + (if (< guess number) + { + (print "It's more than " guess) + (impl (+ tries 1))} + (if (= guess number) + { + (print "You found it!") + tries } + { + (print "It's less than " guess) + (impl (+ tries 1))}))})) (let tries (impl 0)) - (print "You won in " tries " tries.") -})) + (print "You won in " tries " tries.")})) (game) - ``` More examples are available inside `examples/`. diff --git a/examples/99bottles.ark b/examples/99bottles.ark index 10f729852..bfb83c3eb 100644 --- a/examples/99bottles.ark +++ b/examples/99bottles.ark @@ -18,5 +18,4 @@ (while (> n 1) { (print (str:format "%% Bottles of beer on the wall\n%% bottles of beer\nTake one down, pass it around" n n)) (set n (- n 1)) - (print (str:format "%% Bottles of beer on the wall." n)) -}) + (print (str:format "%% Bottles of beer on the wall." n))}) diff --git a/examples/ackermann.ark b/examples/ackermann.ark index b855f2858..879145214 100644 --- a/examples/ackermann.ark +++ b/examples/ackermann.ark @@ -14,11 +14,8 @@ # then (ackermann (- m 1) 1) # else - (ackermann (- m 1) (ackermann m (- n 1))) - ) + (ackermann (- m 1) (ackermann m (- n 1)))) # else - (+ 1 n) - ) -})) + (+ 1 n))})) (print "Ackermann-Péter function, m=3, n=6: " (ackermann 3 6)) diff --git a/examples/blockchain.ark b/examples/blockchain.ark index d2605cb9a..4ca29c813 100644 --- a/examples/blockchain.ark +++ b/examples/blockchain.ark @@ -9,8 +9,7 @@ (let make:block (fun (index timestamp data previous_hash) { (let hash (hash:sha256 (+ (toString index) (toString (math:floor timestamp)) (json:toString data) previous_hash))) (print "made block " hash) - (fun (&index ×tamp &data &previous_hash &hash) ()) -})) + (fun (&index ×tamp &data &previous_hash &hash) ())})) (let make:block:fromJSON (fun (data) (make:block (json:get data "index") @@ -50,12 +49,9 @@ (if (not (nil? tmp)) { (let content (make:block:fromJSON (json:fromString (@ tmp 1)))) - (set other_chains (append other_chains content)) - }) - (del cli) - })) - other_chains -})) + (set other_chains (append other_chains content))}) + (del cli)})) + other_chains })) (let verify_proof_of_work (fun (proof last_proof) (and (> proof last_proof) (= 0 (mod proof magic)) (= 0 (mod proof last_proof))))) @@ -68,12 +64,9 @@ (list:forEach chain (fun (block) { # no need to continue checking the blocks if a block wasn't ok (if (and ok? (not (nil? previous))) - (set ok? (verify_proof_of_work (json:get block.data "proof-of-work") (json:get previous.data "proof-of-work"))) - ()) - (set previous block) - })) - ok? -})) + (set ok? (verify_proof_of_work (json:get block.data "proof-of-work") (json:get previous.data "proof-of-work")))) + (set previous block)})) + ok? })) (let consensus (fun () { (print "consensus running") @@ -82,10 +75,7 @@ # if our chain isn't longest, then we store the longest (list:forEach other_chains (fun (chain) { (if (and (< (len blockchain) (len chain)) (verify_chain chain)) - (set blockchain chain) - ()) - })) -})) + (set blockchain chain))}))})) (let proof_of_work (fun (last_proof) { (print "proof of work being generated") @@ -95,8 +85,7 @@ # the proof of work of the previous block in the chain (while (not (and (= 0 (mod inc magic))) (= 0 (mod inc last_proof))) (set inc (+ 1 inc))) - inc -})) + inc })) (let srv (http:server:create)) (http:server:post srv "/transaction" (fun (request) { @@ -111,8 +100,7 @@ (print (str:format "AMOUNT: %%" (json:get new "amount"))) # return value - [200 "transaction submission successful" "text/plain"] -})) + [200 "transaction submission successful" "text/plain"]})) (http:server:get srv "/blocks" (fun (_) { (print "fetching blocks") @@ -124,16 +112,13 @@ "index" data.index "timestamp" data.timestamp "data" data.data - "hash" data.hash - ]))) - })) + "hash" data.hash])))})) (mut str (toString (@ to_send 0))) (list:forEach (tail to_send) (fun (e) (set str (+ str ", " (toString e))))) - [200 (+ "{\"chain\": [" str "]}") "application/json"] -})) + [200 (+ "{\"chain\": [" str "]}") "application/json"]})) (http:server:get srv "/mine" (fun (data) { (print "mining block") @@ -148,18 +133,22 @@ # the program will hang here until a new proof of work is found (let proof (proof_of_work last_proof)) # once we have the proof of work, we can mine a block so we reward the miner by adding a transaction - (set nodes_transactions (append nodes_transactions (json:fromList [ - "from" "network" - "to" miner_address - "amount" 1 - ]))) + (set nodes_transactions (append nodes_transactions (json:fromList + [ + "from" "network" + "to" miner_address + "amount" 1]))) (print "make block") # gather the data needed to create a new block - (mut new_block (make:block (+ 1 last_block.index) (time) (json:fromList [ - "proof-of-work" proof - "transactions" nodes_transactions - "content" data - ]) last_block.hash)) + (mut new_block (make:block + (+ 1 last_block.index) + (time) + (json:fromList + [ + "proof-of-work" proof + "transactions" nodes_transactions + "content" data ]) + last_block.hash)) (set blockchain (append blockchain new_block)) # empty transactions list @@ -172,11 +161,8 @@ "\"timestamp\": " (toString new_block.timestamp) "," "\"data\": " (toString new_block.data) "," "\"hash\": \"" (toString new_block.hash) "\"" - "}" - ) - "application/json" - ] -})) + "}") + "application/json"]})) (print "Listening on localhost:80 for miner " miner_address) (http:server:listen srv "localhost" 80) diff --git a/examples/callbacks.ark b/examples/callbacks.ark index d4383a187..e6ee543b0 100644 --- a/examples/callbacks.ark +++ b/examples/callbacks.ark @@ -14,8 +14,7 @@ # by putting in it closures that captured d, an element of `data` # and call the function egg on it (set callbacks (append callbacks (fun (&d) (egg d)))) - (set acc (+ 1 acc)) -}) + (set acc (+ 1 acc))}) # then we reset the accumulator (set acc 0) @@ -26,5 +25,4 @@ # thus we need to put it in another pair of parens to call the callback) (puts "Calling callback number " acc ": ") ((@ callbacks acc)) - (set acc (+ 1 acc)) -}) + (set acc (+ 1 acc))}) diff --git a/examples/closures.ark b/examples/closures.ark index 82f9032aa..f818e5632 100644 --- a/examples/closures.ark +++ b/examples/closures.ark @@ -9,8 +9,7 @@ # the return value, our closure # the &name notation is used in the argument list to explicitly capture # a variable (using deep copy) - (fun (&set-age &name &age &weight) ()) -})) + (fun (&set-age &name &age &weight) ())})) # we create 2 humans using such construction, just a nice function call (let bob (create-human "Bob" 0 144)) @@ -38,9 +37,7 @@ (let countdown-from (fun (number) (fun (&number) { (set number (- number 1)) - number - })) -) + number }))) (let countdown-from-3 (countdown-from 3)) diff --git a/examples/counter.ark b/examples/counter.ark index e1bf09230..351c3f4d6 100644 --- a/examples/counter.ark +++ b/examples/counter.ark @@ -5,5 +5,4 @@ (puts "\rHere is a counter: " i "/100") (set i (+ 1 i)) # sleep for 50ms - (sys:sleep 50) -}) + (sys:sleep 50)}) diff --git a/examples/error.ark b/examples/error.ark index a5197735e..d54d0b41d 100644 --- a/examples/error.ark +++ b/examples/error.ark @@ -10,9 +10,7 @@ # then (throw "cannot divide by zero") # the value we should return in case of an error # else - (return (/ 1 x)) # the value returned if everything is ok (if (!= x 0)) - ) -})) + (return (/ 1 x)))})) # the value returned if everything is ok (if (!= x 0)) # this function (try) is implemented in Exceptions.ark (in lib/std/) # and will check the return value of (invert 0) @@ -21,5 +19,4 @@ # it works the same way as a try: { function then } catch { do_something } (try (invert 0) (fun (inverted) (print inverted)) - (fun (err) (print err)) -) + (fun (err) (print err))) diff --git a/examples/factorial.ark b/examples/factorial.ark index c74e12de5..160e2b808 100644 --- a/examples/factorial.ark +++ b/examples/factorial.ark @@ -8,11 +8,9 @@ (while (<= acc n) { (set a (* a acc)) # thus we need to increment the accumulator ourselves - (set acc (+ 1 acc)) - }) + (set acc (+ 1 acc))}) # the return value - a -})) + a })) # then we call the function we just created (print "Factorial 6 (with loop and acc): " (fact 6)) diff --git a/examples/fibo.ark b/examples/fibo.ark index a3d4c3941..ed3dbd708 100644 --- a/examples/fibo.ark +++ b/examples/fibo.ark @@ -4,8 +4,6 @@ # then, its the last value evaluated in this case, thus it's the return value n # else, the last value evaluated in this branch as well - (+ (fibo (- n 1)) (fibo (- n 2))) - )) -) + (+ (fibo (- n 1)) (fibo (- n 2)))))) (print "Fibonacci 28: " (fibo 28)) diff --git a/examples/http.ark b/examples/http.ark index 4c68feeba..93ea3683e 100644 --- a/examples/http.ark +++ b/examples/http.ark @@ -17,14 +17,12 @@ "hello world" (+ "hello, " (toString (http:params:toList data)))) "text/plain" - ] - })) + ]})) # configure the route and the handler, we can also give a string instead of a function (http:server:get srv "/hi" f) (print "starting on localhost:80") # make the server listen forever on the port 80 - (http:server:listen srv "localhost" 80) - } + (http:server:listen srv "localhost" 80)} # else, client { # we give the website and the port @@ -35,8 +33,7 @@ # if we got nil, then we couldn't reach the destination (if (nil? output) (print "couldn't reach the server") - (print output) - ) + (print output)) # we can create multiple clients at the same time (let cli2 (http:client:create "yahoo.com" 80)) @@ -52,6 +49,4 @@ # it should work now (if (nil? output) (print "error") - (print (@ output 0)) # status: 200 - ) - }) + (print (@ output 0)))}) # status: 200 diff --git a/examples/macros.ark b/examples/macros.ark index f122ad426..b7ee159f1 100644 --- a/examples/macros.ark +++ b/examples/macros.ark @@ -1,15 +1,12 @@ !{suffix-dup (sym x) { !{if (> x 1) - (suffix-dup sym (- x 1)) - } - (symcat sym x) -}} + (suffix-dup sym (- x 1))} + (symcat sym x)}} !{partial (func ...defargs) { !{bloc (suffix-dup a (- (argcount func) (len defargs)))} (fun (bloc) (func ...defargs bloc)) - !{undef bloc} -}} + !{undef bloc}}} (let test_func (fun (a b c) (* a b c))) (let test_func1 (partial test_func 1)) @@ -27,13 +24,11 @@ !{if (= var 12) (print "This was executed in a if macro, testing var == 12") - (print "You shouldn't see this") -} + (print "You shouldn't see this")} !{if (and true true) (print "This was executed in a if macro, testing (and true true)") - (print "You shouldn't see this (bis)") -} + (print "You shouldn't see this (bis)")} !{defun (name args body) (let name (fun args body))} (defun a_func (a b) (+ a b)) @@ -58,8 +53,7 @@ ((fun () { !{test (- 1 2 3)} - (print "(sub scope) Reading macro 'test', expected -4, " test) - })) + (print "(sub scope) Reading macro 'test', expected -4, " test)})) (print "(global) Reading macro 'test', expected 6, " test) @@ -68,40 +62,32 @@ (print "(subscope) Reading macro 'test', expected 555, " test) !{undef test} (print "(subscope, undef test) Reading macro 'test', expected 6, " test) - !{undef a} - } -} + !{undef a}}} (print "Demonstrating a threading macro") !{-> (arg fn1 ...fn) { !{if (> (len fn) 0) (-> (fn1 arg) ...fn) - (fn1 arg) - } -}} + (fn1 arg)}}} (let filename "hello.json") (let f1 (fun (data) { (print ">>f1 " data) - (+ data "-f1") -})) + (+ data "-f1")})) (let f2 (fun (data) { (print ">>f2 " data) - (+ data "-f2") -})) + (+ data "-f2")})) (let f3 (fun (data) { (print ">>f3 " data) - (+ data "-f3") -})) + (+ data "-f3")})) (let f4 (fun (data) { (print ">>f4 " data) - (+ data "-f4") -})) + (+ data "-f4")})) (print "We expected calls to go like this: f1, f2, f3, f4") (print (-> filename f1 f2 f3 f4)) # (f4 (f3 (f2 (f1 filename)))) \ No newline at end of file diff --git a/examples/more-or-less.ark b/examples/more-or-less.ark index 5e5641223..dc252772e 100644 --- a/examples/more-or-less.ark +++ b/examples/more-or-less.ark @@ -7,21 +7,19 @@ (let impl (fun (tries) { (let guess (toNumber (input "Input a numeric value: "))) - (if (< guess number) { - (print "It's more than " guess) - (impl (+ tries 1)) - } - (if (= guess number) { - (print "You found it!") - tries - } { - (print "It's less than " guess) - (impl (+ tries 1)) - })) - })) + (if (< guess number) + { + (print "It's more than " guess) + (impl (+ tries 1))} + (if (= guess number) + { + (print "You found it!") + tries } + { + (print "It's less than " guess) + (impl (+ tries 1))}))})) (let tries (impl 0)) - (print "You won in " tries " tries.") -})) + (print "You won in " tries " tries.")})) (game) diff --git a/examples/quicksort.ark b/examples/quicksort.ark index 937bf2255..cba02afe1 100644 --- a/examples/quicksort.ark +++ b/examples/quicksort.ark @@ -6,10 +6,8 @@ (while (< i (len lst)) { (if (cond (@ lst i)) (append! output (@ lst i))) - (set i (+ 1 i)) - }) - output -})) + (set i (+ 1 i))}) + output })) # a quicksort function in ArkScript, a lot smaller than its C++ version! # and according to me, a lot simpler to understand @@ -28,9 +26,7 @@ (concat! less [pivot] more) # return a concatenation of arrays - less - }) -})) + less })})) # an unsorted list to sort (let a [3 6 1 5 1 65 324 765 1 6 3 0 6 9 6 5 3 2 5 6 7 64 645 7 345 432 432 4 324 23]) @@ -44,13 +40,11 @@ (mut i 0) (while (< i rep) { (code) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (let t (/ (* 1000 (- (time) start)) rep)) (print name " average: " t "ms") - t -})) + t })) (print a) # use a quoted argument to defer evaluation and be able to call it multiple times in a fresh context diff --git a/examples/sum_digits.ark b/examples/sum_digits.ark index c3b07c399..571db4903 100644 --- a/examples/sum_digits.ark +++ b/examples/sum_digits.ark @@ -9,16 +9,14 @@ (- o 87) (if (and (>= o 65) (<= 90)) (- o 55) - o)))) - (mod v base) -})) + o )))) + (mod v base)})) (let sum-digits (fun (n base) { (let number (if (not (= "String" (type n))) (toString n) n)) (list:reduce (list:map number (fun (e) (to-base e base))) - (fun (a b) (+ a b))) -})) + (fun (a b) (+ a b)))})) (print (sum-digits 1 10)) # 1 (print (sum-digits 1234 10)) # 10 diff --git a/lib/std b/lib/std index fc8e2e9a7..cb8a2039b 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit fc8e2e9a74f78b19666b3fb5032d902b7ce6cc2c +Subproject commit cb8a2039b29b19f01aeb7a1fd5cdad2b47f40c45 diff --git a/tests/arkscript/async-tests.ark b/tests/arkscript/async-tests.ark index c3d2dfb6e..1568906a6 100644 --- a/tests/arkscript/async-tests.ark +++ b/tests/arkscript/async-tests.ark @@ -17,10 +17,8 @@ (mut acc 0) (while (< a b) { (set acc (+ acc (@ src a))) - (set a (+ 1 a)) - }) - acc - })) + (set a (+ 1 a))}) + acc })) (let start-non-async (time)) (let res-non-async (sum 0 size data)) @@ -31,8 +29,7 @@ (async sum 0 (/ size 4) data) (async sum (/ size 4) (/ size 2) data) (async sum (/ size 2) (- size (/ size 4)) data) - (async sum (- size (/ size 4)) size data) - ]) + (async sum (- size (/ size 4)) size data)]) (let res-async (list:reduce (list:map workers (fun (w) (await w))) (fun (a b) (+ a b)))) (let time-async (- (time) start-async)) @@ -41,7 +38,6 @@ (recap "Async/await tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-async (async-tests)) diff --git a/tests/arkscript/builtins-tests.ark b/tests/arkscript/builtins-tests.ark index dc19a98cb..175d7cc3e 100644 --- a/tests/arkscript/builtins-tests.ark +++ b/tests/arkscript/builtins-tests.ark @@ -29,8 +29,7 @@ (mut i 0) (while (< i 12) { (set tests (assert-eq (@ lst i) nil "list:fill @" tests)) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (del i) (set tests (assert-eq (@ (list:setAt lst 5 "a") 5) "a" "list:setAt" tests)) @@ -70,7 +69,6 @@ # clean up (io:removeFiles "test.txt" "temp/") - tests -})) + tests })) (let passed-builtins (builtin-tests)) diff --git a/tests/arkscript/list-tests.ark b/tests/arkscript/list-tests.ark index 1aca26519..c7332488f 100644 --- a/tests/arkscript/list-tests.ark +++ b/tests/arkscript/list-tests.ark @@ -64,7 +64,6 @@ (recap "List tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-list (list-tests)) diff --git a/tests/arkscript/macro-tests.ark b/tests/arkscript/macro-tests.ark index 235839718..9f897a5f3 100644 --- a/tests/arkscript/macro-tests.ark +++ b/tests/arkscript/macro-tests.ark @@ -11,60 +11,60 @@ (set tests (assert-eq (add_two nice_value 2) 14 "macro add_two and macro value" tests)) !{if (and true true) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (= nice_value 12) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (and true (= nice_value 12)) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (and false (= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (or false (= nice_value 12)) (set tests (assert-val true "macro if" tests)) - (set tests (assert-val false "macro if" tests)) - } + (set tests (assert-val false "macro if" tests))} + !{if (or false (!= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (not (= nice_value 12)) (set tests (assert-val false "macro if" tests)) - (set tests (assert-val true "macro if" tests)) - } + (set tests (assert-val true "macro if" tests))} + !{if (< nice_value 14) (set tests (assert-val true "macro comparison <" tests)) - (set tests (assert-val false "macro comparison <" tests)) - } + (set tests (assert-val false "macro comparison <" tests))} + !{if (> nice_value 14) (set tests (assert-val false "macro comparison >" tests)) - (set tests (assert-val true "macro comparison >" tests)) - } + (set tests (assert-val true "macro comparison >" tests))} + !{if (<= nice_value 12) (set tests (assert-val true "macro comparison <=" tests)) - (set tests (assert-val false "macro comparison <=" tests)) - } + (set tests (assert-val false "macro comparison <=" tests))} + !{if (>= nice_value 12) (set tests (assert-val true "macro comparison >=" tests)) - (set tests (assert-val false "macro comparison >=" tests)) - } + (set tests (assert-val false "macro comparison >=" tests))} + !{if (@ [true false] 0) (set tests (assert-val true "macro if @" tests)) - (set tests (assert-val false "macro if @" tests)) - } + (set tests (assert-val false "macro if @" tests))} + !{if (@ [true false] -2) (set tests (assert-val true "macro if @" tests)) - (set tests (assert-val false "macro if @" tests)) - } + (set tests (assert-val false "macro if @" tests))} + !{if true { !{in_if_1 true} - !{in_if_2 true} - }} + !{in_if_2 true}}} + (set tests (assert-val (and in_if_1 in_if_2) "macro if multiple definitions" tests)) !{undef in_if_1} !{undef in_if_2} @@ -78,11 +78,9 @@ (set tests (assert-eq val 0 "macro value scoping" tests)) !{undef val} (set tests (assert-eq val 6 "macro value undefine" tests)) - !{undef a} # shouldn't yield an error on unknown macros - } + !{undef a}} # shouldn't yield an error on unknown macros - (set tests (assert-eq val 6 "macro value scoping" tests)) - } + (set tests (assert-eq val 6 "macro value scoping" tests))} !{bar (a ...args) (+ a (len args))} (set tests (assert-eq (bar 1) 1 "macro bar spreading" tests)) @@ -138,18 +136,15 @@ !{suffix-dup (sym x) { !{if (> x 1) - (suffix-dup sym (- x 1)) - } - (symcat sym x) - }} + (suffix-dup sym (- x 1))} + (symcat sym x)}} (let magic_func (fun ((suffix-dup a 3)) (- a1 a2 a3))) (set tests (assert-eq (magic_func 1 2 3) (- 1 2 3) "macro symdup" tests)) !{partial (func ...defargs) { !{bloc (suffix-dup a (- (argcount func) (len defargs)))} (fun (bloc) (func ...defargs bloc)) - !{undef bloc} - }} + !{undef bloc}}} (let test_func (fun (a b c) (* a b c))) (let test_func1 (partial test_func 1)) @@ -165,7 +160,6 @@ (recap "Macro tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-macro (macro-tests)) diff --git a/tests/arkscript/string-tests.ark b/tests/arkscript/string-tests.ark index 08f3ce4bf..7cc9ce927 100644 --- a/tests/arkscript/string-tests.ark +++ b/tests/arkscript/string-tests.ark @@ -17,7 +17,6 @@ (recap "String tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-string (string-tests)) diff --git a/tests/arkscript/tests-tools.ark b/tests/arkscript/tests-tools.ark index 724d5da09..145be0ff9 100644 --- a/tests/arkscript/tests-tools.ark +++ b/tests/arkscript/tests-tools.ark @@ -2,43 +2,38 @@ (let assert-eq (fun (val1 val2 message tests) { (assert (= val1 val2) (str:format "%% (%%) - %% SHOULD BE EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-neq (fun (val1 val2 message tests) { (assert (!= val1 val2) (str:format "%% (%%) - %% SHOULD BE NOT EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-gt (fun (val1 val2 message tests) { (assert (> val1 val2) (str:format "%% (%%) - %% SHOULD BE GREATER THAN %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-ge (fun (val1 val2 message tests) { (assert (>= val1 val2) (str:format "%% (%%) - %% SHOULD BE GREATER OR EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-lt (fun (val1 val2 message tests) { (assert (< val1 val2) (str:format "%% (%%) - %% SHOULD BE LESSER THAN %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-le (fun (val1 val2 message tests) { (assert (<= val1 val2) (str:format "%% (%%) - %% SHOULD BE LESSER OR EQUAL TO %%" message tests val1 val2)) - (+ 1 tests) -})) + (+ 1 tests)})) (let assert-val (fun (val0 message tests) { (assert val0 (str:format "%% (%%) - %% SHOULD BE TRUTHY" message tests val0)) - (+ 1 tests) -})) + (+ 1 tests)})) (let recap (fun (test-name tests time_) { (console:color "yellow") (puts " " test-name " ") - (if (<= (len test-name) 20) (puts "\t\t") (puts "\t")) + (if (<= (len test-name) 20) + (puts "\t\t") + (puts "\t")) (console:color "reset") (puts "(") @@ -52,5 +47,4 @@ (console:color "green") (puts (* 1000 time_) "ms\n") - (console:color "reset") -})) + (console:color "reset")})) diff --git a/tests/arkscript/unittests.ark b/tests/arkscript/unittests.ark index 871e660e2..4735f60b9 100644 --- a/tests/arkscript/unittests.ark +++ b/tests/arkscript/unittests.ark @@ -28,7 +28,6 @@ ArkScript language passed-macro passed-list passed-string - passed-async -)) + passed-async )) (print "Completed in " (* 1000 (- (time) start_time)) "ms") diff --git a/tests/arkscript/utf8-tests.ark b/tests/arkscript/utf8-tests.ark index 302b3b65e..c7363defb 100644 --- a/tests/arkscript/utf8-tests.ark +++ b/tests/arkscript/utf8-tests.ark @@ -34,13 +34,11 @@ (let emotes [ "🥳" "😅" "😥" "👿" "🟢" "🙊" "💡" "💻" "🌟" "🔹" "🌐" "🤖" - "🖐" "🤔" "🤩" "🤠" "😊" - ]) + "🖐" "🤔" "🤩" "🤠" "😊"]) (mut i 0) (while (< i (len emotes)) { (set tests (assert-eq (len (@ emotes i)) 4 "checking emotes length" tests)) - (set i (+ 1 i)) - }) + (set i (+ 1 i))}) (set tests (assert-eq "\U0001f47f" "👿" "checking conversion pattern \\U and its emote" tests)) (set tests (assert-eq "\U0001F47F" "👿" "checking conversion pattern \\U and its emote" tests)) @@ -55,7 +53,6 @@ (recap "UTF8 tests passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-utf8 (utf8-tests)) diff --git a/tests/arkscript/vm-tests.ark b/tests/arkscript/vm-tests.ark index 9f85c2aa8..5f7f01f68 100644 --- a/tests/arkscript/vm-tests.ark +++ b/tests/arkscript/vm-tests.ark @@ -90,7 +90,6 @@ (recap "VM operations passed" tests (- (time) start-time)) - tests -})) + tests })) (let passed-vm (vm-tests)) From 4e0c1c8a3af302f3cb3b655ec66d2280b33e2842 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 24 Oct 2022 21:37:14 +0200 Subject: [PATCH 17/42] update lib/std Former-commit-id: c9e815593f953d236d7d97ae79800fc25dcebcb8 --- lib/std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std b/lib/std index cb8a2039b..4fa96f980 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit cb8a2039b29b19f01aeb7a1fd5cdad2b47f40c45 +Subproject commit 4fa96f98073b7b57051451189ae00e2b0cf24909 From a3b143a27cd872659cebc77db6967c1a0f99163b Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 15:11:05 +0100 Subject: [PATCH 18/42] fix: handled calling anonymous non-callable objects which resulted in a segfault now print an error message Former-commit-id: 4b7f8b7170ce53afd67f39a37e2ca26f8aefb993 --- CHANGELOG.md | 1 + include/Ark/VM/inline/VM.inl | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 800b5f193..bc1cc6dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - plugins can be constructed from outside ArkScript lib/modules folder, easing the development process - plugins loading now works as intended: look alongside the given file/bytecode file, then in the std lib folder - new way to create modules, easier to use +- calling a non-callable anonymous object do not result in a segfault ### Removed diff --git a/include/Ark/VM/inline/VM.inl b/include/Ark/VM/inline/VM.inl index 4c6488d89..150c7f48f 100644 --- a/include/Ark/VM/inline/VM.inl +++ b/include/Ark/VM/inline/VM.inl @@ -370,7 +370,12 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_) } default: - throwVMError("Can't call '" + m_state.m_symbols[context.last_symbol] + "': it isn't a Function but a " + types_to_str[static_cast(function.valueType())]); + { + if (m_state.m_symbols.size() > 0) + throwVMError("Can't call '" + m_state.m_symbols[context.last_symbol] + "': it isn't a Function but a " + types_to_str[static_cast(function.valueType())]); + else + throwVMError("A " + types_to_str[static_cast(function.valueType())] + " isn't a callable"); + } } // checking function arity From c415bc8cf451b8b2585e8aa4d8a6c9d0b9db3790 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 15:18:18 +0100 Subject: [PATCH 19/42] fix: macro processor function registring now handles empty nodes Former-commit-id: 55c128b4b656c044fd72c501a50dcfc5ab79f177 --- CHANGELOG.md | 1 + src/arkreactor/Compiler/Macros/Processor.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1cc6dd9..571a9db69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - plugins loading now works as intended: look alongside the given file/bytecode file, then in the std lib folder - new way to create modules, easier to use - calling a non-callable anonymous object do not result in a segfault +- macro processor function registering now handles empty nodes ### Removed diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp index 912e34a85..5d401647d 100644 --- a/src/arkreactor/Compiler/Macros/Processor.cpp +++ b/src/arkreactor/Compiler/Macros/Processor.cpp @@ -117,6 +117,7 @@ namespace Ark::internal if (node.nodeType() == NodeType::List && node.constList().size() > 0 && node.constList()[0].nodeType() == NodeType::Keyword) { Keyword kw = node.constList()[0].keyword(); + // checking for function definition, which can occur only inside an assignment node if (kw != Keyword::Let && kw != Keyword::Mut && kw != Keyword::Set) return; @@ -124,7 +125,7 @@ namespace Ark::internal if (inner.nodeType() != NodeType::List) return; - if (inner.constList()[0].nodeType() == NodeType::Keyword && inner.constList()[0].keyword() == Keyword::Fun) + if (inner.constList().size() > 0 && inner.constList()[0].nodeType() == NodeType::Keyword && inner.constList()[0].keyword() == Keyword::Fun) m_defined_functions[node.constList()[1].string()] = inner.constList()[1]; } } From 021b2f91f9c2de7163dc57748ecf9066a2d44010 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 15:35:44 +0100 Subject: [PATCH 20/42] fix: check if the variable being captured isn't unbound Former-commit-id: ef3ad8068ca1c8977f9747e1cf226ad1aa31a169 --- CHANGELOG.md | 1 + src/arkreactor/VM/VM.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 571a9db69..202b8d4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - new way to create modules, easier to use - calling a non-callable anonymous object do not result in a segfault - macro processor function registering now handles empty nodes +- added a fix to avoid crashes when capturing unbound variables ### Removed diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 24260eeb6..47c6ecc8c 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -475,8 +475,10 @@ namespace Ark if (!context.saved_scope) context.saved_scope = std::make_shared(); - // if it's a captured variable, it can not be nullptr + Value* ptr = (*context.locals.back())[id]; + if (!ptr) + throwVMError("Couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound"); ptr = ptr->valueType() == ValueType::Reference ? ptr->reference() : ptr; (*context.saved_scope.value()).push_back(id, *ptr); From f62923305b1f80e7ba85c8b6ea24da890ca183c6 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 16:11:02 +0100 Subject: [PATCH 21/42] fix: compiler checks if operators receive the correct number of arguments Former-commit-id: a7ad0e7784d69fd4b70df30ae4b45363d2dda88e --- CHANGELOG.md | 1 + include/Ark/Compiler/Compiler.hpp | 9 +++++++ src/arkreactor/Compiler/AST/makeErrorCtx.cpp | 2 +- src/arkreactor/Compiler/Compiler.cpp | 28 +++++++++++++++++++- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 202b8d4b4..8b079a125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - calling a non-callable anonymous object do not result in a segfault - macro processor function registering now handles empty nodes - added a fix to avoid crashes when capturing unbound variables +- checking if the given operator takes one or more arguments at compile time ### Removed diff --git a/include/Ark/Compiler/Compiler.hpp b/include/Ark/Compiler/Compiler.hpp index 587085b36..d77d6a1b7 100644 --- a/include/Ark/Compiler/Compiler.hpp +++ b/include/Ark/Compiler/Compiler.hpp @@ -184,6 +184,15 @@ namespace Ark return std::nullopt; } + /** + * @brief Check if a given instruction is unary (takes only one argument) + * + * @param inst + * @return true the instruction is unary + * @return false + */ + bool isUnaryInst(internal::Instruction inst) noexcept; + /** * @brief Compute specific instruction argument count * diff --git a/src/arkreactor/Compiler/AST/makeErrorCtx.cpp b/src/arkreactor/Compiler/AST/makeErrorCtx.cpp index 0ad646525..968e453c8 100644 --- a/src/arkreactor/Compiler/AST/makeErrorCtx.cpp +++ b/src/arkreactor/Compiler/AST/makeErrorCtx.cpp @@ -101,7 +101,7 @@ namespace Ark::internal std::string makeNodeBasedErrorCtx(const std::string& message, const Node& node) { std::stringstream ss; - ss << message << "\n"; + ss << message << "\n\n"; if (node.filename() != ARK_NO_NAME_FILE) ss << "In file " << node.filename() << "\n"; ss << "On line " << (node.line() + 1) << ":" << node.col() << ", got `" << node << "'\n"; diff --git a/src/arkreactor/Compiler/Compiler.cpp b/src/arkreactor/Compiler/Compiler.cpp index c5e97fd71..d920ff199 100644 --- a/src/arkreactor/Compiler/Compiler.cpp +++ b/src/arkreactor/Compiler/Compiler.cpp @@ -208,6 +208,27 @@ namespace Ark return std::nullopt; } + bool Compiler::isUnaryInst(Instruction inst) noexcept + { + switch (inst) + { + case Instruction::NOT: [[fallthrough]]; + case Instruction::LEN: [[fallthrough]]; + case Instruction::EMPTY: [[fallthrough]]; + case Instruction::TAIL: [[fallthrough]]; + case Instruction::HEAD: [[fallthrough]]; + case Instruction::ISNIL: [[fallthrough]]; + case Instruction::TO_NUM: [[fallthrough]]; + case Instruction::TO_STR: [[fallthrough]]; + case Instruction::TYPE: [[fallthrough]]; + case Instruction::HASFIELD: + return true; + + default: + return false; + } + } + void Compiler::pushSpecificInstArgc(Instruction inst, uint16_t previous, int p) noexcept { if (inst == Instruction::LIST) @@ -653,7 +674,12 @@ namespace Ark } if (exp_count == 1) - page(p).push_back(op_inst); + { + if (isUnaryInst(static_cast(op_inst))) + page(p).push_back(op_inst); + else + throwCompilerError("Operator needs two arguments, but was called with only one", x.constList()[0]); + } // need to check we didn't push the (op A B C D...) things for operators not supporting it if (exp_count > 2) From a8fa7bbc2e8d44d0c8915d76e47d5704900b0fbd Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 16:27:55 +0100 Subject: [PATCH 22/42] fix: adding bound checking on operator @ Former-commit-id: 023fd5f43b546b781929afb52ad1929542c55d33 --- CHANGELOG.md | 1 + src/arkreactor/VM/VM.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b079a125..5b1d9635b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - macro processor function registering now handles empty nodes - added a fix to avoid crashes when capturing unbound variables - checking if the given operator takes one or more arguments at compile time +- adding bound checking on operator @ ### Removed diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 47c6ecc8c..3c2e86ba0 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -1060,9 +1060,19 @@ namespace Ark long idx = static_cast(b->number()); if (a.valueType() == ValueType::List) - push(a.list()[idx < 0 ? a.list().size() + idx : idx], context); + { + if (static_cast(std::abs(idx)) < a.list().size()) + push(a.list()[idx < 0 ? a.list().size() + idx : idx], context); + else + throwVMError("Index (" + std::to_string(idx) + ") out of range (list size: " + std::to_string(a.list().size()) + ")"); + } else if (a.valueType() == ValueType::String) - push(Value(std::string(1, a.string()[idx < 0 ? a.string().size() + idx : idx])), context); + { + if (static_cast(std::abs(idx)) < a.string().size()) + push(Value(std::string(1, a.string()[idx < 0 ? a.string().size() + idx : idx])), context); + else + throwVMError("Index (" + std::to_string(idx) + ") out of range (string size: " + std::to_string(a.string().size()) + ")"); + } else types::generateError( "@", From 5489623e39db62c127515c521905c3e97e0c69ee Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 17:22:54 +0100 Subject: [PATCH 23/42] fix: adding bound checking on operator @ when used in macros Former-commit-id: fc07ac823c8db5b4b5eee29c7dbf2180ba5ac412 --- CHANGELOG.md | 1 + src/arkreactor/Compiler/Macros/Processor.cpp | 22 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b1d9635b..bbb323e0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - added a fix to avoid crashes when capturing unbound variables - checking if the given operator takes one or more arguments at compile time - adding bound checking on operator @ +- adding bound checking on operator @ when used in macros ### Removed diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp index 5d401647d..f5980ba5d 100644 --- a/src/arkreactor/Compiler/Macros/Processor.cpp +++ b/src/arkreactor/Compiler/Macros/Processor.cpp @@ -98,7 +98,7 @@ namespace Ark::internal } } // !{if cond then else} - else if (std::size_t sz = node.constList().size(); sz == 3 || sz == 4) + else if (std::size_t size = node.constList().size(); size == 3 || size == 4) { if (first_node.nodeType() == NodeType::Keyword && first_node.keyword() == Keyword::If) { @@ -351,19 +351,23 @@ namespace Ark::internal if (sublist.nodeType() == NodeType::List && idx.nodeType() == NodeType::Number) { + long size = static_cast(sublist.list().size()); + long real_size = size; long num_idx = static_cast(idx.number()); - long sz = static_cast(sublist.list().size()); - long offset = 0; - if (sz > 0 && sublist.list()[0] == Node::getListNode()) + + // if the first node is the function call to "list", don't count it + if (size > 0 && sublist.list()[0] == Node::getListNode()) { - num_idx = (num_idx >= 0) ? num_idx + 1 : num_idx; - offset = -1; + real_size--; + if (num_idx >= 0) + ++num_idx; } + num_idx = num_idx >= 0 ? num_idx : size + num_idx; - if (num_idx < 0 && sz + num_idx >= 0 && -num_idx < sz) - return sublist.list()[sz + num_idx]; - else if (num_idx >= 0 && num_idx + offset < sz) + if (num_idx < size) return sublist.list()[num_idx]; + else + throwMacroProcessingError("Index (" + std::to_string(static_cast(idx.number())) + ") out of range (list size: " + std::to_string(real_size) + ")", node); } } else if (name == "head") From 5e97f6c667332a3a19a3d3eab0d5898a3d6a9448 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Tue, 1 Nov 2022 18:36:14 +0100 Subject: [PATCH 24/42] tests: added some failure tests (#328) Former-commit-id: 3b0b7a4d7887442016a48bda03bcff920b72a967 --- .github/launch-tests | 37 ++---------------------- CHANGELOG.md | 1 + tests/arkscript/run-tests | 16 +++++++++++ tests/ast/run-tests | 3 +- tests/cpp/run-tests | 0 tests/errors/callable_0.ark | 1 + tests/errors/callable_0.expected | 1 + tests/errors/capture_0.ark | 1 + tests/errors/capture_0.expected | 1 + tests/errors/operators_0.ark | 1 + tests/errors/operators_0.expected | 1 + tests/errors/operators_1.ark | 1 + tests/errors/operators_1.expected | 1 + tests/errors/run-tests | 48 +++++++++++++++++++++++++++++++ 14 files changed, 77 insertions(+), 36 deletions(-) create mode 100755 tests/arkscript/run-tests mode change 100644 => 100755 tests/cpp/run-tests create mode 100644 tests/errors/callable_0.ark create mode 100644 tests/errors/callable_0.expected create mode 100644 tests/errors/capture_0.ark create mode 100644 tests/errors/capture_0.expected create mode 100644 tests/errors/operators_0.ark create mode 100644 tests/errors/operators_0.expected create mode 100644 tests/errors/operators_1.ark create mode 100644 tests/errors/operators_1.expected create mode 100755 tests/errors/run-tests diff --git a/.github/launch-tests b/.github/launch-tests index b6845d94c..6c79721eb 100755 --- a/.github/launch-tests +++ b/.github/launch-tests @@ -1,38 +1,7 @@ #!/usr/bin/env bash -######################################### -# Unit tests -######################################### - -file=arkscript - -if [ -f build/Release/${file}.exe ]; then - ark=./build/Release/${file}.exe -elif [ -f build/${file} ]; then - ark=./build/${file} -else - echo "No ark executable found" && exit 1 -fi - -$ark tests/arkscript/unittests.ark --lib lib/ || exit 1 -for file in lib/std/tests/*.ark; do - $ark $file --lib lib/ -done - -######################################### -# Integration tests -######################################### - +(cd tests/arkscript ; echo ; bash ./run-tests) (cd tests/cpp/ ; echo ; bash ./run-tests) - -######################################### -# AST tests -######################################### - +(cd tests/errors ; echo ; bash ./run-tests) (cd tests/ast/ ; echo ; bash ./run-tests) - -######################################### -# Module tests -######################################### - -(echo ; source ./lib/modules/.github/run-tests) +(source ./lib/modules/.github/run-tests) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb323e0f..51f0df666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased version] ### Added - added fuzzing tools and corpus for [AFL](https://github.com/AFLplusplus/AFLplusplus) +- added some tests for errors ### Changed - plugins can be constructed from outside ArkScript lib/modules folder, easing the development process diff --git a/tests/arkscript/run-tests b/tests/arkscript/run-tests new file mode 100755 index 000000000..48e5226c6 --- /dev/null +++ b/tests/arkscript/run-tests @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +file=arkscript + +if [ -f ../../build/Release/${file}.exe ]; then + ark=../../build/Release/${file}.exe +elif [ -f ../../build/${file} ]; then + ark=../../build/${file} +else + echo "No $file executable found" && exit 1 +fi + +$ark unittests.ark --lib ../../lib/ || exit 1 +for file in ../../lib/std/tests/*.ark; do + $ark $file --lib ../../lib/ +done diff --git a/tests/ast/run-tests b/tests/ast/run-tests index 567979635..a66c57d21 100755 --- a/tests/ast/run-tests +++ b/tests/ast/run-tests @@ -7,7 +7,7 @@ if [ -f ../../build/Release/${file}.exe ]; then elif [ -f ../../build/${file} ]; then ark=../../build/${file} else - echo "No ark executable found" && exit 1 + echo "No $file executable found" && exit 1 fi Reset='\033[0m' @@ -36,7 +36,6 @@ for f in ./*.json; do echo -e "${Green}PASSED${Reset} ${f%.*}" ((passed=passed+1)) fi - done echo " ------------------------------" diff --git a/tests/cpp/run-tests b/tests/cpp/run-tests old mode 100644 new mode 100755 diff --git a/tests/errors/callable_0.ark b/tests/errors/callable_0.ark new file mode 100644 index 000000000..9636f8380 --- /dev/null +++ b/tests/errors/callable_0.ark @@ -0,0 +1 @@ +(()) \ No newline at end of file diff --git a/tests/errors/callable_0.expected b/tests/errors/callable_0.expected new file mode 100644 index 000000000..f7b3c6802 --- /dev/null +++ b/tests/errors/callable_0.expected @@ -0,0 +1 @@ +A Nil isn't a callable \ No newline at end of file diff --git a/tests/errors/capture_0.ark b/tests/errors/capture_0.ark new file mode 100644 index 000000000..71de9f029 --- /dev/null +++ b/tests/errors/capture_0.ark @@ -0,0 +1 @@ +(mut d (fun (&d) (print d))) \ No newline at end of file diff --git a/tests/errors/capture_0.expected b/tests/errors/capture_0.expected new file mode 100644 index 000000000..fd0f6b094 --- /dev/null +++ b/tests/errors/capture_0.expected @@ -0,0 +1 @@ +Couldn't capture 'd' as it is currently unbound \ No newline at end of file diff --git a/tests/errors/operators_0.ark b/tests/errors/operators_0.ark new file mode 100644 index 000000000..62cdd377b --- /dev/null +++ b/tests/errors/operators_0.ark @@ -0,0 +1 @@ +(print (!= 1)) \ No newline at end of file diff --git a/tests/errors/operators_0.expected b/tests/errors/operators_0.expected new file mode 100644 index 000000000..3c539f1ef --- /dev/null +++ b/tests/errors/operators_0.expected @@ -0,0 +1 @@ +CompilationError: Operator needs two arguments, but was called with only one \ No newline at end of file diff --git a/tests/errors/operators_1.ark b/tests/errors/operators_1.ark new file mode 100644 index 000000000..7cc052555 --- /dev/null +++ b/tests/errors/operators_1.ark @@ -0,0 +1 @@ +(@ [] 0) \ No newline at end of file diff --git a/tests/errors/operators_1.expected b/tests/errors/operators_1.expected new file mode 100644 index 000000000..d02aa51d2 --- /dev/null +++ b/tests/errors/operators_1.expected @@ -0,0 +1 @@ +Index (0) out of range (list size: 0) \ No newline at end of file diff --git a/tests/errors/run-tests b/tests/errors/run-tests new file mode 100755 index 000000000..e1b059bbc --- /dev/null +++ b/tests/errors/run-tests @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +file=arkscript + +if [ -f ../../build/Release/${file}.exe ]; then + ark=../../build/Release/${file}.exe +elif [ -f ../../build/${file} ]; then + ark=../../build/${file} +else + echo "No $file executable found" && exit 1 +fi + +Reset='\033[0m' +Black='\033[0;30m' +Red='\033[0;31m' +Green='\033[0;32m' +Yellow='\033[0;33m' +Blue='\033[0;34m' +Purple='\033[0;35m' +Cyan='\033[0;36m' +White='\033[0;37m' + +passed=0 +failed=0 + +for f in ./*.ark; do + output=$($ark $f --lib ../../lib 2>&1) + expected=$(cat ${f%.*}.expected) + + if [[ $(echo $output | grep "$expected") == "" ]]; then + echo -e "${Red}FAILED${Reset} ${f%.*}" + ((failed=failed+1)) + echo -e " ${Yellow}Output${Reset}:" + echo $output + else + echo -e "${Green}PASSED${Reset} ${f%.*}" + ((passed=passed+1)) + fi +done + +echo " ------------------------------" +echo -e " ${Cyan}${passed}${Reset} passed, ${Purple}${failed}${Reset} failed" + +if [[ $failed != 0 ]]; then + exit 1 +else + exit 0 +fi From 325e6deb45d2148fff525f5e13b9494992dc6cb3 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 6 Nov 2022 16:28:12 +0100 Subject: [PATCH 25/42] fix: better arity check for macros (closes #411) Former-commit-id: 625b503e49c70b69c0b056ba9c9da64c97f19174 --- CHANGELOG.md | 1 + .../Compiler/Macros/Executors/List.cpp | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f0df666..66931a422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - checking if the given operator takes one or more arguments at compile time - adding bound checking on operator @ - adding bound checking on operator @ when used in macros +- better arity check for macros ### Removed diff --git a/src/arkreactor/Compiler/Macros/Executors/List.cpp b/src/arkreactor/Compiler/Macros/Executors/List.cpp index 25762c439..88c26e825 100644 --- a/src/arkreactor/Compiler/Macros/Executors/List.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/List.cpp @@ -1,4 +1,5 @@ #include +#include namespace Ark::internal { @@ -21,12 +22,20 @@ namespace Ark::internal { Node temp_body = macro->constList()[2]; Node args = macro->constList()[1]; + std::size_t args_needed = args.list().size(); + std::size_t args_given = node.constList().size() - 1; // remove the first (the name of the macro) + std::string macro_name = macro->constList()[0].string(); + bool has_spread = args.list().back().nodeType() == NodeType::Spread; // bind node->list() to temp_body using macro->constList()[1] std::unordered_map args_applied; std::size_t j = 0; for (std::size_t i = 1, end = node.constList().size(); i < end; ++i) { + // by breaking early if we have too many arguments, the args_applied/args_needed check will fail + if (j >= args_needed) + break; + const std::string& arg_name = args.list()[j].string(); if (args.list()[j].nodeType() == NodeType::Symbol) { @@ -46,22 +55,18 @@ namespace Ark::internal } // check argument count - if (args_applied.size() + 1 == args.list().size() && args.list().back().nodeType() == NodeType::Spread) + if (args_applied.size() + 1 == args_needed && has_spread) { // just a spread we didn't assign args_applied[args.list().back().string()] = Node(NodeType::List); args_applied[args.list().back().string()].push_back(Node::getListNode()); } - else if (args_applied.size() != args.list().size()) - { - std::size_t args_needed = args.list().size(); - std::string macro_name = macro->constList()[0].string(); - if (args.list().back().nodeType() != NodeType::Spread) - throwMacroProcessingError("Macro `" + macro_name + "' got " + std::to_string(args_applied.size()) + " argument(s) but needed " + std::to_string(args_needed), *macro); - else - throwMacroProcessingError("Macro `" + macro_name + "' got " + std::to_string(args_applied.size()) + " argument(s) but needed at least " + std::to_string(args_needed - 1), *macro); - } + if (args_given != args_needed && !has_spread) + throwMacroProcessingError("Macro `" + macro_name + "' got " + std::to_string(args_given) + " argument(s) but needed " + std::to_string(args_needed), node); + else if (args_applied.size() != args_needed && has_spread) + // args_needed - 1 because we do not count the spread as a required argument + throwMacroProcessingError("Macro `" + macro_name + "' got " + std::to_string(args_applied.size()) + " argument(s) but needed at least " + std::to_string(args_needed - 1), node); if (!args_applied.empty()) unify(args_applied, temp_body, nullptr); From d1b897fa135692dfe84c4e1c38754dbd4596e19c Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 6 Nov 2022 16:28:48 +0100 Subject: [PATCH 26/42] tests: reorganizing the errors tests and adding macros tests Former-commit-id: 81201d7c1a9cb11aba955136409b797b36678e8b --- tests/errors/{callable_0.ark => callable/not_callable.ark} | 0 .../{callable_0.expected => callable/not_callable.expected} | 0 tests/errors/{capture_0.ark => capture/unbound.ark} | 0 tests/errors/{capture_0.expected => capture/unbound.expected} | 0 tests/errors/macros/not_enough_args.ark | 4 ++++ tests/errors/macros/not_enough_args.expected | 1 + tests/errors/macros/too_many_args.ark | 4 ++++ tests/errors/macros/too_many_args.expected | 1 + .../errors/{operators_0.ark => operators/not_enough_args.ark} | 0 .../not_enough_args.expected} | 0 tests/errors/{operators_1.ark => operators/out_of_range.ark} | 0 .../{operators_1.expected => operators/out_of_range.expected} | 0 tests/errors/run-tests | 2 +- 13 files changed, 11 insertions(+), 1 deletion(-) rename tests/errors/{callable_0.ark => callable/not_callable.ark} (100%) rename tests/errors/{callable_0.expected => callable/not_callable.expected} (100%) rename tests/errors/{capture_0.ark => capture/unbound.ark} (100%) rename tests/errors/{capture_0.expected => capture/unbound.expected} (100%) create mode 100644 tests/errors/macros/not_enough_args.ark create mode 100644 tests/errors/macros/not_enough_args.expected create mode 100644 tests/errors/macros/too_many_args.ark create mode 100644 tests/errors/macros/too_many_args.expected rename tests/errors/{operators_0.ark => operators/not_enough_args.ark} (100%) rename tests/errors/{operators_0.expected => operators/not_enough_args.expected} (100%) rename tests/errors/{operators_1.ark => operators/out_of_range.ark} (100%) rename tests/errors/{operators_1.expected => operators/out_of_range.expected} (100%) diff --git a/tests/errors/callable_0.ark b/tests/errors/callable/not_callable.ark similarity index 100% rename from tests/errors/callable_0.ark rename to tests/errors/callable/not_callable.ark diff --git a/tests/errors/callable_0.expected b/tests/errors/callable/not_callable.expected similarity index 100% rename from tests/errors/callable_0.expected rename to tests/errors/callable/not_callable.expected diff --git a/tests/errors/capture_0.ark b/tests/errors/capture/unbound.ark similarity index 100% rename from tests/errors/capture_0.ark rename to tests/errors/capture/unbound.ark diff --git a/tests/errors/capture_0.expected b/tests/errors/capture/unbound.expected similarity index 100% rename from tests/errors/capture_0.expected rename to tests/errors/capture/unbound.expected diff --git a/tests/errors/macros/not_enough_args.ark b/tests/errors/macros/not_enough_args.ark new file mode 100644 index 000000000..c90e14de4 --- /dev/null +++ b/tests/errors/macros/not_enough_args.ark @@ -0,0 +1,4 @@ +!{foo (a b c) + (+ a b c)} + +(foo 1 2) \ No newline at end of file diff --git a/tests/errors/macros/not_enough_args.expected b/tests/errors/macros/not_enough_args.expected new file mode 100644 index 000000000..c301f443c --- /dev/null +++ b/tests/errors/macros/not_enough_args.expected @@ -0,0 +1 @@ +Macro `foo' got 2 argument(s) but needed 3 \ No newline at end of file diff --git a/tests/errors/macros/too_many_args.ark b/tests/errors/macros/too_many_args.ark new file mode 100644 index 000000000..79eafadd9 --- /dev/null +++ b/tests/errors/macros/too_many_args.ark @@ -0,0 +1,4 @@ +!{foo (a b c) + (+ a b c)} + +(foo 1 2 3 4) \ No newline at end of file diff --git a/tests/errors/macros/too_many_args.expected b/tests/errors/macros/too_many_args.expected new file mode 100644 index 000000000..2f40f9b67 --- /dev/null +++ b/tests/errors/macros/too_many_args.expected @@ -0,0 +1 @@ +Macro `foo' got 4 argument(s) but needed 3 \ No newline at end of file diff --git a/tests/errors/operators_0.ark b/tests/errors/operators/not_enough_args.ark similarity index 100% rename from tests/errors/operators_0.ark rename to tests/errors/operators/not_enough_args.ark diff --git a/tests/errors/operators_0.expected b/tests/errors/operators/not_enough_args.expected similarity index 100% rename from tests/errors/operators_0.expected rename to tests/errors/operators/not_enough_args.expected diff --git a/tests/errors/operators_1.ark b/tests/errors/operators/out_of_range.ark similarity index 100% rename from tests/errors/operators_1.ark rename to tests/errors/operators/out_of_range.ark diff --git a/tests/errors/operators_1.expected b/tests/errors/operators/out_of_range.expected similarity index 100% rename from tests/errors/operators_1.expected rename to tests/errors/operators/out_of_range.expected diff --git a/tests/errors/run-tests b/tests/errors/run-tests index e1b059bbc..8e75b8ba1 100755 --- a/tests/errors/run-tests +++ b/tests/errors/run-tests @@ -23,7 +23,7 @@ White='\033[0;37m' passed=0 failed=0 -for f in ./*.ark; do +for f in ./**/*.ark; do output=$($ark $f --lib ../../lib 2>&1) expected=$(cat ${f%.*}.expected) From 5ccba08ace035417e73f4215a8375673f0a8c470 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 6 Nov 2022 16:48:10 +0100 Subject: [PATCH 27/42] ci: reorganizing the GitHub CI workflows Former-commit-id: 04b758a44f02b285997f00f81414f024a483e927 --- .github/workflows/ci.yml | 40 +++++++++++++++++++++++++-- .github/workflows/clang-format.yml | 23 --------------- .github/workflows/repo-visualizer.yml | 23 --------------- 3 files changed, 37 insertions(+), 49 deletions(-) delete mode 100644 .github/workflows/clang-format.yml delete mode 100644 .github/workflows/repo-visualizer.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2f003fe3..50c1ab05a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,15 +1,13 @@ -name: "CMake" +name: "Building and testing ArkScript" on: push: branches: [dev, master] paths-ignore: - - '.github/workflows/clang-format.yml' - '.github/workflows/docker.yml' - '.github/workflows/label.yml' - '.github/workflows/lizard.yml' - '.github/workflows/release.yml' - - '.github/workflows/repo-visualizer.yml' - '.vscode/*.*' - 'examples/*.ark' - 'images/*.*' @@ -27,9 +25,45 @@ env: SQLITE_VERSION: 3390100 # 3.39.1 jobs: + check: + name: Formatting check + runs-on: ubuntu-latest + + strategy: + matrix: + path: + - 'src' + - 'include' + + steps: + - uses: actions/checkout@v2 + + - name: Run clang-format check for C++ + uses: HorstBaerbel/action-clang-format@master + with: + scandir: ${{ matrix.path }} + style: 'file' + + repo_visualizer: + runs-on: ubuntu-latest + needs: [] + + steps: + - name: Checkout code + uses: actions/checkout@master + + - name: Update diagram + uses: githubocto/repo-visualizer@main + with: + excluded_paths: 'dist,node_modules,submodules' + should_push: false + output_file: 'diagram.svg' + artifact_name: 'diagram' + build: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.name }} + needs: [check] strategy: fail-fast: false diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml deleted file mode 100644 index 015c6c839..000000000 --- a/.github/workflows/clang-format.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: clang-format check - -on: [pull_request] - -jobs: - check: - name: Formatting check - runs-on: ubuntu-latest - - strategy: - matrix: - path: - - 'src' - - 'include' - - steps: - - uses: actions/checkout@v2 - - - name: Run clang-format check for C++ - uses: HorstBaerbel/action-clang-format@master - with: - scandir: ${{ matrix.path }} - style: 'file' diff --git a/.github/workflows/repo-visualizer.yml b/.github/workflows/repo-visualizer.yml deleted file mode 100644 index 2764224e2..000000000 --- a/.github/workflows/repo-visualizer.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Repo visualizer - -on: - push: - branches: [ master, dev ] - pull_request: - branches: [ master, dev ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@master - - - name: Update diagram - uses: githubocto/repo-visualizer@main - with: - excluded_paths: 'dist,node_modules,submodules' - should_push: false - output_file: 'diagram.svg' - artifact_name: 'diagram' From 06f090036c359762806b834076afe4018a9ffc5a Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 6 Nov 2022 17:15:57 +0100 Subject: [PATCH 28/42] ci: migrating checkout action to v3 and upload action to v3.1.1 Former-commit-id: c62de6436384ed9f3c0cc13b747b07a91df1da1b --- .github/workflows/ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c1ab05a..e12607add 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - 'include' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run clang-format check for C++ uses: HorstBaerbel/action-clang-format@master @@ -50,7 +50,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@master + uses: actions/checkout@v3 - name: Update diagram uses: githubocto/repo-visualizer@main @@ -121,7 +121,7 @@ jobs: } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: recursive @@ -226,13 +226,13 @@ jobs: cp -r tests/cpp temp/ - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 with: name: ${{ matrix.config.artifact }} path: artifact - name: Upload temp artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 with: name: temp-${{ matrix.config.artifact }} path: temp @@ -259,20 +259,20 @@ jobs: - { os: macos-latest, name: "MacOS Clang 12", artifact: "macos-clang-12", } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: recursive - name: Download artifact id: download - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3.0.1 with: name: ${{ matrix.config.artifact }} path: build - name: Download temp artifact id: download-artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3.0.1 with: name: temp-${{ matrix.config.artifact }} path: artifact @@ -310,13 +310,13 @@ jobs: needs: [build] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: recursive - name: Download artifact id: download - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3.0.1 with: name: "ubuntu-clang-11-valgrind" path: build From 1f49d457a3012f86ad03d3dcdc0603ecde273ce3 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 6 Nov 2022 21:12:49 +0100 Subject: [PATCH 29/42] temporarily removing arkc files from the fuzzing corpus --- fuzzing/corpus/events-tests.arkc | Bin 8067 -> 0 bytes fuzzing/corpus/exceptions-tests.arkc | Bin 2344 -> 0 bytes fuzzing/corpus/functional-tests.arkc | Bin 2490 -> 0 bytes fuzzing/corpus/lazy-tests.arkc | Bin 2290 -> 0 bytes fuzzing/corpus/list-tests.arkc | Bin 9862 -> 0 bytes fuzzing/corpus/macros-tests.arkc | Bin 2450 -> 0 bytes fuzzing/corpus/math-tests.arkc | Bin 6710 -> 0 bytes fuzzing/corpus/range-tests.arkc | Bin 5581 -> 0 bytes fuzzing/corpus/string-tests.arkc | Bin 4960 -> 0 bytes fuzzing/corpus/switch-tests.arkc | Bin 2292 -> 0 bytes fuzzing/corpus/tests-tools.arkc | Bin 1520 -> 0 bytes fuzzing/fuzz.sh | 16 ++++++++++++++-- fuzzing/input/tests-tools.arkc | Bin 5 -> 0 bytes fuzzing/reduce_crashes | 8 ++++++++ fuzzing/unique/tests-tools.arkc | Bin 1520 -> 0 bytes 15 files changed, 22 insertions(+), 2 deletions(-) delete mode 100644 fuzzing/corpus/events-tests.arkc delete mode 100644 fuzzing/corpus/exceptions-tests.arkc delete mode 100644 fuzzing/corpus/functional-tests.arkc delete mode 100644 fuzzing/corpus/lazy-tests.arkc delete mode 100644 fuzzing/corpus/list-tests.arkc delete mode 100644 fuzzing/corpus/macros-tests.arkc delete mode 100644 fuzzing/corpus/math-tests.arkc delete mode 100644 fuzzing/corpus/range-tests.arkc delete mode 100644 fuzzing/corpus/string-tests.arkc delete mode 100644 fuzzing/corpus/switch-tests.arkc delete mode 100644 fuzzing/corpus/tests-tools.arkc delete mode 100644 fuzzing/input/tests-tools.arkc create mode 100755 fuzzing/reduce_crashes delete mode 100644 fuzzing/unique/tests-tools.arkc diff --git a/fuzzing/corpus/events-tests.arkc b/fuzzing/corpus/events-tests.arkc deleted file mode 100644 index 671fe7d24903236b226b8216d34b6fdf3babdc76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8067 zcmb_gTXz)45$}W%Xib(z%w;!GP z+n@h4`R~(rYpp~7o%jCr>ercHm;Ulk?@jgVzyEdYmw)s>FhpRt+fEeMoHydF?M?9C zr0|_~+g@`-?6l*yNH_wpB*V41J95&I*QI$~8iY~cxQLvFy{W|20^5gR*LUio5eDth zb7mT$7e>N!+wn{*jOOgdx~MORdaDyOMBNRV&U;aJJjZu}SaWSgVYAbSMLq1qo1KK% z?)bvDa@%3l7IC<#C%kY?B(gAsBWVvt6XS_AFAO6r6ShcnH$q6p1|tW) za(#PEGbZGk4U_AQalk}zqTMM9sQDeQ$N}&E+*z|@2aAg{KD>t@DdSQ_G^)-eqR!pa zn@$k=+S|^No&Xs`}3D38HfkDRuq z#P&wQ&5>3BQ(`yhB=k=3i;ES$s8hs*Mw6n^iK1>&kV7j7z2<_`LVCD?i~r2RWFngN zJP@47ZMjZUQ|(|&Fiqn2jBf`>`v@2YDOFD3Ainj+y3^R8Ah#(Rw&$(djSUe7BHr2* zj_<}V+<2XtE?QwHXo@shIfcmj;ag_`b}M0vt(C1!M+DA0+9aCbjbVzmpr$LX9or~L z773(_y4YmhHEWtjNUA~eBr)zcg~u7_I!51 zT3G~D7!xu;21?r zrvW<-$_c+&hY{|;jOI3~ci915k*a$MZY72eh zcg!C)0X?&GL+BH~7jjPZAxm`uOIJN?X&@a0D%KFD!WCc?jp)aJR0I;(6v%UAnaN~E zmYK*uiG(silSn8NG>L$i7)u1iWEvqTCSoZO7!&c72#kp!N(9D45G8_RB9IcHF|kG> zJSNsigvi7ii4d6xs6>cN@JNKn1Wh6qCTJ4zFcCV5c$k=zh>3|giI|w6NyNp(Si;vP zV+0?X^gH}z(r0jpNvDWE0N(}v8u%XYEbtugUEl}6_kbS)-v^!tegOO}@I&AQ;CbN3 zz~2Bb0)Gp<0{jU0Iq(AT_rQ;V{|Nj9_y^#Zz%IP*!ixf9A6`W`P6*U3Kmz&z6+H1r z2cfOvXMk?x=mT&wgF|P?WR4C!0I_?Bq+cbLengR%mB{MH6p1p%4?XYWZG2RTJp3_5 zUQr?$yblb|QAGK0F4~77=txe?VU!mmT&b*iudSl#O0;>sE|qt>B>zllez-8tWZixJr=|I`h4m8s z!oa>R!V-y|p=HTrZ<#IKgg=9g4FQgUeFXS8mJ^25b{YGAY>CgLPvUtHb!R9zmU9~e zd0aK83UC$MD5*%r(|3bPWLeEgh$yP!E-IkuQq7BJ9iHl~f;*w8y;ZRK_oZS0A{nb; zEEvJDNt;-@!}!nMeo2jKad+bfu!Jcwf+^xr9E!R(by*H2EOcMSV&D{DMzW1{S(*3e z0hD$gkQ#A7jjO8Y&+o?0cEd{+m+r_utTbrp!$_TT$_Q?00BJ>oy2j9Mdg3~W`(UN& z8k#4rzBt_I5gpP|NTo?h_oV@s0apN@cUg74F`Rk_^a`850X_qK3?Xqk&y%qVkyX?u z=~Y#$Bbk~TN{wb81p`avi=<$1S(3p)^!NYnZpXTX$=rqi$CFa+ZSo%cbLdGLa zZ%JL|@HCmwYTZzBJ`F4H3jKO((hVWzp>3BA6;# zY$ig}N+MWzJ6pl>&?R29gmj9^tV}JRR^2(>MXH|snzy8yb$@n$6}>!!vSIfTSx#R|ws2pWxw~m>WZ7wJ z;4;k;jO+ZS5lvvdJL-YURv13Zl;w?IQs!6 zl+|bxb`NMt-Df#{S}8beyt?=qkYeRz&A?E16*`|Z`r)i<4E;&NAdSIHP1)Hi_$ZVQQg5%2aS(5( z<$^Ro)5rhLUgFo90R*lBzN#wdyeD_7kW&7PDX%aPd?_)-)1=isOYl-Rx3q~*6K@pF z8WhXe!m{Gk-g%a^!Dk7bpLFQ8rG!qoysGA};_OaK0P4=2+f4E7x=J&7@6?zVOTJ5| zX5*Vp65OJ>On-LuGM>RE9Al^}z}wB@+vT0C1E)RZJD8QFxgq%cLWxbiso#0n zS4htXh`oh5_F@Nhd=+?+sbi|@iqg6A5=+w8tC!o<>73!4TUi|sP=4w-iF`So8;hx(IA8oq+1i-_}&z)Qe&;ALPN_&Klx{2g!$ zx4>6`9|3iM1F!)t%zvNlimzZlf%FvC|I}?W5}B8Oo&rbr#(D%xUaMtfF`z*ord9Zp z8A94?mAD2h(Ao4VTTYFuN(9Sx5FY@^Ch^hAz#WF!z>fw7=v|G+z}8e!4dCS}{@6)$$0#%M5zMfrMNeUEkYDcAAiY~C(AS?7RmKt97& zmd_MsHWck*2qogA>AlzM{r7>lnegh^N~HdFT}+i$!k76#>teUsTZY!hq@ca6WM52- t^1jv}&+VaWKkJzfS$-JEj|-4!AmZ7lwA{C$Qik!PoCn@q4*s8{{{hE<2M7QF diff --git a/fuzzing/corpus/exceptions-tests.arkc b/fuzzing/corpus/exceptions-tests.arkc deleted file mode 100644 index b90bf5c3f282100620a64172b89eab552f32e218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2344 zcmb_cOH&g;5bm914M}*1_j3_~AcmL4K|G zdi9@p^W@zh;LVd?Po5hMlvV6h@62|8{Y_7I@0ji>QJAVv{^7HcFWDFL`R9+FneV?3 zKfa&+u(YwcUB3U|>$gvHiFd!A#xG>X25e^{L z<*{F1$=b@U>+HG?WK6h9uFRWfiZ11tMG#v>nWKEk@k+Mb%$MwvOa6&ldPBJ~q2f6e5j8GHEO%lZ!^m!0KQpbGZA&c$7BM&kwhD zjcf*5($up_j(+39u z_FLCM|00jH^~mlYW_Mm9mtZ5>XOSh$}tmlUN{eWTTv7F={!$ z9=lRH$Sbm1_b7TXkVYXq$041NE=V_Nby$dcNaRh&Dfvdhy08k;IJz|6O1A`HFNt)m zQ4H=A==u;=ZI2N3Gu1||n29F5ryM+^(l>CNge#%m8tG~dV@As97T(Awj;|B4glKh6 z1)|jvy+E`&bOOQX& zpXKRArQ=Q2j)SPDi6)LV;tGeUZG|7hoktkb4RC}Mbzs@_H0!y+gFsu)H{=~;JI z*$0TD4`CZYgH=q$H$vmk4Fz-pxFG*Xi+wa;=VqMj2&bgiu&zj`$KImpi;WKd5Y+vGafM zKd`W}cE)i=D=Qm2dmAb({O!AsBw%Em;coW!-R}N=Z{NOs&vdtmk`zAWYj;OJy|ee; zjK6=8dHwmvul%doy|2Qai&U4MXI3To z((^qEBW#3(!>S*TWHhp3n-$wY#)R|a%Cfnm=<>E%2XU<~OH_7j&#~myvST?e{^~o9 zC#h7SQk6=(WXXz8rRP+t*^eysb;+$&ANo|WYCBXiskB9vCwAGdIksu#gKRzD#Bz87 zsyGfkCt0o1E^V1E?Qr@J@(l`KdWo{=yN2x*J+QK3YNL<-_cPx>ULNo-k9 zB#D_+%|>k1mC{CCQPmhx_l|=!1!0|rbU{u)x*)(6 z1NRAGp#QxybkCJjqQFb0BH|=&BL_JM3X|xmXaFW{Y@D0HEO+vZ>$7un@aePNr597SRBXCl>VC8eh zcDBtb{~)?izhFfd`ajpC#`~0IVes0!a-YB2__{={4sTRCY=cF4a zh`V(>j1}S98eDP1cVc0Ekbcy)57G}*)&&_Z6?e%19Ywx&Glf(KseK;!@Y^0b4ZCxJOCG8 zCF8J7Lr*}@Li25K0h$Y#2gL*g*EX&vrs8A*yP?MH4{USr@cl#hPpUVH`!_z|5%f3w zMes#=jDk0spL?5gJFRSj=G)B8SmtQP=0n@RunUSWs*#2oO#-E g7C|$rfN^iIYZ5NTpI2*%UA&dq(9P&c6%@Mv0I);!P5=M^ diff --git a/fuzzing/corpus/lazy-tests.arkc b/fuzzing/corpus/lazy-tests.arkc deleted file mode 100644 index d08e7c0e15a0b89f1b31b38b526622ccf7e9b056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2290 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 diff --git a/fuzzing/corpus/macros-tests.arkc b/fuzzing/corpus/macros-tests.arkc deleted file mode 100644 index 34a90c5e27d8f44f75dd1e82127e9c3d2bf4550a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2450 zcmb_dOLG%P5bhmGc4XPcZ-mWb42akUIg(7}5OTl?DqMxZV9N(i)Oa-#DptGB?iz|e zLUG6?m8u-L_T!tL?w*zMUl3&}{@8aO6bqjp z|EK3@G++;S!7OFqL#7IjPP`bpT_)3i;YrhX$_fsxE*#UHyz~&ka0B|6+TN=TleN zz893EJ%dmoDR!e)m6|G4)T&Wy!(cpX9l16sIw1S_xk?J(+OeWWhn#M+908M{+alg^ zUHLD%lp%{msU%lQrBzv$koLBA_P6iJ-|DjdWPfv8Hg=#TtGZq>A4G_u5s!8neNh?q z9)f;m~#(+ntx%l{_zD2h$b@0gF*G zGy$3fokQ|TCe}tO{hUbRF~+f@oOlZxJBHXn$-`K0E5}g>j!w5CvFW#M@5l|M*D|Rj zu1PX7ca#mu4L0u{({sc8xv1*Xg3Q$U%5mLOk-7vSemE(;_~*(NZvmRW*Uq2p!2%j(gO zBPHJKCu5ClXg@Zm(;zW2Sh=%=ylx;z&l2)S1BszMyMvbuWZ^6!Zx~2!-gmZV3Y{+W zbQ{-@uSyD2z_@USv%@?5JHmNoL6ZodM))~|SaiY6=nNztsaZgcVP%Y-b!)~xN1Q#+ zwxIdgCyE`VCFmCVbOL_;_{9PHg+4nEYqW2Y&tUxGfPJYiUeHB6PtsLnBIr`=E8?>q zW2FQC0B%=IImU71=z2{)_}hN`%Umg@LS-Qz{{X@L@pwpkHj~(g6F|K*^jAuf{p(rnQAB4HjFMg zyM=LBhNoo;m$W{6{C*fgsb*oGh0`*M)br}-HC-ESD)pPj0C*l$1o0?vi;AXwJOJFr lv{iYyEP*aU$@hjk&)r@?Y53nSeua%1H?BtH5>+lXe+3w2B**{& diff --git a/fuzzing/corpus/math-tests.arkc b/fuzzing/corpus/math-tests.arkc deleted file mode 100644 index 657139b414c4b67ebf244ebb78bcf218a12a4539..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6710 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 diff --git a/fuzzing/corpus/range-tests.arkc b/fuzzing/corpus/range-tests.arkc deleted file mode 100644 index 3d2053f9bbb50bad0bae970841b5bfc92f481937..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5581 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 diff --git a/fuzzing/corpus/string-tests.arkc b/fuzzing/corpus/string-tests.arkc deleted file mode 100644 index 80c0589aab7f1d6157d0c53d3ec2c4ef1b15fb08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4960 zcmcgv*>V#{6ur}utgsl2F`EfwY!ItuEVBhWn+*mV3yN&F@XL@TB_|BsAS$}ALM zS0XozLWw0B5Q!hxqV&m4p1hRjr96NUa7+TX;xsi}vEkGKT&uffsrZf1_uPq!@A-j* zQ7{qt^ZvRUK=Mr_6S5#;C0C7#<@i=N}MMpYw(O|J$MLbvGzP6YGHO@P+? zR;ZZ}k1uL^4&jwIc8J=66}X`ri8>={)oZz;&hDr+ zAiEa0NWN7s!-znCW%ZZG2Ej_a?jwjrV?1JEKv94WBSXODg zVn+)1h|=|I>q@b8huHO27}={XcD8O0`{~#S1Xx$Lsq3e>WUr{Dq?&F2nXN0_t~d?5 z;cIob-i+RM3K`uL-id(b;HnbP`mpw4^^0;=D^>T|+Vk3rmtMW$H(v!|)Ox-C=IuLC zQ`6UP%-p;+J9qoe{M~yC_a7`SEw4O$^!Ulwa3r=+cYLyI>0VaPnH+oF54@_Vsu%d{ zw(oiF^HeM8aOH>*7Ap;Pf@M0LnMz`-06=81E~PBkjtp1e$H;1BMDIq@Ys?r>PI0oh zTEUk-22H#cHHkk#Z?^DH6hfKm2CkV6WG1fgkt;UZHh#vt$%~_Iq_-v4O-CkRCzHTawc>3yQ9j9(<#v&;dC?qo7E0>;}|e4h5`a z59m(NKG5Bu$3go*3!q0p&w?HWJr8;eva3wPt6(P2cxAMBw81r+=qlbsUhE z)_6ajSW^1|wG*F==970$pAQJF&)^y*2Vxnco7c5`a05?4ZX^ETP3K28oU>TXy@@@W z^#?bdAKjp@^FEk2*y?(pX%^T^m)DS%&1-Aywp%x1x7b#^Y8aWOxue=#?#*z>Xx9|D zbi-UZjC>zPg(neb-JO8(0?Glb31iJ35bpgN1bp>$Dc_)#c9? zIF-WoBKO=gJUM)`$Sm-TiyR_+qKlMETss}RK`ipmrF-IZ@;cozS)|TJ8?wO2!%Fhg zLM?zXf`wmS0B0s>hAB1{&xkhJ%(`LhnJJoUy1xW7Or+bgNng>S3_?R(z7j0lVNBy4 z%v^uGXaDqI-x1ZnVl(&_*pEarYAhvo$0dmN=~1cVGM@Q!N3hT8kCD09Mdp&m;=6@! zJSA7&gF_kKwVljW$b1SJGrE;rdk+qsX0vQ3GXWVE`gSt-St`7bv*a_#nkrH9`Frl9 zTq65L7uhd6WSN%jaKDOW`5#|4#&)vPkhzXEfkadO2G$I8h7fZOjhlGhoV(3Bw;(s$ zMQ0B9%;6l??Jlykg|dwI_O@^^F>l@EOq?0(J6Q8TFTg+gdl%1}{ksP~Wtq!vvI~&8 z|1V`9KyDGeLf@9OPCxEevJB4oINmGov4?g{#xdz8YeSAPdDsQ>k-jJCTMKSp|1RMImnIf_>E;0TR z|AR|6uKf%C2Dg6a&U9#Nn#QDwy}7;Tp2v5dcP!_WC`A6R`uJ7qWBSck^V??R>hkla zA0G~X8HeY~OE34Iq<^QrfBAX!P|$+qy0+6c?RWIvs&DgOjvBV>S|>KOZMW@`uhE2x zPoA^`%I;A0fL9NA0As){a_p*grr^w`)c|msLT#@YkWUlj8@<$Td7@9ylg z2MsM6rdufX!RrG}_VT4t5Bk_J)xW^Q11-wMgYxbnLLMi1Y1iwm3zCL`O(M6|DU^(m zj9@#qYqv>eNgiFtM#4`#qI!efU3X+@mkL{U3r=wlnjsj@9 z3nq%O;UNhP?(j31^9o^OwlS#!5O@oiDxTRmC?2T6cGR_3ddUgQ;Xbnse<;>{`abAoMaSV@;LF3`B5x`-aKH91;Gew2`pO60CaCRT|oX=G}Z$Q_MT&HK;x>z=b-eDRp85RibN*r|U*HD+v$(}d1a1bgBG}tsR&e3}0L`+aHUIzs diff --git a/fuzzing/fuzz.sh b/fuzzing/fuzz.sh index 5f97ba036..94780db49 100755 --- a/fuzzing/fuzz.sh +++ b/fuzzing/fuzz.sh @@ -4,7 +4,19 @@ if [[ $(basename $(pwd)) != "Ark" ]]; then echo "This script should be launched from ArkScript source folder" && exit 1 fi +instance="-M main" +if [[ $# == 1 ]]; then + instance="-S variant-$1" +fi + buildFolder=afl -sudo afl-system-config -afl-fuzz -i fuzzing/input -o fuzzing/output -s 0 -- ${buildFolder}/arkscript @@ -L ./lib +# load tests cases from other fuzzers first +export AFL_IMPORT_FIRST=1 + +afl-fuzz -i fuzzing/input \ + -o fuzzing/output \ + -s 0 \ + -m 64 \ + $instance \ + -- ${buildFolder}/arkscript @@ -L ./lib diff --git a/fuzzing/input/tests-tools.arkc b/fuzzing/input/tests-tools.arkc deleted file mode 100644 index cc7f725b4109a775399a7d0461d6a436d38732d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5 McmYc+%4T2y00i>^KL7v# diff --git a/fuzzing/reduce_crashes b/fuzzing/reduce_crashes new file mode 100755 index 000000000..f12a2c678 --- /dev/null +++ b/fuzzing/reduce_crashes @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +for crash in fuzzing/output/$1/crashes/id*; do + file="${1}-$(basename -- "$crash")" + touch fuzzing/output_triage/$file + afl-tmin -m 64 -i $crash -o fuzzing/output_triage/$file -- ./afl/arkscript @@ -L ./lib +done + diff --git a/fuzzing/unique/tests-tools.arkc b/fuzzing/unique/tests-tools.arkc deleted file mode 100644 index 820d4b104cda74a275f09cef151ed7595a2b86c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1520 zcmb`F&u$V?6vn@~K#{+Nw*Eo1nHXt{2`%kXSExjyq(m4Nu9}hDkft+GXHet9hw%-3 z3SYpzi3<}K?u?0xYW(hHKn6pI33D@Z?tJH*-#z!9YkOxv6v5NG_oVRt#Q632!hM(j z-1}Yp^!?ob_;vfkk4;Z}d3*8m!2D()XZybNMiu!2uWaW~eU9Kt-?#e`Bk7NP1Q7#< z1k?U#I+K%`Gu51_MwkLt;mMwTu5p!t?Gk+I$`ka41ApkqyS2`pO60CaCRT|oX=G}Z$Q_MT&HK;x>z=b-eDRp85RibN*r|U*HD+v$(}d1a1bgBG}tsR&e3}0L`+aHUIzs From 4b6ea0fcda3b910884252b6403c0e8b1df84d8c5 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:15:23 +0100 Subject: [PATCH 30/42] feat: adding recursion limit reached detection --- CHANGELOG.md | 1 + src/arkreactor/VM/VM.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66931a422..4bdf5e434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - added fuzzing tools and corpus for [AFL](https://github.com/AFLplusplus/AFLplusplus) - added some tests for errors +- added recursion limit reached detection ### Changed - plugins can be constructed from outside ArkScript lib/modules folder, easing the development process diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 3c2e86ba0..c50024c05 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -458,8 +458,13 @@ namespace Ark break; case Instruction::CALL: + { + // stack pointer + 2 because we push IP and PP + if (context.sp + 2 >= VMStackSize) + throwVMError("VMError: maximum recursion depth exceeded"); call(context); break; + } case Instruction::CAPTURE: { @@ -1221,15 +1226,14 @@ namespace Ark std::size_t saved_pp = context.pp; uint16_t saved_sp = context.sp; - if (context.fc > 1) + if (uint16_t original_frame_count = context.fc; original_frame_count> 1) { // display call stack trace - uint16_t it = context.fc; Scope old_scope = *context.locals.back().get(); - while (it != 0) + while (context.fc != 0) { - std::cerr << "[" << termcolor::cyan << it << termcolor::reset << "] "; + std::cerr << "[" << termcolor::cyan << context.fc << termcolor::reset << "] "; if (context.pp != 0) { uint16_t id = findNearestVariableIdWithValue( @@ -1250,7 +1254,6 @@ namespace Ark context.ip = ip->pageAddr(); context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); - --it; } else { @@ -1258,7 +1261,7 @@ namespace Ark break; } - if (context.fc - it > 7) + if (original_frame_count - context.fc > 7) { std::printf("...\n"); break; From a41f0c00aa337da2eb951d5915c82f9a79bdbde9 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:16:07 +0100 Subject: [PATCH 31/42] better error messages in the VM --- src/arkreactor/VM/VM.cpp | 34 ++++++++++---------- tests/errors/capture/unbound.expected | 2 +- tests/errors/operators/out_of_range.expected | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index c50024c05..12c8e6cc8 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -130,7 +130,7 @@ namespace Ark [](const std::string& a, const std::string& b) -> std::string { return a + "\n\t- " + b; }); - throwVMError("Could not find module '" + file + "'. Searched in\n\t- " + path + "\n\t- " + lib_path); + throwVMError("ModuleError: Could not find module '" + file + "'. Searched in\n\t- " + path + "\n\t- " + lib_path); } m_shared_lib_objects.emplace_back(lib); @@ -144,7 +144,7 @@ namespace Ark catch (const std::system_error& e) { throwVMError( - "An error occurred while loading module '" + file + "': " + std::string(e.what()) + "\n" + + "ModuleError: An error occurred while loading module '" + file + "': " + std::string(e.what()) + "\n" + "It is most likely because the versions of the module and the language don't match."); } @@ -290,7 +290,7 @@ namespace Ark // push internal reference, shouldn't break anything so far push(var, context); else - throwVMError("unbound variable: " + m_state.m_symbols[context.last_symbol]); + throwVMError("ScopeError: unbound variable: " + m_state.m_symbols[context.last_symbol]); break; } @@ -351,14 +351,14 @@ namespace Ark if (Value* var = findNearestVariable(id, context); var != nullptr) { if (var->isConst()) - throwVMError("can not modify a constant: " + m_state.m_symbols[id]); + throwVMError("MutabilityError: can not modify a constant: " + m_state.m_symbols[id]); *var = *popAndResolveAsPtr(context); var->setConst(false); break; } - throwVMError("unbound variable " + m_state.m_symbols[id] + ", can not change its value"); + throwVMError("ScopeError: unbound variable " + m_state.m_symbols[id] + ", can not change its value"); break; } @@ -375,7 +375,7 @@ namespace Ark // check if we are redefining a variable if (auto val = (*context.locals.back())[id]; val != nullptr) - throwVMError("can not use 'let' to redefine the variable " + m_state.m_symbols[id]); + throwVMError("MutabilityError: can not use 'let' to redefine the variable " + m_state.m_symbols[id]); Value val = *popAndResolveAsPtr(context); val.setConst(true); @@ -483,7 +483,7 @@ namespace Ark Value* ptr = (*context.locals.back())[id]; if (!ptr) - throwVMError("Couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound"); + throwVMError("ScopeError: couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound"); ptr = ptr->valueType() == ValueType::Reference ? ptr->reference() : ptr; (*context.saved_scope.value()).push_back(id, *ptr); @@ -546,7 +546,7 @@ namespace Ark break; } - throwVMError("unbound variable: " + m_state.m_symbols[id]); + throwVMError("ScopeError: unbound variable: " + m_state.m_symbols[id]); break; } @@ -573,7 +573,7 @@ namespace Ark Value* var = popAndResolveAsPtr(context); if (var->valueType() != ValueType::Closure) - throwVMError("the variable `" + m_state.m_symbols[context.last_symbol] + "' isn't a closure, can not get the field `" + m_state.m_symbols[id] + "' from it"); + throwVMError("TypeError: the variable `" + m_state.m_symbols[context.last_symbol] + "' isn't a closure, can not get the field `" + m_state.m_symbols[id] + "' from it"); if (Value* field = (*var->refClosure().scope())[id]; field != nullptr) { @@ -588,7 +588,7 @@ namespace Ark break; } - throwVMError("couldn't find the variable " + m_state.m_symbols[id] + " in the closure enviroment"); + throwVMError("ScopeError: couldn't find the variable " + m_state.m_symbols[id] + " in the closure enviroment"); break; } @@ -688,7 +688,7 @@ namespace Ark Value* list = popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("can not modify a constant list using `append!'"); + throwVMError("MutabilityError: can not modify a constant list using `append!'"); if (list->valueType() != ValueType::List) types::generateError( "append!", @@ -709,7 +709,7 @@ namespace Ark Value* list = popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("can not modify a constant list using `concat!'"); + throwVMError("MutabilityError: can not modify a constant list using `concat!'"); if (list->valueType() != ValueType::List) types::generateError( "concat", @@ -760,7 +760,7 @@ namespace Ark Value number = *popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("can not modify a constant list using `pop!'"); + throwVMError("MutabilityError: can not modify a constant list using `pop!'"); if (list->valueType() != ValueType::List || number.valueType() != ValueType::Number) types::generateError( "pop!", @@ -1069,14 +1069,14 @@ namespace Ark if (static_cast(std::abs(idx)) < a.list().size()) push(a.list()[idx < 0 ? a.list().size() + idx : idx], context); else - throwVMError("Index (" + std::to_string(idx) + ") out of range (list size: " + std::to_string(a.list().size()) + ")"); + throwVMError("IndexError: index (" + std::to_string(idx) + ") out of range (list size: " + std::to_string(a.list().size()) + ")"); } else if (a.valueType() == ValueType::String) { if (static_cast(std::abs(idx)) < a.string().size()) push(Value(std::string(1, a.string()[idx < 0 ? a.string().size() + idx : idx])), context); else - throwVMError("Index (" + std::to_string(idx) + ") out of range (string size: " + std::to_string(a.string().size()) + ")"); + throwVMError("IndexError: index (" + std::to_string(idx) + ") out of range (string size: " + std::to_string(a.string().size()) + ")"); } else types::generateError( @@ -1157,7 +1157,7 @@ namespace Ark #pragma endregion default: - throwVMError("unknown instruction: " + std::to_string(static_cast(inst))); + throwVMError("VMError: unknown instruction: " + std::to_string(static_cast(inst))); break; } @@ -1217,7 +1217,7 @@ namespace Ark void VM::throwVMError(const std::string& message) { - throw std::runtime_error(message); + throw std::runtime_error(message + "\n"); } void VM::backtrace(ExecutionContext& context) noexcept diff --git a/tests/errors/capture/unbound.expected b/tests/errors/capture/unbound.expected index fd0f6b094..74c180b76 100644 --- a/tests/errors/capture/unbound.expected +++ b/tests/errors/capture/unbound.expected @@ -1 +1 @@ -Couldn't capture 'd' as it is currently unbound \ No newline at end of file +ScopeError: couldn't capture 'd' as it is currently unbound \ No newline at end of file diff --git a/tests/errors/operators/out_of_range.expected b/tests/errors/operators/out_of_range.expected index d02aa51d2..7dd64d88d 100644 --- a/tests/errors/operators/out_of_range.expected +++ b/tests/errors/operators/out_of_range.expected @@ -1 +1 @@ -Index (0) out of range (list size: 0) \ No newline at end of file +IndexError: index (0) out of range (list size: 0) \ No newline at end of file From 52804e79a135a8879b7147c3312c598cda19fc6c Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:16:25 +0100 Subject: [PATCH 32/42] tests: adding test for recursion limit --- tests/errors/callable/recursion_depth.ark | 7 +++++++ tests/errors/callable/recursion_depth.expected | 1 + 2 files changed, 8 insertions(+) create mode 100644 tests/errors/callable/recursion_depth.ark create mode 100644 tests/errors/callable/recursion_depth.expected diff --git a/tests/errors/callable/recursion_depth.ark b/tests/errors/callable/recursion_depth.ark new file mode 100644 index 000000000..dd4c6c7a0 --- /dev/null +++ b/tests/errors/callable/recursion_depth.ark @@ -0,0 +1,7 @@ +(let foo (fun (a) { + # hack to avoid getting TCO-ed + (let tmp (foo (+ a 1))) + tmp +})) + +(foo 0) diff --git a/tests/errors/callable/recursion_depth.expected b/tests/errors/callable/recursion_depth.expected new file mode 100644 index 000000000..4235ff91f --- /dev/null +++ b/tests/errors/callable/recursion_depth.expected @@ -0,0 +1 @@ +VMError: maximum recursion depth exceeded \ No newline at end of file From 1c7389e6332179c2d5a3a8e8b65a965c72af3905 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:31:51 +0100 Subject: [PATCH 33/42] enhancing the generation of errors in the VM --- include/Ark/VM/ErrorKind.hpp | 29 ++++++++++++++ include/Ark/VM/VM.hpp | 4 +- include/Ark/VM/inline/VM.inl | 11 +++--- src/arkreactor/VM/VM.cpp | 39 ++++++++++--------- tests/errors/callable/not_callable.expected | 2 +- .../errors/callable/recursion_depth.expected | 2 +- tests/errors/capture/unbound.expected | 2 +- tests/errors/operators/out_of_range.expected | 2 +- 8 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 include/Ark/VM/ErrorKind.hpp diff --git a/include/Ark/VM/ErrorKind.hpp b/include/Ark/VM/ErrorKind.hpp new file mode 100644 index 000000000..d0e2ccde1 --- /dev/null +++ b/include/Ark/VM/ErrorKind.hpp @@ -0,0 +1,29 @@ +#ifndef ARK_VM_ERRORKIND_HPP +#define ARK_VM_ERRORKIND_HPP + +#include +#include + +namespace Ark::internal +{ + enum class ErrorKind + { + VM, + Module, + Mutability, + Scope, + Type, + Index + }; + + constexpr std::array errorKinds = { + "VMError", + "ModuleError", + "MutabilityError", + "ScopeError", + "TypeError", + "IndexError" + }; +} + +#endif diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index 68cb59cd8..f28354262 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -311,9 +312,10 @@ namespace Ark /** * @brief Throw a VM error message * + * @param kind type of VM error * @param message */ - void throwVMError(const std::string& message); + void throwVMError(internal::ErrorKind kind, const std::string& message); /** * @brief Display a backtrace when the VM encounter an exception diff --git a/include/Ark/VM/inline/VM.inl b/include/Ark/VM/inline/VM.inl index 150c7f48f..e08cc3ff7 100644 --- a/include/Ark/VM/inline/VM.inl +++ b/include/Ark/VM/inline/VM.inl @@ -25,7 +25,7 @@ Value VM::call(const std::string& name, Args&&... args) // find id of function auto it = std::find(m_state.m_symbols.begin(), m_state.m_symbols.end(), name); if (it == m_state.m_symbols.end()) - throwVMError("unbound variable: " + name); + throwVMError(ErrorKind::Scope, "Unbound variable: " + name); // convert and push arguments in reverse order std::vector fnargs { { Value(std::forward(args))... } }; @@ -44,13 +44,13 @@ Value VM::call(const std::string& name, Args&&... args) !(vt == ValueType::Reference && (var->reference()->valueType() == ValueType::PageAddr || var->reference()->valueType() == ValueType::Closure))) - throwVMError("Can't call '" + name + "': it isn't a Function but a " + types_to_str[static_cast(vt)]); + throwVMError(ErrorKind::Type, "Can't call '" + name + "': it isn't a Function but a " + types_to_str[static_cast(vt)]); push(Value(var), context); context.last_symbol = id; } else - throwVMError("Couldn't find variable " + name); + throwVMError(ErrorKind::Scope, "Couldn't find variable " + name); std::size_t frames_count = context.fc; // call it @@ -372,9 +372,9 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_) default: { if (m_state.m_symbols.size() > 0) - throwVMError("Can't call '" + m_state.m_symbols[context.last_symbol] + "': it isn't a Function but a " + types_to_str[static_cast(function.valueType())]); + throwVMError(ErrorKind::Type, "Can't call '" + m_state.m_symbols[context.last_symbol] + "': it isn't a Function but a " + types_to_str[static_cast(function.valueType())]); else - throwVMError("A " + types_to_str[static_cast(function.valueType())] + " isn't a callable"); + throwVMError(ErrorKind::Type, "A " + types_to_str[static_cast(function.valueType())] + " isn't a callable"); } } @@ -391,6 +391,7 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_) if (needed_argc != argc) throwVMError( + ErrorKind::Type, "Function '" + m_state.m_symbols[context.last_symbol] + "' needs " + std::to_string(needed_argc) + " arguments, but it received " + std::to_string(argc)); } diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 12c8e6cc8..3f4501896 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -130,7 +130,7 @@ namespace Ark [](const std::string& a, const std::string& b) -> std::string { return a + "\n\t- " + b; }); - throwVMError("ModuleError: Could not find module '" + file + "'. Searched in\n\t- " + path + "\n\t- " + lib_path); + throwVMError(ErrorKind::Module, "Could not find module '" + file + "'. Searched in\n\t- " + path + "\n\t- " + lib_path); } m_shared_lib_objects.emplace_back(lib); @@ -144,7 +144,8 @@ namespace Ark catch (const std::system_error& e) { throwVMError( - "ModuleError: An error occurred while loading module '" + file + "': " + std::string(e.what()) + "\n" + + ErrorKind::Module, + "An error occurred while loading module '" + file + "': " + std::string(e.what()) + "\n" + "It is most likely because the versions of the module and the language don't match."); } @@ -290,7 +291,7 @@ namespace Ark // push internal reference, shouldn't break anything so far push(var, context); else - throwVMError("ScopeError: unbound variable: " + m_state.m_symbols[context.last_symbol]); + throwVMError(ErrorKind::Scope, "Unbound variable: " + m_state.m_symbols[context.last_symbol]); break; } @@ -351,14 +352,14 @@ namespace Ark if (Value* var = findNearestVariable(id, context); var != nullptr) { if (var->isConst()) - throwVMError("MutabilityError: can not modify a constant: " + m_state.m_symbols[id]); + throwVMError(ErrorKind::Mutability, "Can not modify a constant: " + m_state.m_symbols[id]); *var = *popAndResolveAsPtr(context); var->setConst(false); break; } - throwVMError("ScopeError: unbound variable " + m_state.m_symbols[id] + ", can not change its value"); + throwVMError(ErrorKind::Scope, "Unbound variable " + m_state.m_symbols[id] + ", can not change its value"); break; } @@ -375,7 +376,7 @@ namespace Ark // check if we are redefining a variable if (auto val = (*context.locals.back())[id]; val != nullptr) - throwVMError("MutabilityError: can not use 'let' to redefine the variable " + m_state.m_symbols[id]); + throwVMError(ErrorKind::Mutability, "Can not use 'let' to redefine the variable " + m_state.m_symbols[id]); Value val = *popAndResolveAsPtr(context); val.setConst(true); @@ -461,7 +462,7 @@ namespace Ark { // stack pointer + 2 because we push IP and PP if (context.sp + 2 >= VMStackSize) - throwVMError("VMError: maximum recursion depth exceeded"); + throwVMError(ErrorKind::VM, "Maximum recursion depth exceeded"); call(context); break; } @@ -483,7 +484,7 @@ namespace Ark Value* ptr = (*context.locals.back())[id]; if (!ptr) - throwVMError("ScopeError: couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound"); + throwVMError(ErrorKind::Scope, "Couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound"); ptr = ptr->valueType() == ValueType::Reference ? ptr->reference() : ptr; (*context.saved_scope.value()).push_back(id, *ptr); @@ -546,7 +547,7 @@ namespace Ark break; } - throwVMError("ScopeError: unbound variable: " + m_state.m_symbols[id]); + throwVMError(ErrorKind::Scope, "Unbound variable: " + m_state.m_symbols[id]); break; } @@ -573,7 +574,7 @@ namespace Ark Value* var = popAndResolveAsPtr(context); if (var->valueType() != ValueType::Closure) - throwVMError("TypeError: the variable `" + m_state.m_symbols[context.last_symbol] + "' isn't a closure, can not get the field `" + m_state.m_symbols[id] + "' from it"); + throwVMError(ErrorKind::Type, "The variable `" + m_state.m_symbols[context.last_symbol] + "' isn't a closure, can not get the field `" + m_state.m_symbols[id] + "' from it"); if (Value* field = (*var->refClosure().scope())[id]; field != nullptr) { @@ -588,7 +589,7 @@ namespace Ark break; } - throwVMError("ScopeError: couldn't find the variable " + m_state.m_symbols[id] + " in the closure enviroment"); + throwVMError(ErrorKind::Scope, "Couldn't find the variable " + m_state.m_symbols[id] + " in the closure enviroment"); break; } @@ -688,7 +689,7 @@ namespace Ark Value* list = popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("MutabilityError: can not modify a constant list using `append!'"); + throwVMError(ErrorKind::Mutability, "Can not modify a constant list using `append!'"); if (list->valueType() != ValueType::List) types::generateError( "append!", @@ -709,7 +710,7 @@ namespace Ark Value* list = popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("MutabilityError: can not modify a constant list using `concat!'"); + throwVMError(ErrorKind::Mutability, "Can not modify a constant list using `concat!'"); if (list->valueType() != ValueType::List) types::generateError( "concat", @@ -760,7 +761,7 @@ namespace Ark Value number = *popAndResolveAsPtr(context); if (list->isConst()) - throwVMError("MutabilityError: can not modify a constant list using `pop!'"); + throwVMError(ErrorKind::Mutability, "Can not modify a constant list using `pop!'"); if (list->valueType() != ValueType::List || number.valueType() != ValueType::Number) types::generateError( "pop!", @@ -1069,14 +1070,14 @@ namespace Ark if (static_cast(std::abs(idx)) < a.list().size()) push(a.list()[idx < 0 ? a.list().size() + idx : idx], context); else - throwVMError("IndexError: index (" + std::to_string(idx) + ") out of range (list size: " + std::to_string(a.list().size()) + ")"); + throwVMError(ErrorKind::Index, "Index (" + std::to_string(idx) + ") out of range (list size: " + std::to_string(a.list().size()) + ")"); } else if (a.valueType() == ValueType::String) { if (static_cast(std::abs(idx)) < a.string().size()) push(Value(std::string(1, a.string()[idx < 0 ? a.string().size() + idx : idx])), context); else - throwVMError("IndexError: index (" + std::to_string(idx) + ") out of range (string size: " + std::to_string(a.string().size()) + ")"); + throwVMError(ErrorKind::Index, "Index (" + std::to_string(idx) + ") out of range (string size: " + std::to_string(a.string().size()) + ")"); } else types::generateError( @@ -1157,7 +1158,7 @@ namespace Ark #pragma endregion default: - throwVMError("VMError: unknown instruction: " + std::to_string(static_cast(inst))); + throwVMError(ErrorKind::VM, "Unknown instruction: " + std::to_string(static_cast(inst))); break; } @@ -1215,9 +1216,9 @@ namespace Ark return std::numeric_limits::max(); } - void VM::throwVMError(const std::string& message) + void VM::throwVMError(ErrorKind kind, const std::string& message) { - throw std::runtime_error(message + "\n"); + throw std::runtime_error(std::string(errorKinds[static_cast(kind)]) + ": " + message + "\n"); } void VM::backtrace(ExecutionContext& context) noexcept diff --git a/tests/errors/callable/not_callable.expected b/tests/errors/callable/not_callable.expected index f7b3c6802..0cd59a0b8 100644 --- a/tests/errors/callable/not_callable.expected +++ b/tests/errors/callable/not_callable.expected @@ -1 +1 @@ -A Nil isn't a callable \ No newline at end of file +TypeError: A Nil isn't a callable \ No newline at end of file diff --git a/tests/errors/callable/recursion_depth.expected b/tests/errors/callable/recursion_depth.expected index 4235ff91f..3a1cb0a71 100644 --- a/tests/errors/callable/recursion_depth.expected +++ b/tests/errors/callable/recursion_depth.expected @@ -1 +1 @@ -VMError: maximum recursion depth exceeded \ No newline at end of file +VMError: Maximum recursion depth exceeded \ No newline at end of file diff --git a/tests/errors/capture/unbound.expected b/tests/errors/capture/unbound.expected index 74c180b76..2bad3e865 100644 --- a/tests/errors/capture/unbound.expected +++ b/tests/errors/capture/unbound.expected @@ -1 +1 @@ -ScopeError: couldn't capture 'd' as it is currently unbound \ No newline at end of file +ScopeError: Couldn't capture 'd' as it is currently unbound \ No newline at end of file diff --git a/tests/errors/operators/out_of_range.expected b/tests/errors/operators/out_of_range.expected index 7dd64d88d..21ff79627 100644 --- a/tests/errors/operators/out_of_range.expected +++ b/tests/errors/operators/out_of_range.expected @@ -1 +1 @@ -IndexError: index (0) out of range (list size: 0) \ No newline at end of file +IndexError: Index (0) out of range (list size: 0) \ No newline at end of file From 9d159b4b68511062dca76a6d9923242a5c396220 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:36:28 +0100 Subject: [PATCH 34/42] tests: adding tests for function arity errors --- tests/errors/callable/not_enough_args.ark | 2 ++ tests/errors/callable/not_enough_args.expected | 1 + tests/errors/callable/too_many_args.ark | 2 ++ tests/errors/callable/too_many_args.expected | 1 + 4 files changed, 6 insertions(+) create mode 100644 tests/errors/callable/not_enough_args.ark create mode 100644 tests/errors/callable/not_enough_args.expected create mode 100644 tests/errors/callable/too_many_args.ark create mode 100644 tests/errors/callable/too_many_args.expected diff --git a/tests/errors/callable/not_enough_args.ark b/tests/errors/callable/not_enough_args.ark new file mode 100644 index 000000000..805f74cc8 --- /dev/null +++ b/tests/errors/callable/not_enough_args.ark @@ -0,0 +1,2 @@ +(let foo (fun (a b) (+ a b))) +(foo 1) \ No newline at end of file diff --git a/tests/errors/callable/not_enough_args.expected b/tests/errors/callable/not_enough_args.expected new file mode 100644 index 000000000..b5ca5e29e --- /dev/null +++ b/tests/errors/callable/not_enough_args.expected @@ -0,0 +1 @@ +TypeError: Function 'foo' needs 2 arguments, but it received 1 \ No newline at end of file diff --git a/tests/errors/callable/too_many_args.ark b/tests/errors/callable/too_many_args.ark new file mode 100644 index 000000000..be693ae92 --- /dev/null +++ b/tests/errors/callable/too_many_args.ark @@ -0,0 +1,2 @@ +(let foo (fun (a b) (+ a b))) +(foo 1 2 3) \ No newline at end of file diff --git a/tests/errors/callable/too_many_args.expected b/tests/errors/callable/too_many_args.expected new file mode 100644 index 000000000..05ddd8496 --- /dev/null +++ b/tests/errors/callable/too_many_args.expected @@ -0,0 +1 @@ +TypeError: Function 'foo' needs 2 arguments, but it received 3 \ No newline at end of file From c9bada435704d6b6ca5dfc2031d943844a8c546b Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 7 Nov 2022 21:42:10 +0100 Subject: [PATCH 35/42] creating a special error kind for arity error and enhancing the max recursion message --- include/Ark/VM/ErrorKind.hpp | 8 +++++--- include/Ark/VM/inline/VM.inl | 2 +- src/arkreactor/VM/VM.cpp | 9 ++++++--- tests/errors/callable/not_enough_args.expected | 2 +- tests/errors/callable/too_many_args.expected | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/Ark/VM/ErrorKind.hpp b/include/Ark/VM/ErrorKind.hpp index d0e2ccde1..1b12346a5 100644 --- a/include/Ark/VM/ErrorKind.hpp +++ b/include/Ark/VM/ErrorKind.hpp @@ -13,16 +13,18 @@ namespace Ark::internal Mutability, Scope, Type, - Index + Index, + Arity, }; - constexpr std::array errorKinds = { + constexpr std::array errorKinds = { "VMError", "ModuleError", "MutabilityError", "ScopeError", "TypeError", - "IndexError" + "IndexError", + "ArityError" }; } diff --git a/include/Ark/VM/inline/VM.inl b/include/Ark/VM/inline/VM.inl index e08cc3ff7..e14dcb177 100644 --- a/include/Ark/VM/inline/VM.inl +++ b/include/Ark/VM/inline/VM.inl @@ -391,7 +391,7 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_) if (needed_argc != argc) throwVMError( - ErrorKind::Type, + ErrorKind::Arity, "Function '" + m_state.m_symbols[context.last_symbol] + "' needs " + std::to_string(needed_argc) + " arguments, but it received " + std::to_string(argc)); } diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 3f4501896..a705fb945 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -146,7 +146,7 @@ namespace Ark throwVMError( ErrorKind::Module, "An error occurred while loading module '" + file + "': " + std::string(e.what()) + "\n" + - "It is most likely because the versions of the module and the language don't match."); + "It is most likely because the versions of the module and the language don't match."); } // load the mapping data @@ -462,7 +462,10 @@ namespace Ark { // stack pointer + 2 because we push IP and PP if (context.sp + 2 >= VMStackSize) - throwVMError(ErrorKind::VM, "Maximum recursion depth exceeded"); + throwVMError( + ErrorKind::VM, + "Maximum recursion depth exceeded.\n" + "You could consider rewriting your function to make use of tail-call optimization."); call(context); break; } @@ -1227,7 +1230,7 @@ namespace Ark std::size_t saved_pp = context.pp; uint16_t saved_sp = context.sp; - if (uint16_t original_frame_count = context.fc; original_frame_count> 1) + if (uint16_t original_frame_count = context.fc; original_frame_count > 1) { // display call stack trace Scope old_scope = *context.locals.back().get(); diff --git a/tests/errors/callable/not_enough_args.expected b/tests/errors/callable/not_enough_args.expected index b5ca5e29e..bd124bf8d 100644 --- a/tests/errors/callable/not_enough_args.expected +++ b/tests/errors/callable/not_enough_args.expected @@ -1 +1 @@ -TypeError: Function 'foo' needs 2 arguments, but it received 1 \ No newline at end of file +ArityError: Function 'foo' needs 2 arguments, but it received 1 \ No newline at end of file diff --git a/tests/errors/callable/too_many_args.expected b/tests/errors/callable/too_many_args.expected index 05ddd8496..f0584c534 100644 --- a/tests/errors/callable/too_many_args.expected +++ b/tests/errors/callable/too_many_args.expected @@ -1 +1 @@ -TypeError: Function 'foo' needs 2 arguments, but it received 3 \ No newline at end of file +ArityError: Function 'foo' needs 2 arguments, but it received 3 \ No newline at end of file From ecb22de996f565989bd76f0da9d9824fc720815b Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Mon, 12 Dec 2022 19:39:13 +0100 Subject: [PATCH 36/42] cmake: updating the way we set the required C++ version --- CMakeLists.txt | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d83fb521c..986d1b3e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,12 +30,7 @@ file(GLOB_RECURSE SOURCE_FILES add_library(ArkReactor SHARED ${SOURCE_FILES}) -set_target_properties( - ArkReactor - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF) +target_compile_features(ArkReactor PRIVATE cxx_std_17) enable_lto(ArkReactor) @@ -209,18 +204,8 @@ if (ARK_BUILD_EXE) target_link_libraries(arkscript PUBLIC ArkReactor replxx clipp termcolor) target_link_libraries(ark PUBLIC ArkReactor replxx clipp termcolor) - set_target_properties( - arkscript - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF) - set_target_properties( - ark - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF) + target_compile_features(arkscript PRIVATE cxx_std_17) + target_compile_features(ark PRIVATE cxx_std_17) enable_lto(ark) enable_lto(arkscript) From 75ac06d30ed003bbb8a5847ef3d12f5c418debdd Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 4 Dec 2022 12:13:44 +0100 Subject: [PATCH 37/42] ci: fixing jobs and OSes used --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e12607add..6c87e1c3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,54 +70,69 @@ jobs: matrix: config: - { - os: ubuntu-latest, name: "Ubuntu Clang 11", cc: "clang-11", cxx: "clang++-11", - artifact: "ubuntu-clang-11", preconfigure: "", sanitizers: "On" + os: ubuntu-latest, name: "Ubuntu Clang 14", cc: "clang-14", cxx: "clang++-14", + artifact: "ubuntu-clang-14", sanitizers: "On", preconfigure: "" + } + - { + os: ubuntu-latest, name: "Ubuntu Clang 13", cc: "clang-13", cxx: "clang++-13", + artifact: "ubuntu-clang-13", sanitizers: "On", preconfigure: "" + } + - { + os: ubuntu-latest, name: "Ubuntu Clang 12", cc: "clang-12", cxx: "clang++-12", + artifact: "ubuntu-clang-12", sanitizers: "On", preconfigure: "" + } + - { + os: ubuntu-20.04, name: "Ubuntu Clang 11", cc: "clang-11", cxx: "clang++-11", + artifact: "ubuntu-clang-11", sanitizers: "On", preconfigure: "" } - { os: ubuntu-latest, name: "Ubuntu Clang 11 (valgrind)", cc: "clang-11", cxx: "clang++-11", - artifact: "ubuntu-clang-11-valgrind", preconfigure: "", sanitizers: "Off" + artifact: "ubuntu-clang-11-valgrind", sanitizers: "Off", preconfigure: "" + } + - { + os: ubuntu-20.04, name: "Ubuntu Clang 10", cc: "clang-10", cxx: "clang++-10", + artifact: "ubuntu-clang-10", sanitizers: "On", preconfigure: "" } - { - os: ubuntu-latest, name: "Ubuntu Clang 10", cc: "clang-10", cxx: "clang++-10", - artifact: "ubuntu-clang-10", preconfigure: "", sanitizers: "On" + os: ubuntu-20.04, name: "Ubuntu Clang 9", cc: "clang-9", cxx: "clang++-9", + artifact: "ubuntu-clang-9", sanitizers: "On", preconfigure: "" } - { - os: ubuntu-latest, name: "Ubuntu Clang 9", cc: "clang-9", cxx: "clang++-9", - artifact: "ubuntu-clang-9", preconfigure: "", sanitizers: "On" + os: ubuntu-latest, name: "Ubuntu GCC 12", cc: "gcc-12", cxx: "g++-12", + artifact: "ubuntu-gcc-12", sanitizers: "Off", preconfigure: "" } - { os: ubuntu-latest, name: "Ubuntu GCC 11", cc: "gcc-11", cxx: "g++-11", - artifact: "ubuntu-gcc-11", preconfigure: "", sanitizers: "Off" + artifact: "ubuntu-gcc-11", sanitizers: "Off", preconfigure: "" } - { os: ubuntu-latest, name: "Ubuntu GCC 10", cc: "gcc-10", cxx: "g++-10", - artifact: "ubuntu-gcc-10", preconfigure: "", sanitizers: "Off" + artifact: "ubuntu-gcc-10", sanitizers: "Off", preconfigure: "" } - { os: ubuntu-latest, name: "Ubuntu GCC 9", cc: "gcc-9", cxx: "g++-9", - artifact: "ubuntu-gcc-9", preconfigure: "", sanitizers: "Off" + artifact: "ubuntu-gcc-9", sanitizers: "Off", preconfigure: "" } - { - os: ubuntu-latest, name: "Ubuntu GCC 8", cc: "gcc-8", cxx: "g++-8", - artifact: "ubuntu-gcc-8", preconfigure: "", sanitizers: "Off" + os: ubuntu-20.04, name: "Ubuntu GCC 8", cc: "gcc-8", cxx: "g++-8", + artifact: "ubuntu-gcc-8", sanitizers: "Off", preconfigure: "" } - { os: windows-latest, name: "Windows VS 2019", cc: "cl", cxx: "cl", artifact: "windows-msvc-19", environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", - preconfigure: "", sanitizers: "On" + sanitizers: "On", preconfigure: "" } - { os: windows-latest, name: "Windows VS 2017", cc: "cl", cxx: "cl", artifact: "windows-msvc-17", environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Auxiliary/Build/vcvars64.bat", - preconfigure: "", sanitizers: "On" + sanitizers: "On", preconfigure: "" } - { os: macos-latest, name: "MacOS Clang 12", cc: "clang", cxx: "clang++", artifact: "macos-clang-12", - preconfigure: "export OPENSSL_ROOT_DIR=/usr/local/opt/openssl/", - sanitizers: "On" + sanitizers: "On", preconfigure: "export OPENSSL_ROOT_DIR=/usr/local/opt/openssl/" } steps: @@ -126,14 +141,14 @@ jobs: submodules: recursive - name: Update GNU compilers - if: startsWith(matrix.config.name, 'Ubuntu') && startsWith(matrix.config.cc, 'gcc') + if: startsWith(matrix.config.name, 'Ubuntu GCC') shell: bash run: | sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get -yq install ${{ matrix.config.cc }} ${{ matrix.config.cxx }} - name: Update LLVM compilers - if: startsWith(matrix.config.name, 'Ubuntu') && startsWith(matrix.config.cc, 'clang') + if: startsWith(matrix.config.name, 'Ubuntu Clang') shell: bash run: | version=`echo ${{ matrix.config.cc }} | cut -c 7-` @@ -247,13 +262,17 @@ jobs: fail-fast: false matrix: config: - - { os: ubuntu-latest, name: "Ubuntu Clang 11", artifact: "ubuntu-clang-11" } - - { os: ubuntu-latest, name: "Ubuntu Clang 10", artifact: "ubuntu-clang-10" } - - { os: ubuntu-latest, name: "Ubuntu Clang 9", artifact: "ubuntu-clang-9" } + - { os: ubuntu-latest, name: "Ubuntu Clang 14", artifact: "ubuntu-clang-14" } + - { os: ubuntu-latest, name: "Ubuntu Clang 13", artifact: "ubuntu-clang-13" } + - { os: ubuntu-latest, name: "Ubuntu Clang 12", artifact: "ubuntu-clang-12" } + - { os: ubuntu-20.04, name: "Ubuntu Clang 11", artifact: "ubuntu-clang-11" } + - { os: ubuntu-20.04, name: "Ubuntu Clang 10", artifact: "ubuntu-clang-10" } + - { os: ubuntu-20.04, name: "Ubuntu Clang 9", artifact: "ubuntu-clang-9" } + - { os: ubuntu-latest, name: "Ubuntu GCC 12", artifact: "ubuntu-gcc-12" } - { os: ubuntu-latest, name: "Ubuntu GCC 11", artifact: "ubuntu-gcc-11" } - { os: ubuntu-latest, name: "Ubuntu GCC 10", artifact: "ubuntu-gcc-10" } - { os: ubuntu-latest, name: "Ubuntu GCC 9", artifact: "ubuntu-gcc-9" } - - { os: ubuntu-latest, name: "Ubuntu GCC 8", artifact: "ubuntu-gcc-8" } + - { os: ubuntu-20.04, name: "Ubuntu GCC 8", artifact: "ubuntu-gcc-8" } - { os: windows-latest, name: "Windows VS 2019", artifact: "windows-msvc-19", } - { os: windows-latest, name: "Windows VS 2017", artifact: "windows-msvc-17", } - { os: macos-latest, name: "MacOS Clang 12", artifact: "macos-clang-12", } From 7ecff66897b97c2622dd46e0c30ea65e21b72b45 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 18 Feb 2023 17:40:36 +0100 Subject: [PATCH 38/42] refacto: using a proper data structure to handle macro scopes --- CHANGELOG.md | 1 + include/Ark/Compiler/Macros/Executor.hpp | 4 +- include/Ark/Compiler/Macros/MacroScope.hpp | 81 +++++++++++++++++++ include/Ark/Compiler/Macros/Processor.hpp | 9 ++- src/arkreactor/Compiler/Macros/Executor.cpp | 2 +- .../Compiler/Macros/Executors/List.cpp | 2 +- .../Compiler/Macros/Executors/Symbol.cpp | 4 +- src/arkreactor/Compiler/Macros/MacroScope.cpp | 34 ++++++++ src/arkreactor/Compiler/Macros/Processor.cpp | 44 +++++----- 9 files changed, 145 insertions(+), 36 deletions(-) create mode 100644 include/Ark/Compiler/Macros/MacroScope.hpp create mode 100644 src/arkreactor/Compiler/Macros/MacroScope.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bdf5e434..5a81ca395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - adding bound checking on operator @ - adding bound checking on operator @ when used in macros - better arity check for macros +- fixed a bug in the macro processor where macros were deleted when they shouldn't ### Removed diff --git a/include/Ark/Compiler/Macros/Executor.hpp b/include/Ark/Compiler/Macros/Executor.hpp index 65cc86e7d..a889d1c6c 100644 --- a/include/Ark/Compiler/Macros/Executor.hpp +++ b/include/Ark/Compiler/Macros/Executor.hpp @@ -68,9 +68,9 @@ namespace Ark::internal * @details Proxy function for MacroProcessor::findNearestMacro * * @param name - * @return Node* nullptr if no macro was found + * @return const Node* nullptr if no macro was found */ - Node* findNearestMacro(const std::string& name); + const Node* findNearestMacro(const std::string& name) const; /** * @brief Registers macros based on their type diff --git a/include/Ark/Compiler/Macros/MacroScope.hpp b/include/Ark/Compiler/Macros/MacroScope.hpp new file mode 100644 index 000000000..7a867ab13 --- /dev/null +++ b/include/Ark/Compiler/Macros/MacroScope.hpp @@ -0,0 +1,81 @@ +/** + * @file MacroScope.hpp + * @author Alexandre Plateau (lexplt.dev@gmail.com) + * @brief Defines tools to handle macro definitions + * @version 0.1 + * @date 2023-02-18 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef COMPILER_MACROS_SCOPE_HPP +#define COMPILER_MACROS_SCOPE_HPP + +#include + +#include +#include + +namespace Ark::internal +{ + class MacroScope + { + public: + MacroScope(); + + /** + * @brief Construct a new MacroScope object given a depth in the scope hierarchy + * + * @param depth + */ + explicit MacroScope(unsigned int depth); + + /** + * @brief Check if the current scope holds a value for a given symbol, and returns it as a pointer + * + * @param name + * @return Node* pointer to the value if found, nullptr otherwise + */ + const Node* has(const std::string& name) const; + + /** + * @brief Add a new entry in the scope + * + * @param name + * @param node + */ + void add(const std::string& name, const Node& node); + + /** + * @brief Remove a macro in the scope, if it exists + * + * @param name + * @return true if the value was found and removed + * @return false otherwise + */ + bool remove(const std::string& name); + + /** + * @brief Return true if the current scope is empty + * + * @return true + * @return false + */ + inline bool empty() const + { + return m_macros.empty(); + } + + inline unsigned int depth() const + { + return m_depth; + } + + private: + std::unordered_map m_macros; + unsigned int m_depth; + }; +} + +#endif diff --git a/include/Ark/Compiler/Macros/Processor.hpp b/include/Ark/Compiler/Macros/Processor.hpp index 32915b239..db0803294 100644 --- a/include/Ark/Compiler/Macros/Processor.hpp +++ b/include/Ark/Compiler/Macros/Processor.hpp @@ -13,6 +13,7 @@ #define COMPILER_MACROS_PROCESSOR_HPP #include +#include #include #include @@ -56,8 +57,8 @@ namespace Ark::internal private: unsigned m_debug; ///< The debug level uint16_t m_options; - Node m_ast; ///< The modified AST - std::vector> m_macros; ///< Handling macros in a scope fashion + Node m_ast; ///< The modified AST + std::vector m_macros; ///< Handling macros in a scope fashion MacroExecutorPipeline m_executor_pipeline; std::vector m_predefined_macros; ///< Already existing macros, non-keywords, non-builtins std::unordered_map m_defined_functions; @@ -66,9 +67,9 @@ namespace Ark::internal * @brief Find the nearest macro matching a given name * * @param name - * @return Node* nullptr if no macro was found + * @return const Node* nullptr if no macro was found */ - Node* findNearestMacro(const std::string& name); + const Node* findNearestMacro(const std::string& name) const; /** * @brief Find the nearest macro matching a given name and delete it diff --git a/src/arkreactor/Compiler/Macros/Executor.cpp b/src/arkreactor/Compiler/Macros/Executor.cpp index 9de8aeada..cb3bb560d 100644 --- a/src/arkreactor/Compiler/Macros/Executor.cpp +++ b/src/arkreactor/Compiler/Macros/Executor.cpp @@ -12,7 +12,7 @@ namespace Ark::internal MacroExecutor::~MacroExecutor() {} - Node* MacroExecutor::findNearestMacro(const std::string& name) + const Node* MacroExecutor::findNearestMacro(const std::string& name) const { return m_macroprocessor->findNearestMacro(name); } diff --git a/src/arkreactor/Compiler/Macros/Executors/List.cpp b/src/arkreactor/Compiler/Macros/Executors/List.cpp index 88c26e825..0cc63e06f 100644 --- a/src/arkreactor/Compiler/Macros/Executors/List.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/List.cpp @@ -11,7 +11,7 @@ namespace Ark::internal bool ListExecutor::applyMacro(Node& node) { Node& first = node.list()[0]; - Node* macro = findNearestMacro(first.string()); + const Node* macro = findNearestMacro(first.string()); if (macro != nullptr) { diff --git a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp index cf0608b40..672bafce0 100644 --- a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp @@ -10,14 +10,14 @@ namespace Ark::internal bool SymbolExecutor::applyMacro(Node& node) { // error ? - Node* macro = findNearestMacro(node.string()); + const Node* macro = findNearestMacro(node.string()); if (macro != nullptr) { // !{name value} if (macro->constList().size() == 2) { - node = macro->list()[1]; + node = macro->constList()[1]; return true; } } diff --git a/src/arkreactor/Compiler/Macros/MacroScope.cpp b/src/arkreactor/Compiler/Macros/MacroScope.cpp new file mode 100644 index 000000000..184e6136d --- /dev/null +++ b/src/arkreactor/Compiler/Macros/MacroScope.cpp @@ -0,0 +1,34 @@ +#include + +namespace Ark::internal +{ + MacroScope::MacroScope() : + m_depth(0) + {} + + MacroScope::MacroScope(unsigned int depth) : + m_depth(depth) + {} + + const Node* MacroScope::has(const std::string& name) const + { + if (auto res = m_macros.find(name); res != m_macros.end()) + return &res->second; + return nullptr; + } + + void MacroScope::add(const std::string& name, const Node& node) + { + m_macros[name] = node; + } + + bool MacroScope::remove(const std::string& name) + { + if (auto res = m_macros.find(name); res != m_macros.end()) + { + m_macros.erase(res); + return true; + } + return false; + } +} diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp index f5980ba5d..167ce6728 100644 --- a/src/arkreactor/Compiler/Macros/Processor.cpp +++ b/src/arkreactor/Compiler/Macros/Processor.cpp @@ -63,11 +63,11 @@ namespace Ark::internal if (first_node.nodeType() == NodeType::Symbol) { if (first_node.string() != "undef") - m_macros.back()[first_node.string()] = node; + m_macros.back().add(first_node.string(), node); else if (second_node.nodeType() == NodeType::Symbol) // undefine a macro deleteNearestMacro(second_node.string()); else // used undef on a non-symbol - throwMacroProcessingError("can not undefine a macro without it's name", second_node); + throwMacroProcessingError("can not undefine a macro without a name", second_node); return; } throwMacroProcessingError("can not define a macro without a symbol", first_node); @@ -93,7 +93,7 @@ namespace Ark::internal else if (had_spread && n.nodeType() == NodeType::Symbol) throwMacroProcessingError("got another argument after a spread argument, which is invalid", n); } - m_macros.back()[first_node.string()] = node; + m_macros.back().add(first_node.string(), node); return; } } @@ -146,11 +146,10 @@ namespace Ark::internal if (node.list()[i].nodeType() == NodeType::Macro) { // create a scope only if needed - if ((!m_macros.empty() && !m_macros.back().empty() && static_cast(m_macros.back()["#depth"].number()) < depth) || !has_created) + if ((!m_macros.empty() && !m_macros.back().empty() && m_macros.back().depth() < depth) || !has_created) { has_created = true; - m_macros.emplace_back(); - m_macros.back()["#depth"] = Node(static_cast(depth)); + m_macros.emplace_back(depth); } bool had = hadBegin(node.list()[i]); @@ -206,12 +205,12 @@ namespace Ark::internal } // delete a scope only if needed - if (!m_macros.empty() && !m_macros.back().empty() && static_cast(m_macros.back()["#depth"].number()) == depth) + if (!m_macros.empty() && m_macros.back().depth() == depth) m_macros.pop_back(); } } - bool MacroProcessor::applyMacro(Node& node) + bool MacroProcessor::applyMacro(Node& node) // TODO remove { return m_executor_pipeline.applyMacro(node); } @@ -247,9 +246,9 @@ namespace Ark::internal { if (node.nodeType() == NodeType::Symbol) { - Node* macro = findNearestMacro(node.string()); - if (macro != nullptr && macro->list().size() == 2) - return macro->list()[1]; + const Node* macro = findNearestMacro(node.string()); + if (macro != nullptr && macro->constList().size() == 2) + return macro->constList()[1]; else return node; } @@ -279,7 +278,7 @@ namespace Ark::internal (one.nodeType() == two.nodeType() && two.nodeType() == NodeType::Number) ? Node(one.number() op two.number()) : node) const std::string& name = node.list()[0].string(); - if (Node* macro = findNearestMacro(name); macro != nullptr) + if (const Node* macro = findNearestMacro(name); macro != nullptr) { applyMacro(node.list()[0]); if (node.list()[0].nodeType() == NodeType::Unused) @@ -501,18 +500,15 @@ namespace Ark::internal return false; } - Node* MacroProcessor::findNearestMacro(const std::string& name) + const Node* MacroProcessor::findNearestMacro(const std::string& name) const { if (m_macros.empty()) return nullptr; for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it) { - if (it->size() != 0) - { - if (auto res = it->find(name); res != it->end()) - return &res->second; - } + if (auto res = it->has(name); res != nullptr) + return res; } return nullptr; } @@ -524,14 +520,10 @@ namespace Ark::internal for (auto it = m_macros.rbegin(); it != m_macros.rend(); ++it) { - if (it->size() != 0) + if (it->remove(name)) { - if (auto res = it->find(name); res != it->end()) - { - // stop right here because we found one matching macro - it->erase(res); - return; - } + // stop right here because we found one matching macro + return; } } } @@ -599,7 +591,7 @@ namespace Ark::internal return it != operators.end() || it2 != Builtins::builtins.end() || - const_cast(this)->findNearestMacro(node.string()) != nullptr || + findNearestMacro(node.string()) != nullptr || node.string() == "list"; } From d6288097703b3c8783d24fe6b2bfb1cfc76fd525 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 18 Feb 2023 17:40:57 +0100 Subject: [PATCH 39/42] fix: removed UB (luckily this piece of code disappear in v4) --- src/arkreactor/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arkreactor/Utils.cpp b/src/arkreactor/Utils.cpp index 1dd592018..03799843b 100644 --- a/src/arkreactor/Utils.cpp +++ b/src/arkreactor/Utils.cpp @@ -11,7 +11,7 @@ namespace Ark::Utils do { d *= 10; - temp = d - static_cast(d); + temp = d - static_cast(d); decimal_places++; } while (temp > precision && decimal_places < std::numeric_limits::digits10); From 1f8fdccdf24fafa53455adac109acd24cb860770 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 18 Feb 2023 17:52:02 +0100 Subject: [PATCH 40/42] fix: a macro with no argument no longer crash the processor --- CHANGELOG.md | 1 + src/arkreactor/Compiler/Macros/Executors/List.cpp | 2 +- tests/arkscript/macro-tests.ark | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a81ca395..3909fe547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - adding bound checking on operator @ when used in macros - better arity check for macros - fixed a bug in the macro processor where macros were deleted when they shouldn't +- fixed a bug where macro functions with no argument would crash the macro processor ### Removed diff --git a/src/arkreactor/Compiler/Macros/Executors/List.cpp b/src/arkreactor/Compiler/Macros/Executors/List.cpp index 0cc63e06f..9856815bb 100644 --- a/src/arkreactor/Compiler/Macros/Executors/List.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/List.cpp @@ -25,7 +25,7 @@ namespace Ark::internal std::size_t args_needed = args.list().size(); std::size_t args_given = node.constList().size() - 1; // remove the first (the name of the macro) std::string macro_name = macro->constList()[0].string(); - bool has_spread = args.list().back().nodeType() == NodeType::Spread; + bool has_spread = args_needed > 0 && args.list().back().nodeType() == NodeType::Spread; // bind node->list() to temp_body using macro->constList()[1] std::unordered_map args_applied; diff --git a/tests/arkscript/macro-tests.ark b/tests/arkscript/macro-tests.ark index 9f897a5f3..844600aae 100644 --- a/tests/arkscript/macro-tests.ark +++ b/tests/arkscript/macro-tests.ark @@ -4,6 +4,9 @@ (mut tests 0) (let start-time (time)) + !{void () nil} + (set tests (assert-eq (void) nil "macro void with no arguments" tests)) + !{add_two (a b) (+ a b)} !{nice_value 12} From 39cd60091f37f19d7d33767999d3352e3dedc9de Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 19 Feb 2023 18:26:52 +0100 Subject: [PATCH 41/42] refacto: renaming macro list executor to function executor --- include/Ark/Compiler/Macros/Executor.hpp | 1 - .../Macros/Executors/{List.hpp => Function.hpp} | 12 ++++++------ .../Macros/Executors/{List.cpp => Function.cpp} | 7 +++---- src/arkreactor/Compiler/Macros/Executors/Symbol.cpp | 5 +---- src/arkreactor/Compiler/Macros/Processor.cpp | 6 +++--- 5 files changed, 13 insertions(+), 18 deletions(-) rename include/Ark/Compiler/Macros/Executors/{List.hpp => Function.hpp} (63%) rename src/arkreactor/Compiler/Macros/Executors/{List.cpp => Function.cpp} (95%) diff --git a/include/Ark/Compiler/Macros/Executor.hpp b/include/Ark/Compiler/Macros/Executor.hpp index a889d1c6c..aa53d21fd 100644 --- a/include/Ark/Compiler/Macros/Executor.hpp +++ b/include/Ark/Compiler/Macros/Executor.hpp @@ -41,7 +41,6 @@ namespace Ark::internal */ virtual ~MacroExecutor(); - /** * @brief Executes macros in the Node if the Executor can handle it * diff --git a/include/Ark/Compiler/Macros/Executors/List.hpp b/include/Ark/Compiler/Macros/Executors/Function.hpp similarity index 63% rename from include/Ark/Compiler/Macros/Executors/List.hpp rename to include/Ark/Compiler/Macros/Executors/Function.hpp index 4d7c7dbf3..604990239 100644 --- a/include/Ark/Compiler/Macros/Executors/List.hpp +++ b/include/Ark/Compiler/Macros/Executors/Function.hpp @@ -1,11 +1,11 @@ /** - * @file List.hpp - * @author Ray John Alovera (rakista112@gmail.com) + * @file Function.hpp + * @author Ray John Alovera (rakista112@gmail.com), Alexandre Plateau (lexplt.dev@gmail.com) * @brief Executor for List Macros - * @version 0.3 + * @version 1.0 * @date 2021-05-04 * - * @copyright Copyright (c) 2021 + * @copyright Copyright (c) 2021-2023 * */ @@ -18,10 +18,10 @@ namespace Ark::internal { /** - * @brief Handles List macros + * @brief Handles function macros * */ - class ListExecutor : public MacroExecutor + class FunctionExecutor : public MacroExecutor { public: using MacroExecutor::MacroExecutor; diff --git a/src/arkreactor/Compiler/Macros/Executors/List.cpp b/src/arkreactor/Compiler/Macros/Executors/Function.cpp similarity index 95% rename from src/arkreactor/Compiler/Macros/Executors/List.cpp rename to src/arkreactor/Compiler/Macros/Executors/Function.cpp index 9856815bb..eaca0e710 100644 --- a/src/arkreactor/Compiler/Macros/Executors/List.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Function.cpp @@ -1,14 +1,13 @@ -#include -#include +#include namespace Ark::internal { - bool ListExecutor::canHandle(Node& node) + bool FunctionExecutor::canHandle(Node& node) { return node.nodeType() == NodeType::List && node.constList().size() > 0 && node.constList()[0].nodeType() == NodeType::Symbol; } - bool ListExecutor::applyMacro(Node& node) + bool FunctionExecutor::applyMacro(Node& node) { Node& first = node.list()[0]; const Node* macro = findNearestMacro(first.string()); diff --git a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp index 672bafce0..97ecdecb7 100644 --- a/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp +++ b/src/arkreactor/Compiler/Macros/Executors/Symbol.cpp @@ -9,10 +9,7 @@ namespace Ark::internal bool SymbolExecutor::applyMacro(Node& node) { - // error ? - const Node* macro = findNearestMacro(node.string()); - - if (macro != nullptr) + if (const Node* macro = findNearestMacro(node.string()); macro != nullptr) { // !{name value} if (macro->constList().size() == 2) diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp index 167ce6728..e678aea88 100644 --- a/src/arkreactor/Compiler/Macros/Processor.cpp +++ b/src/arkreactor/Compiler/Macros/Processor.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include namespace Ark::internal @@ -19,7 +19,7 @@ namespace Ark::internal m_executor_pipeline = MacroExecutorPipeline( { std::make_shared(this), std::make_shared(this), - std::make_shared(this) }); + std::make_shared(this) }); m_predefined_macros = { "symcat", @@ -210,7 +210,7 @@ namespace Ark::internal } } - bool MacroProcessor::applyMacro(Node& node) // TODO remove + bool MacroProcessor::applyMacro(Node& node) { return m_executor_pipeline.applyMacro(node); } From 65727a9e00b111e8ab2b7c39e6700b98b9f66f7b Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sun, 19 Feb 2023 18:56:15 +0100 Subject: [PATCH 42/42] chore: update changelog --- CHANGELOG.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3909fe547..b95dd9ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased version] +## [3.5.0] - 2023-02-19 ### Added - added fuzzing tools and corpus for [AFL](https://github.com/AFLplusplus/AFLplusplus) - added some tests for errors @@ -20,12 +20,6 @@ - fixed a bug in the macro processor where macros were deleted when they shouldn't - fixed a bug where macro functions with no argument would crash the macro processor -### Removed - - -### Deprecated - - ## [3.4.0] - 2022-09-12 ### Added - added new `async` and `await` builtins