Skip to content

Commit

Permalink
Merge branch 'jumpfix' of github.com:Ruzihm/OpenDream into jumpfix
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruzihm committed Feb 18, 2025
2 parents 5cbb3ab + c7d56ea commit eae9e43
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 29 deletions.
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
36 changes: 35 additions & 1 deletion OpenDreamClient/Interface/Html/HtmlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ void PushCurrentText() {
} else {
tags.Push(tagType);

appendTo.PushTag(new MarkupNode(tagType, null, ParseAttributes(attributes)), selfClosing: attributes[^1] == "/");
bool isSelfClosing = IsSelfClosing(tagType, attributes);
appendTo.PushTag(new MarkupNode(tagType, null, ParseAttributes(attributes)), selfClosing: isSelfClosing);
}

break;
Expand Down Expand Up @@ -139,6 +140,39 @@ void PushCurrentText() {
appendTo.Pop();
}

/**
* <summary>
* Returns if a tag is written in old self-closing form, or if the tag
* represents a void element, which must have no children
* </summary>
*/
private static bool IsSelfClosing(string tagType, string[] attributes) {
if (attributes[^1] == "/") {
return true;
}

switch (tagType) {
case "area":
case "base":
case "br":
case "col":
case "embed":
case "hr":
case "img":
case "input":
case "link":
case "meta":
case "param":
case "source":
case "track":
case "wbr":
return true;

default:
return false;
}
}

private static Dictionary<string, MarkupParameter> ParseAttributes(string[] attributes) {
Dictionary<string, MarkupParameter> parsedAttributes = new();

Expand Down
44 changes: 31 additions & 13 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,39 +1403,57 @@ public static ProcStatus IsInRange(DMProcState state) {
return ProcStatus.Continue;
}

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

state.Push(TypecheckHelper(typeValue, value, true));

return ProcStatus.Continue;
}

public static ProcStatus IsType(DMProcState state) {
DreamValue typeValue = state.Pop();
DreamValue value = state.Pop();

state.Push(TypecheckHelper(typeValue, value, false));

return ProcStatus.Continue;
}

private static DreamValue TypecheckHelper(DreamValue typeValue, DreamValue value, bool doCast) {
// astype() returns null, istype() returns false
DreamValue nullOrFalse = doCast ? DreamValue.Null : DreamValue.False;
TreeEntry? type;

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

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

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

return ProcStatus.Continue;
return nullOrFalse;
}

if (value.TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null) {
state.Push(new DreamValue(dreamObject.IsSubtypeOf(type) ? 1 : 0));
} else {
state.Push(DreamValue.False);
if (dreamObject.IsSubtypeOf(type)) {
return doCast ? new DreamValue(dreamObject) : DreamValue.True;
}

return nullOrFalse;
}

return ProcStatus.Continue;
return nullOrFalse;
}

#endregion Comparisons

#region Flow
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

0 comments on commit eae9e43

Please sign in to comment.