Adds foundation for Wren to replace LUA
parent
95d9630b6b
commit
50f338c68a
@ -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
|
||||
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
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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_
|
||||
Loading…
Reference in New Issue