From a323dcc230099bbb90f8a14b615825e962245495 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 22 Mar 2026 23:29:10 -0400 Subject: [PATCH] feat: add ProgramVariable and ProgramVariableManager for macro variable declarations Co-Authored-By: Claude Sonnet 4.6 --- OpenNest.Posts.Cincinnati/ProgramVariable.cs | 18 +++++ .../ProgramVariableManager.cs | 43 +++++++++++ .../Cincinnati/ProgramVariableManagerTests.cs | 71 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 OpenNest.Posts.Cincinnati/ProgramVariable.cs create mode 100644 OpenNest.Posts.Cincinnati/ProgramVariableManager.cs create mode 100644 OpenNest.Tests/Cincinnati/ProgramVariableManagerTests.cs diff --git a/OpenNest.Posts.Cincinnati/ProgramVariable.cs b/OpenNest.Posts.Cincinnati/ProgramVariable.cs new file mode 100644 index 0000000..d92fe5c --- /dev/null +++ b/OpenNest.Posts.Cincinnati/ProgramVariable.cs @@ -0,0 +1,18 @@ +namespace OpenNest.Posts.Cincinnati +{ + public sealed class ProgramVariable + { + public int Number { get; } + public string Name { get; } + public string Expression { get; set; } + + public ProgramVariable(int number, string name, string expression = null) + { + Number = number; + Name = name; + Expression = expression; + } + + public string Reference => $"#{Number}"; + } +} diff --git a/OpenNest.Posts.Cincinnati/ProgramVariableManager.cs b/OpenNest.Posts.Cincinnati/ProgramVariableManager.cs new file mode 100644 index 0000000..4dfe0b0 --- /dev/null +++ b/OpenNest.Posts.Cincinnati/ProgramVariableManager.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenNest.Posts.Cincinnati +{ + public sealed class ProgramVariableManager + { + private readonly Dictionary _variables = new(); + + public ProgramVariable GetOrCreate(string name, int number, string expression = null) + { + if (_variables.TryGetValue(number, out var existing)) + return existing; + + var variable = new ProgramVariable(number, name, expression); + _variables[number] = variable; + return variable; + } + + public List EmitDeclarations() + { + return _variables.Values + .Where(v => v.Expression != null) + .OrderBy(v => v.Number) + .Select(v => $"{v.Reference}={v.Expression} ({FormatComment(v.Name)})") + .ToList(); + } + + private static string FormatComment(string name) + { + // "LeadInFeedrate" -> "LEAD IN FEEDRATE" + var sb = new StringBuilder(); + foreach (var c in name) + { + if (char.IsUpper(c) && sb.Length > 0) + sb.Append(' '); + sb.Append(char.ToUpper(c)); + } + return sb.ToString(); + } + } +} diff --git a/OpenNest.Tests/Cincinnati/ProgramVariableManagerTests.cs b/OpenNest.Tests/Cincinnati/ProgramVariableManagerTests.cs new file mode 100644 index 0000000..33b35d7 --- /dev/null +++ b/OpenNest.Tests/Cincinnati/ProgramVariableManagerTests.cs @@ -0,0 +1,71 @@ +using OpenNest.Posts.Cincinnati; + +namespace OpenNest.Tests.Cincinnati; + +public class ProgramVariableManagerTests +{ + [Fact] + public void GetOrCreate_ReturnsNewVariable() + { + var mgr = new ProgramVariableManager(); + var v = mgr.GetOrCreate("LeadInFeedrate", 126); + Assert.Equal(126, v.Number); + Assert.Equal("LeadInFeedrate", v.Name); + } + + [Fact] + public void GetOrCreate_ReturnsSameVariable_WhenCalledTwice() + { + var mgr = new ProgramVariableManager(); + var v1 = mgr.GetOrCreate("LeadInFeedrate", 126); + var v2 = mgr.GetOrCreate("LeadInFeedrate", 126); + Assert.Same(v1, v2); + } + + [Fact] + public void GetOrCreate_WithExpression_SetsExpression() + { + var mgr = new ProgramVariableManager(); + var v = mgr.GetOrCreate("LeadInFeedrate", 126, "[#148*0.5]"); + Assert.Equal("[#148*0.5]", v.Expression); + } + + [Fact] + public void GetOrCreate_WithLiteral_SetsExpression() + { + var mgr = new ProgramVariableManager(); + var v = mgr.GetOrCreate("CircleFeedrate", 128, ".8"); + Assert.Equal(".8", v.Expression); + } + + [Fact] + public void Reference_ReturnsHashNumber() + { + var v = new ProgramVariable(126, "LeadInFeedrate"); + Assert.Equal("#126", v.Reference); + } + + [Fact] + public void EmitDeclarations_ProducesCorrectLines() + { + var mgr = new ProgramVariableManager(); + mgr.GetOrCreate("LeadInFeedrate", 126, "[#148*0.5]"); + mgr.GetOrCreate("CircleFeedrate", 128, ".8"); + + var lines = mgr.EmitDeclarations(); + + Assert.Contains("#126=[#148*0.5] (LEAD IN FEEDRATE)", lines); + Assert.Contains("#128=.8 (CIRCLE FEEDRATE)", lines); + } + + [Fact] + public void EmitDeclarations_SkipsVariablesWithNoExpression() + { + var mgr = new ProgramVariableManager(); + mgr.GetOrCreate("ProcessFeedrate", 148); + + var lines = mgr.EmitDeclarations(); + + Assert.Empty(lines); + } +}