diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6c87e1c3d..2818ddc78 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,7 +18,6 @@ on:
- 'LICENCE'
- '.gitignore'
pull_request:
- branches: [dev]
env:
BUILD_TYPE: Debug
@@ -215,7 +214,6 @@ jobs:
shell: bash
run: |
mkdir -p artifact/lib/std
- cp build/ark artifact
cp build/arkscript artifact
cp build/libArkReactor.* artifact
cp lib/*.arkm artifact/lib
@@ -227,7 +225,6 @@ jobs:
shell: bash
run: |
mkdir -p artifact/lib/std
- cp build/$BUILD_TYPE/ark.exe artifact
cp build/$BUILD_TYPE/arkscript.exe artifact
cp build/$BUILD_TYPE/ArkReactor.dll artifact
cp lib/*.arkm artifact/lib
@@ -307,7 +304,7 @@ jobs:
run: |
mv artifact/cpp/out tests/cpp/
mv build/lib/*.arkm lib/
- chmod u+x build/ark build/arkscript tests/cpp/out/*
+ chmod u+x build/arkscript tests/cpp/out/*
- name: Pre-test
if: startsWith(matrix.config.name, 'Windows')
@@ -343,7 +340,7 @@ jobs:
- shell: bash
run: |
mv build/lib/*.arkm lib/
- chmod u+x build/ark build/arkscript
+ chmod u+x build/arkscript
- name: Update LLVM compilers
shell: bash
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6641bb6f5..0efb65787 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -116,7 +116,6 @@ jobs:
shell: bash
run: |
mkdir -p artifact/lib/std
- cp build/ark artifact
cp build/arkscript artifact
cp build/libArkReactor.* artifact
cp lib/*.arkm artifact/lib
@@ -128,7 +127,6 @@ jobs:
shell: bash
run: |
mkdir -p artifact/lib/std
- cp build/$BUILD_TYPE/ark.exe artifact
cp build/$BUILD_TYPE/arkscript.exe artifact
cp build/$BUILD_TYPE/ArkReactor.dll artifact
cp lib/*.arkm artifact/lib
@@ -206,7 +204,7 @@ jobs:
run: |
mv artifact/cpp/out tests/cpp/
mv build/lib/*.arkm lib/
- chmod u+x build/ark build/arkscript tests/cpp/out/*
+ chmod u+x build/arkscript tests/cpp/out/*
- name: Pre-test
if: startsWith(matrix.config.name, 'Windows')
diff --git a/.gitignore b/.gitignore
index 3c6b0a55e..f59d697ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ afl/
# Folders
.vs/
.idea/
+.cache/
build/
ninja/
diff --git a/.gitmodules b/.gitmodules
index d5d8b0eda..137ae9487 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,9 +7,6 @@
[submodule "lib/modules"]
path = lib/modules
url = https://github.com/ArkScript-lang/modules.git
-[submodule "lib/String"]
- path = lib/String
- url = https://github.com/ArkScript-lang/String.git
[submodule "lib/clipp"]
path = lib/clipp
url = https://github.com/muellan/clipp.git
@@ -22,3 +19,6 @@
[submodule "lib/replxx"]
path = lib/replxx
url = https://github.com/AmokHuginnsson/replxx.git
+[submodule "lib/fmt"]
+ path = lib/fmt
+ url = https://github.com/fmtlib/fmt.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b95dd9ce3..e311bae6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
# Change Log
+## [4.0.0] - 20XX-XX-XX
+### Added
+- more tests for the io builtins
+- added lines and code coloration in the error context
+- new dependency: fmtlib
+
+### Changed
+- instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument
+- enhanced the bytecode reader and its command line interface
+- added the padding/instruction/argumentation values when displaying instructions in the bytecode reader
+- fixed underline bug in the error context
+- the str:format functions now expects strings following this syntax: https://fmt.dev/latest/syntax.html
+- more documentation about the compiler implementation
+- more documentation about the virtual machine
+- closures can be now be compared field per field: `(= closure1 closure2)` will work only if they have the same fields (name) and if the values match
+
+### Removed
+- removed unused `NodeType::Closure`
+- removing the custom string, replacing it with std::string (the format engine of the custom string had a lot of memory leaks)
+- removing the custom string, replacing it with std::string (the format engine of the custom string had a lot of memory leaks)
+- `Utils::digPlaces` and `Utils::decPlaces` got removed as they were no longer needed
+- removed deprecated code (`list:removeAt`, `ark` executable now replaced by `arkscript`)
+
## [3.5.0] - 2023-02-19
### Added
- added fuzzing tools and corpus for [AFL](https://github.com/AFLplusplus/AFLplusplus)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 986d1b3e0..a38fd0ad2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.9)
project(ark CXX)
# VERSION
-set(ARK_VERSION_MAJOR 3)
-set(ARK_VERSION_MINOR 4)
+set(ARK_VERSION_MAJOR 4)
+set(ARK_VERSION_MINOR 0)
set(ARK_VERSION_PATCH 0)
include(cmake/link_time_optimization.cmake)
@@ -26,7 +26,8 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# files needed for the library ArkReactor
file(GLOB_RECURSE SOURCE_FILES
- ${ark_SOURCE_DIR}/src/arkreactor/*.cpp)
+ ${ark_SOURCE_DIR}/src/arkreactor/*.cpp
+ ${ark_SOURCE_DIR}/lib/fmt/src/*.cc)
add_library(ArkReactor SHARED ${SOURCE_FILES})
@@ -92,15 +93,14 @@ endif()
# Link libraries
add_subdirectory("${ark_SOURCE_DIR}/lib/termcolor" EXCLUDE_FROM_ALL)
-add_subdirectory("${ark_SOURCE_DIR}/lib/String/" EXCLUDE_FROM_ALL)
target_include_directories(ArkReactor
PUBLIC
"${ark_SOURCE_DIR}/lib/utf8_decoder/"
"${ark_SOURCE_DIR}/lib/picosha2/"
- "${ark_SOURCE_DIR}/lib/String/include/")
+ "${ark_SOURCE_DIR}/lib/fmt/include")
-target_link_libraries(ArkReactor PUBLIC termcolor ArkScriptString)
+target_link_libraries(ArkReactor PUBLIC termcolor)
if (UNIX OR LINUX)
if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG)
@@ -184,7 +184,6 @@ if (ARK_BUILD_EXE)
${ark_SOURCE_DIR}/src/arkscript/main.cpp)
add_executable(arkscript ${EXE_SOURCES})
- add_executable(ark ${EXE_SOURCES})
if (MSVC)
# Disable warnings for lib/replxx
@@ -199,15 +198,10 @@ if (ARK_BUILD_EXE)
add_subdirectory("${ark_SOURCE_DIR}/lib/clipp" EXCLUDE_FROM_ALL)
target_include_directories(arkscript PUBLIC "${ark_SOURCE_DIR}/src/arkscript/")
- target_include_directories(ark PUBLIC "${ark_SOURCE_DIR}/src/arkscript/")
-
target_link_libraries(arkscript PUBLIC ArkReactor replxx clipp termcolor)
- target_link_libraries(ark PUBLIC ArkReactor replxx clipp termcolor)
target_compile_features(arkscript PRIVATE cxx_std_17)
- target_compile_features(ark PRIVATE cxx_std_17)
- enable_lto(ark)
enable_lto(arkscript)
# Installs the arkscript executable.
diff --git a/README.md b/README.md
index 5e6e5848e..2d134b7cd 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# ArkScript 
+# ArkScript 


-
+
@@ -173,15 +173,17 @@ DESCRIPTION
ArkScript programming language
SYNOPSIS
- arkscript -h
- arkscript -v
- arkscript --dev-info
- arkscript -e
- arkscript -c [-d]
- arkscript -bcr -on
- arkscript -bcr [-(a|st|vt)] [-s ]
- arkscript -bcr [-cs] [-p ]
- arkscript [-d] [-L ]
+ arkscript -h
+ arkscript -v
+ arkscript --dev-info
+ arkscript -e
+ arkscript -c [-d]
+ arkscript -bcr -on
+ arkscript -bcr -a [-s ]
+ arkscript -bcr -st [-s ]
+ arkscript -bcr -vt [-s ]
+ arkscript -bcr [-cs] [-p ] [-s ]
+ arkscript [-d] [-L ]
OPTIONS
-h, --help Display this message
@@ -195,9 +197,9 @@ OPTIONS
-a, --all Display all the bytecode segments (default)
-st, --symbols Display only the symbols table
-vt, --values Display only the values table
- -s, --slice Select a slice of instructions in the bytecode
-cs, --code Display only the code segments
-p, --page Set the bytecode reader code segment to display
+ -s, --slice Select a slice of instructions in the bytecode
-L, --lib Set the location of the ArkScript standard library. Paths can be
delimited by ';'
diff --git a/examples/99bottles.ark b/examples/99bottles.ark
index bfb83c3eb..03f599f99 100644
--- a/examples/99bottles.ark
+++ b/examples/99bottles.ark
@@ -16,6 +16,6 @@
(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))
+ (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/blockchain.ark b/examples/blockchain.ark
index 4ca29c813..62c659e37 100644
--- a/examples/blockchain.ark
+++ b/examples/blockchain.ark
@@ -95,9 +95,9 @@
(let new (json:fromString request))
(set nodes_transactions (append nodes_transactions new))
(print "New transaction")
- (print (str:format "FROM: %%" (json:get new "from")))
- (print (str:format "TO: %%" (json:get new "to")))
- (print (str:format "AMOUNT: %%" (json:get new "amount")))
+ (print (str:format "FROM: {}" (json:get new "from")))
+ (print (str:format "TO: {}" (json:get new "to")))
+ (print (str:format "AMOUNT: {}" (json:get new "amount")))
# return value
[200 "transaction submission successful" "text/plain"]}))
diff --git a/examples/run-all b/examples/run-all
index 7fdf61037..a48a0fdf7 100644
--- a/examples/run-all
+++ b/examples/run-all
@@ -10,11 +10,11 @@ Purple='\033[0;35m'
Cyan='\033[0;36m'
White='\033[0;37m'
-maybe_ark=$(which ark)
+maybe_ark=$(which arkscript)
if [[ $($maybe_ark --help | grep "ArkScript programming language") != "" ]]; then
ark=$maybe_ark
-elif [ -f ../build/ark ]; then
- ark=../build/ark
+elif [ -f ../build/arkscript ]; then
+ ark=../build/arkscript
else
echo -e "${Red}Couldn't find ark${Reset}"
exit 1
diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp
index 69ae219ad..a34ce632c 100644
--- a/include/Ark/Builtins/Builtins.hpp
+++ b/include/Ark/Builtins/Builtins.hpp
@@ -4,9 +4,9 @@
* @brief Host the declaration of all the ArkScript builtins
* @version 0.1
* @date 2020-10-27
- *
+ *
* @copyright Copyright (c) 2020-2021
- *
+ *
*/
#ifndef ARK_BUILTINS_BUILTINS_HPP
@@ -35,13 +35,12 @@ namespace Ark::internal::Builtins
// ------------------------------
namespace List
{
- Value reverseList(std::vector& n, VM* vm); // list:reverse, single arg
- Value findInList(std::vector& n, VM* vm); // list:find, 2 arguments
- Value removeAtList(std::vector& n, VM* vm); // list:removeAt, 2 arguments -- DEPRECATED
- Value sliceList(std::vector& n, VM* vm); // list:slice, 4 arguments
- Value sort_(std::vector& n, VM* vm); // list:sort, 1 argument
- Value fill(std::vector& n, VM* vm); // list:fill, 2 arguments
- Value setListAt(std::vector& n, VM* vm); // list:setAt, 3 arguments
+ Value reverseList(std::vector& n, VM* vm); // list:reverse, single arg
+ Value findInList(std::vector& n, VM* vm); // list:find, 2 arguments
+ Value sliceList(std::vector& n, VM* vm); // list:slice, 4 arguments
+ Value sort_(std::vector& n, VM* vm); // list:sort, 1 argument
+ Value fill(std::vector& n, VM* vm); // list:fill, 2 arguments
+ Value setListAt(std::vector& n, VM* vm); // list:setAt, 3 arguments
}
namespace IO
diff --git a/include/Ark/Builtins/BuiltinsErrors.inl b/include/Ark/Builtins/BuiltinsErrors.inl
deleted file mode 100644
index c5e187d7b..000000000
--- a/include/Ark/Builtins/BuiltinsErrors.inl
+++ /dev/null
@@ -1,8 +0,0 @@
-// List
-
-// TO BE DEPRECATED
-#define LIST_RMAT_ARITY "list:removeAt needs 2 arguments: list, index"
-#define LIST_RMAT_TE0 "list:removeAt: list must be a List"
-#define LIST_RMAT_TE1 "list:removeAt: index must be a Number"
-#define LIST_RMAT_OOR "list:removeAt: index out of range"
-// --
diff --git a/include/Ark/Compiler/Common.hpp b/include/Ark/Compiler/Common.hpp
index 593a42b9d..3d2391154 100644
--- a/include/Ark/Compiler/Common.hpp
+++ b/include/Ark/Compiler/Common.hpp
@@ -2,10 +2,10 @@
* @file Common.hpp
* @author Alexandre Plateau (lexplt.dev@gmail.com)
* @brief Common code for the compiler
- * @version 0.3
+ * @version 0.4
* @date 2021-10-02
*
- * @copyright Copyright (c) 2021
+ * @copyright Copyright (c) 2021-2022
*
*/
@@ -34,13 +34,12 @@ namespace Ark::internal
String,
Number,
List,
- Closure,
Macro,
Spread,
Unused
};
- constexpr std::array nodeTypes = {
+ constexpr std::array nodeTypes = {
"Symbol",
"Capture",
"GetField",
@@ -48,7 +47,6 @@ namespace Ark::internal
"String",
"Number",
"List",
- "Closure",
"Macro",
"Spread",
"Unused"
diff --git a/include/Ark/Compiler/Compiler.hpp b/include/Ark/Compiler/Compiler.hpp
index d77d6a1b7..a5a8d4524 100644
--- a/include/Ark/Compiler/Compiler.hpp
+++ b/include/Ark/Compiler/Compiler.hpp
@@ -2,7 +2,7 @@
* @file Compiler.hpp
* @author Alexandre Plateau (lexplt.dev@gmail.com)
* @brief ArkScript compiler is in charge of transforming the AST into bytecode
- * @version 0.3
+ * @version 1.2
* @date 2020-10-27
*
* @copyright Copyright (c) 2020-2021
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -82,8 +83,8 @@ namespace Ark
std::vector m_defined_symbols;
std::vector m_plugins;
std::vector m_values;
- std::vector> m_code_pages;
- std::vector> m_temp_pages; ///< we need temporary code pages for some compilations passes
+ std::vector> m_code_pages;
+ std::vector> m_temp_pages; ///< we need temporary code pages for some compilations passes
bytecode_t m_bytecode;
unsigned m_debug; ///< the debug level of the compiler
@@ -104,9 +105,9 @@ namespace Ark
* @brief helper functions to get a temp or finalized code page
*
* @param i page index, if negative, refers to a temporary code page
- * @return std::vector&
+ * @return std::vector&
*/
- inline std::vector& page(int i) noexcept
+ inline std::vector& page(int i) noexcept
{
if (i >= 0)
return m_code_pages[i];
@@ -117,21 +118,15 @@ namespace Ark
* @brief helper functions to get a temp or finalized code page
*
* @param i page index, if negative, refers to a temporary code page
- * @return std::vector*
+ * @return std::vector*
*/
- inline std::vector* page_ptr(int i) noexcept
+ inline std::vector* page_ptr(int i) noexcept
{
if (i >= 0)
return &m_code_pages[i];
return &m_temp_pages[-i - 1];
}
- inline void setNumberAt(int p, std::size_t at_inst, std::size_t number)
- {
- page(p)[at_inst] = (number & 0xff00) >> 8;
- page(p)[at_inst + 1] = number & 0x00ff;
- }
-
/**
* @brief Count the number of "valid" ark objects in a node
* @details Isn't considered valid a GetField, because we use
@@ -148,7 +143,7 @@ namespace Ark
* @param name symbol name
* @return std::optional position in the operators' list
*/
- std::optional isOperator(const std::string& name) noexcept;
+ std::optional getOperator(const std::string& name) noexcept;
/**
* @brief Checking if a symbol is a builtin
@@ -156,7 +151,7 @@ namespace Ark
* @param name symbol name
* @return std::optional position in the builtins' list
*/
- std::optional isBuiltin(const std::string& name) noexcept;
+ std::optional getBuiltin(const std::string& name) noexcept;
/**
* @brief Check if a symbol needs to be compiled to a specific instruction
@@ -164,7 +159,7 @@ namespace Ark
* @param name
* @return std::optional corresponding instruction if it exists
*/
- inline std::optional isSpecific(const std::string& name) noexcept
+ inline std::optional getSpecific(const std::string& name) noexcept
{
if (name == "list")
return internal::Instruction::LIST;
@@ -198,9 +193,8 @@ namespace Ark
*
* @param inst
* @param previous
- * @param p
*/
- void pushSpecificInstArgc(internal::Instruction inst, uint16_t previous, int p) noexcept;
+ uint16_t computeSpecificInstArgc(internal::Instruction inst, uint16_t previous) noexcept;
/**
* @brief Checking if a symbol may be coming from a plugin
@@ -212,51 +206,41 @@ namespace Ark
bool mayBeFromPlugin(const std::string& name) noexcept;
/**
- * @brief Throw a nice error message
+ * @brief Display a warning message
*
* @param message
* @param node
*/
- [[noreturn]] void throwCompilerError(const std::string& message, const internal::Node& node);
+ void compilerWarning(const std::string& message, const internal::Node& node);
/**
- * @brief Display a warning message
+ * @brief Throw a nice error message
*
* @param message
* @param node
*/
- void compilerWarning(const std::string& message, const internal::Node& node);
+ [[noreturn]] void throwCompilerError(const std::string& message, const internal::Node& node);
/**
- * @brief Compile a single node recursively
+ * @brief Compile an expression (a node) recursively
*
* @param x the internal::Node to compile
* @param p the current page number we're on
- * @param produces_result
+ * @param is_result_unused
* @param is_terminal
* @param var_name
*/
- void _compile(const internal::Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name = "");
+ void compileExpression(const internal::Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name = "");
- void compileSymbol(const internal::Node& x, int p, bool produces_result);
- void compileSpecific(const internal::Node& c0, const internal::Node& x, int p, bool produces_result);
- void compileIf(const internal::Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name);
- void compileFunction(const internal::Node& x, int p, bool produces_result, const std::string& var_name);
+ void compileSymbol(const internal::Node& x, int p, bool is_result_unused);
+ void compileSpecific(const internal::Node& c0, const internal::Node& x, int p, bool is_result_unused);
+ void compileIf(const internal::Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name);
+ void compileFunction(const internal::Node& x, int p, bool is_result_unused, const std::string& var_name);
void compileLetMutSet(internal::Keyword n, const internal::Node& x, int p);
void compileWhile(const internal::Node& x, int p);
- void compileQuote(const internal::Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name);
+ void compileQuote(const internal::Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name);
void compilePluginImport(const internal::Node& x, int p);
- void compileDel(const internal::Node& x, int p);
- void handleCalls(const internal::Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name);
-
- /**
- * @brief Put a value in the bytecode, handling the closures chains
- *
- * @param x value node
- * @param p current page index
- * @param produces_result
- */
- void putValue(const internal::Node& x, int p, bool produces_result);
+ void handleCalls(const internal::Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name);
/**
* @brief Register a given node in the symbol table
@@ -299,14 +283,6 @@ namespace Ark
*/
void checkForUndefinedSymbol();
- /**
- * @brief Push a number on stack (need 2 bytes)
- *
- * @param n the number to push
- * @param page the page where it should land, nullptr for current page
- */
- void pushNumber(uint16_t n, std::vector* page = nullptr) noexcept;
-
/**
* @brief Suggest a symbol of what the user may have meant to input
*
diff --git a/include/Ark/Compiler/Word.hpp b/include/Ark/Compiler/Word.hpp
new file mode 100644
index 000000000..64c5b766b
--- /dev/null
+++ b/include/Ark/Compiler/Word.hpp
@@ -0,0 +1,45 @@
+/**
+ * @file Word.hpp
+ * @author Alexandre Plateau (lexplt.dev@gmail.com)
+ * @brief Describe an instruction and its immediate argument
+ * @version 0.3
+ * @date 2022-07-02
+ *
+ * @copyright Copyright (c) 2022
+ *
+ */
+
+#ifndef ARK_COMPILER_WORD_HPP
+#define ARK_COMPILER_WORD_HPP
+
+#include
+#include
+
+namespace Ark::internal
+{
+ struct bytes_t
+ {
+ uint8_t second;
+ uint8_t first;
+ };
+
+ struct Word
+ {
+ uint8_t padding = 0; ///< Padding reserved for future use
+ uint8_t opcode; ///< Instruction opcode
+ union {
+ uint16_t data; ///< Immediate data, interpreted differently for different instructions
+ bytes_t bytes;
+ };
+
+ Word() :
+ opcode(0), data(0)
+ {}
+
+ Word(uint8_t inst, uint16_t arg = 0) :
+ opcode(inst), data(arg)
+ {}
+ };
+}
+
+#endif
diff --git a/include/Ark/Files.hpp b/include/Ark/Files.hpp
index 1df743e32..c9fd31736 100644
--- a/include/Ark/Files.hpp
+++ b/include/Ark/Files.hpp
@@ -26,7 +26,7 @@ namespace Ark::Utils
* @return true on success
* @return false on failure
*/
- inline bool fileExists(const std::string& name) noexcept // 11
+ inline bool fileExists(const std::string& name) noexcept
{
try
{
@@ -47,7 +47,7 @@ namespace Ark::Utils
*/
inline std::string readFile(const std::string& name)
{
- std::ifstream f(name.c_str());
+ std::ifstream f(name);
// admitting the file exists
return std::string(
(std::istreambuf_iterator(f)),
diff --git a/include/Ark/Utils.hpp b/include/Ark/Utils.hpp
index df13883d4..03a3ca98c 100644
--- a/include/Ark/Utils.hpp
+++ b/include/Ark/Utils.hpp
@@ -64,24 +64,6 @@ namespace Ark::Utils
return end != s.c_str() && *end == '\0' && val != HUGE_VAL;
}
- /**
- * @brief Count the number of decimals for a double
- * @todo remove when migrating to libfmt
- *
- * @param d
- * @return int
- */
- int decPlaces(double d);
-
- /**
- * @brief Count the number of digits for a double
- * @todo remove when migrating to libfmt
- *
- * @param d
- * @return int
- */
- int digPlaces(double d);
-
/**
* @brief Calculate the Levenshtein distance between two strings
*
diff --git a/include/Ark/VM/Scope.hpp b/include/Ark/VM/Scope.hpp
index f4901e6b3..ff6f497b6 100644
--- a/include/Ark/VM/Scope.hpp
+++ b/include/Ark/VM/Scope.hpp
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
namespace Ark::internal
@@ -66,6 +67,14 @@ namespace Ark::internal
*/
Value* operator[](uint16_t id) noexcept;
+ /**
+ * @brief Get a value from its symbol id
+ *
+ * @param id
+ * @return const Value* Returns nullptr if the value can not be found
+ */
+ const Value* operator[](uint16_t id) const noexcept;
+
/**
* @brief Get the id of a variable based on its value ; used for debug only
*
@@ -81,11 +90,15 @@ namespace Ark::internal
*/
std::size_t size() const noexcept;
+ friend ARK_API bool operator==(const Scope& A, const Scope& B) noexcept;
+
friend class Ark::VM;
friend class Ark::internal::Closure;
private:
std::vector> m_data;
+ uint16_t m_min_id;
+ uint16_t m_max_id;
};
}
diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp
index f28354262..0df4087c5 100644
--- a/include/Ark/VM/VM.hpp
+++ b/include/Ark/VM/VM.hpp
@@ -2,7 +2,7 @@
* @file VM.hpp
* @author Alexandre Plateau (lexplt.dev@gmail.com)
* @brief The ArkScript virtual machine
- * @version 0.3
+ * @version 1.0
* @date 2020-10-27
*
* @copyright Copyright (c) 2020-2021
@@ -54,10 +54,6 @@ namespace Ark
*/
explicit VM(State& state) noexcept;
- [[deprecated("Use VM(State&) instead of VM(State*)")]] explicit VM(State* state) noexcept :
- VM(*state)
- {}
-
/**
* @brief Run the bytecode held in the state
*
@@ -197,15 +193,6 @@ namespace Ark
*/
void init() noexcept;
- /**
- * @brief Read a 2 bytes number from the current bytecode page, starting at the current instruction
- * @details Modify the instruction pointer to point on the instruction right after the number.
- *
- * @param context
- * @return uint16_t
- */
- inline uint16_t readNumber(internal::ExecutionContext& context);
-
// ================================================
// stack related
// ================================================
diff --git a/include/Ark/VM/Value.hpp b/include/Ark/VM/Value.hpp
index afed85857..8a660c3c3 100644
--- a/include/Ark/VM/Value.hpp
+++ b/include/Ark/VM/Value.hpp
@@ -20,7 +20,6 @@
#include
#include
#include
-#include // our string implementation
#include
#include
@@ -76,7 +75,7 @@ namespace Ark
using Value_t = std::variant<
double, // 8 bytes
- String, // 16 bytes
+ std::string, // 32 bytes
internal::PageAddr_t, // 2 bytes
ProcType, // 8 bytes
internal::Closure, // 24 bytes
@@ -84,7 +83,7 @@ namespace Ark
std::vector, // 24 bytes
Value* // 8 bytes
>; // +8 bytes overhead
- // total 32 bytes
+ // total 40 bytes
/**
* @brief Construct a new Value object
@@ -148,13 +147,6 @@ namespace Ark
*/
explicit Value(const std::string& value) noexcept;
- /**
- * @brief Construct a new Value object as a String
- *
- * @param value
- */
- explicit Value(const String& value) noexcept;
-
/**
* @brief Construct a new Value object as a String
*
@@ -229,9 +221,9 @@ namespace Ark
/**
* @brief Return the stored string
*
- * @return const String&
+ * @return const std::string&
*/
- inline const String& string() const;
+ inline const std::string& string() const;
/**
* @brief Return the stored list
@@ -257,9 +249,9 @@ namespace Ark
/**
* @brief Return the stored string as a reference
*
- * @return String&
+ * @return std::string&
*/
- String& stringRef();
+ std::string& stringRef();
/**
* @brief Return the stored user type as a reference
diff --git a/include/Ark/VM/Value/Closure.hpp b/include/Ark/VM/Value/Closure.hpp
index a0b3bf87e..8b7d22457 100644
--- a/include/Ark/VM/Value/Closure.hpp
+++ b/include/Ark/VM/Value/Closure.hpp
@@ -95,7 +95,7 @@ namespace Ark::internal
*/
void toString(std::ostream& os, VM& vm) const noexcept;
- friend ARK_API_INLINE bool operator==(const Closure& A, const Closure& B) noexcept;
+ friend ARK_API bool operator==(const Closure& A, const Closure& B) noexcept;
friend ARK_API_INLINE bool operator<(const Closure& A, const Closure& B) noexcept;
private:
@@ -104,11 +104,6 @@ namespace Ark::internal
PageAddr_t m_page_addr;
};
- inline bool operator==(const Closure& A, const Closure& B) noexcept
- {
- return A.m_scope == B.m_scope;
- }
-
inline bool operator<(const Closure& A, const Closure& B) noexcept
{
return A.m_page_addr < B.m_page_addr;
diff --git a/include/Ark/VM/inline/VM.inl b/include/Ark/VM/inline/VM.inl
index e14dcb177..01368c7f9 100644
--- a/include/Ark/VM/inline/VM.inl
+++ b/include/Ark/VM/inline/VM.inl
@@ -141,16 +141,6 @@ inline Value VM::resolve(internal::ExecutionContext* context, std::vector
#pragma region "stack management"
-inline uint16_t VM::readNumber(internal::ExecutionContext& context)
-{
- uint16_t tmp =
- (static_cast(m_state.m_pages[context.pp][context.ip]) << 8) +
- static_cast(m_state.m_pages[context.pp][context.ip + 1]);
-
- ++context.ip;
- return tmp;
-}
-
inline Value* VM::pop(internal::ExecutionContext& context)
{
if (context.sp > 0)
@@ -346,7 +336,7 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_)
context.locals.back()->push_back(context.last_symbol, function);
context.pp = new_page_pointer;
- context.ip = -1; // because we are doing a context.ip++ right after that
+ context.ip = 0;
break;
}
@@ -365,7 +355,7 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_)
swapStackForFunCall(argc, context);
context.pp = new_page_pointer;
- context.ip = -1; // because we are doing a context.ip++ right after that
+ context.ip = 0;
break;
}
@@ -383,10 +373,11 @@ inline void VM::call(internal::ExecutionContext& context, int16_t argc_)
needed_argc = 0;
// every argument is a MUT declaration in the bytecode
- while (m_state.m_pages[context.pp][index] == Instruction::MUT)
+ // index+1 to skip the padding
+ while (m_state.m_pages[context.pp][index + 1] == Instruction::MUT)
{
needed_argc += 1;
- index += 3; // jump the argument of MUT (integer on 2 bits, big endian)
+ index += 4; // instructions are on 4 bytes
}
if (needed_argc != argc)
diff --git a/include/Ark/VM/inline/Value.inl b/include/Ark/VM/inline/Value.inl
index 536094610..b8e8ea3a5 100644
--- a/include/Ark/VM/inline/Value.inl
+++ b/include/Ark/VM/inline/Value.inl
@@ -18,9 +18,9 @@ inline double Value::number() const
return std::get(m_value);
}
-inline const String& Value::string() const
+inline const std::string& Value::string() const
{
- return std::get(m_value);
+ return std::get(m_value);
}
inline const std::vector& Value::constList() const
@@ -100,7 +100,7 @@ inline bool operator!(const Value& A) noexcept
return !A.number();
case ValueType::String:
- return A.string().size() == 0;
+ return A.string().empty();
case ValueType::User:
case ValueType::Nil:
diff --git a/lib/String b/lib/String
deleted file mode 160000
index 2c358d940..000000000
--- a/lib/String
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2c358d940c937eb9b6257ba746dcb7dbac6cecbb
diff --git a/lib/fmt b/lib/fmt
new file mode 160000
index 000000000..7bdf0628b
--- /dev/null
+++ b/lib/fmt
@@ -0,0 +1 @@
+Subproject commit 7bdf0628b1276379886c7f6dda2cef2b3b374f0b
diff --git a/lib/modules b/lib/modules
index 0c99e073c..74f2cd3a2 160000
--- a/lib/modules
+++ b/lib/modules
@@ -1 +1 @@
-Subproject commit 0c99e073c53e9b3d73589ebeb1d7052b728c45b4
+Subproject commit 74f2cd3a246138f336997c8da8cba496f41c8930
diff --git a/src/arkreactor/Builtins/Builtins.cpp b/src/arkreactor/Builtins/Builtins.cpp
index 6ec5920fb..841032099 100644
--- a/src/arkreactor/Builtins/Builtins.cpp
+++ b/src/arkreactor/Builtins/Builtins.cpp
@@ -28,7 +28,6 @@ namespace Ark::internal::Builtins
// List
{ "list:reverse", Value(List::reverseList) },
{ "list:find", Value(List::findInList) },
- { "list:removeAt", Value(List::removeAtList) }, // DEPRECATED
{ "list:slice", Value(List::sliceList) },
{ "list:sort", Value(List::sort_) },
{ "list:fill", Value(List::fill) },
diff --git a/src/arkreactor/Builtins/IO.cpp b/src/arkreactor/Builtins/IO.cpp
index 8be164c8b..9bd14ef6e 100644
--- a/src/arkreactor/Builtins/IO.cpp
+++ b/src/arkreactor/Builtins/IO.cpp
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
namespace Ark::internal::Builtins::IO
{
@@ -61,7 +62,7 @@ namespace Ark::internal::Builtins::IO
Value input(std::vector& n, VM* vm [[maybe_unused]])
{
if (types::check(n, ValueType::String))
- std::printf("%s", n[0].string().c_str());
+ fmt::print(n[0].string());
else if (n.size() != 0)
types::generateError("input", { { types::Contract {}, types::Contract { { types::Typedef("prompt", ValueType::String) } } } }, n);
@@ -87,14 +88,14 @@ namespace Ark::internal::Builtins::IO
{
if (types::check(n, ValueType::String, ValueType::Any))
{
- std::ofstream f(n[0].string().c_str());
+ std::ofstream f(n[0].string());
if (f.is_open())
{
n[1].toString(f, *vm);
f.close();
}
else
- throw std::runtime_error("Couldn't write to file \"" + n[0].stringRef().toString() + "\"");
+ throw std::runtime_error("Couldn't write to file \"" + n[0].stringRef() + "\"");
}
else if (types::check(n, ValueType::String, ValueType::String, ValueType::Any))
{
@@ -106,14 +107,14 @@ namespace Ark::internal::Builtins::IO
if (mode == "a")
ios_mode = std::ios::out | std::ios::app;
- std::ofstream f(n[0].string().c_str(), ios_mode);
+ std::ofstream f(n[0].string(), ios_mode);
if (f.is_open())
{
n[2].toString(f, *vm);
f.close();
}
else
- throw std::runtime_error("Couldn't write to file \"" + n[0].stringRef().toString() + "\"");
+ throw std::runtime_error("Couldn't write to file \"" + n[0].stringRef() + "\"");
}
else
types::generateError(
@@ -142,7 +143,7 @@ namespace Ark::internal::Builtins::IO
{ { types::Contract { { types::Typedef("filename", ValueType::String) } } } },
n);
- auto filename = n[0].string().c_str();
+ std::string filename = n[0].string();
if (!Utils::fileExists(filename))
throw std::runtime_error("Couldn't read file \"" + std::string(filename) + "\": it doesn't exist");
@@ -166,7 +167,7 @@ namespace Ark::internal::Builtins::IO
{ { types::Contract { { types::Typedef("filename", ValueType::String) } } } },
n);
- return Utils::fileExists(n[0].string().c_str()) ? trueSym : falseSym;
+ return Utils::fileExists(n[0].string()) ? trueSym : falseSym;
}
/**
@@ -187,8 +188,8 @@ namespace Ark::internal::Builtins::IO
n);
std::vector r;
- for (const auto& entry : std::filesystem::directory_iterator(n[0].string().c_str()))
- r.emplace_back(entry.path().string().c_str());
+ for (const auto& entry : std::filesystem::directory_iterator(n[0].string()))
+ r.emplace_back(entry.path().string());
return Value(std::move(r));
}
@@ -210,7 +211,7 @@ namespace Ark::internal::Builtins::IO
{ { types::Contract { { types::Typedef("path", ValueType::String) } } } },
n);
- return (std::filesystem::is_directory(std::filesystem::path(n[0].string().c_str()))) ? trueSym : falseSym;
+ return (std::filesystem::is_directory(std::filesystem::path(n[0].string()))) ? trueSym : falseSym;
}
/**
@@ -230,7 +231,7 @@ namespace Ark::internal::Builtins::IO
{ { types::Contract { { types::Typedef("path", ValueType::String) } } } },
n);
- std::filesystem::create_directories(std::filesystem::path(n[0].string().c_str()));
+ std::filesystem::create_directories(std::filesystem::path(n[0].string()));
return nil;
}
@@ -260,7 +261,7 @@ namespace Ark::internal::Builtins::IO
{ { types::Contract { { types::Typedef("filename", ValueType::String), types::Typedef("filenames", ValueType::String, /* variadic */ true) } } } },
n);
- std::filesystem::remove_all(std::filesystem::path(it->string().c_str()));
+ std::filesystem::remove_all(std::filesystem::path(it->string()));
}
return nil;
diff --git a/src/arkreactor/Builtins/List.cpp b/src/arkreactor/Builtins/List.cpp
index f5746142e..5e6a8a933 100644
--- a/src/arkreactor/Builtins/List.cpp
+++ b/src/arkreactor/Builtins/List.cpp
@@ -3,7 +3,6 @@
#include
#include
-#include
#include
#include
@@ -61,44 +60,6 @@ namespace Ark::internal::Builtins::List
return Value(-1);
}
- /**
- * @name list:removeAt
- * @brief Remove an element in a List and return a new one
- * @details The original list is not modified
- * @param list the list to remove an element from
- * @param index the index of the element to remove (can be negative to search from the end)
- * =begin
- * (list:removeAt [1 2 3] 0) # [2 3]
- * (list:removeAt [1 2 3] -1) # [1 2]
- * =end
- * @author https://github.com/SuperFola
- */
- Value removeAtList(std::vector& n, VM* vm [[maybe_unused]])
- {
- static bool has_warned = false;
- if (!has_warned)
- {
- std::cout << "list:removeAt will be deprecated in ArkScript 4.0.0, consider using pop! or pop\n";
- has_warned = true;
- }
-
- // TEMP: not fixing the errors here because this will be deprecated and removed
-
- if (n.size() != 2)
- throw std::runtime_error(LIST_RMAT_ARITY);
- if (n[0].valueType() != ValueType::List)
- throw TypeError(LIST_RMAT_TE0);
- if (n[1].valueType() != ValueType::Number)
- throw TypeError(LIST_RMAT_TE1);
-
- std::size_t idx = static_cast(n[1].number());
- if (idx >= n[0].list().size())
- throw std::runtime_error(LIST_RMAT_OOR);
-
- n[0].list().erase(n[0].list().begin() + idx);
- return n[0];
- }
-
/**
* @name list:slice
* @brief Get a slice from a List
diff --git a/src/arkreactor/Builtins/Mathematics.cpp b/src/arkreactor/Builtins/Mathematics.cpp
index 232190a57..6570a7050 100644
--- a/src/arkreactor/Builtins/Mathematics.cpp
+++ b/src/arkreactor/Builtins/Mathematics.cpp
@@ -3,7 +3,6 @@
#include
-#include
#include
#include
diff --git a/src/arkreactor/Builtins/String.cpp b/src/arkreactor/Builtins/String.cpp
index 40450d17c..809504212 100644
--- a/src/arkreactor/Builtins/String.cpp
+++ b/src/arkreactor/Builtins/String.cpp
@@ -1,8 +1,8 @@
#include
-#include
#include
#include
+#include
#include
#include
@@ -12,15 +12,15 @@ namespace Ark::internal::Builtins::String
/**
* @name str:format
* @brief Format a String given replacements
- * @details The format is %% for anything, and %x for hex numbers
+ * @details https://fmt.dev/latest/syntax.html
* @param format the String to format
* @param values as any argument as you need, of any valid ArkScript type
* =begin
- * (str:format "Hello %%, my name is %%" "world" "ArkScript")
+ * (str:format "Hello {}, my name is {}" "world" "ArkScript")
* # Hello world, my name is ArkScript
*
- * (str:format "Test %% with %%" "1")
- * # Test 1 with %%
+ * (str:format "Test {} with {}" "1")
+ * # Test 1 with {}
* =end
* @author https://github.com/SuperFola
*/
@@ -33,35 +33,37 @@ namespace Ark::internal::Builtins::String
types::Typedef("value", ValueType::Any, /* variadic */ true) } } } },
n);
- ::String f(n[0].string().c_str());
+ std::string all_str = n[0].stringRef();
+ std::string f;
+ std::size_t previous = 0;
for (Value::Iterator it = n.begin() + 1, it_end = n.end(); it != it_end; ++it)
{
+ std::size_t len = all_str.find_first_of('}', previous);
+ std::string current = all_str.substr(previous, len + 1);
+ previous += len + 1;
+
if (it->valueType() == ValueType::String)
- {
- ::String& obj = it->stringRef();
- f.format(f.size() + obj.size(), obj.c_str());
- }
+ current = fmt::format(current, it->stringRef());
else if (it->valueType() == ValueType::Number)
- {
- double obj = it->number();
- f.format(f.size() + Utils::digPlaces(obj) + Utils::decPlaces(obj) + 1, obj);
- }
+ current = fmt::format(current, it->number());
else if (it->valueType() == ValueType::Nil)
- f.format(f.size() + 5, std::string_view("nil"));
+ current = fmt::format(current, "nil");
else if (it->valueType() == ValueType::True)
- f.format(f.size() + 5, std::string_view("true"));
+ current = fmt::format(current, "true");
else if (it->valueType() == ValueType::False)
- f.format(f.size() + 5, std::string_view("false"));
+ current = fmt::format(current, "false");
else
{
std::stringstream ss;
it->toString(ss, *vm);
- f.format(f.size() + ss.str().size(), std::string_view(ss.str().c_str()));
+ current = fmt::format(current, ss.str());
}
+
+ f += current;
}
- n[0].stringRef() = f;
- return n[0];
+
+ return Value(f);
}
/**
@@ -84,7 +86,10 @@ namespace Ark::internal::Builtins::String
{ { types::Contract { { types::Typedef("string", ValueType::String), types::Typedef("substr", ValueType::String) } } } },
n);
- return Value(n[0].stringRef().find(n[1].stringRef()));
+ std::size_t index = n[0].stringRef().find(n[1].stringRef());
+ if (index != std::string::npos)
+ return Value(static_cast(index));
+ return Value(-1);
}
/**
@@ -111,7 +116,7 @@ namespace Ark::internal::Builtins::String
if (id < 0 || static_cast(id) >= n[0].stringRef().size())
throw std::runtime_error("str:removeAt: index out of range");
- n[0].stringRef().erase(id, id + 1);
+ n[0].stringRef().erase(id, 1);
return n[0];
}
diff --git a/src/arkreactor/Builtins/Time.cpp b/src/arkreactor/Builtins/Time.cpp
index d3ea3e6d7..b5d907e0a 100644
--- a/src/arkreactor/Builtins/Time.cpp
+++ b/src/arkreactor/Builtins/Time.cpp
@@ -3,7 +3,6 @@
#undef abs
#include
-#include
#include
namespace Ark::internal::Builtins::Time
diff --git a/src/arkreactor/Compiler/AST/Node.cpp b/src/arkreactor/Compiler/AST/Node.cpp
index 00c5f4ee8..438b9cc06 100644
--- a/src/arkreactor/Compiler/AST/Node.cpp
+++ b/src/arkreactor/Compiler/AST/Node.cpp
@@ -226,10 +226,6 @@ namespace Ark::internal
break;
}
- case NodeType::Closure:
- os << "Closure";
- break;
-
case NodeType::Keyword:
switch (N.keyword())
{
@@ -287,8 +283,7 @@ namespace Ark::internal
if (A.m_type != B.m_type) // should have the same types
return false;
- if (A.m_type != NodeType::List &&
- A.m_type != NodeType::Closure)
+ if (A.m_type != NodeType::List)
return A.m_value == B.m_value;
if (A.m_type == NodeType::List)
diff --git a/src/arkreactor/Compiler/BytecodeReader.cpp b/src/arkreactor/Compiler/BytecodeReader.cpp
index 125c10fb2..9e0fdae95 100644
--- a/src/arkreactor/Compiler/BytecodeReader.cpp
+++ b/src/arkreactor/Compiler/BytecodeReader.cpp
@@ -5,6 +5,7 @@
#undef abs
#include
+#include
#include
#include
@@ -109,10 +110,11 @@ namespace Ark
os << "SHA256: ";
for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
{
- os << std::hex << static_cast(m_bytecode[i]) << std::dec;
+ os << std::hex << static_cast(m_bytecode[i]);
++i;
}
- os << "\n\n";
+ os << "\n\n"
+ << std::dec;
std::vector symbols;
std::vector values;
@@ -259,16 +261,13 @@ namespace Ark
return;
uint16_t pp = 0;
+ std::size_t cumulated_segment_size = i + 3;
while (b[i] == Instruction::CODE_SEGMENT_START && (segment == BytecodeSegment::All || segment == BytecodeSegment::Code || segment == BytecodeSegment::HeadersOnly))
{
i++;
uint16_t size = readNumber(i);
i++;
- uint16_t sliceSize = size;
-
- if (sStart.has_value() && sEnd.has_value())
- sliceSize = sEnd.value() - sStart.value() + 1;
bool displayCode = true;
@@ -276,7 +275,7 @@ namespace Ark
displayCode = pp == page.value();
if (displayCode)
- os << termcolor::magenta << "Code segment " << pp << termcolor::reset << " (length: " << sliceSize << ")\n";
+ os << termcolor::magenta << "Code segment " << pp << termcolor::reset << " (length: " << size << ")\n";
if (size == 0)
{
@@ -285,339 +284,154 @@ namespace Ark
}
else
{
- uint16_t j = i;
+ i += 4 * sStart.value_or(0);
- bool displayLine = segment == BytecodeSegment::HeadersOnly ? false : displayCode;
- while (true)
+ if (cPage.value_or(pp) == pp && segment != BytecodeSegment::HeadersOnly)
{
- uint16_t line_number = i - j;
if (sStart.has_value() && sEnd.has_value() && ((sStart.value() > size) || (sEnd.value() > size)))
{
os << termcolor::red << "Slice start or end can't be greater than the segment size: " << size << termcolor::reset << "\n";
return;
}
- else if (sStart.has_value() && sEnd.has_value() && cPage.has_value())
- displayLine = displayCode && (line_number >= sStart.value() && line_number <= sEnd.value());
-
- if (displayLine)
- os << termcolor::cyan << line_number << termcolor::reset << " " << termcolor::yellow;
- uint8_t inst = b[i];
- i++;
- if (inst == Instruction::NOP)
+ for (std::size_t j = sStart.value_or(0), end = sEnd.value_or(size); j < end; ++j)
{
- if (displayLine)
+ [[maybe_unused]] uint8_t padding = b[i];
+ ++i;
+ uint8_t inst = b[i];
+ ++i;
+ uint16_t arg = readNumber(i);
+ ++i;
+
+ // instruction number
+ os << termcolor::cyan << j << " ";
+ // padding inst arg arg
+ os << termcolor::reset << std::hex
+ << std::setw(2) << std::setfill('0') << static_cast(padding) << " "
+ << std::setw(2) << std::setfill('0') << static_cast(inst) << " "
+ << std::setw(2) << std::setfill('0') << static_cast(b[i - 2]) << " "
+ << std::setw(2) << std::setfill('0') << static_cast(b[i - 1]) << " ";
+ // reset stream
+ os << std::dec << termcolor::yellow;
+
+ if (inst == Instruction::NOP)
os << "NOP\n";
- }
- else if (inst == Instruction::LOAD_SYMBOL)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "LOAD_SYMBOL " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::LOAD_CONST)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "LOAD_CONST " << termcolor::magenta << values[index] << "\n";
- i++;
- }
- else if (inst == Instruction::POP_JUMP_IF_TRUE)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "POP_JUMP_IF_TRUE " << termcolor::red << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::STORE)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "STORE " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::LET)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "LET " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::POP_JUMP_IF_FALSE)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "POP_JUMP_IF_FALSE " << termcolor::red << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::JUMP)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "JUMP " << termcolor::red << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::RET)
- {
- if (displayLine)
+ else if (inst == Instruction::LOAD_SYMBOL)
+ os << "LOAD_SYMBOL " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::LOAD_CONST)
+ os << "LOAD_CONST " << termcolor::magenta << values[arg] << "\n";
+ else if (inst == Instruction::POP_JUMP_IF_TRUE)
+ os << "POP_JUMP_IF_TRUE " << termcolor::red << "(" << arg << ")\n";
+ else if (inst == Instruction::STORE)
+ os << "STORE " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::LET)
+ os << "LET " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::POP_JUMP_IF_FALSE)
+ os << "POP_JUMP_IF_FALSE " << termcolor::red << "(" << arg << ")\n";
+ else if (inst == Instruction::JUMP)
+ os << "JUMP " << termcolor::red << "(" << arg << ")\n";
+ else if (inst == Instruction::RET)
os << "RET\n";
- }
- else if (inst == Instruction::HALT)
- {
- if (displayLine)
+ else if (inst == Instruction::HALT)
os << "HALT\n";
- }
- else if (inst == Instruction::CALL)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "CALL " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::CAPTURE)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "CAPTURE " << termcolor::reset << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::BUILTIN)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "BUILTIN " << termcolor::reset << Builtins::builtins[index].first << "\n";
- i++;
- }
- else if (inst == Instruction::MUT)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "MUT " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::DEL)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "DEL " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::SAVE_ENV)
- {
- if (displayLine)
+ else if (inst == Instruction::CALL)
+ os << "CALL " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::CAPTURE)
+ os << "CAPTURE " << termcolor::reset << symbols[arg] << "\n";
+ else if (inst == Instruction::BUILTIN)
+ os << "BUILTIN " << termcolor::reset << Builtins::builtins[arg].first << "\n";
+ else if (inst == Instruction::MUT)
+ os << "MUT " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::DEL)
+ os << "DEL " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::SAVE_ENV)
os << "SAVE_ENV\n";
- }
- else if (inst == Instruction::GET_FIELD)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "GET_FIELD " << termcolor::green << symbols[index] << "\n";
- i++;
- }
- else if (inst == Instruction::PLUGIN)
- {
- uint16_t index = readNumber(i);
- if (displayLine)
- os << "PLUGIN " << termcolor::magenta << values[index] << "\n";
- i++;
- }
- else if (inst == Instruction::LIST)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "LIST " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::APPEND)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "APPEND " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::CONCAT)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "CONCAT " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::APPEND_IN_PLACE)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "APPEND_IN_PLACE " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::CONCAT_IN_PLACE)
- {
- uint16_t value = readNumber(i);
- if (displayLine)
- os << "CONCAT_IN_PLACE " << termcolor::reset << "(" << value << ")\n";
- i++;
- }
- else if (inst == Instruction::POP_LIST)
- {
- if (displayLine)
+ else if (inst == Instruction::GET_FIELD)
+ os << "GET_FIELD " << termcolor::green << symbols[arg] << "\n";
+ else if (inst == Instruction::PLUGIN)
+ os << "PLUGIN " << termcolor::magenta << values[arg] << "\n";
+ else if (inst == Instruction::LIST)
+ os << "LIST " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::APPEND)
+ os << "APPEND " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::CONCAT)
+ os << "CONCAT " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::APPEND_IN_PLACE)
+ os << "APPEND_IN_PLACE " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::CONCAT_IN_PLACE)
+ os << "CONCAT_IN_PLACE " << termcolor::reset << "(" << arg << ")\n";
+ else if (inst == Instruction::POP_LIST)
os << "POP_LIST " << termcolor::reset << "\n";
- i++;
- }
- else if (inst == Instruction::POP_LIST_IN_PLACE)
- {
- if (displayLine)
+ else if (inst == Instruction::POP_LIST_IN_PLACE)
os << "POP_LIST_IN_PLACE " << termcolor::reset << "\n";
- i++;
- }
- else if (inst == Instruction::POP)
- {
- if (displayLine)
+ else if (inst == Instruction::POP)
os << "POP\n";
- }
- else if (inst == Instruction::ADD)
- {
- if (displayLine)
+ else if (inst == Instruction::ADD)
os << "ADD\n";
- }
- else if (inst == Instruction::SUB)
- {
- if (displayLine)
+ else if (inst == Instruction::SUB)
os << "SUB\n";
- }
- else if (inst == Instruction::MUL)
- {
- if (displayLine)
+ else if (inst == Instruction::MUL)
os << "MUL\n";
- }
- else if (inst == Instruction::DIV)
- {
- if (displayLine)
+ else if (inst == Instruction::DIV)
os << "DIV\n";
- }
- else if (inst == Instruction::GT)
- {
- if (displayLine)
+ else if (inst == Instruction::GT)
os << "GT\n";
- }
- else if (inst == Instruction::LT)
- {
- if (displayLine)
+ else if (inst == Instruction::LT)
os << "LT\n";
- }
- else if (inst == Instruction::LE)
- {
- if (displayLine)
+ else if (inst == Instruction::LE)
os << "LE\n";
- }
- else if (inst == Instruction::GE)
- {
- if (displayLine)
+ else if (inst == Instruction::GE)
os << "GE\n";
- }
- else if (inst == Instruction::NEQ)
- {
- if (displayLine)
+ else if (inst == Instruction::NEQ)
os << "NEQ\n";
- }
- else if (inst == Instruction::EQ)
- {
- if (displayLine)
+ else if (inst == Instruction::EQ)
os << "EQ\n";
- }
- else if (inst == Instruction::LEN)
- {
- if (displayLine)
+ else if (inst == Instruction::LEN)
os << "LEN\n";
- }
- else if (inst == Instruction::EMPTY)
- {
- if (displayLine)
+ else if (inst == Instruction::EMPTY)
os << "EMPTY\n";
- }
- else if (inst == Instruction::TAIL)
- {
- if (displayLine)
+ else if (inst == Instruction::TAIL)
os << "TAIL\n";
- }
- else if (inst == Instruction::HEAD)
- {
- if (displayLine)
+ else if (inst == Instruction::HEAD)
os << "HEAD\n";
- }
- else if (inst == Instruction::ISNIL)
- {
- if (displayLine)
+ else if (inst == Instruction::ISNIL)
os << "ISNIL\n";
- }
- else if (inst == Instruction::ASSERT)
- {
- if (displayLine)
+ else if (inst == Instruction::ASSERT)
os << "ASSERT\n";
- }
- else if (inst == Instruction::TO_NUM)
- {
- if (displayLine)
+ else if (inst == Instruction::TO_NUM)
os << "TO_NUM\n";
- }
- else if (inst == Instruction::TO_STR)
- {
- if (displayLine)
+ else if (inst == Instruction::TO_STR)
os << "TO_STR\n";
- }
- else if (inst == Instruction::AT)
- {
- if (displayLine)
+ else if (inst == Instruction::AT)
os << "AT\n";
- }
- else if (inst == Instruction::AND_)
- {
- if (displayLine)
+ else if (inst == Instruction::AND_)
os << "AND_\n";
- }
- else if (inst == Instruction::OR_)
- {
- if (displayLine)
+ else if (inst == Instruction::OR_)
os << "OR_\n";
- }
- else if (inst == Instruction::MOD)
- {
- if (displayLine)
+ else if (inst == Instruction::MOD)
os << "MOD\n";
- }
- else if (inst == Instruction::TYPE)
- {
- if (displayLine)
+ else if (inst == Instruction::TYPE)
os << "TYPE\n";
- }
- else if (inst == Instruction::HASFIELD)
- {
- if (displayLine)
+ else if (inst == Instruction::HASFIELD)
os << "HASFIELD\n";
- }
- else if (inst == Instruction::NOT)
- {
- if (displayLine)
+ else if (inst == Instruction::NOT)
os << "NOT\n";
- }
- else
- {
- if (displayLine)
+ else
+ {
os << termcolor::reset << "Unknown instruction: " << static_cast(inst) << '\n'
<< termcolor::reset;
- return;
+ return;
+ }
}
-
- if (i - j == size)
- break;
}
+
+ i = cumulated_segment_size + size * 4;
+ cumulated_segment_size += size * 4 + 3;
}
if (displayCode && segment != BytecodeSegment::HeadersOnly)
os << "\n"
<< termcolor::reset;
- if (cPage.has_value() && pp == cPage)
- return;
-
++pp;
if (i == b.size())
diff --git a/src/arkreactor/Compiler/Compiler.cpp b/src/arkreactor/Compiler/Compiler.cpp
index d920ff199..53b1e331f 100644
--- a/src/arkreactor/Compiler/Compiler.cpp
+++ b/src/arkreactor/Compiler/Compiler.cpp
@@ -39,32 +39,49 @@ namespace Ark
m_code_pages.emplace_back(); // create empty page
// gather symbols, values, and start to create code segments
- _compile(m_optimizer.ast(), /* current_page */ 0, /* produces_result */ false, /* is_terminal */ false);
+ compileExpression(m_optimizer.ast(), /* current_page */ 0, /* is_result_unused */ false, /* is_terminal */ false);
// throw an error on undefined symbol uses
checkForUndefinedSymbol();
pushSymAndValTables();
// push the different code segments
- for (const bytecode_t& page : m_code_pages)
+ for (std::size_t i = 0, end = m_code_pages.size(); i < end; ++i)
{
- m_bytecode.push_back(Instruction::CODE_SEGMENT_START);
+ std::vector& page = m_code_pages[i];
+ // just in case we got too far, always add a HALT to be sure the
+ // VM won't do anything crazy
+ page.push_back(Instruction::HALT);
// push number of elements
- pushNumber(static_cast(page.size() + 1));
+ std::size_t page_size = page.size();
+ if (page_size > std::numeric_limits::max())
+ throw std::overflow_error("Size of page " + std::to_string(i) + " exceeds the maximum size of 2^16 - 1");
+
+ m_bytecode.push_back(Instruction::CODE_SEGMENT_START);
+ m_bytecode.push_back(static_cast((page_size & 0xff00) >> 8));
+ m_bytecode.push_back(static_cast(page_size & 0x00ff));
for (auto inst : page)
- m_bytecode.push_back(inst);
- // just in case we got too far, always add a HALT to be sure the
- // VM won't do anything crazy
- m_bytecode.push_back(Instruction::HALT);
+ {
+ m_bytecode.push_back(inst.padding);
+ m_bytecode.push_back(inst.opcode);
+ m_bytecode.push_back(inst.bytes.first);
+ m_bytecode.push_back(inst.bytes.second);
+ }
}
if (!m_code_pages.size())
{
+ // code segment with a single instruction
m_bytecode.push_back(Instruction::CODE_SEGMENT_START);
- pushNumber(1_u16);
+ m_bytecode.push_back(0_u8);
+ m_bytecode.push_back(1_u8);
+
+ m_bytecode.push_back(0_u8);
m_bytecode.push_back(Instruction::HALT);
+ m_bytecode.push_back(0_u8);
+ m_bytecode.push_back(0_u8);
}
constexpr std::size_t header_size = 18;
@@ -106,17 +123,19 @@ namespace Ark
m_bytecode.push_back(0_u8);
// push version
- pushNumber(ARK_VERSION_MAJOR);
- pushNumber(ARK_VERSION_MINOR);
- pushNumber(ARK_VERSION_PATCH);
+ for (int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
+ {
+ m_bytecode.push_back(static_cast((n & 0xff00) >> 8));
+ m_bytecode.push_back(static_cast(n & 0x00ff));
+ }
// push timestamp
unsigned long long timestamp = std::chrono::duration_cast(
std::chrono::system_clock::now().time_since_epoch())
.count();
- for (char c = 0; c < 8; c++)
+ for (std::size_t i = 0; i < 8; ++i)
{
- unsigned shift = 8 * (7 - c);
+ unsigned shift = 8 * (7 - i);
uint8_t ts_byte = (timestamp & (0xffULL << shift)) >> shift;
m_bytecode.push_back(ts_byte);
}
@@ -124,17 +143,14 @@ namespace Ark
void Compiler::pushSymAndValTables()
{
- /*
- - symbols table
- + elements
- - values table header
- + elements
- */
+ std::size_t symbol_size = m_symbols.size();
+ if (symbol_size > std::numeric_limits::max())
+ throw std::overflow_error("Too many symbols: " + std::to_string(symbol_size) + ", exceeds the maximum size of 2^16 - 1");
m_bytecode.push_back(Instruction::SYM_TABLE_START);
- // push size
- pushNumber(static_cast(m_symbols.size()));
- // push elements
+ m_bytecode.push_back(static_cast((symbol_size & 0xff00) >> 8));
+ m_bytecode.push_back(static_cast(symbol_size & 0x00ff));
+
for (auto sym : m_symbols)
{
// push the string, null terminated
@@ -144,12 +160,15 @@ namespace Ark
m_bytecode.push_back(0_u8);
}
- // values table
+ std::size_t value_size = m_values.size();
+ if (value_size > std::numeric_limits::max())
+ throw std::overflow_error("Too many values: " + std::to_string(value_size) + ", exceeds the maximum size of 2^16 - 1");
+
m_bytecode.push_back(Instruction::VAL_TABLE_START);
- // push size
- pushNumber(static_cast(m_values.size()));
- // push elements (separated with 0x00)
- for (auto val : m_values)
+ m_bytecode.push_back(static_cast((value_size & 0xff00) >> 8));
+ m_bytecode.push_back(static_cast(value_size & 0x00ff));
+
+ for (const ValTableElem& val : m_values)
{
if (val.type == ValTableElemType::Number)
{
@@ -169,7 +188,9 @@ namespace Ark
else if (val.type == ValTableElemType::PageAddr)
{
m_bytecode.push_back(Instruction::FUNC_TYPE);
- pushNumber(static_cast(std::get(val.value)));
+ std::size_t addr = std::get(val.value);
+ m_bytecode.push_back(static_cast((addr & 0xff00) >> 8));
+ m_bytecode.push_back(static_cast(addr & 0x00ff));
}
else
throw CompilationError("trying to put a value in the value table, but the type isn't handled.\nCertainly a logic problem in the compiler source code");
@@ -189,7 +210,7 @@ namespace Ark
return n;
}
- std::optional Compiler::isOperator(const std::string& name) noexcept
+ std::optional Compiler::getOperator(const std::string& name) noexcept
{
auto it = std::find(internal::operators.begin(), internal::operators.end(), name);
if (it != internal::operators.end())
@@ -197,7 +218,7 @@ namespace Ark
return std::nullopt;
}
- std::optional Compiler::isBuiltin(const std::string& name) noexcept
+ std::optional Compiler::getBuiltin(const std::string& name) noexcept
{
auto it = std::find_if(Builtins::builtins.begin(), Builtins::builtins.end(),
[&name](const std::pair& element) -> bool {
@@ -229,13 +250,22 @@ namespace Ark
}
}
- void Compiler::pushSpecificInstArgc(Instruction inst, uint16_t previous, int p) noexcept
+ uint16_t Compiler::computeSpecificInstArgc(Instruction inst, uint16_t previous) noexcept
{
- if (inst == Instruction::LIST)
- pushNumber(previous, page_ptr(p));
- else if (inst == Instruction::APPEND || inst == Instruction::APPEND_IN_PLACE ||
- inst == Instruction::CONCAT || inst == Instruction::CONCAT_IN_PLACE)
- pushNumber(previous - 1, page_ptr(p));
+ switch (inst)
+ {
+ case Instruction::LIST:
+ return previous;
+
+ case Instruction::APPEND:
+ case Instruction::APPEND_IN_PLACE:
+ case Instruction::CONCAT:
+ case Instruction::CONCAT_IN_PLACE:
+ return previous - 1;
+
+ default:
+ return 0;
+ }
}
bool Compiler::mayBeFromPlugin(const std::string& name) noexcept
@@ -248,55 +278,47 @@ namespace Ark
return it != m_plugins.end();
}
- void Compiler::throwCompilerError(const std::string& message, const Node& node)
+ void Compiler::compilerWarning(const std::string& message, const Node& node)
{
- throw CompilationError(makeNodeBasedErrorCtx(message, node));
+ if (m_options & FeatureShowWarnings)
+ std::cout << termcolor::yellow << "Warning " << termcolor::reset << makeNodeBasedErrorCtx(message, node) << "\n";
}
- void Compiler::compilerWarning(const std::string& message, const Node& node)
+ void Compiler::throwCompilerError(const std::string& message, const Node& node)
{
- if (m_options & FeatureShowWarnings)
- std::cerr << termcolor::yellow << "Warning " << termcolor::reset << makeNodeBasedErrorCtx(message, node) << "\n";
+ throw CompilationError(makeNodeBasedErrorCtx(message, node));
}
- void Compiler::_compile(const Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name)
+ void Compiler::compileExpression(const Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name)
{
// register symbols
if (x.nodeType() == NodeType::Symbol)
- compileSymbol(x, p, produces_result);
+ compileSymbol(x, p, is_result_unused);
else if (x.nodeType() == NodeType::GetField)
{
- std::string name = x.string();
- // 'name' shouldn't be a builtin/operator, we can use it as-is
uint16_t i = addSymbol(x);
-
- page(p).emplace_back(Instruction::GET_FIELD);
- pushNumber(i, page_ptr(p));
+ page(p).emplace_back(Instruction::GET_FIELD, i);
}
// register values
else if (x.nodeType() == NodeType::String || x.nodeType() == NodeType::Number)
{
uint16_t i = addValue(x);
- if (!produces_result)
- {
- page(p).emplace_back(Instruction::LOAD_CONST);
- pushNumber(i, page_ptr(p));
- }
+ if (!is_result_unused)
+ page(p).emplace_back(Instruction::LOAD_CONST, i);
}
// empty code block should be nil
else if (x.constList().empty())
{
- if (!produces_result)
+ if (!is_result_unused)
{
- auto it_builtin = isBuiltin("nil");
- page(p).emplace_back(Instruction::BUILTIN);
- pushNumber(static_cast(it_builtin.value()), page_ptr(p));
+ static const std::optional nil = getBuiltin("nil");
+ page(p).emplace_back(Instruction::BUILTIN, static_cast(nil.value()));
}
}
// specific instructions
- else if (auto c0 = x.constList()[0]; c0.nodeType() == NodeType::Symbol && isSpecific(c0.string()).has_value())
- compileSpecific(c0, x, p, produces_result);
+ else if (auto c0 = x.constList()[0]; c0.nodeType() == NodeType::Symbol && getSpecific(c0.string()).has_value())
+ compileSpecific(c0, x, p, is_result_unused);
// registering structures
else if (x.constList()[0].nodeType() == NodeType::Keyword)
{
@@ -305,7 +327,7 @@ namespace Ark
switch (n)
{
case Keyword::If:
- compileIf(x, p, produces_result, is_terminal, var_name);
+ compileIf(x, p, is_result_unused, is_terminal, var_name);
break;
case Keyword::Set:
@@ -317,17 +339,17 @@ namespace Ark
break;
case Keyword::Fun:
- compileFunction(x, p, produces_result, var_name);
+ compileFunction(x, p, is_result_unused, var_name);
break;
case Keyword::Begin:
{
for (std::size_t i = 1, size = x.constList().size(); i < size; ++i)
- _compile(
+ compileExpression(
x.constList()[i],
p,
// All the nodes in a begin (except for the last one) are producing a result that we want to drop.
- (i != size - 1) ? true : produces_result,
+ (i != size - 1) ? true : is_result_unused,
// If the begin is a terminal node, only its last node is terminal.
is_terminal ? (i == size - 1) : false,
var_name);
@@ -343,11 +365,11 @@ namespace Ark
break;
case Keyword::Quote:
- compileQuote(x, p, produces_result, is_terminal, var_name);
+ compileQuote(x, p, is_result_unused, is_terminal, var_name);
break;
case Keyword::Del:
- compileDel(x, p);
+ page(p).emplace_back(Instruction::DEL, addSymbol(x.constList()[1]));
break;
}
}
@@ -355,40 +377,32 @@ namespace Ark
{
// if we are here, we should have a function name
// push arguments first, then function name, then call it
- handleCalls(x, p, produces_result, is_terminal, var_name);
+ handleCalls(x, p, is_result_unused, is_terminal, var_name);
}
}
- void Compiler::compileSymbol(const Node& x, int p, bool produces_result)
+ void Compiler::compileSymbol(const Node& x, int p, bool is_result_unused)
{
std::string name = x.string();
- if (auto it_builtin = isBuiltin(name))
- {
- page(p).emplace_back(Instruction::BUILTIN);
- pushNumber(static_cast(it_builtin.value()), page_ptr(p));
- }
- else if (auto it_operator = isOperator(name))
+ if (auto it_builtin = getBuiltin(name))
+ page(p).emplace_back(Instruction::BUILTIN, static_cast(it_builtin.value()));
+ else if (auto it_operator = getOperator(name))
page(p).emplace_back(static_cast(Instruction::FIRST_OPERATOR + it_operator.value()));
- else // var-use
- {
- uint16_t i = addSymbol(x);
-
- page(p).emplace_back(Instruction::LOAD_SYMBOL);
- pushNumber(i, page_ptr(p));
- }
+ else
+ page(p).emplace_back(Instruction::LOAD_SYMBOL, addSymbol(x)); // using the variable
- if (produces_result)
+ if (is_result_unused)
{
compilerWarning("Statement has no effect", x);
page(p).push_back(Instruction::POP);
}
}
- void Compiler::compileSpecific(const Node& c0, const Node& x, int p, bool produces_result)
+ void Compiler::compileSpecific(const Node& c0, const Node& x, int p, bool is_result_unused)
{
std::string name = c0.string();
- Instruction inst = isSpecific(name).value();
+ Instruction inst = getSpecific(name).value();
// length of at least 1 since we got a symbol name
uint16_t argc = countArkObjects(x.constList()) - 1;
@@ -405,53 +419,49 @@ namespace Ark
uint16_t diff = i - j;
while (j < i)
{
- _compile(x.constList()[j], p, false, false);
+ compileExpression(x.constList()[j], p, false, false);
++j;
}
- _compile(x.constList()[i], p, false, false);
+ compileExpression(x.constList()[i], p, false, false);
i -= diff;
}
// put inst and number of arguments
- page(p).emplace_back(inst);
- pushSpecificInstArgc(inst, argc, p);
+ page(p).emplace_back(inst, computeSpecificInstArgc(inst, argc));
- if (produces_result && name.back() != '!') // in-place functions never push a value
+ if (is_result_unused && name.back() != '!') // in-place functions never push a value
{
compilerWarning("Ignoring return value of function", x);
page(p).push_back(Instruction::POP);
}
}
- void Compiler::compileIf(const Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name)
+ void Compiler::compileIf(const Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name)
{
// compile condition
- _compile(x.constList()[1], p, false, false);
+ compileExpression(x.constList()[1], p, false, false);
// jump only if needed to the if
- page(p).push_back(Instruction::POP_JUMP_IF_TRUE);
std::size_t jump_to_if_pos = page(p).size();
- // absolute address to jump to if condition is true
- pushNumber(0_u16, page_ptr(p));
+ page(p).push_back(Instruction::POP_JUMP_IF_TRUE);
// else code
if (x.constList().size() == 4) // we have an else clause
- _compile(x.constList()[3], p, produces_result, is_terminal, var_name);
+ compileExpression(x.constList()[3], p, is_result_unused, is_terminal, var_name);
// when else is finished, jump to end
- page(p).push_back(Instruction::JUMP);
std::size_t jump_to_end_pos = page(p).size();
- pushNumber(0_u16, page_ptr(p));
+ page(p).push_back(Instruction::JUMP);
- // set jump to if pos
- setNumberAt(p, jump_to_if_pos, page(p).size());
+ // absolute address to jump to if condition is true
+ page(p)[jump_to_if_pos].data = static_cast(page(p).size());
// if code
- _compile(x.constList()[2], p, produces_result, is_terminal, var_name);
+ compileExpression(x.constList()[2], p, is_result_unused, is_terminal, var_name);
// set jump to end pos
- setNumberAt(p, jump_to_end_pos, page(p).size());
+ page(p)[jump_to_end_pos].data = static_cast(page(p).size());
}
- void Compiler::compileFunction(const Node& x, int p, bool produces_result, const std::string& var_name)
+ void Compiler::compileFunction(const Node& x, int p, bool is_result_unused, const std::string& var_name)
{
// capture, if needed
for (auto it = x.constList()[1].constList().begin(), it_end = x.constList()[1].constList().end(); it != it_end; ++it)
@@ -464,40 +474,36 @@ namespace Ark
// we didn't find it in the defined symbol list, thus we can't capture it
throwCompilerError("Can not capture " + it->string() + " because it is referencing an unbound variable.", *it);
}
- page(p).emplace_back(Instruction::CAPTURE);
+
addDefinedSymbol(it->string());
- uint16_t var_id = addSymbol(*it);
- pushNumber(var_id, page_ptr(p));
+ page(p).emplace_back(Instruction::CAPTURE, addSymbol(*it));
}
}
// create new page for function body
m_code_pages.emplace_back();
std::size_t page_id = m_code_pages.size() - 1;
- // load value on the stack
- page(p).emplace_back(Instruction::LOAD_CONST);
- // save page_id into the constants table as PageAddr
- pushNumber(addValue(page_id, x), page_ptr(p));
+ // save page_id into the constants table as PageAddr and load the const
+ page(p).emplace_back(Instruction::LOAD_CONST, addValue(page_id, x));
// pushing arguments from the stack into variables in the new scope
for (auto it = x.constList()[1].constList().begin(), it_end = x.constList()[1].constList().end(); it != it_end; ++it)
{
if (it->nodeType() == NodeType::Symbol)
{
- page(page_id).emplace_back(Instruction::MUT);
- uint16_t var_id = addSymbol(*it);
addDefinedSymbol(it->string());
- pushNumber(var_id, page_ptr(page_id));
+ page(page_id).emplace_back(Instruction::MUT, addSymbol(*it));
}
}
// push body of the function
- _compile(x.constList()[2], page_id, false, true, var_name);
+ compileExpression(x.constList()[2], page_id, false, true, var_name);
// return last value on the stack
page(page_id).emplace_back(Instruction::RET);
- if (produces_result)
+ // if the computed function is unused, pop it
+ if (is_result_unused)
{
compilerWarning("Unused declared function", x);
page(p).push_back(Instruction::POP);
@@ -506,20 +512,22 @@ namespace Ark
void Compiler::compileLetMutSet(Keyword n, const Node& x, int p)
{
+ std::string name = x.constList()[1].string();
uint16_t i = addSymbol(x.constList()[1]);
if (n != Keyword::Set)
- addDefinedSymbol(x.constList()[1].string());
+ addDefinedSymbol(name);
// put value before symbol id
- putValue(x, p, false);
+ // starting at index = 2 because x is a (let|mut|set variable ...) node
+ for (std::size_t idx = 2, end = x.constList().size(); idx < end; ++idx)
+ compileExpression(x.constList()[idx], p, false, false, name);
if (n == Keyword::Let)
- page(p).push_back(Instruction::LET);
+ page(p).emplace_back(Instruction::LET, i);
else if (n == Keyword::Mut)
- page(p).push_back(Instruction::MUT);
+ page(p).emplace_back(Instruction::MUT, i);
else
- page(p).push_back(Instruction::STORE);
- pushNumber(i, page_ptr(p));
+ page(p).emplace_back(Instruction::STORE, i);
}
void Compiler::compileWhile(const Node& x, int p)
@@ -527,36 +535,33 @@ namespace Ark
// save current position to jump there at the end of the loop
std::size_t current = page(p).size();
// push condition
- _compile(x.constList()[1], p, false, false);
+ compileExpression(x.constList()[1], p, false, false);
// absolute jump to end of block if condition is false
- page(p).push_back(Instruction::POP_JUMP_IF_FALSE);
std::size_t jump_to_end_pos = page(p).size();
- // absolute address to jump to if condition is false
- pushNumber(0_u16, page_ptr(p));
+ page(p).push_back(Instruction::POP_JUMP_IF_FALSE);
// push code to page
- _compile(x.constList()[2], p, true, false);
+ compileExpression(x.constList()[2], p, true, false);
+
// loop, jump to the condition
- page(p).push_back(Instruction::JUMP);
- // abosolute address
- pushNumber(static_cast(current), page_ptr(p));
- // set jump to end pos
- setNumberAt(p, jump_to_end_pos, page(p).size());
+ page(p).emplace_back(Instruction::JUMP, current);
+
+ // absolute address to jump to if condition is false
+ page(p)[jump_to_end_pos].data = static_cast(page(p).size());
}
- void Compiler::compileQuote(const Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name)
+ void Compiler::compileQuote(const Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name)
{
// create new page for quoted code
m_code_pages.emplace_back();
std::size_t page_id = m_code_pages.size() - 1;
- _compile(x.constList()[1], page_id, false, is_terminal, var_name);
+ compileExpression(x.constList()[1], page_id, false, is_terminal, var_name);
page(page_id).emplace_back(Instruction::RET); // return to the last frame
// call it
uint16_t id = addValue(page_id, x); // save page_id into the constants table as PageAddr
- page(p).emplace_back(Instruction::LOAD_CONST);
- pushNumber(id, page_ptr(p));
+ page(p).emplace_back(Instruction::LOAD_CONST, id);
- if (produces_result)
+ if (is_result_unused)
{
compilerWarning("Unused quote expression", x);
page(p).push_back(Instruction::POP);
@@ -570,24 +575,14 @@ namespace Ark
// save plugin name to use it later
m_plugins.push_back(x.constList()[1].string());
// add plugin instruction + id of the constant refering to the plugin path
- page(p).emplace_back(Instruction::PLUGIN);
- pushNumber(id, page_ptr(p));
+ page(p).emplace_back(Instruction::PLUGIN, id);
}
- void Compiler::compileDel(const Node& x, int p)
- {
- // get id of symbol to delete
- uint16_t i = addSymbol(x.constList()[1]);
-
- page(p).emplace_back(Instruction::DEL);
- pushNumber(i, page_ptr(p));
- }
-
- void Compiler::handleCalls(const Node& x, int p, bool produces_result, bool is_terminal, const std::string& var_name)
+ void Compiler::handleCalls(const Node& x, int p, bool is_result_unused, bool is_terminal, const std::string& var_name)
{
m_temp_pages.emplace_back();
int proc_page = -static_cast(m_temp_pages.size());
- _compile(x.constList()[0], proc_page, false, false); // storing proc
+ compileExpression(x.constList()[0], proc_page, false, false); // storing proc
// trying to handle chained closure.field.field.field...
std::size_t n = 1; // we need it later
@@ -596,17 +591,15 @@ namespace Ark
{
if (x.constList()[n].nodeType() == NodeType::GetField)
{
- _compile(x.constList()[n], proc_page, false, false);
+ compileExpression(x.constList()[n], proc_page, false, false);
n++;
}
else
break;
}
- std::size_t proc_page_len = m_temp_pages.back().size();
- // we know that operators take only 1 instruction, so if there are more
// it's a builtin/function
- if (proc_page_len > 1)
+ if (m_temp_pages.back()[0].opcode < Instruction::FIRST_OPERATOR)
{
if (is_terminal && x.constList()[0].nodeType() == NodeType::Symbol && var_name == x.constList()[0].string())
{
@@ -615,26 +608,22 @@ namespace Ark
// push the arguments in reverse order
for (std::size_t i = x.constList().size() - 1; i >= n; --i)
- _compile(x.constList()[i], p, false, false);
+ compileExpression(x.constList()[i], p, false, false);
// jump to the top of the function
- page(p).push_back(Instruction::JUMP);
- pushNumber(0_u16, page_ptr(p));
-
+ page(p).emplace_back(Instruction::JUMP, 0_u16);
return; // skip the possible Instruction::POP at the end
}
else
{
// push arguments on current page
for (auto exp = x.constList().begin() + n, exp_end = x.constList().end(); exp != exp_end; ++exp)
- _compile(*exp, p, false, false);
+ compileExpression(*exp, p, false, false);
// push proc from temp page
- for (auto const& inst : m_temp_pages.back())
- page(p).push_back(inst);
+ for (const Word& word : m_temp_pages.back())
+ page(p).push_back(word);
m_temp_pages.pop_back();
- // call the procedure
- page(p).push_back(Instruction::CALL);
// number of arguments
std::size_t args_count = 0;
for (auto it = x.constList().begin() + 1, it_end = x.constList().end(); it != it_end; ++it)
@@ -643,23 +632,24 @@ namespace Ark
it->nodeType() != NodeType::Capture)
args_count++;
}
- pushNumber(static_cast(args_count), page_ptr(p));
+ // call the procedure
+ page(p).emplace_back(Instruction::CALL, args_count);
}
}
else // operator
{
// retrieve operator
- auto op_inst = m_temp_pages.back()[0];
+ auto op = m_temp_pages.back()[0];
m_temp_pages.pop_back();
- if (op_inst == Instruction::ASSERT)
- produces_result = false;
+ if (op.opcode == Instruction::ASSERT)
+ is_result_unused = false;
// push arguments on current page
std::size_t exp_count = 0;
for (std::size_t index = n, size = x.constList().size(); index < size; ++index)
{
- _compile(x.constList()[index], p, false, false);
+ compileExpression(x.constList()[index], p, false, false);
if ((index + 1 < size &&
x.constList()[index + 1].nodeType() != NodeType::GetField &&
@@ -670,13 +660,13 @@ namespace Ark
// in order to be able to handle things like (op A B C D...)
// which should be transformed into A B op C op D op...
if (exp_count >= 2)
- page(p).push_back(op_inst);
+ page(p).emplace_back(op.opcode, 2); // TODO generalize to n arguments (n >= 2)
}
if (exp_count == 1)
{
- if (isUnaryInst(static_cast(op_inst)))
- page(p).push_back(op_inst);
+ if (isUnaryInst(static_cast(op.opcode)))
+ page(p).push_back(op.opcode);
else
throwCompilerError("Operator needs two arguments, but was called with only one", x.constList()[0]);
}
@@ -684,7 +674,7 @@ namespace Ark
// need to check we didn't push the (op A B C D...) things for operators not supporting it
if (exp_count > 2)
{
- switch (op_inst)
+ switch (op.opcode)
{
// authorized instructions
case Instruction::ADD: [[fallthrough]];
@@ -699,26 +689,17 @@ namespace Ark
default:
throwCompilerError(
"can not create a chained expression (of length " + std::to_string(exp_count) +
- ") for operator `" + std::string(internal::operators[static_cast(op_inst - Instruction::FIRST_OPERATOR)]) +
+ ") for operator `" + std::string(internal::operators[static_cast(op.opcode - Instruction::FIRST_OPERATOR)]) +
"'. You most likely forgot a `)'.",
x);
}
}
}
- if (produces_result)
+ if (is_result_unused)
page(p).push_back(Instruction::POP);
}
- void Compiler::putValue(const Node& x, int p, bool produces_result)
- {
- std::string name = x.constList()[1].string();
-
- // starting at index = 2 because x is a (let|mut|set variable ...) node
- for (std::size_t idx = 2, end = x.constList().size(); idx < end; ++idx)
- _compile(x.constList()[idx], p, produces_result, false, name);
- }
-
uint16_t Compiler::addSymbol(const Node& sym)
{
// otherwise, add the symbol, and return its id in the table
@@ -817,18 +798,4 @@ namespace Ark
return suggestion;
}
-
- void Compiler::pushNumber(uint16_t n, std::vector* page) noexcept
- {
- if (page == nullptr)
- {
- m_bytecode.push_back((n & 0xff00) >> 8);
- m_bytecode.push_back(n & 0x00ff);
- }
- else
- {
- page->emplace_back((n & 0xff00) >> 8);
- page->emplace_back(n & 0x00ff);
- }
- }
}
diff --git a/src/arkreactor/Compiler/Macros/Processor.cpp b/src/arkreactor/Compiler/Macros/Processor.cpp
index e678aea88..fe304c9b6 100644
--- a/src/arkreactor/Compiler/Macros/Processor.cpp
+++ b/src/arkreactor/Compiler/Macros/Processor.cpp
@@ -602,7 +602,6 @@ namespace Ark::internal
case NodeType::Capture:
case NodeType::GetField:
- case NodeType::Closure:
return false;
case NodeType::Keyword:
diff --git a/src/arkreactor/Utils.cpp b/src/arkreactor/Utils.cpp
index 03799843b..b18b1a00e 100644
--- a/src/arkreactor/Utils.cpp
+++ b/src/arkreactor/Utils.cpp
@@ -2,34 +2,6 @@
namespace Ark::Utils
{
- int decPlaces(double d)
- {
- constexpr double precision = 1e-7;
- double temp = 0.0;
- int decimal_places = 0;
-
- do
- {
- d *= 10;
- temp = d - static_cast(d);
- decimal_places++;
- } while (temp > precision && decimal_places < std::numeric_limits::digits10);
-
- return decimal_places;
- }
-
- int digPlaces(double d)
- {
- int digit_places = 0;
- int i = static_cast(d);
- while (i != 0)
- {
- digit_places++;
- i /= 10;
- }
- return digit_places;
- }
-
int levenshteinDistance(const std::string& str1, const std::string& str2)
{
std::size_t str1_len = str1.size();
diff --git a/src/arkreactor/VM/Scope.cpp b/src/arkreactor/VM/Scope.cpp
index b6c3a4a83..9cbe5f9f0 100644
--- a/src/arkreactor/VM/Scope.cpp
+++ b/src/arkreactor/VM/Scope.cpp
@@ -4,24 +4,35 @@
namespace Ark::internal
{
- Scope::Scope() noexcept
+ Scope::Scope() noexcept :
+ m_min_id(std::numeric_limits::max()), m_max_id(0)
{
m_data.reserve(3);
}
void Scope::push_back(uint16_t id, Value&& val) noexcept
{
+ if (id < m_min_id)
+ m_min_id = id;
+ if (id > m_max_id)
+ m_max_id = id;
+
m_data.emplace_back(std::move(id), std::move(val));
}
void Scope::push_back(uint16_t id, const Value& val) noexcept
{
+ if (id < m_min_id)
+ m_min_id = id;
+ if (id > m_max_id)
+ m_max_id = id;
+
m_data.emplace_back(id, val);
}
bool Scope::has(uint16_t id) noexcept
{
- return operator[](id) != nullptr;
+ return m_min_id <= id && m_max_id <= id && operator[](id) != nullptr;
}
Value* Scope::operator[](uint16_t id) noexcept
@@ -34,6 +45,16 @@ namespace Ark::internal
return nullptr;
}
+ const Value* Scope::operator[](uint16_t id) const noexcept
+ {
+ for (std::size_t i = 0, end = m_data.size(); i < end; ++i)
+ {
+ if (m_data[i].first == id)
+ return &m_data[i].second;
+ }
+ return nullptr;
+ }
+
uint16_t Scope::idFromValue(const Value& val) const noexcept
{
for (std::size_t i = 0, end = m_data.size(); i < end; ++i)
@@ -48,4 +69,22 @@ namespace Ark::internal
{
return m_data.size();
}
+
+ bool operator==(const Scope& A, const Scope& B) noexcept
+ {
+ const std::size_t size = A.size();
+ if (size != B.size())
+ return false;
+ if (A.m_min_id != B.m_min_id || A.m_max_id != B.m_max_id)
+ return false;
+
+ // assuming we have the same closure page address, the element should be in the same order
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ if (A.m_data[i] != B.m_data[i])
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/src/arkreactor/VM/State.cpp b/src/arkreactor/VM/State.cpp
index 97c963ec1..e2093ac60 100644
--- a/src/arkreactor/VM/State.cpp
+++ b/src/arkreactor/VM/State.cpp
@@ -325,7 +325,7 @@ namespace Ark
while (m_bytecode[i] == Instruction::CODE_SEGMENT_START)
{
i++;
- uint16_t size = readNumber(i);
+ uint16_t size = readNumber(i) * 4; // because the instructions are on 4 bytes
i++;
m_pages.emplace_back();
diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp
index a705fb945..9116d3c38 100644
--- a/src/arkreactor/VM/VM.cpp
+++ b/src/arkreactor/VM/VM.cpp
@@ -89,7 +89,7 @@ namespace Ark
{
namespace fs = std::filesystem;
- const std::string file = m_state.m_constants[id].stringRef().toString();
+ const std::string file = m_state.m_constants[id].stringRef();
std::string path = file;
// bytecode loaded from file
@@ -270,9 +270,11 @@ namespace Ark
while (m_running && context.fc > untilFrameCount)
{
// get current instruction
- uint8_t inst = m_state.m_pages[context.pp][context.ip];
+ [[maybe_unused]] uint8_t padding = m_state.m_pages[context.pp][context.ip];
+ uint8_t inst = m_state.m_pages[context.pp][context.ip + 1];
+ uint16_t arg = (static_cast(m_state.m_pages[context.pp][context.ip + 2]) << 8) + static_cast(m_state.m_pages[context.pp][context.ip + 3]);
+ context.ip += 4;
- // and it's time to du-du-du-du-duel!
switch (inst)
{
#pragma region "Instructions"
@@ -284,8 +286,7 @@ namespace Ark
Job: Load a symbol from its id onto the stack
*/
- ++context.ip;
- context.last_symbol = readNumber(context);
+ context.last_symbol = arg;
if (Value* var = findNearestVariable(context.last_symbol, context); var != nullptr)
// push internal reference, shouldn't break anything so far
@@ -304,18 +305,15 @@ namespace Ark
and push a Closure with the page address + environment instead of the constant
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- if (context.saved_scope && m_state.m_constants[id].valueType() == ValueType::PageAddr)
+ if (context.saved_scope && m_state.m_constants[arg].valueType() == ValueType::PageAddr)
{
- push(Value(Closure(context.saved_scope.value(), m_state.m_constants[id].pageAddr())), context);
+ push(Value(Closure(context.saved_scope.value(), m_state.m_constants[arg].pageAddr())), context);
context.saved_scope.reset();
}
else
{
// push internal ref
- push(&(m_state.m_constants[id]), context);
+ push(&(m_state.m_constants[arg]), context);
}
break;
@@ -329,11 +327,8 @@ namespace Ark
Remove the value from the stack no matter what it is
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
if (*popAndResolveAsPtr(context) == Builtins::trueSym)
- context.ip = static_cast(id) - 1; // because we are doing a ++context.ip right after this
+ context.ip = static_cast(arg) * 4; // instructions are 4 bytes
break;
}
@@ -346,20 +341,17 @@ namespace Ark
couldn't find a scope where the variable exists
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- if (Value* var = findNearestVariable(id, context); var != nullptr)
+ if (Value* var = findNearestVariable(arg, context); var != nullptr)
{
if (var->isConst())
- throwVMError(ErrorKind::Mutability, "Can not modify a constant: " + m_state.m_symbols[id]);
+ throwVMError(ErrorKind::Mutability, "Can not modify a constant: " + m_state.m_symbols[arg]);
*var = *popAndResolveAsPtr(context);
var->setConst(false);
break;
}
- throwVMError(ErrorKind::Scope, "Unbound variable " + m_state.m_symbols[id] + ", can not change its value");
+ throwVMError(ErrorKind::Scope, "Unbound variable " + m_state.m_symbols[arg] + ", can not change its value");
break;
}
@@ -371,16 +363,13 @@ namespace Ark
following the given symbol id (cf symbols table)
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
// check if we are redefining a variable
- if (auto val = (*context.locals.back())[id]; val != nullptr)
- throwVMError(ErrorKind::Mutability, "Can not use 'let' to redefine the variable " + m_state.m_symbols[id]);
+ if (auto val = (*context.locals.back())[arg]; val != nullptr)
+ throwVMError(ErrorKind::Mutability, "Can not use 'let' to redefine the variable " + m_state.m_symbols[arg]);
Value val = *popAndResolveAsPtr(context);
val.setConst(true);
- (*context.locals.back()).push_back(id, val);
+ (*context.locals.back()).push_back(arg, val);
break;
}
@@ -393,11 +382,8 @@ namespace Ark
the value from the stack no matter what it is
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
if (*popAndResolveAsPtr(context) == Builtins::falseSym)
- context.ip = static_cast(id) - 1; // because we are doing a ++context.ip right after this
+ context.ip = static_cast(arg) * 4; // instructions are 4 bytes
break;
}
@@ -408,10 +394,7 @@ namespace Ark
Job: Jump to the provided address
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- context.ip = static_cast(id) - 1; // because we are doing a ++context.ip right after this
+ context.ip = static_cast(arg) * 4; // instructions are 4 bytes
break;
}
@@ -466,7 +449,7 @@ namespace Ark
ErrorKind::VM,
"Maximum recursion depth exceeded.\n"
"You could consider rewriting your function to make use of tail-call optimization.");
- call(context);
+ call(context, arg);
break;
}
@@ -479,17 +462,14 @@ namespace Ark
they were created
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
if (!context.saved_scope)
context.saved_scope = std::make_shared();
- Value* ptr = (*context.locals.back())[id];
+ Value* ptr = (*context.locals.back())[arg];
if (!ptr)
- throwVMError(ErrorKind::Scope, "Couldn't capture '" + m_state.m_symbols[id] + "' as it is currently unbound");
+ throwVMError(ErrorKind::Scope, "Couldn't capture '" + m_state.m_symbols[arg] + "' as it is currently unbound");
ptr = ptr->valueType() == ValueType::Reference ? ptr->reference() : ptr;
- (*context.saved_scope.value()).push_back(id, *ptr);
+ (*context.saved_scope.value()).push_back(arg, *ptr);
break;
}
@@ -501,10 +481,7 @@ namespace Ark
Job: Push the builtin function object on the stack
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- push(Builtins::builtins[id].second, context);
+ push(Builtins::builtins[arg].second, context);
break;
}
@@ -516,16 +493,13 @@ namespace Ark
named following the given symbol id (cf symbols table)
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
Value val = *popAndResolveAsPtr(context);
val.setConst(false);
// avoid adding the pair (id, _) multiple times, with different values
- Value* local = (*context.locals.back())[id];
+ Value* local = (*context.locals.back())[arg];
if (local == nullptr)
- (*context.locals.back()).push_back(id, val);
+ (*context.locals.back()).push_back(arg, val);
else
*local = val;
@@ -539,10 +513,7 @@ namespace Ark
Job: Remove a variable/constant named following the given symbol id (cf symbols table)
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- if (Value* var = findNearestVariable(id, context); var != nullptr)
+ if (Value* var = findNearestVariable(arg, context); var != nullptr)
{
if (var->valueType() == ValueType::User)
var->usertypeRef().del();
@@ -550,7 +521,7 @@ namespace Ark
break;
}
- throwVMError(ErrorKind::Scope, "Unbound variable: " + m_state.m_symbols[id]);
+ throwVMError(ErrorKind::Scope, "Unbound variable: " + m_state.m_symbols[arg]);
break;
}
@@ -572,16 +543,14 @@ namespace Ark
stored in TS. Pop TS and push the value of field read on the stack
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
Value* var = popAndResolveAsPtr(context);
if (var->valueType() != ValueType::Closure)
- 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");
+ 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[arg] + "' from it");
- if (Value* field = (*var->refClosure().scope())[id]; field != nullptr)
+ if (Value* field = (*var->refClosure().scope())[arg]; field != nullptr)
{
// check for CALL instruction
+ // doing a +1 on the IP to read the instruction because context.ip is already on the next instruction word (the padding)
if (static_cast(context.ip) + 1 < m_state.m_pages[context.pp].size() && m_state.m_pages[context.pp][context.ip + 1] == Instruction::CALL)
{
context.locals.push_back(var->refClosure().scope());
@@ -592,7 +561,7 @@ namespace Ark
break;
}
- throwVMError(ErrorKind::Scope, "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[arg] + " in the closure enviroment");
break;
}
@@ -604,10 +573,7 @@ namespace Ark
Raise an error if it couldn't find the module.
*/
- ++context.ip;
- uint16_t id = readNumber(context);
-
- loadPlugin(id, context);
+ loadPlugin(arg, context);
break;
}
@@ -617,14 +583,12 @@ namespace Ark
Takes at least 0 arguments and push a list on the stack.
The content is pushed in reverse order
*/
- ++context.ip;
- uint16_t count = readNumber(context);
Value l(ValueType::List);
- if (count != 0)
- l.list().reserve(count);
+ if (arg != 0)
+ l.list().reserve(arg);
- for (uint16_t i = 0; i < count; ++i)
+ for (uint16_t i = 0; i < arg; ++i)
l.push_back(*popAndResolveAsPtr(context));
push(std::move(l), context);
break;
@@ -632,9 +596,6 @@ namespace Ark
case Instruction::APPEND:
{
- ++context.ip;
- uint16_t count = readNumber(context);
-
Value* list = popAndResolveAsPtr(context);
if (list->valueType() != ValueType::List)
types::generateError(
@@ -645,9 +606,9 @@ namespace Ark
const uint16_t size = list->constList().size();
Value obj = Value(*list);
- obj.list().reserve(size + count);
+ obj.list().reserve(size + arg);
- for (uint16_t i = 0; i < count; ++i)
+ for (uint16_t i = 0; i < arg; ++i)
obj.push_back(*popAndResolveAsPtr(context));
push(std::move(obj), context);
break;
@@ -655,9 +616,6 @@ namespace Ark
case Instruction::CONCAT:
{
- ++context.ip;
- uint16_t count = readNumber(context);
-
Value* list = popAndResolveAsPtr(context);
if (list->valueType() != ValueType::List)
types::generateError(
@@ -667,7 +625,7 @@ namespace Ark
Value obj = Value(*list);
- for (uint16_t i = 0; i < count; ++i)
+ for (uint16_t i = 0; i < arg; ++i)
{
Value* next = popAndResolveAsPtr(context);
@@ -686,9 +644,6 @@ namespace Ark
case Instruction::APPEND_IN_PLACE:
{
- ++context.ip;
- uint16_t count = readNumber(context);
-
Value* list = popAndResolveAsPtr(context);
if (list->isConst())
@@ -699,7 +654,7 @@ namespace Ark
{ { types::Contract { { types::Typedef("list", ValueType::List) } } } },
{ *list });
- for (uint16_t i = 0; i < count; ++i)
+ for (uint16_t i = 0; i < arg; ++i)
list->push_back(*popAndResolveAsPtr(context));
break;
@@ -707,9 +662,6 @@ namespace Ark
case Instruction::CONCAT_IN_PLACE:
{
- ++context.ip;
- uint16_t count = readNumber(context);
-
Value* list = popAndResolveAsPtr(context);
if (list->isConst())
@@ -720,7 +672,7 @@ namespace Ark
{ { types::Contract { { types::Typedef("list", ValueType::List) } } } },
{ *list });
- for (uint16_t i = 0; i < count; ++i)
+ for (uint16_t i = 0; i < arg; ++i)
{
Value* next = popAndResolveAsPtr(context);
@@ -960,7 +912,7 @@ namespace Ark
else
{
Value b = *a;
- b.stringRef().erase_front(0);
+ b.stringRef().erase(b.stringRef().begin());
push(std::move(b), context);
}
}
@@ -1023,7 +975,7 @@ namespace Ark
{ *a, *b });
if (*a == Builtins::falseSym)
- throw AssertionFailed(b->stringRef().toString());
+ throw AssertionFailed(b->stringRef());
break;
}
@@ -1038,7 +990,7 @@ namespace Ark
{ *a });
double val;
- if (Utils::isDouble(a->string().c_str(), &val))
+ if (Utils::isDouble(a->string(), &val))
push(Value(val), context);
else
push(Builtins::nil, context);
@@ -1137,7 +1089,7 @@ namespace Ark
if (field->valueType() != ValueType::String)
throw TypeError("Argument no 2 of hasField should be a String");
- auto it = std::find(m_state.m_symbols.begin(), m_state.m_symbols.end(), field->stringRef().toString());
+ auto it = std::find(m_state.m_symbols.begin(), m_state.m_symbols.end(), field->stringRef());
if (it == m_state.m_symbols.end())
{
push(Builtins::falseSym, context);
@@ -1165,9 +1117,6 @@ namespace Ark
break;
}
- // move forward
- ++context.ip;
-
#ifdef ARK_PROFILER_MIPS
++instructions_executed;
#endif
@@ -1294,7 +1243,7 @@ namespace Ark
}
std::cerr << termcolor::reset
- << "At IP: " << (saved_ip != -1 ? saved_ip : 0)
+ << "At IP: " << (saved_ip != -1 ? saved_ip / 4 : 0) // dividing by 4 because the instructions are actually on 4 bytes
<< ", PP: " << saved_pp
<< ", SP: " << saved_sp
<< "\n";
diff --git a/src/arkreactor/VM/Value.cpp b/src/arkreactor/VM/Value.cpp
index d2e4fa60f..f54401a39 100644
--- a/src/arkreactor/VM/Value.cpp
+++ b/src/arkreactor/VM/Value.cpp
@@ -1,6 +1,6 @@
#include
-#include
+#include
#define init_const_type(is_const, type) ((is_const ? (1 << 7) : 0) | static_cast(type))
@@ -72,10 +72,6 @@ namespace Ark
{}
Value::Value(const std::string& value) noexcept :
- m_const_type(init_const_type(false, ValueType::String)), m_value(value.c_str())
- {}
-
- Value::Value(const String& value) noexcept :
m_const_type(init_const_type(false, ValueType::String)), m_value(value)
{}
@@ -119,9 +115,9 @@ namespace Ark
return std::get(m_value);
}
- String& Value::stringRef()
+ std::string& Value::stringRef()
{
- return std::get(m_value);
+ return std::get(m_value);
}
UserType& Value::usertypeRef()
@@ -154,13 +150,12 @@ namespace Ark
case ValueType::Number:
{
double d = number();
- os.precision(Utils::digPlaces(d) + Utils::decPlaces(d));
- os << d;
+ os << fmt::format("{}", d);
break;
}
case ValueType::String:
- os << string().c_str();
+ os << string();
break;
case ValueType::PageAddr:
diff --git a/src/arkreactor/VM/Value/Closure.cpp b/src/arkreactor/VM/Value/Closure.cpp
index 7ecdefbf8..937d1a676 100644
--- a/src/arkreactor/VM/Value/Closure.cpp
+++ b/src/arkreactor/VM/Value/Closure.cpp
@@ -44,4 +44,13 @@ namespace Ark::internal
}
os << ")";
}
+
+ bool operator==(const Closure& A, const Closure& B) noexcept
+ {
+ // they do not come from the same closure builder
+ if (A.m_page_addr != B.m_page_addr)
+ return false;
+
+ return *A.m_scope == *B.m_scope;
+ }
}
diff --git a/src/arkscript/main.cpp b/src/arkscript/main.cpp
index 1fed0a43e..92e878459 100644
--- a/src/arkscript/main.cpp
+++ b/src/arkscript/main.cpp
@@ -17,19 +17,6 @@ int main(int argc, char** argv)
{
using namespace clipp;
-// TODO remove once next major version of ArkScript is available
-#if ARK_VERSION_MAJOR == 4
-# error "this code block should be removed from ArkScript 4.x.y"
-#endif
- {
- namespace fs = std::filesystem;
- fs::path program(argv[0]);
-
- if (program.stem() == "ark")
- std::cout << termcolor::yellow << "Warning" << termcolor::reset << " the command `ark' is being deprecated in favor of `arkscript'" << std::endl;
- }
-
-
enum class mode
{
help,
@@ -86,16 +73,16 @@ int main(int argc, char** argv)
option("-a", "--all").set(segment, Ark::BytecodeSegment::All).doc("Display all the bytecode segments (default)")
| option("-st", "--symbols").set(segment, Ark::BytecodeSegment::Symbols).doc("Display only the symbols table")
| option("-vt", "--values").set(segment, Ark::BytecodeSegment::Values).doc("Display only the values table")
+ | (
+ option("-cs", "--code").set(segment, Ark::BytecodeSegment::Code).doc("Display only the code segments")
+ , option("-p", "--page").set(segment, Ark::BytecodeSegment::Code).doc("Set the bytecode reader code segment to display")
+ & value("page", bcr_page)
+ )
)
, option("-s", "--slice").doc("Select a slice of instructions in the bytecode")
& value("start", bcr_start)
& value("end", bcr_end)
)
- | (
- option("-cs", "--code").set(segment, Ark::BytecodeSegment::Code).doc("Display only the code segments")
- , option("-p", "--page").doc("Set the bytecode reader code segment to display")
- & value("page", bcr_page)
- )
)
)
| (
@@ -171,8 +158,6 @@ int main(int argc, char** argv)
" sizeof(ExecutionContext) = %zuB\n"
"\nMisc\n"
" sizeof(vector) = %zuB\n"
- " sizeof(std::string) = %zuB\n"
- " sizeof(String) = %zuB\n"
" sizeof(char) = %zuB\n"
"\nsizeof(Node) = %zuB\n",
ARK_COMPILER, ARK_COMPILATION_OPTIONS,
@@ -190,8 +175,6 @@ int main(int argc, char** argv)
sizeof(Ark::internal::ExecutionContext),
// misc
sizeof(std::vector),
- sizeof(std::string),
- sizeof(String),
sizeof(char),
sizeof(Ark::internal::Node));
break;
diff --git a/tests/arkscript/builtins-tests.ark b/tests/arkscript/builtins-tests.ark
index 175d7cc3e..82a16e89d 100644
--- a/tests/arkscript/builtins-tests.ark
+++ b/tests/arkscript/builtins-tests.ark
@@ -16,7 +16,6 @@
(set tests (assert-eq (list:find [12] 12) 0 "list:find" tests))
(set tests (assert-eq (list:find [1 2 3] 2) 1 "list:find" tests))
(set tests (assert-eq (list:find [12] nil) -1 "list:find" tests))
- (set tests (assert-eq (list:removeAt base-list 1) [1 3] "list:removeAt" tests)) # DEPRECATED
(set tests (assert-eq (list:slice base-list-enhanced 0 3 1) base-list "list:slice" tests))
(set tests (assert-eq (list:slice base-list-enhanced 0 1 1) [1] "list:slice" tests))
(set tests (assert-eq (list:slice base-list-enhanced 0 3 2) [1 3] "list:slice" tests))
diff --git a/tests/arkscript/list-tests.ark b/tests/arkscript/list-tests.ark
index c7332488f..dd981e64a 100644
--- a/tests/arkscript/list-tests.ark
+++ b/tests/arkscript/list-tests.ark
@@ -27,8 +27,6 @@
(set tests (assert-eq (pop a 1) [1 3] "pop" tests))
(set tests (assert-eq (pop a 2) [1 2] "pop" tests))
(set tests (assert-eq a [1 2 3] "unmodified list" tests))
- (set tests (assert-eq (list:removeAt a 0) [2 3] "remove" tests)) # DEPRECATED
- (set tests (assert-eq (list:removeAt a 2) [1 2] "remove" tests)) # DEPRECATED
(set tests (assert-eq a [1 2 3] "unmodified list" tests))
(set tests (assert-eq (list:reverse a) [3 2 1] "reverse" tests))
(set tests (assert-eq a [1 2 3] "unmodified list" tests))
diff --git a/tests/arkscript/tests-tools.ark b/tests/arkscript/tests-tools.ark
index 145be0ff9..529fef62e 100644
--- a/tests/arkscript/tests-tools.ark
+++ b/tests/arkscript/tests-tools.ark
@@ -1,31 +1,31 @@
(import "console.arkm")
(let assert-eq (fun (val1 val2 message tests) {
- (assert (= val1 val2) (str:format "%% (%%) - %% SHOULD BE EQUAL TO %%" message tests val1 val2))
+ (assert (= val1 val2) (str:format "{} ({}) - {} SHOULD BE EQUAL TO {}" message tests val1 val2))
(+ 1 tests)}))
(let assert-neq (fun (val1 val2 message tests) {
- (assert (!= val1 val2) (str:format "%% (%%) - %% SHOULD BE NOT EQUAL TO %%" message tests val1 val2))
+ (assert (!= val1 val2) (str:format "{} ({}) - {} SHOULD BE NOT EQUAL TO {}" message tests val1 val2))
(+ 1 tests)}))
(let assert-gt (fun (val1 val2 message tests) {
- (assert (> val1 val2) (str:format "%% (%%) - %% SHOULD BE GREATER THAN %%" message tests val1 val2))
+ (assert (> val1 val2) (str:format "{} ({}) - {} SHOULD BE GREATER THAN {}" message tests val1 val2))
(+ 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))
+ (assert (>= val1 val2) (str:format "{} ({}) - {} SHOULD BE GREATER OR EQUAL TO {}" message tests val1 val2))
(+ 1 tests)}))
(let assert-lt (fun (val1 val2 message tests) {
- (assert (< val1 val2) (str:format "%% (%%) - %% SHOULD BE LESSER THAN %%" message tests val1 val2))
+ (assert (< val1 val2) (str:format "{} ({}) - {} SHOULD BE LESSER THAN {}" message tests val1 val2))
(+ 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))
+ (assert (<= val1 val2) (str:format "{} ({}) - {} SHOULD BE LESSER OR EQUAL TO {}" message tests val1 val2))
(+ 1 tests)}))
(let assert-val (fun (val0 message tests) {
- (assert val0 (str:format "%% (%%) - %% SHOULD BE TRUTHY" message tests val0))
+ (assert val0 (str:format "{} ({}) - {} SHOULD BE TRUTHY" message tests val0))
(+ 1 tests)}))
(let recap (fun (test-name tests time_) {
diff --git a/tests/arkscript/vm-tests.ark b/tests/arkscript/vm-tests.ark
index 5f7f01f68..6f78e9606 100644
--- a/tests/arkscript/vm-tests.ark
+++ b/tests/arkscript/vm-tests.ark
@@ -5,6 +5,15 @@
(let start-time (time))
(let closure (fun (&tests) ()))
+ (let make (fun (a b c)
+ (fun (&a &b &c) ())))
+ (let make2 (fun (a b c)
+ (fun (&a &b &c) ())))
+ (let closure_1 (make 1 2 3))
+ (let closure_1_bis closure_1)
+ (let closure_2 (make 1 2 3))
+ (let closure_3 (make 3 2 3))
+ (let closure_4 (make2 1 2 3))
(set tests (assert-eq (+ 1 2) 3 "addition" tests))
(set tests (assert-eq (+ 1.5 2.5) 4.0 "addition (double)" tests))
@@ -87,6 +96,12 @@
(set tests (assert-val (hasField closure "tests") "hasField" tests))
(set tests (assert-val (not (hasField closure "12")) "not hasField" tests))
(set tests (assert-eq (toString closure) "(.tests=0)" "toString closure" tests))
+ (set tests (assert-eq closure_1 closure_1_bis "closure comparison" tests))
+ (set tests (assert-eq closure_1 closure_2 "closure comparison" tests))
+ (set tests (assert-neq closure_1 closure_4 "closure comparison" tests))
+ (set tests (assert-neq closure_2 closure_3 "closure comparison" tests))
+ (set tests (assert-neq closure_1 closure_3 "closure comparison" tests))
+ (set tests (assert-neq closure closure_1 "closure comparison" tests))
(recap "VM operations passed" tests (- (time) start-time))