Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5001302855 | |||
| 6f0c3a729b | |||
| e2692a589a | |||
|
|
f447b6cb98 | ||
|
|
5c885654af | ||
|
|
df8edb549d | ||
|
|
d4aa83869c | ||
|
|
4d7282a30b | ||
|
|
5f88d862b0 | ||
|
|
38020b3cad | ||
|
|
32af8eae4a | ||
|
|
cd21d0ab9d | ||
|
|
0bfff6527a | ||
|
|
5adfa566a0 | ||
|
|
51852887ec | ||
|
|
886566a27b | ||
| f7e474f819 | |||
| a1c1aa0591 | |||
| d3a876ca7d | |||
| 88ad6a5c02 | |||
| d792d2d8c2 | |||
| d2463fd163 | |||
| a0881e3c22 | |||
| 06e5be5c89 | |||
| c8a6db0e4e | |||
| 89caa700cf | |||
| 8a4cce0da4 | |||
| 07dfbc99a2 | |||
| 874997995c | |||
| a38b2736c6 | |||
| 3e47163ee5 | |||
| 2265b8cf08 | |||
| c1d23a00b8 | |||
| bb8d5bfe34 | |||
| aefc2ff9ad | |||
| f61348456b | |||
| 9607c77939 | |||
| d1c0052918 | |||
| 86858ef26f | |||
| 7501c74712 | |||
| 19bff9b32f | |||
| 15f2794133 | |||
| 3c9fafb11a | |||
| e9da5d0cd5 | |||
| be28a6df79 | |||
| 74d860976a | |||
| dc4d588675 | |||
| 77434a4c01 | |||
| bf8eb83575 | |||
| 455b24153c | |||
|
|
501f99f28f | ||
|
|
968bf9dd8d | ||
|
|
7d3ef6a7d6 | ||
|
|
b3b543d31d | ||
| 3a35a1e125 | |||
|
|
ad970577b0 | ||
|
|
2fd11f879c | ||
| 2ffe0bcaa0 | |||
| f12d1f01da | |||
| 3a40eda575 | |||
| 65aedb5a49 | |||
| 6a4d9eb544 | |||
| 7e9f73bfa7 | |||
| 1ad732ea4b | |||
| 828ea58b35 | |||
| 1b53c813b4 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -34,3 +34,9 @@
|
|||||||
|
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
tyche
|
||||||
|
tyche-test
|
||||||
|
libtyche.a
|
||||||
|
libtyche.so*
|
||||||
|
lib/compiler/compiler.lua.h
|
||||||
15
.idea/misc.xml
generated
15
.idea/misc.xml
generated
@@ -6,4 +6,19 @@
|
|||||||
<component name="CMakeWorkspace">
|
<component name="CMakeWorkspace">
|
||||||
<contentRoot DIR="$PROJECT_DIR$" />
|
<contentRoot DIR="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MakefileSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<MakefileProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="version" value="2" />
|
||||||
|
</MakefileProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
</project>
|
</project>
|
||||||
2
.idea/tyche.iml
generated
2
.idea/tyche.iml
generated
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module classpath="CIDR" type="CPP_MODULE" version="4" />
|
<module classpath="External" external.linked.project.id="tyche" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="Makefile" type="CPP_MODULE" version="4" />
|
||||||
127
.old/CMakeLists.txt
Normal file
127
.old/CMakeLists.txt
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
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} SHARED
|
||||||
|
src/common/overloaded.hh
|
||||||
|
src/common/bytearray.hh
|
||||||
|
src/common/bytearray.cc
|
||||||
|
src/bytecode/bytecode.cc
|
||||||
|
src/bytecode/bytecode.hh
|
||||||
|
src/bytecode/bytecodeprototype.hh
|
||||||
|
src/bytecode/constant.hh
|
||||||
|
src/vm/code.cc
|
||||||
|
src/vm/code.hh
|
||||||
|
src/vm/instruction.hh
|
||||||
|
src/vm/instruction.cc
|
||||||
|
src/vm/value.cc
|
||||||
|
src/vm/value.hh
|
||||||
|
src/vm/stack.cc
|
||||||
|
src/vm/stack.hh
|
||||||
|
src/vm/vm_exceptions.hh
|
||||||
|
src/vm/vm.cc
|
||||||
|
src/vm/vm.hh
|
||||||
|
src/vm/expr.cc
|
||||||
|
src/vm/expr.hh
|
||||||
|
src/vm/location.hh
|
||||||
|
src/assembler/lexer.cc
|
||||||
|
src/assembler/lexer.hh
|
||||||
|
src/assembler/assembler.cc
|
||||||
|
src/assembler/assembler.hh
|
||||||
|
src/assembler/as_exceptions.hh
|
||||||
|
src/bytecode/bc_exceptions.hh
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
||||||
|
|
||||||
|
#
|
||||||
|
# tests
|
||||||
|
#
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}-vm-test src/vm/tests.cc)
|
||||||
|
target_link_libraries(${PROJECT_NAME}-vm-test lib${PROJECT_NAME} gtest_main)
|
||||||
|
add_test(NAME tyche_vm_test COMMAND ${PROJECT_NAME}-vm-test)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}-as-test src/assembler/tests.cc)
|
||||||
|
target_link_libraries(${PROJECT_NAME}-as-test lib${PROJECT_NAME} gtest_main)
|
||||||
|
add_test(NAME tyche_as_test COMMAND ${PROJECT_NAME}-as-test)
|
||||||
|
|
||||||
|
#
|
||||||
|
# check for leaks
|
||||||
|
#
|
||||||
|
|
||||||
|
add_custom_target(leaks-vm-test)
|
||||||
|
add_custom_command(TARGET leaks-vm-test
|
||||||
|
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}-vm-test
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# installation
|
||||||
|
#
|
||||||
|
|
||||||
|
install(TARGETS lib${CMAKE_PROJECT_NAME} RUNTIME DESTINATION lib)
|
||||||
264
CMakeLists.txt
264
CMakeLists.txt
@@ -1,127 +1,175 @@
|
|||||||
cmake_minimum_required (VERSION 3.24)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
project(tyche
|
project(tyche
|
||||||
VERSION 0.0.1
|
VERSION 0.1.0
|
||||||
DESCRIPTION "An embeddable/standalone programming language"
|
LANGUAGES C
|
||||||
LANGUAGES C CXX ASM)
|
)
|
||||||
|
|
||||||
#
|
# ---------------------------------------------------------------------------
|
||||||
# project options / configuration
|
# Project-wide settings
|
||||||
#
|
# ---------------------------------------------------------------------------
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 17)
|
# Default to Release if not explicitly set (single-config generators only)
|
||||||
set(CMAKE_CXX_STANDARD 23 CACHE STRING "C++ Standard")
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||||
set_property(GLOBAL PROPERTY CXX_EXTENSIONS OFF)
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
|
||||||
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
endif()
|
||||||
set_property(GLOBAL PROPERTY LINK_WHAT_YOU_USE TRUE)
|
|
||||||
|
|
||||||
# warnings / flags
|
# Toggle between STATIC and SHARED for the main library
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
option(BUILD_SHARED_LIB "Build mylib as a shared library" OFF)
|
||||||
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")
|
include(GNUInstallDirs)
|
||||||
set(warnings ${warnings} -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wsuggest-attribute=cold)
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Warning flags loaded from external files
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Each file contains one flag per line, e.g.:
|
||||||
|
# -Wall
|
||||||
|
# -Wextra
|
||||||
|
# -Wshadow
|
||||||
|
# Lines starting with `#` are treated as comments.
|
||||||
|
function(read_flags_file path out_var)
|
||||||
|
set(flags "")
|
||||||
|
if(EXISTS "${path}")
|
||||||
|
file(STRINGS "${path}" lines)
|
||||||
|
foreach(line IN LISTS lines)
|
||||||
|
string(STRIP "${line}" line)
|
||||||
|
if(line AND NOT line MATCHES "^#")
|
||||||
|
list(APPEND flags "${line}")
|
||||||
endif()
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(WARNING "Warnings file not found: ${path}")
|
||||||
|
endif()
|
||||||
|
set(${out_var} "${flags}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(WARNINGS_DIR "${CMAKE_SOURCE_DIR}/cmake/warnings")
|
||||||
|
read_flags_file("${WARNINGS_DIR}/common.txt" COMMON_WARNINGS)
|
||||||
|
read_flags_file("${WARNINGS_DIR}/gcc.txt" GCC_WARNINGS)
|
||||||
|
read_flags_file("${WARNINGS_DIR}/clang.txt" CLANG_WARNINGS)
|
||||||
|
|
||||||
|
# Build a single list of warnings appropriate for the current compiler
|
||||||
|
set(PROJECT_WARNINGS ${COMMON_WARNINGS})
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||||
|
list(APPEND PROJECT_WARNINGS ${GCC_WARNINGS})
|
||||||
|
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") # matches Clang and AppleClang
|
||||||
|
list(APPEND PROJECT_WARNINGS ${CLANG_WARNINGS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# try to use ccache, if available
|
# ---------------------------------------------------------------------------
|
||||||
find_program(CCACHE_PROGRAM ccache)
|
# Per-config compile/link options exposed via an INTERFACE target
|
||||||
if(CCACHE_PROGRAM)
|
# ---------------------------------------------------------------------------
|
||||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
# Anything that links `project_options` inherits warnings + debug/release tweaks.
|
||||||
endif()
|
add_library(project_options INTERFACE)
|
||||||
|
|
||||||
# ignore warnings in imported files
|
# Warnings apply to every build type
|
||||||
set_source_files_properties(${IMGUI_SRC} PROPERTIES COMPILE_FLAGS "-w")
|
target_compile_options(project_options INTERFACE ${PROJECT_WARNINGS})
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
# Debug-only flags
|
||||||
add_compile_options(-ggdb -O0)
|
target_compile_options(project_options INTERFACE
|
||||||
endif()
|
"$<$<CONFIG:Debug>:-O0>"
|
||||||
|
"$<$<CONFIG:Debug>:-g3>"
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
"$<$<CONFIG:Debug>:-fno-omit-frame-pointer>"
|
||||||
set(DEF B_PRODUCTION_MODE=ON)
|
"$<$<CONFIG:Debug>:-fsanitize=address>"
|
||||||
add_compile_options(-Ofast -flto)
|
"$<$<CONFIG:Debug>:-fsanitize=undefined>"
|
||||||
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)
|
target_link_options(project_options INTERFACE
|
||||||
|
"$<$<CONFIG:Debug>:-fsanitize=address>"
|
||||||
#
|
"$<$<CONFIG:Debug>:-fsanitize=undefined>"
|
||||||
# library
|
|
||||||
#
|
|
||||||
|
|
||||||
add_library(lib${PROJECT_NAME} SHARED
|
|
||||||
src/common/overloaded.hh
|
|
||||||
src/common/bytearray.hh
|
|
||||||
src/common/bytearray.cc
|
|
||||||
src/bytecode/bytecode.cc
|
|
||||||
src/bytecode/bytecode.hh
|
|
||||||
src/bytecode/bytecodeprototype.hh
|
|
||||||
src/bytecode/constant.hh
|
|
||||||
src/vm/code.cc
|
|
||||||
src/vm/code.hh
|
|
||||||
src/vm/instruction.hh
|
|
||||||
src/vm/instruction.cc
|
|
||||||
src/vm/value.cc
|
|
||||||
src/vm/value.hh
|
|
||||||
src/vm/stack.cc
|
|
||||||
src/vm/stack.hh
|
|
||||||
src/vm/vm_exceptions.hh
|
|
||||||
src/vm/vm.cc
|
|
||||||
src/vm/vm.hh
|
|
||||||
src/vm/expr.cc
|
|
||||||
src/vm/expr.hh
|
|
||||||
src/vm/location.hh
|
|
||||||
src/assembler/lexer.cc
|
|
||||||
src/assembler/lexer.hh
|
|
||||||
src/assembler/assembler.cc
|
|
||||||
src/assembler/assembler.hh
|
|
||||||
src/assembler/as_exceptions.hh
|
|
||||||
src/bytecode/bc_exceptions.hh
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
# LeakSanitizer is not supported on macOS — gate it on platform.
|
||||||
|
# (On Linux it's already bundled into ASan, but adding the flag is harmless
|
||||||
|
# and makes intent explicit; on macOS it would fail to link.)
|
||||||
|
if(NOT APPLE)
|
||||||
|
target_compile_options(project_options INTERFACE
|
||||||
|
"$<$<CONFIG:Debug>:-fsanitize=leak>"
|
||||||
|
)
|
||||||
|
target_link_options(project_options INTERFACE
|
||||||
|
"$<$<CONFIG:Debug>:-fsanitize=leak>"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
#
|
target_compile_definitions(project_options INTERFACE
|
||||||
# tests
|
"$<$<CONFIG:Debug>:DEBUG_BUILD=1>"
|
||||||
#
|
)
|
||||||
|
|
||||||
|
# Release-only flags
|
||||||
|
target_compile_options(project_options INTERFACE
|
||||||
|
"$<$<CONFIG:Release>:-O3>"
|
||||||
|
"$<$<CONFIG:Release>:-DNDEBUG>"
|
||||||
|
)
|
||||||
|
target_link_options(project_options INTERFACE
|
||||||
|
"$<$<CONFIG:Release>:-Wl,--as-needed>"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# The library
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if(BUILD_SHARED_LIB)
|
||||||
|
set(MYLIB_KIND SHARED)
|
||||||
|
else()
|
||||||
|
set(MYLIB_KIND STATIC)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(mylib ${MYLIB_KIND}
|
||||||
|
src/mylib/foo.c
|
||||||
|
src/mylib/bar.c
|
||||||
|
)
|
||||||
|
add_library(MyProject::mylib ALIAS mylib)
|
||||||
|
|
||||||
|
target_include_directories(mylib
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
PRIVATE
|
||||||
|
"${CMAKE_SOURCE_DIR}/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(mylib PRIVATE project_options)
|
||||||
|
|
||||||
|
set_target_properties(mylib PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
POSITION_INDEPENDENT_CODE ON
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main executable
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
add_executable(myapp src/app/main.c)
|
||||||
|
target_link_libraries(myapp PRIVATE MyProject::mylib project_options)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Test executable
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
add_executable(mylib_tests tests/test_main.c)
|
||||||
|
target_link_libraries(mylib_tests PRIVATE MyProject::mylib project_options)
|
||||||
|
add_test(NAME mylib_tests COMMAND mylib_tests)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc)
|
# ---------------------------------------------------------------------------
|
||||||
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
|
# Install rules (executable and library only — tests excluded)
|
||||||
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test)
|
# ---------------------------------------------------------------------------
|
||||||
|
install(TARGETS mylib myapp
|
||||||
add_executable(${PROJECT_NAME}-vm-test src/vm/tests.cc)
|
EXPORT MyProjectTargets
|
||||||
target_link_libraries(${PROJECT_NAME}-vm-test lib${PROJECT_NAME} gtest_main)
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
add_test(NAME tyche_vm_test COMMAND ${PROJECT_NAME}-vm-test)
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
add_executable(${PROJECT_NAME}-as-test src/assembler/tests.cc)
|
|
||||||
target_link_libraries(${PROJECT_NAME}-as-test lib${PROJECT_NAME} gtest_main)
|
|
||||||
add_test(NAME tyche_as_test COMMAND ${PROJECT_NAME}-as-test)
|
|
||||||
|
|
||||||
#
|
|
||||||
# check for leaks
|
|
||||||
#
|
|
||||||
|
|
||||||
add_custom_target(leaks-vm-test)
|
|
||||||
add_custom_command(TARGET leaks-vm-test
|
|
||||||
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}-vm-test
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
install(DIRECTORY include/
|
||||||
# installation
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
#
|
FILES_MATCHING PATTERN "*.h"
|
||||||
|
)
|
||||||
|
|
||||||
install(TARGETS lib${CMAKE_PROJECT_NAME} RUNTIME DESTINATION lib)
|
# Optional: export a CMake package config so downstream projects can do
|
||||||
|
# find_package(MyProject CONFIG REQUIRED)
|
||||||
|
install(EXPORT MyProjectTargets
|
||||||
|
FILE MyProjectTargets.cmake
|
||||||
|
NAMESPACE MyProject::
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
|
||||||
|
)
|
||||||
|
|||||||
117
Makefile
Normal file
117
Makefile
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#
|
||||||
|
# user overwritable variables
|
||||||
|
#
|
||||||
|
|
||||||
|
# install prefix
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
|
# add functions to debug assembly to console
|
||||||
|
DEBUG_ASSEMBLY ?= 0
|
||||||
|
|
||||||
|
#
|
||||||
|
# internal flags/options
|
||||||
|
#
|
||||||
|
|
||||||
|
# version
|
||||||
|
|
||||||
|
VERSION_MAJOR=0
|
||||||
|
VERSION_MINOR=1
|
||||||
|
|
||||||
|
VERSION=${VERSION_MAJOR}.${VERSION_MINOR}
|
||||||
|
|
||||||
|
# add compiler-specific warnings
|
||||||
|
|
||||||
|
IS_CLANG := $(shell $(CC) -dM -E - < /dev/null | grep -c __clang__)
|
||||||
|
WARNINGS=@config/WARNINGS
|
||||||
|
ADD_DBG_FLAGS=
|
||||||
|
ifeq ($(IS_CLANG),1)
|
||||||
|
WARNINGS += @config/WARNINGS_CLANG
|
||||||
|
else
|
||||||
|
WARNINGS += @config/WARNINGS_GCC
|
||||||
|
ADD_DBG_FLAGS=-fanalyzer
|
||||||
|
endif
|
||||||
|
|
||||||
|
# debug and release flags
|
||||||
|
|
||||||
|
DEBUG_CFLAGS=-O0 -ggdb3 ${WARNINGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined \
|
||||||
|
-fno-sanitize-recover=all -fstack-protector-strong -fstack-clash-protection -fno-common ${ADD_DBG_FLAGS} \
|
||||||
|
-DCHECK_TYCHE_BUGS=1
|
||||||
|
DEBUG_LDFLAGS=-fsanitize=address -fsanitize=undefined
|
||||||
|
|
||||||
|
# apple clang doesn't support -fsanitize=leak
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifneq ($(UNAME_S),Darwin)
|
||||||
|
DEBUG_CFLAGS += -fsanitize=leak
|
||||||
|
DEBUG_LDFLAGS += -fsanitize=leak
|
||||||
|
endif
|
||||||
|
|
||||||
|
RELEASE_CFLAGS=-O3 -flto=auto -march=native -mtune=native -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -fstack-protector-strong
|
||||||
|
RELEASE_LDFLAGS=-flto=auto
|
||||||
|
|
||||||
|
CFLAGS+=-std=c99 -D_GNU_SOURCE -fPIC -fvisibility=hidden -isystem lib/contrib -MMD -MP
|
||||||
|
LDFLAGS+=
|
||||||
|
|
||||||
|
ifeq ($(DEBUG_ASSEMBLY),1)
|
||||||
|
CFLAGS += -DDEBUG_ASSEMBLY
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# generic targets
|
||||||
|
#
|
||||||
|
|
||||||
|
all: tyche libtyche.a libtyche.so.${VERSION}
|
||||||
|
|
||||||
|
check: tyche-test
|
||||||
|
./tyche-test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f tyche libtyche.a libtyche.so* tyche-test **/*.o **/*.d lib/compiler/compiler.lua.h
|
||||||
|
|
||||||
|
install: tyche libtyche.a libtyche.so.${VERSION} lib/tyche.h
|
||||||
|
install -m 644 libtyche.a libtyche.so.${VERSION} ${PREFIX}/lib
|
||||||
|
install tyche ${PREFIX}/bin
|
||||||
|
install -m 644 lib/tyche.h ${PREFIX}/include
|
||||||
|
ln -s ${PREFIX}/lib/libtyche.so.${VERSION} ${PREFIX}/lib/libtyche.so.${VERSION_MAJOR}
|
||||||
|
ln -s ${PREFIX}/lib/libtyche.so.${VERSION_MAJOR} ${PREFIX}/lib/libtyche.so
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f ${PREFIX}/lib/libtyche.* ${PREFIX}/bin/tyche ${PREFIX}/include/tyche.h
|
||||||
|
|
||||||
|
.PHONY: all check clean install uninstall
|
||||||
|
|
||||||
|
#
|
||||||
|
# TODO - temporary, using Lua for compilation for now
|
||||||
|
#
|
||||||
|
CFLAGS+=`pkg-config --cflags lua`
|
||||||
|
LDFLAGS+=`pkg-config --libs lua`
|
||||||
|
lib/compiler/compiler.lua.h: lib/compiler/compiler.lua
|
||||||
|
luac -o lib/compiler/compiler.out lib/compiler/compiler.lua
|
||||||
|
xxd -i lib/compiler/compiler.out > lib/compiler/compiler.lua.h
|
||||||
|
rm lib/compiler/compiler.out
|
||||||
|
lib/compiler.o: lib/compiler.c lib/compiler/compiler.lua.h
|
||||||
|
|
||||||
|
#
|
||||||
|
# executable files
|
||||||
|
#
|
||||||
|
|
||||||
|
LIB_SRC=lib/value.o lib/stack.o lib/array.o lib/table.o lib/heap.o lib/vm.o lib/expr.o lib/compiler.o lib/code.o lib/utils.o
|
||||||
|
|
||||||
|
tyche: CFLAGS += ${RELEASE_CFLAGS}
|
||||||
|
tyche: LDFLAGS += ${RELEASE_LDFLAGS}
|
||||||
|
tyche: src/tyche.o libtyche.a
|
||||||
|
$(CC) -o $@ $^ ${LDFLAGS}
|
||||||
|
strip $@
|
||||||
|
|
||||||
|
tyche-test: CFLAGS += ${DEBUG_CFLAGS} -DDEBUG_ASSEMBLY
|
||||||
|
tyche-test: LDFLAGS += ${DEBUG_LDFLAGS}
|
||||||
|
tyche-test: test/tests.o libtyche.a
|
||||||
|
$(CC) -o $@ $^ ${LDFLAGS} -I../lib
|
||||||
|
|
||||||
|
libtyche.a: ${LIB_SRC}
|
||||||
|
ar rcs $@ $^
|
||||||
|
|
||||||
|
libtyche.so.${VERSION}: LDFLAGS += ${RELEASE_LDFLAGS}
|
||||||
|
libtyche.so.${VERSION}: ${LIB_SRC}
|
||||||
|
$(CC) -shared -o $@ -Wl,-soname,libfoo.so.${VERSION_MAJOR} $^ ${LDFLAGS}
|
||||||
|
|
||||||
|
-include $(LIB_SRC:.o=.d)
|
||||||
104
TODO.md
104
TODO.md
@@ -1,65 +1,57 @@
|
|||||||
## Bytecode
|
## C
|
||||||
|
|
||||||
- [x] Byte array
|
Decisions:
|
||||||
- Auto-expand
|
- How to handle errors
|
||||||
- Add/retrive byte/int/float/string
|
- How values and heap values will be represented
|
||||||
- Should not be larger than the byte array itself
|
- Transparency and log levels
|
||||||
- [x] Bytecode
|
|
||||||
- Add/retrive all types of data
|
|
||||||
- Keeps no memory except for caching
|
|
||||||
- [x] Refactor bytecode code
|
|
||||||
|
|
||||||
Improvements:
|
- [x] Makefile
|
||||||
- [x] Fixed int type (based on opcode)
|
- [x] VALUE
|
||||||
- [x] Constant type (only floats and strings for now)
|
- [x] Stack
|
||||||
|
- [x] Test application
|
||||||
After some additional development:
|
- [x] Heap
|
||||||
- [ ] Bytecode debugging info
|
- [x] Heap value
|
||||||
|
- [x] Strings
|
||||||
|
- [x] Arrays
|
||||||
## VM
|
- [x] Tables
|
||||||
|
- [ ] VM
|
||||||
- [x] VM
|
- [x] (Lua interface) call assembler
|
||||||
|
- [x] (Lua) generate bytecode
|
||||||
|
- [x] Labels
|
||||||
- [x] Code
|
- [x] Code
|
||||||
- [x] Simple bytecode loader
|
- [x] Interpret bytecode (fast)
|
||||||
- [x] Output bytecode format
|
- [x] Execution loop (fast)
|
||||||
- [x] Value object
|
- [ ] VM operations
|
||||||
- [x] Stack object
|
|
||||||
- [x] External interface
|
|
||||||
- [x] Code execution (except functions)
|
|
||||||
- [x] Functions
|
|
||||||
- [x] Print stack
|
|
||||||
- [x] Assembler
|
|
||||||
- [ ] VM execution
|
|
||||||
- [x] Stack operations (nil, integer, float, string, function)
|
|
||||||
- [x] Integer
|
|
||||||
- [x] Float
|
|
||||||
- [x] String
|
|
||||||
- [x] Expressions
|
- [x] Expressions
|
||||||
- [x] Integer
|
- [x] Local variables
|
||||||
- [x] Float
|
- [x] Functions
|
||||||
- [x] String
|
- [x] With parameters
|
||||||
- [ ] Local/global variables
|
- [x] Debug VM execution
|
||||||
- [ ] Functions
|
- [x] Control flow
|
||||||
- [ ] Constants
|
- [x] Recursion
|
||||||
- [ ] Other operations
|
- [ ] Strings
|
||||||
|
- [ ] From constants
|
||||||
|
- [ ] Garbage collection
|
||||||
- [ ] Arrays
|
- [ ] Arrays
|
||||||
- [ ] Iteration
|
- [ ] Garbage collection
|
||||||
- [ ] Expressions
|
|
||||||
- [ ] Tables
|
- [ ] Tables
|
||||||
- [ ] Iteration
|
- [ ] Garbage collection
|
||||||
- [ ] Metatables
|
- [ ] Metatables
|
||||||
- [ ] Expressions
|
- [ ] Iteration
|
||||||
- [ ] Control flow
|
- [ ] Floats (real)
|
||||||
- [ ] Compilation
|
- [ ] Globals (?)
|
||||||
- [ ] Error handling
|
- [ ] Error handling
|
||||||
- [ ] C++ API
|
- [ ] Stack trace in case of errors
|
||||||
- [ ] Run native code on VM
|
- [ ] Closure/upvalues
|
||||||
- [ ] Run tyche code from C++
|
- [ ] Rest of opcodes
|
||||||
- [ ] C API
|
- [ ] Prepare for release
|
||||||
|
- [ ] Documentation and webpage
|
||||||
|
|
||||||
After some additional development:
|
## Future versions
|
||||||
- [ ] Bytecode loader
|
|
||||||
- Combine multiple chunks
|
- [ ] Debugging information
|
||||||
- Resolve function ids, constant ids, etc
|
- [ ] Debugger
|
||||||
- [ ] Upvalues
|
- [ ] Dynamic language
|
||||||
|
- [ ] Support tools
|
||||||
|
- [ ] Editor syntax file, etc
|
||||||
|
- [ ] Static language
|
||||||
|
|||||||
22
config/WARNINGS
Normal file
22
config/WARNINGS
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Wshadow
|
||||||
|
-Wmissing-prototypes
|
||||||
|
-Wcast-qual
|
||||||
|
-Wcast-align
|
||||||
|
-Wconversion
|
||||||
|
-Wsign-conversion
|
||||||
|
-Wdouble-promotion
|
||||||
|
-Wformat=2
|
||||||
|
-Wformat-security
|
||||||
|
-Wnull-dereference
|
||||||
|
-Wstrict-overflow=4
|
||||||
|
-Wundef
|
||||||
|
-Wswitch-enum
|
||||||
|
-Wswitch-default
|
||||||
|
-Wfloat-equal
|
||||||
|
-Wpointer-arith
|
||||||
|
-Wwrite-strings
|
||||||
|
-Wredundant-decls
|
||||||
|
-Wstack-protector
|
||||||
5
config/WARNINGS_CLANG
Normal file
5
config/WARNINGS_CLANG
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-Wshadow-all
|
||||||
|
-Wcomma
|
||||||
|
-Wassign-enum
|
||||||
|
-Wno-newline-eof
|
||||||
|
-Wno-unused-command-line-argument
|
||||||
7
config/WARNINGS_GCC
Normal file
7
config/WARNINGS_GCC
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-Wlogical-op
|
||||||
|
-Wjump-misses-init
|
||||||
|
-Wduplicated-cond
|
||||||
|
-Wduplicated-branches
|
||||||
|
-Wtrampolines
|
||||||
|
-Walloc-zero
|
||||||
|
-Walloca
|
||||||
45
doc/BYTECODE
45
doc/BYTECODE
@@ -3,33 +3,32 @@ Bytecode format
|
|||||||
|
|
||||||
The bytecode file is composed of the following sections:
|
The bytecode file is composed of the following sections:
|
||||||
|
|
||||||
* HEADER: 16-byte header
|
* HEADER: 8-byte header
|
||||||
[0:3]: Magic
|
[0:3]: Magic
|
||||||
[4]: VM format
|
[4:5]: Bytecode version
|
||||||
[rest]: Reserved for future use
|
[6:7]: Reserved for future use
|
||||||
* TABLE_OF_CONTENTS: list of 8 records pointing to each one of the sections
|
|
||||||
Each record (6 bytes):
|
* CONSTANTS
|
||||||
- Pointer to section: 4 bytes
|
[0:3]: Code start address
|
||||||
- Number of records in section: 2 bytes
|
[4:7]: Number of constants
|
||||||
* [0x0] Constants indexes: pointers to each of the constant locations
|
Each constant:
|
||||||
* Table of 4-byte constant indexes with pointer to constant
|
[0]: Type (0 = string, 1 = real)
|
||||||
(counter start at beginning of raw constants)
|
if string:
|
||||||
* [0x1] Functions indexes: Pointer to functions within the code
|
[...]: string (NULL terminated)
|
||||||
[0:3]: function pointer (counter start at the beginning of executable code)
|
if real
|
||||||
[4:5]: number of parameters
|
[1..4]: real
|
||||||
[6:7]: number of local variables
|
|
||||||
[8:b]: function size
|
* CODE
|
||||||
* [0x2] Constants raw data
|
[0:3]: Debug start address (or zero)
|
||||||
* [0x3] Code: executable code
|
[4:7]: Number of functions
|
||||||
* [0x4] Debugging info
|
Each function:
|
||||||
???
|
[0:3] Address of next function
|
||||||
|
[...] Code
|
||||||
|
[0] : Opcode
|
||||||
|
[between 1 and 4] : Operand
|
||||||
|
|
||||||
The max file size is 2 Gb.
|
The max file size is 2 Gb.
|
||||||
|
|
||||||
## Values can be encoded in the following ways:
|
## Values can be encoded in the following ways:
|
||||||
* The type is defined by the operator.
|
* 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
|
* Constant indexes and function ids are encoded as ints
|
||||||
|
|||||||
65
doc/OPCODES
65
doc/OPCODES
@@ -22,16 +22,17 @@ Stack operations:
|
|||||||
a0 c0 e0 pushi [int] Push int
|
a0 c0 e0 pushi [int] Push int
|
||||||
a1 c1 e1 pushc [index] Push constant
|
a1 c1 e1 pushc [index] Push constant
|
||||||
a2 c2 e2 pushf [function] Push function id
|
a2 c2 e2 pushf [function] Push function id
|
||||||
00 pushz Push zero (or false)
|
00 pushn Push nil
|
||||||
01 pusht Push true
|
01 pushz Push zero (or false)
|
||||||
02 newa Push (create) empty array
|
02 pusht Push true
|
||||||
03 newt Push (create) empty table
|
03 newa Push (create) empty array
|
||||||
04 pop
|
04 newt Push (create) empty table
|
||||||
05 dup
|
05 pop
|
||||||
|
06 dup
|
||||||
|
|
||||||
Local variables:
|
Local variables:
|
||||||
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
||||||
ab cb eb set [index] Set value in stack position (set local variable)
|
ae ce ee set [index] Set value in stack position (set local variable)
|
||||||
a4 c4 e4 dupv [index] Duplicate stack value (load local variable)
|
a4 c4 e4 dupv [index] Duplicate stack value (load local variable)
|
||||||
a5 c5 e5 setg [int] Set global variable
|
a5 c5 e5 setg [int] Set global variable
|
||||||
a6 c6 e6 getg [int] Get global variable
|
a6 c6 e6 getg [int] Get global variable
|
||||||
@@ -44,12 +45,12 @@ Function operations:
|
|||||||
Table and array operations:
|
Table and array operations:
|
||||||
16 getkv Get table's value based on key (pull 1 value, push 1 value)
|
16 getkv Get table's value based on key (pull 1 value, push 1 value)
|
||||||
17 setkv Set table's key and value (pull 2 values from stack)
|
17 setkv Set table's key and value (pull 2 values from stack)
|
||||||
18 geta Get array's position value
|
a8 c8 e8 geti Get array's position value
|
||||||
19 seta Set array's position value (pull 2 values from stack)
|
a9 c9 e9 seti Set array's position value
|
||||||
1a appnd Add value to the end of array
|
18 appnd Add value to the end of array
|
||||||
1b next Push the next pair into the stack (for loops)
|
19 next Push the next pair into the stack (for loops)
|
||||||
1c smt Set value metatable
|
1a smt Set value metatable
|
||||||
1d mt Get value metatable
|
1b mt Get value metatable
|
||||||
|
|
||||||
Logical/arithmetic:
|
Logical/arithmetic:
|
||||||
20 sum Sum top 2 values in stack
|
20 sum Sum top 2 values in stack
|
||||||
@@ -57,24 +58,24 @@ Logical/arithmetic:
|
|||||||
22 mul Multiply top 2 values in stack
|
22 mul Multiply top 2 values in stack
|
||||||
23 div Float division
|
23 div Float division
|
||||||
24 idiv Integer division
|
24 idiv Integer division
|
||||||
25 eq Equality
|
25 mod Modulo
|
||||||
26 neq Inequality
|
26 eq Equality
|
||||||
27 lt Less than
|
27 neq Inequality
|
||||||
28 lte Less than or equals
|
28 lt Less than
|
||||||
29 gt Greater than
|
29 lte Less than or equals
|
||||||
2a gte Greater than or equals
|
2a gt Greater than
|
||||||
2b and Bitwise AND
|
2b gte Greater than or equals
|
||||||
2c or Bitwise OR
|
2c and Bitwise AND
|
||||||
2d xor Bitwise XOR
|
2d or Bitwise OR
|
||||||
2e pow Power
|
2e xor Bitwise XOR
|
||||||
2f shl Shift left
|
2f pow Power
|
||||||
30 shr Shift right
|
30 shl Shift left
|
||||||
31 mod Modulo
|
31 shr Shift right
|
||||||
|
|
||||||
Other value operations:
|
Other value operations:
|
||||||
40 len Get table, array or string size
|
40 len Get table, array or string size
|
||||||
41 type Get type from value at the top of the stack
|
41 type Get type from value at the top of the stack
|
||||||
b0 cast [type] Cast type to another type
|
ad cast [type] Cast type to another type
|
||||||
42 ver Return VM version
|
42 ver Return VM version
|
||||||
|
|
||||||
External code:
|
External code:
|
||||||
@@ -82,12 +83,14 @@ External code:
|
|||||||
49 asmbl Assemble code to bytecode format
|
49 asmbl Assemble code to bytecode format
|
||||||
4a load Load bytecode as function (will place function on stack)
|
4a load Load bytecode as function (will place function on stack)
|
||||||
|
|
||||||
Control flow:
|
Control flow (the destination is always a 16-bit field):
|
||||||
a8 c8 e8 bz [pc] Branch if zero
|
ca bz [pc] Branch if zero
|
||||||
a9 c9 e9 bnz [pc] Branch if not zero
|
cb bnz [pc] Branch if not zero
|
||||||
aa ca ea jmp [pc] Unconditional jump
|
cc jmp [pc] Unconditional jump
|
||||||
* Jumps can only happen within the same function.
|
* Jumps can only happen within the same function.
|
||||||
|
|
||||||
|
Memory management:
|
||||||
|
4b gc Call garbage collector
|
||||||
|
|
||||||
Error handling: (0xa0~0xaf)
|
Error handling: (0xa0~0xaf)
|
||||||
???
|
???
|
||||||
|
|||||||
58
lib/array.c
Normal file
58
lib/array.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct Array {
|
||||||
|
size_t n;
|
||||||
|
size_t cap;
|
||||||
|
VALUE* items;
|
||||||
|
};
|
||||||
|
|
||||||
|
Array* array_new(void)
|
||||||
|
{
|
||||||
|
Array* a = xcalloc(1, sizeof(Array));
|
||||||
|
a->n = 0;
|
||||||
|
a->cap = 1;
|
||||||
|
a->items = xmalloc(1 * sizeof(Array));
|
||||||
|
a->items[0] = create_value_nil();
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array_destroy(Array* a)
|
||||||
|
{
|
||||||
|
free(a->items);
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t array_len(Array const* a)
|
||||||
|
{
|
||||||
|
return a->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE array_get(Array const* a, size_t pos)
|
||||||
|
{
|
||||||
|
if (pos >= a->n)
|
||||||
|
return create_value_nil();
|
||||||
|
return a->items[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
void array_set(Array* a, size_t pos, VALUE v)
|
||||||
|
{
|
||||||
|
// extend array
|
||||||
|
if (pos > a->cap - 1) {
|
||||||
|
a->cap *= 2;
|
||||||
|
a->items = xrealloc(a->items, a->cap * sizeof(VALUE));
|
||||||
|
for (size_t i = a->n; i < a->cap; ++i)
|
||||||
|
a->items[i] = create_value_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set item
|
||||||
|
a->items[pos] = v;
|
||||||
|
if (pos + 1 > a->n)
|
||||||
|
a->n = pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array_append(Array* a, VALUE v)
|
||||||
|
{
|
||||||
|
array_set(a, a->n, v);
|
||||||
|
}
|
||||||
285
lib/code.c
Normal file
285
lib/code.c
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
# error Sorry, big endian architectures are not supported at this time.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAGIC 0xa7d6e9b1
|
||||||
|
|
||||||
|
#define VERSION_ADDR 0x04
|
||||||
|
#define CODE_START_ADDR 0x08
|
||||||
|
#define N_CONST_ADDR 0x0c
|
||||||
|
#define CONST_START 0x10
|
||||||
|
|
||||||
|
#define OP_8BIT_OPERAND 0xa0
|
||||||
|
#define OP_16BIT_OPERAND 0xc0
|
||||||
|
#define OP_32BIT_OPERAND 0xe0
|
||||||
|
|
||||||
|
struct Code {
|
||||||
|
uint8_t const* bytecode;
|
||||||
|
size_t bytecode_sz;
|
||||||
|
uint32_t* const_addr;
|
||||||
|
uint32_t fn_count;
|
||||||
|
uint32_t* fn_addr;
|
||||||
|
uint32_t* fn_sz;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code* code_new(void)
|
||||||
|
{
|
||||||
|
Code* code = xcalloc(1, sizeof(Code));
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void code_destroy(Code* code)
|
||||||
|
{
|
||||||
|
free(code->const_addr);
|
||||||
|
free(code->fn_addr);
|
||||||
|
free(code->fn_sz);
|
||||||
|
free(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT code_load_bytecode(Code* code, uint8_t const* bytecode, size_t bytecode_sz)
|
||||||
|
{
|
||||||
|
// TODO - linking
|
||||||
|
|
||||||
|
if (bytecode_sz < 24)
|
||||||
|
return T_ERR_BYTECODE_TOO_SMALL;
|
||||||
|
|
||||||
|
uint32_t magic;
|
||||||
|
memcpy(&magic, bytecode, sizeof(magic));
|
||||||
|
if (magic != MAGIC)
|
||||||
|
return T_ERR_BYTECODE_INVALID_MAGIC;
|
||||||
|
|
||||||
|
code->bytecode = bytecode;
|
||||||
|
code->bytecode_sz = bytecode_sz;
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (size_t i = 0; i < bytecode_sz; ++i) {
|
||||||
|
if (i % 16 == 0)
|
||||||
|
printf("%04X: ", i);
|
||||||
|
printf("%02x ", bytecode[i]);
|
||||||
|
if (i % 16 == 15)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t n_consts = code_n_consts(code);
|
||||||
|
code->const_addr = xcalloc(n_consts, sizeof(uint32_t));
|
||||||
|
uint32_t addr = CONST_START;
|
||||||
|
for (size_t i = 0; i < n_consts; ++i) {
|
||||||
|
code->const_addr[i] = addr;
|
||||||
|
switch (code_const_type(code, i)) {
|
||||||
|
case TC_STRING: {
|
||||||
|
uint32_t sz = (uint32_t) strlen((const char*) &bytecode[code->const_addr[i] + 1]);
|
||||||
|
addr += sz + 2; // 2 = constant type + NULL terminator
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TC_REAL:
|
||||||
|
addr += 5; // 5 = constant type + float
|
||||||
|
break;
|
||||||
|
case TC_INVALID_TYPE:
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr += 4; // skip debug start address
|
||||||
|
memcpy(&code->fn_count, &bytecode[addr], sizeof(uint32_t)); // number of functions
|
||||||
|
addr += 4;
|
||||||
|
|
||||||
|
code->fn_addr = xcalloc(code->fn_count, sizeof(uint32_t));
|
||||||
|
code->fn_sz = xcalloc(code->fn_count, sizeof(uint32_t));
|
||||||
|
code->fn_addr[0] = addr;
|
||||||
|
uint32_t addr_next;
|
||||||
|
for (size_t i = 1; i < code->fn_count; ++i) {
|
||||||
|
memcpy(&addr_next, &bytecode[addr], sizeof(uint32_t));
|
||||||
|
code->fn_sz[i-1] = addr_next - addr - 4;
|
||||||
|
addr = code->fn_addr[i] = addr_next;
|
||||||
|
}
|
||||||
|
memcpy(&addr_next, &bytecode[addr], sizeof(uint32_t));
|
||||||
|
code->fn_sz[code->fn_count-1] = addr_next - addr - 4;
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t code_n_consts(Code const* code)
|
||||||
|
{
|
||||||
|
uint32_t n_consts; memcpy(&n_consts, &code->bytecode[N_CONST_ADDR], sizeof(uint32_t));
|
||||||
|
return n_consts;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_CONST_TYPE code_const_type(Code const* code, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t t = code->bytecode[code->const_addr[n]];
|
||||||
|
if (t >= TC_INVALID_TYPE)
|
||||||
|
return TC_INVALID_TYPE;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
T_REAL code_const_real(Code const* code, size_t n)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
memcpy(&f, &code->bytecode[code->const_addr[n] + 1], sizeof(float));
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* code_const_string(Code const* code, size_t n)
|
||||||
|
{
|
||||||
|
return (const char*) &code->bytecode[code->const_addr[n] + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t code_n_functions(Code const* code)
|
||||||
|
{
|
||||||
|
return code->fn_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t code_function_sz(Code const* code, uint32_t f_id)
|
||||||
|
{
|
||||||
|
return code->fn_sz[f_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc)
|
||||||
|
{
|
||||||
|
uint32_t addr = code->fn_addr[function_id] + 4 + pc;
|
||||||
|
uint8_t opcode = code->bytecode[addr];
|
||||||
|
int32_t operand = 0;
|
||||||
|
uint8_t sz = 1;
|
||||||
|
|
||||||
|
if (opcode >= OP_8BIT_OPERAND && opcode < OP_16BIT_OPERAND) {
|
||||||
|
operand = (int8_t) code->bytecode[addr + 1];
|
||||||
|
sz = 2;
|
||||||
|
} else if (opcode >= OP_16BIT_OPERAND && opcode < OP_32BIT_OPERAND) {
|
||||||
|
opcode -= 0x20;
|
||||||
|
operand = (int16_t) ((uint16_t) code->bytecode[addr + 1] |
|
||||||
|
(uint16_t) (code->bytecode[addr + 2] << 8));
|
||||||
|
sz = 3;
|
||||||
|
} else if (opcode >= OP_32BIT_OPERAND) {
|
||||||
|
opcode -= 0x40;
|
||||||
|
operand = (int32_t) ((uint32_t) code->bytecode[addr + 1] |
|
||||||
|
(uint32_t) (code->bytecode[addr + 2] << 8) |
|
||||||
|
(uint32_t) (code->bytecode[addr + 3] << 16) |
|
||||||
|
(uint32_t) (code->bytecode[addr + 4] << 24));
|
||||||
|
sz = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Instruction) {
|
||||||
|
.operator = (TYC_INST) opcode,
|
||||||
|
.operand = operand,
|
||||||
|
.sz = sz,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
|
||||||
|
void code_debug_bytecode(Code const* code)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < code->bytecode_sz; ++i) {
|
||||||
|
if (i % 16 == 0)
|
||||||
|
printf("%04X : ", i);
|
||||||
|
printf("%02X ", code->bytecode[i]);
|
||||||
|
if (i % 16 == 15)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void code_decompile(Code const* code)
|
||||||
|
{
|
||||||
|
if (code_n_consts(code) > 0)
|
||||||
|
printf(".const\n");
|
||||||
|
|
||||||
|
for (size_t const_id = 0; const_id < code_n_consts(code); ++const_id) {
|
||||||
|
TYC_CONST_TYPE type = code_const_type(code, const_id);
|
||||||
|
if (type == TC_STRING)
|
||||||
|
printf(" %03zu: \"%s\"\n", const_id, code_const_string(code, const_id));
|
||||||
|
else if (type == TC_REAL)
|
||||||
|
printf(" %03zu: %f\n", const_id, (double) code_const_real(code, const_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t f_id = 0; f_id < code_n_functions(code); ++f_id) {
|
||||||
|
printf(".func %d\n", f_id);
|
||||||
|
uint32_t pc = 0;
|
||||||
|
while (pc < code_function_sz(code, f_id)) {
|
||||||
|
Instruction inst = code_next_instruction(code, f_id, pc);
|
||||||
|
char buf[50];
|
||||||
|
code_parse_instruction(inst, buf, sizeof buf);
|
||||||
|
printf(" %s ; %d\n", buf, pc);
|
||||||
|
pc += inst.sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void code_parse_instruction(Instruction inst, char* outbuf, size_t sz)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
switch (inst.operator) {
|
||||||
|
case TO_PUSHI: n = snprintf(outbuf, sz, "pushi "); break;
|
||||||
|
case TO_PUSHC: n = snprintf(outbuf, sz, "pushc "); break;
|
||||||
|
case TO_PUSHF: n = snprintf(outbuf, sz, "pushf "); break;
|
||||||
|
case TO_PUSHN: n = snprintf(outbuf, sz, "pushn "); break;
|
||||||
|
case TO_PUSHZ: n = snprintf(outbuf, sz, "pushz "); break;
|
||||||
|
case TO_PUSHT: n = snprintf(outbuf, sz, "pusht "); break;
|
||||||
|
case TO_NEWA: n = snprintf(outbuf, sz, "newa "); break;
|
||||||
|
case TO_NEWT: n = snprintf(outbuf, sz, "newt "); break;
|
||||||
|
case TO_POP: n = snprintf(outbuf, sz, "pop "); break;
|
||||||
|
case TO_DUP: n = snprintf(outbuf, sz, "dup "); break;
|
||||||
|
case TO_PUSHV: n = snprintf(outbuf, sz, "pushv "); break;
|
||||||
|
case TO_SET: n = snprintf(outbuf, sz, "set "); break;
|
||||||
|
case TO_DUPV: n = snprintf(outbuf, sz, "dupv "); break;
|
||||||
|
case TO_SETG: n = snprintf(outbuf, sz, "setg "); break;
|
||||||
|
case TO_GETG: n = snprintf(outbuf, sz, "getg "); break;
|
||||||
|
case TO_CALL: n = snprintf(outbuf, sz, "call "); break;
|
||||||
|
case TO_RET: n = snprintf(outbuf, sz, "ret "); break;
|
||||||
|
case TO_RETI: n = snprintf(outbuf, sz, "reti "); break;
|
||||||
|
case TO_GETKV: n = snprintf(outbuf, sz, "getkv "); break;
|
||||||
|
case TO_SETKV: n = snprintf(outbuf, sz, "setkv "); break;
|
||||||
|
case TO_GETI: n = snprintf(outbuf, sz, "geti "); break;
|
||||||
|
case TO_SETI: n = snprintf(outbuf, sz, "seti "); break;
|
||||||
|
case TO_APPND: n = snprintf(outbuf, sz, "appnd "); break;
|
||||||
|
case TO_NEXT: n = snprintf(outbuf, sz, "next "); break;
|
||||||
|
case TO_SMT: n = snprintf(outbuf, sz, "smt "); break;
|
||||||
|
case TO_MT: n = snprintf(outbuf, sz, "mt "); break;
|
||||||
|
case TO_SUM: n = snprintf(outbuf, sz, "sum "); break;
|
||||||
|
case TO_SUB: n = snprintf(outbuf, sz, "sub "); break;
|
||||||
|
case TO_MUL: n = snprintf(outbuf, sz, "mul "); break;
|
||||||
|
case TO_DIV: n = snprintf(outbuf, sz, "div "); break;
|
||||||
|
case TO_IDIV: n = snprintf(outbuf, sz, "idiv "); break;
|
||||||
|
case TO_MOD: n = snprintf(outbuf, sz, "mod "); break;
|
||||||
|
case TO_EQ: n = snprintf(outbuf, sz, "eq "); break;
|
||||||
|
case TO_NEQ: n = snprintf(outbuf, sz, "neq "); break;
|
||||||
|
case TO_LT: n = snprintf(outbuf, sz, "lt "); break;
|
||||||
|
case TO_LTE: n = snprintf(outbuf, sz, "lte "); break;
|
||||||
|
case TO_GT: n = snprintf(outbuf, sz, "gt "); break;
|
||||||
|
case TO_GTE: n = snprintf(outbuf, sz, "gte "); break;
|
||||||
|
case TO_AND: n = snprintf(outbuf, sz, "and "); break;
|
||||||
|
case TO_OR: n = snprintf(outbuf, sz, "or "); break;
|
||||||
|
case TO_XOR: n = snprintf(outbuf, sz, "xor "); break;
|
||||||
|
case TO_POW: n = snprintf(outbuf, sz, "pow "); break;
|
||||||
|
case TO_SHL: n = snprintf(outbuf, sz, "shl "); break;
|
||||||
|
case TO_SHR: n = snprintf(outbuf, sz, "shr "); break;
|
||||||
|
case TO_LEN: n = snprintf(outbuf, sz, "len "); break;
|
||||||
|
case TO_TYPE: n = snprintf(outbuf, sz, "type "); break;
|
||||||
|
case TO_CAST: n = snprintf(outbuf, sz, "cast "); break;
|
||||||
|
case TO_VER: n = snprintf(outbuf, sz, "ver "); break;
|
||||||
|
case TO_CMPL: n = snprintf(outbuf, sz, "cmpl "); break;
|
||||||
|
case TO_ASMBL: n = snprintf(outbuf, sz, "asmbl "); break;
|
||||||
|
case TO_LOAD: n = snprintf(outbuf, sz, "load "); break;
|
||||||
|
case TO_BZ: n = snprintf(outbuf, sz, "bz "); break;
|
||||||
|
case TO_BNZ: n = snprintf(outbuf, sz, "bnz "); break;
|
||||||
|
case TO_JMP: n = snprintf(outbuf, sz, "jmp "); break;
|
||||||
|
case TO_GC: n = snprintf(outbuf, sz, "gc "); break;
|
||||||
|
default: n = snprintf(outbuf, sz, "??? "); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst.operator >= OP_8BIT_OPERAND)
|
||||||
|
snprintf(&outbuf[n], sz + (size_t) n, "%2d", inst.operand);
|
||||||
|
else
|
||||||
|
snprintf(&outbuf[n], sz + (size_t) n, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
44
lib/compiler.c
Normal file
44
lib/compiler.c
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#include "compiler/compiler.lua.h"
|
||||||
|
|
||||||
|
TYC_RESULT code_assemble(const char* code, uint8_t** bytecode, size_t* bytecode_sz)
|
||||||
|
{
|
||||||
|
lua_State* L = luaL_newstate();
|
||||||
|
luaL_openlibs(L);
|
||||||
|
|
||||||
|
int r = luaL_loadbufferx(L, (const char *) lib_compiler_compiler_out, lib_compiler_compiler_out_len, "compiler", "b");
|
||||||
|
if (r == LUA_ERRSYNTAX)
|
||||||
|
abort();
|
||||||
|
else if (r == LUA_ERRMEM)
|
||||||
|
out_of_memory();
|
||||||
|
|
||||||
|
lua_call(L, 0, 1);
|
||||||
|
|
||||||
|
lua_pushstring(L, code);
|
||||||
|
r = lua_pcall(L, 1, 1, 0);
|
||||||
|
if (r == LUA_ERRMEM) {
|
||||||
|
out_of_memory();
|
||||||
|
} else if (r == LUA_ERRERR) {
|
||||||
|
abort();
|
||||||
|
} else if (r == LUA_ERRRUN) {
|
||||||
|
fprintf(stderr, "%s\n", lua_tostring(L, -1));
|
||||||
|
return T_ERR_ASSEMBLER_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lua_isstring(L, -1))
|
||||||
|
abort();
|
||||||
|
*bytecode_sz = (size_t) luaL_len(L, -1);
|
||||||
|
*bytecode = malloc(*bytecode_sz);
|
||||||
|
memcpy(*bytecode, lua_tostring(L, -1), *bytecode_sz);
|
||||||
|
|
||||||
|
lua_close(L);
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
302
lib/compiler/compiler.lua
Normal file
302
lib/compiler/compiler.lua
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
----------------------
|
||||||
|
-- --
|
||||||
|
-- PARSER --
|
||||||
|
-- --
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
local function parse_assembly(source)
|
||||||
|
local proto = {
|
||||||
|
constants = {},
|
||||||
|
functions = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local section = ''
|
||||||
|
local current_f_id = 0
|
||||||
|
|
||||||
|
local next_label = nil
|
||||||
|
for line in source:gmatch("([^\n]+)") do
|
||||||
|
local line = line:gsub("%s*;.*$", "") -- remove comments
|
||||||
|
line = line:match("^%s*(.-)%s*$") -- trim
|
||||||
|
|
||||||
|
if #line == 0 then goto continue end
|
||||||
|
|
||||||
|
if line == ".const" then
|
||||||
|
section = 'const'
|
||||||
|
elseif line:match("%.func%s+%d+") then
|
||||||
|
section = 'function'
|
||||||
|
local f_id = tonumber(line:match("%.func%s+(%d+)"))
|
||||||
|
proto.functions[f_id] = {}
|
||||||
|
current_f_id = f_id
|
||||||
|
elseif section == 'const' then
|
||||||
|
local k, v = line:match("^%s*(%d+)%s*:%s*(.+)$")
|
||||||
|
if not k then error("Invalid row for constant: " .. line) end
|
||||||
|
if v:sub(1, 1) == '"' then
|
||||||
|
proto.constants[tonumber(k)] = line:match('"(.*)"')
|
||||||
|
else
|
||||||
|
proto.constants[tonumber(k)] = tonumber(v)
|
||||||
|
end
|
||||||
|
elseif section == 'function' then
|
||||||
|
local regexes = {
|
||||||
|
"^%s*(%a+)%s+(-?%d+)%s*$", -- instruction + parameter
|
||||||
|
"^%s*(%a+)%s+(@[%a_][%a%d_]*)%s*$", -- instruction + label
|
||||||
|
"^%s*(%a+)%s*$", -- instruction only
|
||||||
|
"^(@[%a_][%a%d_]*):%s*$", -- label
|
||||||
|
}
|
||||||
|
local match = false
|
||||||
|
for i, regex in ipairs(regexes) do
|
||||||
|
local inst, par = line:match(regex)
|
||||||
|
if inst then
|
||||||
|
match = true
|
||||||
|
if i == 1 then -- instruction + parameter
|
||||||
|
table.insert(proto.functions[current_f_id], { inst, tonumber(par), labels = next_label })
|
||||||
|
elseif i == 2 then -- instruction + label
|
||||||
|
table.insert(proto.functions[current_f_id], { inst, par, labels = next_label })
|
||||||
|
elseif i == 3 then -- instruction only
|
||||||
|
table.insert(proto.functions[current_f_id], { inst, labels = next_label })
|
||||||
|
elseif i == 4 then -- label
|
||||||
|
if not next_label then
|
||||||
|
next_label = { inst }
|
||||||
|
else
|
||||||
|
table.insert(next_label, inst)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if i ~= 4 then
|
||||||
|
next_label = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not match then error("Invalid instruction: " .. line) end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
return proto
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
-- --
|
||||||
|
-- BINARY --
|
||||||
|
-- --
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
local instructions = {
|
||||||
|
-- stack operations
|
||||||
|
pushi = 0xa0,
|
||||||
|
pushc = 0xa1,
|
||||||
|
pushf = 0xa2,
|
||||||
|
pushn = 0x00,
|
||||||
|
pushz = 0x01,
|
||||||
|
pusht = 0x02,
|
||||||
|
newa = 0x03,
|
||||||
|
newt = 0x04,
|
||||||
|
pop = 0x05,
|
||||||
|
dup = 0x06,
|
||||||
|
|
||||||
|
-- local variables
|
||||||
|
pushv = 0xa3,
|
||||||
|
set = 0xae,
|
||||||
|
dupv = 0xa4,
|
||||||
|
setg = 0xa5,
|
||||||
|
getg = 0xa6,
|
||||||
|
|
||||||
|
-- function operations
|
||||||
|
call = 0xa7,
|
||||||
|
ret = 0x10,
|
||||||
|
reti = 0x11,
|
||||||
|
|
||||||
|
-- table and array operations
|
||||||
|
getkv = 0x16,
|
||||||
|
setkv = 0x17,
|
||||||
|
geti = 0xa8,
|
||||||
|
seti = 0xa9,
|
||||||
|
appnd = 0x18,
|
||||||
|
next = 0x19,
|
||||||
|
smt = 0x1a,
|
||||||
|
mt = 0x1b,
|
||||||
|
|
||||||
|
-- logical/arithmetic
|
||||||
|
sum = 0x20,
|
||||||
|
sub = 0x21,
|
||||||
|
mul = 0x22,
|
||||||
|
div = 0x23,
|
||||||
|
idiv = 0x24,
|
||||||
|
mod = 0x25,
|
||||||
|
eq = 0x26,
|
||||||
|
neq = 0x27,
|
||||||
|
lt = 0x28,
|
||||||
|
lte = 0x29,
|
||||||
|
gt = 0x2a,
|
||||||
|
gte = 0x2b,
|
||||||
|
['and'] = 0x2c,
|
||||||
|
['or'] = 0x2d,
|
||||||
|
xor = 0x2e,
|
||||||
|
pow = 0x2f,
|
||||||
|
shl = 0x30,
|
||||||
|
shr = 0x31,
|
||||||
|
|
||||||
|
-- other value operations
|
||||||
|
len = 0x40,
|
||||||
|
type = 0x41,
|
||||||
|
cast = 0xad,
|
||||||
|
ver = 0x42,
|
||||||
|
|
||||||
|
-- external code
|
||||||
|
cmpl = 0x48,
|
||||||
|
asmbl = 0x49,
|
||||||
|
load = 0x4a,
|
||||||
|
|
||||||
|
-- control flow
|
||||||
|
bz = 0xca,
|
||||||
|
bnz = 0xcb,
|
||||||
|
jmp = 0xcc,
|
||||||
|
|
||||||
|
-- memory management
|
||||||
|
gc = 0x4b,
|
||||||
|
}
|
||||||
|
|
||||||
|
local MAGIC = 0xa7d6e9b1
|
||||||
|
local VERSION = 1
|
||||||
|
|
||||||
|
local function assemble(proto)
|
||||||
|
local bin = {}
|
||||||
|
|
||||||
|
local push8 = function(data)
|
||||||
|
table.insert(bin, data & 0xff)
|
||||||
|
end
|
||||||
|
|
||||||
|
local push16 = function(data)
|
||||||
|
table.insert(bin, data & 0xff)
|
||||||
|
table.insert(bin, (data >> 8) & 0xff)
|
||||||
|
return #bin - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local push32 = function(data)
|
||||||
|
table.insert(bin, data & 0xff)
|
||||||
|
table.insert(bin, (data >> 8) & 0xff)
|
||||||
|
table.insert(bin, (data >> 16) & 0xff)
|
||||||
|
table.insert(bin, (data >> 24) & 0xff)
|
||||||
|
return #bin - 3
|
||||||
|
end
|
||||||
|
|
||||||
|
local replace16 = function(pos, data)
|
||||||
|
bin[pos] = data & 0xff
|
||||||
|
bin[pos + 1] = (data >> 8) & 0xff
|
||||||
|
end
|
||||||
|
|
||||||
|
local replace32 = function(pos, data)
|
||||||
|
bin[pos] = data & 0xff
|
||||||
|
bin[pos + 1] = (data >> 8) & 0xff
|
||||||
|
bin[pos + 2] = (data >> 16) & 0xff
|
||||||
|
bin[pos + 3] = (data >> 24) & 0xff
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float_to_bits(f)
|
||||||
|
local bytes = string.pack("<f", f)
|
||||||
|
return string.unpack("<I4", bytes)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- header
|
||||||
|
push32(MAGIC)
|
||||||
|
push16(VERSION)
|
||||||
|
push16(0)
|
||||||
|
|
||||||
|
-- constants
|
||||||
|
local code_addr_pos = push32(0) -- code address, to be replaced
|
||||||
|
|
||||||
|
-- number of constants
|
||||||
|
if proto.constants[0] then
|
||||||
|
push32(#proto.constants + 1)
|
||||||
|
else
|
||||||
|
push32(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=0,#proto.constants do
|
||||||
|
local const = proto.constants[i]
|
||||||
|
if type(const) == 'string' then
|
||||||
|
push8(0) -- string type
|
||||||
|
for c in const:gmatch('.') do
|
||||||
|
push8(c:byte())
|
||||||
|
end
|
||||||
|
push8(0) -- string terminator
|
||||||
|
elseif type(const) == 'number' then
|
||||||
|
push8(1) -- float type
|
||||||
|
push32(float_to_bits(const))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
replace32(code_addr_pos, #bin)
|
||||||
|
|
||||||
|
-- code
|
||||||
|
push32(0) -- debug address (TODO)
|
||||||
|
push32(#proto.functions + 1) -- number of functions
|
||||||
|
|
||||||
|
for i = 0, #proto.functions do
|
||||||
|
|
||||||
|
local func = proto.functions[i]
|
||||||
|
local next_function_pos = #bin + 1
|
||||||
|
push32(0) -- to be replaced with next function address
|
||||||
|
|
||||||
|
local function_start = #bin
|
||||||
|
local labels = {}
|
||||||
|
|
||||||
|
for _, inst in ipairs(func) do
|
||||||
|
-- add labels
|
||||||
|
if inst.labels then
|
||||||
|
for _, lbl in ipairs(inst.labels) do
|
||||||
|
labels[lbl] = #bin - function_start
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local opcode, operand = instructions[inst[1]], inst[2]
|
||||||
|
if opcode == nil then error("Unknown instruction " .. inst[1]) end
|
||||||
|
if operand == nil then
|
||||||
|
push8(opcode)
|
||||||
|
elseif type(operand) == 'string' then
|
||||||
|
push8(opcode)
|
||||||
|
table.insert(bin, operand) -- insert the label
|
||||||
|
push8(0) -- byte to be replaced (label is 16-bit)
|
||||||
|
else
|
||||||
|
if opcode >= 0xc0 and opcode < 0xe0 then
|
||||||
|
push8(opcode)
|
||||||
|
push16(operand)
|
||||||
|
elseif operand >= -128 and operand <= 127 then
|
||||||
|
push8(opcode)
|
||||||
|
push8(operand)
|
||||||
|
elseif operand >= -32768 and operand <= 32767 then
|
||||||
|
push8(opcode + 0x20)
|
||||||
|
push16(operand)
|
||||||
|
else
|
||||||
|
push8(opcode + 0x40)
|
||||||
|
push32(operand)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- replace labels
|
||||||
|
for i=function_start,#bin do
|
||||||
|
if type(bin[i]) == 'string' then
|
||||||
|
local label_addr = labels[bin[i]]
|
||||||
|
if label_addr == nil then error("Label not found: " .. bin[i]) end
|
||||||
|
replace16(i, label_addr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
replace32(next_function_pos, #bin)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- for _, b in ipairs(bin) do io.write(string.format("%02x", b) .. ' ') end; print()
|
||||||
|
return string.char(table.unpack(bin))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
-- --
|
||||||
|
-- GENERIC --
|
||||||
|
-- --
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
return function(source)
|
||||||
|
return assemble(parse_assembly(source))
|
||||||
|
end
|
||||||
627
lib/contrib/khash.h
Normal file
627
lib/contrib/khash.h
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
/* The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
An example:
|
||||||
|
|
||||||
|
#include "khash.h"
|
||||||
|
KHASH_MAP_INIT_INT(32, char)
|
||||||
|
int main() {
|
||||||
|
int ret, is_missing;
|
||||||
|
khiter_t k;
|
||||||
|
khash_t(32) *h = kh_init(32);
|
||||||
|
k = kh_put(32, h, 5, &ret);
|
||||||
|
kh_value(h, k) = 10;
|
||||||
|
k = kh_get(32, h, 10);
|
||||||
|
is_missing = (k == kh_end(h));
|
||||||
|
k = kh_get(32, h, 5);
|
||||||
|
kh_del(32, h, k);
|
||||||
|
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||||
|
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||||
|
kh_destroy(32, h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
2013-05-02 (0.2.8):
|
||||||
|
|
||||||
|
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||||
|
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||||
|
hashing on cache performance and is more robust than linear probing.
|
||||||
|
|
||||||
|
In theory, double hashing should be more robust than quadratic probing.
|
||||||
|
However, my implementation is probably not for large hash tables, because
|
||||||
|
the second hash function is closely tied to the first hash function,
|
||||||
|
which reduce the effectiveness of double hashing.
|
||||||
|
|
||||||
|
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||||
|
|
||||||
|
2011-12-29 (0.2.7):
|
||||||
|
|
||||||
|
* Minor code clean up; no actual effect.
|
||||||
|
|
||||||
|
2011-09-16 (0.2.6):
|
||||||
|
|
||||||
|
* The capacity is a power of 2. This seems to dramatically improve the
|
||||||
|
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||||
|
|
||||||
|
- http://code.google.com/p/ulib/
|
||||||
|
- http://nothings.org/computer/judy/
|
||||||
|
|
||||||
|
* Allow to optionally use linear probing which usually has better
|
||||||
|
performance for random input. Double hashing is still the default as it
|
||||||
|
is more robust to certain non-random input.
|
||||||
|
|
||||||
|
* Added Wang's integer hash function (not used by default). This hash
|
||||||
|
function is more robust to certain non-random input.
|
||||||
|
|
||||||
|
2011-02-14 (0.2.5):
|
||||||
|
|
||||||
|
* Allow to declare global functions.
|
||||||
|
|
||||||
|
2009-09-26 (0.2.4):
|
||||||
|
|
||||||
|
* Improve portability
|
||||||
|
|
||||||
|
2008-09-19 (0.2.3):
|
||||||
|
|
||||||
|
* Corrected the example
|
||||||
|
* Improved interfaces
|
||||||
|
|
||||||
|
2008-09-11 (0.2.2):
|
||||||
|
|
||||||
|
* Improved speed a little in kh_put()
|
||||||
|
|
||||||
|
2008-09-10 (0.2.1):
|
||||||
|
|
||||||
|
* Added kh_clear()
|
||||||
|
* Fixed a compiling error
|
||||||
|
|
||||||
|
2008-09-02 (0.2.0):
|
||||||
|
|
||||||
|
* Changed to token concatenation which increases flexibility.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.2):
|
||||||
|
|
||||||
|
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.1):
|
||||||
|
|
||||||
|
* Added destructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __AC_KHASH_H
|
||||||
|
#define __AC_KHASH_H
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
|
||||||
|
Generic hash table library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AC_VERSION_KHASH_H "0.2.8"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* compiler specific configuration */
|
||||||
|
|
||||||
|
#if UINT_MAX == 0xffffffffu
|
||||||
|
typedef unsigned int khint32_t;
|
||||||
|
#elif ULONG_MAX == 0xffffffffu
|
||||||
|
typedef unsigned long khint32_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ULONG_MAX == ULLONG_MAX
|
||||||
|
typedef unsigned long khint64_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long khint64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef kh_inline
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define kh_inline __inline
|
||||||
|
#else
|
||||||
|
#define kh_inline inline
|
||||||
|
#endif
|
||||||
|
#endif /* kh_inline */
|
||||||
|
|
||||||
|
#ifndef klib_unused
|
||||||
|
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||||
|
#define klib_unused __attribute__ ((__unused__))
|
||||||
|
#else
|
||||||
|
#define klib_unused
|
||||||
|
#endif
|
||||||
|
#endif /* klib_unused */
|
||||||
|
|
||||||
|
typedef khint32_t khint_t;
|
||||||
|
typedef khint_t khiter_t;
|
||||||
|
|
||||||
|
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||||
|
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||||
|
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||||
|
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||||
|
|
||||||
|
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||||
|
|
||||||
|
#ifndef kroundup32
|
||||||
|
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef kcalloc
|
||||||
|
#define kcalloc(N,Z) calloc(N,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kmalloc
|
||||||
|
#define kmalloc(Z) malloc(Z)
|
||||||
|
#endif
|
||||||
|
#ifndef krealloc
|
||||||
|
#define krealloc(P,Z) realloc(P,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kfree
|
||||||
|
#define kfree(P) free(P)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const double __ac_HASH_UPPER = 0.77;
|
||||||
|
|
||||||
|
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
typedef struct kh_##name##_s { \
|
||||||
|
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||||
|
khint32_t *flags; \
|
||||||
|
khkey_t *keys; \
|
||||||
|
khval_t *vals; \
|
||||||
|
} kh_##name##_t;
|
||||||
|
|
||||||
|
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||||
|
extern kh_##name##_t *kh_init_##name(void); \
|
||||||
|
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||||
|
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||||
|
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||||
|
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||||
|
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||||
|
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||||
|
|
||||||
|
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||||
|
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h) { \
|
||||||
|
kfree((void *)h->keys); kfree(h->flags); \
|
||||||
|
kfree((void *)h->vals); \
|
||||||
|
kfree(h); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h && h->flags) { \
|
||||||
|
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||||
|
h->size = h->n_occupied = 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||||
|
{ \
|
||||||
|
if (h->n_buckets) { \
|
||||||
|
khint_t k, i, last, mask, step = 0; \
|
||||||
|
mask = h->n_buckets - 1; \
|
||||||
|
k = __hash_func(key); i = k & mask; \
|
||||||
|
last = i; \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
i = (i + (++step)) & mask; \
|
||||||
|
if (i == last) return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||||
|
} else return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||||
|
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||||
|
khint32_t *new_flags = 0; \
|
||||||
|
khint_t j = 1; \
|
||||||
|
{ \
|
||||||
|
kroundup32(new_n_buckets); \
|
||||||
|
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||||
|
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||||
|
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||||
|
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (!new_flags) return -1; \
|
||||||
|
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||||
|
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||||
|
h->keys = new_keys; \
|
||||||
|
if (kh_is_map) { \
|
||||||
|
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||||
|
h->vals = new_vals; \
|
||||||
|
} \
|
||||||
|
} /* otherwise shrink */ \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (j) { /* rehashing is needed */ \
|
||||||
|
for (j = 0; j != h->n_buckets; ++j) { \
|
||||||
|
if (__ac_iseither(h->flags, j) == 0) { \
|
||||||
|
khkey_t key = h->keys[j]; \
|
||||||
|
khval_t val; \
|
||||||
|
khint_t new_mask; \
|
||||||
|
new_mask = new_n_buckets - 1; \
|
||||||
|
if (kh_is_map) val = h->vals[j]; \
|
||||||
|
__ac_set_isdel_true(h->flags, j); \
|
||||||
|
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||||
|
khint_t k, i, step = 0; \
|
||||||
|
k = __hash_func(key); \
|
||||||
|
i = k & new_mask; \
|
||||||
|
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||||
|
__ac_set_isempty_false(new_flags, i); \
|
||||||
|
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||||
|
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||||
|
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||||
|
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||||
|
} else { /* write the element and jump out of the loop */ \
|
||||||
|
h->keys[i] = key; \
|
||||||
|
if (kh_is_map) h->vals[i] = val; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||||
|
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
} \
|
||||||
|
kfree(h->flags); /* free the working space */ \
|
||||||
|
h->flags = new_flags; \
|
||||||
|
h->n_buckets = new_n_buckets; \
|
||||||
|
h->n_occupied = h->size; \
|
||||||
|
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||||
|
{ \
|
||||||
|
khint_t x; \
|
||||||
|
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||||
|
if (h->n_buckets > (h->size<<1)) { \
|
||||||
|
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||||
|
{ \
|
||||||
|
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||||
|
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||||
|
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||||
|
else { \
|
||||||
|
last = i; \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
if (__ac_isdel(h->flags, i)) site = i; \
|
||||||
|
i = (i + (++step)) & mask; \
|
||||||
|
if (i == last) { x = site; break; } \
|
||||||
|
} \
|
||||||
|
if (x == h->n_buckets) { \
|
||||||
|
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||||
|
else x = i; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; ++h->n_occupied; \
|
||||||
|
*ret = 1; \
|
||||||
|
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; \
|
||||||
|
*ret = 2; \
|
||||||
|
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||||
|
return x; \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||||
|
{ \
|
||||||
|
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||||
|
__ac_set_isdel_true(h->flags, x); \
|
||||||
|
--h->size; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||||
|
|
||||||
|
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer hash function
|
||||||
|
@param key The integer [khint32_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer hash function
|
||||||
|
@param key The integer [khint64_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract const char* hash function
|
||||||
|
@param s Pointer to a null terminated string
|
||||||
|
@return The hash value
|
||||||
|
*/
|
||||||
|
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||||
|
{
|
||||||
|
khint_t h = (khint_t)*s;
|
||||||
|
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
/*! @function
|
||||||
|
@abstract Another interface to const char* hash function
|
||||||
|
@param key Pointer to a null terminated string [const char*]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Const char* comparison function
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||||
|
|
||||||
|
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||||
|
{
|
||||||
|
key += ~(key << 15);
|
||||||
|
key ^= (key >> 10);
|
||||||
|
key += (key << 3);
|
||||||
|
key ^= (key >> 6);
|
||||||
|
key += ~(key << 11);
|
||||||
|
key ^= (key >> 16);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||||
|
|
||||||
|
/* --- END OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/* Other convenient macros... */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@abstract Type of the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define khash_t(name) kh_##name##_t
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Initiate a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@return Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_init(name) kh_init_##name()
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Destroy a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Reset a hash table without deallocating memory.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_clear(name, h) kh_clear_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Resize a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param s New size [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Insert a key to the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@param r Extra return code: -1 if the operation failed;
|
||||||
|
0 if the key is present in the hash table;
|
||||||
|
1 if the bucket is empty (never used); 2 if the element in
|
||||||
|
the bucket has been deleted [int*]
|
||||||
|
@return Iterator to the inserted element [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Retrieve a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Remove a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Iterator to the element to be deleted [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Test whether a bucket contains data.
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return 1 if containing data; 0 otherwise [int]
|
||||||
|
*/
|
||||||
|
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get key given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Key [type of keys]
|
||||||
|
*/
|
||||||
|
#define kh_key(h, x) ((h)->keys[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get value given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Value [type of values]
|
||||||
|
@discussion For hash sets, calling this results in segfault.
|
||||||
|
*/
|
||||||
|
#define kh_val(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Alias of kh_val()
|
||||||
|
*/
|
||||||
|
#define kh_value(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the start iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The start iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_begin(h) (khint_t)(0)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the end iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The end iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_end(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of elements in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of elements in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_size(h) ((h)->size)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of buckets in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of buckets in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the entries in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param kvar Variable to which key will be assigned
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(kvar) = kh_key(h,__i); \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the values in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/* More convenient interfaces */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash set containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT(name) \
|
||||||
|
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT64(name) \
|
||||||
|
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
typedef const char *kh_cstr_t;
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_STR(name) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
#endif /* __AC_KHASH_H */
|
||||||
66
lib/expr.c
Normal file
66
lib/expr.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static bool was_init = false;
|
||||||
|
|
||||||
|
typedef TYC_RESULT(*BIN_EXPR_FN)(VALUE, VALUE, VALUE*);
|
||||||
|
static BIN_EXPR_FN bin_expr_fn[TX_COUNT__][TT_COUNT__][TT_COUNT__];
|
||||||
|
|
||||||
|
static TYC_RESULT default_bin_op(VALUE a, VALUE b, VALUE* r) { (void) a; (void) b, (void) r; return T_ERR_EXPR_INCORRECT_TYPES; }
|
||||||
|
|
||||||
|
#define BIN_OP(name) static TYC_RESULT name(VALUE a, VALUE b, VALUE* r)
|
||||||
|
BIN_OP(sum_int_int) { *r = create_value_integer(value_integer(a) + value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(sub_int_int) { *r = create_value_integer(value_integer(a) - value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(mul_int_int) { *r = create_value_integer(value_integer(a) * value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(idiv_int_int) { *r = create_value_integer(value_integer(a) / value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(eq_int_int) { *r = create_value_from_bool(value_integer(a) == value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(neq_int_int) { *r = create_value_from_bool(value_integer(a) != value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(lt_int_int) { *r = create_value_from_bool(value_integer(a) < value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(lte_int_int) { *r = create_value_from_bool(value_integer(a) <= value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(gt_int_int) { *r = create_value_from_bool(value_integer(a) > value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(gte_int_int) { *r = create_value_from_bool(value_integer(a) >= value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(and_int_int) { *r = create_value_integer(value_integer(a) & value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(or_int_int) { *r = create_value_integer(value_integer(a) | value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(xor_int_int) { *r = create_value_integer(value_integer(a) ^ value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(pow_int_int) { *r = create_value_integer((int32_t) powl(value_integer(a), value_integer(b))); return T_OK; }
|
||||||
|
BIN_OP(shl_int_int) { *r = create_value_integer(value_integer(a) << value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(shr_int_int) { *r = create_value_integer(value_integer(a) >> value_integer(b)); return T_OK; }
|
||||||
|
BIN_OP(mod_int_int) { *r = create_value_integer(value_integer(a) % value_integer(b)); return T_OK; }
|
||||||
|
|
||||||
|
void expr_init(void)
|
||||||
|
{
|
||||||
|
if (was_init)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < TX_COUNT__; ++i)
|
||||||
|
for (size_t j = 0; j < TT_COUNT__; ++j)
|
||||||
|
for (size_t k = 0; k < TT_COUNT__; ++k)
|
||||||
|
bin_expr_fn[i][j][k] = default_bin_op;
|
||||||
|
|
||||||
|
bin_expr_fn[TX_SUM][TT_INTEGER][TT_INTEGER] = sum_int_int;
|
||||||
|
bin_expr_fn[TX_SUB][TT_INTEGER][TT_INTEGER] = sub_int_int;
|
||||||
|
bin_expr_fn[TX_MUL][TT_INTEGER][TT_INTEGER] = mul_int_int;
|
||||||
|
bin_expr_fn[TX_IDIV][TT_INTEGER][TT_INTEGER] = idiv_int_int;
|
||||||
|
bin_expr_fn[TX_EQ][TT_INTEGER][TT_INTEGER] = eq_int_int;
|
||||||
|
bin_expr_fn[TX_NEQ][TT_INTEGER][TT_INTEGER] = neq_int_int;
|
||||||
|
bin_expr_fn[TX_LT][TT_INTEGER][TT_INTEGER] = lt_int_int;
|
||||||
|
bin_expr_fn[TX_LTE][TT_INTEGER][TT_INTEGER] = lte_int_int;
|
||||||
|
bin_expr_fn[TX_GT][TT_INTEGER][TT_INTEGER] = gt_int_int;
|
||||||
|
bin_expr_fn[TX_GTE][TT_INTEGER][TT_INTEGER] = gte_int_int;
|
||||||
|
bin_expr_fn[TX_AND][TT_INTEGER][TT_INTEGER] = and_int_int;
|
||||||
|
bin_expr_fn[TX_OR][TT_INTEGER][TT_INTEGER] = or_int_int;
|
||||||
|
bin_expr_fn[TX_XOR][TT_INTEGER][TT_INTEGER] = xor_int_int;
|
||||||
|
bin_expr_fn[TX_POW][TT_INTEGER][TT_INTEGER] = pow_int_int;
|
||||||
|
bin_expr_fn[TX_SHL][TT_INTEGER][TT_INTEGER] = shl_int_int;
|
||||||
|
bin_expr_fn[TX_SHR][TT_INTEGER][TT_INTEGER] = shr_int_int;
|
||||||
|
bin_expr_fn[TX_MOD][TT_INTEGER][TT_INTEGER] = mod_int_int;
|
||||||
|
|
||||||
|
was_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT binary_expr(TYC_EXPR op, VALUE a, VALUE b, VALUE* result)
|
||||||
|
{
|
||||||
|
return bin_expr_fn[op][value_type(a)][value_type(b)](a, b, result);
|
||||||
|
}
|
||||||
141
lib/heap.c
Normal file
141
lib/heap.c
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "khash.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TH_STRING, TH_ARRAY, TH_TABLE,
|
||||||
|
} TYC_HEAP_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TYC_HEAP_TYPE type;
|
||||||
|
union {
|
||||||
|
char* str;
|
||||||
|
// TODO - array and table
|
||||||
|
} value;
|
||||||
|
} HeapValue;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
KHASH_MAP_INIT_INT64(HEAP, HeapValue)
|
||||||
|
KHASH_MAP_INIT_INT64(MARK, bool)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
struct Heap {
|
||||||
|
khash_t(HEAP) *items;
|
||||||
|
};
|
||||||
|
|
||||||
|
Heap* heap_new(void)
|
||||||
|
{
|
||||||
|
Heap* h = xcalloc(1, sizeof(Heap));
|
||||||
|
h->items = kh_init(HEAP);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void heap_free_item(HeapValue value)
|
||||||
|
{
|
||||||
|
switch (value.type) {
|
||||||
|
case TH_STRING:
|
||||||
|
free(value.value.str);
|
||||||
|
break;
|
||||||
|
case TH_ARRAY:
|
||||||
|
abort(); // not implemented yet
|
||||||
|
case TH_TABLE:
|
||||||
|
abort(); // not implemented yet
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void heap_destroy(Heap* h)
|
||||||
|
{
|
||||||
|
for (khiter_t k = kh_begin(h->items); k != kh_end(h->items); ++k) {
|
||||||
|
if (kh_exist(h->items, k)) {
|
||||||
|
HeapValue value = kh_value(h->items, k);
|
||||||
|
heap_free_item(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kh_destroy(HEAP, h->items);
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEAP_KEY heap_add_string(Heap* h, const char* value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
khiter_t k;
|
||||||
|
HEAP_KEY key;
|
||||||
|
|
||||||
|
do {
|
||||||
|
key = (HEAP_KEY) rand();
|
||||||
|
k = kh_get(HEAP, h->items, key);
|
||||||
|
} while (k != kh_end(h->items));
|
||||||
|
|
||||||
|
k = kh_put(HEAP, h->items, key, &ret);
|
||||||
|
if (ret < 0)
|
||||||
|
out_of_memory();
|
||||||
|
|
||||||
|
kh_value(h->items, k) = (HeapValue) {
|
||||||
|
.type = TH_STRING,
|
||||||
|
.value = { .str = strdup(value) }
|
||||||
|
};
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT heap_get_string(Heap const* h, HEAP_KEY key, const char** value)
|
||||||
|
{
|
||||||
|
khiter_t k = kh_get(HEAP, h->items, key);
|
||||||
|
bool is_missing = (k == kh_end(h->items));
|
||||||
|
if (is_missing)
|
||||||
|
return T_ERR_HEAP_KEY_NOT_FOUND;
|
||||||
|
*value = kh_value(h->items, k).value.str;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t heap_size(Heap const* h)
|
||||||
|
{
|
||||||
|
return kh_size(h->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GC
|
||||||
|
//
|
||||||
|
|
||||||
|
void heap_gc(Heap* h, VALUE const* roots, size_t n_roots)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// mark
|
||||||
|
//
|
||||||
|
|
||||||
|
khash_t(MARK) *marked = kh_init(MARK);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n_roots; ++i) {
|
||||||
|
if (value_type(roots[i]) == TT_STRING) {
|
||||||
|
int ret;
|
||||||
|
uint32_t key = value_idx(roots[i]);
|
||||||
|
khiter_t k = kh_put(MARK, marked, key, &ret);
|
||||||
|
if (ret < 0)
|
||||||
|
out_of_memory();
|
||||||
|
kh_value(marked, k) = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// sweep
|
||||||
|
//
|
||||||
|
|
||||||
|
for (khiter_t k = kh_begin(h->items); k != kh_end(h->items); ++k) {
|
||||||
|
if (kh_exist(h->items, k)) {
|
||||||
|
HEAP_KEY key = (HEAP_KEY) kh_key(h->items, k);
|
||||||
|
if (kh_get(MARK, marked, key) == kh_end(marked)) {
|
||||||
|
khiter_t kk = kh_get(HEAP, h->items, key);
|
||||||
|
heap_free_item(kh_value(h->items, kk));
|
||||||
|
kh_del(HEAP, h->items, kk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kh_destroy(MARK, marked);
|
||||||
|
}
|
||||||
241
lib/priv.h
Normal file
241
lib/priv.h
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
#ifndef TYCHE_PRIV_H
|
||||||
|
#define TYCHE_PRIV_H
|
||||||
|
|
||||||
|
#include "tyche.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// INSTRUCTIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// STACK OPERATIONS
|
||||||
|
TO_PUSHI = 0XA0,
|
||||||
|
TO_PUSHC = 0XA1,
|
||||||
|
TO_PUSHF = 0XA2,
|
||||||
|
TO_PUSHN = 0X00,
|
||||||
|
TO_PUSHZ = 0X01,
|
||||||
|
TO_PUSHT = 0X02,
|
||||||
|
TO_NEWA = 0X03,
|
||||||
|
TO_NEWT = 0X04,
|
||||||
|
TO_POP = 0X05,
|
||||||
|
TO_DUP = 0X06,
|
||||||
|
|
||||||
|
// LOCAL VARIABLES
|
||||||
|
TO_PUSHV = 0XA3,
|
||||||
|
TO_SET = 0XAE,
|
||||||
|
TO_DUPV = 0XA4,
|
||||||
|
TO_SETG = 0XA5,
|
||||||
|
TO_GETG = 0XA6,
|
||||||
|
|
||||||
|
// FUNCTION OPERATIONS
|
||||||
|
TO_CALL = 0XA7,
|
||||||
|
TO_RET = 0X10,
|
||||||
|
TO_RETI = 0X11,
|
||||||
|
|
||||||
|
// TABLE AND ARRAY OPERATIONS
|
||||||
|
TO_GETKV = 0X16,
|
||||||
|
TO_SETKV = 0X17,
|
||||||
|
TO_GETI = 0XA8,
|
||||||
|
TO_SETI = 0XA9,
|
||||||
|
TO_APPND = 0X18,
|
||||||
|
TO_NEXT = 0X19,
|
||||||
|
TO_SMT = 0X1A,
|
||||||
|
TO_MT = 0X1B,
|
||||||
|
|
||||||
|
// LOGICAL/ARITHMETIC
|
||||||
|
TO_SUM = 0X20,
|
||||||
|
TO_SUB = 0X21,
|
||||||
|
TO_MUL = 0X22,
|
||||||
|
TO_DIV = 0X23,
|
||||||
|
TO_IDIV = 0X24,
|
||||||
|
TO_MOD = 0X25,
|
||||||
|
TO_EQ = 0X26,
|
||||||
|
TO_NEQ = 0X27,
|
||||||
|
TO_LT = 0X28,
|
||||||
|
TO_LTE = 0X29,
|
||||||
|
TO_GT = 0X2A,
|
||||||
|
TO_GTE = 0X2B,
|
||||||
|
TO_AND = 0X2C,
|
||||||
|
TO_OR = 0X2D,
|
||||||
|
TO_XOR = 0X2E,
|
||||||
|
TO_POW = 0X2F,
|
||||||
|
TO_SHL = 0X30,
|
||||||
|
TO_SHR = 0X31,
|
||||||
|
|
||||||
|
// OTHER VALUE OPERATIONS
|
||||||
|
TO_LEN = 0X40,
|
||||||
|
TO_TYPE = 0X41,
|
||||||
|
TO_CAST = 0XAD,
|
||||||
|
TO_VER = 0X42,
|
||||||
|
|
||||||
|
// EXTERNAL CODE
|
||||||
|
TO_CMPL = 0X48,
|
||||||
|
TO_ASMBL = 0X49,
|
||||||
|
TO_LOAD = 0X4A,
|
||||||
|
|
||||||
|
// CONTROL FLOW
|
||||||
|
TO_BZ = 0XAA,
|
||||||
|
TO_BNZ = 0XAB,
|
||||||
|
TO_JMP = 0XAC,
|
||||||
|
|
||||||
|
// MEMORY MANAGEMENT
|
||||||
|
TO_GC = 0X4B,
|
||||||
|
} TYC_INST;
|
||||||
|
|
||||||
|
//
|
||||||
|
// TYPE DECLARATION
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TYC_TYPE type;
|
||||||
|
union {
|
||||||
|
int32_t i;
|
||||||
|
float f;
|
||||||
|
uint32_t idx;
|
||||||
|
} v;
|
||||||
|
} VALUE;
|
||||||
|
|
||||||
|
typedef struct Stack Stack;
|
||||||
|
typedef struct Array Array;
|
||||||
|
typedef struct Table Table;
|
||||||
|
typedef struct Heap Heap;
|
||||||
|
typedef struct Code Code;
|
||||||
|
|
||||||
|
typedef uint32_t HEAP_KEY;
|
||||||
|
typedef uint64_t TABLE_HASH;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TC_STRING, TC_REAL, TC_INVALID_TYPE
|
||||||
|
} TYC_CONST_TYPE;
|
||||||
|
|
||||||
|
typedef struct Instruction {
|
||||||
|
TYC_INST operator;
|
||||||
|
int32_t operand;
|
||||||
|
uint8_t sz;
|
||||||
|
} Instruction;
|
||||||
|
|
||||||
|
//
|
||||||
|
// UTILS
|
||||||
|
//
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void out_of_memory(void);
|
||||||
|
void* xmalloc(size_t n);
|
||||||
|
void* xcalloc(size_t n, size_t size);
|
||||||
|
void* xrealloc(void* p, size_t n);
|
||||||
|
|
||||||
|
//
|
||||||
|
// VALUE
|
||||||
|
//
|
||||||
|
|
||||||
|
TYC_TYPE value_type(VALUE v);
|
||||||
|
bool type_is_collectable(TYC_TYPE t);
|
||||||
|
|
||||||
|
int32_t value_integer(VALUE v);
|
||||||
|
float value_real(VALUE v);
|
||||||
|
uint32_t value_idx(VALUE v);
|
||||||
|
bool value_is_zero(VALUE v);
|
||||||
|
|
||||||
|
VALUE create_value_nil(void);
|
||||||
|
VALUE create_value_from_bool(bool b);
|
||||||
|
VALUE create_value_integer(int32_t v);
|
||||||
|
VALUE create_value_real(float f);
|
||||||
|
VALUE create_value_idx(TYC_TYPE type, uint32_t idx);
|
||||||
|
|
||||||
|
//
|
||||||
|
// STACK
|
||||||
|
//
|
||||||
|
|
||||||
|
Stack* stack_new(void);
|
||||||
|
void stack_destroy(Stack* s);
|
||||||
|
|
||||||
|
TYC_RESULT stack_push(Stack* s, VALUE v);
|
||||||
|
TYC_RESULT stack_peek(Stack const* s, VALUE* v_out);
|
||||||
|
TYC_RESULT stack_pop(Stack* s, VALUE* v_out);
|
||||||
|
|
||||||
|
size_t stack_size(Stack const* s);
|
||||||
|
|
||||||
|
TYC_RESULT stack_at(Stack const* s, int32_t key, VALUE* v);
|
||||||
|
TYC_RESULT stack_set(Stack* s, int32_t key, VALUE v);
|
||||||
|
|
||||||
|
size_t stack_top_fp(Stack const* s);
|
||||||
|
TYC_RESULT stack_push_fp(Stack* s);
|
||||||
|
TYC_RESULT stack_pop_fp(Stack* s);
|
||||||
|
size_t stack_fp_level(Stack const* s);
|
||||||
|
|
||||||
|
size_t stack_collectable_array(Stack const* s, VALUE** values);
|
||||||
|
|
||||||
|
//
|
||||||
|
// HEAP ARRAY
|
||||||
|
//
|
||||||
|
|
||||||
|
Array* array_new(void);
|
||||||
|
void array_destroy(Array* a);
|
||||||
|
|
||||||
|
size_t array_len(Array const* a);
|
||||||
|
VALUE array_get(Array const* a, size_t pos);
|
||||||
|
void array_set(Array* a, size_t pos, VALUE v);
|
||||||
|
void array_append(Array* a, VALUE v);
|
||||||
|
|
||||||
|
//
|
||||||
|
// HEAP TABLE
|
||||||
|
//
|
||||||
|
|
||||||
|
Table* table_new(Heap const* heap);
|
||||||
|
void table_destroy(Table* t);
|
||||||
|
|
||||||
|
size_t table_len(Table* t);
|
||||||
|
TYC_RESULT table_get(Table const* t, VALUE key, VALUE* value);
|
||||||
|
void table_set(Table* t, VALUE key, VALUE value);
|
||||||
|
void table_del(Table* t, VALUE key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// HEAP
|
||||||
|
//
|
||||||
|
|
||||||
|
Heap* heap_new(void);
|
||||||
|
void heap_destroy(Heap* h);
|
||||||
|
|
||||||
|
HEAP_KEY heap_add_string(Heap* h, const char* value);
|
||||||
|
TYC_RESULT heap_get_string(Heap const* h, HEAP_KEY key, const char** value);
|
||||||
|
|
||||||
|
size_t heap_size(Heap const* h);
|
||||||
|
|
||||||
|
void heap_gc(Heap* h, VALUE const* roots, size_t n_roots);
|
||||||
|
|
||||||
|
//
|
||||||
|
// CODE
|
||||||
|
//
|
||||||
|
|
||||||
|
TYC_RESULT code_assemble(const char* code, uint8_t** bytecode, size_t* bytecode_sz);
|
||||||
|
|
||||||
|
Code* code_new(void);
|
||||||
|
void code_destroy(Code* code);
|
||||||
|
|
||||||
|
TYC_RESULT code_load_bytecode(Code* code, uint8_t const* bytecode, size_t bytecode_sz);
|
||||||
|
|
||||||
|
uint32_t code_n_consts(Code const* code);
|
||||||
|
TYC_CONST_TYPE code_const_type(Code const* code, size_t n);
|
||||||
|
|
||||||
|
T_REAL code_const_real(Code const* code, size_t n);
|
||||||
|
const char* code_const_string(Code const* code, size_t n);
|
||||||
|
|
||||||
|
uint32_t code_n_functions(Code const* code);
|
||||||
|
uint32_t code_function_sz(Code const* code, uint32_t f_id);
|
||||||
|
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc);
|
||||||
|
|
||||||
|
void code_debug_bytecode(Code const* code);
|
||||||
|
void code_decompile(Code const* code);
|
||||||
|
void code_parse_instruction(Instruction inst, char* outbuf, size_t sz);
|
||||||
|
|
||||||
|
//
|
||||||
|
// EXPRESSIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
void expr_init(void);
|
||||||
|
TYC_RESULT binary_expr(TYC_EXPR op, VALUE a, VALUE b, VALUE* result);
|
||||||
|
|
||||||
|
#endif //TYCHE_PRIV_H
|
||||||
148
lib/stack.c
Normal file
148
lib/stack.c
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct Stack {
|
||||||
|
VALUE* stack;
|
||||||
|
size_t stack_n;
|
||||||
|
size_t stack_cap;
|
||||||
|
uint32_t* fp;
|
||||||
|
size_t fp_n;
|
||||||
|
size_t fp_cap;
|
||||||
|
};
|
||||||
|
|
||||||
|
Stack* stack_new(void)
|
||||||
|
{
|
||||||
|
Stack* s = xcalloc(1, sizeof(Stack));
|
||||||
|
|
||||||
|
s->stack_n = 0;
|
||||||
|
s->fp_n = 0;
|
||||||
|
s->stack_cap = 64;
|
||||||
|
s->fp_cap = 8;
|
||||||
|
s->stack = xmalloc(s->stack_cap * sizeof s->stack[0]);
|
||||||
|
s->fp = xmalloc(s->stack_cap * sizeof s->fp[0]);
|
||||||
|
|
||||||
|
assert(s->stack);
|
||||||
|
assert(s->fp);
|
||||||
|
|
||||||
|
stack_push_fp(s);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_destroy(Stack* s)
|
||||||
|
{
|
||||||
|
free(s->stack);
|
||||||
|
free(s->fp);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_push(Stack* s, VALUE v)
|
||||||
|
{
|
||||||
|
if (s->stack_n == s->stack_cap) {
|
||||||
|
s->stack_cap *= 2;
|
||||||
|
s->stack = xrealloc(s->stack, s->stack_cap * sizeof s->stack[0]);
|
||||||
|
assert(s->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->stack[s->stack_n] = v;
|
||||||
|
++s->stack_n;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stack_top_fp(Stack const* s)
|
||||||
|
{
|
||||||
|
return s->fp[s->fp_n - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_peek(Stack const* s, VALUE* v_out)
|
||||||
|
{
|
||||||
|
if (s->stack_n <= stack_top_fp(s))
|
||||||
|
return T_ERR_STACK_UNDERFLOW;
|
||||||
|
if (v_out)
|
||||||
|
*v_out = s->stack[s->stack_n - 1];
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_pop(Stack* s, VALUE* v_out)
|
||||||
|
{
|
||||||
|
TYC_RESULT err = stack_peek(s, v_out);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
--s->stack_n;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stack_size(Stack const* s)
|
||||||
|
{
|
||||||
|
return s->stack_n - stack_top_fp(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_at(Stack const* s, int32_t key, VALUE* v)
|
||||||
|
{
|
||||||
|
if (key >= 0) {
|
||||||
|
if ((int) stack_top_fp(s) + key >= (int) s->stack_n)
|
||||||
|
return T_ERR_STACK_ACCESS_OUT_OF_RANGE;
|
||||||
|
*v = s->stack[(int) stack_top_fp(s) + key];
|
||||||
|
} else {
|
||||||
|
if ((int) s->stack_n + key < (int) stack_top_fp(s))
|
||||||
|
return T_ERR_STACK_ACCESS_OUT_OF_RANGE;
|
||||||
|
*v = s->stack[(int) s->stack_n + key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_set(Stack* s, int32_t key, VALUE v)
|
||||||
|
{
|
||||||
|
if (key >= 0) {
|
||||||
|
if ((int) stack_top_fp(s) + key >= (int) s->stack_n)
|
||||||
|
return T_ERR_STACK_ACCESS_OUT_OF_RANGE;
|
||||||
|
s->stack[(int) stack_top_fp(s) + key] = v;
|
||||||
|
} else {
|
||||||
|
if ((int) s->stack_n + key < (int) stack_top_fp(s))
|
||||||
|
return T_ERR_STACK_ACCESS_OUT_OF_RANGE;
|
||||||
|
s->stack[(int) s->stack_n + key] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_push_fp(Stack* s)
|
||||||
|
{
|
||||||
|
if (s->fp_n == s->fp_cap) {
|
||||||
|
s->fp_cap *= 2;
|
||||||
|
s->fp = xrealloc(s->fp, s->fp_cap * sizeof s->fp[0]);
|
||||||
|
assert(s->fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->fp[s->fp_n] = (uint32_t) s->stack_n;
|
||||||
|
++s->fp_n;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT stack_pop_fp(Stack* s)
|
||||||
|
{
|
||||||
|
if (s->fp_n == 1)
|
||||||
|
return T_ERR_STACK_FP_UNDERFLOW;
|
||||||
|
s->stack_n = stack_top_fp(s);
|
||||||
|
--s->fp_n;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stack_fp_level(Stack const* s)
|
||||||
|
{
|
||||||
|
return s->fp_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stack_collectable_array(Stack const* s, VALUE** values)
|
||||||
|
{
|
||||||
|
size_t j = 0;
|
||||||
|
*values = xmalloc(stack_size(s) * sizeof(VALUE));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->stack_n; ++i)
|
||||||
|
if (type_is_collectable(s->stack[i].type))
|
||||||
|
(*values)[j++] = s->stack[i];
|
||||||
|
return j;
|
||||||
|
}
|
||||||
132
lib/table.c
Normal file
132
lib/table.c
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include "khash.h"
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
KHASH_MAP_INIT_INT64(TABLE_INT, VALUE)
|
||||||
|
KHASH_MAP_INIT_STR(TABLE_STR, VALUE)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
struct Table {
|
||||||
|
khash_t(TABLE_INT)* tbl_int;
|
||||||
|
khash_t(TABLE_STR)* tbl_str;
|
||||||
|
Heap const* heap;
|
||||||
|
};
|
||||||
|
|
||||||
|
Table* table_new(Heap const* heap)
|
||||||
|
{
|
||||||
|
Table* t = xcalloc(1, sizeof(Table));
|
||||||
|
t->tbl_int = kh_init(TABLE_INT);
|
||||||
|
t->tbl_str = kh_init(TABLE_STR);
|
||||||
|
t->heap = heap;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_destroy(Table* t)
|
||||||
|
{
|
||||||
|
kh_destroy(TABLE_INT, t->tbl_int);
|
||||||
|
kh_destroy(TABLE_STR, t->tbl_str);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t table_len(Table* t)
|
||||||
|
{
|
||||||
|
return kh_size(t->tbl_int) + kh_size(t->tbl_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TABLE_HASH value_hash(VALUE v)
|
||||||
|
{
|
||||||
|
switch (value_type(v)) {
|
||||||
|
case TT_NIL:
|
||||||
|
return 0;
|
||||||
|
case TT_INTEGER:
|
||||||
|
return (uint64_t) value_integer(v);
|
||||||
|
case TT_REAL: {
|
||||||
|
uint32_t vv;
|
||||||
|
float f = value_real(v);
|
||||||
|
memcpy(&vv, &f, sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TT_STRING_CONST:
|
||||||
|
return (TABLE_HASH) value_idx(v) | ((TABLE_HASH) 1 << 33);
|
||||||
|
case TT_ARRAY:
|
||||||
|
return (TABLE_HASH) value_idx(v) | ((TABLE_HASH) 1 << 34);
|
||||||
|
case TT_TABLE:
|
||||||
|
return (TABLE_HASH) value_idx(v) | ((TABLE_HASH) 1 << 35);
|
||||||
|
case TT_FUNCTION:
|
||||||
|
return (TABLE_HASH) value_idx(v) | ((TABLE_HASH) 1 << 36);
|
||||||
|
case TT_NATIVE_PTR:
|
||||||
|
return (TABLE_HASH) value_idx(v) | ((TABLE_HASH) 1 << 37);
|
||||||
|
case TT_STRING:
|
||||||
|
case TT_COUNT__:
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT table_get(Table const* t, VALUE key, VALUE* value)
|
||||||
|
{
|
||||||
|
if (value_type(key) == TT_STRING) {
|
||||||
|
const char* skey;
|
||||||
|
if (heap_get_string(t->heap, value_idx(key), &skey) != T_OK)
|
||||||
|
abort();
|
||||||
|
khiter_t k = kh_get(TABLE_STR, t->tbl_str, skey);
|
||||||
|
if (k == kh_end(t->tbl_str))
|
||||||
|
return T_ERR_TABLE_KEY_NOT_FOUND;
|
||||||
|
*value = kh_value(t->tbl_str, k);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TABLE_HASH hash = value_hash(key);
|
||||||
|
khiter_t k = kh_get(TABLE_INT, t->tbl_int, hash);
|
||||||
|
if (k == kh_end(t->tbl_int))
|
||||||
|
return T_ERR_TABLE_KEY_NOT_FOUND;
|
||||||
|
*value = kh_value(t->tbl_int, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_set(Table* t, VALUE key, VALUE value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (value_type(key) == TT_STRING) {
|
||||||
|
const char* skey;
|
||||||
|
if (heap_get_string(t->heap, value_idx(key), &skey) != T_OK)
|
||||||
|
abort();
|
||||||
|
khiter_t k = kh_put(TABLE_STR, t->tbl_str, skey, &ret);
|
||||||
|
if (ret < 0)
|
||||||
|
out_of_memory();
|
||||||
|
kh_value(t->tbl_str, k) = value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TABLE_HASH hash = value_hash(key);
|
||||||
|
khiter_t k = kh_put(TABLE_INT, t->tbl_int, hash, &ret);
|
||||||
|
if (ret < 0)
|
||||||
|
out_of_memory();
|
||||||
|
kh_value(t->tbl_int, k) = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_del(Table* t, VALUE key)
|
||||||
|
{
|
||||||
|
if (value_type(key) == TT_STRING) {
|
||||||
|
const char* skey;
|
||||||
|
if (heap_get_string(t->heap, value_idx(key), &skey) != T_OK)
|
||||||
|
abort();
|
||||||
|
khiter_t k = kh_get(TABLE_STR, t->tbl_str, skey);
|
||||||
|
if (k == kh_end(t->tbl_str))
|
||||||
|
return;
|
||||||
|
kh_del(TABLE_STR, t->tbl_str, k);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TABLE_HASH hash = value_hash(key);
|
||||||
|
khiter_t k = kh_get(TABLE_INT, t->tbl_int, hash);
|
||||||
|
if (k == kh_end(t->tbl_int))
|
||||||
|
return;
|
||||||
|
kh_del(TABLE_INT, t->tbl_int, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
lib/tyche.h
Normal file
55
lib/tyche.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef TYCHE_TYCHE_H
|
||||||
|
#define TYCHE_TYCHE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TT_NIL, TT_INTEGER, TT_REAL, TT_STRING, TT_STRING_CONST, TT_ARRAY, TT_TABLE, TT_FUNCTION, TT_NATIVE_PTR,
|
||||||
|
TT_COUNT__
|
||||||
|
} TYC_TYPE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
T_OK = 0,
|
||||||
|
T_ERR_STACK_UNDERFLOW = -1, T_ERR_STACK_FP_UNDERFLOW = -2, T_ERR_STACK_ACCESS_OUT_OF_RANGE = -3,
|
||||||
|
T_ERR_HEAP_KEY_NOT_FOUND = -10,
|
||||||
|
T_ERR_TABLE_KEY_NOT_FOUND = -20,
|
||||||
|
T_ERR_ASSEMBLER_SYNTAX_ERROR = -30,
|
||||||
|
T_ERR_BYTECODE_TOO_SMALL = -40, T_ERR_BYTECODE_INVALID_MAGIC = -41,
|
||||||
|
T_ERR_TYPE_UNEXPECTED = -50, T_ERR_INVALID_OPCODE = -51, T_ERR_EXPR_INCORRECT_TYPES = -52, T_ERR_VALUE_OUT_OF_RANGE = -53,
|
||||||
|
} TYC_RESULT;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TX_SUM, TX_SUB, TX_MUL, TX_IDIV, TX_EQ, TX_NEQ, TX_LT, TX_LTE, TX_GT, TX_GTE, TX_AND, TX_OR, TX_XOR, TX_POW,
|
||||||
|
TX_SHL, TX_SHR, TX_MOD,
|
||||||
|
TX_COUNT__
|
||||||
|
} TYC_EXPR;
|
||||||
|
|
||||||
|
#define T_REAL float
|
||||||
|
|
||||||
|
typedef struct TycheVM TycheVM;
|
||||||
|
|
||||||
|
// create/destroy VM
|
||||||
|
TycheVM* tyc_new(void);
|
||||||
|
void tyc_destroy(TycheVM* t);
|
||||||
|
|
||||||
|
// debugging (DEBUG_ASSEMBLY needs to be setup in compilation options)
|
||||||
|
void tyc_debug_to_console(TycheVM* T, bool activate);
|
||||||
|
void tyc_assembly_decompile(TycheVM* T);
|
||||||
|
void tyc_print_bytecode(TycheVM* T);
|
||||||
|
|
||||||
|
// code loading and execution
|
||||||
|
TYC_RESULT tyc_load_bytecode(TycheVM* T, uint8_t const* bytecode, size_t bytecode_sz);
|
||||||
|
TYC_RESULT tyc_call(TycheVM* t, uint16_t n_pars);
|
||||||
|
|
||||||
|
// stack manipulation and query
|
||||||
|
size_t tyc_stack_size(TycheVM* T);
|
||||||
|
void tyc_pushnil(TycheVM* T);
|
||||||
|
void tyc_pushinteger(TycheVM* T, int32_t value);
|
||||||
|
TYC_RESULT tyc_type(TycheVM* T, int idx, TYC_TYPE* type);
|
||||||
|
TYC_RESULT tyc_tointeger(TycheVM* T, int idx, int32_t* value);
|
||||||
|
TYC_RESULT tyc_tostring(TycheVM* T, int idx, const char** str);
|
||||||
|
TYC_RESULT tyc_expr(TycheVM* T, TYC_EXPR expr);
|
||||||
|
|
||||||
|
#endif //TYCHE_TYCHE_H
|
||||||
31
lib/utils.c
Normal file
31
lib/utils.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void out_of_memory(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* xmalloc(size_t n)
|
||||||
|
{
|
||||||
|
void* p = malloc(n);
|
||||||
|
if (!p) out_of_memory();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* xcalloc(size_t n, size_t size)
|
||||||
|
{
|
||||||
|
void* p = calloc(n, size);
|
||||||
|
if (!p) out_of_memory();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* xrealloc(void* p, size_t n)
|
||||||
|
{
|
||||||
|
void* q = realloc(p, n);
|
||||||
|
if (!q) out_of_memory();
|
||||||
|
return q;
|
||||||
|
}
|
||||||
76
lib/value.c
Normal file
76
lib/value.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
TYC_TYPE value_type(VALUE v)
|
||||||
|
{
|
||||||
|
return v.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool type_is_collectable(TYC_TYPE t)
|
||||||
|
{
|
||||||
|
return t == TT_STRING || t == TT_ARRAY || t == TT_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t value_integer(VALUE v)
|
||||||
|
{
|
||||||
|
#ifdef CHECK_TYCHE_BUGS
|
||||||
|
if (v.type != TT_INTEGER)
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
return v.v.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
float value_real(VALUE v)
|
||||||
|
{
|
||||||
|
#ifdef CHECK_TYCHE_BUGS
|
||||||
|
if (v.type != TT_REAL)
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
return v.v.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t value_idx(VALUE v)
|
||||||
|
{
|
||||||
|
#ifdef CHECK_TYCHE_BUGS
|
||||||
|
if (v.type != TT_FUNCTION && v.type != TT_NATIVE_PTR && v.type != TT_ARRAY && v.type != TT_TABLE && v.type != TT_STRING && v.type != TT_STRING_CONST)
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
return v.v.idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE create_value_nil(void)
|
||||||
|
{
|
||||||
|
return (VALUE) { .type = TT_NIL };
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE create_value_from_bool(bool b)
|
||||||
|
{
|
||||||
|
return b ? create_value_integer(1) : create_value_integer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE create_value_integer(int32_t v)
|
||||||
|
{
|
||||||
|
return (VALUE) { .type = TT_INTEGER, .v = { .i = v } };
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE create_value_real(float f)
|
||||||
|
{
|
||||||
|
return (VALUE) { .type = TT_REAL, .v = { .f = f } };
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE create_value_idx(TYC_TYPE type, uint32_t idx)
|
||||||
|
{
|
||||||
|
#ifdef CHECK_TYCHE_BUGS
|
||||||
|
if (type != TT_FUNCTION && type != TT_NATIVE_PTR && type != TT_ARRAY && type != TT_TABLE && type != TT_STRING && type != TT_STRING_CONST)
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
return (VALUE) { .type = type, .v = { .idx = idx } };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value_is_zero(VALUE v)
|
||||||
|
{
|
||||||
|
return v.type == TT_NIL || (v.type == TT_INTEGER && v.v.i == 0);
|
||||||
|
}
|
||||||
468
lib/vm.c
Normal file
468
lib/vm.c
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct Location {
|
||||||
|
uint32_t function_id;
|
||||||
|
uint32_t pc;
|
||||||
|
} Location;
|
||||||
|
|
||||||
|
typedef struct LocationStack {
|
||||||
|
Location* locations;
|
||||||
|
size_t sz;
|
||||||
|
size_t cap;
|
||||||
|
} LocationStack;
|
||||||
|
|
||||||
|
struct TycheVM {
|
||||||
|
Stack* stack;
|
||||||
|
Heap* heap;
|
||||||
|
Code* code;
|
||||||
|
LocationStack location_stack;
|
||||||
|
bool debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
static TYC_RESULT step(TycheVM* T);
|
||||||
|
|
||||||
|
#define TRY(x) if ((r = (x)) != T_OK) { return r; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// CREATE/DESTROY VM
|
||||||
|
//
|
||||||
|
|
||||||
|
TycheVM* tyc_new(void)
|
||||||
|
{
|
||||||
|
TycheVM* t = xcalloc(1, sizeof(TycheVM));
|
||||||
|
t->stack = stack_new();
|
||||||
|
t->heap = heap_new();
|
||||||
|
t->code = code_new();
|
||||||
|
t->location_stack = (LocationStack) {
|
||||||
|
.locations = xmalloc(4 * sizeof(Location)),
|
||||||
|
.cap = 4,
|
||||||
|
.sz = 0,
|
||||||
|
};
|
||||||
|
t->debug = false;
|
||||||
|
|
||||||
|
expr_init();
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_destroy(TycheVM* t)
|
||||||
|
{
|
||||||
|
free(t->location_stack.locations);
|
||||||
|
code_destroy(t->code);
|
||||||
|
heap_destroy(t->heap);
|
||||||
|
stack_destroy(t->stack);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DEBUGGING
|
||||||
|
//
|
||||||
|
|
||||||
|
void tyc_debug_to_console(TycheVM* T, bool activate)
|
||||||
|
{
|
||||||
|
T->debug = activate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
|
||||||
|
static void debug_instruction(TycheVM* T, Location* loc, Instruction inst)
|
||||||
|
{
|
||||||
|
if (!T->debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char buf[50];
|
||||||
|
code_parse_instruction(inst, buf, sizeof(buf));
|
||||||
|
printf(": %02d-%04d %s ", loc->function_id, loc->pc, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_value(TycheVM* T, VALUE a)
|
||||||
|
{
|
||||||
|
switch (value_type(a)) {
|
||||||
|
case TT_NIL:
|
||||||
|
printf("[nil]");
|
||||||
|
break;
|
||||||
|
case TT_INTEGER:
|
||||||
|
printf("[%d]", value_integer(a));
|
||||||
|
break;
|
||||||
|
case TT_REAL:
|
||||||
|
printf("[%f]", (double) value_real(a));
|
||||||
|
break;
|
||||||
|
case TT_STRING: {
|
||||||
|
const char* str;
|
||||||
|
if (heap_get_string(T->heap, value_idx(a), &str) == T_OK)
|
||||||
|
printf("[\"%s\"]", str);
|
||||||
|
else
|
||||||
|
printf("[\"(not found)\"]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TT_STRING_CONST: {
|
||||||
|
if (code_const_type(T->code, value_idx(a)) != TC_STRING)
|
||||||
|
printf("[\"(const not a string)\"]");
|
||||||
|
else
|
||||||
|
printf("[\"%s\"]", code_const_string(T->code, value_idx(a)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TT_ARRAY:
|
||||||
|
printf("[(not implemented)]\n");
|
||||||
|
abort();
|
||||||
|
case TT_TABLE:
|
||||||
|
printf("[(not implemented )]\n");
|
||||||
|
abort();
|
||||||
|
case TT_FUNCTION:
|
||||||
|
printf("[func %d]", value_idx(a));
|
||||||
|
break;
|
||||||
|
case TT_NATIVE_PTR:
|
||||||
|
printf("[ptr %p]", (void *) (intptr_t) value_idx(a));
|
||||||
|
break;
|
||||||
|
case TT_COUNT__:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_stack(TycheVM* T)
|
||||||
|
{
|
||||||
|
if (!T->debug)
|
||||||
|
return;
|
||||||
|
if (stack_size(T->stack) == 0) {
|
||||||
|
printf("|empty|\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < stack_size(T->stack); ++i) {
|
||||||
|
VALUE a;
|
||||||
|
stack_at(T->stack, (int32_t) i, &a);
|
||||||
|
debug_value(T, a);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_assembly_decompile(TycheVM* T)
|
||||||
|
{
|
||||||
|
code_decompile(T->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_print_bytecode(TycheVM* T)
|
||||||
|
{
|
||||||
|
code_debug_bytecode(T->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// LOCATION STACK
|
||||||
|
//
|
||||||
|
|
||||||
|
static void push_location(TycheVM* T, uint32_t function_id, uint32_t pc)
|
||||||
|
{
|
||||||
|
if (T->location_stack.sz == T->location_stack.cap) {
|
||||||
|
T->location_stack.cap *= 2;
|
||||||
|
T->location_stack.locations = xrealloc(T->location_stack.locations, T->location_stack.cap * sizeof(Location));
|
||||||
|
}
|
||||||
|
|
||||||
|
T->location_stack.locations[T->location_stack.sz] = (Location) {
|
||||||
|
.function_id = function_id,
|
||||||
|
.pc = pc,
|
||||||
|
};
|
||||||
|
++T->location_stack.sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Location* location_top(TycheVM* T)
|
||||||
|
{
|
||||||
|
if (T->location_stack.sz == 0)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
return &T->location_stack.locations[T->location_stack.sz - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void location_pop(TycheVM* T)
|
||||||
|
{
|
||||||
|
if (T->location_stack.sz == 0)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
--T->location_stack.sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CODE LOADING AND EXECUTION
|
||||||
|
//
|
||||||
|
|
||||||
|
TYC_RESULT tyc_load_bytecode(TycheVM* T, uint8_t const* bytecode, size_t bytecode_sz)
|
||||||
|
{
|
||||||
|
TYC_RESULT r;
|
||||||
|
TRY(code_load_bytecode(T->code, bytecode, bytecode_sz))
|
||||||
|
TRY(stack_push(T->stack, create_value_idx(TT_FUNCTION, 0 /* main */)))
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TYC_RESULT enter_function(TycheVM* T, uint16_t n_pars)
|
||||||
|
{
|
||||||
|
TYC_RESULT r;
|
||||||
|
|
||||||
|
// get parameters
|
||||||
|
VALUE* params = xcalloc(n_pars + 1, sizeof(VALUE));
|
||||||
|
for (uint16_t i = 0; i < n_pars; ++i)
|
||||||
|
TRY(stack_pop(T->stack, ¶ms[i]))
|
||||||
|
|
||||||
|
// get function
|
||||||
|
VALUE function;
|
||||||
|
TRY(stack_pop(T->stack, &function))
|
||||||
|
if (value_type(function) != TT_FUNCTION)
|
||||||
|
return T_ERR_TYPE_UNEXPECTED;
|
||||||
|
|
||||||
|
// enter function
|
||||||
|
push_location(T, value_idx(function), 0);
|
||||||
|
stack_push_fp(T->stack);
|
||||||
|
|
||||||
|
// pass parameters
|
||||||
|
for (int i = n_pars-1; i >= 0; --i)
|
||||||
|
TRY(stack_push(T->stack, params[i]))
|
||||||
|
|
||||||
|
free(params);
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TYC_RESULT run_until_return(TycheVM* T)
|
||||||
|
{
|
||||||
|
TYC_RESULT r;
|
||||||
|
|
||||||
|
size_t level = stack_fp_level(T->stack);
|
||||||
|
while (stack_fp_level(T->stack) >= level)
|
||||||
|
TRY(step(T))
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT tyc_call(TycheVM* T, uint16_t n_pars)
|
||||||
|
{
|
||||||
|
TYC_RESULT r;
|
||||||
|
TRY(enter_function(T, n_pars))
|
||||||
|
TRY(run_until_return(T))
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STACK MANIPULATION AND QUERY
|
||||||
|
//
|
||||||
|
|
||||||
|
size_t tyc_stack_size(TycheVM* T)
|
||||||
|
{
|
||||||
|
return stack_size(T->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_pushnil(TycheVM* T)
|
||||||
|
{
|
||||||
|
stack_push(T->stack, create_value_nil());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_pushinteger(TycheVM* T, int32_t value)
|
||||||
|
{
|
||||||
|
stack_push(T->stack, create_value_integer(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT tyc_type(TycheVM* T, int idx, TYC_TYPE* type)
|
||||||
|
{
|
||||||
|
VALUE v;
|
||||||
|
TYC_RESULT r = stack_at(T->stack, idx, &v);
|
||||||
|
if (r == T_OK)
|
||||||
|
*type = v.type;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT tyc_tointeger(TycheVM* T, int idx, int32_t* value)
|
||||||
|
{
|
||||||
|
VALUE v;
|
||||||
|
TYC_RESULT r;
|
||||||
|
TRY(stack_at(T->stack, idx, &v))
|
||||||
|
if (v.type != TT_INTEGER)
|
||||||
|
return T_ERR_TYPE_UNEXPECTED;
|
||||||
|
*value = value_integer(v);
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT tyc_tostring(TycheVM* T, int idx, const char** str)
|
||||||
|
{
|
||||||
|
VALUE v;
|
||||||
|
TYC_RESULT r;
|
||||||
|
TRY(stack_at(T->stack, idx, &v))
|
||||||
|
if (v.type == TT_STRING)
|
||||||
|
return heap_get_string(T->heap, value_idx(v), str);
|
||||||
|
else if (v.type == TT_STRING_CONST)
|
||||||
|
*str = code_const_string(T->code, value_idx(v));
|
||||||
|
else
|
||||||
|
return T_ERR_TYPE_UNEXPECTED;
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TYC_RESULT tyc_expr(TycheVM* T, TYC_EXPR op)
|
||||||
|
{
|
||||||
|
TYC_RESULT r;
|
||||||
|
VALUE v1, v2, result;
|
||||||
|
|
||||||
|
stack_pop(T->stack, &v2);
|
||||||
|
stack_pop(T->stack, &v1);
|
||||||
|
TRY(binary_expr(op, v1, v2, &result))
|
||||||
|
stack_push(T->stack, result);
|
||||||
|
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP
|
||||||
|
//
|
||||||
|
|
||||||
|
static TYC_RESULT step(TycheVM* T)
|
||||||
|
{
|
||||||
|
VALUE a;
|
||||||
|
TYC_RESULT r;
|
||||||
|
|
||||||
|
Location* loc = location_top(T);
|
||||||
|
Instruction inst = code_next_instruction(T->code, loc->function_id, loc->pc);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
debug_instruction(T, loc, inst);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (inst.operator) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// stack manipulation
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_PUSHN:
|
||||||
|
tyc_pushnil(T);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_PUSHI:
|
||||||
|
tyc_pushinteger(T, inst.operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_PUSHF:
|
||||||
|
if (inst.operand < 0 || inst.operand >= (int) code_n_functions(T->code))
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
TRY(stack_push(T->stack, create_value_idx(TT_FUNCTION, (uint32_t) inst.operand)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_PUSHC:
|
||||||
|
if (inst.operand < 0 || inst.operand >= (int) code_n_consts(T->code))
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
if (code_const_type(T->code, (size_t) inst.operand) == TC_STRING) {
|
||||||
|
TRY(stack_push(T->stack, create_value_idx(TT_STRING_CONST, inst.operand)))
|
||||||
|
} else {
|
||||||
|
abort(); // REAL consts not supported for now
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_POP:
|
||||||
|
TRY(stack_pop(T->stack, NULL))
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// local variables
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_PUSHV:
|
||||||
|
if (inst.operand <= 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
for (int i = 0; i < inst.operand; ++i)
|
||||||
|
tyc_pushnil(T);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_SET:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
TRY(stack_set(T->stack, inst.operand, a))
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_DUPV:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
TRY(stack_at(T->stack, inst.operand, &a))
|
||||||
|
stack_push(T->stack, a);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// expressions
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_SUM: TRY(tyc_expr(T, TX_SUM)); break;
|
||||||
|
case TO_SUB: TRY(tyc_expr(T, TX_SUB)); break;
|
||||||
|
case TO_MUL: TRY(tyc_expr(T, TX_MUL)); break;
|
||||||
|
case TO_IDIV: TRY(tyc_expr(T, TX_IDIV)); break;
|
||||||
|
case TO_EQ: TRY(tyc_expr(T, TX_EQ)); break;
|
||||||
|
case TO_NEQ: TRY(tyc_expr(T, TX_NEQ)); break;
|
||||||
|
case TO_LT: TRY(tyc_expr(T, TX_LT)); break;
|
||||||
|
case TO_LTE: TRY(tyc_expr(T, TX_LTE)); break;
|
||||||
|
case TO_GT: TRY(tyc_expr(T, TX_GT)); break;
|
||||||
|
case TO_GTE: TRY(tyc_expr(T, TX_GTE)); break;
|
||||||
|
case TO_AND: TRY(tyc_expr(T, TX_AND)); break;
|
||||||
|
case TO_OR: TRY(tyc_expr(T, TX_OR)); break;
|
||||||
|
case TO_XOR: TRY(tyc_expr(T, TX_XOR)); break;
|
||||||
|
case TO_POW: TRY(tyc_expr(T, TX_POW)); break;
|
||||||
|
case TO_SHL: TRY(tyc_expr(T, TX_SHL)); break;
|
||||||
|
case TO_SHR: TRY(tyc_expr(T, TX_SHR)); break;
|
||||||
|
case TO_MOD: TRY(tyc_expr(T, TX_MOD)); break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// function calls
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_CALL:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
enter_function(T, (uint16_t) inst.operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_RET:
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
TRY(stack_pop_fp(T->stack))
|
||||||
|
TRY(stack_push(T->stack, a))
|
||||||
|
location_pop(T);
|
||||||
|
goto dont_update_pc;
|
||||||
|
|
||||||
|
//
|
||||||
|
// jumps/branching
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_JMP:
|
||||||
|
if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id))
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
|
||||||
|
case TO_BZ:
|
||||||
|
if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id))
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
if (value_is_zero(a)) {
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_BNZ:
|
||||||
|
if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id))
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE;
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
if (!value_is_zero(a)) {
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return T_ERR_INVALID_OPCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - print stack
|
||||||
|
loc->pc += inst.sz;
|
||||||
|
|
||||||
|
dont_update_pc:
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
debug_stack(T);
|
||||||
|
#endif
|
||||||
|
return T_OK;
|
||||||
|
}
|
||||||
6
src/tests.yaml
Normal file
6
src/tests.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- name: Basic test
|
||||||
|
assembly: |
|
||||||
|
.func 0
|
||||||
|
pushi 2
|
||||||
|
ret
|
||||||
|
expected: 2
|
||||||
7
src/tyche.c
Normal file
7
src/tyche.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("This is not implemented yet.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
132
test/code-tests.lua
Normal file
132
test/code-tests.lua
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
return {
|
||||||
|
{
|
||||||
|
name = "VM: basic",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
pushi 2
|
||||||
|
pushi 3
|
||||||
|
sum
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
expected_stack_size = 1,
|
||||||
|
expected_stack_top = 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: integer expressions",
|
||||||
|
template = [[
|
||||||
|
.func 0
|
||||||
|
pushi %d
|
||||||
|
pushi %d
|
||||||
|
%s
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
scenarios = {
|
||||||
|
{ parameters = { 2, 5, 'sum' }, name = "Sum", expected_stack_top = 7 },
|
||||||
|
{ parameters = { 2, 5, 'sub' }, name = "Subtraction", expected_stack_top = -3 },
|
||||||
|
{ parameters = { 2, 5, 'mul' }, name = "Multiplication", expected_stack_top = 10 },
|
||||||
|
{ parameters = { 20, 3, 'idiv' }, name = "Integer division", expected_stack_top = 6 },
|
||||||
|
{ parameters = { 5, 5, 'eq' }, name = "Equality", expected_stack_top = 1 },
|
||||||
|
{ parameters = { 5, 5, 'neq' }, name = "Inequality", expected_stack_top = 0 },
|
||||||
|
{ parameters = { 4, 5, 'lt' }, name = "Less than", expected_stack_top = 1 },
|
||||||
|
{ parameters = { 5, 5, 'lt' }, name = "Less than", expected_stack_top = 0 },
|
||||||
|
{ parameters = { 4, 5, 'lte' }, name = "Less than or equal", expected_stack_top = 1 },
|
||||||
|
{ parameters = { 5, 5, 'lte' }, name = "Less than or equal", expected_stack_top = 1 },
|
||||||
|
{ parameters = { 5, 5, 'gt' }, name = "Greater than", expected_stack_top = 0 },
|
||||||
|
{ parameters = { 5, 5, 'gte' }, name = "Greater than or equal", expected_stack_top = 1 },
|
||||||
|
{ parameters = { 20, 5, 'and' }, name = "Logical AND", expected_stack_top = 4 },
|
||||||
|
{ parameters = { 20, 5, 'or' }, name = "Logical OR", expected_stack_top = 21 },
|
||||||
|
{ parameters = { 20, 5, 'xor' }, name = "Logical XOR", expected_stack_top = 17 },
|
||||||
|
{ parameters = { 2, 5, 'pow' }, name = "Power", expected_stack_top = 32 },
|
||||||
|
{ parameters = { 2, 5, 'shl' }, name = "Shift left", expected_stack_top = 64 },
|
||||||
|
{ parameters = { 20, 3, 'shr' }, name = "Shift right", expected_stack_top = 2},
|
||||||
|
{ parameters = { 20, 3, 'mod' }, name = "Modulo", expected_stack_top = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: local variables",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
pushv 2 ; local a, b
|
||||||
|
pushi 3 ; a = 3
|
||||||
|
set 0
|
||||||
|
pushi 4 ; b = 4
|
||||||
|
set 1
|
||||||
|
dupv 0 ; return a
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
expected_stack_size = 1,
|
||||||
|
expected_stack_top = 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: functions",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
pushf 1
|
||||||
|
pushi 2
|
||||||
|
pushi 3
|
||||||
|
call 2
|
||||||
|
ret
|
||||||
|
.func 1
|
||||||
|
dupv 0
|
||||||
|
dupv 1
|
||||||
|
sub
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
expected_stack_size = 1,
|
||||||
|
expected_stack_top = -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: jumps (jmp + bnz)",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
jmp @x1 ; 0
|
||||||
|
pushi 5 ; 3
|
||||||
|
@x1:
|
||||||
|
pushi 1 ; 5
|
||||||
|
bnz @x2 ; 7
|
||||||
|
pushi 1 ; 10
|
||||||
|
bz @x3 ; 12
|
||||||
|
@x2:
|
||||||
|
pushi 6 ; 15
|
||||||
|
ret ; 17
|
||||||
|
@x3:
|
||||||
|
pushi 7 ; 18
|
||||||
|
ret ; 20
|
||||||
|
]],
|
||||||
|
--debug_bytecode = true,
|
||||||
|
--decompile = true,
|
||||||
|
--debug = true,
|
||||||
|
expected_stack_top = 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: jumps (bz)",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
jmp @x1
|
||||||
|
pushi 5
|
||||||
|
@x1:
|
||||||
|
pushi 0
|
||||||
|
bnz @x2
|
||||||
|
pushi 0
|
||||||
|
bz @x3
|
||||||
|
@x2:
|
||||||
|
pushi 6
|
||||||
|
ret
|
||||||
|
@x3:
|
||||||
|
pushi 7
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
expected_stack_top = 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "VM: string from const",
|
||||||
|
code = [[
|
||||||
|
.const
|
||||||
|
0: "Hello"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
ret
|
||||||
|
]],
|
||||||
|
expected_stack_top = "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
496
test/tests.c
Normal file
496
test/tests.c
Normal file
@@ -0,0 +1,496 @@
|
|||||||
|
#include "../lib/priv.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#define EQ(a, b) (memcmp(a, b) == 0)
|
||||||
|
|
||||||
|
static void run_assembly_tests(void);
|
||||||
|
static void run_assembly_test(lua_State* L);
|
||||||
|
static void run_assembly_test_code(lua_State* L, bool debug, bool decompile, bool debug_bytecode);
|
||||||
|
static void run_assembly_test_template(lua_State* L, bool debug, bool decompile, bool debug_bytecode);
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
printf("## Values\n");
|
||||||
|
assert(value_type(create_value_integer(42)) == TT_INTEGER);
|
||||||
|
assert(value_integer(create_value_integer(-42)) == -42);
|
||||||
|
assert(fabsf(value_real(create_value_real(42.4f)) - 42.4f) < 0.00001f);
|
||||||
|
assert(value_idx(create_value_idx(TT_FUNCTION, 42)) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Stack\n");
|
||||||
|
|
||||||
|
Stack* s = stack_new();
|
||||||
|
|
||||||
|
stack_push(s, create_value_integer(10));
|
||||||
|
stack_push(s, create_value_integer(20));
|
||||||
|
stack_push(s, create_value_integer(30));
|
||||||
|
|
||||||
|
VALUE v;
|
||||||
|
assert(stack_size(s) == 3);
|
||||||
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 30);
|
||||||
|
assert(stack_at(s, -2, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
|
|
||||||
|
assert(stack_at(s, 3, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
assert(stack_at(s, -4, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
assert(stack_set(s, 1, create_value_integer(99)) == T_OK);
|
||||||
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 99);
|
||||||
|
assert(stack_at(s, -2, &v) == T_OK); assert(value_integer(v) == 99);
|
||||||
|
|
||||||
|
assert(stack_pop(s, NULL) == T_OK);
|
||||||
|
assert(stack_pop(s, NULL) == T_OK);
|
||||||
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
|
assert(stack_pop(s, NULL) == T_OK);
|
||||||
|
assert(stack_size(s) == 0);
|
||||||
|
|
||||||
|
assert(stack_pop(s, NULL) == T_ERR_STACK_UNDERFLOW);
|
||||||
|
|
||||||
|
stack_destroy(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Stack with frame pointer\n");
|
||||||
|
|
||||||
|
Stack* s = stack_new();
|
||||||
|
|
||||||
|
stack_push(s, create_value_integer(10));
|
||||||
|
stack_push(s, create_value_integer(20));
|
||||||
|
stack_push_fp(s);
|
||||||
|
stack_push(s, create_value_integer(30));
|
||||||
|
stack_push(s, create_value_integer(40));
|
||||||
|
stack_push(s, create_value_integer(50));
|
||||||
|
|
||||||
|
VALUE v;
|
||||||
|
assert(stack_size(s) == 3);
|
||||||
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 30);
|
||||||
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 40);
|
||||||
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 50);
|
||||||
|
assert(stack_at(s, -2, &v) == T_OK); assert(value_integer(v) == 40);
|
||||||
|
|
||||||
|
assert(stack_set(s, -2, create_value_integer(99)) == T_OK);
|
||||||
|
assert(stack_at(s, -2, &v) == T_OK); assert(value_integer(v) == 99);
|
||||||
|
|
||||||
|
assert(stack_at(s, 3, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
assert(stack_at(s, -4, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
stack_pop_fp(s);
|
||||||
|
|
||||||
|
assert(stack_size(s) == 2);
|
||||||
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
|
assert(stack_at(s, -2, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
|
|
||||||
|
assert(stack_at(s, 2, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
assert(stack_at(s, -3, &v) == T_ERR_STACK_ACCESS_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
stack_destroy(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Arrays\n");
|
||||||
|
|
||||||
|
Array* a = array_new();
|
||||||
|
assert(array_len(a) == 0);
|
||||||
|
|
||||||
|
array_set(a, 1, create_value_integer(40));
|
||||||
|
assert(array_len(a) == 2);
|
||||||
|
assert(value_type(array_get(a, 0)) == TT_NIL);
|
||||||
|
assert(value_type(array_get(a, 1)) == TT_INTEGER);
|
||||||
|
|
||||||
|
array_append(a, create_value_integer(50));
|
||||||
|
assert(array_len(a) == 3);
|
||||||
|
assert(value_integer(array_get(a, 2)) == 50);
|
||||||
|
|
||||||
|
array_set(a, 2, create_value_integer(60));
|
||||||
|
assert(array_len(a) == 3);
|
||||||
|
assert(value_integer(array_get(a, 2)) == 60);
|
||||||
|
|
||||||
|
array_destroy(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Table - integer index\n");
|
||||||
|
|
||||||
|
Heap* h = heap_new();
|
||||||
|
Table* t = table_new(h);
|
||||||
|
|
||||||
|
table_set(t, create_value_integer(10), create_value_integer(100));
|
||||||
|
table_set(t, create_value_integer(20), create_value_integer(200));
|
||||||
|
|
||||||
|
VALUE v;
|
||||||
|
assert(table_get(t, create_value_integer(10), &v) == T_OK); assert(value_integer(v) == 100);
|
||||||
|
assert(table_get(t, create_value_integer(20), &v) == T_OK); assert(value_integer(v) == 200);
|
||||||
|
|
||||||
|
table_del(t, create_value_integer(20));
|
||||||
|
assert(table_get(t, create_value_integer(10), &v) == T_OK);
|
||||||
|
assert(table_get(t, create_value_integer(20), &v) == T_ERR_TABLE_KEY_NOT_FOUND);
|
||||||
|
|
||||||
|
table_destroy(t);
|
||||||
|
heap_destroy(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Table - string index\n");
|
||||||
|
|
||||||
|
Heap* h = heap_new();
|
||||||
|
Table* t = table_new(h);
|
||||||
|
|
||||||
|
VALUE key1 = create_value_idx(TT_STRING, heap_add_string(h, "key1"));
|
||||||
|
VALUE key2 = create_value_idx(TT_STRING, heap_add_string(h, "key2"));
|
||||||
|
|
||||||
|
table_set(t, key1, create_value_integer(100));
|
||||||
|
table_set(t, key2, create_value_integer(200));
|
||||||
|
|
||||||
|
VALUE key1b = create_value_idx(TT_STRING, heap_add_string(h, "key1"));
|
||||||
|
VALUE key2b = create_value_idx(TT_STRING, heap_add_string(h, "key2"));
|
||||||
|
|
||||||
|
VALUE v;
|
||||||
|
assert(table_get(t, key1b, &v) == T_OK); assert(value_integer(v) == 100);
|
||||||
|
assert(table_get(t, key2b, &v) == T_OK); assert(value_integer(v) == 200);
|
||||||
|
|
||||||
|
table_del(t, key2b);
|
||||||
|
assert(table_get(t, key1b, &v) == T_OK);
|
||||||
|
assert(table_get(t, key2b, &v) == T_ERR_TABLE_KEY_NOT_FOUND);
|
||||||
|
|
||||||
|
table_destroy(t);
|
||||||
|
heap_destroy(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Heap - strings\n");
|
||||||
|
|
||||||
|
Heap* h = heap_new();
|
||||||
|
|
||||||
|
HEAP_KEY key1 = heap_add_string(h, "hello");
|
||||||
|
HEAP_KEY key2 = heap_add_string(h, "world");
|
||||||
|
|
||||||
|
const char* value;
|
||||||
|
assert(heap_get_string(h, key1, &value) == T_OK); assert(strcmp(value, "hello") == 0);
|
||||||
|
assert(heap_get_string(h, key2, &value) == T_OK); assert(strcmp(value, "world") == 0);
|
||||||
|
assert(heap_get_string(h, 1000, &value) == T_ERR_HEAP_KEY_NOT_FOUND);
|
||||||
|
|
||||||
|
heap_destroy(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Heap - string GC\n");
|
||||||
|
|
||||||
|
Stack* s = stack_new();
|
||||||
|
Heap* h = heap_new();
|
||||||
|
|
||||||
|
stack_push(s, create_value_idx(TT_STRING, heap_add_string(h, "item1")));
|
||||||
|
stack_push(s, create_value_idx(TT_STRING, heap_add_string(h, "item2")));
|
||||||
|
stack_push(s, create_value_idx(TT_STRING, heap_add_string(h, "item3")));
|
||||||
|
|
||||||
|
size_t v_sz;
|
||||||
|
VALUE* v_idx;
|
||||||
|
|
||||||
|
assert(heap_size(h) == 3);
|
||||||
|
v_sz = stack_collectable_array(s, &v_idx);
|
||||||
|
heap_gc(h, v_idx, v_sz);
|
||||||
|
free(v_idx);
|
||||||
|
assert(heap_size(h) == 3);
|
||||||
|
|
||||||
|
stack_pop(s, NULL);
|
||||||
|
|
||||||
|
assert(heap_size(h) == 3);
|
||||||
|
v_sz = stack_collectable_array(s, &v_idx);
|
||||||
|
heap_gc(h, v_idx, v_sz);
|
||||||
|
free(v_idx);
|
||||||
|
assert(heap_size(h) == 2);
|
||||||
|
|
||||||
|
stack_pop(s, NULL);
|
||||||
|
v_sz = stack_collectable_array(s, &v_idx);
|
||||||
|
heap_gc(h, v_idx, v_sz);
|
||||||
|
free(v_idx);
|
||||||
|
assert(heap_size(h) == 1);
|
||||||
|
|
||||||
|
heap_destroy(h);
|
||||||
|
stack_destroy(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Bytecode\n");
|
||||||
|
const char* assembly_code =
|
||||||
|
".const\n"
|
||||||
|
" 0: 3.14\n"
|
||||||
|
" 1: \"Hello world\"\n"
|
||||||
|
"\n"
|
||||||
|
".func 0\n"
|
||||||
|
" pushi 2 ; this is a comment\n"
|
||||||
|
" pushi -3\n"
|
||||||
|
" sum\n"
|
||||||
|
" ret\n"
|
||||||
|
".func 1\n"
|
||||||
|
" pushi 5000\n"
|
||||||
|
" ret";
|
||||||
|
|
||||||
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
|
assert(code_assemble(assembly_code, &bytecode, &bytecode_sz) == T_OK);
|
||||||
|
|
||||||
|
Code* code = code_new();
|
||||||
|
|
||||||
|
assert(code_load_bytecode(code, bytecode, bytecode_sz) == T_OK);
|
||||||
|
|
||||||
|
assert(code_n_consts(code) == 2);
|
||||||
|
assert(code_const_type(code, 0) == TC_REAL);
|
||||||
|
assert(code_const_type(code, 1) == TC_STRING);
|
||||||
|
assert(code_const_real(code, 0) > 3.13f && code_const_real(code, 0) < 3.15f);
|
||||||
|
assert(strcmp(code_const_string(code, 1), "Hello world") == 0);
|
||||||
|
assert(code_n_functions(code) == 2);
|
||||||
|
assert(code_function_sz(code, 0) == 6);
|
||||||
|
assert(code_function_sz(code, 1) == 4);
|
||||||
|
|
||||||
|
uint32_t addr = 0;
|
||||||
|
Instruction inst = code_next_instruction(code, 0, addr);
|
||||||
|
assert(inst.operator == TO_PUSHI);
|
||||||
|
assert(inst.operand == 2);
|
||||||
|
assert(inst.sz == 2);
|
||||||
|
addr += inst.sz;
|
||||||
|
|
||||||
|
inst = code_next_instruction(code, 0, addr);
|
||||||
|
assert(inst.operator == TO_PUSHI);
|
||||||
|
assert(inst.operand == -3);
|
||||||
|
addr += inst.sz;
|
||||||
|
|
||||||
|
inst = code_next_instruction(code, 0, addr);
|
||||||
|
assert(inst.operator == TO_SUM);
|
||||||
|
assert(inst.operand == 0);
|
||||||
|
addr += inst.sz;
|
||||||
|
|
||||||
|
inst = code_next_instruction(code, 1, 0);
|
||||||
|
assert(inst.operator == TO_PUSHI);
|
||||||
|
assert(inst.operand == 5000);
|
||||||
|
assert(inst.sz == 3);
|
||||||
|
|
||||||
|
code_destroy(code);
|
||||||
|
free(bytecode);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Bytecode - labels\n");
|
||||||
|
const char* assembly_code =
|
||||||
|
".func 0\n"
|
||||||
|
" jmp @my_label\n"
|
||||||
|
" pushi \n"
|
||||||
|
"@my_label:\n"
|
||||||
|
" ret";
|
||||||
|
|
||||||
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
|
assert(code_assemble(assembly_code, &bytecode, &bytecode_sz) == T_OK);
|
||||||
|
|
||||||
|
Code* code = code_new();
|
||||||
|
assert(code_load_bytecode(code, bytecode, bytecode_sz) == T_OK);
|
||||||
|
|
||||||
|
Instruction inst = code_next_instruction(code, 0, 0);
|
||||||
|
assert(inst.operator == TO_JMP);
|
||||||
|
assert(inst.operand == 4);
|
||||||
|
assert(inst.sz == 3);
|
||||||
|
|
||||||
|
code_destroy(code);
|
||||||
|
free(bytecode);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## VM - Basic\n");
|
||||||
|
|
||||||
|
TycheVM* T = tyc_new();
|
||||||
|
|
||||||
|
tyc_pushinteger(T, 2);
|
||||||
|
tyc_pushinteger(T, 3);
|
||||||
|
assert(tyc_expr(T, TX_SUM) == T_OK);
|
||||||
|
int32_t result; assert(tyc_tointeger(T, -1, &result) == T_OK);
|
||||||
|
assert(result == 5);
|
||||||
|
|
||||||
|
tyc_destroy(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("## Assembly tests\n");
|
||||||
|
run_assembly_tests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_assembly_tests(void)
|
||||||
|
{
|
||||||
|
lua_State* L = luaL_newstate();
|
||||||
|
luaL_openlibs(L);
|
||||||
|
|
||||||
|
int r = luaL_loadfile(L, "./test/code-tests.lua");
|
||||||
|
assert(r == LUA_OK);
|
||||||
|
lua_call(L, 0, 1);
|
||||||
|
assert(lua_istable(L, -1));
|
||||||
|
|
||||||
|
size_t len = (size_t) luaL_len(L, -1);
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
lua_geti(L, -1, (int)i + 1);
|
||||||
|
run_assembly_test(L);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_close(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_assembly_test(lua_State* L)
|
||||||
|
{
|
||||||
|
// print test name
|
||||||
|
lua_getfield(L, -1, "name");
|
||||||
|
printf(" - %s\n", lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// debug?
|
||||||
|
lua_getfield(L, -1, "debug");
|
||||||
|
bool debug = lua_isboolean(L, -1) && lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// decompile?
|
||||||
|
lua_getfield(L, -1, "decompile");
|
||||||
|
bool decompile = lua_isboolean(L, -1) && lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// decompile?
|
||||||
|
lua_getfield(L, -1, "debug_bytecode");
|
||||||
|
bool debug_bytecode = lua_isboolean(L, -1) && lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// has code?
|
||||||
|
lua_getfield(L, -1, "code");
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
run_assembly_test_code(L, debug, decompile, debug_bytecode);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// has template
|
||||||
|
lua_getfield(L, -1, "template");
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
run_assembly_test_template(L, debug, decompile, debug_bytecode);
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_expected_top(lua_State* L, TycheVM* T)
|
||||||
|
{
|
||||||
|
// check stack size
|
||||||
|
lua_getfield(L, -1, "expected_stack_size");
|
||||||
|
if (!lua_isnil(L, -1))
|
||||||
|
assert(tyc_stack_size(T) == (size_t) lua_tointeger(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// check stack top
|
||||||
|
lua_getfield(L, -1, "expected_stack_top");
|
||||||
|
if (lua_isinteger(L, -1)) {
|
||||||
|
TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_INTEGER);
|
||||||
|
int32_t v; assert(tyc_tointeger(T, -1, &v) == T_OK); assert(v == lua_tointeger(L, -1));
|
||||||
|
} else if (lua_isstring(L, -1)) {
|
||||||
|
TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_STRING || type == TT_STRING_CONST);
|
||||||
|
const char* str; assert(tyc_tostring(T, -1, &str) == T_OK); assert(strcmp(str, lua_tostring(L, -1)) == 0);
|
||||||
|
} else if (!lua_isnil(L, -1)) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_assembly_test_code(lua_State* L, bool debug, bool decompile, bool debug_bytecode)
|
||||||
|
{
|
||||||
|
TycheVM* T = tyc_new();
|
||||||
|
tyc_debug_to_console(T, debug);
|
||||||
|
|
||||||
|
// load code
|
||||||
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
|
lua_getfield(L, -1, "code");
|
||||||
|
assert(code_assemble(lua_tostring(L, -1), &bytecode, &bytecode_sz) == T_OK);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// run code
|
||||||
|
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
||||||
|
if (debug_bytecode)
|
||||||
|
tyc_print_bytecode(T);
|
||||||
|
if (decompile)
|
||||||
|
tyc_assembly_decompile(T);
|
||||||
|
assert(tyc_call(T, 0) == T_OK);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
check_expected_top(L, T);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
free(bytecode);
|
||||||
|
tyc_destroy(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_assembly_test_template(lua_State* L, bool debug, bool decompile, bool debug_bytecode)
|
||||||
|
{
|
||||||
|
lua_getfield(L, -1, "template");
|
||||||
|
char* template = strdup(lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "scenarios");
|
||||||
|
assert(!lua_isnil(L, -1));
|
||||||
|
|
||||||
|
long n_scenarios = luaL_len(L, -1);
|
||||||
|
for (long i = 0; i < n_scenarios; ++i) {
|
||||||
|
lua_geti(L, -1, (int)i + 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "name");
|
||||||
|
printf(" .. %s\n", lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// format code
|
||||||
|
luaL_dostring(L, "return string.format");
|
||||||
|
assert(lua_isfunction(L, -1));
|
||||||
|
lua_pushstring(L, template);
|
||||||
|
|
||||||
|
lua_getfield(L, -3, "parameters");
|
||||||
|
assert(!lua_isnil(L, -1));
|
||||||
|
int n_params = (int) luaL_len(L, -1);
|
||||||
|
for (int j = 0; j < n_params; ++j)
|
||||||
|
lua_geti(L, -(j + 1), j + 1);
|
||||||
|
lua_remove(L, -(n_params + 1));
|
||||||
|
|
||||||
|
lua_call(L, n_params + 1, 1);
|
||||||
|
char* formatted_code = strdup(lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// run code
|
||||||
|
TycheVM* T = tyc_new();
|
||||||
|
tyc_debug_to_console(T, debug);
|
||||||
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
|
assert(code_assemble(formatted_code, &bytecode, &bytecode_sz) == T_OK);
|
||||||
|
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
||||||
|
if (debug_bytecode)
|
||||||
|
tyc_print_bytecode(T);
|
||||||
|
if (decompile)
|
||||||
|
tyc_assembly_decompile(T);
|
||||||
|
assert(tyc_call(T, 0) == T_OK);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
check_expected_top(L, T);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
free(bytecode);
|
||||||
|
tyc_destroy(T);
|
||||||
|
free(formatted_code);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
free(template);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user