aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/vm
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation/vm')
-rw-r--r--src/openvic-simulation/vm/Codegen.cpp676
-rw-r--r--src/openvic-simulation/vm/Codegen.hpp45
2 files changed, 597 insertions, 124 deletions
diff --git a/src/openvic-simulation/vm/Codegen.cpp b/src/openvic-simulation/vm/Codegen.cpp
index 675bc2c..34a1093 100644
--- a/src/openvic-simulation/vm/Codegen.cpp
+++ b/src/openvic-simulation/vm/Codegen.cpp
@@ -1,19 +1,34 @@
#include "Codegen.hpp"
+#include <bit> // IWYU pragma: keep
#include <cctype>
+#include <charconv>
#include <cstring>
#include <string_view>
+#include <unordered_map>
+#include <variant>
+#include <openvic-dataloader/detail/SymbolIntern.hpp>
+#include <openvic-dataloader/detail/Utility.hpp>
#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
#include <tsl/ordered_map.h>
+#include <dryad/_detail/assert.hpp>
#include <dryad/node.hpp>
#include <dryad/tree.hpp>
#include <range/v3/algorithm/equal.hpp>
+#include <range/v3/algorithm/reverse.hpp>
+#include "InstanceManager.hpp"
+#include "types/fixed_point/FixedPoint.hpp"
#include <lauf/asm/builder.h>
+#include <lauf/asm/module.h>
+#include <lauf/runtime/builtin.h>
+#include <lauf/runtime/memory.h>
+#include <lauf/runtime/process.h>
+#include <lauf/runtime/value.h>
using namespace OpenVic::Vm;
using namespace ovdl::v2script::ast;
@@ -25,160 +40,418 @@ bool ichar_equals(char a, char b) {
return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
}
-Codegen::Codegen(ovdl::v2script::Parser const& parser, const char* module_name, lauf_asm_build_options options)
- : _parser(parser), _module(module_name), _builder(options) {
- intern_scopes();
+using scope_variant = std::variant<std::monostate>;
+using argument_variant = std::variant<std::monostate>;
+using argument_map = std::unordered_map<std::string_view, argument_variant>;
+
+static constexpr auto argument_size = 32;
+
+struct ov_asm_argument {
+ const char* key;
+
+ union {
+ const char* as_cstr;
+ const scope_variant* as_scope;
+ std::uint64_t as_uint;
+ std::int64_t as_int;
+ const void* as_ptr;
+ } value;
+
+ enum class type_t : std::uint8_t { //
+ cstring,
+ scope,
+ uint,
+ int_,
+ ptr,
+ fixed_point
+ } type;
+
+ const char* val_cstr() const {
+ if (type != type_t::cstring) {
+ return nullptr;
+ }
+ return value.as_cstr;
+ }
+
+ const scope_variant* val_scope() const {
+ if (type != type_t::scope) {
+ return nullptr;
+ }
+ return value.as_scope;
+ }
+
+ std::uint64_t val_uint() const {
+ if (type != type_t::uint) {
+ return 0;
+ }
+ return value.as_uint;
+ }
+
+ std::int64_t val_int() const {
+ if (type != type_t::int_) {
+ return 0;
+ }
+ return value.as_int;
+ }
+
+ const void* val_ptr() const {
+ if (type != type_t::ptr) {
+ return nullptr;
+ }
+ return value.as_ptr;
+ }
+
+ OpenVic::fixed_point_t val_fixed() const {
+ if (type != type_t::fixed_point) {
+ return 0;
+ }
+ return OpenVic::fixed_point_t(value.as_int);
+ }
+};
+
+bool execute_effect(
+ OpenVic::InstanceManager* instance_manager, const char* effect_id, scope_variant& scope, ov_asm_argument*,
+ std::size_t arg_count
+) {
+ return true;
}
-Codegen::Codegen(ovdl::v2script::Parser const& parser, Module&& module, AsmBuilder&& builder)
- : _parser(parser), _module(std::move(module)), _builder(std::move(builder)) {
- intern_scopes();
+ov_asm_argument arguments[argument_size];
+
+// This builtin takes three arguments:
+// * vstack_ptr[0] is an address of the name of the effect
+// * vstack_ptr[1] is an address of the scope reference to apply the effect by
+// * vstack_ptr[2] is an address to an array of pointers to the arguments for the effect
+LAUF_RUNTIME_BUILTIN(call_effect, 3, 0, LAUF_RUNTIME_BUILTIN_DEFAULT, "call_effect", nullptr) {
+ auto user_data = lauf_runtime_get_vm_user_data(process);
+ if (user_data == nullptr) {
+ return lauf_runtime_panic(process, "invalid user data");
+ }
+
+ auto effect_name_addr = vstack_ptr[0].as_address;
+ auto scope_addr = vstack_ptr[1].as_address;
+ auto argument_array_addr = vstack_ptr[2].as_address;
+
+ auto effect_name = lauf_runtime_get_cstr(process, effect_name_addr);
+ if (effect_name == nullptr) {
+ return lauf_runtime_panic(process, "invalid effect name address");
+ }
+
+ auto scope_ptr = lauf_runtime_get_mut_ptr(process, scope_addr, { 0, 1 });
+ if (scope_ptr == nullptr) {
+ return lauf_runtime_panic(process, "invalid scope address");
+ }
+
+ lauf_runtime_allocation array_allocation;
+ if (!lauf_runtime_get_allocation(process, argument_array_addr, &array_allocation)) {
+ return lauf_runtime_panic(process, "invalid arguments address");
+ }
+
+ std::size_t count = array_allocation.size / lauf_asm_type_value.layout.size;
+ if (count > argument_size) {
+ return lauf_runtime_panic(process, "too many arguments");
+ }
+
+ auto argument_array = static_cast<ov_asm_argument*>(lauf_runtime_get_mut_ptr(process, argument_array_addr, { 1, 1 }));
+
+ if (!execute_effect(
+ static_cast<OpenVic::InstanceManager*>(user_data), effect_name, *static_cast<scope_variant*>(scope_ptr),
+ argument_array, count
+ )) {
+ return lauf_runtime_panic(process, "effect could not be found");
+ }
+
+ vstack_ptr += 3;
+ LAUF_RUNTIME_BUILTIN_DISPATCH;
}
-void Codegen::intern_scopes() {
- _has_top_level_province_scopes = static_cast<bool>(_parser.find_intern("province_event"sv));
- _has_top_level_country_scopes = static_cast<bool>(_parser.find_intern("country_event"sv)) ||
- static_cast<bool>(_parser.find_intern("political_decisions"sv));
+// This builtin takes three arguments:
+// * vstack_ptr[0] is an address of the name of the trigger
+// * vstack_ptr[1] is an address of the scope to apply the trigger by
+// * vstack_ptr[2] is an address to an array of pointers to the arguments for the trigger
+// Returns whether the trigger evaluated to true
+LAUF_RUNTIME_BUILTIN(call_trigger, 3, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "call_trigger", &call_effect) {
+ auto effect_name_addr = vstack_ptr[0].as_address;
+ auto scope_addr = vstack_ptr[1].as_address;
+ auto argument_array_addr = vstack_ptr[2].as_address;
-#define PUSH_INTERN(CONTAINTER, NAME) \
- [&](auto&& scope_name) { \
- if (scope_name) \
- CONTAINTER.insert(scope_name); \
- }(_parser.find_intern(#NAME##sv))
+ auto effect_name = lauf_runtime_get_cstr(process, effect_name_addr);
+ if (effect_name == nullptr) {
+ return lauf_runtime_panic(process, "invalid address");
+ }
- PUSH_INTERN(_all_scopes, THIS);
- PUSH_INTERN(_all_scopes, FROM);
- PUSH_INTERN(_all_scopes, from);
- PUSH_INTERN(_all_scopes, cultural_union);
- // Country Scope
- PUSH_INTERN(_country_scopes, capital_scope);
- PUSH_INTERN(_country_scopes, overlord);
- PUSH_INTERN(_country_scopes, sphere_owner);
- // Country Scope Random
- PUSH_INTERN(_country_scopes, random_country);
- PUSH_INTERN(_country_scopes, random_owned);
- PUSH_INTERN(_country_scopes, random_pop);
- // Country Scope Iterative
- PUSH_INTERN(_country_scopes, all_core);
- PUSH_INTERN(_country_scopes, any_country);
- PUSH_INTERN(_country_scopes, any_core);
- PUSH_INTERN(_country_scopes, any_greater_power);
- PUSH_INTERN(_country_scopes, any_neighbor_country);
- PUSH_INTERN(_country_scopes, any_owned_province);
- PUSH_INTERN(_country_scopes, any_pop);
- PUSH_INTERN(_country_scopes, any_sphere_member);
- PUSH_INTERN(_country_scopes, any_state);
- PUSH_INTERN(_country_scopes, any_substate);
- PUSH_INTERN(_country_scopes, war_countries);
+ auto argument_addresses =
+ static_cast<lauf_runtime_value*>(lauf_runtime_get_mut_ptr(process, argument_array_addr, { 1, 1 }));
- // Province Scope
- PUSH_INTERN(_province_scopes, controller);
- PUSH_INTERN(_province_scopes, owner);
- PUSH_INTERN(_province_scopes, state_scope);
- // Province Scope Random
- PUSH_INTERN(_province_scopes, random_neighbor_province);
- PUSH_INTERN(_province_scopes, random_empty_neighbor_province);
- // Province Scope Iterative
- PUSH_INTERN(_province_scopes, any_core);
- PUSH_INTERN(_province_scopes, any_neighbor_province);
- PUSH_INTERN(_province_scopes, any_pop);
+ // TODO: call trigger by name
- // Pop Scope
- PUSH_INTERN(_pop_scopes, country);
- PUSH_INTERN(_pop_scopes, location);
+ vstack_ptr[0].as_sint = 1;
+ vstack_ptr += 2;
+ LAUF_RUNTIME_BUILTIN_DISPATCH;
+}
-#undef PUSH_INTERN
+// Translates a lauf address into the native pointer representation.
+// It takes one argument, which is the address, and returns one argument, which is the pointer.
+LAUF_RUNTIME_BUILTIN(
+ translate_address_to_pointer, 1, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "translate_address_to_pointer", &call_trigger
+) {
+ auto address = vstack_ptr[0].as_address;
+
+ auto ptr = lauf_runtime_get_const_ptr(process, address, { 0, 1 });
+ if (ptr == nullptr) {
+ return lauf_runtime_panic(process, "invalid address");
+ }
+ vstack_ptr[0].as_native_ptr = (void*)ptr;
+
+ LAUF_RUNTIME_BUILTIN_DISPATCH;
}
+// As above, but translates to a C string.
+LAUF_RUNTIME_BUILTIN(
+ translate_address_to_string, 1, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "translate_address_to_string",
+ &translate_address_to_pointer
+) {
+ auto address = vstack_ptr[0].as_address;
+
+ auto ptr = lauf_runtime_get_cstr(process, address);
+ if (ptr == nullptr) {
+ return lauf_runtime_panic(process, "invalid address");
+ }
+ vstack_ptr[0].as_native_ptr = (void*)ptr;
+
+ LAUF_RUNTIME_BUILTIN_DISPATCH;
+}
+
+Codegen::Codegen(ovdl::v2script::Parser const& parser, const char* module_name, lauf_asm_build_options options)
+ : _parser(parser), _module(module_name), _builder(options) {}
-constexpr auto any_ = "any_"sv;
-constexpr auto all_ = "all_"sv;
-constexpr auto war_countries = "war_countries"sv;
+Codegen::Codegen(ovdl::v2script::Parser const& parser, Module&& module, AsmBuilder&& builder)
+ : _parser(parser), _module(std::move(module)), _builder(std::move(builder)) {}
+
+static constexpr auto any_ = "any_"sv;
+static constexpr auto all_ = "all_"sv;
+static constexpr auto war_countries = "war_countries"sv;
-bool Codegen::is_iterative_scope(scope_execution_type execution_type, scope_type active_scope, ovdl::symbol<char> name) const {
+bool Codegen::is_iterative_scope(scope_execution_type execution_type, scope_type active_scope_type, ovdl::symbol<char> name)
+ const {
return std::strncmp(name.c_str(), any_.data(), any_.size()) == 0 ||
std::strncmp(name.c_str(), all_.data(), all_.size()) == 0 ||
std::strncmp(name.c_str(), war_countries.data(), war_countries.size()) == 0;
}
-bool Codegen::is_scope_for(scope_execution_type execution_type, scope_type active_scope, ovdl::symbol<char> name) const {
- if (_all_scopes.contains(name)) {
- return true;
- }
+Codegen::scope_type Codegen::get_scope_type_for(scope_execution_type execution_type, ovdl::symbol<char> name) const {
+ using enum scope_type;
- switch (active_scope) {
- case scope_type::Country:
- if (_country_scopes.contains(name)) {
- return true;
- }
- break;
- case scope_type::State: break;
- case scope_type::Province:
- if (_province_scopes.contains(name)) {
- return true;
- }
- break;
- case scope_type::Pop:
- if (_pop_scopes.contains(name)) {
- return true;
- }
- break;
- }
+#define FIND_SCOPE(SCOPE_TYPE, NAME) \
+ if (_parser.find_intern(#NAME##sv) == name) \
+ return SCOPE_TYPE
- return false;
+ FIND_SCOPE(Generic, THIS);
+ FIND_SCOPE(Generic, FROM);
+ FIND_SCOPE(Generic, from);
+
+ // Country and Pop Scope
+ FIND_SCOPE(Country | Pop, cultural_union);
+
+ // Country Scope
+ FIND_SCOPE(Country, capital_scope);
+ FIND_SCOPE(Country, overlord);
+ FIND_SCOPE(Country, sphere_owner);
+ // Country Scope Random
+ FIND_SCOPE(Country, random_country);
+ FIND_SCOPE(Country, random_owned);
+ FIND_SCOPE(Country, random_pop);
+ // Country Scope Iterative
+ FIND_SCOPE(Country, all_core);
+ FIND_SCOPE(Country, any_country);
+ FIND_SCOPE(Country, any_core);
+ FIND_SCOPE(Country, any_greater_power);
+ FIND_SCOPE(Country, any_neighbor_country);
+ FIND_SCOPE(Country, any_owned_province);
+ FIND_SCOPE(Country, any_pop);
+ FIND_SCOPE(Country, any_sphere_member);
+ FIND_SCOPE(Country, any_state);
+ FIND_SCOPE(Country, any_substate);
+ FIND_SCOPE(Country, war_countries);
+
+ // Province Scope
+ FIND_SCOPE(Province, controller);
+ FIND_SCOPE(Province, owner);
+ FIND_SCOPE(Province, state_scope);
+ // Province Scope Random
+ FIND_SCOPE(Province, random_neighbor_province);
+ FIND_SCOPE(Province, random_empty_neighbor_province);
+ // Province Scope Iterative
+ FIND_SCOPE(Province, any_core);
+ FIND_SCOPE(Province, any_neighbor_province);
+ FIND_SCOPE(Province, any_pop);
+
+ // Pop Scope
+ FIND_SCOPE(Pop, country);
+ FIND_SCOPE(Pop, location);
+
+#undef FIND_SCOPE
+ return None;
}
-void Codegen::generate_effect_from(Codegen::scope_type type, Node* node) {
- bool is_prepping_args = false;
- Codegen::scope_type current_scope = type;
- tsl::ordered_map<std::string_view, std::string_view> prepped_arguments;
+void Codegen::generate_effect_from(scope_type type, Node* node) {
+ struct current_scope_t {
+ scope_type type;
+ ovdl::symbol<char> symbol;
+ } current_scope { .type = type, .symbol = {} };
+
+ bool is_arguments = false;
+ std::unordered_map<ovdl::symbol<char>, FlatValue const*, symbol_hash> named_arguments;
+ ov_asm_argument::type_t ov_asm_type;
dryad::visit_tree(
node, //
- [&](dryad::child_visitor<NodeKind> visitor, AssignStatement* statement) {
+ [&](dryad::child_visitor<NodeKind> visitor, const AssignStatement* statement) {
auto const* left = dryad::node_try_cast<FlatValue>(statement->left());
if (!left) {
return;
}
- if (is_prepping_args) {
- visitor(statement->right());
+ auto const* right = dryad::node_try_cast<FlatValue>(statement->right());
+ if (is_arguments) {
+ if (right) {
+ named_arguments.insert_or_assign(left->value(), right);
+ return;
+ }
+
+ // Boils war's attacker_goal = { casus_belli = [casus belli] } down to attacker_goal = [casus belli]
+ if (auto right_list = dryad::node_try_cast<ListValue>(statement->right())) {
+ if (right_list->statements().empty()) {
+ return;
+ }
+ if (auto right_statement = dryad::node_try_cast<AssignStatement>(right_list->statements().front())) {
+ if (auto right_flat_value = dryad::node_try_cast<FlatValue>(right_statement->right())) {
+ named_arguments.insert_or_assign(left->value(), right_flat_value);
+ }
+ }
+ }
return;
}
- auto const* right = dryad::node_try_cast<FlatValue>(statement->right());
+ bool is_iterative = false;
+ lauf_asm_local* arguments = nullptr;
+
if (!right) {
using enum Codegen::scope_execution_type;
- bool is_iterative = is_iterative_scope(Effect, current_scope, left->value());
- if (is_iterative) {}
- is_prepping_args = is_scope_for(Effect, current_scope, left->value());
+ is_arguments = current_scope.type >> get_scope_type_for(Effect, left->value());
+ if (!is_arguments) {
+ current_scope_t previous_scope = current_scope;
+ current_scope.symbol = left->value();
+ visitor(right);
+ current_scope = previous_scope;
+ return;
+ }
+
+ is_iterative = is_iterative_scope(Effect, current_scope.type, current_scope.symbol);
+ if (is_iterative) {
+ // TODO: loop header
+ }
+
+ // TODO: build arguments from named_arguments
+
+ arguments = lauf_asm_build_local(
+ _builder, lauf_asm_array_layout(LAUF_ASM_NATIVE_LAYOUT_OF(ov_asm_argument), named_arguments.size())
+ );
+
+ std::size_t index = 0;
+ for (auto&& [key, value] : named_arguments) {
+ inst_store_ov_asm_key(arguments, index, key);
+
+ visitor(value);
+ inst_store_ov_asm_value_from_vstack(arguments, index, ovdl::detail::to_underlying(ov_asm_type));
+
+ inst_store_ov_asm_type(arguments, index, ovdl::detail::to_underlying(ov_asm_type));
+ index++;
+ }
+
+ named_arguments.clear();
+ is_arguments = false;
+ } else if (auto yes_symbol = _parser.find_intern("yes"sv); yes_symbol && right->value() == yes_symbol) {
+ // Build empty arguments
+ arguments = lauf_asm_build_local(_builder, lauf_asm_array_layout(lauf_asm_type_value.layout, 0));
+ } else if (_parser.find_intern("no"sv) != right->value()) {
+ arguments = lauf_asm_build_local(_builder, lauf_asm_array_layout(lauf_asm_type_value.layout, 1));
+ inst_store_ov_asm_key_null(arguments, 0);
+
visitor(right);
- if (is_iterative) {}
- } else if (ranges::equal(right->value().view(), "yes"sv, ichar_equals)) {
- // TODO: insert vic2 bytecode scope object address here
- // TODO: calls a builtin for Victoria 2 effects?
- } else if (!ranges::equal(right->value().view(), "no"sv, ichar_equals)) {
- // TODO: single argument execution
+ inst_store_ov_asm_value_from_vstack(arguments, 0, ovdl::detail::to_underlying(ov_asm_type));
+
+ inst_store_ov_asm_type(arguments, 0, ovdl::detail::to_underlying(ov_asm_type));
+ }
+
+ // Load arguments address (vstack[2])
+ lauf_asm_inst_local_addr(_builder, arguments);
+ // Load scope address in lauf (vstack[1])
+ push_instruction_for_scope(current_scope.type, current_scope.symbol);
+ // Create effect name literal (vstack[0])
+ auto effect_name = lauf_asm_build_string_literal(_builder, left->value().c_str());
+ lauf_asm_inst_global_addr(_builder, effect_name);
+ // Consumes vstack[0], vstack[1], and vstack[2]
+ lauf_asm_inst_call_builtin(_builder, call_effect);
+
+ if (is_iterative) {
+ // TODO: loop footer
}
},
- [&](FlatValue* value) {
- // TODO: handle right side
- },
- [&](dryad::traverse_event ev, ListValue* value) {
- if (is_prepping_args && ev == dryad::traverse_event::exit) {
- // TODO: arguments have been prepared, call effect function
- is_prepping_args = false;
- } else if (!is_prepping_args && ev == dryad::traverse_event::enter) {
- // TODO: this is a scope
+ [&](const FlatValue* value) {
+ using enum ov_asm_argument::type_t;
+ if (!is_arguments) {
+ return;
+ }
+
+ if (push_instruction_for_scope(current_scope.type, value->value())) {
+ ov_asm_type = scope;
+ return;
}
+
+ auto view = value->value().view();
+ if (view[0] == '-') {
+ if (std::int64_t value; std::from_chars(view.begin(), view.end(), value).ptr == view.end()) {
+ lauf_asm_inst_sint(_builder, value);
+ ov_asm_type = int_;
+ return;
+ }
+ }
+
+ if (std::uint64_t value; std::from_chars(view.begin(), view.end(), value).ptr == view.end()) {
+ lauf_asm_inst_uint(_builder, value);
+ ov_asm_type = uint;
+ return;
+ }
+
+ {
+ bool success;
+ auto value = fixed_point_t::parse(view, &success);
+ if (success) {
+ lauf_asm_inst_sint(_builder, value.get_raw_value());
+ ov_asm_type = fixed_point;
+ return;
+ }
+ }
+
+ // Create argument string literal
+ auto argument_str = lauf_asm_build_string_literal(_builder, value->value().c_str());
+ lauf_asm_inst_global_addr(_builder, argument_str);
+ ov_asm_type = cstring;
+
+ // TODO: find/create and insert value here
}
);
}
-void Codegen::generate_condition_from(Codegen::scope_type type, Node* node) {
+void Codegen::generate_condition_from(scope_type type, Node* node) {
+ struct current_scope_t {
+ scope_type type;
+ ovdl::symbol<char> symbol;
+ } current_scope { .type = type, .symbol = {} };
+
bool is_prepping_args = false;
- Codegen::scope_type current_scope = type;
tsl::ordered_map<std::string_view, std::string_view> prepped_arguments;
dryad::visit_tree(
@@ -192,15 +465,15 @@ void Codegen::generate_condition_from(Codegen::scope_type type, Node* node) {
auto const* right = dryad::node_try_cast<FlatValue>(statement->right());
if (!right) {
using enum Codegen::scope_execution_type;
- bool is_iterative = is_iterative_scope(Trigger, current_scope, left->value());
+ bool is_iterative = is_iterative_scope(Trigger, current_scope.type, current_scope.symbol);
if (is_iterative) {}
- is_prepping_args = is_scope_for(Trigger, current_scope, left->value());
+ is_prepping_args = current_scope.type >> get_scope_type_for(Trigger, current_scope.symbol);
visitor(right);
if (is_iterative) {}
- } else if (ranges::equal(right->value().view(), "yes"sv, ichar_equals)) {
+ } else if (auto yes_symbol = _parser.find_intern("yes"sv); yes_symbol && right->value() == yes_symbol) {
// TODO: insert vic2 bytecode scope object address here
// TODO: calls a builtin for Victoria 2 triggers?
- } else if (!ranges::equal(right->value().view(), "no"sv, ichar_equals)) {
+ } else if (_parser.find_intern("no"sv) != right->value()) {
// TODO: single argument execution
}
},
@@ -209,3 +482,180 @@ void Codegen::generate_condition_from(Codegen::scope_type type, Node* node) {
}
);
}
+
+bool Codegen::push_instruction_for_scope(scope_type type, ovdl::symbol<char> scope_symbol) {
+
+ auto is_scope = [&](std::string_view name) -> bool {
+ auto intern = _parser.find_intern(name);
+ return intern && intern == scope_symbol;
+ };
+
+ if (is_scope("this"sv)) {
+ inst_push_scope_this();
+ return true;
+ } else if (is_scope("from"sv)) {
+ inst_push_scope_from();
+ return true;
+ } else if (is_scope("cultural_union"sv)) {
+ DRYAD_PRECONDITION(std::has_single_bit(ovdl::detail::to_underlying(type)));
+ switch (type) {
+ case scope_type::Country: //
+ inst_push_get_country_cultural_union();
+ return true;
+ case scope_type::State: //
+ inst_push_get_state_cultural_union();
+ return true;
+ case scope_type::Province: //
+ DRYAD_ASSERT(false, "province scope does not support cultural_union scope");
+ break;
+ case scope_type::Pop: //
+ inst_push_get_pop_cultural_union();
+ return true;
+ default: return false;
+ }
+ } else {
+ DRYAD_PRECONDITION(std::has_single_bit(ovdl::detail::to_underlying(type)));
+ switch (type) {
+ case scope_type::Country:
+ if (is_scope("capital_scope"sv)) {
+ inst_push_get_country_capital();
+ return true;
+ } else if (is_scope("overlord"sv)) {
+ inst_push_get_country_overlord();
+ return true;
+ } else if (is_scope("sphere_owner"sv)) {
+ inst_push_get_country_sphere_owner();
+ return true;
+ } else if (is_scope("random_country"sv)) {
+ inst_push_get_random_country();
+ return true;
+ } else if (is_scope("random_owned"sv)) {
+ inst_push_get_random_owned();
+ return true;
+ } else if (is_scope("random_pop"sv)) {
+ }
+ break;
+ case scope_type::State: break;
+ case scope_type::Province:
+ if (is_scope("controller"sv)) {
+ inst_push_get_province_controller();
+ return true;
+ } else if (is_scope("owner"sv)) {
+ inst_push_get_province_owner();
+ return true;
+ } else if (is_scope("state_scope"sv)) {
+ inst_push_get_province_state();
+ return true;
+ } else if (is_scope("random_neighbor_province"sv)) {
+ inst_push_get_random_neighbor_province();
+ return true;
+ } else if (is_scope("random_empty_neighbor_province"sv)) {
+ inst_push_get_random_empty_neighbor_province();
+ return true;
+ }
+ break;
+ case scope_type::Pop:
+ if (is_scope("country"sv)) {
+ inst_push_get_pop_country();
+ return true;
+ } else if (is_scope("location"sv)) {
+ inst_push_get_pop_location();
+ return true;
+ }
+ break;
+ default: return false;
+ }
+ }
+ return false;
+}
+
+static constexpr lauf_asm_layout aggregate_layouts[] = { LAUF_ASM_NATIVE_LAYOUT_OF(ov_asm_argument::key),
+ LAUF_ASM_NATIVE_LAYOUT_OF(ov_asm_argument::type),
+ LAUF_ASM_NATIVE_LAYOUT_OF(ov_asm_argument::value) };
+
+bool Codegen::inst_store_ov_asm_key_null(lauf_asm_local* local, std::size_t index) {
+ lauf_asm_inst_null(_builder);
+ // Start Load arguments //
+ // vstack[1]
+ lauf_asm_inst_local_addr(_builder, local);
+ // vstack[0]
+ lauf_asm_inst_uint(_builder, index);
+ // Consumes vstack[0] = arguments_index, vstack[1] = argument_key_address, produces array address as
+ // vstack[0]
+ lauf_asm_inst_array_element(_builder, lauf_asm_type_value.layout);
+ // End Load arguments //
+ lauf_asm_inst_aggregate_member(_builder, 0, aggregate_layouts, 3);
+ // Consumes vstack[0] and vstack[1]
+ lauf_asm_inst_store_field(_builder, lauf_asm_type_value, 0);
+ return true;
+}
+
+bool Codegen::inst_store_ov_asm_key(lauf_asm_local* local, std::size_t index, ovdl::symbol<char> key) {
+ // Create key literal
+ auto argument_key = lauf_asm_build_string_literal(_builder, key.c_str());
+ lauf_asm_inst_global_addr(_builder, argument_key);
+ // Translate key literal to cstring
+ lauf_asm_inst_call_builtin(_builder, translate_address_to_string);
+ // Start Load arguments //
+ // vstack[1]
+ lauf_asm_inst_local_addr(_builder, local);
+ // vstack[0]
+ lauf_asm_inst_uint(_builder, index);
+ // Consumes vstack[0] = arguments_index, vstack[1] = argument_key_address, produces array address as
+ // vstack[0]
+ lauf_asm_inst_array_element(_builder, lauf_asm_type_value.layout);
+ // End Load arguments //
+ lauf_asm_inst_aggregate_member(_builder, 0, aggregate_layouts, 3);
+ // Consumes vstack[0] and vstack[1]
+ lauf_asm_inst_store_field(_builder, lauf_asm_type_value, 0);
+ return true;
+}
+
+bool Codegen::inst_store_ov_asm_value_from_vstack(lauf_asm_local* local, std::size_t index, std::uint8_t type) {
+ switch (ovdl::detail::from_underlying<ov_asm_argument::type_t>(type)) {
+ using enum ov_asm_argument::type_t;
+ case cstring:
+ // Translate key literal to cstring
+ lauf_asm_inst_call_builtin(_builder, translate_address_to_string);
+ case scope:
+ case ptr:
+ // Translate address to pointer
+ lauf_asm_inst_call_builtin(_builder, translate_address_to_pointer);
+ case uint:
+ case int_:
+ case fixed_point:
+ // needs no translation
+ break;
+ }
+
+ // Start Load arguments //
+ // vstack[1]
+ lauf_asm_inst_local_addr(_builder, local);
+ // vstack[0]
+ lauf_asm_inst_uint(_builder, index);
+ // Consumes vstack[0] = arguments_index, vstack[1] = argument_key_address, produces array address as
+ // vstack[0]
+ lauf_asm_inst_array_element(_builder, lauf_asm_type_value.layout);
+ // End Load arguments //
+ lauf_asm_inst_aggregate_member(_builder, 2, aggregate_layouts, 3);
+ // Consumes vstack[0] and vstack[1]
+ lauf_asm_inst_store_field(_builder, lauf_asm_type_value, 0);
+ return true;
+}
+
+bool Codegen::inst_store_ov_asm_type(lauf_asm_local* local, std::size_t index, std::uint8_t type) {
+ lauf_asm_inst_uint(_builder, static_cast<lauf_uint>(type));
+ // Start Load arguments //
+ // vstack[1]
+ lauf_asm_inst_local_addr(_builder, local);
+ // vstack[0]
+ lauf_asm_inst_uint(_builder, index);
+ // Consumes vstack[0] = arguments_index, vstack[1] = argument_key_address, produces array address as
+ // vstack[0]
+ lauf_asm_inst_array_element(_builder, lauf_asm_type_value.layout);
+ // End Load arguments //
+ lauf_asm_inst_aggregate_member(_builder, 1, aggregate_layouts, 3);
+ // Consumes vstack[0] and vstack[1]
+ lauf_asm_inst_store_field(_builder, lauf_asm_type_value, 0);
+ return true;
+}
diff --git a/src/openvic-simulation/vm/Codegen.hpp b/src/openvic-simulation/vm/Codegen.hpp
index b50e5f7..c63ab9e 100644
--- a/src/openvic-simulation/vm/Codegen.hpp
+++ b/src/openvic-simulation/vm/Codegen.hpp
@@ -1,13 +1,12 @@
#pragma once
-#include <unordered_set>
-
#include <openvic-dataloader/detail/SymbolIntern.hpp>
#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
#include <openvic-dataloader/v2script/Parser.hpp>
#include "AsmBuilder.hpp"
#include "Module.hpp"
+#include "types/EnumBitfield.hpp"
#include <lauf/asm/builder.h>
namespace OpenVic::Vm {
@@ -51,8 +50,6 @@ namespace OpenVic::Vm {
return _builder;
}
- void intern_scopes();
-
lauf_asm_block* create_block(size_t input_count) {
return lauf_asm_declare_block(_builder, input_count);
}
@@ -62,10 +59,25 @@ namespace OpenVic::Vm {
}
enum class scope_execution_type : std::uint8_t { Effect, Trigger };
- enum class scope_type : std::uint8_t { Country, State, Province, Pop };
+ enum class scope_type : std::uint8_t {
+ None = 0,
+ Country = 1 << 0,
+ State = 1 << 1,
+ Province = 1 << 2,
+ Pop = 1 << 3,
+
+ Generic = Country | State | Province | Pop,
+ };
+
+ enum class trigger_modifier : std::uint8_t { //
+ Not = 1 << 0,
+ And = 1 << 1,
+ Or = 1 << 2,
+ };
- bool is_iterative_scope(scope_execution_type execution_type, scope_type active_scope, ovdl::symbol<char> name) const;
- bool is_scope_for(scope_execution_type execution_type, scope_type active_scope, ovdl::symbol<char> name) const;
+ bool
+ is_iterative_scope(scope_execution_type execution_type, scope_type active_scope_type, ovdl::symbol<char> name) const;
+ scope_type get_scope_type_for(scope_execution_type execution_type, ovdl::symbol<char> name) const;
lauf_asm_function* create_effect_function(scope_type type, ovdl::v2script::ast::Node* node);
lauf_asm_function* create_condition_function(scope_type type, ovdl::v2script::ast::Node* node);
@@ -73,6 +85,13 @@ namespace OpenVic::Vm {
void generate_effect_from(scope_type type, ovdl::v2script::ast::Node* node);
void generate_condition_from(scope_type type, ovdl::v2script::ast::Node* node);
+ bool inst_store_ov_asm_key_null(lauf_asm_local* local, std::size_t index);
+ bool inst_store_ov_asm_key(lauf_asm_local* local, std::size_t index, ovdl::symbol<char> key);
+ bool inst_store_ov_asm_value_from_vstack(lauf_asm_local* local, std::size_t index, std::uint8_t type);
+ bool inst_store_ov_asm_type(lauf_asm_local* local, std::size_t index, std::uint8_t type);
+
+ bool push_instruction_for_scope(scope_type type, ovdl::symbol<char> scope_symbol);
+
// Bytecode instructions //
void inst_push_scope_this();
void inst_push_scope_from();
@@ -86,6 +105,8 @@ namespace OpenVic::Vm {
void inst_push_get_province_owner();
void inst_push_get_province_state();
+ void inst_push_get_state_cultural_union();
+
void inst_push_get_pop_location();
void inst_push_get_pop_country();
void inst_push_get_pop_cultural_union();
@@ -158,11 +179,13 @@ namespace OpenVic::Vm {
Module _module;
AsmBuilder _builder;
ovdl::v2script::Parser const& _parser;
- std::unordered_set<ovdl::symbol<char>, symbol_hash> _all_scopes;
- std::unordered_set<ovdl::symbol<char>, symbol_hash> _country_scopes;
- std::unordered_set<ovdl::symbol<char>, symbol_hash> _province_scopes;
- std::unordered_set<ovdl::symbol<char>, symbol_hash> _pop_scopes;
bool _has_top_level_country_scopes;
bool _has_top_level_province_scopes;
};
}
+
+template<>
+struct OpenVic::enable_bitfield<OpenVic::Vm::Codegen::scope_type> : std::true_type {};
+
+template<>
+struct OpenVic::enable_bitfield<OpenVic::Vm::Codegen::trigger_modifier> : std::true_type {};