Skip to content
3 changes: 2 additions & 1 deletion DMCompiler/Compiler/DM/AST/DMAST.ExpressionConstant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public sealed class DMASTConstantResource(Location location, string path) : DMAS

public sealed class DMASTConstantNull(Location location) : DMASTExpressionConstant(location);

public sealed class DMASTConstantPath(Location location, DMASTPath value) : DMASTExpressionConstant(location) {
public sealed class DMASTConstantPath(Location location, DMASTPath value, Dictionary<string, DMASTExpression>? varOverrides) : DMASTExpressionConstant(location) {
public readonly DMASTPath Value = value;
public readonly Dictionary<string, DMASTExpression>? VarOverrides = varOverrides;
}

public sealed class DMASTUpwardPathSearch(Location location, DMASTExpressionConstant path, DMASTPath search)
Expand Down
22 changes: 11 additions & 11 deletions DMCompiler/Compiler/DM/DMParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2242,7 +2242,7 @@ private void ExpressionTo(out DMASTExpression endRange, out DMASTExpression? ste
return constant;

if (Path(true) is { } path) {
DMASTExpressionConstant pathConstant = new DMASTConstantPath(loc, path);
DMASTExpressionConstant pathConstant = new DMASTConstantPath(loc, path, null);

while (Check(TokenType.DM_Period)) {
DMASTPath? search = Path();
Expand All @@ -2256,34 +2256,34 @@ private void ExpressionTo(out DMASTExpression endRange, out DMASTExpression? ste

Whitespace(); // whitespace between path and modified type

//TODO actual modified type support
if (Check(TokenType.DM_LeftCurlyBracket)) {
Compiler.UnimplementedWarning(path.Location, "Modified types are currently not supported and modified values will be ignored.");

BracketWhitespace();
Check(TokenType.DM_Indent); // The body could be indented. We ignore that. TODO: Better braced block parsing
Whitespace(true);
Dictionary<string, DMASTExpression> overrides = new();
DMASTIdentifier? overriding = Identifier();

while (overriding != null) {
BracketWhitespace();
Consume(TokenType.DM_Equals, "Expected '='");
BracketWhitespace();

Expression(); // TODO: Use this (one day...)

DMASTExpression? value = Expression();
RequireExpression(ref value);
overrides[overriding.Identifier] = value;
if (Check(TokenType.DM_Semicolon)) {
BracketWhitespace();
Whitespace(true);
overriding = Identifier();
} else {
overriding = null;
}
}

Check(TokenType.DM_Dedent); // We ignore indents/dedents in the body
pathConstant = new DMASTConstantPath(loc, path, overrides);
Check(TokenType.DM_Dedent);
BracketWhitespace();
Whitespace(true);
Consume(TokenType.DM_RightCurlyBracket, "Expected '}'");
//The lexer tosses in a newline after '}', but we avoid Newline() because we only want to remove the extra newline, not all of them
Check(TokenType.Newline);
//The lexer tosses in a newline after '}', but we avoid Newline() because we only want to remove the extra newline, not all of them
}

return pathConstant;
Expand Down
41 changes: 39 additions & 2 deletions DMCompiler/DM/Builders/DMExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,44 @@
break;
}

result = new NewPath(Compiler, newPath.Location, path,
Dictionary<string, object?> overrides = new();
if (newPath.Path.VarOverrides is null) {
result = new NewPath(Compiler, newPath.Location, path, overrides,
BuildArgumentList(newPath.Location, newPath.Parameters, inferredPath));
break;
}

if (!ObjectTree.TryGetDMObject(newPath.Path.Value.Path, out var owner)) {
return UnknownReference(newPath.Path.Location, $"Type {newPath.Path.Value.Path} does not exist");
}

var failed = false;
foreach (KeyValuePair<string, DMASTExpression> varOverride in newPath.Path.VarOverrides) {
if (!owner.HasLocalVariable(varOverride.Key)) {
return UnknownIdentifier(newPath.Path.Location, varOverride.Key);
}

if (!BuildExpression(varOverride.Value, inferredPath)
.TryAsConstant(Compiler, out var jsonConstant)) {
if (!BuildExpression(varOverride.Value, inferredPath)
.TryAsJsonRepresentation(Compiler, out var jsonValue)) {
failed = true;
break;
}

overrides[varOverride.Key] = jsonValue;
} else {
jsonConstant.TryAsJsonRepresentation(Compiler, out var jsonValue);
overrides[varOverride.Key] = jsonValue;
}
}

if (failed) {
result = BadExpression(WarningCode.BadExpression, newPath.Path.Location, "Expected a constant expression");
break;
}

result = new NewPath(Compiler, newPath.Location, path, overrides,
BuildArgumentList(newPath.Location, newPath.Parameters, inferredPath));
break;
case DMASTNewExpr newExpr:
Expand All @@ -305,7 +342,7 @@
break;
}

result = new NewPath(Compiler, newInferred.Location, inferredType,
result = new NewPath(Compiler, newInferred.Location, inferredType, new Dictionary<string, object>(),
BuildArgumentList(newInferred.Location, newInferred.Parameters, inferredPath));
break;
case DMASTPreIncrement preIncrement:
Expand Down
15 changes: 14 additions & 1 deletion DMCompiler/DM/Expressions/Builtins.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using DMCompiler.Bytecode;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using DMCompiler.Compiler;
using DMCompiler.Json;

Expand Down Expand Up @@ -66,13 +67,15 @@ internal sealed class New(DMCompiler compiler, Location location, DMExpression e
public override void EmitPushValue(ExpressionContext ctx) {
var argumentInfo = arguments.EmitArguments(ctx, null);

ctx.Proc.PushNull();
expr.EmitPushValue(ctx);
ctx.Proc.CreateObject(argumentInfo.Type, argumentInfo.StackSize);
}
}

// new /x/y/z (...)
internal sealed class NewPath(DMCompiler compiler, Location location, IConstantPath create, ArgumentList arguments) : DMExpression(location) {
internal sealed class NewPath(DMCompiler compiler, Location location, IConstantPath create,
Dictionary<string, object> variableOverrides, ArgumentList arguments) : DMExpression(location) {
public override DreamPath? Path => (create is ConstantTypeReference typeReference) ? typeReference.Path : null;
public override DMComplexValueType ValType => Path?.GetAtomType(compiler) ?? DMValueType.Anything;

Expand All @@ -86,10 +89,19 @@ public override void EmitPushValue(ExpressionContext ctx) {
var newProc = ctx.ObjectTree.GetNewProc(typeReference.Value.Id);

(argumentsType, stackSize) = arguments.EmitArguments(ctx, newProc);

ctx.Proc.PushString(JsonSerializer.Serialize(variableOverrides));
ctx.Proc.PushType(typeReference.Value.Id);
break;
case ConstantProcReference procReference: // "new /proc/new_verb(Destination)" is a thing
(argumentsType, stackSize) = arguments.EmitArguments(ctx, ctx.ObjectTree.AllProcs[procReference.Value.Id]);
if(variableOverrides.Count > 0) {
ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Cannot add a Var Override to a proc");
ctx.Proc.PushNull();
return;
}

ctx.Proc.PushNull();
ctx.Proc.PushProc(procReference.Value.Id);
break;
default:
Expand Down Expand Up @@ -528,6 +540,7 @@ internal sealed class NewList(Location location, DMExpression[] parameters) : DM

public override void EmitPushValue(ExpressionContext ctx) {
foreach (DMExpression parameter in parameters) {
ctx.Proc.PushNull();
parameter.EmitPushValue(ctx);
ctx.Proc.CreateObject(DMCallArgumentsType.None, 0);
}
Expand Down
21 changes: 20 additions & 1 deletion OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using DMCompiler;
using DMCompiler.Bytecode;
Expand Down Expand Up @@ -195,12 +196,18 @@ public static ProcStatus CreateRangeEnumerator(DMProcState state) {
public static ProcStatus CreateObject(DMProcState state) {
var argumentInfo = state.ReadProcArguments();
var val = state.Pop();
Dictionary<string, object>? overrides = null;
if (state.Pop().TryGetValueAsString(out var jsonDict)) {
overrides = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonDict);
}

if (!val.TryGetValueAsType(out var objectType)) {
if (val.TryGetValueAsString(out var pathString)) {
if (!state.Proc.ObjectTree.TryGetTreeEntry(pathString, out objectType)) {
ThrowCannotCreateUnknownObject(val);
}
} else if (val.TryGetValueAsProc(out var proc)) { // new /proc/proc_name(Destination,Name,Desc)
} else if (val.TryGetValueAsProc(out var proc)) {
// new /proc/proc_name(Destination,Name,Desc)
var arguments = state.PopProcArguments(null, argumentInfo.Type, argumentInfo.StackSize);
var destination = arguments.GetArgument(0);

Expand Down Expand Up @@ -234,12 +241,24 @@ public static ProcStatus CreateObject(DMProcState state) {
ThrowInvalidTurfLoc(loc);

state.Proc.DreamMapManager.SetTurf(turf, objectDef, newArguments);
if (overrides is not null) {
foreach (KeyValuePair<string, object> varOverride in overrides) {
turf.SetVariable(varOverride.Key,
state.Proc.ObjectTree.GetDreamValueFromJsonElement(varOverride.Value));
}
}

state.Push(loc);
return ProcStatus.Continue;
}

var newObject = state.Proc.ObjectTree.CreateObject(objectType);
if (overrides is not null) {
foreach (KeyValuePair<string, object> varOverride in overrides) {
newObject.SetVariable(varOverride.Key, state.Proc.ObjectTree.GetDreamValueFromJsonElement(varOverride.Value));
}
}

var s = newObject.InitProc(state.Thread, state.Usr, newArguments);

state.Thread.PushProcState(s);
Expand Down
Loading