Add current input tracking and enhance debug logging in ExpressionConfigEntry
This commit is contained in:
@@ -10,6 +10,7 @@ namespace BanquetForCyka {
|
|||||||
private Func<float, float> _compiledExpression;
|
private Func<float, float> _compiledExpression;
|
||||||
private string _lastValidExpression;
|
private string _lastValidExpression;
|
||||||
private bool _isValid = true;
|
private bool _isValid = true;
|
||||||
|
private string _currentInput;
|
||||||
|
|
||||||
public string Value {
|
public string Value {
|
||||||
get => _configEntry.Value;
|
get => _configEntry.Value;
|
||||||
@@ -17,164 +18,221 @@ namespace BanquetForCyka {
|
|||||||
if (value == _configEntry.Value)
|
if (value == _configEntry.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_currentInput = value;
|
||||||
// Try to compile the expression
|
// Try to compile the expression
|
||||||
try {
|
try {
|
||||||
LogDebug($"Attempting to compile expression: '{value}'");
|
LogDebug($"=== Starting compilation of new expression ===", _debug);
|
||||||
var newExpr = ParseExpression(value);
|
LogDebug($"Input expression: '{value}'", _debug);
|
||||||
|
var newExpr = ParseExpression(value, _debug);
|
||||||
_compiledExpression = newExpr;
|
_compiledExpression = newExpr;
|
||||||
_lastValidExpression = value;
|
_lastValidExpression = value;
|
||||||
_isValid = true;
|
_isValid = true;
|
||||||
_configEntry.Value = value;
|
LogDebug($"=== Expression compilation successful ===", _debug);
|
||||||
LogDebug($"Expression '{value}' compiled successfully");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_isValid = false;
|
_isValid = false;
|
||||||
LogDebug($"Failed to compile expression '{value}': {e.Message}");
|
LogDebug($"=== Expression compilation failed ===", _debug);
|
||||||
LogDebug($"Stack trace: {e.StackTrace}");
|
LogDebug($"Error: {e.Message}", _debug);
|
||||||
// Don't update the config value if invalid
|
LogDebug($"Stack trace: {e.StackTrace}", _debug);
|
||||||
|
LogDebug($"Expression is invalid but keeping input: '{value}'", _debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValid => _isValid;
|
public bool IsValid => _isValid;
|
||||||
public string LastValidExpression => _lastValidExpression;
|
public string LastValidExpression => _lastValidExpression;
|
||||||
|
public string CurrentInput => _currentInput;
|
||||||
|
|
||||||
public ExpressionConfigEntry(ConfigFile config, string section, string key, string defaultValue,
|
public ExpressionConfigEntry(ConfigFile config, string section, string key, string defaultValue,
|
||||||
string description, ConfigEntry<bool> debug = null) {
|
string description, ConfigEntry<bool> debug = null) {
|
||||||
_debug = debug;
|
_debug = debug;
|
||||||
LogDebug($"Initializing ExpressionConfigEntry for {section}.{key}");
|
LogDebug($"=== Initializing ExpressionConfigEntry ===", _debug);
|
||||||
|
LogDebug($"Section: {section}", _debug);
|
||||||
|
LogDebug($"Key: {key}", _debug);
|
||||||
|
LogDebug($"Default value: {defaultValue}", _debug);
|
||||||
|
|
||||||
_configEntry = config.Bind(
|
_configEntry = config.Bind(
|
||||||
section, key, defaultValue,
|
section, key, defaultValue,
|
||||||
new ConfigDescription(description +
|
new ConfigDescription(description +
|
||||||
"\nUse 'v' to represent the input value. Examples: 'v*4', 'v+13', '(v+5)*2'"));
|
"\nUse 'v' to represent the input value. Examples: 'v*4', 'v+13', '(v+5)*2'"));
|
||||||
|
|
||||||
|
_currentInput = defaultValue;
|
||||||
|
|
||||||
// Initial compilation
|
// Initial compilation
|
||||||
try {
|
try {
|
||||||
LogDebug($"Attempting initial compilation of expression: '{_configEntry.Value}'");
|
LogDebug($"=== Starting initial compilation ===", _debug);
|
||||||
_compiledExpression = ParseExpression(_configEntry.Value);
|
LogDebug($"Initial expression: '{_configEntry.Value}'", _debug);
|
||||||
|
_compiledExpression = ParseExpression(_configEntry.Value, _debug);
|
||||||
_lastValidExpression = _configEntry.Value;
|
_lastValidExpression = _configEntry.Value;
|
||||||
LogDebug($"Initial expression '{_configEntry.Value}' compiled successfully");
|
_isValid = true;
|
||||||
|
LogDebug($"=== Initial compilation successful ===", _debug);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogDebug($"Failed to compile initial expression '{_configEntry.Value}': {e.Message}");
|
LogDebug($"=== Initial compilation failed ===", _debug);
|
||||||
LogDebug($"Stack trace: {e.StackTrace}");
|
LogDebug($"Error: {e.Message}", _debug);
|
||||||
|
LogDebug($"Stack trace: {e.StackTrace}", _debug);
|
||||||
|
_isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recompile when config changes
|
// Recompile when config changes
|
||||||
_configEntry.SettingChanged += (sender, args) => {
|
_configEntry.SettingChanged += (sender, args) => {
|
||||||
try {
|
try {
|
||||||
LogDebug($"Config changed, attempting to recompile expression: '{_configEntry.Value}'");
|
LogDebug($"=== Config changed, starting recompilation ===", _debug);
|
||||||
_compiledExpression = ParseExpression(_configEntry.Value);
|
LogDebug($"New expression: '{_configEntry.Value}'", _debug);
|
||||||
|
_compiledExpression = ParseExpression(_configEntry.Value, _debug);
|
||||||
_lastValidExpression = _configEntry.Value;
|
_lastValidExpression = _configEntry.Value;
|
||||||
_isValid = true;
|
_isValid = true;
|
||||||
LogDebug($"Expression '{_configEntry.Value}' recompiled successfully");
|
_currentInput = _configEntry.Value;
|
||||||
|
LogDebug($"=== Recompilation successful ===", _debug);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_isValid = false;
|
_isValid = false;
|
||||||
LogDebug($"Failed to recompile expression '{_configEntry.Value}': {e.Message}");
|
LogDebug($"=== Recompilation failed ===", _debug);
|
||||||
LogDebug($"Stack trace: {e.StackTrace}");
|
LogDebug($"Error: {e.Message}", _debug);
|
||||||
|
LogDebug($"Stack trace: {e.StackTrace}", _debug);
|
||||||
|
LogDebug($"Expression is invalid but keeping input: '{_configEntry.Value}'", _debug);
|
||||||
|
_currentInput = _configEntry.Value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public float Evaluate(float input) {
|
public float Evaluate(float input) {
|
||||||
if (!_isValid) {
|
if (!_isValid) {
|
||||||
LogDebug(
|
LogDebug($"=== Evaluation with invalid expression ===", _debug);
|
||||||
$"Expression is invalid, using last valid expression '{_lastValidExpression}' for input {input}");
|
LogDebug($"Input value: {input}", _debug);
|
||||||
|
LogDebug($"Using last valid expression: '{_lastValidExpression}'", _debug);
|
||||||
return input; // Return original value if expression is invalid
|
return input; // Return original value if expression is invalid
|
||||||
}
|
}
|
||||||
LogDebug($"Evaluating expression '{_configEntry.Value}' with input {input}");
|
LogDebug($"=== Starting expression evaluation ===", _debug);
|
||||||
|
LogDebug($"Expression: '{_configEntry.Value}'", _debug);
|
||||||
|
LogDebug($"Input value: {input}", _debug);
|
||||||
var result = _compiledExpression(input);
|
var result = _compiledExpression(input);
|
||||||
LogDebug($"Expression '{_configEntry.Value}' evaluated: {input} -> {result}");
|
LogDebug($"Output value: {result}", _debug);
|
||||||
|
LogDebug($"=== Evaluation complete ===", _debug);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebug(string message) {
|
private static void LogDebug(string message, ConfigEntry<bool> debug) {
|
||||||
if (_debug?.Value == true) {
|
if (debug?.Value == true) {
|
||||||
Console.WriteLine($"[ExpressionConfigEntry] {message}");
|
Console.WriteLine($"[ExpressionConfigEntry] {message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy of the expression parser from Main
|
// Copy of the expression parser from Main
|
||||||
private static Func<float, float> ParseExpression(string expr) {
|
private static Func<float, float> ParseExpression(string expr, ConfigEntry<bool> debug) {
|
||||||
try {
|
try {
|
||||||
|
LogDebug($"=== Starting expression parsing ===", debug);
|
||||||
|
LogDebug($"Raw expression: '{expr}'", debug);
|
||||||
|
|
||||||
// Remove all whitespace
|
// Remove all whitespace
|
||||||
expr = expr.Replace(" ", "");
|
expr = expr.Replace(" ", "");
|
||||||
|
LogDebug($"After whitespace removal: '{expr}'", debug);
|
||||||
|
|
||||||
var tokens = new List<string>();
|
var tokens = new List<string>();
|
||||||
var current = "";
|
var current = "";
|
||||||
|
|
||||||
// Tokenize the expression
|
// Tokenize the expression
|
||||||
|
LogDebug("=== Tokenizing expression ===", debug);
|
||||||
for (int i = 0; i < expr.Length; i++) {
|
for (int i = 0; i < expr.Length; i++) {
|
||||||
char c = expr[i];
|
char c = expr[i];
|
||||||
if (c == 'v') {
|
if (c == 'v') {
|
||||||
if (current != "") {
|
if (current != "") {
|
||||||
|
LogDebug($"Found number token: '{current}'", debug);
|
||||||
tokens.Add(current);
|
tokens.Add(current);
|
||||||
current = "";
|
current = "";
|
||||||
}
|
}
|
||||||
|
LogDebug("Found variable token: 'v'", debug);
|
||||||
tokens.Add("v");
|
tokens.Add("v");
|
||||||
} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
|
} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
|
||||||
if (current != "") {
|
if (current != "") {
|
||||||
|
LogDebug($"Found number token: '{current}'", debug);
|
||||||
tokens.Add(current);
|
tokens.Add(current);
|
||||||
current = "";
|
current = "";
|
||||||
}
|
}
|
||||||
|
LogDebug($"Found operator token: '{c}'", debug);
|
||||||
tokens.Add(c.ToString());
|
tokens.Add(c.ToString());
|
||||||
} else {
|
} else {
|
||||||
current += c;
|
current += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current != "") {
|
if (current != "") {
|
||||||
|
LogDebug($"Found final number token: '{current}'", debug);
|
||||||
tokens.Add(current);
|
tokens.Add(current);
|
||||||
}
|
}
|
||||||
|
LogDebug($"All tokens: {string.Join(", ", tokens)}", debug);
|
||||||
|
|
||||||
// Convert to postfix notation using shunting yard
|
// Convert to postfix notation using shunting yard
|
||||||
|
LogDebug("=== Converting to postfix notation ===", debug);
|
||||||
var output = new List<string>();
|
var output = new List<string>();
|
||||||
var operators = new Stack<string>();
|
var operators = new Stack<string>();
|
||||||
|
|
||||||
foreach (var token in tokens) {
|
foreach (var token in tokens) {
|
||||||
if (token == "v" || float.TryParse(token, out _)) {
|
if (token == "v" || float.TryParse(token, out _)) {
|
||||||
|
LogDebug($"Pushing operand to output: '{token}'", debug);
|
||||||
output.Add(token);
|
output.Add(token);
|
||||||
} else if (token == "(") {
|
} else if (token == "(") {
|
||||||
|
LogDebug("Pushing '(' to operator stack", debug);
|
||||||
operators.Push(token);
|
operators.Push(token);
|
||||||
} else if (token == ")") {
|
} else if (token == ")") {
|
||||||
|
LogDebug("Found ')', popping operators until '('", debug);
|
||||||
while (operators.Count > 0 && operators.Peek() != "(") {
|
while (operators.Count > 0 && operators.Peek() != "(") {
|
||||||
output.Add(operators.Pop());
|
var op = operators.Pop();
|
||||||
|
LogDebug($"Popped operator to output: '{op}'", debug);
|
||||||
|
output.Add(op);
|
||||||
}
|
}
|
||||||
if (operators.Count > 0)
|
if (operators.Count > 0) {
|
||||||
|
LogDebug("Popping '(' from stack", debug);
|
||||||
operators.Pop(); // Remove "("
|
operators.Pop(); // Remove "("
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
LogDebug($"Processing operator: '{token}'", debug);
|
||||||
while (operators.Count > 0 && operators.Peek() != "(" &&
|
while (operators.Count > 0 && operators.Peek() != "(" &&
|
||||||
GetPrecedence(operators.Peek()) >= GetPrecedence(token)) {
|
GetPrecedence(operators.Peek()) >= GetPrecedence(token)) {
|
||||||
output.Add(operators.Pop());
|
var op = operators.Pop();
|
||||||
|
LogDebug($"Popped higher precedence operator to output: '{op}'", debug);
|
||||||
|
output.Add(op);
|
||||||
}
|
}
|
||||||
|
LogDebug($"Pushing operator to stack: '{token}'", debug);
|
||||||
operators.Push(token);
|
operators.Push(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (operators.Count > 0) {
|
while (operators.Count > 0) {
|
||||||
output.Add(operators.Pop());
|
var op = operators.Pop();
|
||||||
|
LogDebug($"Popping remaining operator to output: '{op}'", debug);
|
||||||
|
output.Add(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogDebug($"Postfix expression: {string.Join(" ", output)}", debug);
|
||||||
|
|
||||||
// Build the expression tree
|
// Build the expression tree
|
||||||
|
LogDebug("=== Building expression tree ===", debug);
|
||||||
var stack = new Stack<Func<float, float>>();
|
var stack = new Stack<Func<float, float>>();
|
||||||
|
|
||||||
foreach (var token in output) {
|
foreach (var token in output) {
|
||||||
if (token == "v") {
|
if (token == "v") {
|
||||||
|
LogDebug("Pushing variable function to stack", debug);
|
||||||
stack.Push(v => v);
|
stack.Push(v => v);
|
||||||
} else if (float.TryParse(token, out float num)) {
|
} else if (float.TryParse(token, out float num)) {
|
||||||
|
LogDebug($"Pushing constant function to stack: {num}", debug);
|
||||||
stack.Push(v => num);
|
stack.Push(v => num);
|
||||||
} else {
|
} else {
|
||||||
var right = stack.Pop();
|
var right = stack.Pop();
|
||||||
var left = stack.Pop();
|
var left = stack.Pop();
|
||||||
|
LogDebug($"Popped two functions for operator: '{token}'", debug);
|
||||||
|
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case "+":
|
case "+":
|
||||||
|
LogDebug("Creating addition function", debug);
|
||||||
stack.Push(v => left(v) + right(v));
|
stack.Push(v => left(v) + right(v));
|
||||||
break;
|
break;
|
||||||
case "-":
|
case "-":
|
||||||
|
LogDebug("Creating subtraction function", debug);
|
||||||
stack.Push(v => left(v) - right(v));
|
stack.Push(v => left(v) - right(v));
|
||||||
break;
|
break;
|
||||||
case "*":
|
case "*":
|
||||||
|
LogDebug("Creating multiplication function", debug);
|
||||||
stack.Push(v => left(v) * right(v));
|
stack.Push(v => left(v) * right(v));
|
||||||
break;
|
break;
|
||||||
case "/":
|
case "/":
|
||||||
|
LogDebug("Creating division function", debug);
|
||||||
stack.Push(v => {
|
stack.Push(v => {
|
||||||
var r = right(v);
|
var r = right(v);
|
||||||
return r != 0 ? left(v) / r : left(v);
|
return r != 0 ? left(v) / r : left(v);
|
||||||
@@ -184,8 +242,11 @@ namespace BanquetForCyka {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogDebug("=== Expression parsing complete ===", debug);
|
||||||
return stack.Pop();
|
return stack.Pop();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
LogDebug($"=== Expression parsing failed ===", debug);
|
||||||
|
LogDebug($"Error: {e.Message}", debug);
|
||||||
throw new FormatException($"Failed to parse expression '{expr}': {e.Message}");
|
throw new FormatException($"Failed to parse expression '{expr}': {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user