XPlor/libs/dsl/reverseeval.h

106 lines
3.6 KiB
C
Raw Normal View History

#ifndef REVERSEEVAL_H
#define REVERSEEVAL_H
#include "operationjournal.h"
#include "ast.h"
#include <QVariant>
/**
* @brief Analyzes expressions to determine invertibility and compute reverse operations.
*
* This is used during parsing to build InverseChain structures that enable
* editing computed fields. When a user edits a computed field, the inverse
* chain is used to calculate what the source field's value should be.
*
* Invertible operations (single source):
* - Addition: x = y + c y = x - c
* - Subtraction: x = y - c y = x + c
* - Multiplication: x = y * c y = x / c
* - Division: x = y / c y = x * c
* - Left shift: x = y << c y = x >> c
* - Right shift: x = y >> c y = x << c
* - Negation: x = -y y = -x
* - Bitwise XOR: x = y ^ c y = x ^ c (XOR is self-inverse)
*
* Non-invertible operations (information loss):
* - Bitwise AND: x = y & c (bits masked out are lost)
* - Bitwise OR: x = y | c (can't determine which bits were originally set)
* - Modulo: x = y % c (infinite solutions)
* - Multiple sources: x = a + b (can't determine which to adjust)
*/
class ReverseEvaluator {
public:
ReverseEvaluator() = default;
/**
* @brief Analyze an expression to build an inverse chain.
* @param expr The expression to analyze
* @param targetField The field name being assigned to (for context)
* @return InverseChain with source field and operations, or canInvert=false if not invertible
*/
InverseChain analyzeExpression(const Expr& expr, const QString& targetField = QString());
/**
* @brief Apply an inverse chain to compute the source value from an edited value.
* @param chain The inverse chain from analyzeExpression
* @param editedValue The new value the user entered
* @return The computed source value
*/
qint64 computeInverse(const InverseChain& chain, qint64 editedValue) const;
/**
* @brief Apply inverse chain with QVariant (delegates to qint64 version).
*/
QVariant computeInverse(const InverseChain& chain, const QVariant& editedValue) const;
/**
* @brief Quick check if an expression is invertible.
* @param expr The expression to check
* @return true if the expression can be reversed to a single source field
*/
bool isInvertible(const Expr& expr) const;
/**
* @brief Get human-readable reason why an expression is not invertible.
* @param expr The expression to check
* @return Empty string if invertible, otherwise the reason
*/
QString whyNotInvertible(const Expr& expr) const;
private:
/**
* @brief Recursively analyze an expression, building up the inverse chain.
* @param expr The expression to analyze
* @param chain The chain being built (modified in place)
* @return true if analysis succeeded and expression is invertible
*/
bool analyzeRecursive(const Expr& expr, InverseChain& chain);
/**
* @brief Check if an expression is a constant (literal value).
*/
bool isConstant(const Expr& expr) const;
/**
* @brief Get the constant value from an expression.
*/
qint64 getConstantValue(const Expr& expr) const;
/**
* @brief Check if an expression is a simple variable reference.
*/
bool isVariable(const Expr& expr) const;
/**
* @brief Get the variable name from an expression.
*/
QString getVariableName(const Expr& expr) const;
/**
* @brief Apply a single inverse operation.
*/
qint64 applyInverseOp(qint64 value, InvertOp op, qint64 operand) const;
};
#endif // REVERSEEVAL_H