diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a89501..4d9c0cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: continue-on-error: ${{ matrix.lua == '5.1' }} strategy: matrix: - os: [ windows-latest, ubuntu-latest ] + os: [ windows-latest, ubuntu-latest, macos-latest ] configuration: [ RelWithDebInfo, MinSizeRel ] lua: [ "5.1", "5.2", "5.3", "5.4" ] diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d89ba5b..c978da9 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ windows-latest, ubuntu-latest ] + os: [ windows-latest, ubuntu-latest, macos-latest ] lua: [ "5.1", "5.2", "5.3", "5.4" ] steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index c8902f4..15951a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.18 FATAL_ERROR) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") project(Fabulist VERSION 0.1.0 @@ -40,8 +44,8 @@ if(FABULIST_COMPILER) endif() if(FABULIST_RUNTIME) - message(WARNING "Fabulist runtime is currently broken - refusing to build runtime") - # add_subdirectory(runtime) + message(STATUS "Building runtime") + add_subdirectory(runtime) endif() if(FABULIST_SAMPLES) diff --git a/cmake/modules/GitVersion.cmake b/cmake/modules/GitVersion.cmake index 1142bde..f744b18 100644 --- a/cmake/modules/GitVersion.cmake +++ b/cmake/modules/GitVersion.cmake @@ -37,7 +37,17 @@ function(extract_git_version prefix) execute_process( COMMAND "${GIT_PROGRAM}" describe --tags "${_git_tag_sha}" OUTPUT_VARIABLE _git_tag + RESULT_VARIABLE _git_tag_success ) + + if(NOT _git_tag_success EQUAL 0) + set(${prefix}_VERSION_STRING "0.0.0-unknown" PARENT_SCOPE) + set(${prefix}_VERSION_MAJOR 0 PARENT_SCOPE) + set(${prefix}_VERSION_MINOR 0 PARENT_SCOPE) + set(${prefix}_VERSION_PATCH 0 PARENT_SCOPE) + return() + endif() + execute_process( COMMAND "${GIT_PROGRAM}" rev-parse --short HEAD OUTPUT_VARIABLE _git_sha diff --git a/compiler/cli/console.hpp b/compiler/cli/console.hpp index 48e0e56..9983c9f 100644 --- a/compiler/cli/console.hpp +++ b/compiler/cli/console.hpp @@ -1,5 +1,5 @@ -#ifndef ARGS_HPP -#define ARGS_HPP +#ifndef CONSOLE_HPP +#define CONSOLE_HPP #include #include @@ -36,4 +36,4 @@ std::optional parse_arguments(int argc, char const** argv); } -#endif /* ARGS_HPP */ +#endif /* CONSOLE_HPP */ diff --git a/compiler/cli/console/parse_getopt.cpp b/compiler/cli/console/parse_getopt.cpp index 21e5f2e..8c163f0 100644 --- a/compiler/cli/console/parse_getopt.cpp +++ b/compiler/cli/console/parse_getopt.cpp @@ -26,7 +26,7 @@ std::ostream& cli::detail::usage(std::ostream& stream, std::optional cli::parse_arguments( int argc, char const** argv) { - cli::parsed_arguments result; + cli::parsed_arguments result{}; struct option options[] = { { "output", optional_argument, nullptr, 'o' }, diff --git a/compiler/cli/main.cpp b/compiler/cli/main.cpp index 95c65cb..a117111 100644 --- a/compiler/cli/main.cpp +++ b/compiler/cli/main.cpp @@ -66,7 +66,8 @@ int main(int argc, char const** argv) } catch (std::exception& e) { - std::cerr << e.what() << "\n"; + std::cerr << "error parsing " << path << ":\n"; + std::cerr << e.what(); std::cerr << cli::error << "compilation halted.\n"; return 1; } diff --git a/compiler/lib/CMakeLists.txt b/compiler/lib/CMakeLists.txt index 939a3a8..9d96413 100644 --- a/compiler/lib/CMakeLists.txt +++ b/compiler/lib/CMakeLists.txt @@ -3,6 +3,9 @@ include(GitVersion) add_library(compiler $<$,$>:compatibility/lua5.1.cpp> + $<$,$>:compatibility/lua5.2.cpp> + $<$,$>:compatibility/lua5.3.cpp> + $<$,$>:compatibility/lua5.4.cpp> compiler/convenience.cpp @@ -16,10 +19,11 @@ add_library(compiler compiler/setup.cpp compiler/setup/actions.cpp + compiler/setup/actions/call.cpp compiler/setup/actions/jump.cpp + compiler/setup/actions/options.cpp compiler/setup/base.cpp - compiler/setup/options.cpp compiler/setup/section.cpp compiler/setup/speaker.cpp diff --git a/compiler/lib/compatibility/lua.hpp b/compiler/lib/compatibility/lua.hpp index 3a4c5f7..399e32d 100644 --- a/compiler/lib/compatibility/lua.hpp +++ b/compiler/lib/compatibility/lua.hpp @@ -41,6 +41,18 @@ int lua_load(lua_State* L, lua_Reader reader, void* data, char const* source, ch void luaL_setfuncs(lua_State* L, luaL_Reg const* reg, int nup); void lua_resume(lua_State* L, lua_State* from, int nargs); +#elif LUA_VERSION_NUM == 502 + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud); + +#elif LUA_VERSION_NUM == 503 + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud); + +#elif LUA_VERSION_NUM == 504 + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud); + #endif #endif /* LUA_HPP */ diff --git a/compiler/lib/compatibility/lua5.2.cpp b/compiler/lib/compatibility/lua5.2.cpp new file mode 100644 index 0000000..d8d18e2 --- /dev/null +++ b/compiler/lib/compatibility/lua5.2.cpp @@ -0,0 +1,15 @@ +// DO NOT INCLUDE "lua.hpp" HERE! THIS WILL BREAK EVERYTHING! + +#include +#include + +static_assert(LUA_VERSION_NUM == 502, "Do not compile this outside of Lua 5.1"); + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud) +{ + lua_pushcfunction(L, func); + + lua_pushlightuserdata(L, ud); + + return lua_pcall(L, 1, 0, 0); +} diff --git a/compiler/lib/compatibility/lua5.3.cpp b/compiler/lib/compatibility/lua5.3.cpp new file mode 100644 index 0000000..a119e28 --- /dev/null +++ b/compiler/lib/compatibility/lua5.3.cpp @@ -0,0 +1,15 @@ +// DO NOT INCLUDE "lua.hpp" HERE! THIS WILL BREAK EVERYTHING! + +#include +#include + +static_assert(LUA_VERSION_NUM == 503, "Do not compile this outside of Lua 5.1"); + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud) +{ + lua_pushcfunction(L, func); + + lua_pushlightuserdata(L, ud); + + return lua_pcall(L, 1, 0, 0); +} diff --git a/compiler/lib/compatibility/lua5.4.cpp b/compiler/lib/compatibility/lua5.4.cpp new file mode 100644 index 0000000..1494279 --- /dev/null +++ b/compiler/lib/compatibility/lua5.4.cpp @@ -0,0 +1,15 @@ +// DO NOT INCLUDE "lua.hpp" HERE! THIS WILL BREAK EVERYTHING! + +#include +#include + +static_assert(LUA_VERSION_NUM == 504, "Do not compile this outside of Lua 5.1"); + +int lua_cpcall(lua_State* L, lua_CFunction func, void* ud) +{ + lua_pushcfunction(L, func); + + lua_pushlightuserdata(L, ud); + + return lua_pcall(L, 1, 0, 0); +} diff --git a/compiler/lib/compiler/parse.cpp b/compiler/lib/compiler/parse.cpp index cc0f629..0796e74 100644 --- a/compiler/lib/compiler/parse.cpp +++ b/compiler/lib/compiler/parse.cpp @@ -14,6 +14,7 @@ using namespace fabulist::compiler; struct reader { std::istream& stream; + std::string name; std::array buffer; }; @@ -68,15 +69,42 @@ int error_handler(lua_State* L) return 1; } -void compiler::parse(std::istream& stream, std::string name) +int parse_main(lua_State* L) { - reader reader{stream, {}}; + void* data = lua_touserdata(L, 1); + auto* reader = static_cast(data); + + lua_pushcclosure(L, error_handler, 0); + + if (lua_load(L, read_func, reader, reader->name.c_str(), "t")) + { + error_handler(L); + lua_error(L); + } + else if (lua_pcall(L, 0, 0, -2)) + { + lua_error(L); + } - lua_pushcclosure(_pimpl->state, error_handler, 0); + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "return"); + lua_rawget(L, -2); + + if (!lua_isnil(L, -1)) + { + lua_pushliteral(L, "detected that an action may be missed from a section"); + error_handler(L); + lua_error(L); + } + + return 0; +} + +void compiler::parse(std::istream& stream, std::string name) +{ + reader reader{stream, name, {}}; - int status = lua_load(_pimpl->state, read_func, &reader, name.c_str(), "t"); - if (status) error_handler(_pimpl->state); - else status = lua_pcall(_pimpl->state, 0, 0, -2); + int status = lua_cpcall(_pimpl->state, parse_main, &reader); if (status) { diff --git a/compiler/lib/compiler/save.cpp b/compiler/lib/compiler/save.cpp index dd6c1ba..73c70d3 100644 --- a/compiler/lib/compiler/save.cpp +++ b/compiler/lib/compiler/save.cpp @@ -71,7 +71,7 @@ nlohmann::json table_to_json(lua_State* L) nlohmann::json result = std::accumulate( values.begin(), values.end(), nlohmann::json{}, - [is_array](nlohmann::json& result, + [is_array](nlohmann::json result, decltype(values)::const_reference value) { if (is_array) diff --git a/compiler/lib/compiler/setup.cpp b/compiler/lib/compiler/setup.cpp index ca41c85..cd0a509 100644 --- a/compiler/lib/compiler/setup.cpp +++ b/compiler/lib/compiler/setup.cpp @@ -15,10 +15,7 @@ static lua_CFunction state_setup_actions[] = { &setup_state, - &setup_speaker, &setup_section, - &setup_options, - &setup_actions, nullptr diff --git a/compiler/lib/compiler/setup/actions.hpp b/compiler/lib/compiler/setup/actions.hpp index 1ff864c..5d80ec5 100644 --- a/compiler/lib/compiler/setup/actions.hpp +++ b/compiler/lib/compiler/setup/actions.hpp @@ -4,7 +4,9 @@ #include "common.hpp" #define ACTIONS \ -ACTION(jump) +ACTION(call) \ +ACTION(jump) \ +ACTION(options) enum class action { diff --git a/compiler/lib/compiler/setup/actions/call.cpp b/compiler/lib/compiler/setup/actions/call.cpp new file mode 100644 index 0000000..2d3a363 --- /dev/null +++ b/compiler/lib/compiler/setup/actions/call.cpp @@ -0,0 +1,37 @@ +#include "../actions.hpp" + +int setup_parameters(lua_State* L); + +template <> +int call_action(lua_State* L) +{ + luaL_checkstring(L, 1); + + lua_createtable(L, 0, 2); + lua_pushliteral(L, "method"); + lua_pushvalue(L, 1); + lua_settable(L, -3); + + lua_pushliteral(L, "type"); + lua_pushliteral(L, "call"); + lua_settable(L, -3); + + lua_pushliteral(L, "parameters"); + lua_newtable(L); + lua_settable(L, -3); + + lua_pushcclosure(L, &setup_parameters, 1); + return 1; +} + +int setup_parameters(lua_State* L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushliteral(L, "parameters"); + lua_pushvalue(L, 1); + lua_settable(L, -3); + + return 1; +} diff --git a/compiler/lib/compiler/setup/actions/jump.cpp b/compiler/lib/compiler/setup/actions/jump.cpp index 4e32036..c2b4af1 100644 --- a/compiler/lib/compiler/setup/actions/jump.cpp +++ b/compiler/lib/compiler/setup/actions/jump.cpp @@ -1,28 +1,18 @@ -#include - #include "../actions.hpp" -constexpr std::pair values[] = { - {"type", "action"}, - {"action", "jump"} -}; - template <> int call_action(lua_State* L) { luaL_checkstring(L, 1); - lua_createtable(L, 0, 1 + (int)std::size(values)); + lua_createtable(L, 0, 2); lua_pushliteral(L, "section"); lua_pushvalue(L, 1); lua_settable(L, -3); - for (auto& pair : values) - { - lua_pushlstring(L, pair.first.data(), pair.first.size()); - lua_pushlstring(L, pair.second.data(), pair.second.size()); - lua_settable(L, -3); - } + lua_pushliteral(L, "type"); + lua_pushliteral(L, "jump"); + lua_settable(L, -3); return 1; } diff --git a/compiler/lib/compiler/setup/actions/options.cpp b/compiler/lib/compiler/setup/actions/options.cpp new file mode 100644 index 0000000..00b45ad --- /dev/null +++ b/compiler/lib/compiler/setup/actions/options.cpp @@ -0,0 +1,76 @@ +#include "../actions.hpp" + +static int create_options_action(lua_State* L); +static int create_options_multiple(lua_State* L) +{ + luaL_checkstring(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushcclosure(L, &create_options_action, 2); + return 1; +} + +static int create_options_action(lua_State* L) +{ + if (lua_type(L, 1) != LUA_TTABLE) + { + const char* message = lua_pushfstring(L, + "table expected, got %s", + luaL_typename(L, 1)); + + return luaL_argerror(L, 1, message); + } + + lua_pushvalue(L, lua_upvalueindex(2)); + + lua_pushliteral(L, "options"); + lua_gettable(L, -2); + + size_t length = lua_rawlen(L, -1); + + lua_createtable(L, 0, 2); + + lua_pushliteral(L, "name"); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_settable(L, -3); + + lua_pushliteral(L, "actions"); + lua_pushvalue(L, 1); + lua_settable(L, -3); + + lua_rawseti(L, -2, (int)length + 1); + + lua_pop(L, 1); + + lua_pushcclosure(L, &create_options_multiple, 1); + return 1; +} + +template <> +int call_action(lua_State* L) +{ + luaL_checkstring(L, 1); + + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "current_section"); + lua_rawget(L, -2); + + size_t length = lua_rawlen(L, -1); + + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + + lua_rawseti(L, -3, (int)length + 1); + lua_remove(L, -2); + lua_remove(L, -2); + + lua_pushliteral(L, "type"); + lua_pushliteral(L, "options"); + lua_settable(L, -3); + + lua_pushliteral(L, "options"); + lua_createtable(L, 1, 0); + lua_settable(L, -3); + + lua_pushcclosure(L, &create_options_action, 2); + return 1; +} diff --git a/compiler/lib/compiler/setup/base.cpp b/compiler/lib/compiler/setup/base.cpp index e1eb33f..7112804 100644 --- a/compiler/lib/compiler/setup/base.cpp +++ b/compiler/lib/compiler/setup/base.cpp @@ -1,12 +1,65 @@ +#include +#include + #include "common.hpp" static luaL_Reg builtins[] = { { "speaker", create_speaker }, { "section", create_section }, - { "options", create_options }, { nullptr, nullptr } }; +void push_to_section(lua_State* L, lua_Debug* ar) +{ + if (ar->event == LUA_HOOKRET) + { + lua_getinfo(L, "S", ar); + + int top = lua_gettop(L); + + if (lua_type(L, top) == LUA_TTABLE) + { + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "return"); + lua_pushvalue(L, top); + lua_rawset(L, -3); + lua_pop(L, 1); + } + else if (strcmp(ar->what, "main") != 0) + { + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "return"); + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); + } + } + else if (ar->event == LUA_HOOKLINE) + { + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "return"); + lua_rawget(L, -2); + + if (!lua_isnil(L, -1)) + { + lua_pushliteral(L, "current_section"); + lua_rawget(L, -3); + + size_t length = lua_rawlen(L, -1); + + lua_pushvalue(L, -2); + lua_rawseti(L, -2, (int)length + 1); + lua_pop(L, 1); + } + + lua_pop(L, 1); + lua_pushliteral(L, "return"); + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); + } +} + int setup_state(lua_State* L) { lua_pushvalue(L, LUA_REGISTRYINDEX); // +registry @@ -36,5 +89,7 @@ int setup_state(lua_State* L) lua_pop(L, 1); // -registry + lua_sethook(L, push_to_section, LUA_MASKRET | LUA_MASKLINE, 0); + return 0; } diff --git a/compiler/lib/compiler/setup/common.hpp b/compiler/lib/compiler/setup/common.hpp index 199dcce..769f2d0 100644 --- a/compiler/lib/compiler/setup/common.hpp +++ b/compiler/lib/compiler/setup/common.hpp @@ -4,13 +4,10 @@ #include "../compiler.hpp" int setup_state(lua_State* L); -int setup_speaker(lua_State* L); int setup_section(lua_State* L); -int setup_options(lua_State* L); int setup_actions(lua_State* L); int create_speaker(lua_State* L); int create_section(lua_State* L); -int create_options(lua_State* L); #endif /* SETUP_COMMON_HPP */ diff --git a/compiler/lib/compiler/setup/options.cpp b/compiler/lib/compiler/setup/options.cpp deleted file mode 100644 index c226e31..0000000 --- a/compiler/lib/compiler/setup/options.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "common.hpp" - -static int create_options_action(lua_State* L) -{ - if (lua_type(L, 1) != LUA_TTABLE) - { - const char* message = lua_pushfstring(L, - "table expected, got %s", - luaL_typename(L, 1)); - - return luaL_argerror(L, 1, message); - } - - lua_pushvalue(L, LUA_REGISTRYINDEX); - lua_pushliteral(L, "current_section"); - lua_rawget(L, -2); - - size_t length = lua_rawlen(L, -1); - - lua_createtable(L, 0, 3); - - lua_pushliteral(L, "type"); - lua_pushliteral(L, "option"); - lua_settable(L, -3); - - lua_pushliteral(L, "name"); - lua_pushvalue(L, lua_upvalueindex(1)); - lua_settable(L, -3); - - lua_pushliteral(L, "actions"); - lua_pushvalue(L, 1); - lua_settable(L, -3); - - lua_rawseti(L, -2, (int)length + 1); - - lua_pop(L, 2); - - lua_pushcfunction(L, &create_options); - return 1; -} - -int create_options(lua_State* L) -{ - size_t size; - const char* text = luaL_checklstring(L, 1, &size); - - (void)text; - - lua_pushcclosure(L, &create_options_action, 1); - return 1; -} - -int setup_options(lua_State*) -{ - return 0; -} diff --git a/compiler/lib/compiler/setup/speaker.cpp b/compiler/lib/compiler/setup/speaker.cpp index 97d79ac..659b767 100644 --- a/compiler/lib/compiler/setup/speaker.cpp +++ b/compiler/lib/compiler/setup/speaker.cpp @@ -79,8 +79,3 @@ int create_speaker(lua_State* L) return 0; } - -int setup_speaker(lua_State*) -{ - return 0; -} diff --git a/examples/loop.lua b/examples/loop.lua index 0215dae..149b730 100644 --- a/examples/loop.lua +++ b/examples/loop.lua @@ -1,3 +1,4 @@ +do speaker "narrator" section "root" @@ -11,4 +12,6 @@ options section "buyMilk" narrator "Oh no, they're all out of milk." -jump "root" \ No newline at end of file +call "increment" { "attempts" } +jump "root" +end diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ed76db0..a3e71c4 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,27 +1,52 @@ include(GenerateExportHeader) +include(GitVersion) add_library(runtime - speaker.cpp + action.cpp + actions/call.cpp + actions/jump.cpp + actions/line.cpp + actions/options.cpp + + decoder.cpp + + runtime.cpp + + section.cpp + state.cpp story.cpp - story/environment.cpp - story/implementation.cpp - story/loading.cpp + + version.cpp ) set(public_headers - include/fabulist/speaker.hpp - include/fabulist/story.hpp + include/fabulist/runtime/action.hpp + + include/fabulist/runtime/section.hpp + include/fabulist/runtime/state.hpp + include/fabulist/runtime/story.hpp + + include/fabulist/runtime/version.hpp ) generate_export_header(runtime PREFIX_NAME FABULIST_ - EXPORT_FILE_NAME fabulist_export.hpp + EXPORT_FILE_NAME fabulist_runtime_export.hpp ) +generate_git_version_header(runtime + PREFIX_NAME FABULIST_ + NAMESPACE fabulist::runtime + VERSION_FILE_NAME fabulist_runtime_git_version.hpp + USE_NAMESPACE USE_STD_STRING_VIEW USE_CONSTEXPR +) + +list(APPEND public_headers ${CMAKE_CURRENT_BINARY_DIR}/fabulist_runtime_export.hpp) + set_target_properties(runtime PROPERTIES - OUTPUT_NAME fabulist-runtime - PUBLIC_HEADER "${public_headers};${CMAKE_CURRENT_BINARY_DIR}/fabulist_export.hpp" + OUTPUT_NAME fabulist-runtime-$ + PUBLIC_HEADER "${public_headers}" ) target_compile_features(runtime @@ -39,6 +64,8 @@ target_include_directories(runtime ) target_link_libraries(runtime + PRIVATE + nlohmann_json::nlohmann_json PUBLIC lua::lua ) @@ -47,5 +74,5 @@ install( TARGETS runtime EXPORT fabulist LIBRARY DESTINATION lib - PUBLIC_HEADER DESTINATION include/fabulist + PUBLIC_HEADER DESTINATION include/fabulist/runtime ) diff --git a/runtime/action.cpp b/runtime/action.cpp new file mode 100644 index 0000000..75331d4 --- /dev/null +++ b/runtime/action.cpp @@ -0,0 +1,33 @@ +#include +#include + +using namespace fabulist::runtime; + +void action::execute(state& state) const +{ + _impl->execute(state); +} + +std::string action::type() const +{ + return _impl->type(); +} + + +actions::action* action::operator->() +{ + return _impl.get(); +} +actions::action const* action::operator->() const +{ + return _impl.get(); +} + +actions::action* action::operator*() +{ + return _impl.get(); +} +actions::action const* action::operator*() const +{ + return _impl.get(); +} diff --git a/runtime/actions/call.cpp b/runtime/actions/call.cpp new file mode 100644 index 0000000..e09a721 --- /dev/null +++ b/runtime/actions/call.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include +#include +#include +#include + +namespace fr = fabulist::runtime; +using namespace fr::actions; + +struct detail::call +{ + std::string method; + std::vector parameters; +}; + +call::call(std::string const& method, std::vector parameters) +: action{} +, _pimpl{ new detail::call { method, parameters }} +{ } +call::~call() noexcept = default; +call::call(call&&) = default; +call& call::operator=(call&&) = default; + +void call::execute(state& state) const +{ + auto const& methods = state.get_story()->get_runtime()->methods(); + auto method = methods.at(_pimpl->method); + + method(_pimpl->parameters); +} + +std::string call::type() const noexcept +{ + return "call"; +} + +std::string call::method() const noexcept +{ + return _pimpl->method; +} + +template<> +call fr::decoder_traits::decoder(decoder_ctx const& ctx) +{ + return call{ + ctx.get_value("method"), + ctx.get_value>("parameters") + }; +} diff --git a/runtime/actions/jump.cpp b/runtime/actions/jump.cpp new file mode 100644 index 0000000..93220cb --- /dev/null +++ b/runtime/actions/jump.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +namespace fr = fabulist::runtime; +using namespace fr::actions; + +struct detail::jump +{ + std::string section; +}; + +jump::jump(std::string const& section) + : action{} + , _pimpl{ new detail::jump { section }} +{ } +jump::~jump() noexcept = default; +jump::jump(jump&&) = default; +jump& jump::operator=(jump&&) = default; + +void jump::execute(state& state) const +{ + state.jump_to_section(_pimpl->section); +} + +std::string jump::type() const noexcept +{ + return "jump"; +} + +std::string jump::section() const noexcept +{ + return _pimpl->section; +} + +template<> +jump fr::decoder_traits::decoder(decoder_ctx const& ctx) +{ + return jump{ + ctx.get_value("section") + }; +} diff --git a/runtime/actions/line.cpp b/runtime/actions/line.cpp new file mode 100644 index 0000000..21f827b --- /dev/null +++ b/runtime/actions/line.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include + +namespace fr = fabulist::runtime; +using namespace fr::actions; + +struct detail::line +{ + std::string speaker; + std::string text; +}; + +line::line(std::string const& speaker, std::string const& text) + : action{} + , _pimpl{ new detail::line{ speaker, text } } +{ } +line::~line() noexcept = default; +line::line(line&&) = default; +line& line::operator=(line&&) = default; + +void line::execute(state&) const +{ + +} + +std::string line::type() const noexcept +{ + return "line"; +} + +std::string line::speaker() const noexcept +{ + return _pimpl->speaker; +} + +std::string line::text() const noexcept +{ + return _pimpl->text; +} + +template<> +line fr::decoder_traits::decoder(decoder_ctx const& ctx) +{ + return line{ + ctx.get_value("speaker"), + ctx.get_value("text") + }; +} diff --git a/runtime/actions/options.cpp b/runtime/actions/options.cpp new file mode 100644 index 0000000..4288a4a --- /dev/null +++ b/runtime/actions/options.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +#include +#include + +namespace fr = fabulist::runtime; +using namespace fr::actions; + + + +struct detail::options +{ + std::vector