Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve parity handling of null in various operators #2129

Merged
merged 6 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Content.Tests/DMProject/Tests/Operators/Division.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@
var/list/expected = list(
1,
"Error",
10,
"Error",
"Error", // index 5
"Error",
"Error",
"Error",
"Error",
"Error", // index 10
"Error",
"Error",
"Error",
"Error",
"Error", // index 15
"Error",
"Error",
"Error",
"Error",
"Error", // index 20
"Error",
"Error",
"Error",
"Error",
"Error",
"Error",
"Error",
0,
0,
0,
0, // index 25
0,
0,
0,
0,
0, // index 30
0,
0,
0,
0,
"Error",
"Error",
"Error", // index 35
"Error",
"Error",
"Error",
Expand Down Expand Up @@ -128,4 +128,4 @@
"Error"
)

test_binary_operator(/proc/divide, expected)
test_binary_operator(/proc/divide, expected)
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var/list/operator_test_values = list(
result = "Error"

if (result ~! expected_result)
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)], instead got [json_encode(result)]")
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)], instead got [json_encode(result)] at index [i - 1]")

/proc/test_binary_operator(var/operator_proc, var/list/expected)
var/i = 1
Expand All @@ -48,4 +48,4 @@ var/list/operator_test_values = list(
result = "Error"

if (result ~! expected_result)
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)] and [json_encode(b)], instead got [json_encode(result)]")
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)] and [json_encode(b)], instead got [json_encode(result)] at index [i - 1]")
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@

ASSERT(C(4) / C(2) == C(2))
ASSERT(C(null) / C(2) == C(0))
//ASSERT(C(2) / C(null) == C(2)) //runtime: undefined operation
ASSERT(C(2) / C(null) == C(2))
ASSERT(C(null) / C(null) == C(0))

ASSERT(C(4) % C(3) == C(1))
ASSERT(C(null) % C(3) == C(0))
Expand Down
82 changes: 61 additions & 21 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -923,14 +923,16 @@ public static ProcStatus BitShiftLeft(DMProcState state) {
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
state.Push(new DreamValue(first.MustGetValueAsInteger() << second.MustGetValueAsInteger()));
break;
case DreamValue.DreamValueType.Float when second.IsNull:
state.Push(new DreamValue(first.MustGetValueAsInteger()));
break;
default:
throw new Exception($"Invalid bit shift left operation on {first} and {second}");
}

return ProcStatus.Continue;
}


public static ProcStatus BitShiftLeftReference(DMProcState state) {
DreamReference reference = state.ReadReference();
DreamValue second = state.Pop();
Expand All @@ -943,9 +945,13 @@ public static ProcStatus BitShiftLeftReference(DMProcState state) {
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
result = new DreamValue(first.MustGetValueAsInteger() << second.MustGetValueAsInteger());
break;
case DreamValue.DreamValueType.Float when second.IsNull:
result = new DreamValue(first.MustGetValueAsInteger());
break;
default:
throw new Exception($"Invalid bit shift left operation on {first} and {second}");
}

state.AssignReference(reference, result);
state.Push(result);
return ProcStatus.Continue;
Expand All @@ -955,12 +961,18 @@ public static ProcStatus BitShiftRight(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();

if (first.IsNull) {
state.Push(new DreamValue(0));
} else if (first.Type == DreamValue.DreamValueType.Float && second.Type == DreamValue.DreamValueType.Float) {
state.Push(new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger()));
} else {
throw new Exception($"Invalid bit shift right operation on {first} and {second}");
switch (first.Type) {
case DreamValue.DreamValueType.DreamObject when first.IsNull:
state.Push(new DreamValue(0));
break;
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
state.Push(new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger()));
break;
case DreamValue.DreamValueType.Float when second.IsNull:
state.Push(new DreamValue(first.MustGetValueAsInteger()));
break;
default:
throw new Exception($"Invalid bit shift right operation on {first} and {second}");
}

return ProcStatus.Continue;
Expand All @@ -978,9 +990,13 @@ public static ProcStatus BitShiftRightReference(DMProcState state) {
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
result = new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger());
break;
case DreamValue.DreamValueType.Float when second.IsNull:
result = new DreamValue(first.MustGetValueAsInteger());
break;
default:
throw new Exception($"Invalid bit shift right operation on {first} and {second}");
}

state.AssignReference(reference, result);
state.Push(result);
return ProcStatus.Continue;
Expand Down Expand Up @@ -1072,20 +1088,30 @@ public static ProcStatus Combine(DMProcState state) {
public static ProcStatus Divide(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();
if (first.IsNull) {
state.Push(new(0));
} else if (first.TryGetValueAsFloat(out var firstFloat) && second.TryGetValueAsFloat(out var secondFloat)) {
if (secondFloat == 0) {
throw new Exception("Division by zero");
}

state.Push(new(firstFloat / secondFloat));
} else if (first.TryGetValueAsDreamObject<DreamObject>(out var firstDreamObject)) {
var result = firstDreamObject.OperatorDivide(second, state);
state.Push(result);
} else {
throw new Exception($"Invalid divide operation on {first} and {second}");
switch (first.Type) {
case DreamValue.DreamValueType.DreamObject when first.IsNull:
state.Push(new DreamValue(0));
break;
case DreamValue.DreamValueType.Float when second.IsNull:
state.Push(new DreamValue(first.MustGetValueAsFloat()));
break;
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
var secondFloat = second.MustGetValueAsFloat();
if (secondFloat == 0) {
throw new Exception("Division by zero");
}

state.Push(new DreamValue(first.MustGetValueAsFloat() / secondFloat));
break;
case DreamValue.DreamValueType.DreamObject:
var result = first.MustGetValueAsDreamObject()!.OperatorDivide(second, state);
state.Push(result);
break;
default:
throw new Exception($"Invalid divide operation on {first} and {second}");
}

return ProcStatus.Continue;
}

Expand Down Expand Up @@ -1128,6 +1154,9 @@ public static ProcStatus Mask(DMProcState state) {
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
result = new DreamValue(first.MustGetValueAsInteger() & second.MustGetValueAsInteger());
break;
case DreamValue.DreamValueType.Float when second.IsNull:
result = new DreamValue(0);
break;
default:
throw new Exception("Invalid mask operation on " + first + " and " + second);
}
Expand Down Expand Up @@ -2739,8 +2768,19 @@ private static DreamValue BitXorValues(DreamObjectTree objectTree, DreamValue fi
}

return new DreamValue(newList);
} else {
return new DreamValue(first.MustGetValueAsInteger() ^ second.MustGetValueAsInteger());
}

switch (first.Type) {
case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float:
return new DreamValue(first.MustGetValueAsInteger() ^ second.MustGetValueAsInteger());
case DreamValue.DreamValueType.DreamObject when first.IsNull && second.IsNull:
return DreamValue.Null;
case DreamValue.DreamValueType.DreamObject when first.IsNull && second.Type == DreamValue.DreamValueType.Float:
return new DreamValue(second.MustGetValueAsInteger());
case DreamValue.DreamValueType.Float when second.IsNull:
return new DreamValue(first.MustGetValueAsInteger());
default:
throw new Exception($"Invalid xor operation on {first} and {second}");
}
}

Expand Down
Loading