This commit is contained in:
Jordan Wages 2026-06-24 03:34:35 -05:00
commit 4d6a0c6bab

View file

@ -4,32 +4,47 @@ using System.IO;
namespace CSMic namespace CSMic
{ {
/// <summary> The interpreter that parses user input at runtime into strongly typed .Net values. </summary>
public class InputInterpreter public class InputInterpreter
{ {
#region Members #region Members
/// <summary> The numeric value store. </summary>
private decimal numericValue = 0; private decimal numericValue = 0;
/// <summary> The string value store. </summary>
private string stringValue = string.Empty; private string stringValue = string.Empty;
/// <summary> The time taken for the last entry the parser interpreted. </summary>
private TimeSpan lastExecutionTime = TimeSpan.Zero; private TimeSpan lastExecutionTime = TimeSpan.Zero;
// Variable stores #region Variable stores
/// <summary> (Immutable) The numeric variables. </summary>
private readonly Dictionary<string, decimal> numericVariables; private readonly Dictionary<string, decimal> numericVariables;
/// <summary> (Immutable) The numeric array variables. </summary>
private readonly Dictionary<string, decimal[]> numericArrayVariables; private readonly Dictionary<string, decimal[]> numericArrayVariables;
private readonly Dictionary<string, string> expressionVariables; /// <summary> (Immutable) The expression variables. </summary>
private readonly Dictionary<string, string> expressionVariables;
#endregion
// Function registry /// <summary> (Immutable) The function registry. </summary>
private readonly Dictionary<string, ICodedFunction> functions; private readonly Dictionary<string, ICodedFunction> functions;
// Tracks expression variables currently being evaluated to prevent recursion /// <summary>
/// (Immutable) Tracks expression variables currently being evaluated to prevent recursion.
/// </summary>
private readonly List<string> evaluationStack; private readonly List<string> evaluationStack;
// Shared recursion tracker across nested evaluations /// <summary> Shared recursion tracker used across nested evaluations. </summary>
private sealed class RecursionTracker { public int Hits; } private sealed class RecursionTracker {
/// <summary> The number of recursive trips detected. </summary>
public int Hits;
}
/// <summary> (Immutable) The recursion tracker. </summary>
private readonly RecursionTracker recursion; private readonly RecursionTracker recursion;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> Default constructor. </summary>
public InputInterpreter() public InputInterpreter()
{ {
numericVariables = new Dictionary<string, decimal>(StringComparer.Ordinal); numericVariables = new Dictionary<string, decimal>(StringComparer.Ordinal);
@ -40,7 +55,8 @@ namespace CSMic
recursion = new RecursionTracker(); recursion = new RecursionTracker();
} }
// Internal constructor to create a child interpreter that shares stores /// <summary> Internal constructor to create a child interpreter that shares stores. </summary>
/// <param name="parent"> The parent <see cref="InputInterpreter"/>. </param>
internal InputInterpreter(InputInterpreter parent) internal InputInterpreter(InputInterpreter parent)
{ {
this.numericVariables = parent.numericVariables; this.numericVariables = parent.numericVariables;
@ -57,6 +73,8 @@ namespace CSMic
#region Properties #region Properties
/// <summary> Gets the numeric value. </summary>
/// <value> The numeric value. </value>
public decimal NumericValue public decimal NumericValue
{ {
get get
@ -65,6 +83,8 @@ namespace CSMic
} }
} }
/// <summary> Gets the string value. </summary>
/// <value> The string value. </value>
public string StringValue public string StringValue
{ {
get get
@ -73,6 +93,8 @@ namespace CSMic
} }
} }
/// <summary> Gets the last execution time. </summary>
/// <value> The last execution time. </value>
public TimeSpan LastExecutionTime public TimeSpan LastExecutionTime
{ {
get get
@ -81,6 +103,8 @@ namespace CSMic
} }
} }
/// <summary> Gets the variables. </summary>
/// <value> The encapsulated variables. </value>
public IEnumerable<Variable> Variables public IEnumerable<Variable> Variables
{ {
get get
@ -95,12 +119,17 @@ namespace CSMic
#region Output Plumbing #region Output Plumbing
/// <summary> Hydrates long-lived output variables for delayed access. </summary>
/// <param name="numericValue"> The numeric value store. </param>
/// <param name="stringValue"> The string value store. </param>
internal void ProduceOutput(decimal numericValue, string stringValue) internal void ProduceOutput(decimal numericValue, string stringValue)
{ {
this.numericValue = numericValue; this.numericValue = numericValue;
this.stringValue = stringValue; this.stringValue = stringValue;
} }
/// <summary> Hydrates long-lived output variables for delayed access. </summary>
/// <param name="functionValue"> The function value to be converted. </param>
internal void ProduceOutput(FunctionValue functionValue) internal void ProduceOutput(FunctionValue functionValue)
{ {
switch (functionValue.Type) switch (functionValue.Type)
@ -126,12 +155,24 @@ namespace CSMic
#region Variable APIs #region Variable APIs
/// <summary> Attempts to get a numeric value from the variable store with a given name. </summary>
/// <param name="name"> The name. </param>
/// <param name="value"> [out] The value. </param>
/// <returns> True if it succeeds, false if it fails. </returns>
internal bool TryGetNumeric(string name, out decimal value) internal bool TryGetNumeric(string name, out decimal value)
=> numericVariables.TryGetValue(name, out value); => numericVariables.TryGetValue(name, out value);
/// <summary> Attempts to get a numeric array from the variable store with a given name. </summary>
/// <param name="name"> The name. </param>
/// <param name="values"> [out] The values. </param>
/// <returns> True if it succeeds, false if it fails. </returns>
internal bool TryGetNumericArray(string name, out decimal[] values) internal bool TryGetNumericArray(string name, out decimal[] values)
=> numericArrayVariables.TryGetValue(name, out values!); => numericArrayVariables.TryGetValue(name, out values!);
/// <summary> Attempts to get an expression from the variable store with a given name. </summary>
/// <param name="name"> The name. </param>
/// <param name="expr"> [out] The expression. </param>
/// <returns> True if it succeeds, false if it fails. </returns>
internal bool TryGetExpression(string name, out string expr) internal bool TryGetExpression(string name, out string expr)
{ {
if (expressionVariables.TryGetValue(name, out expr!)) if (expressionVariables.TryGetValue(name, out expr!))
@ -142,9 +183,16 @@ namespace CSMic
return false; return false;
} }
// Recursion tracking helpers managed by the parser when evaluating expression variables /// <summary>
/// Recursion tracking helpers managed by the parser when evaluating expression variables.
/// </summary>
/// <param name="name"> The name of the variable to check. </param>
/// <returns> True if currently evaluating, false if not. </returns>
internal bool IsEvaluating(string name) => evaluationStack.Contains(name); internal bool IsEvaluating(string name) => evaluationStack.Contains(name);
/// <summary> Begins evaluating a named expression. </summary>
/// <param name="name"> The name of the expression variable. </param>
/// <returns> The current recurrsion depth. </returns>
internal int BeginEvaluating(string name) internal int BeginEvaluating(string name)
{ {
int depth = evaluationStack.Count; int depth = evaluationStack.Count;
@ -152,6 +200,8 @@ namespace CSMic
return depth; return depth;
} }
/// <summary> Ends evaluating a named expression. </summary>
/// <param name="depth"> The recurrsion depth. </param>
internal void EndEvaluating(int depth) internal void EndEvaluating(int depth)
{ {
while (evaluationStack.Count > depth) while (evaluationStack.Count > depth)
@ -160,46 +210,67 @@ namespace CSMic
} }
} }
// Recursion hit scoping across a single top-level expression evaluation /// <summary> Recursion hit scoping across a single top-level expression evaluation. </summary>
/// <returns> An int. </returns>
internal int BeginRecursionScope() internal int BeginRecursionScope()
{ {
return recursion.Hits; return recursion.Hits;
} }
// Returns true if recursion occurred within this scope /// <summary> Returns true if recursion occurred within this scope. </summary>
/// <param name="startHits"> The initial recurrsive hit counter. </param>
/// <returns> True if recurrsion occurred within this scope, false otherwise. </returns>
internal bool EndRecursionScope(int startHits) internal bool EndRecursionScope(int startHits)
{ {
return recursion.Hits > startHits; return recursion.Hits > startHits;
} }
/// <summary> Mark a recursion hit. </summary>
internal void MarkRecursionHit() internal void MarkRecursionHit()
{ {
recursion.Hits++; recursion.Hits++;
} }
/// <summary> Assign a numeric value to a variable. </summary>
/// <param name="name"> The name of the variable. </param>
/// <param name="value"> [out] The value. </param>
internal void AssignNumeric(string name, decimal value) internal void AssignNumeric(string name, decimal value)
{ {
numericVariables[name] = value; numericVariables[name] = value;
// Remove conflicting bindings // Remove conflicting bindings
expressionVariables.Remove(name); expressionVariables.Remove(name);
numericArrayVariables.Remove(name);
} }
/// <summary> Assign a numeric array set to a variable. </summary>
/// <param name="name"> The name of the variable. </param>
/// <param name="values"> [out] The array values. </param>
internal void AssignNumericArray(string name, decimal[] values) internal void AssignNumericArray(string name, decimal[] values)
{ {
numericArrayVariables[name] = values; numericArrayVariables[name] = values;
// Remove conflicting bindings
numericVariables.Remove(name);
expressionVariables.Remove(name);
} }
/// <summary> Assign an expression to a variable. </summary>
/// <param name="name"> The name of the variable. </param>
/// <param name="expressionText"> The expression text. </param>
internal void AssignExpression(string name, string expressionText) internal void AssignExpression(string name, string expressionText)
{ {
expressionVariables[name] = expressionText; expressionVariables[name] = expressionText;
// Remove conflicting numeric value // Remove conflicting numeric value
numericVariables.Remove(name); numericVariables.Remove(name);
numericArrayVariables.Remove(name);
} }
#endregion #endregion
#region Expression Evaluation #region Expression Evaluation
/// <summary> Evaluates an expression. </summary>
/// <param name="expressionText"> The expression text. </param>
/// <returns> The result of the expression. </returns>
internal FunctionValue EvaluateExpression(string expressionText) internal FunctionValue EvaluateExpression(string expressionText)
{ {
// Create a child interpreter sharing stores, so ProduceOutput doesn't affect parent state // Create a child interpreter sharing stores, so ProduceOutput doesn't affect parent state
@ -221,7 +292,10 @@ namespace CSMic
} }
} }
// Primary developer-facing API: interpret input and return numeric result /// <summary> Interpret input and return a numeric result. </summary>
/// <param name="input"> The input text to parse. </param>
/// <returns> A decimal value representing the immediate result. </returns>
/// <remarks> This is the primary developer-facing API. </remarks>
public decimal Interpret(string input) public decimal Interpret(string input)
{ {
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
@ -263,11 +337,24 @@ namespace CSMic
#region Functions #region Functions
/// <summary> Registers a function described by <see cref="ICodedFunction"/> interface. </summary>
/// <param name="function"> The function. </param>
/// <seealso cref="FunctionArgument"/>
/// <seealso cref="FunctionValueType"/>
/// <seealso cref="FunctionValue"/>
public void RegisterFunction(ICodedFunction function) public void RegisterFunction(ICodedFunction function)
{ {
functions[function.Name] = function; functions[function.Name] = function;
} }
/// <summary> Executes a named function. </summary>
/// <param name="name"> The name. </param>
/// <param name="args"> A variable-length parameters list containing arguments. </param>
/// <returns> The result of the function execution. </returns>
/// <seealso cref="FunctionArgument"/>
/// <seealso cref="FunctionValueType"/>
/// <seealso cref="FunctionValue"/>
/// <seealso cref="ICodedFunction"/>
internal FunctionValue ExecuteFunction(string name, params FunctionArgument[] args) internal FunctionValue ExecuteFunction(string name, params FunctionArgument[] args)
{ {
if (functions.TryGetValue(name, out var fn)) if (functions.TryGetValue(name, out var fn))