#include "reverseeval.h" InverseChain ReverseEvaluator::analyzeExpression(const Expr& expr, const QString& targetField) { Q_UNUSED(targetField); InverseChain chain; chain.canInvert = analyzeRecursive(expr, chain); if (!chain.canInvert && chain.reason.isEmpty()) { chain.reason = "Expression structure not supported for inversion"; } return chain; } bool ReverseEvaluator::analyzeRecursive(const Expr& expr, InverseChain& chain) { // Case 1: Simple variable reference - this is the source field if (isVariable(expr)) { chain.sourceField = getVariableName(expr); return true; } // Case 2: Literal value - not invertible (no source to modify) if (isConstant(expr)) { chain.reason = "Expression is a constant with no source field"; return false; } // Case 3: Unary operations if (std::holds_alternative(expr.node)) { const auto& unary = std::get(expr.node); if (unary.op == "-") { // -x → apply negate operation if (!analyzeRecursive(*unary.rhs, chain)) { return false; } chain.operations.append({InvertOp::Negate, 0}); return true; } else if (unary.op == "~") { // ~x → bitwise not is self-inverse, but not in InvertOp chain.reason = "Bitwise NOT inversion not implemented"; return false; } else if (unary.op == "!") { // !x → not invertible (boolean conversion loses information) chain.reason = "Logical NOT is not invertible (boolean conversion)"; return false; } } // Case 4: Binary operations if (std::holds_alternative(expr.node)) { const auto& binary = std::get(expr.node); bool leftIsVar = !isConstant(*binary.lhs); bool rightIsVar = !isConstant(*binary.rhs); // If both sides are non-constant, we can't invert (multiple sources) if (leftIsVar && rightIsVar) { chain.reason = "Expression has multiple variable sources"; return false; } // If both sides are constant, nothing to invert if (!leftIsVar && !rightIsVar) { chain.reason = "Expression has no variable sources"; return false; } // Get the constant value qint64 constVal; const Expr* varExpr; bool constOnRight; if (leftIsVar) { constVal = getConstantValue(*binary.rhs); varExpr = binary.lhs.get(); constOnRight = true; } else { constVal = getConstantValue(*binary.lhs); varExpr = binary.rhs.get(); constOnRight = false; } // First, recurse into the variable side if (!analyzeRecursive(*varExpr, chain)) { return false; } // Now add the inverse operation based on the binary operator const QString& op = binary.op; if (op == "+") { // x = y + c → y = x - c // x = c + y → y = x - c (same inverse) chain.operations.append({InvertOp::Sub, constVal}); return true; } else if (op == "-") { if (constOnRight) { // x = y - c → y = x + c chain.operations.append({InvertOp::Add, constVal}); } else { // x = c - y → y = c - x chain.operations.append({InvertOp::SubFrom, constVal}); } return true; } else if (op == "*") { // x = y * c → y = x / c if (constVal == 0) { chain.reason = "Cannot invert multiplication by zero"; return false; } chain.operations.append({InvertOp::Div, constVal}); return true; } else if (op == "/") { if (constOnRight) { // x = y / c → y = x * c chain.operations.append({InvertOp::Mul, constVal}); } else { // x = c / y → y = c / x chain.reason = "Division with constant on left is not supported"; return false; } return true; } else if (op == "%") { chain.reason = "Modulo operation is not invertible (infinite solutions)"; return false; } else if (op == "<<") { if (constOnRight) { // x = y << c → y = x >> c chain.operations.append({InvertOp::Shr, constVal}); } else { chain.reason = "Left shift with constant on left is not supported"; return false; } return true; } else if (op == ">>") { if (constOnRight) { // x = y >> c → y = x << c chain.operations.append({InvertOp::Shl, constVal}); } else { chain.reason = "Right shift with constant on left is not supported"; return false; } return true; } else if (op == "&") { chain.reason = "Bitwise AND is not invertible (bits masked out are lost)"; return false; } else if (op == "|") { chain.reason = "Bitwise OR is not invertible (cannot determine original bits)"; return false; } else if (op == "^") { // x = y ^ c → y = x ^ c (XOR is its own inverse!) chain.operations.append({InvertOp::BitXor, constVal}); return true; } else if (op == "==" || op == "!=" || op == "<" || op == "<=" || op == ">" || op == ">=" || op == "&&" || op == "||") { chain.reason = "Comparison/logical operations are not invertible"; return false; } else { chain.reason = QString("Unknown binary operator: %1").arg(op); return false; } } // Case 5: Function calls - generally not invertible if (std::holds_alternative(expr.node)) { chain.reason = "Function calls are not invertible"; return false; } // Case 6: Member access (x.field) if (std::holds_alternative(expr.node)) { chain.reason = "Member access expressions are not invertible"; return false; } // Case 7: Pipe operations if (std::holds_alternative(expr.node)) { chain.reason = "Pipe expressions are not invertible"; return false; } chain.reason = "Unknown expression type"; return false; } qint64 ReverseEvaluator::computeInverse(const InverseChain& chain, qint64 editedValue) const { if (!chain.canInvert) { return editedValue; // Can't invert, return as-is } qint64 result = editedValue; // Apply operations in order (they were added in the order they need to be applied) for (const auto& op_pair : chain.operations) { result = applyInverseOp(result, op_pair.first, op_pair.second); } return result; } QVariant ReverseEvaluator::computeInverse(const InverseChain& chain, const QVariant& editedValue) const { return QVariant::fromValue(computeInverse(chain, editedValue.toLongLong())); } qint64 ReverseEvaluator::applyInverseOp(qint64 value, InvertOp op, qint64 operand) const { switch (op) { case InvertOp::Identity: return value; case InvertOp::Add: return value + operand; case InvertOp::Sub: return value - operand; case InvertOp::SubFrom: // For x = c - y, inverse is y = c - x return operand - value; case InvertOp::Mul: return value * operand; case InvertOp::Div: if (operand == 0) return value; // Avoid division by zero return value / operand; case InvertOp::Shl: return value << operand; case InvertOp::Shr: return value >> operand; case InvertOp::Negate: return -value; case InvertOp::BitAnd: case InvertOp::BitOr: case InvertOp::Modulo: // These are non-invertible, should never be called return value; case InvertOp::BitXor: // XOR is self-inverse return value ^ operand; } return value; } bool ReverseEvaluator::isInvertible(const Expr& expr) const { InverseChain chain; return const_cast(this)->analyzeRecursive(expr, chain); } QString ReverseEvaluator::whyNotInvertible(const Expr& expr) const { InverseChain chain; if (const_cast(this)->analyzeRecursive(expr, chain)) { return QString(); // It is invertible } return chain.reason; } bool ReverseEvaluator::isConstant(const Expr& expr) const { return std::holds_alternative(expr.node) || std::holds_alternative(expr.node); } qint64 ReverseEvaluator::getConstantValue(const Expr& expr) const { if (std::holds_alternative(expr.node)) { return std::get(expr.node).v; } return 0; } bool ReverseEvaluator::isVariable(const Expr& expr) const { return std::holds_alternative(expr.node); } QString ReverseEvaluator::getVariableName(const Expr& expr) const { if (std::holds_alternative(expr.node)) { return std::get(expr.node).name; } return QString(); }