Adds foundation for Wren to replace LUA

master
Joey Pollack 3 years ago
parent 95d9630b6b
commit 50f338c68a

@ -94,7 +94,8 @@ set(LUNARIUM_SRC
"src/assets/loaders/asset_index.cpp"
"src/input/keyboard.cpp"
"src/input/input_manager.cpp"
"src/scripting/script_manager.cpp"
"src/scripting/wren_state.cpp"
"src/scripting/wren_script.cpp"
"src/scripting/coreAPI.cpp"
"src/world/world.cpp"
"src/world/entity.cpp"
@ -128,10 +129,6 @@ if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/glm/CMakeLists.txt")
message(FATAL_ERROR "GLM submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/lua/CMakeLists.txt")
message(FATAL_ERROR "LUA submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/freetype/CMakeLists.txt")
message(FATAL_ERROR "FREETYPE submodule was not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
@ -152,15 +149,15 @@ add_subdirectory(external/glm)
# add dearimgui
add_subdirectory(external/dearimgui)
# add lua -- https://github.com/walterschell/Lua
add_subdirectory(external/lua)
# add freetype
add_subdirectory(external/freetype)
# add box2d
add_subdirectory(external/box2d)
# add wren
add_subdirectory(external/wren)
# add nativefiledialog
add_subdirectory(external/nativefiledialog-extended)
@ -183,14 +180,13 @@ target_include_directories(${PROJECT_NAME}
PUBLIC external
PUBLIC external/glfw/include
PUBLIC external/glm
PUBLIC external/lua/lua5.4.3/include
PUBLIC external/dearimgui
PUBLIC external/pugixml/src
PUBLIC external/glad/include
PUBLIC external/freetype/include
PUBLIC external/box2d/include
PUBLIC external/entt
PUBLIC external/nativefiledialog-extended/src/include
PUBLIC external/wren/include
)
target_link_directories(${PROJECT_NAME}
@ -202,9 +198,10 @@ target_link_directories(${PROJECT_NAME}
PRIVATE external/freetype/src
PRIVATE external/box2d/bin
PRIVATE external/nativefiledialog-extended
PRIVATE external/wren
)
target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui lua_static freetype nfd testbed)
target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui freetype nfd testbed wren)
if (NOT NO_EDITOR)
target_link_libraries(${PROJECT_NAME} editor)

@ -1,6 +1,6 @@
Editor:
☐ Editor Assets need to switch to using UUIDs @critical
✔ Remove Entity @done(22-10-14 18:28)
✔ Remove Component @done(22-10-14 18:28)
✔ Add Entity children @done(22-09-14 15:07)

@ -1,53 +0,0 @@
// The MIT License (MIT)
// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
// 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.
// This file was generated with a script.
// Generated 2020-10-03 21:34:25.034794 UTC
// This header was generated with sol v3.2.1 (revision 48eea7b5)
// https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_CONFIG_HPP
#define SOL_SINGLE_CONFIG_HPP
// beginning of sol/config.hpp
/* Base, empty configuration file!
To override, place a file in your include paths of the form:
. (your include path here)
| sol (directory, or equivalent)
| config.hpp (your config.hpp file)
So that when sol2 includes the file
#include <sol/config.hpp>
it gives you the configuration values you desire. Configuration values can be
seen in the safety.rst of the doc/src, or at
https://sol2.readthedocs.io/en/latest/safety.html ! You can also pass them through
the build system, or the command line options of your compiler.
*/
// end of sol/config.hpp
#endif // SOL_SINGLE_CONFIG_HPP

@ -1,828 +0,0 @@
// The MIT License (MIT)
// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
// 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.
// This file was generated with a script.
// Generated 2020-10-03 21:34:25.022965 UTC
// This header was generated with sol v3.2.1 (revision 48eea7b5)
// https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP
#define SOL_SINGLE_INCLUDE_FORWARD_HPP
// beginning of sol/forward.hpp
#ifndef SOL_FORWARD_HPP
#define SOL_FORWARD_HPP
// beginning of sol/version.hpp
#include <sol/config.hpp>
#include <cstdint>
#define SOL_VERSION_MAJOR 3
#define SOL_VERSION_MINOR 5
#define SOL_VERSION_PATCH 0
#define SOL_VERSION_STRING "3.5.0"
#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH))
#define SOL_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0)
#define SOL_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0)
#define SOL_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3)
#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0)
#define SOL_ON |
#define SOL_OFF ^
#define SOL_DEFAULT_ON +
#define SOL_DEFAULT_OFF -
#if defined(_MSC_VER)
#define SOL_COMPILER_CLANG_I_ SOL_OFF
#define SOL_COMPILER_GCC_I_ SOL_OFF
#define SOL_COMPILER_EDG_I_ SOL_OFF
#define SOL_COMPILER_VCXX_I_ SOL_ON
#elif defined(__clang__)
#define SOL_COMPILER_CLANG_I_ SOL_ON
#define SOL_COMPILER_GCC_I_ SOL_OFF
#define SOL_COMPILER_EDG_I_ SOL_OFF
#define SOL_COMPILER_VCXX_I_ SOL_OFF
#elif defined(__GNUC__)
#define SOL_COMPILER_CLANG_I_ SOL_OFF
#define SOL_COMPILER_GCC_I_ SOL_ON
#define SOL_COMPILER_EDG_I_ SOL_OFF
#define SOL_COMPILER_VCXX_I_ SOL_OFF
#else
#define SOL_COMPILER_CLANG_I_ SOL_OFF
#define SOL_COMPILER_GCC_I_ SOL_OFF
#define SOL_COMPILER_EDG_I_ SOL_OFF
#define SOL_COMPILER_VCXX_I_ SOL_OFF
#endif
#if defined(__MINGW32__)
#define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_ON
#else
#define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_OFF
#endif
#if SIZE_MAX <= 0xFFFFULL
#define SOL_PLATFORM_X16_I_ SOL_ON
#define SOL_PLATFORM_X86_I_ SOL_OFF
#define SOL_PLATFORM_X64_I_ SOL_OFF
#elif SIZE_MAX <= 0xFFFFFFFFULL
#define SOL_PLATFORM_X16_I_ SOL_OFF
#define SOL_PLATFORM_X86_I_ SOL_ON
#define SOL_PLATFORM_X64_I_ SOL_OFF
#else
#define SOL_PLATFORM_X16_I_ SOL_OFF
#define SOL_PLATFORM_X86_I_ SOL_OFF
#define SOL_PLATFORM_X64_I_ SOL_ON
#endif
#define SOL_PLATFORM_ARM32_I_ SOL_OFF
#define SOL_PLATFORM_ARM64_I_ SOL_OFF
#if defined(_WIN32)
#define SOL_PLATFORM_WINDOWS_I_ SOL_ON
#else
#define SOL_PLATFORM_WINDOWS_I_ SOL_OFF
#endif
#if defined(__APPLE__)
#define SOL_PLATFORM_APPLE_I_ SOL_ON
#else
#define SOL_PLATFORM_APPLE_I_ SOL_OFF
#endif
#if defined(__unix__)
#define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON
#else
#define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF
#endif
#if defined(__linux__)
#define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON
#else
#define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF
#endif
#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF
#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF
#if defined(SOL_IN_DEBUG_DETECTED)
#if SOL_IN_DEBUG_DETECTED != 0
#define SOL_DEBUG_BUILD_I_ SOL_ON
#else
#define SOL_DEBUG_BUILD_I_ SOL_OFF
#endif
#elif !defined(NDEBUG)
#if SOL_IS_ON(SOL_COMPILER_VCXX_I_) && defined(_DEBUG)
#define SOL_DEBUG_BUILD_I_ SOL_ON
#elif (SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_)) && !defined(__OPTIMIZE__)
#define SOL_DEBUG_BUILD_I_ SOL_ON
#else
#define SOL_DEBUG_BUILD_I_ SOL_OFF
#endif
#else
#define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF
#endif // We are in a debug mode of some sort
#if defined(SOL_NO_EXCEPTIONS)
#if (SOL_NO_EXCEPTIONS != 0)
#define SOL_EXCEPTIONS_I_ SOL_OFF
#else
#define SOL_EXCEPTIONS_I_ SOL_ON
#endif
#elif SOL_IS_ON(SOL_COMPILER_VCXX_I_)
#if !defined(_CPPUNWIND)
#define SOL_EXCEPTIONS_I_ SOL_OFF
#else
#define SOL_EXCEPTIONS_I_ SOL_ON
#endif
#elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_)
#if !defined(__EXCEPTIONS)
#define SOL_EXCEPTIONS_I_ SOL_OFF
#else
#define SOL_EXCEPTIONS_I_ SOL_ON
#endif
#else
#define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_NO_RTTI)
#if (SOL_NO_RTTI != 0)
#define SOL_RTTI_I_ SOL_OFF
#else
#define SOL_RTTI_I_ SOL_ON
#endif
#elif SOL_IS_ON(SOL_COMPILER_VCXX_I_)
#if !defined(_CPPRTTI)
#define SOL_RTTI_I_ SOL_OFF
#else
#define SOL_RTTI_I_ SOL_ON
#endif
#elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_)
#if !defined(__GXX_RTTI)
#define SOL_RTTI_I_ SOL_OFF
#else
#define SOL_RTTI_I_ SOL_ON
#endif
#else
#define SOL_RTTI_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_NO_THREAD_LOCAL) && (SOL_NO_THREAD_LOCAL != 0)
#define SOL_USE_THREAD_LOCAL_I_ SOL_OFF
#else
#define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON
#endif // thread_local keyword is bjorked on some platforms
#if defined(SOL_ALL_SAFETIES_ON) && (SOL_ALL_SAFETIES_ON != 0)
#define SOL_ALL_SAFETIES_ON_I_ SOL_ON
#else
#define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF
#endif
#if defined(SOL_SAFE_GETTER) && (SOL_SAFE_GETTER != 0)
#define SOL_SAFE_GETTER_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_GETTER_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_USERTYPE) && (SOL_SAFE_USERTYPE != 0)
#define SOL_SAFE_USERTYPE_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_USERTYPE_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_REFERENCES) && (SOL_SAFE_REFERENCES != 0)
#define SOL_SAFE_REFERENCES_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_REFERENCES_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF
#endif
#endif
#if (defined(SOL_SAFE_FUNCTIONS) && (SOL_SAFE_FUNCTIONS != 0)) \
|| (defined(SOL_SAFE_FUNCTION_OBJECTS) && (SOL_SAFE_FUNCTION_OBJECTS != 0))
#define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_FUNCTION_CALLS) && (SOL_SAFE_FUNCTION_CALLS != 0)
#define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_PROXIES) && (SOL_SAFE_PROXIES != 0)
#define SOL_SAFE_PROXIES_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_PROXIES_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_NUMERICS) && (SOL_SAFE_NUMERICS != 0)
#define SOL_SAFE_NUMERICS_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_NUMERICS_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_SAFE_STACK_CHECK) && (SOL_SAFE_STACK_CHECK != 0)
#define SOL_SAFE_STACK_CHECK_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_SAFE_STACK_CHECK_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON
#else
#define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF
#endif
#endif
#if (defined(SOL_NO_CHECK_NUMBER_PRECISION) && (SOL_NO_CHECK_NUMBER_PRECISION != 0)) \
|| (defined(SOL_NO_CHECKING_NUMBER_PRECISION) && (SOL_NO_CHECKING_NUMBER_PRECISION != 0))
#define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
#elif SOL_IS_ON(SOL_SAFE_NUMERICS_I_)
#define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON
#else
#define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_STRINGS_ARE_NUMBERS)
#if (SOL_STRINGS_ARE_NUMBERS != 0)
#define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON
#else
#define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF
#endif
#else
#define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF
#endif
#if defined(SOL_ENABLE_INTEROP) && (SOL_ENABLE_INTEROP != 0) \
|| defined(SOL_USE_INTEROP) && (SOL_USE_INTEROP != 0)
#define SOL_USE_INTEROP_I_ SOL_ON
#else
#define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF
#endif
#if defined(SOL_NO_NIL)
#if (SOL_NO_NIL != 0)
#define SOL_NIL_I_ SOL_OFF
#else
#define SOL_NIL_I_ SOL_ON
#endif
#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil)
#define SOL_NIL_I_ SOL_DEFAULT_OFF
#else
#define SOL_NIL_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_USERTYPE_TYPE_BINDING_INFO)
#if (SOL_USERTYPE_TYPE_BINDING_INFO != 0)
#define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON
#else
#define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF
#endif
#else
#define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON
#endif // We should generate a my_type.__type table with lots of class information for usertypes
#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT)
#if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0)
#define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
#else
#define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
#endif
#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES)
#if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0)
#define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
#else
#define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
#endif
#else
#define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON
#endif // make is_automagical on/off by default
#if defined(SOL_STD_VARIANT)
#if (SOL_STD_VARIANT != 0)
#define SOL_STD_VARIANT_I_ SOL_ON
#else
#define SOL_STD_VARIANT_I_ SOL_OFF
#endif
#else
#if SOL_IS_ON(SOL_COMPILER_CLANG_I_) && SOL_IS_ON(SOL_PLATFORM_APPLE_I_)
#if defined(__has_include)
#if __has_include(<variant>)
#define SOL_STD_VARIANT_I_ SOL_ON
#else
#define SOL_STD_VARIANT_I_ SOL_OFF
#endif
#else
#define SOL_STD_VARIANT_I_ SOL_OFF
#endif
#else
#define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
#endif
#endif // make is_automagical on/off by default
#if defined(SOL_NOEXCEPT_FUNCTION_TYPE)
#if (SOL_NOEXCEPT_FUNCTION_TYPE != 0)
#define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
#else
#define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
#endif
#else
#if defined(__cpp_noexcept_function_type)
#define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
#elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L))
// There is a bug in the VC++ compiler??
// on /std:c++latest under x86 conditions (VS 15.5.2),
// compiler errors are tossed for noexcept markings being on function types
// that are identical in every other way to their non-noexcept marked types function types...
// VS 2019: There is absolutely a bug.
#define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
#else
#define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON
#endif
#endif // noexcept is part of a function's type
#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0
#define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE
#else
#define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024
#endif
#if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
#define SOL_ID_SIZE_I_ SOL_ID_SIZE
#else
#define SOL_ID_SIZE_I_ 512
#endif
#if defined(LUA_IDSIZE) && LUA_IDSIZE > 0
#define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE
#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
#define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE
#else
#define SOL_FILE_ID_SIZE_I_ 2048
#endif
#if defined(SOL_PRINT_ERRORS)
#if (SOL_PRINT_ERRORS != 0)
#define SOL_PRINT_ERRORS_I_ SOL_ON
#else
#define SOL_PRINT_ERRORS_I_ SOL_OFF
#endif
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_PRINT_ERRORS_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON
#else
#define SOL_PRINT_ERRORS_I_ SOL_OFF
#endif
#endif
#if defined(SOL_DEFAULT_PASS_ON_ERROR) && (SOL_DEFAULT_PASS_ON_ERROR != 0)
#define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
#else
#if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_)
#define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
#elif SOL_IS_ON(SOL_DEBUG_BUILD_I_)
#define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_ON
#else
#define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF
#endif
#endif
#if defined(SOL_USING_CXX_LUA)
#if (SOL_USING_CXX_LUA != 0)
#define SOL_USE_CXX_LUA_I_ SOL_ON
#else
#define SOL_USE_CXX_LUA_I_ SOL_OFF
#endif
#elif defined(SOL_USE_CXX_LUA)
#if (SOL_USE_CXX_LUA != 0)
#define SOL_USE_CXX_LUA_I_ SOL_ON
#else
#define SOL_USE_CXX_LUA_I_ SOL_OFF
#endif
#else
#define SOL_USE_CXX_LUA_I_ SOL_OFF
#endif
#if defined(SOL_USING_CXX_LUAJIT)
#if (SOL_USING_CXX_LUA != 0)
#define SOL_USE_CXX_LUAJIT_I_ SOL_ON
#else
#define SOL_USE_CXX_LUAJIT_I_ SOL_OFF
#endif
#elif defined(SOL_USE_CXX_LUAJIT)
#if (SOL_USE_CXX_LUA != 0)
#define SOL_USE_CXX_LUAJIT_I_ SOL_ON
#else
#define SOL_USE_CXX_LUAJIT_I_ SOL_OFF
#endif
#else
#define SOL_USE_CXX_LUAJIT_I_ SOL_OFF
#endif
#if defined(SOL_NO_LUA_HPP)
#if (SOL_NO_LUA_HPP != 0)
#define SOL_USE_LUA_HPP_I_ SOL_OFF
#else
#define SOL_USE_LUA_HPP_I_ SOL_ON
#endif
#elif defined(SOL_USING_CXX_LUA)
#define SOL_USE_LUA_HPP_I_ SOL_OFF
#elif defined(__has_include)
#if __has_include(<lua.hpp>)
#define SOL_USE_LUA_HPP_I_ SOL_ON
#else
#define SOL_USE_LUA_HPP_I_ SOL_OFF
#endif
#else
#define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_CONTAINERS_START)
#define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START
#elif defined(SOL_CONTAINERS_START_INDEX)
#define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX
#elif defined(SOL_CONTAINER_START_INDEX)
#define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX
#else
#define SOL_CONTAINER_START_INDEX_I_ 1
#endif
#if defined (SOL_NO_MEMORY_ALIGNMENT)
#if (SOL_NO_MEMORY_ALIGNMENT != 0)
#define SOL_ALIGN_MEMORY_I_ SOL_OFF
#else
#define SOL_ALIGN_MEMORY_I_ SOL_ON
#endif
#else
#define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_USE_BOOST)
#if (SOL_USE_BOOST != 0)
#define SOL_USE_BOOST_I_ SOL_ON
#else
#define SOL_USE_BOOST_I_ SOL_OFF
#endif
#else
#define SOL_USE_BOOST_I_ SOL_OFF
#endif
#if defined(SOL_USE_UNSAFE_BASE_LOOKUP)
#if (SOL_USE_UNSAFE_BASE_LOOKUP != 0)
#define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON
#else
#define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
#endif
#else
#define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
#endif
#if defined(SOL_INSIDE_UNREAL)
#if (SOL_INSIDE_UNREAL != 0)
#define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
#else
#define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF
#endif
#else
#if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER)
#define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
#else
#define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF
#endif
#endif
#if defined(SOL_NO_COMPAT)
#if (SOL_NO_COMPAT != 0)
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF
#else
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON
#endif
#else
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
#endif
#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
#if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
#endif
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
#endif
#if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6)
// MinGW is off its rocker in some places...
#define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
#else
#define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF
#endif
// end of sol/version.hpp
#include <utility>
#include <type_traits>
#include <string_view>
#if SOL_IS_ON(SOL_USE_CXX_LUA_I_) || SOL_IS_ON(SOL_USE_CXX_LUAJIT_I_)
struct lua_State;
#else
extern "C" {
struct lua_State;
}
#endif // C++ Mangling for Lua vs. Not
namespace sol {
enum class type;
class stateless_reference;
template <bool b>
class basic_reference;
using reference = basic_reference<false>;
using main_reference = basic_reference<true>;
class stateless_stack_reference;
class stack_reference;
template <typename A>
class basic_bytecode;
struct lua_value;
struct proxy_base_tag;
template <typename>
struct proxy_base;
template <typename, typename>
struct table_proxy;
template <bool, typename>
class basic_table_core;
template <bool b>
using table_core = basic_table_core<b, reference>;
template <bool b>
using main_table_core = basic_table_core<b, main_reference>;
template <bool b>
using stack_table_core = basic_table_core<b, stack_reference>;
template <typename base_type>
using basic_table = basic_table_core<false, base_type>;
using table = table_core<false>;
using global_table = table_core<true>;
using main_table = main_table_core<false>;
using main_global_table = main_table_core<true>;
using stack_table = stack_table_core<false>;
using stack_global_table = stack_table_core<true>;
template <typename>
struct basic_lua_table;
using lua_table = basic_lua_table<reference>;
using stack_lua_table = basic_lua_table<stack_reference>;
template <typename T, typename base_type>
class basic_usertype;
template <typename T>
using usertype = basic_usertype<T, reference>;
template <typename T>
using stack_usertype = basic_usertype<T, stack_reference>;
template <typename base_type>
class basic_metatable;
using metatable = basic_metatable<reference>;
using stack_metatable = basic_metatable<stack_reference>;
template <typename base_t>
struct basic_environment;
using environment = basic_environment<reference>;
using main_environment = basic_environment<main_reference>;
using stack_environment = basic_environment<stack_reference>;
template <typename T, bool>
class basic_function;
template <typename T, bool, typename H>
class basic_protected_function;
using unsafe_function = basic_function<reference, false>;
using safe_function = basic_protected_function<reference, false, reference>;
using main_unsafe_function = basic_function<main_reference, false>;
using main_safe_function = basic_protected_function<main_reference, false, reference>;
using stack_unsafe_function = basic_function<stack_reference, false>;
using stack_safe_function = basic_protected_function<stack_reference, false, reference>;
using stack_aligned_unsafe_function = basic_function<stack_reference, true>;
using stack_aligned_safe_function = basic_protected_function<stack_reference, true, reference>;
using protected_function = safe_function;
using main_protected_function = main_safe_function;
using stack_protected_function = stack_safe_function;
using stack_aligned_protected_function = stack_aligned_safe_function;
#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_)
using function = protected_function;
using main_function = main_protected_function;
using stack_function = stack_protected_function;
using stack_aligned_function = stack_aligned_safe_function;
#else
using function = unsafe_function;
using main_function = main_unsafe_function;
using stack_function = stack_unsafe_function;
using stack_aligned_function = stack_aligned_unsafe_function;
#endif
using stack_aligned_stack_handler_function = basic_protected_function<stack_reference, true, stack_reference>;
struct unsafe_function_result;
struct protected_function_result;
using safe_function_result = protected_function_result;
#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_)
using function_result = safe_function_result;
#else
using function_result = unsafe_function_result;
#endif
template <typename base_t>
class basic_object_base;
template <typename base_t>
class basic_object;
template <typename base_t>
class basic_userdata;
template <typename base_t>
class basic_lightuserdata;
template <typename base_t>
class basic_coroutine;
template <typename base_t>
class basic_thread;
using object = basic_object<reference>;
using userdata = basic_userdata<reference>;
using lightuserdata = basic_lightuserdata<reference>;
using thread = basic_thread<reference>;
using coroutine = basic_coroutine<reference>;
using main_object = basic_object<main_reference>;
using main_userdata = basic_userdata<main_reference>;
using main_lightuserdata = basic_lightuserdata<main_reference>;
using main_coroutine = basic_coroutine<main_reference>;
using stack_object = basic_object<stack_reference>;
using stack_userdata = basic_userdata<stack_reference>;
using stack_lightuserdata = basic_lightuserdata<stack_reference>;
using stack_thread = basic_thread<stack_reference>;
using stack_coroutine = basic_coroutine<stack_reference>;
struct stack_proxy_base;
struct stack_proxy;
struct variadic_args;
struct variadic_results;
struct stack_count;
struct this_state;
struct this_main_state;
struct this_environment;
class state_view;
class state;
template <typename T>
struct as_table_t;
template <typename T>
struct as_container_t;
template <typename T>
struct nested;
template <typename T>
struct light;
template <typename T>
struct user;
template <typename T>
struct as_args_t;
template <typename T>
struct protect_t;
template <typename F, typename... Policies>
struct policy_wrapper;
template <typename T>
struct usertype_traits;
template <typename T>
struct unique_usertype_traits;
template <typename... Args>
struct types {
typedef std::make_index_sequence<sizeof...(Args)> indices;
static constexpr std::size_t size() {
return sizeof...(Args);
}
};
template <typename T>
struct derive : std::false_type {
typedef types<> type;
};
template <typename T>
struct base : std::false_type {
typedef types<> type;
};
template <typename T>
struct weak_derive {
static bool value;
};
template <typename T>
bool weak_derive<T>::value = false;
namespace stack {
struct record;
}
#if SOL_IS_OFF(SOL_USE_BOOST_I_)
template <class T>
class optional;
template <class T>
class optional<T&>;
#endif
using check_handler_type = int(lua_State*, int, type, type, const char*);
} // namespace sol
#define SOL_BASE_CLASSES(T, ...) \
namespace sol { \
template <> \
struct base<T> : std::true_type { \
typedef ::sol::types<__VA_ARGS__> type; \
}; \
} \
void a_sol3_detail_function_decl_please_no_collide()
#define SOL_DERIVED_CLASSES(T, ...) \
namespace sol { \
template <> \
struct derive<T> : std::true_type { \
typedef ::sol::types<__VA_ARGS__> type; \
}; \
} \
void a_sol3_detail_function_decl_please_no_collide()
#endif // SOL_FORWARD_HPP
// end of sol/forward.hpp
#endif // SOL_SINGLE_INCLUDE_FORWARD_HPP

26674
external/sol/sol.hpp vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1,32 @@
This is the (likely incomplete) list of people who have made Wren what it is.
If you submit a patch to Wren, please add your name and email address to the
end of this list.
Robert Nystrom <robert@stuffwithstuff.com>
Kyle Marek-Spartz <kyle.marek.spartz@gmail.com>
Paul Woolcock <paul@woolcock.us>
Evan Shaw <edsrzf@gmail.com>
Gavin Schulz <gavin.schulz@gmail.com>
Lukas Werling <lukas.werling@gmail.com>
Marco Lizza <marco.lizza@gmail.com>
Raymond Sohn <raymondsohn@gmail.com>
Thorbjørn Lindeijer <bjorn@lindeijer.nl>
Patricio Mac Adden <patriciomacadden@gmail.com>
Evan Hahn <me@evanhahn.com>
Starbeamrainbowlabs <contact@starbeamrainbowlabs.com>
Alexander Roper <minirop@gmail.com>
Will Speak <will@willspeak.me>
Damien Radtke <damienradtke@gmail.com>
Max Ferguson <maxxferguson@gmail.com>
Sven Bergström <sven@underscorediscovery.com>
Kyle Charters <kylewcharters@gmail.com>
Marshall Bowers <elliott.codes@gmail.com>
Michal Kozakiewicz <michalkozakiewicz3@gmail.com>
Charlotte Koch <cfkoch@edgebsd.org>
Michel Hermier <michel.hermier@gmail.com>
Taylor Hoff <primdevs@gmail.com>
ruby0x1 <ruby0x1@pm.me>
Kolja Kube <code@koljaku.be>
Alexander Klingenbeck <alexander.klingenbeck@gmx.de>
Aviv Beeri <avbeeri@gmail.com>

@ -0,0 +1,26 @@
set(MODULE_NAME wren)
# specify the libraries source files
set( wren_SRC
vm/wren_compiler.c
vm/wren_core.c
vm/wren_debug.c
vm/wren_primitive.c
vm/wren_utils.c
vm/wren_value.c
vm/wren_vm.c
optional/wren_opt_meta.c
optional/wren_opt_random.c
)
# add the library
add_library(${MODULE_NAME} ${wren_SRC})
target_include_directories(${MODULE_NAME}
PUBLIC "${PROJECT_BINARY_DIR}"
PUBLIC include
PUBLIC optional
PUBLIC vm
)

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2021 Robert Nystrom and Wren Contributors
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.

@ -0,0 +1,13 @@
This contains the Wren source code. It is organized like so:
* `include`: the public header directory for the VM. If you are embedding the
VM in your own application, you will add this to your include path.
* `vm`: the source code for the Wren VM itself. If you are embedding the VM in
your own application from source, you will compile the files here into your
app.
* `optional`: the Wren and C source code for the optional modules. These are
built in to the VM and can be used even when you embed the VM in your own
application. But they are also optional and can be compiled out by setting
defines.

@ -0,0 +1,554 @@
#ifndef wren_h
#define wren_h
#include <stdarg.h>
#include <stdlib.h>
#include <stdbool.h>
// The Wren semantic version number components.
#define WREN_VERSION_MAJOR 0
#define WREN_VERSION_MINOR 4
#define WREN_VERSION_PATCH 0
// A human-friendly string representation of the version.
#define WREN_VERSION_STRING "0.4.0"
// A monotonically increasing numeric representation of the version number. Use
// this if you want to do range checks over versions.
#define WREN_VERSION_NUMBER (WREN_VERSION_MAJOR * 1000000 + \
WREN_VERSION_MINOR * 1000 + \
WREN_VERSION_PATCH)
#ifndef WREN_API
#if defined(_MSC_VER) && defined(WREN_API_DLLEXPORT)
#define WREN_API __declspec( dllexport )
#else
#define WREN_API
#endif
#endif //WREN_API
// A single virtual machine for executing Wren code.
//
// Wren has no global state, so all state stored by a running interpreter lives
// here.
typedef struct WrenVM WrenVM;
// A handle to a Wren object.
//
// This lets code outside of the VM hold a persistent reference to an object.
// After a handle is acquired, and until it is released, this ensures the
// garbage collector will not reclaim the object it references.
typedef struct WrenHandle WrenHandle;
// A generic allocation function that handles all explicit memory management
// used by Wren. It's used like so:
//
// - To allocate new memory, [memory] is NULL and [newSize] is the desired
// size. It should return the allocated memory or NULL on failure.
//
// - To attempt to grow an existing allocation, [memory] is the memory, and
// [newSize] is the desired size. It should return [memory] if it was able to
// grow it in place, or a new pointer if it had to move it.
//
// - To shrink memory, [memory] and [newSize] are the same as above but it will
// always return [memory].
//
// - To free memory, [memory] will be the memory to free and [newSize] will be
// zero. It should return NULL.
typedef void* (*WrenReallocateFn)(void* memory, size_t newSize, void* userData);
// A function callable from Wren code, but implemented in C.
typedef void (*WrenForeignMethodFn)(WrenVM* vm);
// A finalizer function for freeing resources owned by an instance of a foreign
// class. Unlike most foreign methods, finalizers do not have access to the VM
// and should not interact with it since it's in the middle of a garbage
// collection.
typedef void (*WrenFinalizerFn)(void* data);
// Gives the host a chance to canonicalize the imported module name,
// potentially taking into account the (previously resolved) name of the module
// that contains the import. Typically, this is used to implement relative
// imports.
typedef const char* (*WrenResolveModuleFn)(WrenVM* vm,
const char* importer, const char* name);
// Forward declare
struct WrenLoadModuleResult;
// Called after loadModuleFn is called for module [name]. The original returned result
// is handed back to you in this callback, so that you can free memory if appropriate.
typedef void (*WrenLoadModuleCompleteFn)(WrenVM* vm, const char* name, struct WrenLoadModuleResult result);
// The result of a loadModuleFn call.
// [source] is the source code for the module, or NULL if the module is not found.
// [onComplete] an optional callback that will be called once Wren is done with the result.
typedef struct WrenLoadModuleResult
{
const char* source;
WrenLoadModuleCompleteFn onComplete;
void* userData;
} WrenLoadModuleResult;
// Loads and returns the source code for the module [name].
typedef WrenLoadModuleResult (*WrenLoadModuleFn)(WrenVM* vm, const char* name);
// Returns a pointer to a foreign method on [className] in [module] with
// [signature].
typedef WrenForeignMethodFn (*WrenBindForeignMethodFn)(WrenVM* vm,
const char* module, const char* className, bool isStatic,
const char* signature);
// Displays a string of text to the user.
typedef void (*WrenWriteFn)(WrenVM* vm, const char* text);
typedef enum
{
// A syntax or resolution error detected at compile time.
WREN_ERROR_COMPILE,
// The error message for a runtime error.
WREN_ERROR_RUNTIME,
// One entry of a runtime error's stack trace.
WREN_ERROR_STACK_TRACE
} WrenErrorType;
// Reports an error to the user.
//
// An error detected during compile time is reported by calling this once with
// [type] `WREN_ERROR_COMPILE`, the resolved name of the [module] and [line]
// where the error occurs, and the compiler's error [message].
//
// A runtime error is reported by calling this once with [type]
// `WREN_ERROR_RUNTIME`, no [module] or [line], and the runtime error's
// [message]. After that, a series of [type] `WREN_ERROR_STACK_TRACE` calls are
// made for each line in the stack trace. Each of those has the resolved
// [module] and [line] where the method or function is defined and [message] is
// the name of the method or function.
typedef void (*WrenErrorFn)(
WrenVM* vm, WrenErrorType type, const char* module, int line,
const char* message);
typedef struct
{
// The callback invoked when the foreign object is created.
//
// This must be provided. Inside the body of this, it must call
// [wrenSetSlotNewForeign()] exactly once.
WrenForeignMethodFn allocate;
// The callback invoked when the garbage collector is about to collect a
// foreign object's memory.
//
// This may be `NULL` if the foreign class does not need to finalize.
WrenFinalizerFn finalize;
} WrenForeignClassMethods;
// Returns a pair of pointers to the foreign methods used to allocate and
// finalize the data for instances of [className] in resolved [module].
typedef WrenForeignClassMethods (*WrenBindForeignClassFn)(
WrenVM* vm, const char* module, const char* className);
typedef struct
{
// The callback Wren will use to allocate, reallocate, and deallocate memory.
//
// If `NULL`, defaults to a built-in function that uses `realloc` and `free`.
WrenReallocateFn reallocateFn;
// The callback Wren uses to resolve a module name.
//
// Some host applications may wish to support "relative" imports, where the
// meaning of an import string depends on the module that contains it. To
// support that without baking any policy into Wren itself, the VM gives the
// host a chance to resolve an import string.
//
// Before an import is loaded, it calls this, passing in the name of the
// module that contains the import and the import string. The host app can
// look at both of those and produce a new "canonical" string that uniquely
// identifies the module. This string is then used as the name of the module
// going forward. It is what is passed to [loadModuleFn], how duplicate
// imports of the same module are detected, and how the module is reported in
// stack traces.
//
// If you leave this function NULL, then the original import string is
// treated as the resolved string.
//
// If an import cannot be resolved by the embedder, it should return NULL and
// Wren will report that as a runtime error.
//
// Wren will take ownership of the string you return and free it for you, so
// it should be allocated using the same allocation function you provide
// above.
WrenResolveModuleFn resolveModuleFn;
// The callback Wren uses to load a module.
//
// Since Wren does not talk directly to the file system, it relies on the
// embedder to physically locate and read the source code for a module. The
// first time an import appears, Wren will call this and pass in the name of
// the module being imported. The method will return a result, which contains
// the source code for that module. Memory for the source is owned by the
// host application, and can be freed using the onComplete callback.
//
// This will only be called once for any given module name. Wren caches the
// result internally so subsequent imports of the same module will use the
// previous source and not call this.
//
// If a module with the given name could not be found by the embedder, it
// should return NULL and Wren will report that as a runtime error.
WrenLoadModuleFn loadModuleFn;
// The callback Wren uses to find a foreign method and bind it to a class.
//
// When a foreign method is declared in a class, this will be called with the
// foreign method's module, class, and signature when the class body is
// executed. It should return a pointer to the foreign function that will be
// bound to that method.
//
// If the foreign function could not be found, this should return NULL and
// Wren will report it as runtime error.
WrenBindForeignMethodFn bindForeignMethodFn;
// The callback Wren uses to find a foreign class and get its foreign methods.
//
// When a foreign class is declared, this will be called with the class's
// module and name when the class body is executed. It should return the
// foreign functions uses to allocate and (optionally) finalize the bytes
// stored in the foreign object when an instance is created.
WrenBindForeignClassFn bindForeignClassFn;
// The callback Wren uses to display text when `System.print()` or the other
// related functions are called.
//
// If this is `NULL`, Wren discards any printed text.
WrenWriteFn writeFn;
// The callback Wren uses to report errors.
//
// When an error occurs, this will be called with the module name, line
// number, and an error message. If this is `NULL`, Wren doesn't report any
// errors.
WrenErrorFn errorFn;
// The number of bytes Wren will allocate before triggering the first garbage
// collection.
//
// If zero, defaults to 10MB.
size_t initialHeapSize;
// After a collection occurs, the threshold for the next collection is
// determined based on the number of bytes remaining in use. This allows Wren
// to shrink its memory usage automatically after reclaiming a large amount
// of memory.
//
// This can be used to ensure that the heap does not get too small, which can
// in turn lead to a large number of collections afterwards as the heap grows
// back to a usable size.
//
// If zero, defaults to 1MB.
size_t minHeapSize;
// Wren will resize the heap automatically as the number of bytes
// remaining in use after a collection changes. This number determines the
// amount of additional memory Wren will use after a collection, as a
// percentage of the current heap size.
//
// For example, say that this is 50. After a garbage collection, when there
// are 400 bytes of memory still in use, the next collection will be triggered
// after a total of 600 bytes are allocated (including the 400 already in
// use.)
//
// Setting this to a smaller number wastes less memory, but triggers more
// frequent garbage collections.
//
// If zero, defaults to 50.
int heapGrowthPercent;
// User-defined data associated with the VM.
void* userData;
} WrenConfiguration;
typedef enum
{
WREN_RESULT_SUCCESS,
WREN_RESULT_COMPILE_ERROR,
WREN_RESULT_RUNTIME_ERROR
} WrenInterpretResult;
// The type of an object stored in a slot.
//
// This is not necessarily the object's *class*, but instead its low level
// representation type.
typedef enum
{
WREN_TYPE_BOOL,
WREN_TYPE_NUM,
WREN_TYPE_FOREIGN,
WREN_TYPE_LIST,
WREN_TYPE_MAP,
WREN_TYPE_NULL,
WREN_TYPE_STRING,
// The object is of a type that isn't accessible by the C API.
WREN_TYPE_UNKNOWN
} WrenType;
// Get the current wren version number.
//
// Can be used to range checks over versions.
WREN_API int wrenGetVersionNumber();
// Initializes [configuration] with all of its default values.
//
// Call this before setting the particular fields you care about.
WREN_API void wrenInitConfiguration(WrenConfiguration* configuration);
// Creates a new Wren virtual machine using the given [configuration]. Wren
// will copy the configuration data, so the argument passed to this can be
// freed after calling this. If [configuration] is `NULL`, uses a default
// configuration.
WREN_API WrenVM* wrenNewVM(WrenConfiguration* configuration);
// Disposes of all resources is use by [vm], which was previously created by a
// call to [wrenNewVM].
WREN_API void wrenFreeVM(WrenVM* vm);
// Immediately run the garbage collector to free unused memory.
WREN_API void wrenCollectGarbage(WrenVM* vm);
// Runs [source], a string of Wren source code in a new fiber in [vm] in the
// context of resolved [module].
WREN_API WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
const char* source);
// Creates a handle that can be used to invoke a method with [signature] on
// using a receiver and arguments that are set up on the stack.
//
// This handle can be used repeatedly to directly invoke that method from C
// code using [wrenCall].
//
// When you are done with this handle, it must be released using
// [wrenReleaseHandle].
WREN_API WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
// Calls [method], using the receiver and arguments previously set up on the
// stack.
//
// [method] must have been created by a call to [wrenMakeCallHandle]. The
// arguments to the method must be already on the stack. The receiver should be
// in slot 0 with the remaining arguments following it, in order. It is an
// error if the number of arguments provided does not match the method's
// signature.
//
// After this returns, you can access the return value from slot 0 on the stack.
WREN_API WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
// Releases the reference stored in [handle]. After calling this, [handle] can
// no longer be used.
WREN_API void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
// The following functions are intended to be called from foreign methods or
// finalizers. The interface Wren provides to a foreign method is like a
// register machine: you are given a numbered array of slots that values can be
// read from and written to. Values always live in a slot (unless explicitly
// captured using wrenGetSlotHandle(), which ensures the garbage collector can
// find them.
//
// When your foreign function is called, you are given one slot for the receiver
// and each argument to the method. The receiver is in slot 0 and the arguments
// are in increasingly numbered slots after that. You are free to read and
// write to those slots as you want. If you want more slots to use as scratch
// space, you can call wrenEnsureSlots() to add more.
//
// When your function returns, every slot except slot zero is discarded and the
// value in slot zero is used as the return value of the method. If you don't
// store a return value in that slot yourself, it will retain its previous
// value, the receiver.
//
// While Wren is dynamically typed, C is not. This means the C interface has to
// support the various types of primitive values a Wren variable can hold: bool,
// double, string, etc. If we supported this for every operation in the C API,
// there would be a combinatorial explosion of functions, like "get a
// double-valued element from a list", "insert a string key and double value
// into a map", etc.
//
// To avoid that, the only way to convert to and from a raw C value is by going
// into and out of a slot. All other functions work with values already in a
// slot. So, to add an element to a list, you put the list in one slot, and the
// element in another. Then there is a single API function wrenInsertInList()
// that takes the element out of that slot and puts it into the list.
//
// The goal of this API is to be easy to use while not compromising performance.
// The latter means it does not do type or bounds checking at runtime except
// using assertions which are generally removed from release builds. C is an
// unsafe language, so it's up to you to be careful to use it correctly. In
// return, you get a very fast FFI.
// Returns the number of slots available to the current foreign method.
WREN_API int wrenGetSlotCount(WrenVM* vm);
// Ensures that the foreign method stack has at least [numSlots] available for
// use, growing the stack if needed.
//
// Does not shrink the stack if it has more than enough slots.
//
// It is an error to call this from a finalizer.
WREN_API void wrenEnsureSlots(WrenVM* vm, int numSlots);
// Gets the type of the object in [slot].
WREN_API WrenType wrenGetSlotType(WrenVM* vm, int slot);
// Reads a boolean value from [slot].
//
// It is an error to call this if the slot does not contain a boolean value.
WREN_API bool wrenGetSlotBool(WrenVM* vm, int slot);
// Reads a byte array from [slot].
//
// The memory for the returned string is owned by Wren. You can inspect it
// while in your foreign method, but cannot keep a pointer to it after the
// function returns, since the garbage collector may reclaim it.
//
// Returns a pointer to the first byte of the array and fill [length] with the
// number of bytes in the array.
//
// It is an error to call this if the slot does not contain a string.
WREN_API const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length);
// Reads a number from [slot].
//
// It is an error to call this if the slot does not contain a number.
WREN_API double wrenGetSlotDouble(WrenVM* vm, int slot);
// Reads a foreign object from [slot] and returns a pointer to the foreign data
// stored with it.
//
// It is an error to call this if the slot does not contain an instance of a
// foreign class.
WREN_API void* wrenGetSlotForeign(WrenVM* vm, int slot);
// Reads a string from [slot].
//
// The memory for the returned string is owned by Wren. You can inspect it
// while in your foreign method, but cannot keep a pointer to it after the
// function returns, since the garbage collector may reclaim it.
//
// It is an error to call this if the slot does not contain a string.
WREN_API const char* wrenGetSlotString(WrenVM* vm, int slot);
// Creates a handle for the value stored in [slot].
//
// This will prevent the object that is referred to from being garbage collected
// until the handle is released by calling [wrenReleaseHandle()].
WREN_API WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
// Stores the boolean [value] in [slot].
WREN_API void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
// Stores the array [length] of [bytes] in [slot].
//
// The bytes are copied to a new string within Wren's heap, so you can free
// memory used by them after this is called.
WREN_API void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length);
// Stores the numeric [value] in [slot].
WREN_API void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
// Creates a new instance of the foreign class stored in [classSlot] with [size]
// bytes of raw storage and places the resulting object in [slot].
//
// This does not invoke the foreign class's constructor on the new instance. If
// you need that to happen, call the constructor from Wren, which will then
// call the allocator foreign method. In there, call this to create the object
// and then the constructor will be invoked when the allocator returns.
//
// Returns a pointer to the foreign object's data.
WREN_API void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
// Stores a new empty list in [slot].
WREN_API void wrenSetSlotNewList(WrenVM* vm, int slot);
// Stores a new empty map in [slot].
WREN_API void wrenSetSlotNewMap(WrenVM* vm, int slot);
// Stores null in [slot].
WREN_API void wrenSetSlotNull(WrenVM* vm, int slot);
// Stores the string [text] in [slot].
//
// The [text] is copied to a new string within Wren's heap, so you can free
// memory used by it after this is called. The length is calculated using
// [strlen()]. If the string may contain any null bytes in the middle, then you
// should use [wrenSetSlotBytes()] instead.
WREN_API void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
// Stores the value captured in [handle] in [slot].
//
// This does not release the handle for the value.
WREN_API void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
// Returns the number of elements in the list stored in [slot].
WREN_API int wrenGetListCount(WrenVM* vm, int slot);
// Reads element [index] from the list in [listSlot] and stores it in
// [elementSlot].
WREN_API void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
// Sets the value stored at [index] in the list at [listSlot],
// to the value from [elementSlot].
WREN_API void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
// Takes the value stored at [elementSlot] and inserts it into the list stored
// at [listSlot] at [index].
//
// As in Wren, negative indexes can be used to insert from the end. To append
// an element, use `-1` for the index.
WREN_API void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
// Returns the number of entries in the map stored in [slot].
WREN_API int wrenGetMapCount(WrenVM* vm, int slot);
// Returns true if the key in [keySlot] is found in the map placed in [mapSlot].
WREN_API bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot);
// Retrieves a value with the key in [keySlot] from the map in [mapSlot] and
// stores it in [valueSlot].
WREN_API void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
// Takes the value stored at [valueSlot] and inserts it into the map stored
// at [mapSlot] with key [keySlot].
WREN_API void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
// Removes a value from the map in [mapSlot], with the key from [keySlot],
// and place it in [removedValueSlot]. If not found, [removedValueSlot] is
// set to null, the same behaviour as the Wren Map API.
WREN_API void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
int removedValueSlot);
// Looks up the top level variable with [name] in resolved [module] and stores
// it in [slot].
WREN_API void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
int slot);
// Looks up the top level variable with [name] in resolved [module],
// returns false if not found. The module must be imported at the time,
// use wrenHasModule to ensure that before calling.
WREN_API bool wrenHasVariable(WrenVM* vm, const char* module, const char* name);
// Returns true if [module] has been imported/resolved before, false if not.
WREN_API bool wrenHasModule(WrenVM* vm, const char* module);
// Sets the current fiber to be aborted, and uses the value in [slot] as the
// runtime error object.
WREN_API void wrenAbortFiber(WrenVM* vm, int slot);
// Returns the user data associated with the WrenVM.
WREN_API void* wrenGetUserData(WrenVM* vm);
// Sets user data associated with the WrenVM.
WREN_API void wrenSetUserData(WrenVM* vm, void* userData);
#endif

@ -0,0 +1,11 @@
#ifndef wren_hpp
#define wren_hpp
// This is a convenience header for users that want to compile Wren as C and
// link to it from a C++ application.
extern "C" {
#include "wren.h"
}
#endif

@ -0,0 +1,96 @@
#include "wren_opt_meta.h"
#if WREN_OPT_META
#include <string.h>
#include "wren_vm.h"
#include "wren_opt_meta.wren.inc"
void metaCompile(WrenVM* vm)
{
const char* source = wrenGetSlotString(vm, 1);
bool isExpression = wrenGetSlotBool(vm, 2);
bool printErrors = wrenGetSlotBool(vm, 3);
// TODO: Allow passing in module?
// Look up the module surrounding the callsite. This is brittle. The -2 walks
// up the callstack assuming that the meta module has one level of
// indirection before hitting the user's code. Any change to meta may require
// this constant to be tweaked.
ObjFiber* currentFiber = vm->fiber;
ObjFn* fn = currentFiber->frames[currentFiber->numFrames - 2].closure->fn;
ObjString* module = fn->module->name;
ObjClosure* closure = wrenCompileSource(vm, module->value, source,
isExpression, printErrors);
// Return the result. We can't use the public API for this since we have a
// bare ObjClosure*.
if (closure == NULL)
{
vm->apiStack[0] = NULL_VAL;
}
else
{
vm->apiStack[0] = OBJ_VAL(closure);
}
}
void metaGetModuleVariables(WrenVM* vm)
{
wrenEnsureSlots(vm, 3);
Value moduleValue = wrenMapGet(vm->modules, vm->apiStack[1]);
if (IS_UNDEFINED(moduleValue))
{
vm->apiStack[0] = NULL_VAL;
return;
}
ObjModule* module = AS_MODULE(moduleValue);
ObjList* names = wrenNewList(vm, module->variableNames.count);
vm->apiStack[0] = OBJ_VAL(names);
// Initialize the elements to null in case a collection happens when we
// allocate the strings below.
for (int i = 0; i < names->elements.count; i++)
{
names->elements.data[i] = NULL_VAL;
}
for (int i = 0; i < names->elements.count; i++)
{
names->elements.data[i] = OBJ_VAL(module->variableNames.data[i]);
}
}
const char* wrenMetaSource()
{
return metaModuleSource;
}
WrenForeignMethodFn wrenMetaBindForeignMethod(WrenVM* vm,
const char* className,
bool isStatic,
const char* signature)
{
// There is only one foreign method in the meta module.
ASSERT(strcmp(className, "Meta") == 0, "Should be in Meta class.");
ASSERT(isStatic, "Should be static.");
if (strcmp(signature, "compile_(_,_,_)") == 0)
{
return metaCompile;
}
if (strcmp(signature, "getModuleVariables_(_)") == 0)
{
return metaGetModuleVariables;
}
ASSERT(false, "Unknown method.");
return NULL;
}
#endif

@ -0,0 +1,18 @@
#ifndef wren_opt_meta_h
#define wren_opt_meta_h
#include "wren_common.h"
#include "wren.h"
// This module defines the Meta class and its associated methods.
#if WREN_OPT_META
const char* wrenMetaSource();
WrenForeignMethodFn wrenMetaBindForeignMethod(WrenVM* vm,
const char* className,
bool isStatic,
const char* signature);
#endif
#endif

@ -0,0 +1,32 @@
class Meta {
static getModuleVariables(module) {
if (!(module is String)) Fiber.abort("Module name must be a string.")
var result = getModuleVariables_(module)
if (result != null) return result
Fiber.abort("Could not find a module named '%(module)'.")
}
static eval(source) {
if (!(source is String)) Fiber.abort("Source code must be a string.")
var closure = compile_(source, false, false)
// TODO: Include compile errors.
if (closure == null) Fiber.abort("Could not compile source code.")
closure.call()
}
static compileExpression(source) {
if (!(source is String)) Fiber.abort("Source code must be a string.")
return compile_(source, true, true)
}
static compile(source) {
if (!(source is String)) Fiber.abort("Source code must be a string.")
return compile_(source, false, true)
}
foreign static compile_(source, isExpression, printErrors)
foreign static getModuleVariables_(module)
}

@ -0,0 +1,34 @@
// Generated automatically from src/optional/wren_opt_meta.wren. Do not edit.
static const char* metaModuleSource =
"class Meta {\n"
" static getModuleVariables(module) {\n"
" if (!(module is String)) Fiber.abort(\"Module name must be a string.\")\n"
" var result = getModuleVariables_(module)\n"
" if (result != null) return result\n"
"\n"
" Fiber.abort(\"Could not find a module named '%(module)'.\")\n"
" }\n"
"\n"
" static eval(source) {\n"
" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n"
"\n"
" var closure = compile_(source, false, false)\n"
" // TODO: Include compile errors.\n"
" if (closure == null) Fiber.abort(\"Could not compile source code.\")\n"
"\n"
" closure.call()\n"
" }\n"
"\n"
" static compileExpression(source) {\n"
" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n"
" return compile_(source, true, true)\n"
" }\n"
"\n"
" static compile(source) {\n"
" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n"
" return compile_(source, false, true)\n"
" }\n"
"\n"
" foreign static compile_(source, isExpression, printErrors)\n"
" foreign static getModuleVariables_(module)\n"
"}\n";

@ -0,0 +1,144 @@
#include "wren_opt_random.h"
#if WREN_OPT_RANDOM
#include <string.h>
#include <time.h>
#include "wren.h"
#include "wren_vm.h"
#include "wren_opt_random.wren.inc"
// Implements the well equidistributed long-period linear PRNG (WELL512a).
//
// https://en.wikipedia.org/wiki/Well_equidistributed_long-period_linear
typedef struct
{
uint32_t state[16];
uint32_t index;
} Well512;
// Code from: http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf
static uint32_t advanceState(Well512* well)
{
uint32_t a, b, c, d;
a = well->state[well->index];
c = well->state[(well->index + 13) & 15];
b = a ^ c ^ (a << 16) ^ (c << 15);
c = well->state[(well->index + 9) & 15];
c ^= (c >> 11);
a = well->state[well->index] = b ^ c;
d = a ^ ((a << 5) & 0xda442d24U);
well->index = (well->index + 15) & 15;
a = well->state[well->index];
well->state[well->index] = a ^ b ^ d ^ (a << 2) ^ (b << 18) ^ (c << 28);
return well->state[well->index];
}
static void randomAllocate(WrenVM* vm)
{
Well512* well = (Well512*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(Well512));
well->index = 0;
}
static void randomSeed0(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
srand((uint32_t)time(NULL));
for (int i = 0; i < 16; i++)
{
well->state[i] = rand();
}
}
static void randomSeed1(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
srand((uint32_t)wrenGetSlotDouble(vm, 1));
for (int i = 0; i < 16; i++)
{
well->state[i] = rand();
}
}
static void randomSeed16(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
for (int i = 0; i < 16; i++)
{
well->state[i] = (uint32_t)wrenGetSlotDouble(vm, i + 1);
}
}
static void randomFloat(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
// A double has 53 bits of precision in its mantissa, and we'd like to take
// full advantage of that, so we need 53 bits of random source data.
// First, start with 32 random bits, shifted to the left 21 bits.
double result = (double)advanceState(well) * (1 << 21);
// Then add another 21 random bits.
result += (double)(advanceState(well) & ((1 << 21) - 1));
// Now we have a number from 0 - (2^53). Divide be the range to get a double
// from 0 to 1.0 (half-inclusive).
result /= 9007199254740992.0;
wrenSetSlotDouble(vm, 0, result);
}
static void randomInt0(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
wrenSetSlotDouble(vm, 0, (double)advanceState(well));
}
const char* wrenRandomSource()
{
return randomModuleSource;
}
WrenForeignClassMethods wrenRandomBindForeignClass(WrenVM* vm,
const char* module,
const char* className)
{
ASSERT(strcmp(className, "Random") == 0, "Should be in Random class.");
WrenForeignClassMethods methods;
methods.allocate = randomAllocate;
methods.finalize = NULL;
return methods;
}
WrenForeignMethodFn wrenRandomBindForeignMethod(WrenVM* vm,
const char* className,
bool isStatic,
const char* signature)
{
ASSERT(strcmp(className, "Random") == 0, "Should be in Random class.");
if (strcmp(signature, "<allocate>") == 0) return randomAllocate;
if (strcmp(signature, "seed_()") == 0) return randomSeed0;
if (strcmp(signature, "seed_(_)") == 0) return randomSeed1;
if (strcmp(signature, "seed_(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)") == 0)
{
return randomSeed16;
}
if (strcmp(signature, "float()") == 0) return randomFloat;
if (strcmp(signature, "int()") == 0) return randomInt0;
ASSERT(false, "Unknown method.");
return NULL;
}
#endif

@ -0,0 +1,20 @@
#ifndef wren_opt_random_h
#define wren_opt_random_h
#include "wren_common.h"
#include "wren.h"
#if WREN_OPT_RANDOM
const char* wrenRandomSource();
WrenForeignClassMethods wrenRandomBindForeignClass(WrenVM* vm,
const char* module,
const char* className);
WrenForeignMethodFn wrenRandomBindForeignMethod(WrenVM* vm,
const char* className,
bool isStatic,
const char* signature);
#endif
#endif

@ -0,0 +1,95 @@
foreign class Random {
construct new() {
seed_()
}
construct new(seed) {
if (seed is Num) {
seed_(seed)
} else if (seed is Sequence) {
if (seed.isEmpty) Fiber.abort("Sequence cannot be empty.")
// TODO: Empty sequence.
var seeds = []
for (element in seed) {
if (!(element is Num)) Fiber.abort("Sequence elements must all be numbers.")
seeds.add(element)
if (seeds.count == 16) break
}
// Cycle the values to fill in any missing slots.
var i = 0
while (seeds.count < 16) {
seeds.add(seeds[i])
i = i + 1
}
seed_(
seeds[0], seeds[1], seeds[2], seeds[3],
seeds[4], seeds[5], seeds[6], seeds[7],
seeds[8], seeds[9], seeds[10], seeds[11],
seeds[12], seeds[13], seeds[14], seeds[15])
} else {
Fiber.abort("Seed must be a number or a sequence of numbers.")
}
}
foreign seed_()
foreign seed_(seed)
foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)
foreign float()
float(end) { float() * end }
float(start, end) { float() * (end - start) + start }
foreign int()
int(end) { (float() * end).floor }
int(start, end) { (float() * (end - start)).floor + start }
sample(list) {
if (list.count == 0) Fiber.abort("Not enough elements to sample.")
return list[int(list.count)]
}
sample(list, count) {
if (count > list.count) Fiber.abort("Not enough elements to sample.")
var result = []
// The algorithm described in "Programming pearls: a sample of brilliance".
// Use a hash map for sample sizes less than 1/4 of the population size and
// an array of booleans for larger samples. This simple heuristic improves
// performance for large sample sizes as well as reduces memory usage.
if (count * 4 < list.count) {
var picked = {}
for (i in list.count - count...list.count) {
var index = int(i + 1)
if (picked.containsKey(index)) index = i
picked[index] = true
result.add(list[index])
}
} else {
var picked = List.filled(list.count, false)
for (i in list.count - count...list.count) {
var index = int(i + 1)
if (picked[index]) index = i
picked[index] = true
result.add(list[index])
}
}
return result
}
shuffle(list) {
if (list.isEmpty) return
// Fisher-Yates shuffle.
for (i in 0...list.count - 1) {
var from = int(i, list.count)
var temp = list[from]
list[from] = list[i]
list[i] = temp
}
}
}

@ -0,0 +1,97 @@
// Generated automatically from src/optional/wren_opt_random.wren. Do not edit.
static const char* randomModuleSource =
"foreign class Random {\n"
" construct new() {\n"
" seed_()\n"
" }\n"
"\n"
" construct new(seed) {\n"
" if (seed is Num) {\n"
" seed_(seed)\n"
" } else if (seed is Sequence) {\n"
" if (seed.isEmpty) Fiber.abort(\"Sequence cannot be empty.\")\n"
"\n"
" // TODO: Empty sequence.\n"
" var seeds = []\n"
" for (element in seed) {\n"
" if (!(element is Num)) Fiber.abort(\"Sequence elements must all be numbers.\")\n"
"\n"
" seeds.add(element)\n"
" if (seeds.count == 16) break\n"
" }\n"
"\n"
" // Cycle the values to fill in any missing slots.\n"
" var i = 0\n"
" while (seeds.count < 16) {\n"
" seeds.add(seeds[i])\n"
" i = i + 1\n"
" }\n"
"\n"
" seed_(\n"
" seeds[0], seeds[1], seeds[2], seeds[3],\n"
" seeds[4], seeds[5], seeds[6], seeds[7],\n"
" seeds[8], seeds[9], seeds[10], seeds[11],\n"
" seeds[12], seeds[13], seeds[14], seeds[15])\n"
" } else {\n"
" Fiber.abort(\"Seed must be a number or a sequence of numbers.\")\n"
" }\n"
" }\n"
"\n"
" foreign seed_()\n"
" foreign seed_(seed)\n"
" foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)\n"
"\n"
" foreign float()\n"
" float(end) { float() * end }\n"
" float(start, end) { float() * (end - start) + start }\n"
"\n"
" foreign int()\n"
" int(end) { (float() * end).floor }\n"
" int(start, end) { (float() * (end - start)).floor + start }\n"
"\n"
" sample(list) {\n"
" if (list.count == 0) Fiber.abort(\"Not enough elements to sample.\")\n"
" return list[int(list.count)]\n"
" }\n"
" sample(list, count) {\n"
" if (count > list.count) Fiber.abort(\"Not enough elements to sample.\")\n"
"\n"
" var result = []\n"
"\n"
" // The algorithm described in \"Programming pearls: a sample of brilliance\".\n"
" // Use a hash map for sample sizes less than 1/4 of the population size and\n"
" // an array of booleans for larger samples. This simple heuristic improves\n"
" // performance for large sample sizes as well as reduces memory usage.\n"
" if (count * 4 < list.count) {\n"
" var picked = {}\n"
" for (i in list.count - count...list.count) {\n"
" var index = int(i + 1)\n"
" if (picked.containsKey(index)) index = i\n"
" picked[index] = true\n"
" result.add(list[index])\n"
" }\n"
" } else {\n"
" var picked = List.filled(list.count, false)\n"
" for (i in list.count - count...list.count) {\n"
" var index = int(i + 1)\n"
" if (picked[index]) index = i\n"
" picked[index] = true\n"
" result.add(list[index])\n"
" }\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" shuffle(list) {\n"
" if (list.isEmpty) return\n"
"\n"
" // Fisher-Yates shuffle.\n"
" for (i in 0...list.count - 1) {\n"
" var from = int(i, list.count)\n"
" var temp = list[from]\n"
" list[from] = list[i]\n"
" list[i] = temp\n"
" }\n"
" }\n"
"}\n";

@ -0,0 +1,200 @@
#ifndef wren_common_h
#define wren_common_h
// This header contains macros and defines used across the entire Wren
// implementation. In particular, it contains "configuration" defines that
// control how Wren works. Some of these are only used while hacking on Wren
// itself.
//
// This header is *not* intended to be included by code outside of Wren itself.
// Wren pervasively uses the C99 integer types (uint16_t, etc.) along with some
// of the associated limit constants (UINT32_MAX, etc.). The constants are not
// part of standard C++, so aren't included by default by C++ compilers when you
// include <stdint> unless __STDC_LIMIT_MACROS is defined.
#define __STDC_LIMIT_MACROS
#include <stdint.h>
// These flags let you control some details of the interpreter's implementation.
// Usually they trade-off a bit of portability for speed. They default to the
// most efficient behavior.
// If true, then Wren uses a NaN-tagged double for its core value
// representation. Otherwise, it uses a larger more conventional struct. The
// former is significantly faster and more compact. The latter is useful for
// debugging and may be more portable.
//
// Defaults to on.
#ifndef WREN_NAN_TAGGING
#define WREN_NAN_TAGGING 1
#endif
// If true, the VM's interpreter loop uses computed gotos. See this for more:
// http://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Labels-as-Values.html
// Enabling this speeds up the main dispatch loop a bit, but requires compiler
// support.
// see https://bullno1.com/blog/switched-goto for alternative
// Defaults to true on supported compilers.
#ifndef WREN_COMPUTED_GOTO
#if defined(_MSC_VER) && !defined(__clang__)
// No computed gotos in Visual Studio.
#define WREN_COMPUTED_GOTO 0
#else
#define WREN_COMPUTED_GOTO 1
#endif
#endif
// The VM includes a number of optional modules. You can choose to include
// these or not. By default, they are all available. To disable one, set the
// corresponding `WREN_OPT_<name>` define to `0`.
#ifndef WREN_OPT_META
#define WREN_OPT_META 1
#endif
#ifndef WREN_OPT_RANDOM
#define WREN_OPT_RANDOM 1
#endif
// These flags are useful for debugging and hacking on Wren itself. They are not
// intended to be used for production code. They default to off.
// Set this to true to stress test the GC. It will perform a collection before
// every allocation. This is useful to ensure that memory is always correctly
// reachable.
#define WREN_DEBUG_GC_STRESS 0
// Set this to true to log memory operations as they occur.
#define WREN_DEBUG_TRACE_MEMORY 0
// Set this to true to log garbage collections as they occur.
#define WREN_DEBUG_TRACE_GC 0
// Set this to true to print out the compiled bytecode of each function.
#define WREN_DEBUG_DUMP_COMPILED_CODE 0
// Set this to trace each instruction as it's executed.
#define WREN_DEBUG_TRACE_INSTRUCTIONS 0
// The maximum number of module-level variables that may be defined at one time.
// This limitation comes from the 16 bits used for the arguments to
// `CODE_LOAD_MODULE_VAR` and `CODE_STORE_MODULE_VAR`.
#define MAX_MODULE_VARS 65536
// The maximum number of arguments that can be passed to a method. Note that
// this limitation is hardcoded in other places in the VM, in particular, the
// `CODE_CALL_XX` instructions assume a certain maximum number.
#define MAX_PARAMETERS 16
// The maximum name of a method, not including the signature. This is an
// arbitrary but enforced maximum just so we know how long the method name
// strings need to be in the parser.
#define MAX_METHOD_NAME 64
// The maximum length of a method signature. Signatures look like:
//
// foo // Getter.
// foo() // No-argument method.
// foo(_) // One-argument method.
// foo(_,_) // Two-argument method.
// init foo() // Constructor initializer.
//
// The maximum signature length takes into account the longest method name, the
// maximum number of parameters with separators between them, "init ", and "()".
#define MAX_METHOD_SIGNATURE (MAX_METHOD_NAME + (MAX_PARAMETERS * 2) + 6)
// The maximum length of an identifier. The only real reason for this limitation
// is so that error messages mentioning variables can be stack allocated.
#define MAX_VARIABLE_NAME 64
// The maximum number of fields a class can have, including inherited fields.
// This is explicit in the bytecode since `CODE_CLASS` and `CODE_SUBCLASS` take
// a single byte for the number of fields. Note that it's 255 and not 256
// because creating a class takes the *number* of fields, not the *highest
// field index*.
#define MAX_FIELDS 255
// Use the VM's allocator to allocate an object of [type].
#define ALLOCATE(vm, type) \
((type*)wrenReallocate(vm, NULL, 0, sizeof(type)))
// Use the VM's allocator to allocate an object of [mainType] containing a
// flexible array of [count] objects of [arrayType].
#define ALLOCATE_FLEX(vm, mainType, arrayType, count) \
((mainType*)wrenReallocate(vm, NULL, 0, \
sizeof(mainType) + sizeof(arrayType) * (count)))
// Use the VM's allocator to allocate an array of [count] elements of [type].
#define ALLOCATE_ARRAY(vm, type, count) \
((type*)wrenReallocate(vm, NULL, 0, sizeof(type) * (count)))
// Use the VM's allocator to free the previously allocated memory at [pointer].
#define DEALLOCATE(vm, pointer) wrenReallocate(vm, pointer, 0, 0)
// The Microsoft compiler does not support the "inline" modifier when compiling
// as plain C.
#if defined( _MSC_VER ) && !defined(__cplusplus)
#define inline _inline
#endif
// This is used to clearly mark flexible-sized arrays that appear at the end of
// some dynamically-allocated structs, known as the "struct hack".
#if __STDC_VERSION__ >= 199901L
// In C99, a flexible array member is just "[]".
#define FLEXIBLE_ARRAY
#else
// Elsewhere, use a zero-sized array. It's technically undefined behavior,
// but works reliably in most known compilers.
#define FLEXIBLE_ARRAY 0
#endif
// Assertions are used to validate program invariants. They indicate things the
// program expects to be true about its internal state during execution. If an
// assertion fails, there is a bug in Wren.
//
// Assertions add significant overhead, so are only enabled in debug builds.
#ifdef DEBUG
#include <stdio.h>
#define ASSERT(condition, message) \
do \
{ \
if (!(condition)) \
{ \
fprintf(stderr, "[%s:%d] Assert failed in %s(): %s\n", \
__FILE__, __LINE__, __func__, message); \
abort(); \
} \
} while (false)
// Indicates that we know execution should never reach this point in the
// program. In debug mode, we assert this fact because it's a bug to get here.
//
// In release mode, we use compiler-specific built in functions to tell the
// compiler the code can't be reached. This avoids "missing return" warnings
// in some cases and also lets it perform some optimizations by assuming the
// code is never reached.
#define UNREACHABLE() \
do \
{ \
fprintf(stderr, "[%s:%d] This code should not be reached in %s()\n", \
__FILE__, __LINE__, __func__); \
abort(); \
} while (false)
#else
#define ASSERT(condition, message) do { } while (false)
// Tell the compiler that this part of the code will never be reached.
#if defined( _MSC_VER )
#define UNREACHABLE() __assume(0)
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define UNREACHABLE() __builtin_unreachable()
#else
#define UNREACHABLE()
#endif
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,57 @@
#ifndef wren_compiler_h
#define wren_compiler_h
#include "wren.h"
#include "wren_value.h"
typedef struct sCompiler Compiler;
// This module defines the compiler for Wren. It takes a string of source code
// and lexes, parses, and compiles it. Wren uses a single-pass compiler. It
// does not build an actual AST during parsing and then consume that to
// generate code. Instead, the parser directly emits bytecode.
//
// This forces a few restrictions on the grammar and semantics of the language.
// Things like forward references and arbitrary lookahead are much harder. We
// get a lot in return for that, though.
//
// The implementation is much simpler since we don't need to define a bunch of
// AST data structures. More so, we don't have to deal with managing memory for
// AST objects. The compiler does almost no dynamic allocation while running.
//
// Compilation is also faster since we don't create a bunch of temporary data
// structures and destroy them after generating code.
// Compiles [source], a string of Wren source code located in [module], to an
// [ObjFn] that will execute that code when invoked. Returns `NULL` if the
// source contains any syntax errors.
//
// If [isExpression] is `true`, [source] should be a single expression, and
// this compiles it to a function that evaluates and returns that expression.
// Otherwise, [source] should be a series of top level statements.
//
// If [printErrors] is `true`, any compile errors are output to stderr.
// Otherwise, they are silently discarded.
ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
bool isExpression, bool printErrors);
// When a class is defined, its superclass is not known until runtime since
// class definitions are just imperative statements. Most of the bytecode for a
// a method doesn't care, but there are two places where it matters:
//
// - To load or store a field, we need to know the index of the field in the
// instance's field array. We need to adjust this so that subclass fields
// are positioned after superclass fields, and we don't know this until the
// superclass is known.
//
// - Superclass calls need to know which superclass to dispatch to.
//
// We could handle this dynamically, but that adds overhead. Instead, when a
// method is bound, we walk the bytecode for the function and patch it up.
void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn);
// Reaches all of the heap-allocated objects in use by [compiler] (and all of
// its parents) so that they are not collected by the GC.
void wrenMarkCompiler(WrenVM* vm, Compiler* compiler);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,23 @@
#ifndef wren_core_h
#define wren_core_h
#include "wren_vm.h"
// This module defines the built-in classes and their primitives methods that
// are implemented directly in C code. Some languages try to implement as much
// of the core module itself in the primary language instead of in the host
// language.
//
// With Wren, we try to do as much of it in C as possible. Primitive methods
// are always faster than code written in Wren, and it minimizes startup time
// since we don't have to parse, compile, and execute Wren code.
//
// There is one limitation, though. Methods written in C cannot call Wren ones.
// They can only be the top of the callstack, and immediately return. This
// makes it difficult to have primitive methods that rely on polymorphic
// behavior. For example, `System.print` should call `toString` on its argument,
// including user-defined `toString` methods on user-defined classes.
void wrenInitializeCore(WrenVM* vm);
#endif

@ -0,0 +1,483 @@
class Bool {}
class Fiber {}
class Fn {}
class Null {}
class Num {}
class Sequence {
all(f) {
var result = true
for (element in this) {
result = f.call(element)
if (!result) return result
}
return result
}
any(f) {
var result = false
for (element in this) {
result = f.call(element)
if (result) return result
}
return result
}
contains(element) {
for (item in this) {
if (element == item) return true
}
return false
}
count {
var result = 0
for (element in this) {
result = result + 1
}
return result
}
count(f) {
var result = 0
for (element in this) {
if (f.call(element)) result = result + 1
}
return result
}
each(f) {
for (element in this) {
f.call(element)
}
}
isEmpty { iterate(null) ? false : true }
map(transformation) { MapSequence.new(this, transformation) }
skip(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
}
return SkipSequence.new(this, count)
}
take(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
}
return TakeSequence.new(this, count)
}
where(predicate) { WhereSequence.new(this, predicate) }
reduce(acc, f) {
for (element in this) {
acc = f.call(acc, element)
}
return acc
}
reduce(f) {
var iter = iterate(null)
if (!iter) Fiber.abort("Can't reduce an empty sequence.")
// Seed with the first element.
var result = iteratorValue(iter)
while (iter = iterate(iter)) {
result = f.call(result, iteratorValue(iter))
}
return result
}
join() { join("") }
join(sep) {
var first = true
var result = ""
for (element in this) {
if (!first) result = result + sep
first = false
result = result + element.toString
}
return result
}
toList {
var result = List.new()
for (element in this) {
result.add(element)
}
return result
}
}
class MapSequence is Sequence {
construct new(sequence, fn) {
_sequence = sequence
_fn = fn
}
iterate(iterator) { _sequence.iterate(iterator) }
iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }
}
class SkipSequence is Sequence {
construct new(sequence, count) {
_sequence = sequence
_count = count
}
iterate(iterator) {
if (iterator) {
return _sequence.iterate(iterator)
} else {
iterator = _sequence.iterate(iterator)
var count = _count
while (count > 0 && iterator) {
iterator = _sequence.iterate(iterator)
count = count - 1
}
return iterator
}
}
iteratorValue(iterator) { _sequence.iteratorValue(iterator) }
}
class TakeSequence is Sequence {
construct new(sequence, count) {
_sequence = sequence
_count = count
}
iterate(iterator) {
if (!iterator) _taken = 1 else _taken = _taken + 1
return _taken > _count ? null : _sequence.iterate(iterator)
}
iteratorValue(iterator) { _sequence.iteratorValue(iterator) }
}
class WhereSequence is Sequence {
construct new(sequence, fn) {
_sequence = sequence
_fn = fn
}
iterate(iterator) {
while (iterator = _sequence.iterate(iterator)) {
if (_fn.call(_sequence.iteratorValue(iterator))) break
}
return iterator
}
iteratorValue(iterator) { _sequence.iteratorValue(iterator) }
}
class String is Sequence {
bytes { StringByteSequence.new(this) }
codePoints { StringCodePointSequence.new(this) }
split(delimiter) {
if (!(delimiter is String) || delimiter.isEmpty) {
Fiber.abort("Delimiter must be a non-empty string.")
}
var result = []
var last = 0
var index = 0
var delimSize = delimiter.byteCount_
var size = byteCount_
while (last < size && (index = indexOf(delimiter, last)) != -1) {
result.add(this[last...index])
last = index + delimSize
}
if (last < size) {
result.add(this[last..-1])
} else {
result.add("")
}
return result
}
replace(from, to) {
if (!(from is String) || from.isEmpty) {
Fiber.abort("From must be a non-empty string.")
} else if (!(to is String)) {
Fiber.abort("To must be a string.")
}
var result = ""
var last = 0
var index = 0
var fromSize = from.byteCount_
var size = byteCount_
while (last < size && (index = indexOf(from, last)) != -1) {
result = result + this[last...index] + to
last = index + fromSize
}
if (last < size) result = result + this[last..-1]
return result
}
trim() { trim_("\t\r\n ", true, true) }
trim(chars) { trim_(chars, true, true) }
trimEnd() { trim_("\t\r\n ", false, true) }
trimEnd(chars) { trim_(chars, false, true) }
trimStart() { trim_("\t\r\n ", true, false) }
trimStart(chars) { trim_(chars, true, false) }
trim_(chars, trimStart, trimEnd) {
if (!(chars is String)) {
Fiber.abort("Characters must be a string.")
}
var codePoints = chars.codePoints.toList
var start
if (trimStart) {
while (start = iterate(start)) {
if (!codePoints.contains(codePointAt_(start))) break
}
if (start == false) return ""
} else {
start = 0
}
var end
if (trimEnd) {
end = byteCount_ - 1
while (end >= start) {
var codePoint = codePointAt_(end)
if (codePoint != -1 && !codePoints.contains(codePoint)) break
end = end - 1
}
if (end < start) return ""
} else {
end = -1
}
return this[start..end]
}
*(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
}
var result = ""
for (i in 0...count) {
result = result + this
}
return result
}
}
class StringByteSequence is Sequence {
construct new(string) {
_string = string
}
[index] { _string.byteAt_(index) }
iterate(iterator) { _string.iterateByte_(iterator) }
iteratorValue(iterator) { _string.byteAt_(iterator) }
count { _string.byteCount_ }
}
class StringCodePointSequence is Sequence {
construct new(string) {
_string = string
}
[index] { _string.codePointAt_(index) }
iterate(iterator) { _string.iterate(iterator) }
iteratorValue(iterator) { _string.codePointAt_(iterator) }
count { _string.count }
}
class List is Sequence {
addAll(other) {
for (element in other) {
add(element)
}
return other
}
sort() { sort {|low, high| low < high } }
sort(comparer) {
if (!(comparer is Fn)) {
Fiber.abort("Comparer must be a function.")
}
quicksort_(0, count - 1, comparer)
return this
}
quicksort_(low, high, comparer) {
if (low < high) {
var p = partition_(low, high, comparer)
quicksort_(low, p - 1, comparer)
quicksort_(p + 1, high, comparer)
}
}
partition_(low, high, comparer) {
var p = this[high]
var i = low - 1
for (j in low..(high-1)) {
if (comparer.call(this[j], p)) {
i = i + 1
var t = this[i]
this[i] = this[j]
this[j] = t
}
}
var t = this[i+1]
this[i+1] = this[high]
this[high] = t
return i+1
}
toString { "[%(join(", "))]" }
+(other) {
var result = this[0..-1]
for (element in other) {
result.add(element)
}
return result
}
*(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
}
var result = []
for (i in 0...count) {
result.addAll(this)
}
return result
}
}
class Map is Sequence {
keys { MapKeySequence.new(this) }
values { MapValueSequence.new(this) }
toString {
var first = true
var result = "{"
for (key in keys) {
if (!first) result = result + ", "
first = false
result = result + "%(key): %(this[key])"
}
return result + "}"
}
iteratorValue(iterator) {
return MapEntry.new(
keyIteratorValue_(iterator),
valueIteratorValue_(iterator))
}
}
class MapEntry {
construct new(key, value) {
_key = key
_value = value
}
key { _key }
value { _value }
toString { "%(_key):%(_value)" }
}
class MapKeySequence is Sequence {
construct new(map) {
_map = map
}
iterate(n) { _map.iterate(n) }
iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }
}
class MapValueSequence is Sequence {
construct new(map) {
_map = map
}
iterate(n) { _map.iterate(n) }
iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }
}
class Range is Sequence {}
class System {
static print() {
writeString_("\n")
}
static print(obj) {
writeObject_(obj)
writeString_("\n")
return obj
}
static printAll(sequence) {
for (object in sequence) writeObject_(object)
writeString_("\n")
}
static write(obj) {
writeObject_(obj)
return obj
}
static writeAll(sequence) {
for (object in sequence) writeObject_(object)
}
static writeObject_(obj) {
var string = obj.toString
if (string is String) {
writeString_(string)
} else {
writeString_("[invalid toString]")
}
}
}
class ClassAttributes {
self { _attributes }
methods { _methods }
construct new(attributes, methods) {
_attributes = attributes
_methods = methods
}
toString { "attributes:%(_attributes) methods:%(_methods)" }
}

@ -0,0 +1,485 @@
// Generated automatically from src/vm/wren_core.wren. Do not edit.
static const char* coreModuleSource =
"class Bool {}\n"
"class Fiber {}\n"
"class Fn {}\n"
"class Null {}\n"
"class Num {}\n"
"\n"
"class Sequence {\n"
" all(f) {\n"
" var result = true\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" if (!result) return result\n"
" }\n"
" return result\n"
" }\n"
"\n"
" any(f) {\n"
" var result = false\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" if (result) return result\n"
" }\n"
" return result\n"
" }\n"
"\n"
" contains(element) {\n"
" for (item in this) {\n"
" if (element == item) return true\n"
" }\n"
" return false\n"
" }\n"
"\n"
" count {\n"
" var result = 0\n"
" for (element in this) {\n"
" result = result + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" count(f) {\n"
" var result = 0\n"
" for (element in this) {\n"
" if (f.call(element)) result = result + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" each(f) {\n"
" for (element in this) {\n"
" f.call(element)\n"
" }\n"
" }\n"
"\n"
" isEmpty { iterate(null) ? false : true }\n"
"\n"
" map(transformation) { MapSequence.new(this, transformation) }\n"
"\n"
" skip(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
" }\n"
"\n"
" return SkipSequence.new(this, count)\n"
" }\n"
"\n"
" take(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
" }\n"
"\n"
" return TakeSequence.new(this, count)\n"
" }\n"
"\n"
" where(predicate) { WhereSequence.new(this, predicate) }\n"
"\n"
" reduce(acc, f) {\n"
" for (element in this) {\n"
" acc = f.call(acc, element)\n"
" }\n"
" return acc\n"
" }\n"
"\n"
" reduce(f) {\n"
" var iter = iterate(null)\n"
" if (!iter) Fiber.abort(\"Can't reduce an empty sequence.\")\n"
"\n"
" // Seed with the first element.\n"
" var result = iteratorValue(iter)\n"
" while (iter = iterate(iter)) {\n"
" result = f.call(result, iteratorValue(iter))\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" join() { join(\"\") }\n"
"\n"
" join(sep) {\n"
" var first = true\n"
" var result = \"\"\n"
"\n"
" for (element in this) {\n"
" if (!first) result = result + sep\n"
" first = false\n"
" result = result + element.toString\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" toList {\n"
" var result = List.new()\n"
" for (element in this) {\n"
" result.add(element)\n"
" }\n"
" return result\n"
" }\n"
"}\n"
"\n"
"class MapSequence is Sequence {\n"
" construct new(sequence, fn) {\n"
" _sequence = sequence\n"
" _fn = fn\n"
" }\n"
"\n"
" iterate(iterator) { _sequence.iterate(iterator) }\n"
" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n"
"}\n"
"\n"
"class SkipSequence is Sequence {\n"
" construct new(sequence, count) {\n"
" _sequence = sequence\n"
" _count = count\n"
" }\n"
"\n"
" iterate(iterator) {\n"
" if (iterator) {\n"
" return _sequence.iterate(iterator)\n"
" } else {\n"
" iterator = _sequence.iterate(iterator)\n"
" var count = _count\n"
" while (count > 0 && iterator) {\n"
" iterator = _sequence.iterate(iterator)\n"
" count = count - 1\n"
" }\n"
" return iterator\n"
" }\n"
" }\n"
"\n"
" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n"
"}\n"
"\n"
"class TakeSequence is Sequence {\n"
" construct new(sequence, count) {\n"
" _sequence = sequence\n"
" _count = count\n"
" }\n"
"\n"
" iterate(iterator) {\n"
" if (!iterator) _taken = 1 else _taken = _taken + 1\n"
" return _taken > _count ? null : _sequence.iterate(iterator)\n"
" }\n"
"\n"
" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n"
"}\n"
"\n"
"class WhereSequence is Sequence {\n"
" construct new(sequence, fn) {\n"
" _sequence = sequence\n"
" _fn = fn\n"
" }\n"
"\n"
" iterate(iterator) {\n"
" while (iterator = _sequence.iterate(iterator)) {\n"
" if (_fn.call(_sequence.iteratorValue(iterator))) break\n"
" }\n"
" return iterator\n"
" }\n"
"\n"
" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n"
"}\n"
"\n"
"class String is Sequence {\n"
" bytes { StringByteSequence.new(this) }\n"
" codePoints { StringCodePointSequence.new(this) }\n"
"\n"
" split(delimiter) {\n"
" if (!(delimiter is String) || delimiter.isEmpty) {\n"
" Fiber.abort(\"Delimiter must be a non-empty string.\")\n"
" }\n"
"\n"
" var result = []\n"
"\n"
" var last = 0\n"
" var index = 0\n"
"\n"
" var delimSize = delimiter.byteCount_\n"
" var size = byteCount_\n"
"\n"
" while (last < size && (index = indexOf(delimiter, last)) != -1) {\n"
" result.add(this[last...index])\n"
" last = index + delimSize\n"
" }\n"
"\n"
" if (last < size) {\n"
" result.add(this[last..-1])\n"
" } else {\n"
" result.add(\"\")\n"
" }\n"
" return result\n"
" }\n"
"\n"
" replace(from, to) {\n"
" if (!(from is String) || from.isEmpty) {\n"
" Fiber.abort(\"From must be a non-empty string.\")\n"
" } else if (!(to is String)) {\n"
" Fiber.abort(\"To must be a string.\")\n"
" }\n"
"\n"
" var result = \"\"\n"
"\n"
" var last = 0\n"
" var index = 0\n"
"\n"
" var fromSize = from.byteCount_\n"
" var size = byteCount_\n"
"\n"
" while (last < size && (index = indexOf(from, last)) != -1) {\n"
" result = result + this[last...index] + to\n"
" last = index + fromSize\n"
" }\n"
"\n"
" if (last < size) result = result + this[last..-1]\n"
"\n"
" return result\n"
" }\n"
"\n"
" trim() { trim_(\"\\t\\r\\n \", true, true) }\n"
" trim(chars) { trim_(chars, true, true) }\n"
" trimEnd() { trim_(\"\\t\\r\\n \", false, true) }\n"
" trimEnd(chars) { trim_(chars, false, true) }\n"
" trimStart() { trim_(\"\\t\\r\\n \", true, false) }\n"
" trimStart(chars) { trim_(chars, true, false) }\n"
"\n"
" trim_(chars, trimStart, trimEnd) {\n"
" if (!(chars is String)) {\n"
" Fiber.abort(\"Characters must be a string.\")\n"
" }\n"
"\n"
" var codePoints = chars.codePoints.toList\n"
"\n"
" var start\n"
" if (trimStart) {\n"
" while (start = iterate(start)) {\n"
" if (!codePoints.contains(codePointAt_(start))) break\n"
" }\n"
"\n"
" if (start == false) return \"\"\n"
" } else {\n"
" start = 0\n"
" }\n"
"\n"
" var end\n"
" if (trimEnd) {\n"
" end = byteCount_ - 1\n"
" while (end >= start) {\n"
" var codePoint = codePointAt_(end)\n"
" if (codePoint != -1 && !codePoints.contains(codePoint)) break\n"
" end = end - 1\n"
" }\n"
"\n"
" if (end < start) return \"\"\n"
" } else {\n"
" end = -1\n"
" }\n"
"\n"
" return this[start..end]\n"
" }\n"
"\n"
" *(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
" }\n"
"\n"
" var result = \"\"\n"
" for (i in 0...count) {\n"
" result = result + this\n"
" }\n"
" return result\n"
" }\n"
"}\n"
"\n"
"class StringByteSequence is Sequence {\n"
" construct new(string) {\n"
" _string = string\n"
" }\n"
"\n"
" [index] { _string.byteAt_(index) }\n"
" iterate(iterator) { _string.iterateByte_(iterator) }\n"
" iteratorValue(iterator) { _string.byteAt_(iterator) }\n"
"\n"
" count { _string.byteCount_ }\n"
"}\n"
"\n"
"class StringCodePointSequence is Sequence {\n"
" construct new(string) {\n"
" _string = string\n"
" }\n"
"\n"
" [index] { _string.codePointAt_(index) }\n"
" iterate(iterator) { _string.iterate(iterator) }\n"
" iteratorValue(iterator) { _string.codePointAt_(iterator) }\n"
"\n"
" count { _string.count }\n"
"}\n"
"\n"
"class List is Sequence {\n"
" addAll(other) {\n"
" for (element in other) {\n"
" add(element)\n"
" }\n"
" return other\n"
" }\n"
"\n"
" sort() { sort {|low, high| low < high } }\n"
"\n"
" sort(comparer) {\n"
" if (!(comparer is Fn)) {\n"
" Fiber.abort(\"Comparer must be a function.\")\n"
" }\n"
" quicksort_(0, count - 1, comparer)\n"
" return this\n"
" }\n"
"\n"
" quicksort_(low, high, comparer) {\n"
" if (low < high) {\n"
" var p = partition_(low, high, comparer)\n"
" quicksort_(low, p - 1, comparer)\n"
" quicksort_(p + 1, high, comparer)\n"
" }\n"
" }\n"
"\n"
" partition_(low, high, comparer) {\n"
" var p = this[high]\n"
" var i = low - 1\n"
" for (j in low..(high-1)) {\n"
" if (comparer.call(this[j], p)) { \n"
" i = i + 1\n"
" var t = this[i]\n"
" this[i] = this[j]\n"
" this[j] = t\n"
" }\n"
" }\n"
" var t = this[i+1]\n"
" this[i+1] = this[high]\n"
" this[high] = t\n"
" return i+1\n"
" }\n"
"\n"
" toString { \"[%(join(\", \"))]\" }\n"
"\n"
" +(other) {\n"
" var result = this[0..-1]\n"
" for (element in other) {\n"
" result.add(element)\n"
" }\n"
" return result\n"
" }\n"
"\n"
" *(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
" }\n"
"\n"
" var result = []\n"
" for (i in 0...count) {\n"
" result.addAll(this)\n"
" }\n"
" return result\n"
" }\n"
"}\n"
"\n"
"class Map is Sequence {\n"
" keys { MapKeySequence.new(this) }\n"
" values { MapValueSequence.new(this) }\n"
"\n"
" toString {\n"
" var first = true\n"
" var result = \"{\"\n"
"\n"
" for (key in keys) {\n"
" if (!first) result = result + \", \"\n"
" first = false\n"
" result = result + \"%(key): %(this[key])\"\n"
" }\n"
"\n"
" return result + \"}\"\n"
" }\n"
"\n"
" iteratorValue(iterator) {\n"
" return MapEntry.new(\n"
" keyIteratorValue_(iterator),\n"
" valueIteratorValue_(iterator))\n"
" }\n"
"}\n"
"\n"
"class MapEntry {\n"
" construct new(key, value) {\n"
" _key = key\n"
" _value = value\n"
" }\n"
"\n"
" key { _key }\n"
" value { _value }\n"
"\n"
" toString { \"%(_key):%(_value)\" }\n"
"}\n"
"\n"
"class MapKeySequence is Sequence {\n"
" construct new(map) {\n"
" _map = map\n"
" }\n"
"\n"
" iterate(n) { _map.iterate(n) }\n"
" iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }\n"
"}\n"
"\n"
"class MapValueSequence is Sequence {\n"
" construct new(map) {\n"
" _map = map\n"
" }\n"
"\n"
" iterate(n) { _map.iterate(n) }\n"
" iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }\n"
"}\n"
"\n"
"class Range is Sequence {}\n"
"\n"
"class System {\n"
" static print() {\n"
" writeString_(\"\\n\")\n"
" }\n"
"\n"
" static print(obj) {\n"
" writeObject_(obj)\n"
" writeString_(\"\\n\")\n"
" return obj\n"
" }\n"
"\n"
" static printAll(sequence) {\n"
" for (object in sequence) writeObject_(object)\n"
" writeString_(\"\\n\")\n"
" }\n"
"\n"
" static write(obj) {\n"
" writeObject_(obj)\n"
" return obj\n"
" }\n"
"\n"
" static writeAll(sequence) {\n"
" for (object in sequence) writeObject_(object)\n"
" }\n"
"\n"
" static writeObject_(obj) {\n"
" var string = obj.toString\n"
" if (string is String) {\n"
" writeString_(string)\n"
" } else {\n"
" writeString_(\"[invalid toString]\")\n"
" }\n"
" }\n"
"}\n"
"\n"
"class ClassAttributes {\n"
" self { _attributes }\n"
" methods { _methods }\n"
" construct new(attributes, methods) {\n"
" _attributes = attributes\n"
" _methods = methods\n"
" }\n"
" toString { \"attributes:%(_attributes) methods:%(_methods)\" }\n"
"}\n";

@ -0,0 +1,388 @@
#include <stdio.h>
#include "wren_debug.h"
void wrenDebugPrintStackTrace(WrenVM* vm)
{
// Bail if the host doesn't enable printing errors.
if (vm->config.errorFn == NULL) return;
ObjFiber* fiber = vm->fiber;
if (IS_STRING(fiber->error))
{
vm->config.errorFn(vm, WREN_ERROR_RUNTIME,
NULL, -1, AS_CSTRING(fiber->error));
}
else
{
// TODO: Print something a little useful here. Maybe the name of the error's
// class?
vm->config.errorFn(vm, WREN_ERROR_RUNTIME,
NULL, -1, "[error object]");
}
for (int i = fiber->numFrames - 1; i >= 0; i--)
{
CallFrame* frame = &fiber->frames[i];
ObjFn* fn = frame->closure->fn;
// Skip over stub functions for calling methods from the C API.
if (fn->module == NULL) continue;
// The built-in core module has no name. We explicitly omit it from stack
// traces since we don't want to highlight to a user the implementation
// detail of what part of the core module is written in C and what is Wren.
if (fn->module->name == NULL) continue;
// -1 because IP has advanced past the instruction that it just executed.
int line = fn->debug->sourceLines.data[frame->ip - fn->code.data - 1];
vm->config.errorFn(vm, WREN_ERROR_STACK_TRACE,
fn->module->name->value, line,
fn->debug->name);
}
}
static void dumpObject(Obj* obj)
{
switch (obj->type)
{
case OBJ_CLASS:
printf("[class %s %p]", ((ObjClass*)obj)->name->value, obj);
break;
case OBJ_CLOSURE: printf("[closure %p]", obj); break;
case OBJ_FIBER: printf("[fiber %p]", obj); break;
case OBJ_FN: printf("[fn %p]", obj); break;
case OBJ_FOREIGN: printf("[foreign %p]", obj); break;
case OBJ_INSTANCE: printf("[instance %p]", obj); break;
case OBJ_LIST: printf("[list %p]", obj); break;
case OBJ_MAP: printf("[map %p]", obj); break;
case OBJ_MODULE: printf("[module %p]", obj); break;
case OBJ_RANGE: printf("[range %p]", obj); break;
case OBJ_STRING: printf("%s", ((ObjString*)obj)->value); break;
case OBJ_UPVALUE: printf("[upvalue %p]", obj); break;
default: printf("[unknown object %d]", obj->type); break;
}
}
void wrenDumpValue(Value value)
{
#if WREN_NAN_TAGGING
if (IS_NUM(value))
{
printf("%.14g", AS_NUM(value));
}
else if (IS_OBJ(value))
{
dumpObject(AS_OBJ(value));
}
else
{
switch (GET_TAG(value))
{
case TAG_FALSE: printf("false"); break;
case TAG_NAN: printf("NaN"); break;
case TAG_NULL: printf("null"); break;
case TAG_TRUE: printf("true"); break;
case TAG_UNDEFINED: UNREACHABLE();
}
}
#else
switch (value.type)
{
case VAL_FALSE: printf("false"); break;
case VAL_NULL: printf("null"); break;
case VAL_NUM: printf("%.14g", AS_NUM(value)); break;
case VAL_TRUE: printf("true"); break;
case VAL_OBJ: dumpObject(AS_OBJ(value)); break;
case VAL_UNDEFINED: UNREACHABLE();
}
#endif
}
static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int start = i;
uint8_t* bytecode = fn->code.data;
Code code = (Code)bytecode[i];
int line = fn->debug->sourceLines.data[i];
if (lastLine == NULL || *lastLine != line)
{
printf("%4d:", line);
if (lastLine != NULL) *lastLine = line;
}
else
{
printf(" ");
}
printf(" %04d ", i++);
#define READ_BYTE() (bytecode[i++])
#define READ_SHORT() (i += 2, (bytecode[i - 2] << 8) | bytecode[i - 1])
#define BYTE_INSTRUCTION(name) \
printf("%-16s %5d\n", name, READ_BYTE()); \
break
switch (code)
{
case CODE_CONSTANT:
{
int constant = READ_SHORT();
printf("%-16s %5d '", "CONSTANT", constant);
wrenDumpValue(fn->constants.data[constant]);
printf("'\n");
break;
}
case CODE_NULL: printf("NULL\n"); break;
case CODE_FALSE: printf("FALSE\n"); break;
case CODE_TRUE: printf("TRUE\n"); break;
case CODE_LOAD_LOCAL_0: printf("LOAD_LOCAL_0\n"); break;
case CODE_LOAD_LOCAL_1: printf("LOAD_LOCAL_1\n"); break;
case CODE_LOAD_LOCAL_2: printf("LOAD_LOCAL_2\n"); break;
case CODE_LOAD_LOCAL_3: printf("LOAD_LOCAL_3\n"); break;
case CODE_LOAD_LOCAL_4: printf("LOAD_LOCAL_4\n"); break;
case CODE_LOAD_LOCAL_5: printf("LOAD_LOCAL_5\n"); break;
case CODE_LOAD_LOCAL_6: printf("LOAD_LOCAL_6\n"); break;
case CODE_LOAD_LOCAL_7: printf("LOAD_LOCAL_7\n"); break;
case CODE_LOAD_LOCAL_8: printf("LOAD_LOCAL_8\n"); break;
case CODE_LOAD_LOCAL: BYTE_INSTRUCTION("LOAD_LOCAL");
case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL");
case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE");
case CODE_STORE_UPVALUE: BYTE_INSTRUCTION("STORE_UPVALUE");
case CODE_LOAD_MODULE_VAR:
{
int slot = READ_SHORT();
printf("%-16s %5d '%s'\n", "LOAD_MODULE_VAR", slot,
fn->module->variableNames.data[slot]->value);
break;
}
case CODE_STORE_MODULE_VAR:
{
int slot = READ_SHORT();
printf("%-16s %5d '%s'\n", "STORE_MODULE_VAR", slot,
fn->module->variableNames.data[slot]->value);
break;
}
case CODE_LOAD_FIELD_THIS: BYTE_INSTRUCTION("LOAD_FIELD_THIS");
case CODE_STORE_FIELD_THIS: BYTE_INSTRUCTION("STORE_FIELD_THIS");
case CODE_LOAD_FIELD: BYTE_INSTRUCTION("LOAD_FIELD");
case CODE_STORE_FIELD: BYTE_INSTRUCTION("STORE_FIELD");
case CODE_POP: printf("POP\n"); break;
case CODE_CALL_0:
case CODE_CALL_1:
case CODE_CALL_2:
case CODE_CALL_3:
case CODE_CALL_4:
case CODE_CALL_5:
case CODE_CALL_6:
case CODE_CALL_7:
case CODE_CALL_8:
case CODE_CALL_9:
case CODE_CALL_10:
case CODE_CALL_11:
case CODE_CALL_12:
case CODE_CALL_13:
case CODE_CALL_14:
case CODE_CALL_15:
case CODE_CALL_16:
{
int numArgs = bytecode[i - 1] - CODE_CALL_0;
int symbol = READ_SHORT();
printf("CALL_%-11d %5d '%s'\n", numArgs, symbol,
vm->methodNames.data[symbol]->value);
break;
}
case CODE_SUPER_0:
case CODE_SUPER_1:
case CODE_SUPER_2:
case CODE_SUPER_3:
case CODE_SUPER_4:
case CODE_SUPER_5:
case CODE_SUPER_6:
case CODE_SUPER_7:
case CODE_SUPER_8:
case CODE_SUPER_9:
case CODE_SUPER_10:
case CODE_SUPER_11:
case CODE_SUPER_12:
case CODE_SUPER_13:
case CODE_SUPER_14:
case CODE_SUPER_15:
case CODE_SUPER_16:
{
int numArgs = bytecode[i - 1] - CODE_SUPER_0;
int symbol = READ_SHORT();
int superclass = READ_SHORT();
printf("SUPER_%-10d %5d '%s' %5d\n", numArgs, symbol,
vm->methodNames.data[symbol]->value, superclass);
break;
}
case CODE_JUMP:
{
int offset = READ_SHORT();
printf("%-16s %5d to %d\n", "JUMP", offset, i + offset);
break;
}
case CODE_LOOP:
{
int offset = READ_SHORT();
printf("%-16s %5d to %d\n", "LOOP", offset, i - offset);
break;
}
case CODE_JUMP_IF:
{
int offset = READ_SHORT();
printf("%-16s %5d to %d\n", "JUMP_IF", offset, i + offset);
break;
}
case CODE_AND:
{
int offset = READ_SHORT();
printf("%-16s %5d to %d\n", "AND", offset, i + offset);
break;
}
case CODE_OR:
{
int offset = READ_SHORT();
printf("%-16s %5d to %d\n", "OR", offset, i + offset);
break;
}
case CODE_CLOSE_UPVALUE: printf("CLOSE_UPVALUE\n"); break;
case CODE_RETURN: printf("RETURN\n"); break;
case CODE_CLOSURE:
{
int constant = READ_SHORT();
printf("%-16s %5d ", "CLOSURE", constant);
wrenDumpValue(fn->constants.data[constant]);
printf(" ");
ObjFn* loadedFn = AS_FN(fn->constants.data[constant]);
for (int j = 0; j < loadedFn->numUpvalues; j++)
{
int isLocal = READ_BYTE();
int index = READ_BYTE();
if (j > 0) printf(", ");
printf("%s %d", isLocal ? "local" : "upvalue", index);
}
printf("\n");
break;
}
case CODE_CONSTRUCT: printf("CONSTRUCT\n"); break;
case CODE_FOREIGN_CONSTRUCT: printf("FOREIGN_CONSTRUCT\n"); break;
case CODE_CLASS:
{
int numFields = READ_BYTE();
printf("%-16s %5d fields\n", "CLASS", numFields);
break;
}
case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n"); break;
case CODE_END_CLASS: printf("END_CLASS\n"); break;
case CODE_METHOD_INSTANCE:
{
int symbol = READ_SHORT();
printf("%-16s %5d '%s'\n", "METHOD_INSTANCE", symbol,
vm->methodNames.data[symbol]->value);
break;
}
case CODE_METHOD_STATIC:
{
int symbol = READ_SHORT();
printf("%-16s %5d '%s'\n", "METHOD_STATIC", symbol,
vm->methodNames.data[symbol]->value);
break;
}
case CODE_END_MODULE:
printf("END_MODULE\n");
break;
case CODE_IMPORT_MODULE:
{
int name = READ_SHORT();
printf("%-16s %5d '", "IMPORT_MODULE", name);
wrenDumpValue(fn->constants.data[name]);
printf("'\n");
break;
}
case CODE_IMPORT_VARIABLE:
{
int variable = READ_SHORT();
printf("%-16s %5d '", "IMPORT_VARIABLE", variable);
wrenDumpValue(fn->constants.data[variable]);
printf("'\n");
break;
}
case CODE_END:
printf("END\n");
break;
default:
printf("UKNOWN! [%d]\n", bytecode[i - 1]);
break;
}
// Return how many bytes this instruction takes, or -1 if it's an END.
if (code == CODE_END) return -1;
return i - start;
#undef READ_BYTE
#undef READ_SHORT
}
int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i)
{
return dumpInstruction(vm, fn, i, NULL);
}
void wrenDumpCode(WrenVM* vm, ObjFn* fn)
{
printf("%s: %s\n",
fn->module->name == NULL ? "<core>" : fn->module->name->value,
fn->debug->name);
int i = 0;
int lastLine = -1;
for (;;)
{
int offset = dumpInstruction(vm, fn, i, &lastLine);
if (offset == -1) break;
i += offset;
}
printf("\n");
}
void wrenDumpStack(ObjFiber* fiber)
{
printf("(fiber %p) ", fiber);
for (Value* slot = fiber->stack; slot < fiber->stackTop; slot++)
{
wrenDumpValue(*slot);
printf(" | ");
}
printf("\n");
}

@ -0,0 +1,27 @@
#ifndef wren_debug_h
#define wren_debug_h
#include "wren_value.h"
#include "wren_vm.h"
// Prints the stack trace for the current fiber.
//
// Used when a fiber throws a runtime error which is not caught.
void wrenDebugPrintStackTrace(WrenVM* vm);
// The "dump" functions are used for debugging Wren itself. Normal code paths
// will not call them unless one of the various DEBUG_ flags is enabled.
// Prints a representation of [value] to stdout.
void wrenDumpValue(Value value);
// Prints a representation of the bytecode for [fn] at instruction [i].
int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i);
// Prints the disassembled code for [fn] to stdout.
void wrenDumpCode(WrenVM* vm, ObjFn* fn);
// Prints the contents of the current stack for [fiber] to stdout.
void wrenDumpStack(ObjFiber* fiber);
#endif

@ -0,0 +1,34 @@
#ifndef wren_math_h
#define wren_math_h
#include <math.h>
#include <stdint.h>
// A union to let us reinterpret a double as raw bits and back.
typedef union
{
uint64_t bits64;
uint32_t bits32[2];
double num;
} WrenDoubleBits;
#define WREN_DOUBLE_QNAN_POS_MIN_BITS (UINT64_C(0x7FF8000000000000))
#define WREN_DOUBLE_QNAN_POS_MAX_BITS (UINT64_C(0x7FFFFFFFFFFFFFFF))
#define WREN_DOUBLE_NAN (wrenDoubleFromBits(WREN_DOUBLE_QNAN_POS_MIN_BITS))
static inline double wrenDoubleFromBits(uint64_t bits)
{
WrenDoubleBits data;
data.bits64 = bits;
return data.num;
}
static inline uint64_t wrenDoubleToBits(double num)
{
WrenDoubleBits data;
data.num = num;
return data.bits64;
}
#endif

@ -0,0 +1,217 @@
// This defines the bytecode instructions used by the VM. It does so by invoking
// an OPCODE() macro which is expected to be defined at the point that this is
// included. (See: http://en.wikipedia.org/wiki/X_Macro for more.)
//
// The first argument is the name of the opcode. The second is its "stack
// effect" -- the amount that the op code changes the size of the stack. A
// stack effect of 1 means it pushes a value and the stack grows one larger.
// -2 means it pops two values, etc.
//
// Note that the order of instructions here affects the order of the dispatch
// table in the VM's interpreter loop. That in turn affects caching which
// affects overall performance. Take care to run benchmarks if you change the
// order here.
// Load the constant at index [arg].
OPCODE(CONSTANT, 1)
// Push null onto the stack.
OPCODE(NULL, 1)
// Push false onto the stack.
OPCODE(FALSE, 1)
// Push true onto the stack.
OPCODE(TRUE, 1)
// Pushes the value in the given local slot.
OPCODE(LOAD_LOCAL_0, 1)
OPCODE(LOAD_LOCAL_1, 1)
OPCODE(LOAD_LOCAL_2, 1)
OPCODE(LOAD_LOCAL_3, 1)
OPCODE(LOAD_LOCAL_4, 1)
OPCODE(LOAD_LOCAL_5, 1)
OPCODE(LOAD_LOCAL_6, 1)
OPCODE(LOAD_LOCAL_7, 1)
OPCODE(LOAD_LOCAL_8, 1)
// Note: The compiler assumes the following _STORE instructions always
// immediately follow their corresponding _LOAD ones.
// Pushes the value in local slot [arg].
OPCODE(LOAD_LOCAL, 1)
// Stores the top of stack in local slot [arg]. Does not pop it.
OPCODE(STORE_LOCAL, 0)
// Pushes the value in upvalue [arg].
OPCODE(LOAD_UPVALUE, 1)
// Stores the top of stack in upvalue [arg]. Does not pop it.
OPCODE(STORE_UPVALUE, 0)
// Pushes the value of the top-level variable in slot [arg].
OPCODE(LOAD_MODULE_VAR, 1)
// Stores the top of stack in top-level variable slot [arg]. Does not pop it.
OPCODE(STORE_MODULE_VAR, 0)
// Pushes the value of the field in slot [arg] of the receiver of the current
// function. This is used for regular field accesses on "this" directly in
// methods. This instruction is faster than the more general CODE_LOAD_FIELD
// instruction.
OPCODE(LOAD_FIELD_THIS, 1)
// Stores the top of the stack in field slot [arg] in the receiver of the
// current value. Does not pop the value. This instruction is faster than the
// more general CODE_LOAD_FIELD instruction.
OPCODE(STORE_FIELD_THIS, 0)
// Pops an instance and pushes the value of the field in slot [arg] of it.
OPCODE(LOAD_FIELD, 0)
// Pops an instance and stores the subsequent top of stack in field slot
// [arg] in it. Does not pop the value.
OPCODE(STORE_FIELD, -1)
// Pop and discard the top of stack.
OPCODE(POP, -1)
// Invoke the method with symbol [arg]. The number indicates the number of
// arguments (not including the receiver).
OPCODE(CALL_0, 0)
OPCODE(CALL_1, -1)
OPCODE(CALL_2, -2)
OPCODE(CALL_3, -3)
OPCODE(CALL_4, -4)
OPCODE(CALL_5, -5)
OPCODE(CALL_6, -6)
OPCODE(CALL_7, -7)
OPCODE(CALL_8, -8)
OPCODE(CALL_9, -9)
OPCODE(CALL_10, -10)
OPCODE(CALL_11, -11)
OPCODE(CALL_12, -12)
OPCODE(CALL_13, -13)
OPCODE(CALL_14, -14)
OPCODE(CALL_15, -15)
OPCODE(CALL_16, -16)
// Invoke a superclass method with symbol [arg]. The number indicates the
// number of arguments (not including the receiver).
OPCODE(SUPER_0, 0)
OPCODE(SUPER_1, -1)
OPCODE(SUPER_2, -2)
OPCODE(SUPER_3, -3)
OPCODE(SUPER_4, -4)
OPCODE(SUPER_5, -5)
OPCODE(SUPER_6, -6)
OPCODE(SUPER_7, -7)
OPCODE(SUPER_8, -8)
OPCODE(SUPER_9, -9)
OPCODE(SUPER_10, -10)
OPCODE(SUPER_11, -11)
OPCODE(SUPER_12, -12)
OPCODE(SUPER_13, -13)
OPCODE(SUPER_14, -14)
OPCODE(SUPER_15, -15)
OPCODE(SUPER_16, -16)
// Jump the instruction pointer [arg] forward.
OPCODE(JUMP, 0)
// Jump the instruction pointer [arg] backward.
OPCODE(LOOP, 0)
// Pop and if not truthy then jump the instruction pointer [arg] forward.
OPCODE(JUMP_IF, -1)
// If the top of the stack is false, jump [arg] forward. Otherwise, pop and
// continue.
OPCODE(AND, -1)
// If the top of the stack is non-false, jump [arg] forward. Otherwise, pop
// and continue.
OPCODE(OR, -1)
// Close the upvalue for the local on the top of the stack, then pop it.
OPCODE(CLOSE_UPVALUE, -1)
// Exit from the current function and return the value on the top of the
// stack.
OPCODE(RETURN, 0)
// Creates a closure for the function stored at [arg] in the constant table.
//
// Following the function argument is a number of arguments, two for each
// upvalue. The first is true if the variable being captured is a local (as
// opposed to an upvalue), and the second is the index of the local or
// upvalue being captured.
//
// Pushes the created closure.
OPCODE(CLOSURE, 1)
// Creates a new instance of a class.
//
// Assumes the class object is in slot zero, and replaces it with the new
// uninitialized instance of that class. This opcode is only emitted by the
// compiler-generated constructor metaclass methods.
OPCODE(CONSTRUCT, 0)
// Creates a new instance of a foreign class.
//
// Assumes the class object is in slot zero, and replaces it with the new
// uninitialized instance of that class. This opcode is only emitted by the
// compiler-generated constructor metaclass methods.
OPCODE(FOREIGN_CONSTRUCT, 0)
// Creates a class. Top of stack is the superclass. Below that is a string for
// the name of the class. Byte [arg] is the number of fields in the class.
OPCODE(CLASS, -1)
// Ends a class.
// Atm the stack contains the class and the ClassAttributes (or null).
OPCODE(END_CLASS, -2)
// Creates a foreign class. Top of stack is the superclass. Below that is a
// string for the name of the class.
OPCODE(FOREIGN_CLASS, -1)
// Define a method for symbol [arg]. The class receiving the method is popped
// off the stack, then the function defining the body is popped.
//
// If a foreign method is being defined, the "function" will be a string
// identifying the foreign method. Otherwise, it will be a function or
// closure.
OPCODE(METHOD_INSTANCE, -2)
// Define a method for symbol [arg]. The class whose metaclass will receive
// the method is popped off the stack, then the function defining the body is
// popped.
//
// If a foreign method is being defined, the "function" will be a string
// identifying the foreign method. Otherwise, it will be a function or
// closure.
OPCODE(METHOD_STATIC, -2)
// This is executed at the end of the module's body. Pushes NULL onto the stack
// as the "return value" of the import statement and stores the module as the
// most recently imported one.
OPCODE(END_MODULE, 1)
// Import a module whose name is the string stored at [arg] in the constant
// table.
//
// Pushes null onto the stack so that the fiber for the imported module can
// replace that with a dummy value when it returns. (Fibers always return a
// value when resuming a caller.)
OPCODE(IMPORT_MODULE, 1)
// Import a variable from the most recently imported module. The name of the
// variable to import is at [arg] in the constant table. Pushes the loaded
// variable's value.
OPCODE(IMPORT_VARIABLE, 1)
// This pseudo-instruction indicates the end of the bytecode. It should
// always be preceded by a `CODE_RETURN`, so is never actually executed.
OPCODE(END, 0)

@ -0,0 +1,119 @@
#include "wren_primitive.h"
#include <math.h>
// Validates that [value] is an integer within `[0, count)`. Also allows
// negative indices which map backwards from the end. Returns the valid positive
// index value. If invalid, reports an error and returns `UINT32_MAX`.
static uint32_t validateIndexValue(WrenVM* vm, uint32_t count, double value,
const char* argName)
{
if (!validateIntValue(vm, value, argName)) return UINT32_MAX;
// Negative indices count from the end.
if (value < 0) value = count + value;
// Check bounds.
if (value >= 0 && value < count) return (uint32_t)value;
vm->fiber->error = wrenStringFormat(vm, "$ out of bounds.", argName);
return UINT32_MAX;
}
bool validateFn(WrenVM* vm, Value arg, const char* argName)
{
if (IS_CLOSURE(arg)) return true;
RETURN_ERROR_FMT("$ must be a function.", argName);
}
bool validateNum(WrenVM* vm, Value arg, const char* argName)
{
if (IS_NUM(arg)) return true;
RETURN_ERROR_FMT("$ must be a number.", argName);
}
bool validateIntValue(WrenVM* vm, double value, const char* argName)
{
if (trunc(value) == value) return true;
RETURN_ERROR_FMT("$ must be an integer.", argName);
}
bool validateInt(WrenVM* vm, Value arg, const char* argName)
{
// Make sure it's a number first.
if (!validateNum(vm, arg, argName)) return false;
return validateIntValue(vm, AS_NUM(arg), argName);
}
bool validateKey(WrenVM* vm, Value arg)
{
if (wrenMapIsValidKey(arg)) return true;
RETURN_ERROR("Key must be a value type.");
}
uint32_t validateIndex(WrenVM* vm, Value arg, uint32_t count,
const char* argName)
{
if (!validateNum(vm, arg, argName)) return UINT32_MAX;
return validateIndexValue(vm, count, AS_NUM(arg), argName);
}
bool validateString(WrenVM* vm, Value arg, const char* argName)
{
if (IS_STRING(arg)) return true;
RETURN_ERROR_FMT("$ must be a string.", argName);
}
uint32_t calculateRange(WrenVM* vm, ObjRange* range, uint32_t* length,
int* step)
{
*step = 0;
// Edge case: an empty range is allowed at the end of a sequence. This way,
// list[0..-1] and list[0...list.count] can be used to copy a list even when
// empty.
if (range->from == *length &&
range->to == (range->isInclusive ? -1.0 : (double)*length))
{
*length = 0;
return 0;
}
uint32_t from = validateIndexValue(vm, *length, range->from, "Range start");
if (from == UINT32_MAX) return UINT32_MAX;
// Bounds check the end manually to handle exclusive ranges.
double value = range->to;
if (!validateIntValue(vm, value, "Range end")) return UINT32_MAX;
// Negative indices count from the end.
if (value < 0) value = *length + value;
// Convert the exclusive range to an inclusive one.
if (!range->isInclusive)
{
// An exclusive range with the same start and end points is empty.
if (value == from)
{
*length = 0;
return from;
}
// Shift the endpoint to make it inclusive, handling both increasing and
// decreasing ranges.
value += value >= from ? -1 : 1;
}
// Check bounds.
if (value < 0 || value >= *length)
{
vm->fiber->error = CONST_STRING(vm, "Range end out of bounds.");
return UINT32_MAX;
}
uint32_t to = (uint32_t)value;
*length = abs((int)(from - to)) + 1;
*step = from < to ? 1 : -1;
return from;
}

@ -0,0 +1,109 @@
#ifndef wren_primitive_h
#define wren_primitive_h
#include "wren_vm.h"
// Binds a primitive method named [name] (in Wren) implemented using C function
// [fn] to `ObjClass` [cls].
#define PRIMITIVE(cls, name, function) \
do \
{ \
int symbol = wrenSymbolTableEnsure(vm, \
&vm->methodNames, name, strlen(name)); \
Method method; \
method.type = METHOD_PRIMITIVE; \
method.as.primitive = prim_##function; \
wrenBindMethod(vm, cls, symbol, method); \
} while (false)
// Binds a primitive method named [name] (in Wren) implemented using C function
// [fn] to `ObjClass` [cls], but as a FN call.
#define FUNCTION_CALL(cls, name, function) \
do \
{ \
int symbol = wrenSymbolTableEnsure(vm, \
&vm->methodNames, name, strlen(name)); \
Method method; \
method.type = METHOD_FUNCTION_CALL; \
method.as.primitive = prim_##function; \
wrenBindMethod(vm, cls, symbol, method); \
} while (false)
// Defines a primitive method whose C function name is [name]. This abstracts
// the actual type signature of a primitive function and makes it clear which C
// functions are invoked as primitives.
#define DEF_PRIMITIVE(name) \
static bool prim_##name(WrenVM* vm, Value* args)
#define RETURN_VAL(value) \
do \
{ \
args[0] = value; \
return true; \
} while (false)
#define RETURN_OBJ(obj) RETURN_VAL(OBJ_VAL(obj))
#define RETURN_BOOL(value) RETURN_VAL(BOOL_VAL(value))
#define RETURN_FALSE RETURN_VAL(FALSE_VAL)
#define RETURN_NULL RETURN_VAL(NULL_VAL)
#define RETURN_NUM(value) RETURN_VAL(NUM_VAL(value))
#define RETURN_TRUE RETURN_VAL(TRUE_VAL)
#define RETURN_ERROR(msg) \
do \
{ \
vm->fiber->error = wrenNewStringLength(vm, msg, sizeof(msg) - 1); \
return false; \
} while (false)
#define RETURN_ERROR_FMT(...) \
do \
{ \
vm->fiber->error = wrenStringFormat(vm, __VA_ARGS__); \
return false; \
} while (false)
// Validates that the given [arg] is a function. Returns true if it is. If not,
// reports an error and returns false.
bool validateFn(WrenVM* vm, Value arg, const char* argName);
// Validates that the given [arg] is a Num. Returns true if it is. If not,
// reports an error and returns false.
bool validateNum(WrenVM* vm, Value arg, const char* argName);
// Validates that [value] is an integer. Returns true if it is. If not, reports
// an error and returns false.
bool validateIntValue(WrenVM* vm, double value, const char* argName);
// Validates that the given [arg] is an integer. Returns true if it is. If not,
// reports an error and returns false.
bool validateInt(WrenVM* vm, Value arg, const char* argName);
// Validates that [arg] is a valid object for use as a map key. Returns true if
// it is. If not, reports an error and returns false.
bool validateKey(WrenVM* vm, Value arg);
// Validates that the argument at [argIndex] is an integer within `[0, count)`.
// Also allows negative indices which map backwards from the end. Returns the
// valid positive index value. If invalid, reports an error and returns
// `UINT32_MAX`.
uint32_t validateIndex(WrenVM* vm, Value arg, uint32_t count,
const char* argName);
// Validates that the given [arg] is a String. Returns true if it is. If not,
// reports an error and returns false.
bool validateString(WrenVM* vm, Value arg, const char* argName);
// Given a [range] and the [length] of the object being operated on, determines
// the series of elements that should be chosen from the underlying object.
// Handles ranges that count backwards from the end as well as negative ranges.
//
// Returns the index from which the range should start or `UINT32_MAX` if the
// range is invalid. After calling, [length] will be updated with the number of
// elements in the resulting sequence. [step] will be direction that the range
// is going: `1` if the range is increasing from the start index or `-1` if the
// range is decreasing.
uint32_t calculateRange(WrenVM* vm, ObjRange* range, uint32_t* length,
int* step);
#endif

@ -0,0 +1,207 @@
#include <string.h>
#include "wren_utils.h"
#include "wren_vm.h"
DEFINE_BUFFER(Byte, uint8_t);
DEFINE_BUFFER(Int, int);
DEFINE_BUFFER(String, ObjString*);
void wrenSymbolTableInit(SymbolTable* symbols)
{
wrenStringBufferInit(symbols);
}
void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols)
{
wrenStringBufferClear(vm, symbols);
}
int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length)
{
ObjString* symbol = AS_STRING(wrenNewStringLength(vm, name, length));
wrenPushRoot(vm, &symbol->obj);
wrenStringBufferWrite(vm, symbols, symbol);
wrenPopRoot(vm);
return symbols->count - 1;
}
int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length)
{
// See if the symbol is already defined.
int existing = wrenSymbolTableFind(symbols, name, length);
if (existing != -1) return existing;
// New symbol, so add it.
return wrenSymbolTableAdd(vm, symbols, name, length);
}
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length)
{
// See if the symbol is already defined.
// TODO: O(n). Do something better.
for (int i = 0; i < symbols->count; i++)
{
if (wrenStringEqualsCString(symbols->data[i], name, length)) return i;
}
return -1;
}
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable)
{
for (int i = 0; i < symbolTable->count; i++)
{
wrenGrayObj(vm, &symbolTable->data[i]->obj);
}
// Keep track of how much memory is still in use.
vm->bytesAllocated += symbolTable->capacity * sizeof(*symbolTable->data);
}
int wrenUtf8EncodeNumBytes(int value)
{
ASSERT(value >= 0, "Cannot encode a negative value.");
if (value <= 0x7f) return 1;
if (value <= 0x7ff) return 2;
if (value <= 0xffff) return 3;
if (value <= 0x10ffff) return 4;
return 0;
}
int wrenUtf8Encode(int value, uint8_t* bytes)
{
if (value <= 0x7f)
{
// Single byte (i.e. fits in ASCII).
*bytes = value & 0x7f;
return 1;
}
else if (value <= 0x7ff)
{
// Two byte sequence: 110xxxxx 10xxxxxx.
*bytes = 0xc0 | ((value & 0x7c0) >> 6);
bytes++;
*bytes = 0x80 | (value & 0x3f);
return 2;
}
else if (value <= 0xffff)
{
// Three byte sequence: 1110xxxx 10xxxxxx 10xxxxxx.
*bytes = 0xe0 | ((value & 0xf000) >> 12);
bytes++;
*bytes = 0x80 | ((value & 0xfc0) >> 6);
bytes++;
*bytes = 0x80 | (value & 0x3f);
return 3;
}
else if (value <= 0x10ffff)
{
// Four byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx.
*bytes = 0xf0 | ((value & 0x1c0000) >> 18);
bytes++;
*bytes = 0x80 | ((value & 0x3f000) >> 12);
bytes++;
*bytes = 0x80 | ((value & 0xfc0) >> 6);
bytes++;
*bytes = 0x80 | (value & 0x3f);
return 4;
}
// Invalid Unicode value. See: http://tools.ietf.org/html/rfc3629
UNREACHABLE();
return 0;
}
int wrenUtf8Decode(const uint8_t* bytes, uint32_t length)
{
// Single byte (i.e. fits in ASCII).
if (*bytes <= 0x7f) return *bytes;
int value;
uint32_t remainingBytes;
if ((*bytes & 0xe0) == 0xc0)
{
// Two byte sequence: 110xxxxx 10xxxxxx.
value = *bytes & 0x1f;
remainingBytes = 1;
}
else if ((*bytes & 0xf0) == 0xe0)
{
// Three byte sequence: 1110xxxx 10xxxxxx 10xxxxxx.
value = *bytes & 0x0f;
remainingBytes = 2;
}
else if ((*bytes & 0xf8) == 0xf0)
{
// Four byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx.
value = *bytes & 0x07;
remainingBytes = 3;
}
else
{
// Invalid UTF-8 sequence.
return -1;
}
// Don't read past the end of the buffer on truncated UTF-8.
if (remainingBytes > length - 1) return -1;
while (remainingBytes > 0)
{
bytes++;
remainingBytes--;
// Remaining bytes must be of form 10xxxxxx.
if ((*bytes & 0xc0) != 0x80) return -1;
value = value << 6 | (*bytes & 0x3f);
}
return value;
}
int wrenUtf8DecodeNumBytes(uint8_t byte)
{
// If the byte starts with 10xxxxx, it's the middle of a UTF-8 sequence, so
// don't count it at all.
if ((byte & 0xc0) == 0x80) return 0;
// The first byte's high bits tell us how many bytes are in the UTF-8
// sequence.
if ((byte & 0xf8) == 0xf0) return 4;
if ((byte & 0xf0) == 0xe0) return 3;
if ((byte & 0xe0) == 0xc0) return 2;
return 1;
}
// From: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
int wrenPowerOf2Ceil(int n)
{
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n;
}
uint32_t wrenValidateIndex(uint32_t count, int64_t value)
{
// Negative indices count from the end.
if (value < 0) value = count + value;
// Check bounds.
if (value >= 0 && value < count) return (uint32_t)value;
return UINT32_MAX;
}

@ -0,0 +1,126 @@
#ifndef wren_utils_h
#define wren_utils_h
#include "wren.h"
#include "wren_common.h"
// Reusable data structures and other utility functions.
// Forward declare this here to break a cycle between wren_utils.h and
// wren_value.h.
typedef struct sObjString ObjString;
// We need buffers of a few different types. To avoid lots of casting between
// void* and back, we'll use the preprocessor as a poor man's generics and let
// it generate a few type-specific ones.
#define DECLARE_BUFFER(name, type) \
typedef struct \
{ \
type* data; \
int count; \
int capacity; \
} name##Buffer; \
void wren##name##BufferInit(name##Buffer* buffer); \
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer); \
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
int count); \
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data)
// This should be used once for each type instantiation, somewhere in a .c file.
#define DEFINE_BUFFER(name, type) \
void wren##name##BufferInit(name##Buffer* buffer) \
{ \
buffer->data = NULL; \
buffer->capacity = 0; \
buffer->count = 0; \
} \
\
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer) \
{ \
wrenReallocate(vm, buffer->data, 0, 0); \
wren##name##BufferInit(buffer); \
} \
\
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
int count) \
{ \
if (buffer->capacity < buffer->count + count) \
{ \
int capacity = wrenPowerOf2Ceil(buffer->count + count); \
buffer->data = (type*)wrenReallocate(vm, buffer->data, \
buffer->capacity * sizeof(type), capacity * sizeof(type)); \
buffer->capacity = capacity; \
} \
\
for (int i = 0; i < count; i++) \
{ \
buffer->data[buffer->count++] = data; \
} \
} \
\
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) \
{ \
wren##name##BufferFill(vm, buffer, data, 1); \
}
DECLARE_BUFFER(Byte, uint8_t);
DECLARE_BUFFER(Int, int);
DECLARE_BUFFER(String, ObjString*);
// TODO: Change this to use a map.
typedef StringBuffer SymbolTable;
// Initializes the symbol table.
void wrenSymbolTableInit(SymbolTable* symbols);
// Frees all dynamically allocated memory used by the symbol table, but not the
// SymbolTable itself.
void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols);
// Adds name to the symbol table. Returns the index of it in the table.
int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length);
// Adds name to the symbol table. Returns the index of it in the table. Will
// use an existing symbol if already present.
int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length);
// Looks up name in the symbol table. Returns its index if found or -1 if not.
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length);
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable);
// Returns the number of bytes needed to encode [value] in UTF-8.
//
// Returns 0 if [value] is too large to encode.
int wrenUtf8EncodeNumBytes(int value);
// Encodes value as a series of bytes in [bytes], which is assumed to be large
// enough to hold the encoded result.
//
// Returns the number of written bytes.
int wrenUtf8Encode(int value, uint8_t* bytes);
// Decodes the UTF-8 sequence starting at [bytes] (which has max [length]),
// returning the code point.
//
// Returns -1 if the bytes are not a valid UTF-8 sequence.
int wrenUtf8Decode(const uint8_t* bytes, uint32_t length);
// Returns the number of bytes in the UTF-8 sequence starting with [byte].
//
// If the character at that index is not the beginning of a UTF-8 sequence,
// returns 0.
int wrenUtf8DecodeNumBytes(uint8_t byte);
// Returns the smallest power of two that is equal to or greater than [n].
int wrenPowerOf2Ceil(int n);
// Validates that [value] is within `[0, count)`. Also allows
// negative indices which map backwards from the end. Returns the valid positive
// index value. If invalid, returns `UINT32_MAX`.
uint32_t wrenValidateIndex(uint32_t count, int64_t value);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,890 @@
#ifndef wren_value_h
#define wren_value_h
#include <stdbool.h>
#include <string.h>
#include "wren_common.h"
#include "wren_math.h"
#include "wren_utils.h"
// This defines the built-in types and their core representations in memory.
// Since Wren is dynamically typed, any variable can hold a value of any type,
// and the type can change at runtime. Implementing this efficiently is
// critical for performance.
//
// The main type exposed by this is [Value]. A C variable of that type is a
// storage location that can hold any Wren value. The stack, module variables,
// and instance fields are all implemented in C as variables of type Value.
//
// The built-in types for booleans, numbers, and null are unboxed: their value
// is stored directly in the Value, and copying a Value copies the value. Other
// types--classes, instances of classes, functions, lists, and strings--are all
// reference types. They are stored on the heap and the Value just stores a
// pointer to it. Copying the Value copies a reference to the same object. The
// Wren implementation calls these "Obj", or objects, though to a user, all
// values are objects.
//
// There is also a special singleton value "undefined". It is used internally
// but never appears as a real value to a user. It has two uses:
//
// - It is used to identify module variables that have been implicitly declared
// by use in a forward reference but not yet explicitly declared. These only
// exist during compilation and do not appear at runtime.
//
// - It is used to represent unused map entries in an ObjMap.
//
// There are two supported Value representations. The main one uses a technique
// called "NaN tagging" (explained in detail below) to store a number, any of
// the value types, or a pointer, all inside one double-precision floating
// point number. A larger, slower, Value type that uses a struct to store these
// is also supported, and is useful for debugging the VM.
//
// The representation is controlled by the `WREN_NAN_TAGGING` define. If that's
// defined, Nan tagging is used.
// These macros cast a Value to one of the specific object types. These do *not*
// perform any validation, so must only be used after the Value has been
// ensured to be the right type.
#define AS_CLASS(value) ((ObjClass*)AS_OBJ(value)) // ObjClass*
#define AS_CLOSURE(value) ((ObjClosure*)AS_OBJ(value)) // ObjClosure*
#define AS_FIBER(v) ((ObjFiber*)AS_OBJ(v)) // ObjFiber*
#define AS_FN(value) ((ObjFn*)AS_OBJ(value)) // ObjFn*
#define AS_FOREIGN(v) ((ObjForeign*)AS_OBJ(v)) // ObjForeign*
#define AS_INSTANCE(value) ((ObjInstance*)AS_OBJ(value)) // ObjInstance*
#define AS_LIST(value) ((ObjList*)AS_OBJ(value)) // ObjList*
#define AS_MAP(value) ((ObjMap*)AS_OBJ(value)) // ObjMap*
#define AS_MODULE(value) ((ObjModule*)AS_OBJ(value)) // ObjModule*
#define AS_NUM(value) (wrenValueToNum(value)) // double
#define AS_RANGE(v) ((ObjRange*)AS_OBJ(v)) // ObjRange*
#define AS_STRING(v) ((ObjString*)AS_OBJ(v)) // ObjString*
#define AS_CSTRING(v) (AS_STRING(v)->value) // const char*
// These macros promote a primitive C value to a full Wren Value. There are
// more defined below that are specific to the Nan tagged or other
// representation.
#define BOOL_VAL(boolean) ((boolean) ? TRUE_VAL : FALSE_VAL) // boolean
#define NUM_VAL(num) (wrenNumToValue(num)) // double
#define OBJ_VAL(obj) (wrenObjectToValue((Obj*)(obj))) // Any Obj___*
// These perform type tests on a Value, returning `true` if the Value is of the
// given type.
#define IS_BOOL(value) (wrenIsBool(value)) // Bool
#define IS_CLASS(value) (wrenIsObjType(value, OBJ_CLASS)) // ObjClass
#define IS_CLOSURE(value) (wrenIsObjType(value, OBJ_CLOSURE)) // ObjClosure
#define IS_FIBER(value) (wrenIsObjType(value, OBJ_FIBER)) // ObjFiber
#define IS_FN(value) (wrenIsObjType(value, OBJ_FN)) // ObjFn
#define IS_FOREIGN(value) (wrenIsObjType(value, OBJ_FOREIGN)) // ObjForeign
#define IS_INSTANCE(value) (wrenIsObjType(value, OBJ_INSTANCE)) // ObjInstance
#define IS_LIST(value) (wrenIsObjType(value, OBJ_LIST)) // ObjList
#define IS_MAP(value) (wrenIsObjType(value, OBJ_MAP)) // ObjMap
#define IS_RANGE(value) (wrenIsObjType(value, OBJ_RANGE)) // ObjRange
#define IS_STRING(value) (wrenIsObjType(value, OBJ_STRING)) // ObjString
// Creates a new string object from [text], which should be a bare C string
// literal. This determines the length of the string automatically at compile
// time based on the size of the character array (-1 for the terminating '\0').
#define CONST_STRING(vm, text) wrenNewStringLength((vm), (text), sizeof(text) - 1)
// Identifies which specific type a heap-allocated object is.
typedef enum {
OBJ_CLASS,
OBJ_CLOSURE,
OBJ_FIBER,
OBJ_FN,
OBJ_FOREIGN,
OBJ_INSTANCE,
OBJ_LIST,
OBJ_MAP,
OBJ_MODULE,
OBJ_RANGE,
OBJ_STRING,
OBJ_UPVALUE
} ObjType;
typedef struct sObjClass ObjClass;
// Base struct for all heap-allocated objects.
typedef struct sObj Obj;
struct sObj
{
ObjType type;
bool isDark;
// The object's class.
ObjClass* classObj;
// The next object in the linked list of all currently allocated objects.
struct sObj* next;
};
#if WREN_NAN_TAGGING
typedef uint64_t Value;
#else
typedef enum
{
VAL_FALSE,
VAL_NULL,
VAL_NUM,
VAL_TRUE,
VAL_UNDEFINED,
VAL_OBJ
} ValueType;
typedef struct
{
ValueType type;
union
{
double num;
Obj* obj;
} as;
} Value;
#endif
DECLARE_BUFFER(Value, Value);
// A heap-allocated string object.
struct sObjString
{
Obj obj;
// Number of bytes in the string, not including the null terminator.
uint32_t length;
// The hash value of the string's contents.
uint32_t hash;
// Inline array of the string's bytes followed by a null terminator.
char value[FLEXIBLE_ARRAY];
};
// The dynamically allocated data structure for a variable that has been used
// by a closure. Whenever a function accesses a variable declared in an
// enclosing function, it will get to it through this.
//
// An upvalue can be either "closed" or "open". An open upvalue points directly
// to a [Value] that is still stored on the fiber's stack because the local
// variable is still in scope in the function where it's declared.
//
// When that local variable goes out of scope, the upvalue pointing to it will
// be closed. When that happens, the value gets copied off the stack into the
// upvalue itself. That way, it can have a longer lifetime than the stack
// variable.
typedef struct sObjUpvalue
{
// The object header. Note that upvalues have this because they are garbage
// collected, but they are not first class Wren objects.
Obj obj;
// Pointer to the variable this upvalue is referencing.
Value* value;
// If the upvalue is closed (i.e. the local variable it was pointing to has
// been popped off the stack) then the closed-over value will be hoisted out
// of the stack into here. [value] will then be changed to point to this.
Value closed;
// Open upvalues are stored in a linked list by the fiber. This points to the
// next upvalue in that list.
struct sObjUpvalue* next;
} ObjUpvalue;
// The type of a primitive function.
//
// Primitives are similar to foreign functions, but have more direct access to
// VM internals. It is passed the arguments in [args]. If it returns a value,
// it places it in `args[0]` and returns `true`. If it causes a runtime error
// or modifies the running fiber, it returns `false`.
typedef bool (*Primitive)(WrenVM* vm, Value* args);
// TODO: See if it's actually a perf improvement to have this in a separate
// struct instead of in ObjFn.
// Stores debugging information for a function used for things like stack
// traces.
typedef struct
{
// The name of the function. Heap allocated and owned by the FnDebug.
char* name;
// An array of line numbers. There is one element in this array for each
// bytecode in the function's bytecode array. The value of that element is
// the line in the source code that generated that instruction.
IntBuffer sourceLines;
} FnDebug;
// A loaded module and the top-level variables it defines.
//
// While this is an Obj and is managed by the GC, it never appears as a
// first-class object in Wren.
typedef struct
{
Obj obj;
// The currently defined top-level variables.
ValueBuffer variables;
// Symbol table for the names of all module variables. Indexes here directly
// correspond to entries in [variables].
SymbolTable variableNames;
// The name of the module.
ObjString* name;
} ObjModule;
// A function object. It wraps and owns the bytecode and other debug information
// for a callable chunk of code.
//
// Function objects are not passed around and invoked directly. Instead, they
// are always referenced by an [ObjClosure] which is the real first-class
// representation of a function. This isn't strictly necessary if they function
// has no upvalues, but lets the rest of the VM assume all called objects will
// be closures.
typedef struct
{
Obj obj;
ByteBuffer code;
ValueBuffer constants;
// The module where this function was defined.
ObjModule* module;
// The maximum number of stack slots this function may use.
int maxSlots;
// The number of upvalues this function closes over.
int numUpvalues;
// The number of parameters this function expects. Used to ensure that .call
// handles a mismatch between number of parameters and arguments. This will
// only be set for fns, and not ObjFns that represent methods or scripts.
int arity;
FnDebug* debug;
} ObjFn;
// An instance of a first-class function and the environment it has closed over.
// Unlike [ObjFn], this has captured the upvalues that the function accesses.
typedef struct
{
Obj obj;
// The function that this closure is an instance of.
ObjFn* fn;
// The upvalues this function has closed over.
ObjUpvalue* upvalues[FLEXIBLE_ARRAY];
} ObjClosure;
typedef struct
{
// Pointer to the current (really next-to-be-executed) instruction in the
// function's bytecode.
uint8_t* ip;
// The closure being executed.
ObjClosure* closure;
// Pointer to the first stack slot used by this call frame. This will contain
// the receiver, followed by the function's parameters, then local variables
// and temporaries.
Value* stackStart;
} CallFrame;
// Tracks how this fiber has been invoked, aside from the ways that can be
// detected from the state of other fields in the fiber.
typedef enum
{
// The fiber is being run from another fiber using a call to `try()`.
FIBER_TRY,
// The fiber was directly invoked by `runInterpreter()`. This means it's the
// initial fiber used by a call to `wrenCall()` or `wrenInterpret()`.
FIBER_ROOT,
// The fiber is invoked some other way. If [caller] is `NULL` then the fiber
// was invoked using `call()`. If [numFrames] is zero, then the fiber has
// finished running and is done. If [numFrames] is one and that frame's `ip`
// points to the first byte of code, the fiber has not been started yet.
FIBER_OTHER,
} FiberState;
typedef struct sObjFiber
{
Obj obj;
// The stack of value slots. This is used for holding local variables and
// temporaries while the fiber is executing. It is heap-allocated and grown
// as needed.
Value* stack;
// A pointer to one past the top-most value on the stack.
Value* stackTop;
// The number of allocated slots in the stack array.
int stackCapacity;
// The stack of call frames. This is a dynamic array that grows as needed but
// never shrinks.
CallFrame* frames;
// The number of frames currently in use in [frames].
int numFrames;
// The number of [frames] allocated.
int frameCapacity;
// Pointer to the first node in the linked list of open upvalues that are
// pointing to values still on the stack. The head of the list will be the
// upvalue closest to the top of the stack, and then the list works downwards.
ObjUpvalue* openUpvalues;
// The fiber that ran this one. If this fiber is yielded, control will resume
// to this one. May be `NULL`.
struct sObjFiber* caller;
// If the fiber failed because of a runtime error, this will contain the
// error object. Otherwise, it will be null.
Value error;
FiberState state;
} ObjFiber;
typedef enum
{
// A primitive method implemented in C in the VM. Unlike foreign methods,
// this can directly manipulate the fiber's stack.
METHOD_PRIMITIVE,
// A primitive that handles .call on Fn.
METHOD_FUNCTION_CALL,
// A externally-defined C method.
METHOD_FOREIGN,
// A normal user-defined method.
METHOD_BLOCK,
// No method for the given symbol.
METHOD_NONE
} MethodType;
typedef struct
{
MethodType type;
// The method function itself. The [type] determines which field of the union
// is used.
union
{
Primitive primitive;
WrenForeignMethodFn foreign;
ObjClosure* closure;
} as;
} Method;
DECLARE_BUFFER(Method, Method);
struct sObjClass
{
Obj obj;
ObjClass* superclass;
// The number of fields needed for an instance of this class, including all
// of its superclass fields.
int numFields;
// The table of methods that are defined in or inherited by this class.
// Methods are called by symbol, and the symbol directly maps to an index in
// this table. This makes method calls fast at the expense of empty cells in
// the list for methods the class doesn't support.
//
// You can think of it as a hash table that never has collisions but has a
// really low load factor. Since methods are pretty small (just a type and a
// pointer), this should be a worthwhile trade-off.
MethodBuffer methods;
// The name of the class.
ObjString* name;
// The ClassAttribute for the class, if any
Value attributes;
};
typedef struct
{
Obj obj;
uint8_t data[FLEXIBLE_ARRAY];
} ObjForeign;
typedef struct
{
Obj obj;
Value fields[FLEXIBLE_ARRAY];
} ObjInstance;
typedef struct
{
Obj obj;
// The elements in the list.
ValueBuffer elements;
} ObjList;
typedef struct
{
// The entry's key, or UNDEFINED_VAL if the entry is not in use.
Value key;
// The value associated with the key. If the key is UNDEFINED_VAL, this will
// be false to indicate an open available entry or true to indicate a
// tombstone -- an entry that was previously in use but was then deleted.
Value value;
} MapEntry;
// A hash table mapping keys to values.
//
// We use something very simple: open addressing with linear probing. The hash
// table is an array of entries. Each entry is a key-value pair. If the key is
// the special UNDEFINED_VAL, it indicates no value is currently in that slot.
// Otherwise, it's a valid key, and the value is the value associated with it.
//
// When entries are added, the array is dynamically scaled by GROW_FACTOR to
// keep the number of filled slots under MAP_LOAD_PERCENT. Likewise, if the map
// gets empty enough, it will be resized to a smaller array. When this happens,
// all existing entries are rehashed and re-added to the new array.
//
// When an entry is removed, its slot is replaced with a "tombstone". This is an
// entry whose key is UNDEFINED_VAL and whose value is TRUE_VAL. When probing
// for a key, we will continue past tombstones, because the desired key may be
// found after them if the key that was removed was part of a prior collision.
// When the array gets resized, all tombstones are discarded.
typedef struct
{
Obj obj;
// The number of entries allocated.
uint32_t capacity;
// The number of entries in the map.
uint32_t count;
// Pointer to a contiguous array of [capacity] entries.
MapEntry* entries;
} ObjMap;
typedef struct
{
Obj obj;
// The beginning of the range.
double from;
// The end of the range. May be greater or less than [from].
double to;
// True if [to] is included in the range.
bool isInclusive;
} ObjRange;
// An IEEE 754 double-precision float is a 64-bit value with bits laid out like:
//
// 1 Sign bit
// | 11 Exponent bits
// | | 52 Mantissa (i.e. fraction) bits
// | | |
// S[Exponent-][Mantissa------------------------------------------]
//
// The details of how these are used to represent numbers aren't really
// relevant here as long we don't interfere with them. The important bit is NaN.
//
// An IEEE double can represent a few magical values like NaN ("not a number"),
// Infinity, and -Infinity. A NaN is any value where all exponent bits are set:
//
// v--NaN bits
// -11111111111----------------------------------------------------
//
// Here, "-" means "doesn't matter". Any bit sequence that matches the above is
// a NaN. With all of those "-", it obvious there are a *lot* of different
// bit patterns that all mean the same thing. NaN tagging takes advantage of
// this. We'll use those available bit patterns to represent things other than
// numbers without giving up any valid numeric values.
//
// NaN values come in two flavors: "signalling" and "quiet". The former are
// intended to halt execution, while the latter just flow through arithmetic
// operations silently. We want the latter. Quiet NaNs are indicated by setting
// the highest mantissa bit:
//
// v--Highest mantissa bit
// -[NaN ]1---------------------------------------------------
//
// If all of the NaN bits are set, it's not a number. Otherwise, it is.
// That leaves all of the remaining bits as available for us to play with. We
// stuff a few different kinds of things here: special singleton values like
// "true", "false", and "null", and pointers to objects allocated on the heap.
// We'll use the sign bit to distinguish singleton values from pointers. If
// it's set, it's a pointer.
//
// v--Pointer or singleton?
// S[NaN ]1---------------------------------------------------
//
// For singleton values, we just enumerate the different values. We'll use the
// low bits of the mantissa for that, and only need a few:
//
// 3 Type bits--v
// 0[NaN ]1------------------------------------------------[T]
//
// For pointers, we are left with 51 bits of mantissa to store an address.
// That's more than enough room for a 32-bit address. Even 64-bit machines
// only actually use 48 bits for addresses, so we've got plenty. We just stuff
// the address right into the mantissa.
//
// Ta-da, double precision numbers, pointers, and a bunch of singleton values,
// all stuffed into a single 64-bit sequence. Even better, we don't have to
// do any masking or work to extract number values: they are unmodified. This
// means math on numbers is fast.
#if WREN_NAN_TAGGING
// A mask that selects the sign bit.
#define SIGN_BIT ((uint64_t)1 << 63)
// The bits that must be set to indicate a quiet NaN.
#define QNAN ((uint64_t)0x7ffc000000000000)
// If the NaN bits are set, it's not a number.
#define IS_NUM(value) (((value) & QNAN) != QNAN)
// An object pointer is a NaN with a set sign bit.
#define IS_OBJ(value) (((value) & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT))
#define IS_FALSE(value) ((value) == FALSE_VAL)
#define IS_NULL(value) ((value) == NULL_VAL)
#define IS_UNDEFINED(value) ((value) == UNDEFINED_VAL)
// Masks out the tag bits used to identify the singleton value.
#define MASK_TAG (7)
// Tag values for the different singleton values.
#define TAG_NAN (0)
#define TAG_NULL (1)
#define TAG_FALSE (2)
#define TAG_TRUE (3)
#define TAG_UNDEFINED (4)
#define TAG_UNUSED2 (5)
#define TAG_UNUSED3 (6)
#define TAG_UNUSED4 (7)
// Value -> 0 or 1.
#define AS_BOOL(value) ((value) == TRUE_VAL)
// Value -> Obj*.
#define AS_OBJ(value) ((Obj*)(uintptr_t)((value) & ~(SIGN_BIT | QNAN)))
// Singleton values.
#define NULL_VAL ((Value)(uint64_t)(QNAN | TAG_NULL))
#define FALSE_VAL ((Value)(uint64_t)(QNAN | TAG_FALSE))
#define TRUE_VAL ((Value)(uint64_t)(QNAN | TAG_TRUE))
#define UNDEFINED_VAL ((Value)(uint64_t)(QNAN | TAG_UNDEFINED))
// Gets the singleton type tag for a Value (which must be a singleton).
#define GET_TAG(value) ((int)((value) & MASK_TAG))
#else
// Value -> 0 or 1.
#define AS_BOOL(value) ((value).type == VAL_TRUE)
// Value -> Obj*.
#define AS_OBJ(v) ((v).as.obj)
// Determines if [value] is a garbage-collected object or not.
#define IS_OBJ(value) ((value).type == VAL_OBJ)
#define IS_FALSE(value) ((value).type == VAL_FALSE)
#define IS_NULL(value) ((value).type == VAL_NULL)
#define IS_NUM(value) ((value).type == VAL_NUM)
#define IS_UNDEFINED(value) ((value).type == VAL_UNDEFINED)
// Singleton values.
#define FALSE_VAL ((Value){ VAL_FALSE, { 0 } })
#define NULL_VAL ((Value){ VAL_NULL, { 0 } })
#define TRUE_VAL ((Value){ VAL_TRUE, { 0 } })
#define UNDEFINED_VAL ((Value){ VAL_UNDEFINED, { 0 } })
#endif
// Creates a new "raw" class. It has no metaclass or superclass whatsoever.
// This is only used for bootstrapping the initial Object and Class classes,
// which are a little special.
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name);
// Makes [superclass] the superclass of [subclass], and causes subclass to
// inherit its methods. This should be called before any methods are defined
// on subclass.
void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass);
// Creates a new class object as well as its associated metaclass.
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
ObjString* name);
void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method);
// Creates a new closure object that invokes [fn]. Allocates room for its
// upvalues, but assumes outside code will populate it.
ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn);
// Creates a new fiber object that will invoke [closure].
ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure);
// Adds a new [CallFrame] to [fiber] invoking [closure] whose stack starts at
// [stackStart].
static inline void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber,
ObjClosure* closure, Value* stackStart)
{
// The caller should have ensured we already have enough capacity.
ASSERT(fiber->frameCapacity > fiber->numFrames, "No memory for call frame.");
CallFrame* frame = &fiber->frames[fiber->numFrames++];
frame->stackStart = stackStart;
frame->closure = closure;
frame->ip = closure->fn->code.data;
}
// Ensures [fiber]'s stack has at least [needed] slots.
void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed);
static inline bool wrenHasError(const ObjFiber* fiber)
{
return !IS_NULL(fiber->error);
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size);
// Creates a new empty function. Before being used, it must have code,
// constants, etc. added to it.
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module, int maxSlots);
void wrenFunctionBindName(WrenVM* vm, ObjFn* fn, const char* name, int length);
// Creates a new instance of the given [classObj].
Value wrenNewInstance(WrenVM* vm, ObjClass* classObj);
// Creates a new list with [numElements] elements (which are left
// uninitialized.)
ObjList* wrenNewList(WrenVM* vm, uint32_t numElements);
// Inserts [value] in [list] at [index], shifting down the other elements.
void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index);
// Removes and returns the item at [index] from [list].
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index);
// Searches for [value] in [list], returns the index or -1 if not found.
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value);
// Creates a new empty map.
ObjMap* wrenNewMap(WrenVM* vm);
// Validates that [arg] is a valid object for use as a map key. Returns true if
// it is and returns false otherwise. Use validateKey usually, for a runtime error.
// This separation exists to aid the API in surfacing errors to the developer as well.
static inline bool wrenMapIsValidKey(Value arg);
// Looks up [key] in [map]. If found, returns the value. Otherwise, returns
// `UNDEFINED_VAL`.
Value wrenMapGet(ObjMap* map, Value key);
// Associates [key] with [value] in [map].
void wrenMapSet(WrenVM* vm, ObjMap* map, Value key, Value value);
void wrenMapClear(WrenVM* vm, ObjMap* map);
// Removes [key] from [map], if present. Returns the value for the key if found
// or `NULL_VAL` otherwise.
Value wrenMapRemoveKey(WrenVM* vm, ObjMap* map, Value key);
// Creates a new module.
ObjModule* wrenNewModule(WrenVM* vm, ObjString* name);
// Creates a new range from [from] to [to].
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive);
// Creates a new string object and copies [text] into it.
//
// [text] must be non-NULL.
Value wrenNewString(WrenVM* vm, const char* text);
// Creates a new string object of [length] and copies [text] into it.
//
// [text] may be NULL if [length] is zero.
Value wrenNewStringLength(WrenVM* vm, const char* text, size_t length);
// Creates a new string object by taking a range of characters from [source].
// The range starts at [start], contains [count] bytes, and increments by
// [step].
Value wrenNewStringFromRange(WrenVM* vm, ObjString* source, int start,
uint32_t count, int step);
// Produces a string representation of [value].
Value wrenNumToString(WrenVM* vm, double value);
// Creates a new formatted string from [format] and any additional arguments
// used in the format string.
//
// This is a very restricted flavor of formatting, intended only for internal
// use by the VM. Two formatting characters are supported, each of which reads
// the next argument as a certain type:
//
// $ - A C string.
// @ - A Wren string object.
Value wrenStringFormat(WrenVM* vm, const char* format, ...);
// Creates a new string containing the UTF-8 encoding of [value].
Value wrenStringFromCodePoint(WrenVM* vm, int value);
// Creates a new string from the integer representation of a byte
Value wrenStringFromByte(WrenVM* vm, uint8_t value);
// Creates a new string containing the code point in [string] starting at byte
// [index]. If [index] points into the middle of a UTF-8 sequence, returns an
// empty string.
Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index);
// Search for the first occurence of [needle] within [haystack] and returns its
// zero-based offset. Returns `UINT32_MAX` if [haystack] does not contain
// [needle].
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle,
uint32_t startIndex);
// Returns true if [a] and [b] represent the same string.
static inline bool wrenStringEqualsCString(const ObjString* a,
const char* b, size_t length)
{
return a->length == length && memcmp(a->value, b, length) == 0;
}
// Creates a new open upvalue pointing to [value] on the stack.
ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value);
// Mark [obj] as reachable and still in use. This should only be called
// during the sweep phase of a garbage collection.
void wrenGrayObj(WrenVM* vm, Obj* obj);
// Mark [value] as reachable and still in use. This should only be called
// during the sweep phase of a garbage collection.
void wrenGrayValue(WrenVM* vm, Value value);
// Mark the values in [buffer] as reachable and still in use. This should only
// be called during the sweep phase of a garbage collection.
void wrenGrayBuffer(WrenVM* vm, ValueBuffer* buffer);
// Processes every object in the gray stack until all reachable objects have
// been marked. After that, all objects are either white (freeable) or black
// (in use and fully traversed).
void wrenBlackenObjects(WrenVM* vm);
// Releases all memory owned by [obj], including [obj] itself.
void wrenFreeObj(WrenVM* vm, Obj* obj);
// Returns the class of [value].
//
// Unlike wrenGetClassInline in wren_vm.h, this is not inlined. Inlining helps
// performance (significantly) in some cases, but degrades it in others. The
// ones used by the implementation were chosen to give the best results in the
// benchmarks.
ObjClass* wrenGetClass(WrenVM* vm, Value value);
// Returns true if [a] and [b] are strictly the same value. This is identity
// for object values, and value equality for unboxed values.
static inline bool wrenValuesSame(Value a, Value b)
{
#if WREN_NAN_TAGGING
// Value types have unique bit representations and we compare object types
// by identity (i.e. pointer), so all we need to do is compare the bits.
return a == b;
#else
if (a.type != b.type) return false;
if (a.type == VAL_NUM) return a.as.num == b.as.num;
return a.as.obj == b.as.obj;
#endif
}
// Returns true if [a] and [b] are equivalent. Immutable values (null, bools,
// numbers, ranges, and strings) are equal if they have the same data. All
// other values are equal if they are identical objects.
bool wrenValuesEqual(Value a, Value b);
// Returns true if [value] is a bool. Do not call this directly, instead use
// [IS_BOOL].
static inline bool wrenIsBool(Value value)
{
#if WREN_NAN_TAGGING
return value == TRUE_VAL || value == FALSE_VAL;
#else
return value.type == VAL_FALSE || value.type == VAL_TRUE;
#endif
}
// Returns true if [value] is an object of type [type]. Do not call this
// directly, instead use the [IS___] macro for the type in question.
static inline bool wrenIsObjType(Value value, ObjType type)
{
return IS_OBJ(value) && AS_OBJ(value)->type == type;
}
// Converts the raw object pointer [obj] to a [Value].
static inline Value wrenObjectToValue(Obj* obj)
{
#if WREN_NAN_TAGGING
// The triple casting is necessary here to satisfy some compilers:
// 1. (uintptr_t) Convert the pointer to a number of the right size.
// 2. (uint64_t) Pad it up to 64 bits in 32-bit builds.
// 3. Or in the bits to make a tagged Nan.
// 4. Cast to a typedef'd value.
return (Value)(SIGN_BIT | QNAN | (uint64_t)(uintptr_t)(obj));
#else
Value value;
value.type = VAL_OBJ;
value.as.obj = obj;
return value;
#endif
}
// Interprets [value] as a [double].
static inline double wrenValueToNum(Value value)
{
#if WREN_NAN_TAGGING
return wrenDoubleFromBits(value);
#else
return value.as.num;
#endif
}
// Converts [num] to a [Value].
static inline Value wrenNumToValue(double num)
{
#if WREN_NAN_TAGGING
return wrenDoubleToBits(num);
#else
Value value;
value.type = VAL_NUM;
value.as.num = num;
return value;
#endif
}
static inline bool wrenMapIsValidKey(Value arg)
{
return IS_BOOL(arg)
|| IS_CLASS(arg)
|| IS_NULL(arg)
|| IS_NUM(arg)
|| IS_RANGE(arg)
|| IS_STRING(arg);
}
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,251 @@
#ifndef wren_vm_h
#define wren_vm_h
#include "wren_common.h"
#include "wren_compiler.h"
#include "wren_value.h"
#include "wren_utils.h"
// The maximum number of temporary objects that can be made visible to the GC
// at one time.
#define WREN_MAX_TEMP_ROOTS 8
typedef enum
{
#define OPCODE(name, _) CODE_##name,
#include "wren_opcodes.h"
#undef OPCODE
} Code;
// A handle to a value, basically just a linked list of extra GC roots.
//
// Note that even non-heap-allocated values can be stored here.
struct WrenHandle
{
Value value;
WrenHandle* prev;
WrenHandle* next;
};
struct WrenVM
{
ObjClass* boolClass;
ObjClass* classClass;
ObjClass* fiberClass;
ObjClass* fnClass;
ObjClass* listClass;
ObjClass* mapClass;
ObjClass* nullClass;
ObjClass* numClass;
ObjClass* objectClass;
ObjClass* rangeClass;
ObjClass* stringClass;
// The fiber that is currently running.
ObjFiber* fiber;
// The loaded modules. Each key is an ObjString (except for the main module,
// whose key is null) for the module's name and the value is the ObjModule
// for the module.
ObjMap* modules;
// The most recently imported module. More specifically, the module whose
// code has most recently finished executing.
//
// Not treated like a GC root since the module is already in [modules].
ObjModule* lastModule;
// Memory management data:
// The number of bytes that are known to be currently allocated. Includes all
// memory that was proven live after the last GC, as well as any new bytes
// that were allocated since then. Does *not* include bytes for objects that
// were freed since the last GC.
size_t bytesAllocated;
// The number of total allocated bytes that will trigger the next GC.
size_t nextGC;
// The first object in the linked list of all currently allocated objects.
Obj* first;
// The "gray" set for the garbage collector. This is the stack of unprocessed
// objects while a garbage collection pass is in process.
Obj** gray;
int grayCount;
int grayCapacity;
// The list of temporary roots. This is for temporary or new objects that are
// not otherwise reachable but should not be collected.
//
// They are organized as a stack of pointers stored in this array. This
// implies that temporary roots need to have stack semantics: only the most
// recently pushed object can be released.
Obj* tempRoots[WREN_MAX_TEMP_ROOTS];
int numTempRoots;
// Pointer to the first node in the linked list of active handles or NULL if
// there are none.
WrenHandle* handles;
// Pointer to the bottom of the range of stack slots available for use from
// the C API. During a foreign method, this will be in the stack of the fiber
// that is executing a method.
//
// If not in a foreign method, this is initially NULL. If the user requests
// slots by calling wrenEnsureSlots(), a stack is created and this is
// initialized.
Value* apiStack;
WrenConfiguration config;
// Compiler and debugger data:
// The compiler that is currently compiling code. This is used so that heap
// allocated objects used by the compiler can be found if a GC is kicked off
// in the middle of a compile.
Compiler* compiler;
// There is a single global symbol table for all method names on all classes.
// Method calls are dispatched directly by index in this table.
SymbolTable methodNames;
};
// A generic allocation function that handles all explicit memory management.
// It's used like so:
//
// - To allocate new memory, [memory] is NULL and [oldSize] is zero. It should
// return the allocated memory or NULL on failure.
//
// - To attempt to grow an existing allocation, [memory] is the memory,
// [oldSize] is its previous size, and [newSize] is the desired size.
// It should return [memory] if it was able to grow it in place, or a new
// pointer if it had to move it.
//
// - To shrink memory, [memory], [oldSize], and [newSize] are the same as above
// but it will always return [memory].
//
// - To free memory, [memory] will be the memory to free and [newSize] and
// [oldSize] will be zero. It should return NULL.
void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize);
// Invoke the finalizer for the foreign object referenced by [foreign].
void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign);
// Creates a new [WrenHandle] for [value].
WrenHandle* wrenMakeHandle(WrenVM* vm, Value value);
// Compile [source] in the context of [module] and wrap in a fiber that can
// execute it.
//
// Returns NULL if a compile error occurred.
ObjClosure* wrenCompileSource(WrenVM* vm, const char* module,
const char* source, bool isExpression,
bool printErrors);
// Looks up a variable from a previously-loaded module.
//
// Aborts the current fiber if the module or variable could not be found.
Value wrenGetModuleVariable(WrenVM* vm, Value moduleName, Value variableName);
// Returns the value of the module-level variable named [name] in the main
// module.
Value wrenFindVariable(WrenVM* vm, ObjModule* module, const char* name);
// Adds a new implicitly declared top-level variable named [name] to [module]
// based on a use site occurring on [line].
//
// Does not check to see if a variable with that name is already declared or
// defined. Returns the symbol for the new variable or -2 if there are too many
// variables defined.
int wrenDeclareVariable(WrenVM* vm, ObjModule* module, const char* name,
size_t length, int line);
// Adds a new top-level variable named [name] to [module], and optionally
// populates line with the line of the implicit first use (line can be NULL).
//
// Returns the symbol for the new variable, -1 if a variable with the given name
// is already defined, or -2 if there are too many variables defined.
// Returns -3 if this is a top-level lowercase variable (localname) that was
// used before being defined.
int wrenDefineVariable(WrenVM* vm, ObjModule* module, const char* name,
size_t length, Value value, int* line);
// Pushes [closure] onto [fiber]'s callstack to invoke it. Expects [numArgs]
// arguments (including the receiver) to be on the top of the stack already.
static inline void wrenCallFunction(WrenVM* vm, ObjFiber* fiber,
ObjClosure* closure, int numArgs)
{
// Grow the call frame array if needed.
if (fiber->numFrames + 1 > fiber->frameCapacity)
{
int max = fiber->frameCapacity * 2;
fiber->frames = (CallFrame*)wrenReallocate(vm, fiber->frames,
sizeof(CallFrame) * fiber->frameCapacity, sizeof(CallFrame) * max);
fiber->frameCapacity = max;
}
// Grow the stack if needed.
int stackSize = (int)(fiber->stackTop - fiber->stack);
int needed = stackSize + closure->fn->maxSlots;
wrenEnsureStack(vm, fiber, needed);
wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs);
}
// Marks [obj] as a GC root so that it doesn't get collected.
void wrenPushRoot(WrenVM* vm, Obj* obj);
// Removes the most recently pushed temporary root.
void wrenPopRoot(WrenVM* vm);
// Returns the class of [value].
//
// Defined here instead of in wren_value.h because it's critical that this be
// inlined. That means it must be defined in the header, but the wren_value.h
// header doesn't have a full definitely of WrenVM yet.
static inline ObjClass* wrenGetClassInline(WrenVM* vm, Value value)
{
if (IS_NUM(value)) return vm->numClass;
if (IS_OBJ(value)) return AS_OBJ(value)->classObj;
#if WREN_NAN_TAGGING
switch (GET_TAG(value))
{
case TAG_FALSE: return vm->boolClass; break;
case TAG_NAN: return vm->numClass; break;
case TAG_NULL: return vm->nullClass; break;
case TAG_TRUE: return vm->boolClass; break;
case TAG_UNDEFINED: UNREACHABLE();
}
#else
switch (value.type)
{
case VAL_FALSE: return vm->boolClass;
case VAL_NULL: return vm->nullClass;
case VAL_NUM: return vm->numClass;
case VAL_TRUE: return vm->boolClass;
case VAL_OBJ: return AS_OBJ(value)->classObj;
case VAL_UNDEFINED: UNREACHABLE();
}
#endif
UNREACHABLE();
return NULL;
}
// Returns `true` if [name] is a local variable name (starts with a lowercase
// letter).
static inline bool wrenIsLocalName(const char* name)
{
return name[0] >= 'a' && name[0] <= 'z';
}
static inline bool wrenIsFalsyValue(Value value)
{
return IS_FALSE(value) || IS_NULL(value);
}
#endif

@ -29,7 +29,6 @@
#include <gui/gui.h>
// #include <gui/logGui.h>
// #include <gui/luaConsole.h>
#include <scripting/script_manager.h>
#include <scripting/coreAPI.h>
#include <iostream>
@ -84,8 +83,7 @@ namespace lunarium
mpInstance->mpRunMode = nullptr;
// Shutdown subsystems
CoreAPI::FreeInstance();
ScriptManager::FreeInstance();
//CoreAPI::FreeInstance();
//LuaConsole::FreeInstance();
//LogGui::FreeInstance();
@ -221,21 +219,21 @@ namespace lunarium
}
// SCRIPTING
ScriptManager& scriptMan = ScriptManager::GetInstance();
result = scriptMan.Initialize();
if (Failed(result))
{
Logger::Warn(LogCategory::CORE,
"Could not initialized the LUA script manager: %s", result.Description);
}
CoreAPI& capi = CoreAPI::GetInstance();
result = capi.Initialize(scriptMan);
if (Failed(result))
{
Logger::Warn(LogCategory::CORE,
"Could not initialized the LUA Core API: %s", result.Description);
}
// ScriptManager& scriptMan = ScriptManager::GetInstance();
// result = scriptMan.Initialize();
// if (Failed(result))
// {
// Logger::Warn(LogCategory::CORE,
// "Could not initialized the LUA script manager: %s", result.Description);
// }
// CoreAPI& capi = CoreAPI::GetInstance();
// result = capi.Initialize(scriptMan);
// if (Failed(result))
// {
// Logger::Warn(LogCategory::CORE,
// "Could not initialized the LUA Core API: %s", result.Description);
// }
// RUN MODE
const char* types[] = { "game", "editor", "test" };
@ -325,12 +323,12 @@ namespace lunarium
std::string command;
if (con && con->GetNewCommand(command))
{
// Logger::Debug(LogCategory::SCRIPTING, "New LUA command: %s", command.c_str());
OpRes result = ScriptManager::RunScript(command.c_str());
if (Failed(result))
{
Logger::Error(LogCategory::SCRIPTING, result.Description.c_str());
}
// Logger::Debug(LogCategory::SCRIPT, "New LUA command: %s", command.c_str());
// OpRes result = ScriptManager::RunScript(command.c_str());
// if (Failed(result))
// {
// Logger::Error(LogCategory::SCRIPT, result.Description.c_str());
// }
}
}

@ -15,7 +15,7 @@
namespace lunarium
{
CoreConsole::CoreConsole()
: Console("Core Console", ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar
: Console("Core Console", PanelDockZone::DDZ_NONE, false, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoCollapse), mDockIsInit(false)
{

@ -43,12 +43,12 @@ namespace lunarium
////////////////////////////////////////////////////////////
// CONSOLE
////////////////////////////////////////////////////////////
Console::Console(const char* name, int window_flags)
: Panel(name, PanelDockZone::DDZ_NONE, false, window_flags),
Console::Console(const char* name, PanelDockZone dock_zone, bool isOpen, int window_flags)
: Panel(name, dock_zone, isOpen, window_flags),
mbNewCommand(false), mRecalledCommand(-1), mIsFocused(false), mListener(nullptr),
mbOglDebug(false), mbInfoVerbose(false), mpListener(nullptr)
mbOglDebug(false), mbInfoVerbose(false), mpListener(nullptr), mInputWindowHeight(20)
{
memset(mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(mBuffer, 0, CONSOLE_BUFFER_SIZE);
mpListener = Logger::GetInstance()->AddListener(new GuiListener(this));
}
@ -64,6 +64,22 @@ namespace lunarium
{
return mIsFocused;
}
void Console::SetOGLDebugFlag(bool flag)
{
mbOglDebug = flag;
}
void Console::SetVerboseFlag(bool flag)
{
mbInfoVerbose = flag;
}
f32 Console::GetInputWindowHeight() const
{
return mInputWindowHeight;
}
void Console::Update(float dt)
{
@ -75,27 +91,13 @@ namespace lunarium
void Console::DoFrame()
{
float history_height = ImGui::GetWindowSize().y - 45;
ImGui::BeginChild("history", ImVec2(0, history_height), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoMove);
//float history_height = ImGui::GetWindowSize().y - 45;
ImGui::BeginChild("history", ImVec2(ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - (GetInputWindowHeight() + 5)));
mIsFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
// for (int i = 0; i < mCommandHistory.size(); i++)
// {
// const char* msg = mCommandHistory[i].c_str();
// int len = strlen(msg);
// if (i == mRecalledCommand)
// {
// ImGui::TextColored(ImVec4(0.2f, 0.9f, 0.5f, 1.0f), msg);
// }
// else
// {
// ImGui::TextUnformatted(msg);
// }
// }
for (int i = 0; i < mMsgHistory.size(); i++)
for (int i = 0; i < mMsgHistory.size(); i++)
{
if ((mMsgHistory[i].find("[GRAPHICS_INTERNAL_DEBUG]") != std::string::npos && !mbOglDebug)
if ((mMsgHistory[i].find("[GRAPHICS INTERNAL DEBUG]") != std::string::npos && !mbOglDebug)
|| (mMsgHistory[i].find("[TRACE]") != std::string::npos && !mbInfoVerbose))
{
continue;
@ -104,7 +106,7 @@ namespace lunarium
ImVec4 color(1.0f, 1.0f, 1.0f, 1.0f);
if (mMsgHistory[i].find("[LUA]") != std::string::npos)
if (mMsgHistory[i].find("[SCRIPT]") != std::string::npos)
{
color = ImVec4(0.25f, 0.5f, 0.95f, 1.0f);
}
@ -132,13 +134,13 @@ namespace lunarium
ImGui::EndChild();
ImGui::Separator();
ImGui::BeginChild("input", ImVec2(0, 20), false, ImGuiWindowFlags_NoScrollbar);
ImGui::BeginChild("input", ImVec2(0, mInputWindowHeight), false, ImGuiWindowFlags_NoScrollbar);
mIsFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
if (ImGui::InputText("command", mBuffer, LUA_CON_BUFFER_SIZE, ImGuiInputTextFlags_EnterReturnsTrue
if (ImGui::InputText("command", mBuffer, CONSOLE_BUFFER_SIZE, ImGuiInputTextFlags_EnterReturnsTrue
| ImGuiInputTextFlags_CallbackHistory, Console::MyCallback, (void*)this))
{
mCommandHistory.push_back(mBuffer);
memset(mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(mBuffer, 0, CONSOLE_BUFFER_SIZE);
mbNewCommand = true;
// Doesn't keep the focus on the input text field
@ -202,7 +204,7 @@ namespace lunarium
if (changeCommand)
{
memset(pConsole->mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(pConsole->mBuffer, 0, CONSOLE_BUFFER_SIZE);
strcpy(pConsole->mBuffer, pConsole->mCommandHistory[pConsole->mRecalledCommand].c_str());
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, pConsole->mBuffer);

@ -10,6 +10,7 @@
#ifndef CONSOLE_H_
#define CONSOLE_H_
#include <core/common_defs.h>
#include <gui/panel.h>
#include <utils/logger.h>
#include <vector>
@ -28,33 +29,39 @@ namespace lunarium
Console* mpConsole;
};
const int LUA_CON_BUFFER_SIZE = 64;
const int CONSOLE_BUFFER_SIZE = 64;
class Console : public Panel
{
public:
Console(const char* name, int window_flags);
Console(const char* name, PanelDockZone dock_zone, bool isOpen = false, int window_flags = 0);
virtual ~Console();
virtual void Update(float dt);
virtual void DoFrame();
bool IsFocused() const;
// LUA COMMAND STUFF
f32 GetInputWindowHeight() const;
// INPUT COMMAND STUFF
const std::vector<std::string>* GetCommandHistory() const;
std::string GetLastCommand();
bool GetNewCommand(std::string& command); // returns true if there is a new and false if not
bool GetNewCommand(std::string& command); // returns true if there is a new command and false if not
static int MyCallback(ImGuiInputTextCallbackData* data);
void SetOGLDebugFlag(bool flag);
void SetVerboseFlag(bool flag);
private:
// LUA COMMAND STUFF
// INPUT COMMAND STUFF
LogListener* mpListener;
char mBuffer[LUA_CON_BUFFER_SIZE];
char mBuffer[CONSOLE_BUFFER_SIZE];
std::vector<std::string> mCommandHistory;
bool mbNewCommand;
int mRecalledCommand;
// float mAlpha;
bool mIsFocused;
const f32 mInputWindowHeight;
// LOG STUFF
std::vector<std::string> mMsgHistory;

@ -198,4 +198,13 @@ namespace lunarium
return ImGui::Button(label);
}
bool ImGuiExt::CheckBoxLeft(const char* label, bool* v)
{
ImGui::Text(label);
ImGui::SameLine();
std::string lb = "##";
lb.append(label);
return ImGui::Checkbox(lb.c_str(), v);
}
}

@ -26,6 +26,7 @@ namespace lunarium
static bool FloatControl(const std::string& label, float& values, float resetValue = 0.0f, float columnWidth = 100.0f);
static void TextCentered(const std::string text);
static bool ButtonCentered(const char* label, float alignment = 0.5f);
static bool CheckBoxLeft(const char* label, bool* v);
};
}

@ -21,7 +21,7 @@ namespace lunarium
LuaConsole::LuaConsole()
: mbShow(false), mbStickToWindow(true), mbNewCommand(false), mRecalledCommand(-1)
{
memset(mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(mBuffer, 0, CONSOLE_BUFFER_SIZE);
}
LuaConsole& LuaConsole::GetInstance()
@ -71,7 +71,7 @@ namespace lunarium
if (changeCommand)
{
memset(mpInstance->mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(mpInstance->mBuffer, 0, CONSOLE_BUFFER_SIZE);
strcpy(mpInstance->mBuffer, mpInstance->mCommandHistory[mpInstance->mRecalledCommand].c_str());
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, mpInstance->mBuffer);
@ -145,11 +145,11 @@ namespace lunarium
ImGui::EndChild();
ImGui::Separator();
ImGui::BeginChild("console", ImVec2(0, 40), true, ImGuiWindowFlags_NoScrollbar);
if (ImGui::InputText("command", mBuffer, LUA_CON_BUFFER_SIZE, ImGuiInputTextFlags_EnterReturnsTrue
if (ImGui::InputText("command", mBuffer, CONSOLE_BUFFER_SIZE, ImGuiInputTextFlags_EnterReturnsTrue
| ImGuiInputTextFlags_CallbackHistory, LuaConsole::MyCallback))
{
mCommandHistory.push_back(mBuffer);
memset(mBuffer, 0, LUA_CON_BUFFER_SIZE);
memset(mBuffer, 0, CONSOLE_BUFFER_SIZE);
mbNewCommand = true;
// Doesn't keep the focus on the input text field

@ -17,7 +17,7 @@ struct ImGuiInputTextCallbackData;
namespace lunarium
{
const int LUA_CON_BUFFER_SIZE = 64;
const int CONSOLE_BUFFER_SIZE = 64;
class LuaConsole
{
public:
@ -42,7 +42,7 @@ namespace lunarium
static LuaConsole* mpInstance;
bool mbShow;
bool mbStickToWindow;
char mBuffer[LUA_CON_BUFFER_SIZE];
char mBuffer[CONSOLE_BUFFER_SIZE];
std::vector<std::string> mCommandHistory;
bool mbNewCommand;
int mRecalledCommand;

@ -7,12 +7,14 @@ set(EDITOR_SRC
"component_guis.cpp"
"panels/about.cpp"
"panels/asset_browser.cpp"
"panels/editor_console.cpp"
"panels/world_tree.cpp"
"panels/world_view.cpp"
"panels/properties_view.cpp"
"contents/content_manager.cpp"
"contents/editor_asset.cpp"
"contents/world.cpp"
"contents/script.cpp"
"contents/tile_map.cpp"
"contents/tile_set.cpp"
"tools/map_editor/map_editor.cpp"
@ -36,5 +38,6 @@ target_include_directories(editor
PUBLIC ../../../external/glad/include
PUBLIC ../../../external/glfw/include
PUBLIC ../../../external/box2d/include
PUBLIC ../../../external/wren/include
PUBLIC ../../../external/
)

@ -209,6 +209,7 @@ namespace lunarium { namespace editor
ofs << std::setw(4) << oj;
ofs.close();
Logger::Info(Editor::LogCat, "content_meta.json updated successfully");
return OpRes::OK();
}

@ -0,0 +1,14 @@
/******************************************************************************
* File - script.h
* Author - Joey Pollack
* Date - 2022/11/02 (y/m/d)
* Mod Date - 2022/11/02 (y/m/d)
* Description - Editor asset for script files
******************************************************************************/
#include "script.h"
namespace lunarium { namespace editor
{
}}

@ -0,0 +1,22 @@
/******************************************************************************
* File - script.h
* Author - Joey Pollack
* Date - 2022/11/02 (y/m/d)
* Mod Date - 2022/11/02 (y/m/d)
* Description - Editor asset for script files
******************************************************************************/
#ifndef LUNARIUM_SCRIPT_H_
#define LUNARIUM_SCRIPT_H_
#include "editor_asset.h"
namespace lunarium { namespace editor
{
class Script : public EditorAsset
{
};
}}
#endif // LUNARIUM_SCRIPT_H_

@ -24,6 +24,7 @@
#include "panels/world_view.h"
#include "panels/properties_view.h"
#include "panels/asset_browser.h"
#include "panels/editor_console.h"
// Tools
#include "tools/map_editor/map_editor.h"
@ -77,6 +78,7 @@ namespace editor
mPanelManager.AddPanel(new WorldTree(this), mPanels.WorldTree).LogIfFailed(LogCat);
mPanelManager.AddPanel(new WorldView(this), mPanels.WorldView).LogIfFailed(LogCat);
mPanelManager.AddPanel(new PropertiesView(this), mPanels.PropertiesView).LogIfFailed(LogCat);
mPanelManager.AddPanel(new EditorConsole(), mPanels.Console).LogIfFailed(LogCat);
return OpRes::OK();
}

@ -81,10 +81,11 @@ namespace lunarium { namespace editor
PanelManager mPanelManager;
struct
{
uint32_t AssetBrowser;
uint32_t WorldTree;
uint32_t WorldView;
uint32_t PropertiesView;
u32 AssetBrowser;
u32 WorldTree;
u32 WorldView;
u32 PropertiesView;
u32 Console;
} mPanels;

@ -0,0 +1,63 @@
/******************************************************************************
* File - editor_console.h
* Author - Joey Pollack
* Date - 2022/10/26 (y/m/d)
* Mod Date - 2022/10/26 (y/m/d)
* Description - The console log for the game editor
******************************************************************************/
#include "editor_console.h"
#include <core/core.h>
#include <gui/gui.h>
#include <gui/imgui_ext.h>
namespace lunarium { namespace editor
{
EditorConsole::EditorConsole()
: Console("Console", PanelDockZone::DDZ_BOTTOM, true, (ImGuiWindowFlags)ImGuiWindowFlags_NoCollapse),
mGraphicsDebug(false), mInfoVerbose(false)
{
}
EditorConsole::~EditorConsole()
{
}
void EditorConsole::DoFrame()
{
ImVec2 window_size = ImGui::GetWindowSize();
float child_height = ImGui::GetFrameHeight() * 1.75;
ImGui::BeginChild("World View Toolbar", ImVec2(ImGui::GetContentRegionAvailWidth(), child_height), true);
DoToolBar();
ImGui::EndChild();
ImGui::PushFont(Core::GUI().GetFont(GuiFont::FONT_ROBO_SMALL));
//ImVec2 region = ImGui::GetContentRegionAvail();
// ImGui::SetNextWindowSize(ImVec2(ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - 750), ImGuiCond_Always);
Console::DoFrame();
ImGui::PopFont();
}
void EditorConsole::PreBegin()
{
}
void EditorConsole::DoToolBar()
{
if (ImGuiExt::CheckBoxLeft("Graphics Debug", &mGraphicsDebug))
{
SetOGLDebugFlag(mGraphicsDebug);
}
ImGui::SameLine();
if (ImGuiExt::CheckBoxLeft("Info Verbose", &mInfoVerbose))
{
SetVerboseFlag(mInfoVerbose);
}
}
}}

@ -0,0 +1,33 @@
/******************************************************************************
* File - editor_console.h
* Author - Joey Pollack
* Date - 2022/10/26 (y/m/d)
* Mod Date - 2022/10/26 (y/m/d)
* Description - The console log for the game editor
******************************************************************************/
#ifndef LUNARIUM_EDITOR_CONSOLE_H_
#define LUNARIUM_EDITOR_CONSOLE_H_
#include <gui/console.h>
namespace lunarium { namespace editor
{
class EditorConsole : public Console
{
public:
EditorConsole();
virtual ~EditorConsole();
void DoFrame() override;
void PreBegin() override;
private:
void DoToolBar();
bool mGraphicsDebug;
bool mInfoVerbose;
};
}}
#endif // LUNARIUM_EDITOR_CONSOLE_H_

@ -9,7 +9,7 @@
******************************************************************************/
#include "coreAPI.h"
#include "script_manager.h"
#include "wren_state.h"
#include <core/core.h>
#include <utils/logger.h>
@ -33,9 +33,9 @@ namespace lunarium
mpInstance = nullptr;
}
OpRes CoreAPI::Initialize(ScriptManager& sman)
OpRes CoreAPI::Initialize(WrenState& sman)
{
mCat = sman.mCat;
mCat = sman.GetLogCat();
// Register methods
// sman.mState["SetWindowSize"] = &CoreAPI::SetWindowSize;

@ -11,12 +11,13 @@
#ifndef CORE_API_H_
#define CORE_API_H_
#include <core/common_defs.h>
#include <utils/op_res.h>
namespace lunarium
{
class ScriptManager;
class WrenState;
class CoreAPI
{
@ -24,11 +25,11 @@ namespace lunarium
static CoreAPI& GetInstance();
static void FreeInstance();
OpRes Initialize(ScriptManager& sman);
OpRes Initialize(WrenState& state);
private:
static CoreAPI* mpInstance;
uint32_t mCat;
u32 mCat;
public: // API

@ -1,54 +0,0 @@
/******************************************************************************
* File - script_manager.cpp
* Author - Joey Pollack
* Date - 2021/08/30 (y/m/d)
* Mod Date - 2021/09/02 (y/m/d)
* Description - Manage the main LUA state, Run given scripts and check for
* errors. Report errors in the debug log. Also keep a
* history of errors that can be queried.
******************************************************************************/
#include "script_manager.h"
#include <utils/logger.h>
#include <sstream>
namespace lunarium
{
ScriptManager* ScriptManager::mpInstance = nullptr;
ScriptManager& ScriptManager::GetInstance()
{
if (mpInstance == nullptr)
{
mpInstance = new ScriptManager;
}
return *mpInstance;
}
void ScriptManager::FreeInstance()
{
delete mpInstance;
mpInstance = nullptr;
}
OpRes ScriptManager::Initialize()
{
mCat = Logger::RegisterCategory("SCRIPT");
return OpRes::OK();
}
OpRes ScriptManager::RunScript(const char* code)
{
if (!mpInstance)
{
return OpRes::Fail("ScriptManager::RunScript failed because the instance is null!");
}
// TODO: Use the state to run the script and check for errors
return OpRes::OK();
}
}

@ -1,35 +0,0 @@
/******************************************************************************
* File - script_manager.h
* Author - Joey Pollack
* Date - 2021/08/30 (y/m/d)
* Mod Date - 2021/09/02 (y/m/d)
* Description - Manage the main wren state, Run given scripts and check for
* errors. Report errors in the debug log. Also keep a
* history of errors that can be queried.
******************************************************************************/
#ifndef SCRIPT_MANAGER_H_
#define SCRIPT_MANAGER_H_
#include <utils/op_res.h>
namespace lunarium
{
class ScriptManager
{
public:
static ScriptManager& GetInstance();
static void FreeInstance();
OpRes Initialize();
static OpRes RunScript(const char* code);
private:
static ScriptManager* mpInstance;
uint32_t mCat;
friend class CoreAPI;
};
}
#endif // SCRIPT_MANAGER_H_

@ -0,0 +1,49 @@
/******************************************************************************
* File - wren_script.cpp
* Author - Joey Pollack
* Date - 2022/10/25 (y/m/d)
* Mod Date - 2022/10/25 (y/m/d)
* Description - Wraps a string that contains a wren script.
******************************************************************************/
#include "wren_script.h"
namespace lunarium
{
WrenScript::WrenScript(std::string module_name, std::string code)
: mModuleName(module_name), mCode(code)
{
}
void WrenScript::SetModuleName(std::string name)
{
mModuleName = name;
}
void WrenScript::SetScriptCode(std::string code)
{
mCode = code;
}
void WrenScript::AppendScriptCode(std::string code)
{
mCode += code;
}
void WrenScript::ClearScript()
{
mCode = "";
}
std::string WrenScript::GetModuleName() const
{
return mModuleName;
}
std::string WrenScript::GetScriptCode() const
{
return mCode;
}
}

@ -0,0 +1,34 @@
/******************************************************************************
* File - wren_script.h
* Author - Joey Pollack
* Date - 2022/10/25 (y/m/d)
* Mod Date - 2022/10/25 (y/m/d)
* Description - Wraps a string that contains a wren script.
******************************************************************************/
#ifndef LUNARIUM_WREN_SCRIPT_H_
#define LUNARIUM_WREN_SCRIPT_H_
#include <string>
namespace lunarium
{
class WrenScript
{
public:
WrenScript(std::string module_name = "", std::string code = "");
void SetModuleName(std::string name);
void SetScriptCode(std::string code);
void AppendScriptCode(std::string code);
void ClearScript();
std::string GetModuleName() const;
std::string GetScriptCode() const;
private:
std::string mModuleName;
std::string mCode;
};
}
#endif // LUNARIUM_WREN_SCRIPT_H_

@ -0,0 +1,88 @@
/******************************************************************************
* File - wren_state.cpp
* Author - Joey Pollack
* Date - 2022/10/25 (y/m/d)
* Mod Date - 2022/10/25 (y/m/d)
* Description - Manages the wren vm, runs scripts. This is the main
* interface for wren.
******************************************************************************/
#include "wren_state.h"
#include "wren_script.h"
#include <utils/logger.h>
namespace lunarium
{
WrenState::~WrenState()
{
Shutdown();
}
void WrenState::Shutdown()
{
wrenFreeVM(mpVM);
mpVM = nullptr;
}
u32 WrenState::mLogCat = Logger::RegisterCategory("SCRIPT");
OpRes WrenState::Initialize()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = WrenState::WriteFN;
config.errorFn = WrenState::ErrorFN;
mpVM = wrenNewVM(&config);
return OpRes::OK();
}
u32 WrenState::GetLogCat() const
{
return mLogCat;
}
void WrenState::RunScript(WrenScript* script)
{
if (!mpVM)
{
Logger::Error(mLogCat, "Could not run script - the VM is nullptr");
return;
}
WrenInterpretResult result = wrenInterpret(mpVM, script->GetModuleName().c_str(), script->GetScriptCode().c_str());
switch (result)
{
case WREN_RESULT_COMPILE_ERROR: Logger::Trace(mLogCat, "Script compile error in module: %s", script->GetModuleName().c_str()); break;
case WREN_RESULT_RUNTIME_ERROR: Logger::Trace(mLogCat, "Script runtime error in module: %s", script->GetModuleName().c_str()); break;
case WREN_RESULT_SUCCESS: Logger::Trace(mLogCat, "Script %s run successfully", script->GetModuleName().c_str()); break;
}
}
void WrenState::WriteFN(WrenVM* vm, const char* text)
{
Logger::Info(mLogCat, text);
}
void WrenState::ErrorFN(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message)
{
switch (type)
{
case WREN_ERROR_COMPILE:
Logger::Error(mLogCat, "Script compile error: [%s line %d] %s\n", module, line, message);
break;
case WREN_ERROR_STACK_TRACE:
Logger::Error(mLogCat, "[%s line %d] in %s\n", module, line, message);
break;
case WREN_ERROR_RUNTIME:
Logger::Error(mLogCat, "Script Runtime Error: %s\n", message);
break;
}
}
}

@ -0,0 +1,46 @@
/******************************************************************************
* File - wren_state.h
* Author - Joey Pollack
* Date - 2022/10/25 (y/m/d)
* Mod Date - 2022/10/25 (y/m/d)
* Description - Manages the wren vm, runs scripts. This is the main
* interface for wren.
******************************************************************************/
#ifndef LUNARIUM_WREN_STATE_H_
#define LUNARIUM_WREN_STATE_H_
#include <core/common_defs.h>
#include <utils/op_res.h>
#include <wren.hpp>
#include <string>
namespace lunarium
{
class WrenScript;
class WrenState
{
public:
~WrenState();
OpRes Initialize();
void Shutdown();
u32 GetLogCat() const;
void RunScript(WrenScript* script);
// CALL BACKS
static void WriteFN(WrenVM* vm, const char* text);
static void ErrorFN(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message);
private:
static u32 mLogCat;
WrenVM* mpVM;
};
}
#endif // LUNARIUM_WREN_STATE_H_

@ -32,6 +32,12 @@ namespace lunarium
}
void World::InitScriptState()
{
}
void World::OnLoad()
{
// TODO: Call OnLoad in the world script and on each region script

@ -15,6 +15,7 @@
#include <core/common_defs.h>
#include <core/types.h>
#include <assets/serializing/json_serializable.h>
#include <scripting/wren_state.h>
#include <utils/op_res.h>
#include <entt/entt.hpp>
#include "components.h"
@ -103,6 +104,7 @@ namespace lunarium
private:
LUUID mUUID;
std::string mName;
WrenState mScriptState;
// State critical Data
entt::registry mECSRegistry;
@ -123,6 +125,7 @@ namespace lunarium
// TEST STUFF
private: // HELPERS
void InitScriptState();
void RenderEditor(lunarium::Renderer2D* pGraphics) const;
glm::mat4 GetParentTransform(LUUID parent);
void DrawHeirarchy(lunarium::Renderer2D* g, entt::entity& entity, TransformComponent& my_trans, BlockOutComponent& bo_comp, glm::mat4 current_transform);

@ -6,19 +6,19 @@ DockId=0x00000003,0
[Window][World Tree]
Pos=0,22
Size=204,522
Size=220,522
Collapsed=0
DockId=0x00000007,0
[Window][World View]
Pos=206,22
Size=816,522
Pos=222,22
Size=783,522
Collapsed=0
DockId=0x00000008,0
[Window][Properties]
Pos=1024,22
Size=256,522
Pos=1007,22
Size=273,522
Collapsed=0
DockId=0x00000006,0
@ -28,17 +28,65 @@ Size=1280,698
Collapsed=0
[Window][Debug##Default]
Pos=60,60
Size=400,400
Pos=776,280
Size=218,304
Collapsed=0
[Window][Map Canvas]
ViewportPos=424,206
ViewportId=0xDAA48CA2
Pos=8,52
Size=965,696
Collapsed=0
DockId=0x0000000E,0
ClassId=0x00003019
[Window][Tile Set View]
ViewportPos=424,206
ViewportId=0xDAA48CA2
Pos=975,52
Size=437,696
Collapsed=0
DockId=0x0000000C,0
ClassId=0x00003019
[Window][Map Editor]
ViewportPos=424,206
ViewportId=0xDAA48CA2
Size=1420,756
Collapsed=0
[Window][About]
Pos=524,246
Size=200,110
Collapsed=0
[Window][RENDER INFO]
ViewportPos=1478,48
ViewportId=0xC2ADD2B6
Size=400,800
Collapsed=0
[Window][TileSet View]
ViewportPos=382,140
ViewportId=0x9EC003AC
Size=1054,820
Collapsed=0
[Docking][Data]
DockSpace ID=0x2F8DD699 Pos=240,154 Size=1280,698 Split=Y
DockSpace ID=0x27CF68A1 Pos=432,258 Size=1404,696 Split=Y
DockNode ID=0x00000009 Parent=0x27CF68A1 SizeRef=1280,538 Split=X
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=965,538 Split=X
DockNode ID=0x0000000D Parent=0x0000000B SizeRef=204,538
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=816,538 CentralNode=1 Selected=0xB3CA1100
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=437,538 Selected=0xB093288C
DockNode ID=0x0000000A Parent=0x27CF68A1 SizeRef=1280,180
DockSpace ID=0x2F8DD699 Window=0xBEB76114 Pos=300,116 Size=1280,698 Split=Y
DockNode ID=0x00000001 Parent=0x2F8DD699 SizeRef=1280,538 Split=X
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1022,538 Split=X
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=204,538 Selected=0xFD1747F8
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=816,538 CentralNode=1 Selected=0xB4D4B2AA
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=256,538 Selected=0xC89E3217
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1071,538 Split=X
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=220,538 Selected=0xFD1747F8
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=849,538 CentralNode=1 Selected=0xB4D4B2AA
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=291,538 Selected=0xC89E3217
DockNode ID=0x00000002 Parent=0x2F8DD699 SizeRef=1280,180 Split=X
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=255,180 Selected=0xB04EBF39
DockNode ID=0x00000004 Parent=0x00000002 SizeRef=1023,180

Loading…
Cancel
Save