From 21fed35042ea9292478c25450911dbad22b9a133 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Feb 2022 18:49:38 +0000 Subject: [PATCH 01/25] refactor(runtime): Begin rewriting compiler --- runtime/include/fabulist/speaker.hpp | 47 ----------------- runtime/include/fabulist/story.hpp | 66 ------------------------ runtime/include_private/speaker_impl.hpp | 22 -------- runtime/include_private/story_impl.hpp | 41 --------------- runtime/speaker.cpp | 18 ------- runtime/story.cpp | 37 ------------- runtime/story/environment.cpp | 53 ------------------- runtime/story/implementation.cpp | 14 ----- runtime/story/loading.cpp | 43 --------------- 9 files changed, 341 deletions(-) delete mode 100644 runtime/include/fabulist/speaker.hpp delete mode 100644 runtime/include/fabulist/story.hpp delete mode 100644 runtime/include_private/speaker_impl.hpp delete mode 100644 runtime/include_private/story_impl.hpp delete mode 100644 runtime/speaker.cpp delete mode 100644 runtime/story.cpp delete mode 100644 runtime/story/environment.cpp delete mode 100644 runtime/story/implementation.cpp delete mode 100644 runtime/story/loading.cpp diff --git a/runtime/include/fabulist/speaker.hpp b/runtime/include/fabulist/speaker.hpp deleted file mode 100644 index 0ae5332..0000000 --- a/runtime/include/fabulist/speaker.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef FABULIST_SPEAKER_HPP -#define FABULIST_SPEAKER_HPP - -#include -#include - -#include - -namespace fabulist -{ - -namespace detail -{ - -struct speaker_impl; - -} - -/** - * Defines a type used to represent a speaker within a story. - */ -class FABULIST_RUNTIME_EXPORT speaker -{ - public: - /** - * Gets the name of this speaker. - * - * @return The name of the speaker - */ - std::string get_name() const; - - explicit speaker(std::string name); - ~speaker() noexcept; - speaker(const speaker&) = delete; - speaker& operator=(const speaker&) = delete; - speaker(speaker&&); - speaker& operator=(speaker&&); - - - private: - friend struct detail::speaker_impl; - std::unique_ptr _impl; -}; - -} - -#endif /* FABULIST_SPEAKER_HPP */ diff --git a/runtime/include/fabulist/story.hpp b/runtime/include/fabulist/story.hpp deleted file mode 100644 index ad4ac82..0000000 --- a/runtime/include/fabulist/story.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef FABULIST_STORY_HPP -#define FABULIST_STORY_HPP - -#include -#include -#include - -#include - -namespace fabulist -{ - -namespace detail -{ - -struct story_impl; - -}; - -class FABULIST_RUNTIME_EXPORT speaker; -class FABULIST_RUNTIME_EXPORT state; - -/** - * Defines a type used to represent a story in its entirety. - * - * This type does not include any sort of state - */ -class FABULIST_RUNTIME_EXPORT story -{ - public: - /** - * Creates a state which can be used to execute this story - * - * @param section The section to begin executing from. - */ - state create_state(std::string section); - - ~story() noexcept; - story(const story&) = delete; - story& operator=(const story&) = delete; - story(story&&); - story& operator=(story&&); - - speaker const* add_speaker(std::string name); - speaker const* get_speaker(std::string name); - - private: - friend struct detail::story_impl; - std::unique_ptr _impl; - - explicit story(); - - friend story load_story_from_stream(std::istream&); -}; - -/** - * Loads a story from the given stream. This could be from the filesystem, a - * virtual filesystem, a network request or an in-memory buffer. - * - * @param stream The stream to load the story from. - */ -story FABULIST_RUNTIME_EXPORT load_story_from_stream(std::istream& stream); - -} - -#endif /* FABULIST_STORY_HPP */ diff --git a/runtime/include_private/speaker_impl.hpp b/runtime/include_private/speaker_impl.hpp deleted file mode 100644 index 84e2fe8..0000000 --- a/runtime/include_private/speaker_impl.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FABULIST_SPEAKER_IMPL_HPP -#define FABULIST_SPEAKER_IMPL_HPP - -#include - -#include - -namespace fabulist::detail -{ - -struct speaker_impl -{ - std::string name; - - explicit speaker_impl(std::string name) - : name(name) - { } -}; - -}; - -#endif /* FABULIST_SPEAKER_IMPL_HPP */ diff --git a/runtime/include_private/story_impl.hpp b/runtime/include_private/story_impl.hpp deleted file mode 100644 index 6d0c4cf..0000000 --- a/runtime/include_private/story_impl.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef FABULIST_STORY_IMPL_HPP -#define FABULIST_STORY_IMPL_HPP - -#include -#include -#include - -#include - -#include - - -namespace fabulist -{ - -class speaker; -class story; - -} - -namespace fabulist::detail -{ - -void setup_environment(sol::state& L); - -struct story_impl -{ - sol::state L; - std::unordered_map> speakers; - - explicit story_impl(story*); - ~story_impl() noexcept = default; - story_impl(const story_impl&) = delete; - story_impl& operator=(const story_impl&) = delete; - story_impl(story_impl&&) = default; - story_impl& operator=(story_impl&&) = default; -}; - -} - -#endif /* FABULIST_STORY_IMPL_HPP */ diff --git a/runtime/speaker.cpp b/runtime/speaker.cpp deleted file mode 100644 index bb3494f..0000000 --- a/runtime/speaker.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include - -using namespace fabulist; - -speaker::speaker(std::string name) - : _impl(std::make_unique(name)) -{ } - -speaker::~speaker() noexcept = default; -speaker::speaker(speaker&&) = default; -speaker& speaker::operator=(speaker&&) = default; - -std::string speaker::get_name() const -{ - return _impl->name; -} diff --git a/runtime/story.cpp b/runtime/story.cpp deleted file mode 100644 index f4a2315..0000000 --- a/runtime/story.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include -#include - -#include - -#include - -using namespace fabulist; - -story::story() - : _impl(std::make_unique(this)) -{ } - -story::~story() noexcept = default; -story::story(story&&) = default; -story& story::operator=(story&&) = default; - -speaker const* story::add_speaker(std::string name) -{ - auto [spk, succ] = _impl->speakers.emplace(name, std::make_unique(name)); - - return spk->second.get(); -} - -speaker const* story::get_speaker(std::string name) -{ - auto it = _impl->speakers.find(name); - - if (it != _impl->speakers.end()) - { - return it->second.get(); - } - - return nullptr; -} diff --git a/runtime/story/environment.cpp b/runtime/story/environment.cpp deleted file mode 100644 index 5a1d02d..0000000 --- a/runtime/story/environment.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include - -#include -#include - -#include - -#include - -using namespace fabulist; - -sol::function options(const std::string& option, sol::this_state s, int n) { - sol::state_view lua(s); - std::cout << n << ". " << option << "\n"; - - return sol::make_object(lua, - [n](const sol::table&, sol::this_state s) -> sol::function { - sol::state_view lua(s); - return sol::make_object(lua, - [n](const std::string& option, sol::this_state s) { - return options(option, s, n + 1); - }); - }); -} - -sol::function options_root(const std::string& option, sol::this_state s) -{ - return options(option, s, 1); -} - -void detail::setup_environment(sol::state& L) -{ - L.new_usertype("speaker", - sol::call_constructor, - sol::factories([&L](const std::string& name) { - auto spk = std::make_shared(name); - L[name] = spk; - return spk; - }), - sol::meta_function::call, - [](std::shared_ptr speaker, const std::string& line) { - std::cout << '[' << speaker->get_name() << "] " << line << '\n'; - return speaker; - }); - - L["section"] = [](const std::string& name) { - std::cout << "===== NEW SECTION: " << name << " =====\n"; - }; - - L["options"] = options_root; - - L["jump"] = [](const std::string&){}; -} diff --git a/runtime/story/implementation.cpp b/runtime/story/implementation.cpp deleted file mode 100644 index 8fa512f..0000000 --- a/runtime/story/implementation.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include - -#include - -#include -#include - -using namespace fabulist::detail; - -story_impl::story_impl(story*) - : L{}, speakers{} -{ - setup_environment(L); -} diff --git a/runtime/story/loading.cpp b/runtime/story/loading.cpp deleted file mode 100644 index 124cd24..0000000 --- a/runtime/story/loading.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include - -#include -#include - -#include - -#include - -#include - -using namespace fabulist; - -struct reader { - std::istream& stream; - std::vector buffer; -}; - -const char* read_func(lua_State*, void* data, size_t* size) -{ - auto* reader = static_cast(data); - - if (reader->stream) - { - auto read = reader->stream.readsome( - reader->buffer.data(), reader->buffer.size()); - - *size = read; - return reader->buffer.data(); - } - - return nullptr; -} - -story fabulist::load_story_from_stream(std::istream& stream) -{ - story story; - - reader tmpReader{stream, std::vector(4096)}; - auto code = story._impl->L.safe_script(read_func, &tmpReader, "story"); - - return story; -} From 98835d8ccc39ef69e1e62348231aa4c1852cf932 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Feb 2022 18:57:31 +0000 Subject: [PATCH 02/25] feat(runtime): Add Story type --- CMakeLists.txt | 4 ++-- runtime/CMakeLists.txt | 11 +++------ runtime/include/fabulist/runtime/story.hpp | 26 ++++++++++++++++++++++ runtime/story.cpp | 1 + 4 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 runtime/include/fabulist/runtime/story.hpp create mode 100644 runtime/story.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c8902f4..f460a04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,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/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ed76db0..c1513c2 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,21 +1,16 @@ include(GenerateExportHeader) add_library(runtime - speaker.cpp story.cpp - story/environment.cpp - story/implementation.cpp - story/loading.cpp ) set(public_headers - include/fabulist/speaker.hpp - include/fabulist/story.hpp + include/fabulist/runtime/story.hpp ) generate_export_header(runtime PREFIX_NAME FABULIST_ - EXPORT_FILE_NAME fabulist_export.hpp + EXPORT_FILE_NAME fabulist_runtime_export.hpp ) set_target_properties(runtime @@ -47,5 +42,5 @@ install( TARGETS runtime EXPORT fabulist LIBRARY DESTINATION lib - PUBLIC_HEADER DESTINATION include/fabulist + PUBLIC_HEADER DESTINATION include/fabulist/runtime ) diff --git a/runtime/include/fabulist/runtime/story.hpp b/runtime/include/fabulist/runtime/story.hpp new file mode 100644 index 0000000..54952ba --- /dev/null +++ b/runtime/include/fabulist/runtime/story.hpp @@ -0,0 +1,26 @@ +#ifndef STORY_HPP +#define STORY_HPP + +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime +{ + +namespace detail +{ + +class story; + +} + +class FABULIST_RUNTIME_EXPORT story +{ + private: + std::unique_ptr _pimpl; +}; + +} + +#endif /* STORY_HPP */ diff --git a/runtime/story.cpp b/runtime/story.cpp new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/runtime/story.cpp @@ -0,0 +1 @@ + From 1ec3af527c826f62fe9c19f7f13e43ebaea97cbf Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Feb 2022 19:18:08 +0000 Subject: [PATCH 03/25] feat(runtime): Add sections to story --- runtime/include/fabulist/runtime/section.hpp | 30 ++++++++++++++++++++ runtime/include/fabulist/runtime/story.hpp | 6 ++++ runtime/section.cpp | 13 +++++++++ runtime/story.cpp | 14 +++++++++ 4 files changed, 63 insertions(+) create mode 100644 runtime/include/fabulist/runtime/section.hpp create mode 100644 runtime/section.cpp diff --git a/runtime/include/fabulist/runtime/section.hpp b/runtime/include/fabulist/runtime/section.hpp new file mode 100644 index 0000000..faf2c7c --- /dev/null +++ b/runtime/include/fabulist/runtime/section.hpp @@ -0,0 +1,30 @@ +#ifndef SECTION_HPP +#define SECTION_HPP + +#include +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime +{ + +namespace detail +{ + +class section; + +} + +class FABULIST_RUNTIME_EXPORT section +{ + public: + std::string get_name(); + + private: + std::unique_ptr _pimpl; +}; + +} + +#endif /* SECTION_HPP */ diff --git a/runtime/include/fabulist/runtime/story.hpp b/runtime/include/fabulist/runtime/story.hpp index 54952ba..0827791 100644 --- a/runtime/include/fabulist/runtime/story.hpp +++ b/runtime/include/fabulist/runtime/story.hpp @@ -2,12 +2,15 @@ #define STORY_HPP #include +#include #include "fabulist_runtime_export.hpp" namespace fabulist::runtime { +class section; + namespace detail { @@ -17,6 +20,9 @@ class story; class FABULIST_RUNTIME_EXPORT story { + public: + std::vector
get_sections(); + private: std::unique_ptr _pimpl; }; diff --git a/runtime/section.cpp b/runtime/section.cpp new file mode 100644 index 0000000..a1d13c9 --- /dev/null +++ b/runtime/section.cpp @@ -0,0 +1,13 @@ +#include + +using namespace fabulist::runtime; + +class detail::section +{ + +}; + +std::string section::get_name() +{ + return "temporary name"; +} diff --git a/runtime/story.cpp b/runtime/story.cpp index 8b13789..775946e 100644 --- a/runtime/story.cpp +++ b/runtime/story.cpp @@ -1 +1,15 @@ +#include +#include +using namespace fabulist::runtime; + +class detail::story +{ + public: + std::vector
sections; +}; + +std::vector
story::get_sections() +{ + return _pimpl->sections; +} From c705398e2fd7dde3c0c4c03ea4b3dc378273ca10 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Feb 2022 20:50:30 +0000 Subject: [PATCH 04/25] feat(runtime): Wire up base API types --- runtime/CMakeLists.txt | 4 ++ runtime/include/fabulist/runtime/section.hpp | 8 ++++ runtime/include/fabulist/runtime/state.hpp | 39 ++++++++++++++++++++ runtime/include/fabulist/runtime/story.hpp | 12 +++++- runtime/section.cpp | 8 ++++ runtime/state.cpp | 27 ++++++++++++++ runtime/story.cpp | 17 ++++++++- 7 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 runtime/include/fabulist/runtime/state.hpp create mode 100644 runtime/state.cpp diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index c1513c2..6e20e27 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,10 +1,14 @@ include(GenerateExportHeader) add_library(runtime + section.cpp + state.cpp story.cpp ) set(public_headers + include/fabulist/runtime/section.hpp + include/fabulist/runtime/state.hpp include/fabulist/runtime/story.hpp ) diff --git a/runtime/include/fabulist/runtime/section.hpp b/runtime/include/fabulist/runtime/section.hpp index faf2c7c..8bf537b 100644 --- a/runtime/include/fabulist/runtime/section.hpp +++ b/runtime/include/fabulist/runtime/section.hpp @@ -19,6 +19,14 @@ class section; class FABULIST_RUNTIME_EXPORT section { public: + explicit section(); + ~section() noexcept; + section(const section&) = delete; + section& operator=(const section&) = delete; + section(section&&); + section& operator=(section&&); + + std::string get_name(); private: diff --git a/runtime/include/fabulist/runtime/state.hpp b/runtime/include/fabulist/runtime/state.hpp new file mode 100644 index 0000000..7a54b6c --- /dev/null +++ b/runtime/include/fabulist/runtime/state.hpp @@ -0,0 +1,39 @@ +#ifndef STATE_HPP +#define STATE_HPP + +#include +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime +{ + +namespace detail +{ + +class state; + +} + +class FABULIST_RUNTIME_EXPORT state +{ + public: + explicit state(); + ~state() noexcept; + state(const state&) = delete; + state& operator=(const state&) = delete; + state(state&&); + state& operator=(state&&); + + + // TODO: is std::any safe here? + std::any get_variable(std::string variable); + + private: + std::unique_ptr _pimpl; +}; + +} + +#endif /* STATE_HPP */ diff --git a/runtime/include/fabulist/runtime/story.hpp b/runtime/include/fabulist/runtime/story.hpp index 0827791..af04fcf 100644 --- a/runtime/include/fabulist/runtime/story.hpp +++ b/runtime/include/fabulist/runtime/story.hpp @@ -10,6 +10,7 @@ namespace fabulist::runtime { class section; +class state; namespace detail { @@ -21,7 +22,16 @@ class story; class FABULIST_RUNTIME_EXPORT story { public: - std::vector
get_sections(); + explicit story(); + ~story() noexcept; + story(const story&) = delete; + story& operator=(const story&) = delete; + story(story&&); + story& operator=(story&&); + + + state create_state(std::string section); + std::vector
const& get_sections(); private: std::unique_ptr _pimpl; diff --git a/runtime/section.cpp b/runtime/section.cpp index a1d13c9..4e6cf68 100644 --- a/runtime/section.cpp +++ b/runtime/section.cpp @@ -1,4 +1,5 @@ #include +#include using namespace fabulist::runtime; @@ -7,6 +8,13 @@ class detail::section }; +section::section() + : _pimpl{new detail::section{}} +{ } +section::~section() noexcept = default; +section::section(section&&) = default; +section& section::operator=(section&&) = default; + std::string section::get_name() { return "temporary name"; diff --git a/runtime/state.cpp b/runtime/state.cpp new file mode 100644 index 0000000..e8acca6 --- /dev/null +++ b/runtime/state.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include + +using namespace fabulist::runtime; + +class detail::state +{ + public: + std::unordered_map variables; +}; + +state::state() + : _pimpl{new detail::state{}} +{ } + +state::~state() noexcept = default; +state::state(state&&) = default; +state& state::operator=(state&&) = default; + +std::any state::get_variable(std::string variable) +{ + auto [it, inserted] = _pimpl->variables.try_emplace(variable, std::any{}); + + return *it; +} diff --git a/runtime/story.cpp b/runtime/story.cpp index 775946e..193ac9b 100644 --- a/runtime/story.cpp +++ b/runtime/story.cpp @@ -1,4 +1,5 @@ #include +#include #include using namespace fabulist::runtime; @@ -6,10 +7,22 @@ using namespace fabulist::runtime; class detail::story { public: - std::vector
sections; + std::vector sections; }; -std::vector
story::get_sections() +story::story() + : _pimpl{new detail::story{}} +{ } +story::~story() noexcept = default; +story::story(story&&) = default; +story& story::operator=(story&&) = default; + +state story::create_state(std::string) +{ + return state{}; +} + +std::vector
const& story::get_sections() { return _pimpl->sections; } From cf584dd6e2aa0449a71fb290e65646a720de710e Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Feb 2022 23:24:10 +0000 Subject: [PATCH 05/25] feat(runtime): Integrate gitversion into runtime --- runtime/CMakeLists.txt | 18 ++++++++++++++++-- runtime/include/fabulist/runtime/version.hpp | 17 +++++++++++++++++ runtime/version.cpp | 16 ++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 runtime/include/fabulist/runtime/version.hpp create mode 100644 runtime/version.cpp diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 6e20e27..6005eec 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,15 +1,20 @@ include(GenerateExportHeader) +include(GitVersion) add_library(runtime section.cpp state.cpp story.cpp + + version.cpp ) set(public_headers include/fabulist/runtime/section.hpp include/fabulist/runtime/state.hpp include/fabulist/runtime/story.hpp + + include/fabulist/runtime/version.hpp ) generate_export_header(runtime @@ -17,10 +22,19 @@ generate_export_header(runtime 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 diff --git a/runtime/include/fabulist/runtime/version.hpp b/runtime/include/fabulist/runtime/version.hpp new file mode 100644 index 0000000..8c61c27 --- /dev/null +++ b/runtime/include/fabulist/runtime/version.hpp @@ -0,0 +1,17 @@ +#ifndef VERSION_HPP +#define VERSION_HPP + +#include +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime +{ + +FABULIST_RUNTIME_EXPORT std::tuple get_version(); +FABULIST_RUNTIME_EXPORT std::string_view get_version_string(); + +} + +#endif /* VERSION_HPP */ diff --git a/runtime/version.cpp b/runtime/version.cpp new file mode 100644 index 0000000..3b49c66 --- /dev/null +++ b/runtime/version.cpp @@ -0,0 +1,16 @@ +#include + +#include "fabulist_runtime_git_version.hpp" + +using namespace fabulist; +using namespace fabulist::runtime; + +std::string_view runtime::get_version_string() +{ + return version::version_string; +} + +std::tuple fabulist::runtime::get_version() +{ + return {version::major, version::minor, version::patch}; +} From c43460e44243a39257ce0fcb3d6b473f7d3e2fc6 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 Feb 2022 22:24:06 +0000 Subject: [PATCH 06/25] feat(runtime): Start work on actions --- runtime/CMakeLists.txt | 10 ++++ runtime/action.cpp | 17 ++++++ runtime/actions/line.cpp | 13 +++++ runtime/include/fabulist/runtime/action.hpp | 39 +++++++++++++ .../fabulist/runtime/action_private.hpp | 19 ++++++ .../include/fabulist/runtime/actions/line.hpp | 32 ++++++++++ runtime/include/fabulist/runtime/story.hpp | 5 ++ runtime/story/parse.cpp | 58 +++++++++++++++++++ 8 files changed, 193 insertions(+) create mode 100644 runtime/action.cpp create mode 100644 runtime/actions/line.cpp create mode 100644 runtime/include/fabulist/runtime/action.hpp create mode 100644 runtime/include/fabulist/runtime/action_private.hpp create mode 100644 runtime/include/fabulist/runtime/actions/line.hpp create mode 100644 runtime/story/parse.cpp diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 6005eec..40fab75 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -2,14 +2,22 @@ include(GenerateExportHeader) include(GitVersion) add_library(runtime + action.cpp + + actions/line.cpp + section.cpp state.cpp + story.cpp + story/parse.cpp version.cpp ) set(public_headers + include/fabulist/runtime/action.hpp + include/fabulist/runtime/section.hpp include/fabulist/runtime/state.hpp include/fabulist/runtime/story.hpp @@ -52,6 +60,8 @@ target_include_directories(runtime ) target_link_libraries(runtime + PRIVATE + nlohmann_json::nlohmann_json PUBLIC lua::lua ) diff --git a/runtime/action.cpp b/runtime/action.cpp new file mode 100644 index 0000000..b09a798 --- /dev/null +++ b/runtime/action.cpp @@ -0,0 +1,17 @@ +#include +#include + +using namespace fabulist::runtime; + +action::action(detail::action* pimpl) + : _pimpl{pimpl} +{ } + +action::~action() noexcept = default; +action::action(action&&) = default; +action& action::operator=(action&&) = default; + +std::string action::type() +{ + return _pimpl->type; +} diff --git a/runtime/actions/line.cpp b/runtime/actions/line.cpp new file mode 100644 index 0000000..87b91bb --- /dev/null +++ b/runtime/actions/line.cpp @@ -0,0 +1,13 @@ +#include +#include + +using namespace fabulist::runtime; +using namespace fabulist::runtime::actions; + +line::line() + : action{new runtime::detail::action{}} +{ }; +line::~line() noexcept +{ } +line::line(line&&) = default; +line& line::operator=(line&&) = default; diff --git a/runtime/include/fabulist/runtime/action.hpp b/runtime/include/fabulist/runtime/action.hpp new file mode 100644 index 0000000..7745383 --- /dev/null +++ b/runtime/include/fabulist/runtime/action.hpp @@ -0,0 +1,39 @@ +#ifndef ACTION_HPP +#define ACTION_HPP + +#include +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime +{ + +namespace detail +{ + +class action; + +} + +class FABULIST_RUNTIME_EXPORT action +{ + public: + virtual ~action() noexcept; + action(const action&) = delete; + action& operator=(const action&) = delete; + action(action&&); + action& operator=(action&&); + + std::string type(); + + + protected: + explicit action(detail::action* pimpl); + + std::unique_ptr _pimpl; +}; + +} + +#endif /* ACTION_HPP */ diff --git a/runtime/include/fabulist/runtime/action_private.hpp b/runtime/include/fabulist/runtime/action_private.hpp new file mode 100644 index 0000000..659f64d --- /dev/null +++ b/runtime/include/fabulist/runtime/action_private.hpp @@ -0,0 +1,19 @@ +#ifndef ACTION_PRIVATE_HPP +#define ACTION_PRIVATE_HPP + +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime::detail +{ + +class FABULIST_RUNTIME_EXPORT action +{ + public: + std::string type; +}; + +} + +#endif /* ACTION_PRIVATE_HPP */ diff --git a/runtime/include/fabulist/runtime/actions/line.hpp b/runtime/include/fabulist/runtime/actions/line.hpp new file mode 100644 index 0000000..f2926f1 --- /dev/null +++ b/runtime/include/fabulist/runtime/actions/line.hpp @@ -0,0 +1,32 @@ +#ifndef ACTIONS_LINE_HPP +#define ACTIONS_LINE_HPP + +#include + +#include "fabulist_runtime_export.hpp" + +namespace fabulist::runtime::actions +{ + +namespace detail +{ + +class line; + +} + +class FABULIST_RUNTIME_EXPORT line : public action +{ + public: + explicit line(); + ~line() noexcept override; + line(const line&) = delete; + line& operator=(const line&) = delete; + line(line&&); + line& operator=(line&&); + +}; + +} + +#endif /* ACTIONS_LINE_HPP */ diff --git a/runtime/include/fabulist/runtime/story.hpp b/runtime/include/fabulist/runtime/story.hpp index af04fcf..d5c2193 100644 --- a/runtime/include/fabulist/runtime/story.hpp +++ b/runtime/include/fabulist/runtime/story.hpp @@ -1,6 +1,8 @@ #ifndef STORY_HPP #define STORY_HPP +#include +#include #include #include @@ -37,6 +39,9 @@ class FABULIST_RUNTIME_EXPORT story std::unique_ptr _pimpl; }; +story parse_story(std::filesystem::path path); +story parse_story(std::istream& stream); + } #endif /* STORY_HPP */ diff --git a/runtime/story/parse.cpp b/runtime/story/parse.cpp new file mode 100644 index 0000000..4a9da6f --- /dev/null +++ b/runtime/story/parse.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include + +#include +#include +#include +#include + +using namespace fabulist::runtime; + +story fabulist::runtime::parse_story(std::filesystem::path path) +{ + std::ifstream file{path}; + return parse_story(file); +} + +section parse_section(nlohmann::json& value); +action parse_action(nlohmann::json const& value); + +story fabulist::runtime::parse_story(std::istream& stream) +{ + nlohmann::json root; + stream >> root; + + if (!root.is_object()) + throw std::runtime_error{"expected json object"}; + + std::unordered_map sections; + + for (const auto& [key, value] : root.items()) + { + sections[key] = parse_section(value); + } + + return story{};//sections}; +} + +section parse_section(nlohmann::json& value) +{ + if (!value.is_array()) + throw std::runtime_error{"expected json array"}; + + std::vector actions; + + for (const auto& elem : value) + { + actions.push_back(parse_action(elem)); + } + + return section{};//actions}; +} + +action parse_action(nlohmann::json const&) +{ + return actions::line{}; +} From e74f18b55a1ac7271855b7cf1e6ec065056b7cd9 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 Feb 2022 22:24:26 +0000 Subject: [PATCH 07/25] feat(samples/cli): Rewrite CLI sample --- samples/CMakeLists.txt | 3 +- samples/cli/CMakeLists.txt | 26 +++++- samples/cli/console.cpp | 47 +++++++++++ samples/cli/console.hpp | 39 +++++++++ samples/cli/console/parse_getopt.cpp | 91 ++++++++++++++++++++ samples/cli/main.cpp | 122 ++++++++++++--------------- 6 files changed, 255 insertions(+), 73 deletions(-) create mode 100644 samples/cli/console.cpp create mode 100644 samples/cli/console.hpp create mode 100644 samples/cli/console/parse_getopt.cpp diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index d69359d..1c665ad 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -8,6 +8,5 @@ cmake_dependent_option(FABULIST_SAMPLE_CLI ) if(FABULIST_SAMPLE_CLI) - message(WARNING "Fabulist runtime is currently broken - refusing to build cli sample") - # add_subdirectory(cli) + add_subdirectory(cli) endif() diff --git a/samples/cli/CMakeLists.txt b/samples/cli/CMakeLists.txt index 3782873..eca36bb 100644 --- a/samples/cli/CMakeLists.txt +++ b/samples/cli/CMakeLists.txt @@ -1,2 +1,24 @@ -add_executable(cli main.cpp) -target_link_libraries(cli PUBLIC runtime) +include(CheckCXXSymbolExists) + +check_cxx_symbol_exists(getopt "getopt.h" HAVE_GETOPT) +check_cxx_symbol_exists(getopt_long "getopt.h" HAVE_GETOPT_LONG) + +add_executable(cli + # console output utilities + console.cpp + $<$:console/parse_getopt.cpp> + + # main entry point + main.cpp +) + +target_link_libraries(cli + PUBLIC + runtime +) + +target_compile_definitions(cli + PRIVATE + $<$:HAVE_GETOPT_LONG> + $<$:HAVE_GETOPT> +) diff --git a/samples/cli/console.cpp b/samples/cli/console.cpp new file mode 100644 index 0000000..6963e5e --- /dev/null +++ b/samples/cli/console.cpp @@ -0,0 +1,47 @@ +#include + +#include "console.hpp" + +// A dummy ostream used to disable verbose output if the flag isn't set +class null_ostream : public std::ostream +{ +public: + null_ostream() : std::ostream(nullptr) + { } +}; + +static std::string program; +static bool enable_verbose; +static null_ostream null_output; + +void cli::detail::set_program_name(std::string name) +{ + program = name; +} + +std::ostream& cli::error(std::ostream& stream) +{ + return stream << program << ": "; +} + +std::ostream& cli::usage(std::ostream& stream) +{ + return cli::detail::usage(stream, program); +} + +std::ostream& cli::verbose(std::ostream& stream) +{ + if (!enable_verbose) + { + return null_output; + } + else + { + return stream; + } +} + +void cli::setup_output(cli::parsed_arguments const& arguments) +{ + enable_verbose = arguments.enable_verbose; +} diff --git a/samples/cli/console.hpp b/samples/cli/console.hpp new file mode 100644 index 0000000..89b3cba --- /dev/null +++ b/samples/cli/console.hpp @@ -0,0 +1,39 @@ +#ifndef CONSOLE_HPP +#define CONSOLE_HPP + +#include +#include +#include +#include + +namespace cli +{ + +namespace detail +{ + +std::ostream& usage(std::ostream& stream, std::string program_name); +void set_program_name(std::string program_name); + +} + +struct parsed_arguments +{ + std::vector input_files; + std::optional section; + bool enable_verbose; + bool show_usage; + bool show_version; +}; + +std::ostream& error(std::ostream& stream); +std::ostream& verbose(std::ostream& stream); +std::ostream& usage(std::ostream& stream); + +void setup_output(parsed_arguments const& arguments); + +std::optional parse_arguments(int argc, char const** argv); + +} + +#endif /* CONSOLE_HPP */ diff --git a/samples/cli/console/parse_getopt.cpp b/samples/cli/console/parse_getopt.cpp new file mode 100644 index 0000000..97aa597 --- /dev/null +++ b/samples/cli/console/parse_getopt.cpp @@ -0,0 +1,91 @@ +#include + +#include + +#include "../console.hpp" + +std::ostream& cli::detail::usage(std::ostream& stream, + std::string program_name) +{ + return stream + << "Usage: " << program_name << " [OPTION]... FILE\n" + << "Command line execution environment for Fabulist files.\n" + << "\n" + << "Arguments:\n" + << " FILE Fabulist file to execute.\n" + << "\n" + << "Options:\n" + << " -h, --help Display this help and exit\n" + << " -s, --section[=SECTION] Section to begin execution at\n" + << " (default: root)\n" + << " -V, --verbose Enable verbose mode\n" + << " -v, --version Display runtime version and exit\n"; +} + +std::optional cli::parse_arguments( + int argc, char const** argv) +{ + cli::parsed_arguments result; + + struct option options[] = { + { "help", no_argument, nullptr, 'h' }, + { "section", optional_argument, nullptr, 's' }, + { "verbose", no_argument, nullptr, 'V' }, + { "version", no_argument, nullptr, 'v' }, + { nullptr, 0, nullptr, 0 } + }; + + cli::detail::set_program_name(argv[0]); + + // why is getopt the way that it is + char* const* AAAAA = const_cast(argv); + + int longopt_ind = 0; + while (true) + { + int c = getopt_long(argc, AAAAA, "hs:vV", options, &longopt_ind); + if (c == -1) break; + + switch (c) + { + case 'h': + result.show_usage = true; + break; + + case 's': + result.section = optarg; + break; + + case 'V': + result.enable_verbose = true; + break; + + case 'v': + result.show_version = true; + break; + + case '?': + return std::nullopt; + } + } + + if (optind >= argc && !(result.show_usage || result.show_version)) + { + std::cerr << cli::error << "missing operand\n"; + return std::nullopt; + } + else if ((argc - optind) > 1) + { + std::cerr << cli::error << "too many operands\n"; + return std::nullopt; + } + else + { + for (int i = optind; i < argc; i++) + { + result.input_files.push_back(argv[i]); + } + } + + return result; +} diff --git a/samples/cli/main.cpp b/samples/cli/main.cpp index 885b45f..41a81f7 100644 --- a/samples/cli/main.cpp +++ b/samples/cli/main.cpp @@ -1,94 +1,78 @@ -#include -#include -#include #include -#include +#include +#include -char const* program; +#include "console.hpp" -std::ostream& usage(std::ostream& stream) -{ - return stream - << "Usage: " << program << " [OPTION]... FILE\n" - << "Command line execution environment for Fabulist files.\n" - << "\n" - << "Arguments:\n" - << " FILE Fabulist file to execute.\n" - << "\n" - << "Options:\n" - << " -h, --help Display this help and exit\n" - << " -s, --section[=SECTION] Section to begin execution at\n" - << " (default: root)\n"; -} +using namespace fabulist::runtime; -static struct option options[] = { - { "section", optional_argument, NULL, 's' }, - { "help", no_argument, NULL, 'h' }, - { NULL, 0, NULL, 0} +template +struct wrap_optional +{ + using type = std::optional; }; -bool parseargs(int argc, char** argv, - char const** section, - char const** file) +template +struct wrap_optional> { - int longopt_ind = 0; - while (true) - { - int c = getopt_long(argc, argv, "s::h", options, &longopt_ind); - if (c == -1) break; + using type = std::optional; +}; - switch (c) - { - case 's': - *section = optarg; - break; - case 'h': - case '?': - return false; - } - } +template +using wrap_optional_t = typename wrap_optional::type; - if (optind >= argc) - { - std::cerr << program << ": missing operand\n"; - return false; - } - else if ((argc - optind) > 1) - { - std::cerr << program << ": too many operands\n"; - return false; - } - else - { - *file = argv[optind]; - } +template +wrap_optional_t get_member(std::optional arguments, + T cli::parsed_arguments::*member) +{ + if (!arguments.has_value()) + return std::nullopt; - return true; + return arguments.value().*member; } -int main(int argc, char** argv) +int main(int argc, char const** argv) { - program = argv[0]; + auto args = cli::parse_arguments(argc, argv); + + if (args.has_value()) + cli::setup_output(args.value()); - char const* section = "root"; - char const* story_file_location; - if (!parseargs(argc, argv, §ion, &story_file_location)) + if (get_member(args, &cli::parsed_arguments::show_usage).value_or(true)) { - std::cerr << usage; + std::cerr << cli::usage; return 1; } - std::ifstream story_file{story_file_location}; - - if (!story_file.is_open()) + if (get_member(args, &cli::parsed_arguments::show_version).value_or(false)) { - std::cerr << program << ": failed to open " - << story_file_location << "\n"; - return 2; + std::cerr << "Fabulist runtime " << fabulist::runtime::get_version_string() << "\n"; + return 1; } - fabulist::story story = fabulist::load_story_from_stream(story_file); + auto input_file = get_member(args, &cli::parsed_arguments::input_files) + .value_or(std::vector{}) + .at(0); + + auto story = [&input_file]() + { + if (input_file[0] == '-') + { + std::cerr << cli::verbose << "Parsing from standard input\n"; + + return fabulist::runtime::parse_story(std::cin); + } + else + { + std::filesystem::path story_location{input_file}; + std::cerr << cli::verbose << "Parsing " << story_location << "\n"; + + return fabulist::runtime::parse_story(story_location); + } + }(); + + std::cerr << cli::verbose << "Beginning execution of story at (somewhere)\n"; return 0; } From d854f8c5c455a4bbfebeecae8a6056e491b714c0 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 Feb 2022 22:24:59 +0000 Subject: [PATCH 08/25] refactor(compiler/cli): Fix include guard name --- compiler/cli/console.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 */ From 1cc01eccebc72427dcdfcb62df30875eaa71512d Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 18 Mar 2022 13:23:45 +0000 Subject: [PATCH 09/25] feat: The runtime may actually work now --- compiler/cli/console/parse_getopt.cpp | 2 +- compiler/cli/main.cpp | 3 +- compiler/lib/CMakeLists.txt | 2 +- compiler/lib/compiler/parse.cpp | 40 +++++-- compiler/lib/compiler/setup.cpp | 3 - compiler/lib/compiler/setup/actions.hpp | 3 +- compiler/lib/compiler/setup/actions/jump.cpp | 18 +--- .../lib/compiler/setup/actions/options.cpp | 76 +++++++++++++ compiler/lib/compiler/setup/base.cpp | 57 +++++++++- compiler/lib/compiler/setup/common.hpp | 3 - compiler/lib/compiler/setup/options.cpp | 56 ---------- compiler/lib/compiler/setup/speaker.cpp | 5 - examples/loop.lua | 4 +- runtime/CMakeLists.txt | 9 +- runtime/action.cpp | 34 ++++-- runtime/actions/jump.cpp | 43 ++++++++ runtime/actions/line.cpp | 51 +++++++-- runtime/actions/options.cpp | 94 ++++++++++++++++ runtime/decoder.cpp | 61 +++++++++++ runtime/decoder_private.hpp | 16 +++ runtime/include/fabulist/runtime/action.hpp | 29 +++-- .../fabulist/runtime/action_private.hpp | 19 ---- .../fabulist/runtime/actions/action.hpp | 35 ++++++ .../include/fabulist/runtime/actions/jump.hpp | 42 ++++++++ .../include/fabulist/runtime/actions/line.hpp | 17 ++- .../fabulist/runtime/actions/options.hpp | 56 ++++++++++ runtime/include/fabulist/runtime/decoder.hpp | 85 +++++++++++++++ .../fabulist/runtime/load_exception.hpp | 33 ++++++ runtime/include/fabulist/runtime/runtime.hpp | 68 ++++++++++++ runtime/include/fabulist/runtime/section.hpp | 17 ++- runtime/include/fabulist/runtime/state.hpp | 51 ++++++++- runtime/include/fabulist/runtime/story.hpp | 9 +- runtime/runtime.cpp | 100 ++++++++++++++++++ runtime/section.cpp | 41 +++++-- runtime/state.cpp | 79 ++++++++++++-- runtime/story.cpp | 14 +-- runtime/story/parse.cpp | 58 ---------- samples/CMakeLists.txt | 1 + samples/cli/console.cpp | 81 ++++++++++++++ samples/cli/console/parse_getopt.cpp | 2 +- samples/cli/main.cpp | 75 ++++++++++++- 41 files changed, 1258 insertions(+), 234 deletions(-) create mode 100644 compiler/lib/compiler/setup/actions/options.cpp delete mode 100644 compiler/lib/compiler/setup/options.cpp create mode 100644 runtime/actions/jump.cpp create mode 100644 runtime/actions/options.cpp create mode 100644 runtime/decoder.cpp create mode 100644 runtime/decoder_private.hpp delete mode 100644 runtime/include/fabulist/runtime/action_private.hpp create mode 100644 runtime/include/fabulist/runtime/actions/action.hpp create mode 100644 runtime/include/fabulist/runtime/actions/jump.hpp create mode 100644 runtime/include/fabulist/runtime/actions/options.hpp create mode 100644 runtime/include/fabulist/runtime/decoder.hpp create mode 100644 runtime/include/fabulist/runtime/load_exception.hpp create mode 100644 runtime/include/fabulist/runtime/runtime.hpp create mode 100644 runtime/runtime.cpp delete mode 100644 runtime/story/parse.cpp 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..2bc5616 100644 --- a/compiler/lib/CMakeLists.txt +++ b/compiler/lib/CMakeLists.txt @@ -17,9 +17,9 @@ add_library(compiler compiler/setup/actions.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/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/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..5b4c678 100644 --- a/compiler/lib/compiler/setup/actions.hpp +++ b/compiler/lib/compiler/setup/actions.hpp @@ -4,7 +4,8 @@ #include "common.hpp" #define ACTIONS \ -ACTION(jump) +ACTION(jump) \ +ACTION(options) enum class action { 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..f00f467 100644 --- a/examples/loop.lua +++ b/examples/loop.lua @@ -1,3 +1,4 @@ +do speaker "narrator" section "root" @@ -11,4 +12,5 @@ options section "buyMilk" narrator "Oh no, they're all out of milk." -jump "root" \ No newline at end of file +jump "root" +end diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 40fab75..cad6e67 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -3,14 +3,17 @@ include(GitVersion) add_library(runtime action.cpp - + actions/jump.cpp actions/line.cpp + actions/options.cpp + + decoder.cpp + + runtime.cpp section.cpp state.cpp - story.cpp - story/parse.cpp version.cpp ) diff --git a/runtime/action.cpp b/runtime/action.cpp index b09a798..75331d4 100644 --- a/runtime/action.cpp +++ b/runtime/action.cpp @@ -1,17 +1,33 @@ #include -#include +#include using namespace fabulist::runtime; -action::action(detail::action* pimpl) - : _pimpl{pimpl} -{ } +void action::execute(state& state) const +{ + _impl->execute(state); +} + +std::string action::type() const +{ + return _impl->type(); +} + -action::~action() noexcept = default; -action::action(action&&) = default; -action& action::operator=(action&&) = default; +actions::action* action::operator->() +{ + return _impl.get(); +} +actions::action const* action::operator->() const +{ + return _impl.get(); +} -std::string action::type() +actions::action* action::operator*() +{ + return _impl.get(); +} +actions::action const* action::operator*() const { - return _pimpl->type; + return _impl.get(); } 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 index 87b91bb..21f827b 100644 --- a/runtime/actions/line.cpp +++ b/runtime/actions/line.cpp @@ -1,13 +1,50 @@ #include -#include -using namespace fabulist::runtime; -using namespace fabulist::runtime::actions; +#include +#include -line::line() - : action{new runtime::detail::action{}} -{ }; -line::~line() noexcept +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..d1c319d --- /dev/null +++ b/runtime/actions/options.cpp @@ -0,0 +1,94 @@ +#include +#include + +#include +#include + +#include +#include + +namespace fr = fabulist::runtime; +using namespace fr::actions; + + + +struct detail::options +{ + std::vector