diff --git a/DMCompiler/Compiler/DM/AST/DMAST.ObjectStatements.cs b/DMCompiler/Compiler/DM/AST/DMAST.ObjectStatements.cs
index a0f4f9dd81..ff3d8319ec 100644
--- a/DMCompiler/Compiler/DM/AST/DMAST.ObjectStatements.cs
+++ b/DMCompiler/Compiler/DM/AST/DMAST.ObjectStatements.cs
@@ -7,6 +7,12 @@ namespace DMCompiler.Compiler.DM.AST;
///
public abstract class DMASTStatement(Location location) : DMASTNode(location);
+///
+/// Used when there was an error parsing a statement
+///
+/// Emit an error code before creating!
+public sealed class DMASTInvalidStatement(Location location) : DMASTStatement(location);
+
public sealed class DMASTObjectDefinition(Location location, DreamPath path, DMASTBlockInner? innerBlock)
: DMASTStatement(location) {
/// Unlike other Path variables stored by AST nodes, this path is guaranteed to be the real, absolute path of this object definition block.
diff --git a/DMCompiler/Compiler/DM/DMParser.cs b/DMCompiler/Compiler/DM/DMParser.cs
index c66199337f..2949029862 100644
--- a/DMCompiler/Compiler/DM/DMParser.cs
+++ b/DMCompiler/Compiler/DM/DMParser.cs
@@ -268,17 +268,28 @@ public DMASTFile File() {
return new DMASTProcDefinition(loc, CurrentPath, parameters.ToArray(), procBlock, types);
}
- //Object definition
- if (Block() is { } block) {
- Compiler.VerbosePrint($"Parsed object {CurrentPath}");
- return new DMASTObjectDefinition(loc, CurrentPath, block);
- }
-
//Var definition(s)
if (CurrentPath.FindElement("var") != -1) {
+ bool isIndented = false;
DreamPath varPath = CurrentPath;
List varDefinitions = new();
+ var possibleNewline = Current();
+ if (Newline()) {
+ if (Check(TokenType.DM_Indent)) {
+ isIndented = true;
+ DMASTPath? newVarPath = Path();
+ if (newVarPath == null) {
+ Emit(WarningCode.InvalidVarDefinition, "Expected a var definition");
+ return new DMASTInvalidStatement(CurrentLoc);
+ }
+
+ varPath = CurrentPath.AddToPath(newVarPath.Path.PathString);
+ } else {
+ ReuseToken(possibleNewline);
+ }
+ }
+
while (true) {
Whitespace();
@@ -304,24 +315,26 @@ public DMASTFile File() {
var varDef = new DMASTObjectVarDefinition(loc, varPath, value, valType);
varDefinitions.Add(varDef);
- if (Check(TokenType.DM_Comma)) {
+ if (Check(TokenType.DM_Comma) || (isIndented && Newline())) {
Whitespace();
DMASTPath? newVarPath = Path();
if (newVarPath == null) {
Emit(WarningCode.InvalidVarDefinition, "Expected a var definition");
break;
- } else if (newVarPath.Path.Elements.Length > 1) { // TODO: This is valid DM
- Emit(WarningCode.BadToken, newVarPath.Location, "Invalid var name");
- break;
}
- varPath = CurrentPath.AddToPath("../" + newVarPath.Path.PathString);
+ varPath = CurrentPath.AddToPath(
+ isIndented ? newVarPath.Path.PathString
+ : "../" + newVarPath.Path.PathString);
} else {
break;
}
}
+ if (isIndented)
+ Consume(TokenType.DM_Dedent, "Expected end of var block");
+
return (varDefinitions.Count == 1)
? varDefinitions[0]
: new DMASTMultipleObjectVarDefinitions(loc, varDefinitions.ToArray());
@@ -336,6 +349,12 @@ public DMASTFile File() {
return new DMASTObjectVarOverride(loc, CurrentPath, value);
}
+ //Object definition
+ if (Block() is { } block) {
+ Compiler.VerbosePrint($"Parsed object {CurrentPath}");
+ return new DMASTObjectDefinition(loc, CurrentPath, block);
+ }
+
//Empty object definition
Compiler.VerbosePrint($"Parsed object {CurrentPath} - empty");
return new DMASTObjectDefinition(loc, CurrentPath, null);
diff --git a/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs b/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs
index c4b558c95d..612576368e 100644
--- a/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs
+++ b/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs
@@ -40,6 +40,8 @@ private void ProcessStatement(DMASTStatement statement, DreamPath currentType) {
}
switch (statement) {
+ case DMASTInvalidStatement:
+ break; // An error should have been emitted by whatever made this
case DMASTObjectDefinition objectDefinition:
CodeTree.AddType(objectDefinition.Path);
if (objectDefinition.InnerBlock != null)