From c5f35b9d34233806d36e499b7d2a31c462e4f9fc Mon Sep 17 00:00:00 2001 From: wagesj45 Date: Tue, 19 Aug 2025 03:51:05 -0500 Subject: [PATCH] v2: Wire InputInterpreter runtime APIs for v2 grammar Add in-memory stores and APIs required by the v2 Coco/R grammar: - Variables: AssignNumeric, AssignExpression, AssignNumericArray; TryGetNumeric, TryGetExpression, TryGetNumericArray. - Mixed-arg function dispatch: ExecuteFunction(name, FunctionArgument[]), plus RegisterFunction. - Expression evaluation for :=: EvaluateExpression(expressionText) via generated Scanner/Parser with a child interpreter sharing stores. - Output handling preserved: ProduceOutput(FunctionValue) updates NumericValue/StringValue. These changes allow the generated parser to compile and run against the v2 runtime, enforcing the numeric-first, strings-as-arguments-only model. --- src/core/InputInterpreter.cs | 133 +++++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/src/core/InputInterpreter.cs b/src/core/InputInterpreter.cs index cae5446..9bcb797 100644 --- a/src/core/InputInterpreter.cs +++ b/src/core/InputInterpreter.cs @@ -1,4 +1,6 @@ using System.Runtime.CompilerServices; +using System.Text; +using System.IO; namespace csmic { @@ -9,29 +11,45 @@ namespace csmic private decimal numericValue = 0; private string stringValue = string.Empty; + // Variable stores + private readonly Dictionary numericVariables; + private readonly Dictionary numericArrayVariables; + private readonly Dictionary expressionVariables; + + // Function registry + private readonly Dictionary functions; + + #endregion + + #region Constructors + + public InputInterpreter() + { + numericVariables = new(StringComparer.Ordinal); + numericArrayVariables = new(StringComparer.Ordinal); + expressionVariables = new(StringComparer.Ordinal); + functions = new(StringComparer.Ordinal); + } + + // Internal constructor to create a child interpreter that shares stores + internal InputInterpreter(InputInterpreter parent) + { + this.numericVariables = parent.numericVariables; + this.numericArrayVariables = parent.numericArrayVariables; + this.expressionVariables = parent.expressionVariables; + this.functions = parent.functions; + } + #endregion #region Properties - public decimal NumericValue - { - get - { - return numericValue; - } - } - - public string StringValue - { - get - { - return stringValue; - } - } + public decimal NumericValue => numericValue; + public string StringValue => stringValue; #endregion - #region Methods + #region Output Plumbing internal void ProduceOutput(decimal numericValue, string stringValue) { @@ -48,14 +66,10 @@ namespace csmic ProduceOutput(numericValue, string.Empty); break; case ValueType.String: - if (functionValue.Value != null && functionValue.Value is string) - { - ProduceOutput(0, functionValue.Value!.ToString()); - } + if (functionValue.Value is string s) + ProduceOutput(0, s); else - { ProduceOutput(0, string.Empty); - } break; case ValueType.None: default: @@ -65,5 +79,80 @@ namespace csmic } #endregion + + #region Variable APIs + + internal bool TryGetNumeric(string name, out decimal value) + => numericVariables.TryGetValue(name, out value); + + internal bool TryGetNumericArray(string name, out decimal[] values) + => numericArrayVariables.TryGetValue(name, out values!); + + internal bool TryGetExpression(string name, out string expr) + => expressionVariables.TryGetValue(name, out expr!); + + internal void AssignNumeric(string name, decimal value) + { + numericVariables[name] = value; + // Remove conflicting bindings + expressionVariables.Remove(name); + } + + internal void AssignNumericArray(string name, decimal[] values) + { + numericArrayVariables[name] = values; + } + + internal void AssignExpression(string name, string expressionText) + { + expressionVariables[name] = expressionText; + // Remove conflicting numeric value + numericVariables.Remove(name); + } + + #endregion + + #region Expression Evaluation + + internal FunctionValue EvaluateExpression(string expressionText) + { + // Create a child interpreter sharing stores, so ProduceOutput doesn't affect parent state + var child = new InputInterpreter(this); + using var ms = new MemoryStream(Encoding.UTF8.GetBytes(expressionText)); + var scanner = new csmic.Interpreter.Scanner(ms); + var parser = new csmic.Interpreter.Parser(scanner) + { + Interpreter = child + }; + parser.Parse(); + return parser.Result; + } + + #endregion + + #region Functions + + internal void RegisterFunction(string name, ICodedFunction function) + { + functions[name] = function; + } + + internal FunctionValue ExecuteFunction(string name, params FunctionArgument[] args) + { + if (functions.TryGetValue(name, out var fn)) + { + try + { + return fn.Execute(args); + } + catch + { + return new FunctionValue(ValueType.None, null); + } + } + return new FunctionValue(ValueType.None, null); + } + + #endregion } }