inkcpp_rb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +7 -0
- data/README.md +3 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +10 -0
- data/bin/tapioca +29 -0
- data/ext/inkcpp_rb/extconf.rb +19 -0
- data/ext/inkcpp_rb/inkcpp/.clang-format +99 -0
- data/ext/inkcpp_rb/inkcpp/.github/FUNDING.yml +1 -0
- data/ext/inkcpp_rb/inkcpp/.github/workflows/build.yml +344 -0
- data/ext/inkcpp_rb/inkcpp/.github/workflows/release.yml +49 -0
- data/ext/inkcpp_rb/inkcpp/.gitignore +25 -0
- data/ext/inkcpp_rb/inkcpp/.gitmodules +9 -0
- data/ext/inkcpp_rb/inkcpp/CMakeLists.txt +170 -0
- data/ext/inkcpp_rb/inkcpp/CODE_OF_CONDUCT.md +76 -0
- data/ext/inkcpp_rb/inkcpp/CONTRIBUTING.md +55 -0
- data/ext/inkcpp_rb/inkcpp/Config.cmake.in +2 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example/CMakeLists.txt +13 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example/main.c +38 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example/main.cpp +40 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example/test.ink +8 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example/test.ink.json +1 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/cmake_example.zip +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/InkCPP_DEMO.zip +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/CreateThread.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/HandleChoice.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/ListElementOf.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/MinimalRuntime.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/MinimalThread.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/ObseverChange.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/TagListGetValue.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Documentation/unreal/imgs/YieldResume.png +0 -0
- data/ext/inkcpp_rb/inkcpp/Doxyfile +2825 -0
- data/ext/inkcpp_rb/inkcpp/LICENSE.txt +22 -0
- data/ext/inkcpp_rb/inkcpp/Minimal.runsettings +8 -0
- data/ext/inkcpp_rb/inkcpp/README.md +192 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/CMakeLists.txt +67 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/array.h +481 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/avl_array.h +833 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/casting.h +93 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/choice.cpp +54 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/collections/restorable.cpp +124 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/collections/restorable.h +406 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/container_operations.cpp +52 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/container_operations.h +34 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/executioner.h +179 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/functional.cpp +86 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/functions.cpp +54 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/functions.h +40 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/globals_impl.cpp +289 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/globals_impl.h +149 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/header.cpp +44 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/choice.h +106 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/functional.h +327 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/globals.h +196 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/list.h +187 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/runner.h +291 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/snapshot.h +61 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/story.h +219 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/story_ptr.h +233 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/traits.h +270 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/include/types.h +169 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_impl.cpp +79 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_impl.h +39 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_operations.cpp +276 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_operations.h +356 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_table.cpp +841 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/list_table.h +450 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/numeric_operations.cpp +40 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/numeric_operations.h +529 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/operation_bases.h +164 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/operations.h +100 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/output.cpp +528 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/output.h +153 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/platform.h +22 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/random.h +38 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/runner_impl.cpp +1396 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/runner_impl.h +336 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/simple_restorable_stack.h +335 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/snapshot_impl.cpp +182 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/snapshot_impl.h +91 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/snapshot_interface.h +57 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/stack.cpp +618 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/stack.h +243 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/story_impl.cpp +361 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/story_impl.h +92 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/story_ptr.cpp +75 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/string_operations.cpp +125 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/string_operations.h +67 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/string_table.cpp +149 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/string_table.h +47 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/string_utils.h +207 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/system.cpp +39 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/tuple.hpp +151 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/value.cpp +279 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp/value.h +666 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/CMakeLists.txt +62 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/include/inkcpp.h +393 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/inkcpp.cpp +344 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/inkcpp_c.pc.in +10 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/tests/ExternalFunction.c +56 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/tests/Globals.c +98 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/tests/Lists.c +73 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/tests/Observer.c +36 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_c/tests/Snapshot.c +65 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_cl/CMakeLists.txt +49 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_cl/inkcpp_cl.cpp +215 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_cl/test.cpp +209 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_cl/test.h +8 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/CMakeLists.txt +37 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/binary_emitter.cpp +446 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/binary_emitter.h +70 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/binary_stream.cpp +166 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/binary_stream.h +79 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/command.cpp +107 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/compiler.cpp +96 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/emitter.cpp +62 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/emitter.h +104 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/include/compilation_results.h +22 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/include/compiler.h +44 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/json.hpp +24596 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/json_compiler.cpp +411 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/json_compiler.h +62 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/list_data.cpp +47 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/list_data.h +70 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/reporter.cpp +107 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_compiler/reporter.h +55 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/CMakeLists.txt +19 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/example.py +78 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/src/module.cpp +317 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/conftest.py +53 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/test_ExternalFunctions.py +35 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/test_Globals.py +40 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/test_Lists.py +43 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/test_Observer.py +27 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/tests/test_Snapshot.py +57 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_py/unreal_example.ink +71 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Array.cpp +115 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/CMakeLists.txt +117 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Callstack.cpp +392 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/EmptyStringForDivert.cpp +36 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ExternalFunctionsExecuteProperly.cpp +34 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/FallbackFunction.cpp +77 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Globals.cpp +73 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/InkyJson.cpp +34 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/LabelCondition.cpp +60 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Lists.cpp +144 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/LookaheadSafe.cpp +46 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Main.cpp +7 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/MoveTo.cpp +95 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/NewLines.cpp +76 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/NoEarlyTags.cpp +33 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Observer.cpp +245 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Pointer.cpp +191 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Restorable.cpp +294 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/SpaceAfterBracketChoice.cpp +45 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Stack.cpp +224 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Tags.cpp +131 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ThirdTierChoiceAfterBrackets.cpp +38 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/UTF8.cpp +56 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/Value.cpp +210 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/catch.hpp +17970 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/AHF.ink +7 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ChoiceBracketStory.ink +7 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/EmptyStringForDivert.ink +13 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ExternalFunctionsExecuteProperly.ink +11 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/FallBack.ink +15 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/GlobalStory.ink +9 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/LabelConditionStory.ink +5 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/LinesStory.ink +42 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ListLogicStory.ink +40 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ListStory.ink +8 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/LookaheadSafe.ink +14 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/MoveTo.ink +36 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/NoEarlyTags.ink +19 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ObserverStory.ink +8 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/SimpleStoryFlow.ink +65 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/TagsStory.ink +22 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/TheIntercept.ink +1686 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/ThirdTierChoiceAfterBracketsStory.ink +13 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/UTF-8-demo.txt +212 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/UTF8Story.ink +218 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/simple-1.1.1-inklecate.json +154 -0
- data/ext/inkcpp_rb/inkcpp/inkcpp_test/ink/simple-1.1.1-inky.json +160 -0
- data/ext/inkcpp_rb/inkcpp/notes/ArchitectureNotes.md +54 -0
- data/ext/inkcpp_rb/inkcpp/notes/ListNotes.md +69 -0
- data/ext/inkcpp_rb/inkcpp/notes/OperationNotes.md +35 -0
- data/ext/inkcpp_rb/inkcpp/notes/TagsNotes.md +24 -0
- data/ext/inkcpp_rb/inkcpp/notes/WhitespaceNotes.md +28 -0
- data/ext/inkcpp_rb/inkcpp/proofing/README.md +3 -0
- data/ext/inkcpp_rb/inkcpp/proofing/inkcpp_runtime_driver +12 -0
- data/ext/inkcpp_rb/inkcpp/pyproject.toml +63 -0
- data/ext/inkcpp_rb/inkcpp/setup.py +166 -0
- data/ext/inkcpp_rb/inkcpp/shared/CMakeLists.txt +14 -0
- data/ext/inkcpp_rb/inkcpp/shared/private/command.h +172 -0
- data/ext/inkcpp_rb/inkcpp/shared/private/header.h +46 -0
- data/ext/inkcpp_rb/inkcpp/shared/public/config.h +53 -0
- data/ext/inkcpp_rb/inkcpp/shared/public/system.h +307 -0
- data/ext/inkcpp_rb/inkcpp/shared/public/version.h +14 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestAllSequenceTypes.ink +59 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestArithmetic.ink +17 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestBasicStringLiterals.ink +8 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestBasicTunnel.ink +10 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestBlanksInInlineSequences.ink +51 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestCallStackEvaluation.ink +15 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestChoiceCount.ink +15 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestChoiceDivertsToDone.ink +6 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestChoiceWithBracketsOnly.ink +9 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestCompareDivertTargets.ink +26 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestComplexTunnels.ink +22 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestConditionalChoiceInWeave.ink +19 -0
- data/ext/inkcpp_rb/inkcpp/tests/TestTunnelOnwardsAfterTunnel.ink +17 -0
- data/ext/inkcpp_rb/inkcpp/unreal/CMakeLists.txt +51 -0
- data/ext/inkcpp_rb/inkcpp/unreal/UE_example.ink +92 -0
- data/ext/inkcpp_rb/inkcpp/unreal/blueprint_filter.js +377 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Resources/Icon128.png +0 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkAsset.cpp +47 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp +40 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp +86 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +265 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +239 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +143 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/TagList.cpp +95 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Private/inkcpp.cpp +13 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkAsset.h +50 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h +58 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h +139 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkList.h +102 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +177 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h +30 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +215 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +245 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/TagList.h +77 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h +217 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs +62 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +237 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.h +43 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/Private/inkcpp_editor.cpp +13 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in +24 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/Public/inkcpp_editor.h +9 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/Source/inkcpp_editor/inkcpp_editor.Build.cs +61 -0
- data/ext/inkcpp_rb/inkcpp/unreal/inkcpp/inkcpp.uplugin +44 -0
- data/ext/inkcpp_rb/inkcpp/unreal/render.css +1 -0
- data/ext/inkcpp_rb/inkcpp_rb.cpp +321 -0
- data/inkcpp_rb.gemspec +54 -0
- data/rbi/inkcpp_rb.rbi +211 -0
- data/sorbet/config +4 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/minitest.rbi +119 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/benchmark@0.4.0.rbi +618 -0
- data/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
- data/sorbet/rbi/gems/minitest@5.25.4.rbi +1547 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
- data/sorbet/rbi/gems/parallel@1.26.3.rbi +291 -0
- data/sorbet/rbi/gems/prism@1.3.0.rbi +40040 -0
- data/sorbet/rbi/gems/rake-compiler@1.2.8.rbi +9 -0
- data/sorbet/rbi/gems/rake@13.2.1.rbi +3033 -0
- data/sorbet/rbi/gems/rbi@0.2.2.rbi +4527 -0
- data/sorbet/rbi/gems/rice@4.3.3.rbi +44 -0
- data/sorbet/rbi/gems/spoom@1.5.0.rbi +4932 -0
- data/sorbet/rbi/gems/tapioca@0.16.7.rbi +3611 -0
- data/sorbet/rbi/gems/thor@1.3.2.rbi +4378 -0
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
- data/sorbet/rbi/gems/yard@0.9.37.rbi +18379 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- metadata +400 -0
|
@@ -0,0 +1,1396 @@
|
|
|
1
|
+
/* Copyright (c) 2024 Julian Benda
|
|
2
|
+
*
|
|
3
|
+
* This file is part of inkCPP which is released under MIT license.
|
|
4
|
+
* See file LICENSE.txt or go to
|
|
5
|
+
* https://github.com/JBenda/inkcpp for full license details.
|
|
6
|
+
*/
|
|
7
|
+
#include "runner_impl.h"
|
|
8
|
+
#include "story_impl.h"
|
|
9
|
+
#include "command.h"
|
|
10
|
+
#include "choice.h"
|
|
11
|
+
#include "globals_impl.h"
|
|
12
|
+
#include "header.h"
|
|
13
|
+
#include "string_utils.h"
|
|
14
|
+
#include "snapshot_impl.h"
|
|
15
|
+
#include "system.h"
|
|
16
|
+
#include "value.h"
|
|
17
|
+
|
|
18
|
+
namespace ink::runtime
|
|
19
|
+
{
|
|
20
|
+
const choice* runner_interface::get_choice(size_t index) const
|
|
21
|
+
{
|
|
22
|
+
inkAssert(index < num_choices(), "Choice out of bounds!");
|
|
23
|
+
return begin() + index;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
size_t runner_interface::num_choices() const { return end() - begin(); }
|
|
27
|
+
} // namespace ink::runtime
|
|
28
|
+
|
|
29
|
+
namespace ink::runtime::internal
|
|
30
|
+
{
|
|
31
|
+
|
|
32
|
+
template<>
|
|
33
|
+
value* runner_impl::get_var<runner_impl::Scope::GLOBAL>(hash_t variableName)
|
|
34
|
+
{
|
|
35
|
+
return _globals->get_variable(variableName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
template<>
|
|
39
|
+
value* runner_impl::get_var<runner_impl::Scope::LOCAL>(hash_t variableName)
|
|
40
|
+
{
|
|
41
|
+
value* ret = _stack.get(variableName);
|
|
42
|
+
if (! ret) {
|
|
43
|
+
return nullptr;
|
|
44
|
+
}
|
|
45
|
+
if (ret->type() == value_type::value_pointer) {
|
|
46
|
+
auto [name, ci] = ret->get<value_type::value_pointer>();
|
|
47
|
+
inkAssert(ci == 0, "only Global pointer are allowd on the _stack!");
|
|
48
|
+
return get_var<runner_impl::Scope::GLOBAL>(name);
|
|
49
|
+
}
|
|
50
|
+
return ret;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
template<>
|
|
54
|
+
value* runner_impl::get_var<runner_impl::Scope::NONE>(hash_t variableName)
|
|
55
|
+
{
|
|
56
|
+
value* ret = get_var<Scope::LOCAL>(variableName);
|
|
57
|
+
if (ret) {
|
|
58
|
+
return ret;
|
|
59
|
+
}
|
|
60
|
+
return get_var<Scope::GLOBAL>(variableName);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
template<runner_impl::Scope Hint>
|
|
64
|
+
const value* runner_impl::get_var(hash_t variableName) const
|
|
65
|
+
{
|
|
66
|
+
return const_cast<runner_impl*>(this)->get_var<Hint>(variableName);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
template<>
|
|
70
|
+
void runner_impl::set_var<runner_impl::Scope::GLOBAL>(
|
|
71
|
+
hash_t variableName, const value& val, bool is_redef
|
|
72
|
+
)
|
|
73
|
+
{
|
|
74
|
+
if (is_redef) {
|
|
75
|
+
value* src = _globals->get_variable(variableName);
|
|
76
|
+
_globals->set_variable(variableName, src->redefine(val, _globals->lists()));
|
|
77
|
+
} else {
|
|
78
|
+
_globals->set_variable(variableName, val);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const value* runner_impl::dereference(const value& val)
|
|
83
|
+
{
|
|
84
|
+
if (val.type() != value_type::value_pointer) {
|
|
85
|
+
return &val;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
auto [name, ci] = val.get<value_type::value_pointer>();
|
|
89
|
+
if (ci == 0) {
|
|
90
|
+
return get_var<Scope::GLOBAL>(name);
|
|
91
|
+
}
|
|
92
|
+
return _stack.get_from_frame(ci, name);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
template<>
|
|
96
|
+
void runner_impl::set_var<runner_impl::Scope::LOCAL>(
|
|
97
|
+
hash_t variableName, const value& val, bool is_redef
|
|
98
|
+
)
|
|
99
|
+
{
|
|
100
|
+
if (val.type() == value_type::value_pointer) {
|
|
101
|
+
inkAssert(is_redef == false, "value pointer can only use to initelize variable!");
|
|
102
|
+
auto [name, ci] = val.get<value_type::value_pointer>();
|
|
103
|
+
if (ci == 0) {
|
|
104
|
+
_stack.set(variableName, val);
|
|
105
|
+
} else {
|
|
106
|
+
const value* dref = dereference(val);
|
|
107
|
+
if (dref == nullptr) {
|
|
108
|
+
value v = val;
|
|
109
|
+
auto ref = v.get<value_type::value_pointer>();
|
|
110
|
+
v.set<value_type::value_pointer>(ref.name, 0);
|
|
111
|
+
_stack.set(variableName, v);
|
|
112
|
+
} else {
|
|
113
|
+
_ref_stack.set(variableName, val);
|
|
114
|
+
_stack.set(variableName, *dref);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
if (is_redef) {
|
|
119
|
+
value* src = _stack.get(variableName);
|
|
120
|
+
if (src->type() == value_type::value_pointer) {
|
|
121
|
+
auto [name, ci] = src->get<value_type::value_pointer>();
|
|
122
|
+
inkAssert(ci == 0, "Only global pointer are allowed on _stack!");
|
|
123
|
+
set_var<Scope::GLOBAL>(
|
|
124
|
+
name, get_var<Scope::GLOBAL>(name)->redefine(val, _globals->lists()), true
|
|
125
|
+
);
|
|
126
|
+
} else {
|
|
127
|
+
_stack.set(variableName, src->redefine(val, _globals->lists()));
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
_stack.set(variableName, val);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
template<>
|
|
136
|
+
void runner_impl::set_var<runner_impl::Scope::NONE>(
|
|
137
|
+
hash_t variableName, const value& val, bool is_redef
|
|
138
|
+
)
|
|
139
|
+
{
|
|
140
|
+
inkAssert(is_redef, "define set scopeless variables!");
|
|
141
|
+
if (_stack.get(variableName)) {
|
|
142
|
+
return set_var<Scope::LOCAL>(variableName, val, is_redef);
|
|
143
|
+
} else {
|
|
144
|
+
return set_var<Scope::GLOBAL>(variableName, val, is_redef);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
template<typename T>
|
|
149
|
+
inline T runner_impl::read()
|
|
150
|
+
{
|
|
151
|
+
using header = ink::internal::header;
|
|
152
|
+
// Sanity
|
|
153
|
+
inkAssert(_ptr + sizeof(T) <= _story->end(), "Unexpected EOF in Ink execution");
|
|
154
|
+
|
|
155
|
+
// Read memory
|
|
156
|
+
T val = *( const T* ) _ptr;
|
|
157
|
+
if (_story->get_header().endien == header::endian_types::differ) {
|
|
158
|
+
val = header::swap_bytes(val);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Advance ip
|
|
162
|
+
_ptr += sizeof(T);
|
|
163
|
+
|
|
164
|
+
// Return
|
|
165
|
+
return val;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
template<>
|
|
169
|
+
inline const char* runner_impl::read()
|
|
170
|
+
{
|
|
171
|
+
offset_t str = read<offset_t>();
|
|
172
|
+
return _story->string(str);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
choice& runner_impl::add_choice()
|
|
176
|
+
{
|
|
177
|
+
inkAssert(
|
|
178
|
+
config::maxChoices < 0 || _choices.size() < static_cast<size_t>(config::maxChoices),
|
|
179
|
+
"Ran out of choice storage!"
|
|
180
|
+
);
|
|
181
|
+
return _choices.push();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void runner_impl::clear_choices()
|
|
185
|
+
{
|
|
186
|
+
// TODO: Garbage collection? ? which garbage ?
|
|
187
|
+
_fallback_choice = nullopt;
|
|
188
|
+
_choices.clear();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
void runner_impl::clear_tags()
|
|
192
|
+
{
|
|
193
|
+
_tags.clear();
|
|
194
|
+
_choice_tags_begin = -1;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void runner_impl::jump(ip_t dest, bool record_visits)
|
|
198
|
+
{
|
|
199
|
+
// Optimization: if we are _is_falling, then we can
|
|
200
|
+
// _should be_ able to safely assume that there is nothing to do here. A falling
|
|
201
|
+
// divert should only be taking us from a container to that same container's end point
|
|
202
|
+
// without entering any other containers
|
|
203
|
+
// OR IF if target is same position do nothing
|
|
204
|
+
// could happend if jumping to and of an unnamed container
|
|
205
|
+
if (dest == _ptr) {
|
|
206
|
+
_ptr = dest;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const uint32_t* iter = nullptr;
|
|
211
|
+
container_t id;
|
|
212
|
+
ip_t offset = nullptr;
|
|
213
|
+
size_t comm_end;
|
|
214
|
+
bool reversed = _ptr > dest;
|
|
215
|
+
|
|
216
|
+
if (reversed) {
|
|
217
|
+
comm_end = 0;
|
|
218
|
+
iter = nullptr;
|
|
219
|
+
const ContainerData* old_iter = nullptr;
|
|
220
|
+
const uint32_t* last_comm_iter = nullptr;
|
|
221
|
+
_container.rev_iter(old_iter);
|
|
222
|
+
|
|
223
|
+
// find commen part of old and new stack
|
|
224
|
+
while (_story->iterate_containers(iter, id, offset)) {
|
|
225
|
+
if (old_iter == nullptr || offset >= dest) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
if (old_iter != nullptr && id == old_iter->id) {
|
|
229
|
+
last_comm_iter = iter;
|
|
230
|
+
_container.rev_iter(old_iter);
|
|
231
|
+
++comm_end;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// clear old part from stack
|
|
236
|
+
while (_container.size() > comm_end) {
|
|
237
|
+
_container.pop();
|
|
238
|
+
}
|
|
239
|
+
iter = last_comm_iter;
|
|
240
|
+
|
|
241
|
+
} else {
|
|
242
|
+
iter = nullptr;
|
|
243
|
+
comm_end = _container.size();
|
|
244
|
+
// go to current possition in container list
|
|
245
|
+
while (_story->iterate_containers(iter, id, offset)) {
|
|
246
|
+
if (offset >= _ptr) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
_story->iterate_containers(iter, id, offset, true);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// move to destination and update container stack on the go
|
|
254
|
+
while (_story->iterate_containers(iter, id, offset)) {
|
|
255
|
+
if (offset >= dest) {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
if (_container.empty() || _container.top().id != id) {
|
|
259
|
+
_container.push({.id = id, .offset = offset});
|
|
260
|
+
} else {
|
|
261
|
+
_container.pop();
|
|
262
|
+
if (_container.size() < comm_end) {
|
|
263
|
+
comm_end = _container.size();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
_ptr = dest;
|
|
268
|
+
|
|
269
|
+
// if we jump directly to a named container start, go inside, if its a ONLY_FIRST container
|
|
270
|
+
// it will get visited in the next step
|
|
271
|
+
if (offset == dest && static_cast<Command>(offset[0]) == Command::START_CONTAINER_MARKER) {
|
|
272
|
+
_ptr += 6;
|
|
273
|
+
_container.push({.id = id, .offset = offset});
|
|
274
|
+
if (reversed && comm_end == _container.size() - 1) {
|
|
275
|
+
++comm_end;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// iff all container (until now) are entered at first position
|
|
280
|
+
bool allEnteredAtStart = true;
|
|
281
|
+
ip_t child_position = dest;
|
|
282
|
+
if (record_visits) {
|
|
283
|
+
const ContainerData* iData = nullptr;
|
|
284
|
+
size_t level = _container.size();
|
|
285
|
+
while (_container.iter(iData)
|
|
286
|
+
&& (level > comm_end
|
|
287
|
+
|| _story->container_flag(iData->offset) & CommandFlag::CONTAINER_MARKER_ONLY_FIRST)
|
|
288
|
+
) {
|
|
289
|
+
auto parrent_offset = iData->offset;
|
|
290
|
+
inkAssert(child_position >= parrent_offset, "Container stack order is broken");
|
|
291
|
+
// 6 == len of START_CONTAINER_SIGNAL, if its 6 bytes behind the container it is a unnnamed
|
|
292
|
+
// subcontainers first child check if child_positino is the first child of current container
|
|
293
|
+
allEnteredAtStart = allEnteredAtStart && ((child_position - parrent_offset) <= 6);
|
|
294
|
+
child_position = parrent_offset;
|
|
295
|
+
_globals->visit(iData->id, allEnteredAtStart);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
template<frame_type type>
|
|
301
|
+
void runner_impl::start_frame(uint32_t target)
|
|
302
|
+
{
|
|
303
|
+
if constexpr (type == frame_type::function) {
|
|
304
|
+
// add a function start marker
|
|
305
|
+
_output << values::func_start;
|
|
306
|
+
}
|
|
307
|
+
// Push next address onto the callstack
|
|
308
|
+
{
|
|
309
|
+
size_t address = _ptr - _story->instructions();
|
|
310
|
+
_stack.push_frame<type>(address, _evaluation_mode);
|
|
311
|
+
_ref_stack.push_frame<type>(address, _evaluation_mode);
|
|
312
|
+
}
|
|
313
|
+
_evaluation_mode = false; // unset eval mode when enter function or tunnel
|
|
314
|
+
|
|
315
|
+
// Do the jump
|
|
316
|
+
inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!");
|
|
317
|
+
jump(_story->instructions() + target, true);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
frame_type runner_impl::execute_return()
|
|
321
|
+
{
|
|
322
|
+
// Pop the callstack
|
|
323
|
+
_ref_stack.fetch_values(_stack);
|
|
324
|
+
frame_type type;
|
|
325
|
+
offset_t offset = _stack.pop_frame(&type, _evaluation_mode);
|
|
326
|
+
_ref_stack.push_values(_stack);
|
|
327
|
+
{
|
|
328
|
+
frame_type t;
|
|
329
|
+
bool eval;
|
|
330
|
+
// TODO: write all refs to new frame
|
|
331
|
+
offset_t o = _ref_stack.pop_frame(&t, eval);
|
|
332
|
+
inkAssert(
|
|
333
|
+
o == offset && t == type && eval == _evaluation_mode,
|
|
334
|
+
"_ref_stack and _stack should be in frame sync!"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// SPECIAL: On function, do a trim
|
|
339
|
+
if (type == frame_type::function) {
|
|
340
|
+
_output << values::func_end;
|
|
341
|
+
} else if (type == frame_type::tunnel) {
|
|
342
|
+
// if we return but there is a divert target on top of
|
|
343
|
+
// the evaluation stack, we should follow this instead
|
|
344
|
+
// inkproof: I060
|
|
345
|
+
if (! _eval.is_empty() && _eval.top().type() == value_type::divert) {
|
|
346
|
+
start_frame<frame_type::tunnel>(_eval.pop().get<value_type::divert>());
|
|
347
|
+
return type;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
// Jump to the old offset
|
|
353
|
+
inkAssert(
|
|
354
|
+
_story->instructions() + offset < _story->end(),
|
|
355
|
+
"Callstack return is outside bounds of story!"
|
|
356
|
+
);
|
|
357
|
+
jump(_story->instructions() + offset, false);
|
|
358
|
+
|
|
359
|
+
// Return frame type
|
|
360
|
+
return type;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
runner_impl::runner_impl(const story_impl* data, globals global)
|
|
364
|
+
: _story(data)
|
|
365
|
+
, _globals(global.cast<globals_impl>())
|
|
366
|
+
, _operations(
|
|
367
|
+
global.cast<globals_impl>()->strings(), global.cast<globals_impl>()->lists(), _rng,
|
|
368
|
+
*global.cast<globals_impl>(), *data, static_cast<const runner_interface&>(*this)
|
|
369
|
+
)
|
|
370
|
+
, _backup(nullptr)
|
|
371
|
+
, _done(nullptr)
|
|
372
|
+
, _choices()
|
|
373
|
+
, _container(ContainerData{})
|
|
374
|
+
, _rng(time(NULL))
|
|
375
|
+
{
|
|
376
|
+
_ptr = _story->instructions();
|
|
377
|
+
_evaluation_mode = false;
|
|
378
|
+
_choice_tags_begin = -1;
|
|
379
|
+
|
|
380
|
+
// register with globals
|
|
381
|
+
_globals->add_runner(this);
|
|
382
|
+
if (_globals->lists()) {
|
|
383
|
+
_output.set_list_meta(_globals->lists());
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// initialize globals if necessary
|
|
387
|
+
if (! _globals->are_globals_initialized()) {
|
|
388
|
+
_globals->initialize_globals(this);
|
|
389
|
+
|
|
390
|
+
// Set us back to the beginning of the story
|
|
391
|
+
reset();
|
|
392
|
+
_ptr = _story->instructions();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
runner_impl::~runner_impl()
|
|
397
|
+
{
|
|
398
|
+
// unregister with globals
|
|
399
|
+
if (_globals.is_valid()) {
|
|
400
|
+
_globals->remove_runner(this);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
#ifdef INK_ENABLE_STL
|
|
405
|
+
std::string runner_impl::getline()
|
|
406
|
+
{
|
|
407
|
+
advance_line();
|
|
408
|
+
std::string result = _output.get();
|
|
409
|
+
if (! has_choices() && _fallback_choice) {
|
|
410
|
+
choose(~0);
|
|
411
|
+
}
|
|
412
|
+
// Return result
|
|
413
|
+
inkAssert(_output.is_empty(), "Output should be empty after getline!");
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
void runner_impl::getline(std::ostream& out)
|
|
418
|
+
{
|
|
419
|
+
// Advance interpreter one line
|
|
420
|
+
advance_line();
|
|
421
|
+
// Write into out
|
|
422
|
+
out << _output;
|
|
423
|
+
if (! has_choices() && _fallback_choice) {
|
|
424
|
+
choose(~0);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Make sure we read everything
|
|
428
|
+
inkAssert(_output.is_empty(), "Output should be empty after getline!");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
std::string runner_impl::getall()
|
|
432
|
+
{
|
|
433
|
+
// Advance interpreter until we're stopped
|
|
434
|
+
std::stringstream str;
|
|
435
|
+
while (can_continue()) {
|
|
436
|
+
getline(str);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Read output into std::string
|
|
440
|
+
|
|
441
|
+
// Return result
|
|
442
|
+
inkAssert(_output.is_empty(), "Output should be empty after getall!");
|
|
443
|
+
return str.str();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void runner_impl::getall(std::ostream& out)
|
|
447
|
+
{
|
|
448
|
+
// Advance interpreter until we're stopped
|
|
449
|
+
while (can_continue()) {
|
|
450
|
+
advance_line();
|
|
451
|
+
// Send output into stream
|
|
452
|
+
out << _output;
|
|
453
|
+
}
|
|
454
|
+
// Return result
|
|
455
|
+
inkAssert(_output.is_empty(), "Output should be empty after getall!");
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
#endif
|
|
459
|
+
#ifdef INK_ENABLE_UNREAL
|
|
460
|
+
FString runner_impl::getline()
|
|
461
|
+
{
|
|
462
|
+
clear_tags();
|
|
463
|
+
advance_line();
|
|
464
|
+
FString result(ANSI_TO_TCHAR(_output.get_alloc(_globals->strings(), _globals->lists())));
|
|
465
|
+
|
|
466
|
+
if (! has_choices() && _fallback_choice) {
|
|
467
|
+
choose(~0);
|
|
468
|
+
}
|
|
469
|
+
// Return result
|
|
470
|
+
inkAssert(_output.is_empty(), "Output should be empty after getline!");
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
#endif
|
|
474
|
+
|
|
475
|
+
void runner_impl::advance_line()
|
|
476
|
+
{
|
|
477
|
+
// Step while we still have instructions to execute
|
|
478
|
+
while (_ptr != nullptr) {
|
|
479
|
+
// Stop if we hit a new line
|
|
480
|
+
if (line_step()) {
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// can be in save state becaues of choice
|
|
486
|
+
// Garbage collection TODO: How often do we want to do this?
|
|
487
|
+
if (_saved) {
|
|
488
|
+
restore();
|
|
489
|
+
}
|
|
490
|
+
_globals->gc();
|
|
491
|
+
if (_output.saved()) {
|
|
492
|
+
_output.restore();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
bool runner_impl::can_continue() const { return _ptr != nullptr && ! has_choices(); }
|
|
497
|
+
|
|
498
|
+
void runner_impl::choose(size_t index)
|
|
499
|
+
{
|
|
500
|
+
if (has_choices()) {
|
|
501
|
+
inkAssert(index < _choices.size(), "Choice index out of range");
|
|
502
|
+
}
|
|
503
|
+
_globals->turn();
|
|
504
|
+
// Get the choice
|
|
505
|
+
const auto& c = has_choices() ? _choices[index] : _fallback_choice.value();
|
|
506
|
+
|
|
507
|
+
// Get its thread
|
|
508
|
+
thread_t choiceThread = c._thread;
|
|
509
|
+
|
|
510
|
+
// Figure out where our previous pointer was for that thread
|
|
511
|
+
ip_t prev = nullptr;
|
|
512
|
+
if (choiceThread == ~0) {
|
|
513
|
+
prev = _done;
|
|
514
|
+
} else {
|
|
515
|
+
prev = _threads.get(choiceThread);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Make sure we have a previous pointer
|
|
519
|
+
inkAssert(prev != nullptr, "No 'done' point recorded before finishing choice output");
|
|
520
|
+
|
|
521
|
+
// Move to the previous pointer so we track our movements correctly
|
|
522
|
+
jump(prev, false);
|
|
523
|
+
_done = nullptr;
|
|
524
|
+
|
|
525
|
+
// Collapse callstacks to the correct thread
|
|
526
|
+
_stack.collapse_to_thread(choiceThread);
|
|
527
|
+
_ref_stack.collapse_to_thread(choiceThread);
|
|
528
|
+
_threads.clear();
|
|
529
|
+
|
|
530
|
+
// Jump to destination and clear choice list
|
|
531
|
+
jump(_story->instructions() + c.path(), true);
|
|
532
|
+
clear_choices();
|
|
533
|
+
clear_tags();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
void runner_impl::getline_silent()
|
|
537
|
+
{
|
|
538
|
+
// advance and clear output stream
|
|
539
|
+
advance_line();
|
|
540
|
+
_output.clear();
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
bool runner_impl::has_tags() const { return num_tags() > 0; }
|
|
544
|
+
|
|
545
|
+
size_t runner_impl::num_tags() const
|
|
546
|
+
{
|
|
547
|
+
return _choice_tags_begin < 0 ? _tags.size() : _choice_tags_begin;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const char* runner_impl::get_tag(size_t index) const
|
|
551
|
+
{
|
|
552
|
+
inkAssert(index < _tags.size(), "Tag index exceeds _num_tags");
|
|
553
|
+
return _tags[index];
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
snapshot* runner_impl::create_snapshot() const { return _globals->create_snapshot(); }
|
|
557
|
+
|
|
558
|
+
size_t runner_impl::snap(unsigned char* data, snapper& snapper) const
|
|
559
|
+
{
|
|
560
|
+
unsigned char* ptr = data;
|
|
561
|
+
bool should_write = data != nullptr;
|
|
562
|
+
snapper.current_runner_tags = _tags[0].ptr();
|
|
563
|
+
std::uintptr_t offset = _ptr != nullptr ? _ptr - _story->instructions() : 0;
|
|
564
|
+
ptr = snap_write(ptr, offset, should_write);
|
|
565
|
+
offset = _backup - _story->instructions();
|
|
566
|
+
ptr = snap_write(ptr, offset, should_write);
|
|
567
|
+
offset = _done - _story->instructions();
|
|
568
|
+
ptr = snap_write(ptr, offset, should_write);
|
|
569
|
+
ptr = snap_write(ptr, _rng.get_state(), should_write);
|
|
570
|
+
ptr = snap_write(ptr, _evaluation_mode, should_write);
|
|
571
|
+
ptr = snap_write(ptr, _string_mode, should_write);
|
|
572
|
+
ptr = snap_write(ptr, _saved_evaluation_mode, should_write);
|
|
573
|
+
ptr = snap_write(ptr, _saved, should_write);
|
|
574
|
+
ptr = snap_write(ptr, _is_falling, should_write);
|
|
575
|
+
ptr += _output.snap(data ? ptr : nullptr, snapper);
|
|
576
|
+
ptr += _stack.snap(data ? ptr : nullptr, snapper);
|
|
577
|
+
ptr += _ref_stack.snap(data ? ptr : nullptr, snapper);
|
|
578
|
+
ptr += _eval.snap(data ? ptr : nullptr, snapper);
|
|
579
|
+
ptr = snap_write(ptr, _choice_tags_begin, should_write);
|
|
580
|
+
ptr += _tags.snap(data ? ptr : nullptr, snapper);
|
|
581
|
+
ptr += _container.snap(data ? ptr : nullptr, snapper);
|
|
582
|
+
ptr += _threads.snap(data ? ptr : nullptr, snapper);
|
|
583
|
+
ptr = snap_write(ptr, _fallback_choice.has_value(), should_write);
|
|
584
|
+
if (_fallback_choice) {
|
|
585
|
+
ptr += _fallback_choice.value().snap(data ? ptr : nullptr, snapper);
|
|
586
|
+
}
|
|
587
|
+
ptr += _choices.snap(data ? ptr : nullptr, snapper);
|
|
588
|
+
return ptr - data;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const unsigned char* runner_impl::snap_load(const unsigned char* data, loader& loader)
|
|
592
|
+
{
|
|
593
|
+
auto ptr = data;
|
|
594
|
+
std::uintptr_t offset;
|
|
595
|
+
ptr = snap_read(ptr, offset);
|
|
596
|
+
_ptr = offset == 0 ? nullptr : _story->instructions() + offset;
|
|
597
|
+
ptr = snap_read(ptr, offset);
|
|
598
|
+
_backup = _story->instructions() + offset;
|
|
599
|
+
ptr = snap_read(ptr, offset);
|
|
600
|
+
_done = _story->instructions() + offset;
|
|
601
|
+
int32_t seed;
|
|
602
|
+
ptr = snap_read(ptr, seed);
|
|
603
|
+
_rng.srand(seed);
|
|
604
|
+
ptr = snap_read(ptr, _evaluation_mode);
|
|
605
|
+
ptr = snap_read(ptr, _string_mode);
|
|
606
|
+
ptr = snap_read(ptr, _saved_evaluation_mode);
|
|
607
|
+
ptr = snap_read(ptr, _saved);
|
|
608
|
+
ptr = snap_read(ptr, _is_falling);
|
|
609
|
+
ptr = _output.snap_load(ptr, loader);
|
|
610
|
+
ptr = _stack.snap_load(ptr, loader);
|
|
611
|
+
ptr = _ref_stack.snap_load(ptr, loader);
|
|
612
|
+
ptr = _eval.snap_load(ptr, loader);
|
|
613
|
+
int choice_tags_begin;
|
|
614
|
+
ptr = snap_read(ptr, choice_tags_begin);
|
|
615
|
+
_choice_tags_begin = choice_tags_begin;
|
|
616
|
+
ptr = _tags.snap_load(ptr, loader);
|
|
617
|
+
loader.current_runner_tags = _tags[0].ptr();
|
|
618
|
+
ptr = _container.snap_load(ptr, loader);
|
|
619
|
+
ptr = _threads.snap_load(ptr, loader);
|
|
620
|
+
bool has_fallback_choice;
|
|
621
|
+
ptr = snap_read(ptr, has_fallback_choice);
|
|
622
|
+
_fallback_choice = nullopt;
|
|
623
|
+
if (has_fallback_choice) {
|
|
624
|
+
_fallback_choice.emplace();
|
|
625
|
+
ptr = _fallback_choice.value().snap_load(ptr, loader);
|
|
626
|
+
}
|
|
627
|
+
ptr = _choices.snap_load(ptr, loader);
|
|
628
|
+
return ptr;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
#ifdef INK_ENABLE_CSTD
|
|
632
|
+
const char* runner_impl::getline_alloc()
|
|
633
|
+
{
|
|
634
|
+
advance_line();
|
|
635
|
+
const char* res = _output.get_alloc(_globals->strings(), _globals->lists());
|
|
636
|
+
if (! has_choices() && _fallback_choice) {
|
|
637
|
+
choose(~0);
|
|
638
|
+
}
|
|
639
|
+
inkAssert(_output.is_empty(), "Output should be empty after getline!");
|
|
640
|
+
return res;
|
|
641
|
+
}
|
|
642
|
+
#endif
|
|
643
|
+
|
|
644
|
+
bool runner_impl::move_to(hash_t path)
|
|
645
|
+
{
|
|
646
|
+
// find the path
|
|
647
|
+
ip_t destination = _story->find_offset_for(path);
|
|
648
|
+
if (destination == nullptr) {
|
|
649
|
+
// TODO: Error state?
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Clear state and move to destination
|
|
654
|
+
reset();
|
|
655
|
+
_ptr = _story->instructions();
|
|
656
|
+
jump(destination, false);
|
|
657
|
+
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
void runner_impl::internal_bind(hash_t name, internal::function_base* function)
|
|
662
|
+
{
|
|
663
|
+
_functions.add(name, function);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
runner_impl::change_type runner_impl::detect_change() const
|
|
667
|
+
{
|
|
668
|
+
// Check if the old newline is still present (hasn't been glu'd) and
|
|
669
|
+
// if there is new text (non-whitespace) in the stream since saving
|
|
670
|
+
bool stillHasNewline = _output.saved_ends_with(value_type::newline);
|
|
671
|
+
bool hasAddedNewText = _output.text_past_save() || _tags.last_size() < num_tags();
|
|
672
|
+
|
|
673
|
+
// Newline is still there and there's no new text
|
|
674
|
+
if (stillHasNewline && ! hasAddedNewText) {
|
|
675
|
+
return change_type::no_change;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// If the newline is gone, we got glue'd. Continue as if we never had that newline
|
|
679
|
+
if (! stillHasNewline) {
|
|
680
|
+
return change_type::newline_removed;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// If there's new text content, we went too far
|
|
684
|
+
if (hasAddedNewText) {
|
|
685
|
+
return change_type::extended_past_newline;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
inkFail("Invalid change detction. Never should be here!");
|
|
689
|
+
return change_type::no_change;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
bool runner_impl::line_step()
|
|
693
|
+
{
|
|
694
|
+
// Step the interpreter
|
|
695
|
+
step();
|
|
696
|
+
|
|
697
|
+
// If we're not within string evaluation
|
|
698
|
+
if (! _output.has_marker()) {
|
|
699
|
+
// If we have a saved state after a previous newline
|
|
700
|
+
// don't do this if we behind choice
|
|
701
|
+
if (_saved && ! has_choices() && ! _fallback_choice) {
|
|
702
|
+
// Check for changes in the output stream
|
|
703
|
+
switch (detect_change()) {
|
|
704
|
+
case change_type::extended_past_newline:
|
|
705
|
+
// We've gone too far. Restore to before we moved past the newline and return that we are
|
|
706
|
+
// done
|
|
707
|
+
restore();
|
|
708
|
+
return true;
|
|
709
|
+
case change_type::newline_removed:
|
|
710
|
+
// Newline was removed. Proceed as if we never hit it
|
|
711
|
+
forget();
|
|
712
|
+
break;
|
|
713
|
+
case change_type::no_change: break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// If we're on a newline
|
|
718
|
+
if (_output.ends_with(value_type::newline)) {
|
|
719
|
+
// Unless we are out of content, we are going to try
|
|
720
|
+
// to continue a little further. This is to check for
|
|
721
|
+
// glue (which means there is potentially more content
|
|
722
|
+
// in this line) OR for non-text content such as choices.
|
|
723
|
+
if (_ptr != nullptr) {
|
|
724
|
+
// Save a snapshot of the current runtime state so we
|
|
725
|
+
// can return here if we end up hitting a new line
|
|
726
|
+
// forget();
|
|
727
|
+
if (! _saved) {
|
|
728
|
+
save();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
// Otherwise, make sure we don't have any snapshots hanging around
|
|
732
|
+
// expect we are in choice handleing
|
|
733
|
+
else if (! has_choices() && ! _fallback_choice) {
|
|
734
|
+
forget();
|
|
735
|
+
} else {
|
|
736
|
+
_output.forget();
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
void runner_impl::step()
|
|
745
|
+
{
|
|
746
|
+
#ifndef INK_ENABLE_UNREAL
|
|
747
|
+
try
|
|
748
|
+
#endif
|
|
749
|
+
{
|
|
750
|
+
inkAssert(_ptr != nullptr, "Can not step! Do not have a valid pointer");
|
|
751
|
+
|
|
752
|
+
// Load current command
|
|
753
|
+
Command cmd = read<Command>();
|
|
754
|
+
CommandFlag flag = read<CommandFlag>();
|
|
755
|
+
|
|
756
|
+
// If we're falling and we hit a non-fallthrough command, stop the fall.
|
|
757
|
+
if (_is_falling
|
|
758
|
+
&& ! (
|
|
759
|
+
(cmd == Command::DIVERT && flag & CommandFlag::DIVERT_IS_FALLTHROUGH)
|
|
760
|
+
|| cmd == Command::END_CONTAINER_MARKER
|
|
761
|
+
)) {
|
|
762
|
+
_is_falling = false;
|
|
763
|
+
set_done_ptr(nullptr);
|
|
764
|
+
}
|
|
765
|
+
if (cmd >= Command::OP_BEGIN && cmd < Command::OP_END) {
|
|
766
|
+
_operations(cmd, _eval);
|
|
767
|
+
} else {
|
|
768
|
+
switch (cmd) {
|
|
769
|
+
// == Value Commands ==
|
|
770
|
+
case Command::STR: {
|
|
771
|
+
const char* str = read<const char*>();
|
|
772
|
+
if (_evaluation_mode) {
|
|
773
|
+
_eval.push(value{}.set<value_type::string>(str));
|
|
774
|
+
} else {
|
|
775
|
+
_output << value{}.set<value_type::string>(str);
|
|
776
|
+
}
|
|
777
|
+
} break;
|
|
778
|
+
case Command::INT: {
|
|
779
|
+
int val = read<int>();
|
|
780
|
+
if (_evaluation_mode) {
|
|
781
|
+
_eval.push(value{}.set<value_type::int32>(val));
|
|
782
|
+
}
|
|
783
|
+
// TEST-CASE B006 don't print integers
|
|
784
|
+
} break;
|
|
785
|
+
case Command::BOOL: {
|
|
786
|
+
bool val = read<int>() ? true : false;
|
|
787
|
+
if (_evaluation_mode) {
|
|
788
|
+
_eval.push(value{}.set<value_type::boolean>(val));
|
|
789
|
+
} else {
|
|
790
|
+
_output << value{}.set<value_type::boolean>(val);
|
|
791
|
+
}
|
|
792
|
+
} break;
|
|
793
|
+
case Command::FLOAT: {
|
|
794
|
+
float val = read<float>();
|
|
795
|
+
if (_evaluation_mode) {
|
|
796
|
+
_eval.push(value{}.set<value_type::float32>(val));
|
|
797
|
+
}
|
|
798
|
+
// TEST-CASE B006 don't print floats
|
|
799
|
+
} break;
|
|
800
|
+
case Command::VALUE_POINTER: {
|
|
801
|
+
hash_t val = read<hash_t>();
|
|
802
|
+
if (_evaluation_mode) {
|
|
803
|
+
_eval.push(value{}.set<value_type::value_pointer>(val, static_cast<char>(flag) - 1));
|
|
804
|
+
} else {
|
|
805
|
+
inkFail("never conciderd what should happend here! (value pointer print)");
|
|
806
|
+
}
|
|
807
|
+
} break;
|
|
808
|
+
case Command::LIST: {
|
|
809
|
+
list_table::list list(read<int>());
|
|
810
|
+
if (_evaluation_mode) {
|
|
811
|
+
_eval.push(value{}.set<value_type::list>(list));
|
|
812
|
+
} else {
|
|
813
|
+
char* str = _globals->strings().create(_globals->lists().stringLen(list) + 1);
|
|
814
|
+
_globals->lists().toString(str, list)[0] = 0;
|
|
815
|
+
_output << value{}.set<value_type::string>(str);
|
|
816
|
+
}
|
|
817
|
+
} break;
|
|
818
|
+
case Command::DIVERT_VAL: {
|
|
819
|
+
inkAssert(_evaluation_mode, "Can not push divert value into the output stream!");
|
|
820
|
+
|
|
821
|
+
// Push the divert target onto the stack
|
|
822
|
+
uint32_t target = read<uint32_t>();
|
|
823
|
+
_eval.push(value{}.set<value_type::divert>(target));
|
|
824
|
+
} break;
|
|
825
|
+
case Command::NEWLINE: {
|
|
826
|
+
if (_evaluation_mode) {
|
|
827
|
+
_eval.push(values::newline);
|
|
828
|
+
} else {
|
|
829
|
+
if (! _output.ends_with(value_type::newline)) {
|
|
830
|
+
_output << values::newline;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
} break;
|
|
834
|
+
case Command::GLUE: {
|
|
835
|
+
if (_evaluation_mode) {
|
|
836
|
+
_eval.push(values::glue);
|
|
837
|
+
} else {
|
|
838
|
+
_output << values::glue;
|
|
839
|
+
}
|
|
840
|
+
} break;
|
|
841
|
+
case Command::VOID: {
|
|
842
|
+
if (_evaluation_mode) {
|
|
843
|
+
_eval.push(values::null); // TODO: void type?
|
|
844
|
+
}
|
|
845
|
+
} break;
|
|
846
|
+
|
|
847
|
+
// == Divert commands
|
|
848
|
+
case Command::DIVERT: {
|
|
849
|
+
// Find divert address
|
|
850
|
+
uint32_t target = read<uint32_t>();
|
|
851
|
+
|
|
852
|
+
// Check for condition
|
|
853
|
+
if (flag & CommandFlag::DIVERT_HAS_CONDITION && ! _eval.pop().truthy(_globals->lists())) {
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// SPECIAL: Fallthrough divert. We're starting to fall out of containers
|
|
858
|
+
if (flag & CommandFlag::DIVERT_IS_FALLTHROUGH && ! _is_falling) {
|
|
859
|
+
// Record the position of the instruction pointer at the first fallthrough.
|
|
860
|
+
// We'll use this if we run out of content and hit an implied "done" to restore
|
|
861
|
+
// our position when a choice is chosen. See ::choose
|
|
862
|
+
set_done_ptr(_ptr);
|
|
863
|
+
_is_falling = true;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// If we're falling out of the story, then we're hitting an implied done
|
|
867
|
+
if (_is_falling && _story->instructions() + target == _story->end()) {
|
|
868
|
+
// Wait! We may be returning from a function!
|
|
869
|
+
frame_type type;
|
|
870
|
+
if (_stack.has_frame(&type)
|
|
871
|
+
&& type == frame_type::function) // implicit return is only for functions
|
|
872
|
+
{
|
|
873
|
+
// push null and return
|
|
874
|
+
_eval.push(values::null);
|
|
875
|
+
|
|
876
|
+
// HACK
|
|
877
|
+
_ptr += sizeof(Command) + sizeof(CommandFlag);
|
|
878
|
+
execute_return();
|
|
879
|
+
} else {
|
|
880
|
+
on_done(false);
|
|
881
|
+
}
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Do the jump
|
|
886
|
+
inkAssert(
|
|
887
|
+
_story->instructions() + target < _story->end(), "Diverting past end of story data!"
|
|
888
|
+
);
|
|
889
|
+
jump(_story->instructions() + target, true);
|
|
890
|
+
} break;
|
|
891
|
+
case Command::DIVERT_TO_VARIABLE: {
|
|
892
|
+
// Get variable value
|
|
893
|
+
hash_t variable = read<hash_t>();
|
|
894
|
+
|
|
895
|
+
// Check for condition
|
|
896
|
+
if (flag & CommandFlag::DIVERT_HAS_CONDITION && ! _eval.pop().truthy(_globals->lists())) {
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const value* val = get_var(variable);
|
|
901
|
+
inkAssert(val, "Jump destiniation needs to be defined!");
|
|
902
|
+
|
|
903
|
+
// Move to location
|
|
904
|
+
jump(_story->instructions() + val->get<value_type::divert>(), true);
|
|
905
|
+
inkAssert(_ptr < _story->end(), "Diverted past end of story data!");
|
|
906
|
+
} break;
|
|
907
|
+
|
|
908
|
+
// == Terminal commands
|
|
909
|
+
case Command::DONE: on_done(true); break;
|
|
910
|
+
|
|
911
|
+
case Command::END: _ptr = nullptr; break;
|
|
912
|
+
|
|
913
|
+
// == Tunneling
|
|
914
|
+
case Command::TUNNEL: {
|
|
915
|
+
uint32_t target;
|
|
916
|
+
// Find divert address
|
|
917
|
+
if (flag & CommandFlag::TUNNEL_TO_VARIABLE) {
|
|
918
|
+
hash_t var_name = read<hash_t>();
|
|
919
|
+
const value* val = get_var(var_name);
|
|
920
|
+
inkAssert(val != nullptr, "Variable containing tunnel target could not be found!");
|
|
921
|
+
target = val->get<value_type::divert>();
|
|
922
|
+
} else {
|
|
923
|
+
target = read<uint32_t>();
|
|
924
|
+
}
|
|
925
|
+
start_frame<frame_type::tunnel>(target);
|
|
926
|
+
} break;
|
|
927
|
+
case Command::FUNCTION: {
|
|
928
|
+
uint32_t target;
|
|
929
|
+
// Find divert address
|
|
930
|
+
if (flag & CommandFlag::FUNCTION_TO_VARIABLE) {
|
|
931
|
+
hash_t var_name = read<hash_t>();
|
|
932
|
+
const value* val = get_var(var_name);
|
|
933
|
+
inkAssert(val != nullptr, "Varibale containing function could not be found!");
|
|
934
|
+
target = val->get<value_type::divert>();
|
|
935
|
+
} else {
|
|
936
|
+
target = read<uint32_t>();
|
|
937
|
+
}
|
|
938
|
+
if (! (flag & CommandFlag::FALLBACK_FUNCTION)) {
|
|
939
|
+
start_frame<frame_type::function>(target);
|
|
940
|
+
} else {
|
|
941
|
+
inkAssert(! _eval.is_empty(), "fallback function but no function call before?");
|
|
942
|
+
if (_eval.top_value().type() == value_type::ex_fn_not_found) {
|
|
943
|
+
_eval.pop();
|
|
944
|
+
inkAssert(
|
|
945
|
+
target != 0,
|
|
946
|
+
"Exetrnal function was not binded, and no fallback function provided!"
|
|
947
|
+
);
|
|
948
|
+
start_frame<frame_type::function>(target);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
} break;
|
|
952
|
+
case Command::TUNNEL_RETURN:
|
|
953
|
+
case Command::FUNCTION_RETURN: {
|
|
954
|
+
execute_return();
|
|
955
|
+
} break;
|
|
956
|
+
|
|
957
|
+
case Command::THREAD: {
|
|
958
|
+
// Push a thread frame so we can return easily
|
|
959
|
+
// TODO We push ahead of a single divert. Is that correct in all cases....?????
|
|
960
|
+
auto returnTo = _ptr + CommandSize<uint32_t>;
|
|
961
|
+
_stack.push_frame<frame_type::thread>(
|
|
962
|
+
returnTo - _story->instructions(), _evaluation_mode
|
|
963
|
+
);
|
|
964
|
+
_ref_stack.push_frame<frame_type::thread>(
|
|
965
|
+
returnTo - _story->instructions(), _evaluation_mode
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
// Fork a new thread on the callstack
|
|
969
|
+
thread_t thread = _stack.fork_thread();
|
|
970
|
+
{
|
|
971
|
+
thread_t t = _ref_stack.fork_thread();
|
|
972
|
+
inkAssert(t == thread, "ref_stack and stack should be in sync!");
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Push that thread onto our thread stack
|
|
976
|
+
_threads.push(thread);
|
|
977
|
+
} break;
|
|
978
|
+
|
|
979
|
+
// == set temporärie variable
|
|
980
|
+
case Command::DEFINE_TEMP: {
|
|
981
|
+
hash_t variableName = read<hash_t>();
|
|
982
|
+
bool is_redef = flag & CommandFlag::ASSIGNMENT_IS_REDEFINE;
|
|
983
|
+
|
|
984
|
+
// Get the top value and put it into the variable
|
|
985
|
+
value v = _eval.pop();
|
|
986
|
+
set_var<Scope::LOCAL>(variableName, v, is_redef);
|
|
987
|
+
} break;
|
|
988
|
+
|
|
989
|
+
case Command::SET_VARIABLE: {
|
|
990
|
+
hash_t variableName = read<hash_t>();
|
|
991
|
+
|
|
992
|
+
// Check if it's a redefinition (not yet used, seems important for pointers later?)
|
|
993
|
+
bool is_redef = flag & CommandFlag::ASSIGNMENT_IS_REDEFINE;
|
|
994
|
+
|
|
995
|
+
// If not, we're setting a global (temporary variables are explicitely defined as such,
|
|
996
|
+
// where globals are defined using SET_VARIABLE).
|
|
997
|
+
value val = _eval.pop();
|
|
998
|
+
if (is_redef) {
|
|
999
|
+
set_var(variableName, val, is_redef);
|
|
1000
|
+
} else {
|
|
1001
|
+
set_var<Scope::GLOBAL>(variableName, val, is_redef);
|
|
1002
|
+
}
|
|
1003
|
+
} break;
|
|
1004
|
+
|
|
1005
|
+
// == Function calls
|
|
1006
|
+
case Command::CALL_EXTERNAL: {
|
|
1007
|
+
// Read function name
|
|
1008
|
+
hash_t functionName = read<hash_t>();
|
|
1009
|
+
|
|
1010
|
+
// Interpret flag as argument count
|
|
1011
|
+
int numArguments = ( int ) flag;
|
|
1012
|
+
|
|
1013
|
+
// find and execute. will automatically push a valid if applicable
|
|
1014
|
+
auto* fn = _functions.find(functionName);
|
|
1015
|
+
if (fn == nullptr) {
|
|
1016
|
+
_eval.push(values::ex_fn_not_found);
|
|
1017
|
+
} else if (_output.saved() && _output.saved_ends_with(value_type::newline) && ! fn->lookaheadSafe()) {
|
|
1018
|
+
// TODO: seperate token?
|
|
1019
|
+
_output.append(values::null);
|
|
1020
|
+
} else {
|
|
1021
|
+
fn->call(&_eval, numArguments, _globals->strings(), _globals->lists());
|
|
1022
|
+
}
|
|
1023
|
+
} break;
|
|
1024
|
+
|
|
1025
|
+
// == Evaluation stack
|
|
1026
|
+
case Command::START_EVAL: _evaluation_mode = true; break;
|
|
1027
|
+
case Command::END_EVAL:
|
|
1028
|
+
_evaluation_mode = false;
|
|
1029
|
+
|
|
1030
|
+
// Assert stack is empty? Is that necessary?
|
|
1031
|
+
break;
|
|
1032
|
+
case Command::OUTPUT: {
|
|
1033
|
+
value v = _eval.pop();
|
|
1034
|
+
_output << v;
|
|
1035
|
+
} break;
|
|
1036
|
+
case Command::POP: _eval.pop(); break;
|
|
1037
|
+
case Command::DUPLICATE: _eval.push(_eval.top_value()); break;
|
|
1038
|
+
case Command::PUSH_VARIABLE_VALUE: {
|
|
1039
|
+
// Try to find in local stack
|
|
1040
|
+
hash_t variableName = read<hash_t>();
|
|
1041
|
+
const value* val = get_var(variableName);
|
|
1042
|
+
|
|
1043
|
+
inkAssert(val != nullptr, "Could not find variable!");
|
|
1044
|
+
_eval.push(*val);
|
|
1045
|
+
break;
|
|
1046
|
+
}
|
|
1047
|
+
case Command::START_STR: {
|
|
1048
|
+
inkAssert(_evaluation_mode, "Can not enter string mode while not in evaluation mode!");
|
|
1049
|
+
_string_mode = true;
|
|
1050
|
+
_evaluation_mode = false;
|
|
1051
|
+
_output << values::marker;
|
|
1052
|
+
} break;
|
|
1053
|
+
case Command::END_STR: {
|
|
1054
|
+
// TODO: Assert we really had a marker on there?
|
|
1055
|
+
inkAssert(! _evaluation_mode, "Must be in evaluation mode");
|
|
1056
|
+
_string_mode = false;
|
|
1057
|
+
_evaluation_mode = true;
|
|
1058
|
+
|
|
1059
|
+
// Load value from output stream
|
|
1060
|
+
// Push onto stack
|
|
1061
|
+
_eval.push(value{}.set<value_type::string>(
|
|
1062
|
+
_output.get_alloc<false>(_globals->strings(), _globals->lists())
|
|
1063
|
+
));
|
|
1064
|
+
} break;
|
|
1065
|
+
|
|
1066
|
+
case Command::START_TAG: {
|
|
1067
|
+
_output << values::marker;
|
|
1068
|
+
} break;
|
|
1069
|
+
|
|
1070
|
+
case Command::END_TAG: {
|
|
1071
|
+
auto tag = _output.get_alloc<true>(_globals->strings(), _globals->lists());
|
|
1072
|
+
if (_string_mode && _choice_tags_begin < 0) {
|
|
1073
|
+
_choice_tags_begin = _tags.size();
|
|
1074
|
+
}
|
|
1075
|
+
_tags.push() = tag;
|
|
1076
|
+
} break;
|
|
1077
|
+
|
|
1078
|
+
// == Choice commands
|
|
1079
|
+
case Command::CHOICE: {
|
|
1080
|
+
// Read path
|
|
1081
|
+
uint32_t path = read<uint32_t>();
|
|
1082
|
+
|
|
1083
|
+
// If we're a once only choice, make sure our destination hasn't
|
|
1084
|
+
// been visited
|
|
1085
|
+
if (flag & CommandFlag::CHOICE_IS_ONCE_ONLY) {
|
|
1086
|
+
// Need to convert offset to container index
|
|
1087
|
+
container_t destination = -1;
|
|
1088
|
+
if (_story->get_container_id(_story->instructions() + path, destination)) {
|
|
1089
|
+
// Ignore the choice if we've visited the destination before
|
|
1090
|
+
if (_globals->visits(destination) > 0) {
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
} else {
|
|
1094
|
+
inkAssert(false, "Destination for choice block does not have counting flags.");
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Choice is conditional
|
|
1099
|
+
if (flag & CommandFlag::CHOICE_HAS_CONDITION) {
|
|
1100
|
+
// Only show if the top of the eval stack is 'truthy'
|
|
1101
|
+
if (! _eval.pop().truthy(_globals->lists())) {
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// Use a marker to start compiling the choice text
|
|
1107
|
+
_output << values::marker;
|
|
1108
|
+
value stack[2];
|
|
1109
|
+
int sc = 0;
|
|
1110
|
+
|
|
1111
|
+
if (flag & CommandFlag::CHOICE_HAS_START_CONTENT) {
|
|
1112
|
+
stack[sc++] = _eval.pop();
|
|
1113
|
+
}
|
|
1114
|
+
if (flag & CommandFlag::CHOICE_HAS_CHOICE_ONLY_CONTENT) {
|
|
1115
|
+
stack[sc++] = _eval.pop();
|
|
1116
|
+
}
|
|
1117
|
+
for (; sc; --sc) {
|
|
1118
|
+
_output << stack[sc - 1];
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// fetch relevant tags
|
|
1122
|
+
const snap_tag* tags = nullptr;
|
|
1123
|
+
if (_choice_tags_begin >= 0 && _tags[_tags.size() - 1] != nullptr) {
|
|
1124
|
+
for (tags = _tags.end() - 1;
|
|
1125
|
+
*(tags - 1) != nullptr && (tags - _tags.begin()) > _choice_tags_begin; --tags)
|
|
1126
|
+
;
|
|
1127
|
+
_tags.push() = nullptr;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Create choice and record it
|
|
1131
|
+
if (flag & CommandFlag::CHOICE_IS_INVISIBLE_DEFAULT) {
|
|
1132
|
+
_fallback_choice.emplace();
|
|
1133
|
+
_fallback_choice.value().setup(
|
|
1134
|
+
_output, _globals->strings(), _globals->lists(), _choices.size(), path,
|
|
1135
|
+
current_thread(), tags->ptr()
|
|
1136
|
+
);
|
|
1137
|
+
} else {
|
|
1138
|
+
add_choice().setup(
|
|
1139
|
+
_output, _globals->strings(), _globals->lists(), _choices.size(), path,
|
|
1140
|
+
current_thread(), tags->ptr()
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
// save stack at last choice
|
|
1144
|
+
if (_saved) {
|
|
1145
|
+
forget();
|
|
1146
|
+
}
|
|
1147
|
+
save();
|
|
1148
|
+
} break;
|
|
1149
|
+
case Command::START_CONTAINER_MARKER: {
|
|
1150
|
+
// Keep track of current container
|
|
1151
|
+
auto index = read<uint32_t>();
|
|
1152
|
+
// offset points to command, command has size 6
|
|
1153
|
+
_container.push({.id = index, .offset = _ptr - 6});
|
|
1154
|
+
|
|
1155
|
+
// Increment visit count
|
|
1156
|
+
if (flag & CommandFlag::CONTAINER_MARKER_TRACK_VISITS
|
|
1157
|
+
|| flag & CommandFlag::CONTAINER_MARKER_TRACK_TURNS) {
|
|
1158
|
+
_globals->visit(_container.top().id, true);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
} break;
|
|
1162
|
+
case Command::END_CONTAINER_MARKER: {
|
|
1163
|
+
container_t index = read<container_t>();
|
|
1164
|
+
|
|
1165
|
+
inkAssert(_container.top().id == index, "Leaving container we are not in!");
|
|
1166
|
+
|
|
1167
|
+
// Move up out of the current container
|
|
1168
|
+
_container.pop();
|
|
1169
|
+
|
|
1170
|
+
// SPECIAL: If we've popped all containers, then there's an implied
|
|
1171
|
+
// 'done' command or return
|
|
1172
|
+
if (_container.empty()) {
|
|
1173
|
+
_is_falling = false;
|
|
1174
|
+
|
|
1175
|
+
frame_type type;
|
|
1176
|
+
if (! _threads.empty()) {
|
|
1177
|
+
on_done(false);
|
|
1178
|
+
break;
|
|
1179
|
+
} else if (_stack.has_frame(&type) && type == frame_type::function) // implicit return
|
|
1180
|
+
// is only for
|
|
1181
|
+
// functions
|
|
1182
|
+
{
|
|
1183
|
+
// push null and return
|
|
1184
|
+
_eval.push(values::null);
|
|
1185
|
+
|
|
1186
|
+
// HACK
|
|
1187
|
+
_ptr += sizeof(Command) + sizeof(CommandFlag);
|
|
1188
|
+
execute_return();
|
|
1189
|
+
} else if (_ptr == _story->end()) { // check needed, because it colud exist an unnamed
|
|
1190
|
+
// toplevel container (empty named container stack
|
|
1191
|
+
// != empty container stack)
|
|
1192
|
+
on_done(true);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
} break;
|
|
1196
|
+
case Command::VISIT: {
|
|
1197
|
+
// Push the visit count for the current container to the top
|
|
1198
|
+
// is 0-indexed for some reason. idk why but this is what ink expects
|
|
1199
|
+
_eval.push(
|
|
1200
|
+
value{}.set<value_type::int32>(( int ) _globals->visits(_container.top().id) - 1)
|
|
1201
|
+
);
|
|
1202
|
+
} break;
|
|
1203
|
+
case Command::TURN: {
|
|
1204
|
+
_eval.push(value{}.set<value_type::int32>(( int ) _globals->turns()));
|
|
1205
|
+
} break;
|
|
1206
|
+
case Command::SEQUENCE: {
|
|
1207
|
+
// TODO: The C# ink runtime does a bunch of fancy logic
|
|
1208
|
+
// to make sure each element is picked at least once in every
|
|
1209
|
+
// iteration loop. I don't feel like replicating that right now.
|
|
1210
|
+
// So, let's just return a random number and *shrug*
|
|
1211
|
+
int sequenceLength = _eval.pop().get<value_type::int32>();
|
|
1212
|
+
int index = _eval.pop().get<value_type::int32>();
|
|
1213
|
+
|
|
1214
|
+
_eval.push(value{}.set<value_type::int32>(static_cast<int32_t>(_rng.rand(sequenceLength)))
|
|
1215
|
+
);
|
|
1216
|
+
} break;
|
|
1217
|
+
case Command::SEED: {
|
|
1218
|
+
int32_t seed = _eval.pop().get<value_type::int32>();
|
|
1219
|
+
_rng.srand(seed);
|
|
1220
|
+
|
|
1221
|
+
_eval.push(values::null);
|
|
1222
|
+
} break;
|
|
1223
|
+
|
|
1224
|
+
case Command::READ_COUNT: {
|
|
1225
|
+
// Get container index
|
|
1226
|
+
container_t container = read<container_t>();
|
|
1227
|
+
|
|
1228
|
+
// Push the read count for the requested container index
|
|
1229
|
+
_eval.push(value{}.set<value_type::int32>(( int ) _globals->visits(container)));
|
|
1230
|
+
} break;
|
|
1231
|
+
case Command::TAG: {
|
|
1232
|
+
_tags.push() = read<const char*>();
|
|
1233
|
+
} break;
|
|
1234
|
+
default: inkAssert(false, "Unrecognized command!"); break;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
}
|
|
1239
|
+
#ifndef INK_ENABLE_UNREAL
|
|
1240
|
+
catch (...) {
|
|
1241
|
+
// Reset our whole state as it's probably corrupt
|
|
1242
|
+
reset();
|
|
1243
|
+
throw;
|
|
1244
|
+
}
|
|
1245
|
+
#endif
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
void runner_impl::on_done(bool setDone)
|
|
1249
|
+
{
|
|
1250
|
+
// If we're in a thread
|
|
1251
|
+
if (! _threads.empty()) {
|
|
1252
|
+
// Get the thread ID of the current thread
|
|
1253
|
+
thread_t completedThreadId = _threads.pop();
|
|
1254
|
+
|
|
1255
|
+
// Push in a complete marker
|
|
1256
|
+
_stack.complete_thread(completedThreadId);
|
|
1257
|
+
_ref_stack.complete_thread(completedThreadId);
|
|
1258
|
+
|
|
1259
|
+
// Go to where the thread started
|
|
1260
|
+
frame_type type = execute_return();
|
|
1261
|
+
inkAssert(
|
|
1262
|
+
type == frame_type::thread,
|
|
1263
|
+
"Expected thread frame marker to hold return to value but none found..."
|
|
1264
|
+
);
|
|
1265
|
+
// if thread ends, move stave point with, else the thread end marker is missing
|
|
1266
|
+
// and we can't collect the other threads
|
|
1267
|
+
if (_saved) {
|
|
1268
|
+
forget();
|
|
1269
|
+
save();
|
|
1270
|
+
}
|
|
1271
|
+
} else {
|
|
1272
|
+
if (setDone) {
|
|
1273
|
+
set_done_ptr(_ptr);
|
|
1274
|
+
}
|
|
1275
|
+
_ptr = nullptr;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
void runner_impl::set_done_ptr(ip_t ptr)
|
|
1280
|
+
{
|
|
1281
|
+
thread_t curr = current_thread();
|
|
1282
|
+
if (curr == ~0) {
|
|
1283
|
+
_done = ptr;
|
|
1284
|
+
} else {
|
|
1285
|
+
_threads.set(curr, ptr);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
void runner_impl::reset()
|
|
1290
|
+
{
|
|
1291
|
+
_eval.clear();
|
|
1292
|
+
_output.clear();
|
|
1293
|
+
_stack.clear();
|
|
1294
|
+
_ref_stack.clear();
|
|
1295
|
+
_threads.clear();
|
|
1296
|
+
_evaluation_mode = false;
|
|
1297
|
+
_saved = false;
|
|
1298
|
+
_choices.clear();
|
|
1299
|
+
_ptr = nullptr;
|
|
1300
|
+
_done = nullptr;
|
|
1301
|
+
_container.clear();
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
void runner_impl::mark_used(string_table& strings, list_table& lists) const
|
|
1305
|
+
{
|
|
1306
|
+
// Find strings in output and stacks
|
|
1307
|
+
_output.mark_used(strings, lists);
|
|
1308
|
+
_stack.mark_used(strings, lists);
|
|
1309
|
+
// ref_stack has no strings and lists!
|
|
1310
|
+
_eval.mark_used(strings, lists);
|
|
1311
|
+
|
|
1312
|
+
// Take into account tags
|
|
1313
|
+
for (size_t i = 0; i < _tags.size(); ++i) {
|
|
1314
|
+
strings.mark_used(_tags[i]);
|
|
1315
|
+
}
|
|
1316
|
+
// Take into account choice text
|
|
1317
|
+
for (size_t i = 0; i < _choices.size(); i++) {
|
|
1318
|
+
strings.mark_used(_choices[i]._text);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
void runner_impl::save()
|
|
1323
|
+
{
|
|
1324
|
+
inkAssert(! _saved, "Runner state already saved");
|
|
1325
|
+
|
|
1326
|
+
_saved = true;
|
|
1327
|
+
_output.save();
|
|
1328
|
+
_stack.save();
|
|
1329
|
+
_ref_stack.save();
|
|
1330
|
+
_backup = _ptr;
|
|
1331
|
+
_container.save();
|
|
1332
|
+
_globals->save();
|
|
1333
|
+
_eval.save();
|
|
1334
|
+
_threads.save();
|
|
1335
|
+
_choices.save();
|
|
1336
|
+
_tags.save();
|
|
1337
|
+
_saved_evaluation_mode = _evaluation_mode;
|
|
1338
|
+
|
|
1339
|
+
// Not doing this anymore. There can be lingering stack entries from function returns
|
|
1340
|
+
// inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty");
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
void runner_impl::restore()
|
|
1344
|
+
{
|
|
1345
|
+
inkAssert(_saved, "Can't restore. No runner state saved.");
|
|
1346
|
+
// the output can be restored without the rest
|
|
1347
|
+
if (_output.saved()) {
|
|
1348
|
+
_output.restore();
|
|
1349
|
+
}
|
|
1350
|
+
_stack.restore();
|
|
1351
|
+
_ref_stack.restore();
|
|
1352
|
+
_ptr = _backup;
|
|
1353
|
+
_container.restore();
|
|
1354
|
+
_globals->restore();
|
|
1355
|
+
_eval.restore();
|
|
1356
|
+
_threads.restore();
|
|
1357
|
+
_choices.restore();
|
|
1358
|
+
_tags.restore();
|
|
1359
|
+
_evaluation_mode = _saved_evaluation_mode;
|
|
1360
|
+
|
|
1361
|
+
// Not doing this anymore. There can be lingering stack entries from function returns
|
|
1362
|
+
// inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty");
|
|
1363
|
+
|
|
1364
|
+
_saved = false;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
void runner_impl::forget()
|
|
1368
|
+
{
|
|
1369
|
+
// Do nothing if we haven't saved
|
|
1370
|
+
if (! _saved) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
_output.forget();
|
|
1375
|
+
_stack.forget();
|
|
1376
|
+
_ref_stack.forget();
|
|
1377
|
+
_container.forget();
|
|
1378
|
+
_globals->forget();
|
|
1379
|
+
_eval.forget();
|
|
1380
|
+
_threads.forget();
|
|
1381
|
+
_choices.forgett();
|
|
1382
|
+
_tags.forgett();
|
|
1383
|
+
|
|
1384
|
+
// Nothing to do for eval stack. It should just stay as it is
|
|
1385
|
+
|
|
1386
|
+
_saved = false;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
#ifdef INK_ENABLE_STL
|
|
1390
|
+
std::ostream& operator<<(std::ostream& out, runner_impl& in)
|
|
1391
|
+
{
|
|
1392
|
+
in.getline(out);
|
|
1393
|
+
return out;
|
|
1394
|
+
}
|
|
1395
|
+
#endif
|
|
1396
|
+
} // namespace ink::runtime::internal
|