From 97ebfeb7a910ebd561e88f17b16e32cb61118da3 Mon Sep 17 00:00:00 2001 From: DigitalCodeCrafter Date: Tue, 25 Nov 2025 18:45:54 +0100 Subject: [PATCH 1/3] added range assertion instruction --- src/kOS.Safe/Compilation/Opcode.cs | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/kOS.Safe/Compilation/Opcode.cs b/src/kOS.Safe/Compilation/Opcode.cs index d7a65a8d1..081b8ed34 100644 --- a/src/kOS.Safe/Compilation/Opcode.cs +++ b/src/kOS.Safe/Compilation/Opcode.cs @@ -88,6 +88,7 @@ public enum ByteCode :byte TESTARGBOTTOM = 0x61, TESTCANCELLED = 0x62, JUMPSTACK = 0x63, + ASSERTRANGE = 0x64, // Augmented bogus placeholder versions of the normal // opcodes: These only exist in the program temporarily @@ -947,6 +948,58 @@ public override void Execute(ICpu cpu) } } + /// + /// + /// Asserts that the top value of the stack is a value ranging between MinValue and MaxValue + /// + /// + /// assertrange min max + /// ... val -- ... val + /// + public class OpcodeAssertRange : Opcode + { + protected override string Name { get { return "assertrange"; } } + public override ByteCode Code { get { return ByteCode.ASSERTRANGE; } } + + [MLField(0, false)] + public double MinValue { get; set; } + [MLField(1, false)] + public double MaxValue { get; set; } + + protected OpcodeAssertRange() + { + } + + public OpcodeAssertRange(int min, int max) + { + MinValue = min; + MaxValue = max; + } + + public override void PopulateFromMLFields(List fields) + { + if (fields == null || fields.Count < 2) + throw new Exception(String.Format("Saved field in ML file for OpcodeAssertRange seems to be missing. Version mismatch?")); + MinValue = Convert.ToDouble(fields[0]); + MaxValue = Convert.ToDouble(fields[1]); + } + + public override void Execute(ICpu cpu) + { + object value = cpu.PeekRawArgument(0, out bool ok); + if (!ok) + throw new KOSException("Failed to assert range: failed to load stack argument"); + + if (value is not ScalarValue scalar) + throw new KOSException("Failed to assert range: cannot assert range for non scalar"); + + double actual = scalar.GetDoubleValue(); + + if (actual < MinValue || actual > MaxValue) + throw new KOSException("Value ({0}) out of range ({1}..{2})", actual, MinValue, MaxValue); + } + } + /// /// Stops executing for this cycle. Has no stack effect. /// From 1360e22f17ee51909fab2f0ccc3dc929f0feafa9 Mon Sep 17 00:00:00 2001 From: DigitalCodeCrafter Date: Tue, 25 Nov 2025 19:22:22 +0100 Subject: [PATCH 2/3] changed constructor to use doubles... --- src/kOS.Safe/Compilation/Opcode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kOS.Safe/Compilation/Opcode.cs b/src/kOS.Safe/Compilation/Opcode.cs index 081b8ed34..39c72acd0 100644 --- a/src/kOS.Safe/Compilation/Opcode.cs +++ b/src/kOS.Safe/Compilation/Opcode.cs @@ -970,7 +970,7 @@ protected OpcodeAssertRange() { } - public OpcodeAssertRange(int min, int max) + public OpcodeAssertRange(double min, double max) { MinValue = min; MaxValue = max; From eba94b823fcb67c2c2f66f7019053ed0986a7f0d Mon Sep 17 00:00:00 2001 From: DigitalCodeCrafter Date: Tue, 25 Nov 2025 20:19:13 +0100 Subject: [PATCH 3/3] added open bounds if PseudoNull as argument --- src/kOS.Safe/Compilation/Opcode.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/kOS.Safe/Compilation/Opcode.cs b/src/kOS.Safe/Compilation/Opcode.cs index 39c72acd0..113de3e07 100644 --- a/src/kOS.Safe/Compilation/Opcode.cs +++ b/src/kOS.Safe/Compilation/Opcode.cs @@ -962,9 +962,9 @@ public class OpcodeAssertRange : Opcode public override ByteCode Code { get { return ByteCode.ASSERTRANGE; } } [MLField(0, false)] - public double MinValue { get; set; } + public double? MinValue { get; set; } [MLField(1, false)] - public double MaxValue { get; set; } + public double? MaxValue { get; set; } protected OpcodeAssertRange() { @@ -980,8 +980,8 @@ public override void PopulateFromMLFields(List fields) { if (fields == null || fields.Count < 2) throw new Exception(String.Format("Saved field in ML file for OpcodeAssertRange seems to be missing. Version mismatch?")); - MinValue = Convert.ToDouble(fields[0]); - MaxValue = Convert.ToDouble(fields[1]); + MinValue = !(fields[0] is PseudoNull) ? Convert.ToDouble(fields[0]) : (double?)null; + MaxValue = !(fields[1] is PseudoNull) ? Convert.ToDouble(fields[1]) : (double?)null; } public override void Execute(ICpu cpu) @@ -995,8 +995,14 @@ public override void Execute(ICpu cpu) double actual = scalar.GetDoubleValue(); - if (actual < MinValue || actual > MaxValue) - throw new KOSException("Value ({0}) out of range ({1}..{2})", actual, MinValue, MaxValue); + if ((MinValue.HasValue && actual < MinValue.Value) || + (MaxValue.HasValue && actual > MaxValue.Value)) + { + throw new KOSException( + $"assertrange failed: value {value} not in range " + + $"[{MinValue?.ToString() ?? "-infinity"}, {MaxValue?.ToString() ?? "infinity"}]" + ); + } } }