#ifndef REVERSEEVAL_H #define REVERSEEVAL_H #include "operationjournal.h" #include "ast.h" #include /** * @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