XPlor/libs/dsl/reverseeval.h
njohnson 3e3883cfeb Add DSL journaling infrastructure for field editing and write-back
Introduce operation journaling to track how values are read from the
binary stream, enabling future write-back of edited fields.

New components:
- OperationJournal: Records read operations with stream offsets, sizes,
  byte order, and original values during parsing
- ReverseEval: Analyzes expressions for invertibility (e.g., x+5 can be
  reversed to compute the source field when editing the result)
- Recompiler: Infrastructure for recompiling modified values back to
  binary format

Interpreter enhancements:
- Add runTypeInternalWithJournal() for journaled parsing
- Journal scalar reads, byte reads, strings, skip/seek operations
- Add deadrising_lzx() built-in for Dead Rising 2 LZX decompression
- Support _deadrising_lzx_stem context variable for nested archives

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:53:20 -05:00

106 lines
3.6 KiB
C++

#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