mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-21 22:03:36 -07:00
Added LogicExpressions.
This commit is contained in:
parent
e1f689ade4
commit
40b3e899a5
2 changed files with 1334 additions and 0 deletions
1034
soh/soh/Enhancements/randomizer/logic_expression.cpp
Normal file
1034
soh/soh/Enhancements/randomizer/logic_expression.cpp
Normal file
File diff suppressed because it is too large
Load diff
300
soh/soh/Enhancements/randomizer/logic_expression.h
Normal file
300
soh/soh/Enhancements/randomizer/logic_expression.h
Normal file
|
@ -0,0 +1,300 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "logic.h"
|
||||
extern std::shared_ptr<Rando::Logic> logic;
|
||||
|
||||
class LogicExpression {
|
||||
public:
|
||||
using ValueVariant = std::variant<bool, int, uint8_t, uint16_t>;
|
||||
// Define callback type for evaluation tracing
|
||||
using EvaluationCallback = std::function<void(const std::string&, const std::string&, int, const std::string&, const ValueVariant&)>;
|
||||
|
||||
static std::unique_ptr<LogicExpression> Parse(const std::string& exprStr);
|
||||
std::string ToString() const;
|
||||
const std::vector<std::unique_ptr<LogicExpression>>& GetChildren() const;
|
||||
|
||||
// Add optional callback parameter to Evaluate
|
||||
template <typename T> T Evaluate(const EvaluationCallback& callback = nullptr) const {
|
||||
return Impl::GetValue<T>(impl->Evaluate("0", 0, callback));
|
||||
}
|
||||
|
||||
private:
|
||||
struct Impl {
|
||||
enum class Type { And, Or, Not, Comparison, FunctionCall, Value, Add, Subtract, Multiply, Divide, Ternary };
|
||||
|
||||
enum class ValueType { Identifier, Boolean, Number, Enum };
|
||||
|
||||
Type type;
|
||||
ValueType valueType;
|
||||
std::string value;
|
||||
std::string operation;
|
||||
std::string functionName;
|
||||
std::vector<std::shared_ptr<Impl>> children;
|
||||
std::unique_ptr<std::string> expressionString;
|
||||
Impl* parent;
|
||||
size_t startIndex;
|
||||
size_t endIndex;
|
||||
|
||||
// Modified to accept path and depth
|
||||
ValueVariant Evaluate(const std::string& path = "0", int depth = 0, const EvaluationCallback& callback = nullptr) const;
|
||||
|
||||
// Helper to get a string representation of the type
|
||||
std::string GetTypeString() const;
|
||||
|
||||
template <typename T> static T GetValue(const ValueVariant& value) {
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
if (std::holds_alternative<bool>(value))
|
||||
return std::get<bool>(value);
|
||||
if (std::holds_alternative<int>(value))
|
||||
return std::get<int>(value) != 0;
|
||||
if (std::holds_alternative<uint8_t>(value))
|
||||
return std::get<uint8_t>(value) != 0;
|
||||
if (std::holds_alternative<uint16_t>(value))
|
||||
return std::get<uint16_t>(value) != 0;
|
||||
throw std::bad_variant_access();
|
||||
} else if constexpr (std::is_same_v<T, int>)
|
||||
return std::get<int>(value);
|
||||
else if constexpr (std::is_same_v<T, uint8_t>)
|
||||
return std::holds_alternative<uint8_t>(value) ? std::get<uint8_t>(value)
|
||||
: static_cast<uint8_t>(std::get<int>(value));
|
||||
else if constexpr (std::is_same_v<T, uint16_t>)
|
||||
return std::holds_alternative<uint16_t>(value) ? std::get<uint16_t>(value)
|
||||
: static_cast<uint16_t>(std::get<int>(value));
|
||||
else if constexpr (std::is_enum_v<T>)
|
||||
return static_cast<T>(std::get<int>(value));
|
||||
else if constexpr (std::is_same_v<T, ValueVariant>)
|
||||
return value;
|
||||
else
|
||||
static_assert(sizeof(T) == 0, "Unsupported function parameter type");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string GetExprErrorContext() const;
|
||||
// Updated to pass callback to children
|
||||
ValueVariant EvaluateFunction(const std::string& path = "0", int depth = 0, const EvaluationCallback& callback = nullptr) const;
|
||||
ValueVariant EvaluateEnum() const;
|
||||
ValueVariant EvaluateVariable() const;
|
||||
// Updated to pass callback to children
|
||||
ValueVariant EvaluateArithmetic(char op, const std::string& path = "0", int depth = 0,
|
||||
const EvaluationCallback& callback = nullptr) const;
|
||||
|
||||
using FunctionAdapter = std::function<ValueVariant(const std::vector<std::shared_ptr<Impl>>&, const std::string&, int, const EvaluationCallback&)>;
|
||||
|
||||
static std::unordered_map<std::string, FunctionAdapter> functionAdapters;
|
||||
static void PopulateFunctionAdapters();
|
||||
static std::unordered_map<std::string, int> enumMap;
|
||||
static void PopulateEnumMap();
|
||||
static std::unordered_map<std::string, FunctionAdapter> variableAdapters;
|
||||
static void PopulateVariableAdapters();
|
||||
|
||||
friend bool IsEnumConstant(const std::string& s);
|
||||
|
||||
// Define FunctionAdapter as a unified interface for wrapping functions.
|
||||
// It represents a function that takes a vector of LogicExpression pointers
|
||||
// and returns a ValueVariant.
|
||||
// using FunctionAdapter = std::function<ValueVariant(const
|
||||
// std::vector<std::unique_ptr<LogicExpression::Impl>>&)>;
|
||||
|
||||
// Below are helper templates and functions that enable dynamic registration and invocation
|
||||
// of functions using the "Template Magic" method. These utilities deduce function parameter types,
|
||||
// convert LogicExpression arguments to the expected types, check argument counts, and finally call the
|
||||
// functions. The idea is to expose a unified FunctionAdapter interface that wraps any function with varying
|
||||
// argument types.
|
||||
|
||||
// Helper: function_traits for lambdas and function pointers.
|
||||
// This trait extracts the return type and parameter types for a function pointer or lambda.
|
||||
// It supports both function pointers (first specialization) and lambda objects (via the deduced operator()).
|
||||
template <typename T> struct function_traits;
|
||||
|
||||
template <typename R, typename... Args> struct function_traits<R (*)(Args...)> {
|
||||
using result_type = R; // The return type of the function.
|
||||
using args_tuple = std::tuple<Args...>; // A tuple of the function's parameter types.
|
||||
};
|
||||
|
||||
// For lambdas and other callable objects, we deduce the type from the operator() member.
|
||||
template <typename Function> struct function_traits : function_traits<decltype(&Function::operator())> {};
|
||||
|
||||
// Specialization for const member function pointers (for lambdas).
|
||||
template <typename C, typename R, typename... Args> struct function_traits<R (C::*)(Args...) const> {
|
||||
using result_type = R; // The return type.
|
||||
using args_tuple = std::tuple<Args...>; // Tuple of argument types.
|
||||
};
|
||||
|
||||
// --- New specialization for non-const member function pointers ---
|
||||
template <typename C, typename R, typename... Args> struct function_traits<R (C::*)(Args...)> {
|
||||
using result_type = R; // The return type.
|
||||
using args_tuple = std::tuple<Args...>; // Tuple of argument types.
|
||||
};
|
||||
|
||||
using ConditionFn = std::function<bool()>;
|
||||
|
||||
// Helper to conditionally convert an LogicExpression argument.
|
||||
// If T is the expected function type (e.g. ConditionFn), this helper wraps the expression into a
|
||||
// callable lambda. Otherwise, it evaluates the expression immediately to convert it into a bool, int, or enum
|
||||
// value. Template parameter T represents the expected type of the argument for the function
|
||||
// being registered.
|
||||
template <typename T>
|
||||
static T EvaluateArg(const std::shared_ptr<LogicExpression::Impl>& expr, const std::string& path,
|
||||
int depth, const EvaluationCallback& callback) {
|
||||
if constexpr (std::is_same_v<T, ConditionFn>) {
|
||||
// Wrap the expression in a lambda to delay evaluation until the function is called.
|
||||
return [&expr = *expr, path, depth, callback]() -> bool {
|
||||
return std::get<bool>(expr.Evaluate(path, depth + 1, callback));
|
||||
};
|
||||
} else {
|
||||
// For value types, evaluate the expression immediately.
|
||||
try {
|
||||
auto value = expr->Evaluate(path, depth + 1, callback);
|
||||
return GetValue<T>(value);
|
||||
} catch (const std::bad_variant_access&) {
|
||||
throw std::runtime_error("Invalid argument type: " + expr->GetExprErrorContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to call the function with extracted (converted) arguments.
|
||||
// This function unpacks the tuple of expected argument types using an index sequence,
|
||||
// converts each provided LogicExpression (using EvaluateArg) to the required type, and then
|
||||
// calls 'function'. functionName is used for error reporting.
|
||||
template <typename Function, typename... Args, size_t... Is>
|
||||
static ValueVariant CallFunctionImpl(const std::string& functionName, Function function,
|
||||
const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback,
|
||||
std::index_sequence<Is...>) {
|
||||
// Each args[Is] is converted to its expected type using EvaluateArg<Args> and passed to
|
||||
// 'function'.
|
||||
return function(EvaluateArg<Args>(args[Is], path + "." + std::to_string(Is), depth, callback)...);
|
||||
}
|
||||
|
||||
// Wraps a function into a FunctionAdapter by ensuring the correct number of arguments are provided
|
||||
// and by utilizing CallFunctionImpl to perform the actual function call with type-checked
|
||||
// parameters. It throws an error if the number of provided arguments does not match the expected count.
|
||||
// Template parameter Function is the original function type, and Args are its parameter types.
|
||||
template <typename Function, typename... Args>
|
||||
static ValueVariant MakeFunctionAdapter(const std::string& functionName, Function function,
|
||||
const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback) {
|
||||
constexpr size_t expectedArgCount = sizeof...(Args);
|
||||
if (args.size() != expectedArgCount) {
|
||||
throw std::runtime_error("Function " + functionName + " expects " + std::to_string(expectedArgCount) +
|
||||
" arguments, but got " + std::to_string(args.size()));
|
||||
}
|
||||
return CallFunctionImpl<Function, Args...>(functionName, function, args, path, depth, callback,
|
||||
std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
// Wrapper that deduces parameter types from the function and returns a FunctionAdapter.
|
||||
// This adapter wraps the original function so that it can be called with a vector of LogicExpression pointers.
|
||||
// It performs deducing of the function's expected parameter types via function_traits,
|
||||
// and then uses MakeFunctionAdapter to perform argument validation and conversion before invocation.
|
||||
template <typename Function>
|
||||
static FunctionAdapter RegisterFunction(const std::string& functionName, Function function) {
|
||||
using traits = function_traits<Function>;
|
||||
using ArgsTuple = typename traits::args_tuple;
|
||||
return [functionName, function](const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback) -> ValueVariant {
|
||||
return std::apply(
|
||||
[&](auto... dummy) {
|
||||
return MakeFunctionAdapter<Function, decltype(dummy)...>(functionName, function, args,
|
||||
path, depth, callback);
|
||||
},
|
||||
ArgsTuple{}); // Unpacks the expected parameter types.
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to call pointer-to-member function on "logic" with extracted arguments.
|
||||
template <typename MemberFunction, typename... Args, size_t... Is>
|
||||
static ValueVariant CallMemberFunctionImpl(const std::string& functionName, MemberFunction function,
|
||||
const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback,
|
||||
std::index_sequence<Is...>) {
|
||||
return ((*logic).*function)(EvaluateArg<Args>(args[Is], path + "." + std::to_string(Is), depth, callback)...);
|
||||
}
|
||||
|
||||
// Implementation of RegisterLogicFunction that wraps a pointer-to-member function from Logic.
|
||||
template <typename Function>
|
||||
static FunctionAdapter RegisterLogicFunction(const std::string& functionName, Function function) {
|
||||
using traits = function_traits<Function>;
|
||||
using ArgsTuple = typename traits::args_tuple;
|
||||
return [functionName, function](const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback) -> ValueVariant {
|
||||
return std::apply(
|
||||
[&](auto... dummy) {
|
||||
constexpr size_t expectedArgCount = sizeof...(dummy);
|
||||
if (args.size() != expectedArgCount) {
|
||||
throw std::runtime_error("Function " + functionName + " expects " +
|
||||
std::to_string(expectedArgCount) + " arguments, but got " +
|
||||
std::to_string(args.size()));
|
||||
}
|
||||
return CallMemberFunctionImpl<Function, decltype(dummy)...>(
|
||||
functionName, function, args, path, depth, callback,
|
||||
std::index_sequence_for<decltype(dummy)...>{});
|
||||
},
|
||||
ArgsTuple{});
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to call member function with default parameters
|
||||
// This function evaluates provided arguments and fills missing ones with defaults.
|
||||
template <typename Function, typename Tuple, size_t... Is>
|
||||
static ValueVariant
|
||||
CallMemberFunctionWithDefaultsImpl(const std::string& functionName, Function function,
|
||||
const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback,
|
||||
Tuple&& defaults, std::index_sequence<Is...>) {
|
||||
constexpr size_t expectedArgCount = sizeof...(Is);
|
||||
|
||||
// Ensure the number of provided arguments does not exceed the expected count
|
||||
if (args.size() > expectedArgCount) {
|
||||
throw std::runtime_error("Function " + functionName + " expects up to " +
|
||||
std::to_string(expectedArgCount) + " arguments, but got " +
|
||||
std::to_string(args.size()));
|
||||
}
|
||||
|
||||
// Evaluate provided arguments and fill missing ones with defaults
|
||||
return ((*logic).*
|
||||
function)((Is < args.size() ?
|
||||
EvaluateArg<std::tuple_element_t<Is, std::decay_t<Tuple>>>(args[Is], path + "." + std::to_string(Is), depth, callback)
|
||||
: std::get<Is>(defaults))...);
|
||||
}
|
||||
|
||||
// Wrapper for member functions with default parameters
|
||||
template <typename Function, typename... Args>
|
||||
static FunctionAdapter RegisterLogicFunctionWithDefaults(const std::string& functionName, Function function,
|
||||
std::tuple<Args...> defaults) {
|
||||
return [functionName, function, defaults](const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback) -> ValueVariant {
|
||||
constexpr size_t expectedArgCount = sizeof...(Args);
|
||||
return CallMemberFunctionWithDefaultsImpl(functionName, function, args, path, depth, callback,
|
||||
defaults,
|
||||
std::make_index_sequence<expectedArgCount>{});
|
||||
};
|
||||
}
|
||||
|
||||
// Add this function in the Template Magic section to implement variable registration.
|
||||
template <typename T> static FunctionAdapter RegisterLogicVariable(const std::string& varName, T Rando::Logic::*var) {
|
||||
return [varName, var](const std::vector<std::shared_ptr<LogicExpression::Impl>>& args,
|
||||
const std::string& path, int depth, const EvaluationCallback& callback) -> ValueVariant {
|
||||
if (!args.empty()) {
|
||||
throw std::runtime_error("Variable " + varName + " expects 0 arguments, but got " +
|
||||
std::to_string(args.size()));
|
||||
}
|
||||
auto value = (*logic).*var;
|
||||
return GetValue<T>(value);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<Impl> impl;
|
||||
std::vector<std::unique_ptr<LogicExpression>> children;
|
||||
|
||||
friend class Parser;
|
||||
friend bool IsEnumConstant(const std::string& s);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue