diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs
index 250ed587e0..ef305dcfe6 100644
--- a/DMCompiler/DM/Builders/DMProcBuilder.cs
+++ b/DMCompiler/DM/Builders/DMProcBuilder.cs
@@ -602,37 +602,30 @@ public void ProcessLoopAssignment(LValue lValue) {
}
}
- public void ProcessStatementForList(DMExpression list, DMExpression outputVar, DMComplexValueType? dmTypes, DMASTProcBlockInner body) {
+ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, DMComplexValueType? typeCheck, DMASTProcBlockInner body) {
if (outputVar is not LValue lValue) {
compiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var");
- lValue = null;
+ lValue = new BadLValue(outputVar.Location);
}
- // Depending on the var's type and possibly a given "as [types]", an implicit istype() check is performed
- DreamPath? implicitTypeCheck = null;
- if (dmTypes == null) {
- // No "as" means the var's type will be used
- implicitTypeCheck = lValue?.Path;
- } else if (dmTypes.Value.TypePath != null) {
- // "as /datum" will perform a check for /datum
- implicitTypeCheck = dmTypes.Value.TypePath;
- } else if (!dmTypes.Value.IsAnything) {
- // "as anything" performs no check. Other values are unimplemented.
- compiler.UnimplementedWarning(outputVar.Location,
- $"As type {dmTypes} in for loops is unimplemented. No type check will be performed.");
+ // Having no "as [types]" will use the var's type for the type filter
+ if (typeCheck == null && lValue.Path != null) {
+ typeCheck = lValue.Path;
}
+ bool performingImplicitIsType = false;
list.EmitPushValue(ExprContext);
- if (implicitTypeCheck != null) {
- if (compiler.DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) {
+ if (typeCheck?.TypePath is { } typeCheckPath) { // We have a specific type to filter for
+ if (compiler.DMObjectTree.TryGetTypeId(typeCheckPath, out var filterTypeId)) {
// Create an enumerator that will do the implicit istype() for us
- proc.CreateFilteredListEnumerator(filterTypeId, implicitTypeCheck.Value);
+ proc.CreateFilteredListEnumerator(filterTypeId, typeCheckPath);
} else {
compiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location,
- $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist");
+ $"Cannot filter enumeration by type {typeCheckPath}, it does not exist");
proc.CreateListEnumerator();
}
- } else {
+ } else { // Either no type filter or we're using the slower "as [types]"
+ performingImplicitIsType = !(typeCheck is null || typeCheck.Value.IsAnything);
proc.CreateListEnumerator();
}
@@ -643,8 +636,43 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D
{
proc.MarkLoopContinue(loopLabel);
- if (lValue != null) {
- ProcessLoopAssignment(lValue);
+ ProcessLoopAssignment(lValue);
+
+ // "as mob|etc" will insert code equivalent to "if(!(istype(X, mob) || istype(X, etc))) continue;"
+ // It would be ideal if the type filtering could be done by the interpreter, like it does when the var has a type
+ // But the code currently isn't structured in a way that it could be done nicely
+ if (performingImplicitIsType) {
+ var afterTypeCheckIf = proc.NewLabelName();
+ var afterTypeCheckExpr = proc.NewLabelName();
+
+ void CheckType(DMValueType type, DreamPath path, ref bool doOr) {
+ if (!typeCheck!.Value.Type.HasFlag(type))
+ return;
+ if (!compiler.DMObjectTree.TryGetTypeId(path, out var typeId))
+ return;
+
+ if (doOr)
+ proc.BooleanOr(afterTypeCheckExpr);
+ doOr = true;
+
+ lValue.EmitPushValue(ExprContext);
+ proc.PushType(typeId);
+ proc.IsType();
+ }
+
+ bool doOr = false; // Only insert BooleanOr after the first type
+ CheckType(DMValueType.Area, DreamPath.Area, ref doOr);
+ CheckType(DMValueType.Turf, DreamPath.Turf, ref doOr);
+ CheckType(DMValueType.Obj, DreamPath.Obj, ref doOr);
+ CheckType(DMValueType.Mob, DreamPath.Mob, ref doOr);
+ proc.AddLabel(afterTypeCheckExpr);
+ if (doOr) {
+ proc.Not();
+ proc.JumpIfFalse(afterTypeCheckIf);
+ proc.Continue();
+ }
+
+ proc.AddLabel(afterTypeCheckIf);
}
ProcessBlockInner(body);
diff --git a/DMCompiler/DM/DMProc.cs b/DMCompiler/DM/DMProc.cs
index 670442f16b..73600868a1 100644
--- a/DMCompiler/DM/DMProc.cs
+++ b/DMCompiler/DM/DMProc.cs
@@ -579,11 +579,6 @@ public void Pop() {
WriteOpcode(DreamProcOpcode.Pop);
}
- public void PopReference(DMReference reference) {
- WriteOpcode(DreamProcOpcode.PopReference);
- WriteReference(reference, false);
- }
-
public void BooleanOr(string endLabel) {
WriteOpcode(DreamProcOpcode.BooleanOr);
WriteLabel(endLabel);
diff --git a/DMCompiler/DM/Expressions/LValue.cs b/DMCompiler/DM/Expressions/LValue.cs
index d5fd05f468..afae2566f9 100644
--- a/DMCompiler/DM/Expressions/LValue.cs
+++ b/DMCompiler/DM/Expressions/LValue.cs
@@ -31,6 +31,19 @@ public virtual void EmitPushInitial(ExpressionContext ctx) {
}
}
+///
+/// Used when there was an error regarding L-Values
+///
+/// Emit an error code before creating!
+internal sealed class BadLValue(Location location) : LValue(location, null) {
+ public override void EmitPushValue(ExpressionContext ctx) {
+ // It's normal to have this expression exist when there are errors in the code
+ // But in the runtime we say it's a compiler bug because the compiler should never have output it
+ ctx.Proc.PushString("Encountered a bad LValue (compiler bug!)");
+ ctx.Proc.Throw();
+ }
+}
+
// global
internal class Global(Location location) : LValue(location, null) {
public override DMReference EmitReference(ExpressionContext ctx, string endLabel,
diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
index 777574d9dc..9e9ef6afaa 100644
--- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
+++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
@@ -89,7 +89,7 @@ public static ProcStatus CreateAssociativeList(DMProcState state) {
return ProcStatus.Continue;
}
- private static IDreamValueEnumerator GetContentsEnumerator(DreamObjectTree objectTree, AtomManager atomManager, DreamValue value, TreeEntry? filterType) {
+ private static IDreamValueEnumerator GetContentsEnumerator(AtomManager atomManager, DreamValue value, TreeEntry? filterType) {
if (!value.TryGetValueAsDreamList(out var list)) {
if (value.TryGetValueAsDreamObject(out var dreamObject)) {
if (dreamObject == null)
@@ -121,7 +121,7 @@ private static IDreamValueEnumerator GetContentsEnumerator(DreamObjectTree objec
public static ProcStatus CreateListEnumerator(DMProcState state) {
var enumeratorId = state.ReadInt();
- var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), null);
+ var enumerator = GetContentsEnumerator(state.Proc.AtomManager, state.Pop(), null);
state.Enumerators[enumeratorId] = enumerator;
return ProcStatus.Continue;
@@ -131,7 +131,7 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) {
var enumeratorId = state.ReadInt();
var filterTypeId = state.ReadInt();
var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId);
- var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType);
+ var enumerator = GetContentsEnumerator(state.Proc.AtomManager, state.Pop(), filterType);
state.Enumerators[enumeratorId] = enumerator;
return ProcStatus.Continue;
@@ -281,29 +281,21 @@ public static ProcStatus EnumerateNoAssign(DMProcState state) {
///
/// Helper function of to handle text macros that are "suffix" (coming after the noun) pronouns
///
+ ///
+ ///
+ ///
/// This should be in MALE,FEMALE,PLURAL,NEUTER order.
- private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadOnlySpan interps, int prevInterpIndex, string[] pronouns)
- {
- DreamObject? dreamObject;
+ private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadOnlySpan interps, int prevInterpIndex, string[] pronouns) {
if (prevInterpIndex == -1 || prevInterpIndex >= interps.Length) // We should probably be throwing here
- {
return;
- }
- interps[prevInterpIndex].TryGetValueAsDreamObject(out dreamObject);
- if (dreamObject == null)
- {
+ if (!interps[prevInterpIndex].TryGetValueAsDreamObject(out var dreamObject))
return;
- }
- bool hasGender = dreamObject.TryGetVariable("gender", out var objectGender); // NOTE: in DM, this has to be a native property.
- if (!hasGender)
- {
+ if (!dreamObject.TryGetVariable("gender", out var objectGender)) // NOTE: in DM, this has to be a native property.
return;
- }
if (!objectGender.TryGetValueAsString(out var genderStr))
return;
- switch(genderStr)
- {
+ switch(genderStr) {
case "male":
formattedString.Append(pronouns[0]);
return;
@@ -319,18 +311,17 @@ private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadO
default:
return;
}
-
}
private static void ToRoman(ref StringBuilder formattedString, ReadOnlySpan interps, int nextInterpIndex, bool upperCase) {
char[] arr;
if(upperCase) {
- arr = new char[] { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
+ arr = new[] { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
} else {
- arr = new char[] { 'm', 'd', 'c', 'l', 'x', 'v', 'i' };
+ arr = new[] { 'm', 'd', 'c', 'l', 'x', 'v', 'i' };
}
- int[] numArr = new int[] { 1000, 500, 100, 50, 10, 5, 1 };
+ int[] numArr = new[] { 1000, 500, 100, 50, 10, 5, 1 };
if(!interps[nextInterpIndex].TryGetValueAsFloat(out float value)) {
return;
@@ -363,6 +354,7 @@ private static void ToRoman(ref StringBuilder formattedString, ReadOnlySpan(out var dreamObject)) {
bool hasName = dreamObject.TryGetVariable("name", out var objectName);
if (!hasName) continue;
string nameStr = objectName.Stringify();
- if (!DreamObject.StringIsProper(nameStr))
- {
+ if (!DreamObject.StringIsProper(nameStr)) {
formattedString.Append(formatType == StringFormatEncoder.FormatSuffix.UpperDefiniteArticle ? "The " : "the ");
}
}
+
continue;
}
case StringFormatEncoder.FormatSuffix.UpperIndefiniteArticle:
@@ -477,40 +467,38 @@ public static ProcStatus FormatString(DMProcState state) {
}
//Suffix macros
case StringFormatEncoder.FormatSuffix.UpperSubjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "He", "She", "They", "Tt" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "He", "She", "They", "Tt" });
break;
case StringFormatEncoder.FormatSuffix.LowerSubjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "he", "she", "they", "it" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "he", "she", "they", "it" });
break;
case StringFormatEncoder.FormatSuffix.UpperPossessiveAdjective:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "His", "Her", "Their", "Its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "His", "Her", "Their", "Its" });
break;
case StringFormatEncoder.FormatSuffix.LowerPossessiveAdjective:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "his", "her", "their", "its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "his", "her", "their", "its" });
break;
case StringFormatEncoder.FormatSuffix.ObjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "him", "her", "them", "it" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "him", "her", "them", "it" });
break;
case StringFormatEncoder.FormatSuffix.ReflexivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "himself", "herself", "themself", "itself" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "himself", "herself", "themself", "itself" });
break;
case StringFormatEncoder.FormatSuffix.UpperPossessivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "His", "Hers", "Theirs", "Its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "His", "Hers", "Theirs", "Its" });
break;
case StringFormatEncoder.FormatSuffix.LowerPossessivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "his", "hers", "theirs", "its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "his", "hers", "theirs", "its" });
break;
case StringFormatEncoder.FormatSuffix.PluralSuffix:
- if (interps[prevInterpIndex].TryGetValueAsFloat(out var pluralNumber) && pluralNumber == 1)
- {
+ if (interps[prevInterpIndex].TryGetValueAsFloat(out var pluralNumber) && pluralNumber.Equals(1f))
continue;
- }
+
formattedString.Append("s");
continue;
case StringFormatEncoder.FormatSuffix.OrdinalIndicator:
var interp = interps[prevInterpIndex];
if (interp.TryGetValueAsInteger(out var ordinalNumber)) {
-
// For some mystical reason byond converts \th to integers
// This is slightly hacky but the only reliable way I know how to replace the number
// Need to call stringy to make sure its the right length to cut
@@ -539,7 +527,7 @@ public static ProcStatus FormatString(DMProcState state) {
formattedString.Append("0th");
}
} else if (interp.TryGetValueAsDreamObject(out var interpObj)) {
- var typeStr = interpObj.ObjectDefinition.Type.ToString();
+ var typeStr = interpObj.ObjectDefinition.Type;
var lastIdx = formattedString.ToString().LastIndexOf(typeStr);
if (lastIdx != -1) { // Can this even fail?
formattedString.Remove(lastIdx, typeStr.Length);
@@ -550,6 +538,7 @@ public static ProcStatus FormatString(DMProcState state) {
// we support this behavior for some non-floats but not all, so just append 0th anyways for now
formattedString.Append("0th");
}
+
continue;
case StringFormatEncoder.FormatSuffix.LowerRoman:
postPrefix = formatType;
@@ -582,7 +571,7 @@ public static ProcStatus Initial(DMProcState state) {
return ProcStatus.Continue;
}
- if (!key.TryGetValueAsString(out string property)) {
+ if (!key.TryGetValueAsString(out string? property)) {
throw new Exception("Invalid var for initial() call: " + key);
}
@@ -707,9 +696,11 @@ public static ProcStatus PushGlobalVars(DMProcState state) {
state.Push(new DreamValue(new DreamGlobalVars(state.Proc.ObjectTree.List.ObjectDefinition)));
return ProcStatus.Continue;
}
+
#endregion Values
#region Math
+
public static ProcStatus Add(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();
@@ -732,6 +723,7 @@ public static ProcStatus Add(DMProcState state) {
if (second.Type == DreamValue.DreamValueType.Float) {
output = new DreamValue(firstFloat + second.MustGetValueAsFloat());
}
+
break;
}
case DreamValue.DreamValueType.String when second.Type == DreamValue.DreamValueType.String:
@@ -1048,6 +1040,7 @@ public static ProcStatus BooleanOr(DMProcState state) {
state.Push(a);
state.Jump(jumpPosition);
}
+
return ProcStatus.Continue;
}
@@ -1217,6 +1210,7 @@ public static ProcStatus Multiply(DMProcState state) {
} else {
throw new Exception($"Invalid multiply operation on {first} and {second}");
}
+
return ProcStatus.Continue;
}
@@ -1310,9 +1304,11 @@ public static ProcStatus Subtract(DMProcState state) {
return ProcStatus.Continue;
}
+
#endregion Math
#region Comparisons
+
public static ProcStatus CompareEquals(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();
@@ -1457,6 +1453,7 @@ private static DreamValue TypecheckHelper(DreamValue typeValue, DreamValue value
#endregion Comparisons
#region Flow
+
public static ProcStatus Call(DMProcState state) {
DreamReference procRef = state.ReadReference();
var argumentInfo = state.ReadProcArguments();
@@ -1865,9 +1862,11 @@ public static ProcStatus ReturnReferenceValue(DMProcState state) {
state.SetReturn(state.GetReferenceValue(reference));
return ProcStatus.Returned;
}
+
#endregion Flow
#region Builtins
+
public static ProcStatus GetStep(DMProcState state) {
var d = state.Pop();
var l = state.Pop();
@@ -2253,10 +2252,10 @@ public static ProcStatus PickUnweighted(DMProcState state) {
}
public static ProcStatus Prob(DMProcState state) {
- DreamValue P = state.Pop();
+ DreamValue probability = state.Pop();
- if (P.TryGetValueAsFloat(out float probability)) {
- int result = (state.DreamManager.Random.Prob(probability / 100)) ? 1 : 0;
+ if (probability.TryGetValueAsFloat(out float probabilityValue)) {
+ int result = (state.DreamManager.Random.Prob(probabilityValue / 100)) ? 1 : 0;
state.Push(new DreamValue(result));
} else {
@@ -2276,7 +2275,7 @@ public static ProcStatus IsSaved(DMProcState state) {
return ProcStatus.Continue;
}
- if (!key.TryGetValueAsString(out string property)) {
+ if (!key.TryGetValueAsString(out string? property)) {
throw new Exception($"Invalid var for issaved() call: {key}");
}
@@ -2304,9 +2303,11 @@ public static ProcStatus IsSaved(DMProcState state) {
return ProcStatus.Continue;
}
+
#endregion Builtins
#region Others
+
private static void PerformOutput(DreamValue a, DreamValue b) {
if (a.TryGetValueAsDreamResource(out var resource)) {
resource.Output(b);
@@ -2650,9 +2651,11 @@ public static ProcStatus DereferenceCall(DMProcState state) {
return state.Call(proc, instance, arguments);
}
+
#endregion Others
#region Helpers
+
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
private static bool IsEqual(DreamValue first, DreamValue second) {
// null should only ever be equal to null
@@ -2756,9 +2759,10 @@ private static bool IsGreaterThan(DreamValue first, DreamValue second) {
default: {
if (first.IsNull) {
if (second.Type == DreamValue.DreamValueType.Float) return 0 > second.MustGetValueAsFloat();
- if (second.TryGetValueAsString(out var s)) return false;
+ if (second.TryGetValueAsString(out _)) return false;
if (second.IsNull) return false;
}
+
throw new Exception("Invalid greater than comparison on " + first + " and " + second);
}
}
@@ -2778,6 +2782,7 @@ private static bool IsLessThan(DreamValue first, DreamValue second) {
if (second.TryGetValueAsString(out var s)) return s != "";
if (second.IsNull) return false;
}
+
throw new Exception("Invalid less than comparison between " + first + " and " + second);
}
}
@@ -2841,6 +2846,7 @@ private static DreamValue ModulusModulusValues(DreamValue first, DreamValue seco
fraction -= MathF.Truncate(fraction);
return new DreamValue(fraction * secondFloat);
}
+
throw new Exception("Invalid modulusmodulus operation on " + first + " and " + second);
}
@@ -2913,16 +2919,18 @@ private static DreamValue CalculateGradient(List gradientValues, Dre
float normalized = (index - leftBound) / (rightBound - leftBound);
// Cheap way to make sure the gradient works at the extremes (eg 1 and 0)
- if (!left.HasValue || (right.HasValue && normalized == 1) || (right.HasValue && normalized == 0)) {
+ if (!left.HasValue || (right.HasValue && normalized.Equals(1f)) || (right.HasValue && normalized == 0)) {
if (right?.AByte == 255) {
- return new DreamValue(right?.ToHexNoAlpha().ToLower() ?? "#00000000");
+ return new DreamValue(right.Value.ToHexNoAlpha().ToLower());
}
+
return new DreamValue(right?.ToHex().ToLower() ?? "#00000000");
} else if (!right.HasValue) {
- if (left?.AByte == 255) {
- return new DreamValue(left?.ToHexNoAlpha().ToLower() ?? "#00000000");
+ if (left.Value.AByte == 255) {
+ return new DreamValue(left.Value.ToHexNoAlpha().ToLower());
}
- return new DreamValue(left?.ToHex().ToLower() ?? "#00000000");
+
+ return new DreamValue(left.Value.ToHex().ToLower());
} else if (!left.HasValue && !right.HasValue) {
throw new InvalidOperationException("Failed to find any colors");
}
@@ -3027,7 +3035,6 @@ public static ProcStatus PushNStrings(DMProcState state) {
state.Push(new DreamValue(str));
}
-
return ProcStatus.Continue;
}
@@ -3094,7 +3101,7 @@ public static ProcStatus SwitchOnFloat(DMProcState state) {
int casePosition = state.ReadInt();
var test = state.Pop();
if (test.TryGetValueAsFloat(out var value)) {
- if (testValue == value) {
+ if (testValue.Equals(value)) {
state.Jump(casePosition);
} else {
state.Push(test);
@@ -3123,7 +3130,6 @@ public static ProcStatus SwitchOnString(DMProcState state) {
return ProcStatus.Continue;
}
-
public static ProcStatus PushNOfStringFloat(DMProcState state) {
int count = state.ReadInt();
diff --git a/OpenDreamRuntime/Procs/DreamEnumerators.cs b/OpenDreamRuntime/Procs/DreamEnumerators.cs
index 6cd5583e31..b9da196424 100644
--- a/OpenDreamRuntime/Procs/DreamEnumerators.cs
+++ b/OpenDreamRuntime/Procs/DreamEnumerators.cs
@@ -11,21 +11,13 @@ public interface IDreamValueEnumerator {
/// Enumerates a range of numbers with a given step
/// for (var/i in 1 to 10 step 2)
///
-internal sealed class DreamValueRangeEnumerator : IDreamValueEnumerator {
- private float _current;
- private readonly float _end;
- private readonly float _step;
-
- public DreamValueRangeEnumerator(float rangeStart, float rangeEnd, float step) {
- _current = rangeStart - step;
- _end = rangeEnd;
- _step = step;
- }
+internal sealed class DreamValueRangeEnumerator(float rangeStart, float rangeEnd, float step) : IDreamValueEnumerator {
+ private float _current = rangeStart - step;
public bool Enumerate(DMProcState state, DreamReference? reference) {
- _current += _step;
+ _current += step;
- bool successful = (_step > 0) ? _current <= _end : _current >= _end;
+ bool successful = (step > 0) ? _current <= rangeEnd : _current >= rangeEnd;
if (successful && reference != null) // Only assign if it was successful
state.AssignReference(reference.Value, new DreamValue(_current));
@@ -36,14 +28,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
///
/// Enumerates over an IEnumerable of DreamObjects, possibly filtering for a certain type
///
-internal sealed class DreamObjectEnumerator : IDreamValueEnumerator {
- private readonly IEnumerator _dreamObjectEnumerator;
- private readonly TreeEntry? _filterType;
-
- public DreamObjectEnumerator(IEnumerable dreamObjects, TreeEntry? filterType = null) {
- _dreamObjectEnumerator = dreamObjects.GetEnumerator();
- _filterType = filterType;
- }
+internal sealed class DreamObjectEnumerator(IEnumerable dreamObjects, TreeEntry? filterType = null) : IDreamValueEnumerator {
+ private readonly IEnumerator _dreamObjectEnumerator = dreamObjects.GetEnumerator();
public bool Enumerate(DMProcState state, DreamReference? reference) {
bool success = _dreamObjectEnumerator.MoveNext();
@@ -51,8 +37,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
while(success && _dreamObjectEnumerator.Current.Deleted) //skip over deleted
success = _dreamObjectEnumerator.MoveNext();
- if (_filterType != null) {
- while (success && (_dreamObjectEnumerator.Current.Deleted || !_dreamObjectEnumerator.Current.IsSubtypeOf(_filterType))) {
+ if (filterType != null) {
+ while (success && (_dreamObjectEnumerator.Current.Deleted || !_dreamObjectEnumerator.Current.IsSubtypeOf(filterType))) {
success = _dreamObjectEnumerator.MoveNext();
}
}
@@ -68,20 +54,15 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over an array of DreamValues
/// for (var/i in list(1, 2, 3))
///
-internal sealed class DreamValueArrayEnumerator : IDreamValueEnumerator {
- private readonly DreamValue[] _dreamValueArray;
+internal sealed class DreamValueArrayEnumerator(DreamValue[] dreamValueArray) : IDreamValueEnumerator {
private int _current = -1;
- public DreamValueArrayEnumerator(DreamValue[] dreamValueArray) {
- _dreamValueArray = dreamValueArray;
- }
-
public bool Enumerate(DMProcState state, DreamReference? reference) {
_current++;
- bool success = _current < _dreamValueArray.Length;
+ bool success = _current < dreamValueArray.Length;
if (reference != null)
- state.AssignReference(reference.Value, success ? _dreamValueArray[_current] : DreamValue.Null); // Assign regardless of success
+ state.AssignReference(reference.Value, success ? dreamValueArray[_current] : DreamValue.Null); // Assign regardless of success
return success;
}
}
@@ -90,27 +71,20 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over an array of DreamValues, filtering for a certain type
/// for (var/obj/item/I in contents)
///
-internal sealed class FilteredDreamValueArrayEnumerator : IDreamValueEnumerator {
- private readonly DreamValue[] _dreamValueArray;
- private readonly TreeEntry _filterType;
+internal sealed class FilteredDreamValueArrayEnumerator(DreamValue[] dreamValueArray, TreeEntry filterType) : IDreamValueEnumerator {
private int _current = -1;
- public FilteredDreamValueArrayEnumerator(DreamValue[] dreamValueArray, TreeEntry filterType) {
- _dreamValueArray = dreamValueArray;
- _filterType = filterType;
- }
-
public bool Enumerate(DMProcState state, DreamReference? reference) {
do {
_current++;
- if (_current >= _dreamValueArray.Length) {
+ if (_current >= dreamValueArray.Length) {
if (reference != null)
state.AssignReference(reference.Value, DreamValue.Null);
return false;
}
- DreamValue value = _dreamValueArray[_current];
- if (value.TryGetValueAsDreamObject(out var dreamObject) && (dreamObject?.IsSubtypeOf(_filterType) ?? false)) {
+ DreamValue value = dreamValueArray[_current];
+ if (value.TryGetValueAsDreamObject(out var dreamObject) && (dreamObject?.IsSubtypeOf(filterType) ?? false)) {
if (reference != null)
state.AssignReference(reference.Value, value);
return true;
@@ -123,12 +97,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over all atoms in the world, possibly filtering for a certain type
/// for (var/obj/item/I in world)
///
-internal sealed class WorldContentsEnumerator : IDreamValueEnumerator {
- private readonly IEnumerator _enumerator;
-
- public WorldContentsEnumerator(AtomManager atomManager, TreeEntry? filterType) {
- _enumerator = atomManager.EnumerateAtoms(filterType).GetEnumerator();
- }
+internal sealed class WorldContentsEnumerator(AtomManager atomManager, TreeEntry? filterType) : IDreamValueEnumerator {
+ private readonly IEnumerator _enumerator = atomManager.EnumerateAtoms(filterType).GetEnumerator();
public bool Enumerate(DMProcState state, DreamReference? reference) {
bool success = _enumerator.MoveNext();