diff --git a/src/kOS.Safe/Compilation/Opcode.cs b/src/kOS.Safe/Compilation/Opcode.cs index d7a65a8d1..113de3e07 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,64 @@ 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(double min, double 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 = !(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) + { + 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 ((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"}]" + ); + } + } + } + /// /// Stops executing for this cycle. Has no stack effect. ///