Skip to content

Commit

Permalink
Add support for tagged choices and new tag syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
JBenda committed Jun 26, 2023
1 parent c822526 commit 9a70126
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 65 deletions.
71 changes: 37 additions & 34 deletions inkcpp/choice.cpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
#include "choice.h"

#include "output.h"
#include "string_utils.h"
#include "string_table.h"
#include "string_utils.h"

namespace ink
{
namespace runtime
namespace ink {
namespace runtime {
choice& choice::setup( internal::basic_stream& in, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread, const char* const* tags )
{
choice& choice::setup(internal::basic_stream& in, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread)
char* text = nullptr;
// if we only have one item in our output stream
if ( in.queued() == 2 )
{
char* text = nullptr;
// if we only have one item in our output stream
if (in.queued() == 2)
{
// If it's a string, just grab it. Otherwise, use allocation
const internal::value& data = in.peek();
switch (data.type())
{
case internal::value_type::string:
text = strings.duplicate(data.get<internal::value_type::string>());
in.discard(2);
break;
default:
text = in.get_alloc(strings, lists);
}
}
else
// If it's a string, just grab it. Otherwise, use allocation
const internal::value& data = in.peek();
switch ( data.type() )
{
// Non-string. Must allocate
text = in.get_alloc(strings, lists);
case internal::value_type::string:
text = strings.duplicate( data.get<internal::value_type::string>() );
in.discard( 2 );
break;
default:
text = in.get_alloc( strings, lists );
}
char* end = text;
while(*end) { ++end; }
end = ink::runtime::internal::clean_string<true, true>(text, end);
*end = 0;
_text = text;
// Index/path
_index = index;
_path = path;
_thread = thread;
return *this;
}
else
{
// Non-string. Must allocate
text = in.get_alloc( strings, lists );
}
char* end = text;
while ( *end )
{
++end;
}
end = ink::runtime::internal::clean_string<true, true>( text, end );
*end = 0;
_text = text;
// Index/path
_index = index;
_path = path;
_thread = thread;
_tags = tags;
return *this;
}
}
} // namespace ink::runtime
21 changes: 19 additions & 2 deletions inkcpp/include/choice.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,30 @@ namespace ink
const char* text() const { return _text; }

choice() : choice(0) {}
choice(int) : _text{nullptr}, _index{~0}, _path{~0u}, _thread{~0u} {}
choice(int) : _tags{nullptr}, _text{nullptr}, _index{~0}, _path{~0u}, _thread{~0u} {}

bool has_tags() const { return _tags != nullptr; }
size_t num_tags() const
{
size_t i = 0;
if (has_tags()) while ( _tags[i] != nullptr )
{
++i;
};
return i;
}
const char* get_tag(size_t index) const {
return _tags[index];
}

private:
friend class internal::runner_impl;

uint32_t path() const { return _path; }
choice& setup(internal::basic_stream&, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread);
choice& setup( internal::basic_stream&, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread, const char* const* tags );

private:
const char* const* _tags;
const char* _text;
int _index;
uint32_t _path;
Expand Down
41 changes: 38 additions & 3 deletions inkcpp/runner_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "header.h"
#include "string_utils.h"
#include "snapshot_impl.h"
#include "value.h"

namespace ink::runtime
{
Expand Down Expand Up @@ -164,6 +165,7 @@ namespace ink::runtime::internal
void runner_impl::clear_tags()
{
_tags.clear();
_choice_tags_begin = -1;
}

void runner_impl::jump(ip_t dest, bool record_visits)
Expand Down Expand Up @@ -330,6 +332,7 @@ namespace ink::runtime::internal
{
_ptr = _story->instructions();
_evaluation_mode = false;
_choice_tags_begin = -1;

// register with globals
_globals->add_runner(this);
Expand Down Expand Up @@ -526,7 +529,7 @@ namespace ink::runtime::internal

size_t runner_impl::num_tags() const
{
return _tags.size();
return _choice_tags_begin < 0 ? _tags.size() : _choice_tags_begin;
}

const char* runner_impl::get_tag(size_t index) const
Expand All @@ -549,13 +552,15 @@ namespace ink::runtime::internal
ptr = snap_write(ptr, _done, should_write);
ptr = snap_write(ptr, _rng.get_state(), should_write);
ptr = snap_write(ptr, _evaluation_mode, should_write);
ptr = snap_write(ptr, _string_mode, should_write);
ptr = snap_write(ptr, _saved_evaluation_mode, should_write);
ptr = snap_write(ptr, _saved, should_write);
ptr = snap_write(ptr, _is_falling, should_write);
ptr += _output.snap(data ? ptr : nullptr, snapper);
ptr += _stack.snap(data ? ptr : nullptr, snapper);
ptr += _ref_stack.snap(data ? ptr : nullptr, snapper);
ptr += _eval.snap(data ? ptr : nullptr, snapper);
ptr = snap_write(ptr, _choice_tags_begin, should_write);
ptr = snap_write(ptr, _tags.size(), should_write);
for (const auto& tag : _tags) {
std::uintptr_t offset = tag - snapper.story_string_table;
Expand Down Expand Up @@ -600,6 +605,9 @@ namespace ink::runtime::internal
ptr = _stack.snap_load(ptr, loader);
ptr = _ref_stack.snap_load(ptr, loader);
ptr = _eval.snap_load(ptr, loader);
int choice_tags_begin;
ptr = snap_read(ptr, choice_tags_begin);
_choice_tags_begin = choice_tags_begin;
size_t num_tags;
ptr = snap_read(ptr, num_tags);
for(size_t i = 0; i < num_tags; ++i) {
Expand Down Expand Up @@ -1082,13 +1090,15 @@ namespace ink::runtime::internal
case Command::START_STR:
{
inkAssert(_evaluation_mode, "Can not enter string mode while not in evaluation mode!");
_string_mode = true;
_evaluation_mode = false;
_output << values::marker;
} break;
case Command::END_STR:
{
// TODO: Assert we really had a marker on there?
inkAssert(!_evaluation_mode, "Must be in evaluation mode");
_string_mode = false;
_evaluation_mode = true;

// Load value from output stream
Expand All @@ -1098,6 +1108,20 @@ namespace ink::runtime::internal
_globals->lists())));
} break;

case Command::START_TAG:
{
_output << values::marker;
} break;

case Command::END_TAG:
{
auto tag = _output.get_alloc<true>(_globals->strings(), _globals->lists());
if(_string_mode && _choice_tags_begin < 0) {
_choice_tags_begin = _tags.size();
}
_tags.push() = tag;
} break;

// == Choice commands
case Command::CHOICE:
{
Expand Down Expand Up @@ -1141,12 +1165,19 @@ namespace ink::runtime::internal
}
for(;sc;--sc) { _output << stack[sc-1]; }

// fetch relevant tags
const char* const* tags = nullptr;
if (_choice_tags_begin >= 0 && _tags[_tags.size()-1] != nullptr) {
for(tags = _tags.end() - 1; *(tags-1) != nullptr && (tags - _tags.begin()) > _choice_tags_begin; --tags);
_tags.push() = nullptr;
}

// Create choice and record it
if (flag & CommandFlag::CHOICE_IS_INVISIBLE_DEFAULT) {
_fallback_choice
= choice{}.setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread());
= choice{}.setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread(), tags);
} else {
add_choice().setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread());
add_choice().setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread(), tags);
}
// save stack at last choice
if(_saved) { forget(); }
Expand Down Expand Up @@ -1314,6 +1345,10 @@ namespace ink::runtime::internal
// ref_stack has no strings and lists!
_eval.mark_used(strings, lists);

// Take into account tags
for (size_t i = 0; i < _tags.size(); ++i) {
strings.mark_used(_tags[i]);
}
// Take into account choice text
for (size_t i = 0; i < _choices.size(); i++)
strings.mark_used(_choices[i]._text);
Expand Down
2 changes: 2 additions & 0 deletions inkcpp/runner_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ namespace ink::runtime::internal

// Evaluation stack
bool _evaluation_mode = false;
bool _string_mode = false;
internal::eval_stack<abs(config::limitEvalStackDepth), config::limitEvalStackDepth < 0> _eval;
bool _saved_evaluation_mode = false;

Expand All @@ -250,6 +251,7 @@ namespace ink::runtime::internal

// Tag list
managed_array<const char*, config::limitActiveTags < 0, abs(config::limitActiveTags)> _tags;
int _choice_tags_begin;

// TODO: Move to story? Both?
functions _functions;
Expand Down
9 changes: 8 additions & 1 deletion inkcpp_cl/inkcpp_cl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,14 @@ int main(int argc, const char** argv)
int index = 1;
for (const ink::runtime::choice& c : *thread)
{
std::cout << index++ << ": " << c.text() << std::endl;
std::cout << index++ << ": " << c.text();
if(c.has_tags()) {
std::cout << "\n\t";
for(size_t i = 0; i < c.num_tags(); ++i) {
std::cout << "# " << c.get_tag(i) << " ";
}
}
std::cout << std::endl;
}

int c = 0;
Expand Down
4 changes: 3 additions & 1 deletion inkcpp_compiler/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace ink
"\n",
"<>",
"void",
"#",
"inkcpp_TAG",
"inkcpp_DIVERT",
"inkcpp_DIVERT_TO_VARIABLE",
"inkcpp_TUNNEL",
Expand All @@ -40,6 +40,8 @@ namespace ink

"str",
"/str",
"#",
"/#",

"inkcpp_CHOICE",
"thread",
Expand Down
9 changes: 7 additions & 2 deletions inkcpp_compiler/json_compiler.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "json_compiler.h"

#include "list_data.h"
#include "system.h"
#include "version.h"

#include <string_view>
#include <iostream>
Expand All @@ -19,15 +21,15 @@ namespace ink::compiler::internal
void json_compiler::compile(const nlohmann::json& input, emitter* output, compilation_results* results)
{
// Get the runtime version
int inkVersion = input["inkVersion"];
_ink_version = input["inkVersion"];
// TODO: Do something with version number

// Start the output
set_results(results);
_emitter = output;

// Initialize emitter
_emitter->start(inkVersion, results);
_emitter->start(_ink_version, results);

if(auto itr = input.find("listDefs"); itr != input.end()) {
compile_lists_definition(*itr);
Expand Down Expand Up @@ -396,6 +398,9 @@ namespace ink::compiler::internal

else if (get(command, "#", val))
{
if (_ink_version > 20) {
ink_exception("with inkVerison 21 the tag system chages, and the '#: <tag>' is deprecated now");
}
_emitter->write_string(Command::TAG, CommandFlag::NO_FLAGS, val);
}

Expand Down
1 change: 1 addition & 0 deletions inkcpp_compiler/json_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ namespace ink::compiler::internal
container_t _next_container_index;

list_data _list_meta;
int _ink_version;
};
}
Loading

0 comments on commit 9a70126

Please sign in to comment.