XPlor/libs/dsl/reverseeval.cpp

299 lines
9.4 KiB
C++
Raw Normal View History

#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::Unary>(expr.node)) {
const auto& unary = std::get<Expr::Unary>(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::Binary>(expr.node)) {
const auto& binary = std::get<Expr::Binary>(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::Call>(expr.node)) {
chain.reason = "Function calls are not invertible";
return false;
}
// Case 6: Member access (x.field)
if (std::holds_alternative<Expr::Member>(expr.node)) {
chain.reason = "Member access expressions are not invertible";
return false;
}
// Case 7: Pipe operations
if (std::holds_alternative<Expr::Pipe>(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<ReverseEvaluator*>(this)->analyzeRecursive(expr, chain);
}
QString ReverseEvaluator::whyNotInvertible(const Expr& expr) const {
InverseChain chain;
if (const_cast<ReverseEvaluator*>(this)->analyzeRecursive(expr, chain)) {
return QString(); // It is invertible
}
return chain.reason;
}
bool ReverseEvaluator::isConstant(const Expr& expr) const {
return std::holds_alternative<Expr::Int>(expr.node) ||
std::holds_alternative<Expr::String>(expr.node);
}
qint64 ReverseEvaluator::getConstantValue(const Expr& expr) const {
if (std::holds_alternative<Expr::Int>(expr.node)) {
return std::get<Expr::Int>(expr.node).v;
}
return 0;
}
bool ReverseEvaluator::isVariable(const Expr& expr) const {
return std::holds_alternative<Expr::Var>(expr.node);
}
QString ReverseEvaluator::getVariableName(const Expr& expr) const {
if (std::holds_alternative<Expr::Var>(expr.node)) {
return std::get<Expr::Var>(expr.node).name;
}
return QString();
}