Byte array (#1)
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakePythonSetting">
|
||||||
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
|
</component>
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/tyche.iml" filepath="$PROJECT_DIR$/.idea/tyche.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
.idea/tyche.iml
generated
Normal file
2
.idea/tyche.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||||
7
.idea/vcs.xml
generated
Normal file
7
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
92
CMakeLists.txt
Normal file
92
CMakeLists.txt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
cmake_minimum_required (VERSION 3.24)
|
||||||
|
|
||||||
|
project(tyche
|
||||||
|
VERSION 0.0.1
|
||||||
|
DESCRIPTION "An embeddable/standalone programming language"
|
||||||
|
LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
|
#
|
||||||
|
# project options / configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD 23 CACHE STRING "C++ Standard")
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set_property(GLOBAL PROPERTY CXX_EXTENSIONS OFF)
|
||||||
|
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
set_property(GLOBAL PROPERTY LINK_WHAT_YOU_USE TRUE)
|
||||||
|
|
||||||
|
# warnings / flags
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(warnings -Wall -Wextra -Wformat-nonliteral -Wundef -Wshadow -Wwrite-strings -Wfloat-equal -Wswitch-default -Wmissing-format-attribute -Wswitch-enum -Wmissing-noreturn -Wno-unused-parameter -Wno-unused)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
set(warnings ${warnings} -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wsuggest-attribute=cold)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# try to use ccache, if available
|
||||||
|
find_program(CCACHE_PROGRAM ccache)
|
||||||
|
if(CCACHE_PROGRAM)
|
||||||
|
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ignore warnings in imported files
|
||||||
|
set_source_files_properties(${IMGUI_SRC} PROPERTIES COMPILE_FLAGS "-w")
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_compile_options(-ggdb -O0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
set(DEF B_PRODUCTION_MODE=ON)
|
||||||
|
add_compile_options(-Ofast -flto)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# libraries
|
||||||
|
#
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
googletest
|
||||||
|
# Specify the commit you depend on and update it regularly.
|
||||||
|
URL https://github.com/google/googletest/releases/download/v1.17.0/googletest-1.17.0.tar.gz
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
|
#
|
||||||
|
# library
|
||||||
|
#
|
||||||
|
|
||||||
|
add_library(lib${PROJECT_NAME} STATIC
|
||||||
|
src/bytecode/bytearray.hh
|
||||||
|
src/bytecode/bytearray.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
||||||
|
|
||||||
|
#
|
||||||
|
# tests
|
||||||
|
#
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc)
|
||||||
|
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
|
||||||
|
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test)
|
||||||
|
|
||||||
|
#
|
||||||
|
# check for leaks
|
||||||
|
#
|
||||||
|
|
||||||
|
add_custom_target(leaks)
|
||||||
|
add_custom_command(TARGET leaks
|
||||||
|
POST_BUILD
|
||||||
|
COMMENT "Check for leaks using valgrind."
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMAND valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=${CMAKE_SOURCE_DIR}/valgrind.supp ./${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# installation
|
||||||
|
#
|
||||||
|
|
||||||
|
install(TARGETS lib${CMAKE_PROJECT_NAME} RUNTIME DESTINATION lib)
|
||||||
12
TODO.md
Normal file
12
TODO.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
## Bytecode
|
||||||
|
|
||||||
|
- [x] Byte array
|
||||||
|
- Auto-expand
|
||||||
|
- Add/retrive byte/int/float/string
|
||||||
|
- Should not be larger than the byte array itself
|
||||||
|
- [ ] Chunk
|
||||||
|
- Add/retrive all types of data
|
||||||
|
- Keeps no memory except for caching
|
||||||
|
- [ ] Chunk loader
|
||||||
|
- Combine multiple chunks
|
||||||
|
- Resolve function ids, constant ids, etc
|
||||||
125
doc/OPCODES
Normal file
125
doc/OPCODES
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
Operations
|
||||||
|
----------
|
||||||
|
|
||||||
|
Stack operations: (0x00~0x1f)
|
||||||
|
pushn [int] Push int
|
||||||
|
pushr [float] Push float (real)
|
||||||
|
pushs [string] Push string
|
||||||
|
pshcn [index] Push int from constant list
|
||||||
|
pshcr [index] Push float from constant list
|
||||||
|
pshcs [index] Push string from constant list
|
||||||
|
pushf [function] Push function id
|
||||||
|
pushz Push zero (or false)
|
||||||
|
pusht Push true
|
||||||
|
newa [array] Push (create) empty array
|
||||||
|
newt [table] Push (create) empty table
|
||||||
|
pop
|
||||||
|
dup
|
||||||
|
|
||||||
|
Local variables: (0x20~0x2f)
|
||||||
|
setl [int] Set stack top as indexed local variable
|
||||||
|
getl [int] Get indexed local variable and place on stack
|
||||||
|
setg [int] Set global variable
|
||||||
|
getg [int] Get global variable
|
||||||
|
|
||||||
|
Function operations: (0x30~0x3f)
|
||||||
|
call [n_pars] Enter function on stack toplevel (passing n next stack values as parameters)
|
||||||
|
ret Leave a function (return value in stack)
|
||||||
|
retn Leave a function (return nil)
|
||||||
|
|
||||||
|
Control flow: (0x40~0x4f)
|
||||||
|
bz [pc] Branch if zero
|
||||||
|
bnz [pc] Branch if not zero
|
||||||
|
jmp [pc] Unconditional jump
|
||||||
|
* Jumps can only happen within the same function.
|
||||||
|
|
||||||
|
Logical/arithmetic: (0x50~0x6f)
|
||||||
|
sum Sum top 2 values in stack
|
||||||
|
sub Subtract top 2 values in stack
|
||||||
|
mul Multiply top 2 values in stack
|
||||||
|
div Float division
|
||||||
|
idiv Integer division
|
||||||
|
eq Equality
|
||||||
|
neq Inequality
|
||||||
|
lt Less than
|
||||||
|
lte Less than or equals
|
||||||
|
gt Greater than
|
||||||
|
gte Greater than or equals
|
||||||
|
and Bitwise AND
|
||||||
|
or Bitwise OR
|
||||||
|
xor Bitwise XOR
|
||||||
|
|
||||||
|
Table and array operations: (0x70~07xf)
|
||||||
|
getkv Get table's value based on key (pull 1 value, push 1 value)
|
||||||
|
setkv Set table's key and value (pull 2 values from stack)
|
||||||
|
geta Get array's position value
|
||||||
|
seta Set array's position value (pull 2 values from stack)
|
||||||
|
appnd Add value to the end of array
|
||||||
|
next Push the next pair into the stack (for loops)
|
||||||
|
smt Set value metatable
|
||||||
|
mt Get value metatable
|
||||||
|
|
||||||
|
Other value operations: (0x80~0x8f)
|
||||||
|
len Get table, array or string size
|
||||||
|
type Get type from value at the top of the stack
|
||||||
|
cast [type] Cast type to another type
|
||||||
|
ver Return VM version
|
||||||
|
|
||||||
|
External code: (0x90~0x9f)
|
||||||
|
cmpl Compile code to assembly
|
||||||
|
asmbl Assemble code to bytecode format
|
||||||
|
load Load bytecode as function (will place function on stack)
|
||||||
|
|
||||||
|
Error handling: (0xa0~0xaf)
|
||||||
|
???
|
||||||
|
|
||||||
|
|
||||||
|
Bytecode format
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The bytecode file is composed of the following sections:
|
||||||
|
|
||||||
|
* [0x0] 16-byte header
|
||||||
|
[00]: VM format
|
||||||
|
[??]: reserved
|
||||||
|
* [0x1] Index: pointers to each one of the sections, up to 8
|
||||||
|
Each pointer: 4 bits
|
||||||
|
* [0x2] Constants: all constants (such as strings) used in the code
|
||||||
|
* Table of 4-bit constant indexes with pointer to constant
|
||||||
|
* Raw constant data
|
||||||
|
* [0x3] Functions: Pointer to functions within the code
|
||||||
|
[0:3]: function pointer
|
||||||
|
[4:5]: number of parameters
|
||||||
|
[6:7]: number of local variables
|
||||||
|
* [0x4] Code: executable code
|
||||||
|
[1-byte]: operation
|
||||||
|
[variable]: operand (see value encoding below)
|
||||||
|
* [0x5] Debugging info
|
||||||
|
???
|
||||||
|
|
||||||
|
The max file size is 2 Gb.
|
||||||
|
|
||||||
|
## Values can be encoded in the following ways:
|
||||||
|
* The type is defined by the operator.
|
||||||
|
* Encoding varies according to the type:
|
||||||
|
int: use protobuf format
|
||||||
|
float: 4-bit floating point
|
||||||
|
string: int-defined length, followed by the string proper - no null terminator
|
||||||
|
* Constant indexes and function ids are encoded as ints
|
||||||
|
|
||||||
|
|
||||||
|
Internal handling of values
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
## Supported types
|
||||||
|
Nil 0
|
||||||
|
Integer 1
|
||||||
|
Float 2
|
||||||
|
String 3
|
||||||
|
Array 4
|
||||||
|
Table 5
|
||||||
|
Function 6
|
||||||
|
NativePointer 7
|
||||||
|
|
||||||
|
## Internal format
|
||||||
|
???
|
||||||
84
src/bytecode/bytearray.cc
Normal file
84
src/bytecode/bytearray.cc
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "bytearray.hh"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace tyche {
|
||||||
|
|
||||||
|
void ByteArray::add_byte(uint32_t addr, uint8_t byte)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
data_.at(addr) = byte;
|
||||||
|
} catch (std::out_of_range&) {
|
||||||
|
data_.resize(addr + 1, 0);
|
||||||
|
data_.at(addr) = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteArray::add_int(uint32_t addr, int32_t value)
|
||||||
|
{
|
||||||
|
uint32_t zz = ((uint32_t)(value << 1)) ^ ((uint32_t)(value >> 31));
|
||||||
|
while (zz > 0x7F) {
|
||||||
|
add_byte(addr++, (zz & 0x7F) | 0x80);
|
||||||
|
zz >>= 7;
|
||||||
|
}
|
||||||
|
add_byte(addr, zz & 0x7F);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteArray::add_float(uint32_t addr, float value)
|
||||||
|
{
|
||||||
|
uint32_t bits;
|
||||||
|
std::memcpy(&bits, &value, 4);
|
||||||
|
add_byte(addr, (uint8_t)(bits));
|
||||||
|
add_byte(addr+1, (uint8_t)(bits >> 8));
|
||||||
|
add_byte(addr+2, (uint8_t)(bits >> 16));
|
||||||
|
add_byte(addr+3, (uint8_t)(bits >> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteArray::add_string(uint32_t addr, std::string const& str)
|
||||||
|
{
|
||||||
|
for (uint8_t c: str)
|
||||||
|
add_byte(addr++, c);
|
||||||
|
add_byte(addr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ByteArray::get_byte(uint32_t addr) const
|
||||||
|
{
|
||||||
|
return data_.at(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int32_t, size_t> ByteArray::get_int(uint32_t addr) const
|
||||||
|
{
|
||||||
|
uint32_t zz = 0;
|
||||||
|
int shift = 0;
|
||||||
|
for (size_t i = 0; shift < 35; i++) {
|
||||||
|
uint8_t byte = get_byte(addr++);
|
||||||
|
zz |= (uint32_t)(byte & 0x7F) << shift;
|
||||||
|
if (!(byte & 0x80)) {
|
||||||
|
int32_t value = (int32_t)((zz >> 1) ^ -(zz & 1));
|
||||||
|
return std::make_pair(value, (int)(i + 1));
|
||||||
|
}
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
throw BytecodeParsingError("Error parsing int32 at position " + std::to_string(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<float, size_t> ByteArray::get_float(uint32_t addr) const
|
||||||
|
{
|
||||||
|
uint32_t bits = (uint32_t) get_byte(addr)
|
||||||
|
| (uint32_t) get_byte(addr+1) << 8
|
||||||
|
| (uint32_t) get_byte(addr+2) << 16
|
||||||
|
| (uint32_t) get_byte(addr+3) << 24;
|
||||||
|
float value;
|
||||||
|
std::memcpy(&value, &bits, 4);
|
||||||
|
return { value, 4 };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, size_t> ByteArray::get_string(uint32_t addr) const
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
while (char c = (char) get_byte(addr++))
|
||||||
|
data += c;
|
||||||
|
return { data, data.size() + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
src/bytecode/bytearray.hh
Normal file
36
src/bytecode/bytearray.hh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef TYCHE_BYTEARRAY_HH
|
||||||
|
#define TYCHE_BYTEARRAY_HH
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace tyche {
|
||||||
|
|
||||||
|
class ByteArray {
|
||||||
|
public:
|
||||||
|
void add_byte(uint32_t addr, uint8_t byte);
|
||||||
|
void add_int(uint32_t addr, int32_t value);
|
||||||
|
void add_float(uint32_t addr, float value);
|
||||||
|
void add_string(uint32_t addr, std::string const& str);
|
||||||
|
|
||||||
|
[[nodiscard]] uint8_t get_byte(uint32_t addr) const;
|
||||||
|
[[nodiscard]] std::pair<int32_t, size_t> get_int(uint32_t addr) const;
|
||||||
|
[[nodiscard]] std::pair<float, size_t> get_float(uint32_t addr) const;
|
||||||
|
[[nodiscard]] std::pair<std::string, size_t> get_string(uint32_t addr) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<uint8_t> const& data() const { return data_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BytecodeParsingError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
explicit BytecodeParsingError(std::string const& str) : std::runtime_error(str.c_str()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TYCHE_BYTEARRAY_HH
|
||||||
45
src/bytecode/tests.cc
Normal file
45
src/bytecode/tests.cc
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "bytearray.hh"
|
||||||
|
|
||||||
|
using namespace tyche;
|
||||||
|
|
||||||
|
TEST(ByteArray, ByteArray)
|
||||||
|
{
|
||||||
|
auto test = [](std::function<void(ByteArray&)> const& f, std::vector<uint8_t> const& expected) {
|
||||||
|
ByteArray ba;
|
||||||
|
f(ba);
|
||||||
|
ASSERT_EQ(ba.data().size(), expected.size());
|
||||||
|
ASSERT_EQ(std::memcmp(ba.data().data(), expected.data(), ba.data().size()), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TESTX(a, ...) test([](ByteArray& ba) { a; }, std::vector<uint8_t>({ __VA_ARGS__ }));
|
||||||
|
|
||||||
|
TESTX(ba.add_byte(1, 0xab), 0x00, 0xab)
|
||||||
|
|
||||||
|
ByteArray ba;
|
||||||
|
ba.add_byte(1, 0xab); ASSERT_EQ(ba.get_byte(1), 0xab);
|
||||||
|
|
||||||
|
ba.add_int(1, 12); ASSERT_EQ(ba.get_int(1), std::make_pair(12, 1));
|
||||||
|
ba.add_int(1, -12); ASSERT_EQ(ba.get_int(1), std::make_pair(-12, 1));
|
||||||
|
ba.add_int(1, 5000); ASSERT_EQ(ba.get_int(1), std::make_pair(5000, 2));
|
||||||
|
ba.add_int(1, 5000300); ASSERT_EQ(ba.get_int(1), std::make_pair(5000300, 4));
|
||||||
|
ba.add_int(1, -5000300); ASSERT_EQ(ba.get_int(1), std::make_pair(-5000300, 4));
|
||||||
|
|
||||||
|
ba.add_float(1, 3.14); ASSERT_FLOAT_EQ(ba.get_float(1).first, 3.14);
|
||||||
|
ba.add_float(1, -3.14); ASSERT_FLOAT_EQ(ba.get_float(1).first, -3.14);
|
||||||
|
ba.add_float(1, -5000300.1324); ASSERT_FLOAT_EQ(ba.get_float(1).first, -5000300.1324);
|
||||||
|
|
||||||
|
ba.add_string(1, "Hello world!"); ASSERT_EQ(ba.get_string(1), std::make_pair("Hello world!", 13));
|
||||||
|
|
||||||
|
#undef TESTX
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user