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

Implement astype() #2211

Merged
merged 4 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 18 additions & 0 deletions Content.Tests/DMProject/Tests/Builtins/astype.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

/datum/foo
/datum/foo/bar

/proc/test_null()
var/datum/D = new
var/datum/foo/bar/B = astype(D)
return isnull(B)

/proc/test_type()
var/datum/foo/bar/B = new
var/datum/D = astype(B)
var/datum/foo/F = astype(D)
return F.type

/proc/RunTest()
ASSERT(test_null())
ASSERT(test_type() == /datum/foo/bar)
3 changes: 2 additions & 1 deletion DMCompiler/Bytecode/DreamProcOpcode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ public enum DreamProcOpcode : byte {
Ftp = 0x46,
[OpcodeMetadata(-1)]
Initial = 0x47,
//0x48
[OpcodeMetadata(-1)]
AsType = 0x48,
[OpcodeMetadata(-1)]
IsType = 0x49,
[OpcodeMetadata(-2)]
Expand Down
1 change: 1 addition & 0 deletions DMCompiler/Compiler/DM/AST/DMAST.ExpressionBinary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public sealed class DMASTPower(Location location, DMASTExpression a, DMASTExpres
public sealed class DMASTAdd(Location location, DMASTExpression a, DMASTExpression b) : DMASTBinary(location, a, b);
public sealed class DMASTSubtract(Location location, DMASTExpression a, DMASTExpression b) : DMASTBinary(location, a, b);
public sealed class DMASTArctan2(Location location, DMASTExpression xExpression, DMASTExpression yExpression) : DMASTBinary(location, xExpression, yExpression);
public sealed class DMASTAsType(Location location, DMASTExpression value, DMASTExpression type) : DMASTBinary(location, value, type);
public sealed class DMASTIsType(Location location, DMASTExpression value, DMASTExpression type) : DMASTBinary(location, value, type);
public sealed class DMASTGetStep(Location location, DMASTExpression refValue, DMASTExpression dir) : DMASTBinary(location, refValue, dir);
public sealed class DMASTGetDir(Location location, DMASTExpression loc1, DMASTExpression loc2) : DMASTBinary(location, loc1, loc2);
Expand Down
1 change: 1 addition & 0 deletions DMCompiler/Compiler/DM/AST/DMAST.ExpressionUnary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public sealed class DMASTNameof(Location location, DMASTExpression expression) :
public sealed class DMASTIsSaved(Location location, DMASTExpression expression) : DMASTUnary(location, expression);
public sealed class DMASTIsNull(Location location, DMASTExpression value) : DMASTUnary(location, value);
public sealed class DMASTLength(Location location, DMASTExpression value) : DMASTUnary(location, value);
public sealed class DMASTImplicitAsType(Location location, DMASTExpression value) : DMASTUnary(location, value);
public sealed class DMASTImplicitIsType(Location location, DMASTExpression value) : DMASTUnary(location, value);

/// <summary>
Expand Down
10 changes: 10 additions & 0 deletions DMCompiler/Compiler/DM/DMParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,16 @@ private void BracketWhitespace() {
? new DMASTLog(callLoc, callParameters[0].Value, null)
: new DMASTLog(callLoc, callParameters[1].Value, callParameters[0].Value);
}
case "astype": {
if (callParameters.Length != 1 && callParameters.Length != 2) {
Emit(WarningCode.InvalidArgumentCount, callLoc, "astype() requires 1 or 2 arguments");
return new DMASTInvalidExpression(callLoc);
}

return callParameters.Length == 1
? new DMASTImplicitAsType(callLoc, callParameters[0].Value)
: new DMASTAsType(callLoc, callParameters[0].Value, callParameters[1].Value);
}
case "istype": {
if (callParameters.Length != 1 && callParameters.Length != 2) {
Emit(WarningCode.InvalidArgumentCount, callLoc, "istype() requires 1 or 2 arguments");
Expand Down
35 changes: 21 additions & 14 deletions DMCompiler/DM/Builders/DMExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private DMExpression BuildExpression(DMASTExpression expression, DreamPath? infe
case DMASTNotEqual notEqual: result = BuildNotEqual(notEqual, inferredPath); break;
case DMASTDereference deref: result = BuildDereference(deref, inferredPath); break;
case DMASTLocate locate: result = BuildLocate(locate, inferredPath); break;
case DMASTImplicitAsType implicitAsType: result = BuildImplicitAsType(implicitAsType, inferredPath); break;
case DMASTImplicitIsType implicitIsType: result = BuildImplicitIsType(implicitIsType, inferredPath); break;
case DMASTList list: result = BuildList(list, inferredPath); break;
case DMASTDimensionalList dimensionalList: result = BuildDimensionalList(dimensionalList, inferredPath); break;
Expand Down Expand Up @@ -338,25 +339,21 @@ private DMExpression BuildExpression(DMASTExpression expression, DreamPath? infe
BuildExpression(locateCoordinates.Y, inferredPath),
BuildExpression(locateCoordinates.Z, inferredPath));
break;
case DMASTAsType asType: {
var lhs = BuildExpression(asType.LHS, inferredPath);
var rhs = BuildExpression(asType.RHS, lhs.Path);

result = new AsType(asType.Location, lhs, rhs);
break;
}
case DMASTIsSaved isSaved:
result = new IsSaved(isSaved.Location, BuildExpression(isSaved.Value, inferredPath));
break;
case DMASTIsType isType: {
if (isType.RHS is DMASTIdentifier { Identifier: "__IMPLIED_TYPE__" }) {
var expr = BuildExpression(isType.LHS, inferredPath);
if (expr.Path is null) {
result = BadExpression(WarningCode.BadExpression, isType.Location,
"A type could not be inferred!");
break;
}
var lhs = BuildExpression(isType.LHS, inferredPath);
var rhs = BuildExpression(isType.RHS, lhs.Path);

result = new IsTypeInferred(isType.Location, expr, expr.Path.Value);
break;
}

result = new IsType(isType.Location,
BuildExpression(isType.LHS, inferredPath),
BuildExpression(isType.RHS, inferredPath));
result = new IsType(isType.Location, lhs, rhs);
break;
}

Expand Down Expand Up @@ -1072,6 +1069,16 @@ private DMExpression BuildLocate(DMASTLocate locate, DreamPath? inferredPath) {
return new Locate(locate.Location, pathExpr, container);
}

private DMExpression BuildImplicitAsType(DMASTImplicitAsType asType, DreamPath? inferredPath) {
var expr = BuildExpression(asType.Value, inferredPath);

if (inferredPath is null) {
return BadExpression(WarningCode.BadExpression, asType.Location, "Could not infer a type");
}

return new AsTypeInferred(asType.Location, expr, inferredPath.Value);
}

private DMExpression BuildImplicitIsType(DMASTImplicitIsType isType, DreamPath? inferredPath) {
var expr = BuildExpression(isType.Value, inferredPath);

Expand Down
4 changes: 4 additions & 0 deletions DMCompiler/DM/DMProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,10 @@ public void IsSaved() {
WriteOpcode(DreamProcOpcode.IsSaved);
}

public void AsType() {
WriteOpcode(DreamProcOpcode.AsType);
}

public void IsType() {
WriteOpcode(DreamProcOpcode.IsType);
}
Expand Down
24 changes: 24 additions & 0 deletions DMCompiler/DM/Expressions/Builtins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,30 @@ public override void EmitPushValue(ExpressionContext ctx) {
}
}

// astype(x, y)
internal sealed class AsType(Location location, DMExpression expr, DMExpression path) : DMExpression(location) {
public override void EmitPushValue(ExpressionContext ctx) {
expr.EmitPushValue(ctx);
path.EmitPushValue(ctx);
ctx.Proc.AsType();
}
}

// astype(x)
internal sealed class AsTypeInferred(Location location, DMExpression expr, DreamPath path) : DMExpression(location) {
public override void EmitPushValue(ExpressionContext ctx) {
if (!ctx.ObjectTree.TryGetTypeId(path, out var typeId)) {
ctx.Compiler.Emit(WarningCode.ItemDoesntExist, Location, $"Type {path} does not exist");

return;
}

expr.EmitPushValue(ctx);
ctx.Proc.PushType(typeId);
ctx.Proc.AsType();
}
}

// istype(x, y)
internal sealed class IsType(Location location, DMExpression expr, DMExpression path) : DMExpression(location) {
public override DMComplexValueType ValType => DMValueType.Num;
Expand Down
34 changes: 34 additions & 0 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,40 @@ public static ProcStatus IsInRange(DMProcState state) {
return ProcStatus.Continue;
}

public static ProcStatus AsType(DMProcState state) {
DreamValue typeValue = state.Pop();
DreamValue value = state.Pop();
TreeEntry? type;

if (typeValue.TryGetValueAsDreamObject(out var typeObject)) {
if (typeObject == null) {
state.Push(DreamValue.Null);
return ProcStatus.Continue;
}

type = typeObject.ObjectDefinition.TreeEntry;
} else if (typeValue.TryGetValueAsAppearance(out _)) {
// /image matches an appearance
state.Push(value.TryGetValueAsDreamObject<DreamObjectImage>(out var imageObject)
? new DreamValue(imageObject)
: DreamValue.Null);

return ProcStatus.Continue;
} else if (!typeValue.TryGetValueAsType(out type)) {
state.Push(DreamValue.Null);

return ProcStatus.Continue;
}

if (value.TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null && dreamObject.IsSubtypeOf(type)) {
state.Push(new DreamValue(dreamObject));
} else {
state.Push(DreamValue.Null);
}

return ProcStatus.Continue;
}

public static ProcStatus IsType(DMProcState state) {
DreamValue typeValue = state.Pop();
DreamValue value = state.Pop();
Expand Down
1 change: 1 addition & 0 deletions OpenDreamRuntime/Procs/DMProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ public sealed class DMProcState : ProcState {
{DreamProcOpcode.Link, DMOpcodeHandlers.Link},
{DreamProcOpcode.Ftp, DMOpcodeHandlers.Ftp},
{DreamProcOpcode.Initial, DMOpcodeHandlers.Initial},
{DreamProcOpcode.AsType, DMOpcodeHandlers.AsType},
{DreamProcOpcode.IsType, DMOpcodeHandlers.IsType},
{DreamProcOpcode.LocateCoord, DMOpcodeHandlers.LocateCoord},
{DreamProcOpcode.Locate, DMOpcodeHandlers.Locate},
Expand Down
Loading