From cc85bee344db5cb2a8e6727c5c30268ae0db0692 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sat, 14 Oct 2023 17:01:14 -0700 Subject: [PATCH] honestly really cursed code --- DMCompiler/Compiler/DM/DMAST.cs | 6 +++ DMCompiler/Compiler/DM/DMParser.cs | 57 ++++++++++++++++++++----- DMCompiler/DM/Visitors/DMProcBuilder.cs | 2 +- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/DMCompiler/Compiler/DM/DMAST.cs b/DMCompiler/Compiler/DM/DMAST.cs index b60eb9a1bee..b90e66913b8 100644 --- a/DMCompiler/Compiler/DM/DMAST.cs +++ b/DMCompiler/Compiler/DM/DMAST.cs @@ -620,6 +620,12 @@ public sealed class DMASTProcBlockInner : DMASTNode { /// public readonly DMASTProcStatement[] SetStatements; + /// + /// This is used to determine if we should flag for WarningCode.EmptyBlock, + /// which we don't do if there was a manually entered semicolon within. + /// + public bool NotCompletelyEmpty; + /// Initializes an empty block. public DMASTProcBlockInner(Location location) : base(location) { Statements = Array.Empty(); diff --git a/DMCompiler/Compiler/DM/DMParser.cs b/DMCompiler/Compiler/DM/DMParser.cs index 66990b5a7e9..69058ae7902 100644 --- a/DMCompiler/Compiler/DM/DMParser.cs +++ b/DMCompiler/Compiler/DM/DMParser.cs @@ -525,6 +525,7 @@ public DMASTFile File() { var loc = Current().Location; if (Check(TokenType.DM_LeftCurlyBracket)) { DMASTProcBlockInner? block; + bool hadSemicolon = false; Whitespace(); Newline(); @@ -537,9 +538,13 @@ public DMASTFile File() { List setStatements = new(); // set statements are weird and must be held separately. do { - (List? stmts, List? setStmts) = ProcBlockInner(); // Hope you understand tuples - if (stmts is not null) statements.AddRange(stmts); - if (setStmts is not null) setStatements.AddRange(setStmts); + (List? stmts, List? setStmts, bool innerHadSemicolon) = ProcBlockInner(); // Hope you understand tuples + if (stmts is not null) + statements.AddRange(stmts); + if (setStmts is not null) + setStatements.AddRange(setStmts); + if (innerHadSemicolon) + hadSemicolon = innerHadSemicolon; if (!Check(TokenType.DM_RightCurlyBracket)) { Error(WarningCode.BadToken, "Expected end of braced block"); @@ -552,7 +557,10 @@ public DMASTFile File() { } } while (true); - block = new DMASTProcBlockInner(loc, statements.ToArray(), setStatements.ToArray()); + block = new DMASTProcBlockInner(loc, statements.ToArray(), setStatements.ToArray()) + { + NotCompletelyEmpty = hadSemicolon + }; } return block; @@ -566,13 +574,16 @@ public DMASTFile File() { if (Check(TokenType.DM_Indent)) { List statements = new(); List setStatements = new(); // set statements are weird and must be held separately. + bool hadSemicolon = false; do { - (List? statements, List? setStatements) blockInner = ProcBlockInner(); + (List? statements, List? setStatements, bool hadSemicolon) blockInner = ProcBlockInner(); if (blockInner.statements is not null) statements.AddRange(blockInner.statements); if (blockInner.setStatements is not null) setStatements.AddRange(blockInner.setStatements); + if (blockInner.hadSemicolon) + hadSemicolon = blockInner.hadSemicolon; if (!Check(TokenType.DM_Dedent)) { Error("Expected end of proc statement", throwException: false); @@ -583,17 +594,26 @@ public DMASTFile File() { } } while (true); - return new DMASTProcBlockInner(loc, statements.ToArray(), setStatements.ToArray()); + return new DMASTProcBlockInner(loc, statements.ToArray(), setStatements.ToArray()) + { + NotCompletelyEmpty = hadSemicolon + }; } return null; } - public (List?, List?) ProcBlockInner() { + /// + /// Parses the inner of a proc block + /// + /// We return if there was a semicolon within for WarningCode.EmptyBlock + /// (list of proc statements, list of set statements, had a semicolon within) + public (List?, List?, bool) ProcBlockInner() { List procStatements = new(); List setStatements = new(); // We have to store them separately because they're evaluated first DMASTProcStatement? statement = null; + bool hadSemicolon = false; do { Whitespace(); @@ -613,11 +633,14 @@ public DMASTFile File() { DMASTProcBlockInner? blockInner = ProcBlock(); if (blockInner != null) procStatements.AddRange(blockInner.Statements); } - } while (Delimiter() || statement is DMASTProcStatementLabel); + } while (DelimiterHasSemicolon(ref hadSemicolon) || statement is DMASTProcStatementLabel); + Whitespace(); - if (procStatements.Count == 0) return (null,null); - return (procStatements, setStatements); + if (procStatements.Count == 0) + return (null, null, hadSemicolon); + + return (procStatements, setStatements, hadSemicolon); } public DMASTProcStatement? ProcStatement() { @@ -2616,5 +2639,19 @@ private bool Delimiter() { return hasDelimiter; } + + private bool DelimiterHasSemicolon(ref bool hasSemicolon) { + bool hasDelimiter = false; + while ( + // Once it is set to true, keep it that way + (!hasSemicolon && (hasSemicolon = Current().Type == TokenType.DM_Semicolon)) + || Check(TokenType.DM_Semicolon) + || Newline() + ) { + hasDelimiter = true; + } + + return hasDelimiter; + } } } diff --git a/DMCompiler/DM/Visitors/DMProcBuilder.cs b/DMCompiler/DM/Visitors/DMProcBuilder.cs index 14e540c08f4..136f574510d 100644 --- a/DMCompiler/DM/Visitors/DMProcBuilder.cs +++ b/DMCompiler/DM/Visitors/DMProcBuilder.cs @@ -85,7 +85,7 @@ private void ProcessBlockInner(DMASTProcBlockInner block, bool silenceEmptyBlock DMCompiler.Emit(e.Error); } } - if(!silenceEmptyBlockWarning && block.Statements.Length == 0) { // If this block has no real statements + if(!silenceEmptyBlockWarning && block.Statements.Length == 0 && !block.NotCompletelyEmpty) { // If this block has no real statements // Not an error in BYOND, but we do have an emission for this! if(block.SetStatements.Length != 0) { // Give a more articulate message about this, since it's kinda weird DMCompiler.Emit(WarningCode.EmptyBlock,block.Location,"Empty block detected - set statements are executed outside of, before, and unconditional to, this block");