/* Copyright (c) 2024 Julian Benda * * This file is part of inkCPP which is released under MIT license. * See file LICENSE.txt or go to * https://github.com/JBenda/inkcpp for full license details. */ /// implements operations on lists #include "stack.h" #include "value.h" #include "operations.h" #include "list_table.h" #define call4_Wrap(OP, RET, FUN) call4_Wrap_diff(OP, RET, RET, RET, RET, FUN) #define call4_Wrap_diff(OP, RET0, RET1, RET2, RET3, FUN) \ void operation::operator()( \ basic_eval_stack& stack, value* vals \ ) \ { \ call4(RET0, RET1, RET2, RET3, FUN); \ } #define call4(RET0, RET1, RET2, RET3, FUN) \ if (vals[0].type() == value_type::list_flag) { \ if (vals[1].type() == value_type::list) { \ stack.push(value{}.set( \ _list_table.FUN(vals[0].get(), vals[1].get()) \ )); \ } else { \ inkAssert( \ vals[1].type() == value_type::list_flag, \ "list operation was called but second argument is not a list or list_flag" \ ); \ stack.push(value{}.set(_list_table.FUN( \ vals[0].get(), vals[1].get() \ ))); \ } \ } else { \ inkAssert( \ vals[0].type() == value_type::list, \ "list operation was called but first argument is not a list or a list_flag!" \ ); \ if (vals[1].type() == value_type::list) { \ stack.push(value{}.set( \ _list_table.FUN(vals[0].get(), vals[1].get()) \ )); \ } else { \ inkAssert( \ vals[1].type() == value_type::list_flag, \ "list operation was called but second argument ist not a list or list_flag!" \ ); \ stack.push(value{}.set( \ _list_table.FUN(vals[0].get(), vals[1].get()) \ )); \ } \ } #define call2(OP, RET0, RET1, FUN) \ void operation::operator()( \ basic_eval_stack& stack, value* vals \ ) \ { \ stack.push(value{}.set(_list_table.FUN(vals[0].get()))); \ } \ void operation::operator()( \ basic_eval_stack& stack, value* vals \ ) \ { \ stack.push(value{}.set(_list_table.FUN(vals[0].get()) \ )); \ } namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals ) { if (vals[1].type() == value_type::int32 || vals[1].type() == value_type::uint32) { int i = vals[1].type() == value_type::int32 ? vals[1].get() : static_cast(vals[1].get()); inkAssert( vals[0].type() == value_type::list, "try to use list add function but value is not of type list" ); stack.push(value{}.set(_list_table.add(vals[0].get(), i))); } else { call4(list, list, list, list, add); } } void operation::operator()( basic_eval_stack& stack, value* vals ) { inkAssert( vals[0].type() == value_type::list_flag, "try to use add function with list_flag results but first argument is not a list_flag!" ); inkAssert( vals[1].type() == value_type::int32 || vals[1].type() == value_type::uint32, "try modify a list flag with a non intiger type!" ); int i = vals[1].type() == value_type::int32 ? vals[1].get() : static_cast(vals[1].get()); stack.push( value{}.set(_list_table.add(vals[0].get(), i)) ); } void operation::operator()( basic_eval_stack& stack, value* vals ) { if (vals[1].type() == value_type::int32 || vals[1].type() == value_type::uint32) { int i = vals[1].type() == value_type::int32 ? vals[1].get() : vals[1].get(); inkAssert( vals[0].type() == value_type::list, "A in list resulting subtraction needs at leas one list as argument!" ); stack.push(value{}.set(_list_table.sub(vals[0].get(), i))); } else { call4(list_flag, list_flag, list, list, sub); } } void operation::operator()( basic_eval_stack& stack, value* vals ) { inkAssert( vals[0].type() == value_type::list_flag, "subtraction resulting in list_flag needs a list_flag as first arguments!" ); inkAssert( vals[1].type() == value_type::int32 || vals[1].type() == value_type::uint32, "Try to subtract non integer value from list_flag." ); int i = vals[1].type() == value_type::int32 ? vals[1].get() : vals[1].get(); stack.push( value{}.set(_list_table.sub(vals[0].get(), i)) ); } void operation::operator()( basic_eval_stack& stack, value* vals ) { call4(list_flag, list_flag, list, list_flag, intersect); } call2(NOT, boolean, boolean, not_bool); call2(LIST_COUNT, int32, int32, count); call2(LIST_MIN, list_flag, list_flag, min); call2(LIST_MAX, list_flag, list_flag, max); call2(LIST_ALL, list, list, all); call2(LIST_INVERT, list, list, invert); call4_Wrap(LESS_THAN, boolean, less); call4_Wrap(LESS_THAN_EQUALS, boolean, less_equal); call4_Wrap(GREATER_THAN, boolean, greater); call4_Wrap(GREATER_THAN_EQUALS, boolean, greater_equal); call4_Wrap(IS_EQUAL, boolean, equal); call4_Wrap(NOT_EQUAL, boolean, not_equal); call4_Wrap(HAS, boolean, has); call4_Wrap(HASNT, boolean, hasnt); void operation::operator()( basic_eval_stack& stack, value* vals ) { stack.push( value{}.set(_list_table.lrnd(vals[0].get(), _prng)) ); } void operation::operator()( basic_eval_stack& stack, value* vals ) { stack.push( value{}.set(_list_table.lrnd(vals[0].get())) ); } void operation::operator()( basic_eval_stack& stack, value* vals ) { inkAssert( vals[0].type() == value_type::string, "list_flag construction needs the list name as string as first argument!" ); inkAssert( vals[1].type() == value_type::int32, "list_flag construction needs the flag numeric value as second argument!" ); list_flag entry = _list_table.get_list_id(vals[0].get()); entry.flag = vals[1].get(); entry = _list_table.external_fvalue_to_internal(entry); stack.push(value{}.set(entry)); } int get_limit(const value& val, const list_table& lists) { if (val.type() == value_type::int32) { return val.get(); } else { inkAssert(val.type() == value_type::list_flag, "flag value must be a integer or a list_flag"); return lists.get_flag_value(val.get()); } } void operation::operator()( basic_eval_stack& stack, value* vals ) { inkAssert(vals[0].type() == value_type::list, "Can't get range of non list type!"); stack.push(value{}.set(_list_table.range( vals[0].get(), get_limit(vals[1], _list_table), get_limit(vals[2], _list_table) ))); } void convert_bools(value* vals, const list_table& lists, bool* res) { for (int i = 0; i < 2; ++i) { switch (vals[i].type()) { case value_type::list: res[i] = lists.to_bool(vals[i].get()); break; case value_type::list_flag: res[i] = lists.to_bool(vals[i].get()); break; default: res[i] = casting::numeric_cast(vals[i]); } } } void operation::operator()( basic_eval_stack& stack, value* vals ) { bool res[2]; convert_bools(vals, _list_table, res); stack.push(value{}.set(res[0] && res[1])); } void operation::operator()( basic_eval_stack& stack, value* vals ) { bool res[2]; convert_bools(vals, _list_table, res); stack.push(value{}.set(res[0] && res[1])); } void operation::operator()( basic_eval_stack& stack, value* vals ) { bool res[2]; convert_bools(vals, _list_table, res); stack.push(value{}.set(res[0] || res[1])); } void operation::operator()( basic_eval_stack& stack, value* vals ) { bool res[2]; convert_bools(vals, _list_table, res); stack.push(value{}.set(res[0] || res[1])); } } // namespace ink::runtime::internal