diff --git a/Content.IntegrationTests/Content.IntegrationTests.csproj b/Content.IntegrationTests/Content.IntegrationTests.csproj index 820b1737a7..737388f6d2 100644 --- a/Content.IntegrationTests/Content.IntegrationTests.csproj +++ b/Content.IntegrationTests/Content.IntegrationTests.csproj @@ -8,6 +8,8 @@ 12 NU1507 true + Content.IntegrationTests + Content.IntegrationTests diff --git a/Content.IntegrationTests/SetupCompileDM.cs b/Content.IntegrationTests/SetupCompileDM.cs index ff532c0ddb..bb15fab3cb 100644 --- a/Content.IntegrationTests/SetupCompileDM.cs +++ b/Content.IntegrationTests/SetupCompileDM.cs @@ -15,7 +15,8 @@ public sealed class SetupCompileDm { [OneTimeSetUp] public void Compile() { - bool successfulCompile = DMCompiler.DMCompiler.Compile(new() { + DMCompiler.DMCompiler compiler = new(); + bool successfulCompile = compiler.Compile(new() { Files = new() { DmEnvironment } }); diff --git a/Content.Tests/Content.Tests.csproj b/Content.Tests/Content.Tests.csproj index a6ddc575d8..29f7538883 100644 --- a/Content.Tests/Content.Tests.csproj +++ b/Content.Tests/Content.Tests.csproj @@ -11,6 +11,8 @@ enable NU1507 true + Content.Tests + Content.Tests diff --git a/Content.Tests/DMTests.cs b/Content.Tests/DMTests.cs index 606286c3a4..684534e7e2 100644 --- a/Content.Tests/DMTests.cs +++ b/Content.Tests/DMTests.cs @@ -37,15 +37,16 @@ public enum DMTestFlags { [OneTimeSetUp] public void OneTimeSetup() { + var dmCompiler = new DMCompiler.DMCompiler(); IoCManager.InjectDependencies(this); _taskManager.Initialize(); - Compile(InitializeEnvironment); + Compile(dmCompiler, InitializeEnvironment); _dreamMan.PreInitialize(Path.ChangeExtension(InitializeEnvironment, "json")); _dreamMan.OnException += OnException; } - private static string? Compile(string sourceFile) { - bool successfulCompile = DMCompiler.DMCompiler.Compile(new() { + private string? Compile(DMCompiler.DMCompiler compiler, string sourceFile) { + bool successfulCompile = compiler.Compile(new() { Files = [sourceFile] }); @@ -64,10 +65,11 @@ public void TestFiles(string sourceFile, DMTestFlags testFlags, int errorCode) { string initialDirectory = Directory.GetCurrentDirectory(); TestContext.WriteLine($"--- TEST {sourceFile} | Flags: {testFlags}"); try { - string? compiledFile = Compile(Path.Join(initialDirectory, TestsDirectory, sourceFile)); + var dmCompiler = new DMCompiler.DMCompiler(); + var compiledFile = Compile(dmCompiler, Path.Join(initialDirectory, TestsDirectory, sourceFile)); if (testFlags.HasFlag(DMTestFlags.CompileError)) { Assert.That(errorCode == -1, Is.False, "Expected an error code"); - Assert.That(DMCompiler.DMCompiler.UniqueEmissions.Contains((WarningCode)errorCode), Is.True, $"Expected error code \"{errorCode}\" was not found"); + Assert.That(dmCompiler.UniqueEmissions.Contains((WarningCode)errorCode), Is.True, $"Expected error code \"{errorCode}\" was not found"); Assert.That(compiledFile, Is.Null, "Expected an error during DM compilation"); Cleanup(compiledFile); @@ -167,29 +169,28 @@ private static DMTestFlags GetDMTestFlags(string sourceFile, out int errorCode) DMTestFlags testFlags = DMTestFlags.NoError; errorCode = -1; // If it's null GetTests() fusses about a NRE - using (StreamReader reader = new StreamReader(sourceFile)) { - string? firstLine = reader.ReadLine(); - if (firstLine == null) - return testFlags; - if (firstLine.Contains("IGNORE", StringComparison.InvariantCulture)) - testFlags |= DMTestFlags.Ignore; - if (firstLine.Contains("COMPILE ERROR", StringComparison.InvariantCulture)) { - testFlags |= DMTestFlags.CompileError; - - Match match = ErrorCodeRegex().Match(firstLine); // "OD" followed by exactly 4 numbers - if (match.Success) { - errorCode = int.Parse(match.Groups[1].Value); - } + using StreamReader reader = new StreamReader(sourceFile); + string? firstLine = reader.ReadLine(); + if (firstLine == null) + return testFlags; + if (firstLine.Contains("IGNORE", StringComparison.InvariantCulture)) + testFlags |= DMTestFlags.Ignore; + if (firstLine.Contains("COMPILE ERROR", StringComparison.InvariantCulture)) { + testFlags |= DMTestFlags.CompileError; + + Match match = ErrorCodeRegex().Match(firstLine); // "OD" followed by exactly 4 numbers + if (match.Success) { + errorCode = int.Parse(match.Groups[1].Value); } - - if (firstLine.Contains("RUNTIME ERROR", StringComparison.InvariantCulture)) - testFlags |= DMTestFlags.RuntimeError; - if (firstLine.Contains("RETURN TRUE", StringComparison.InvariantCulture)) - testFlags |= DMTestFlags.ReturnTrue; - if (firstLine.Contains("NO RETURN", StringComparison.InvariantCulture)) - testFlags |= DMTestFlags.NoReturn; } + if (firstLine.Contains("RUNTIME ERROR", StringComparison.InvariantCulture)) + testFlags |= DMTestFlags.RuntimeError; + if (firstLine.Contains("RETURN TRUE", StringComparison.InvariantCulture)) + testFlags |= DMTestFlags.ReturnTrue; + if (firstLine.Contains("NO RETURN", StringComparison.InvariantCulture)) + testFlags |= DMTestFlags.NoReturn; + return testFlags; } diff --git a/DMCompiler/Compiler/DM/DMLexer.cs b/DMCompiler/Compiler/DM/DMLexer.cs index d56495fd6b..df06797f36 100644 --- a/DMCompiler/Compiler/DM/DMLexer.cs +++ b/DMCompiler/Compiler/DM/DMLexer.cs @@ -3,7 +3,7 @@ namespace DMCompiler.Compiler.DM; -public sealed class DMLexer : TokenLexer { +internal sealed class DMLexer : TokenLexer { public static readonly List ValidEscapeSequences = [ "icon", "Roman", "roman", @@ -68,7 +68,7 @@ public sealed class DMLexer : TokenLexer { private readonly Stack _indentationStack = new(new[] { 0 }); /// The enumerable list of tokens output by . - public DMLexer(string sourceName, IEnumerable source) : base(sourceName, source) { } + internal DMLexer(string sourceName, IEnumerable source) : base(sourceName, source) { } protected override Token ParseNextToken() { Token token; diff --git a/DMCompiler/Compiler/DM/DMParser.cs b/DMCompiler/Compiler/DM/DMParser.cs index 3f68700214..d48d13a707 100644 --- a/DMCompiler/Compiler/DM/DMParser.cs +++ b/DMCompiler/Compiler/DM/DMParser.cs @@ -4,7 +4,7 @@ using DMCompiler.DM; namespace DMCompiler.Compiler.DM { - public partial class DMParser(DMLexer lexer) : Parser(lexer) { + internal partial class DMParser(DMCompiler compiler, DMLexer lexer) : Parser(compiler, lexer) { protected Location CurrentLoc => Current().Location; protected DreamPath CurrentPath = DreamPath.Root; @@ -211,7 +211,7 @@ public DMASTFile File() { //Proc definition if (Check(TokenType.DM_LeftParenthesis)) { - DMCompiler.VerbosePrint($"Parsing proc {CurrentPath}()"); + Compiler.VerbosePrint($"Parsing proc {CurrentPath}()"); BracketWhitespace(); var parameters = DefinitionParameters(out var wasIndeterminate); @@ -249,7 +249,7 @@ public DMASTFile File() { } if (procBlock?.Statements.Length is 0 or null) { - DMCompiler.Emit(WarningCode.EmptyProc, loc, + Compiler.Emit(WarningCode.EmptyProc, loc, "Empty proc detected - add an explicit \"return\" statement"); } @@ -270,7 +270,7 @@ public DMASTFile File() { //Object definition if (Block() is { } block) { - DMCompiler.VerbosePrint($"Parsed object {CurrentPath}"); + Compiler.VerbosePrint($"Parsed object {CurrentPath}"); return new DMASTObjectDefinition(loc, CurrentPath, block); } @@ -337,7 +337,7 @@ public DMASTFile File() { } //Empty object definition - DMCompiler.VerbosePrint($"Parsed object {CurrentPath}"); + Compiler.VerbosePrint($"Parsed object {CurrentPath}"); return new DMASTObjectDefinition(loc, CurrentPath, null); } @@ -385,12 +385,12 @@ public DMASTFile File() { } } else if (Check(OperatorOverloadTypes)) { if (operatorToken is { Type: TokenType.DM_ConstantString, Value: not "" }) { - DMCompiler.Emit(WarningCode.BadToken, operatorToken.Location, + Compiler.Emit(WarningCode.BadToken, operatorToken.Location, "The quotes in a stringify overload must be empty"); } if (!ImplementedOperatorOverloadTypes.Contains(operatorToken.Type)) { - DMCompiler.UnimplementedWarning(operatorToken.Location, + Compiler.UnimplementedWarning(operatorToken.Location, $"operator{operatorToken.PrintableText} overloads are not implemented. They will be defined but never called."); } @@ -475,7 +475,7 @@ public DMASTFile File() { do { var identifier = Identifier(); if (identifier == null) { - DMCompiler.Emit(WarningCode.BadToken, Current().Location, "Identifier expected"); + Compiler.Emit(WarningCode.BadToken, Current().Location, "Identifier expected"); return null; } @@ -1279,7 +1279,7 @@ DMASTProcBlockInner GetForBody(Location forLocation) { } else { statement = ProcStatement(); if (statement == null) { - DMCompiler.Emit(WarningCode.MissingBody, forLocation, "Expected body or statement"); + Compiler.Emit(WarningCode.MissingBody, forLocation, "Expected body or statement"); statement = new DMASTInvalidProcStatement(loc); } } @@ -1450,7 +1450,7 @@ private DMASTProcStatementSwitch.SwitchCase[] SwitchInner() { DMASTExpression? expression = Expression(); if (expression == null) { if (expressions.Count == 0) - DMCompiler.Emit(WarningCode.BadExpression, Current().Location, "Expected an expression"); + Compiler.Emit(WarningCode.BadExpression, Current().Location, "Expected an expression"); break; } @@ -1460,7 +1460,7 @@ private DMASTProcStatementSwitch.SwitchCase[] SwitchInner() { var loc = Current().Location; DMASTExpression? rangeEnd = Expression(); if (rangeEnd == null) { - DMCompiler.Emit(WarningCode.BadExpression, loc, "Expected an upper limit"); + Compiler.Emit(WarningCode.BadExpression, loc, "Expected an upper limit"); rangeEnd = new DMASTConstantNull(loc); // Fallback to null } @@ -1491,7 +1491,7 @@ private DMASTProcStatementSwitch.SwitchCase[] SwitchInner() { if (Current().Type == TokenType.DM_If) { //From now on, all if/elseif/else are actually part of this if's chain, not the switch's. //Ambiguous, but that is parity behaviour. Ergo, the following emission. - DMCompiler.Emit(WarningCode.SuspiciousSwitchCase, loc, + Compiler.Emit(WarningCode.SuspiciousSwitchCase, loc, "Expected \"if\" or \"else\" - \"else if\" is ambiguous as a switch case and may cause unintended flow"); } @@ -1742,9 +1742,9 @@ private List DefinitionParameters(out bool wasIndeterm } var type = AsComplexTypes(); - DMObjectTree.TryGetDMObject(path.Path, out var dmType); + Compiler.DMObjectTree.TryGetDMObject(path.Path, out var dmType); if (type is { Type: not DMValueType.Anything } && (value is null or DMASTConstantNull) && (dmType?.IsSubtypeOf(DreamPath.Datum) ?? false)) { - DMCompiler.Emit(WarningCode.ImplicitNullType, loc, $"Variable \"{path.Path}\" is null but not a subtype of atom nor explicitly typed as nullable, append \"|null\" to \"as\". It will implicitly be treated as nullable."); + Compiler.Emit(WarningCode.ImplicitNullType, loc, $"Variable \"{path.Path}\" is null but not a subtype of atom nor explicitly typed as nullable, append \"|null\" to \"as\". It will implicitly be treated as nullable."); type |= DMValueType.Null; } @@ -2220,7 +2220,7 @@ private void ExpressionTo(out DMASTExpression endRange, out DMASTExpression? ste //TODO actual modified type support if (Check(TokenType.DM_LeftCurlyBracket)) { - DMCompiler.UnimplementedWarning(path.Location, "Modified types are currently not supported and modified values will be ignored."); + 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 @@ -2376,10 +2376,10 @@ private void BracketWhitespace() { switch (token.Type) { case TokenType.DM_Colon: - DMCompiler.Emit(WarningCode.RuntimeSearchOperator, token.Location, "Runtime search operator ':' should be avoided; prefer typecasting and using '.' instead"); + Compiler.Emit(WarningCode.RuntimeSearchOperator, token.Location, "Runtime search operator ':' should be avoided; prefer typecasting and using '.' instead"); goto case TokenType.DM_QuestionPeriod; case TokenType.DM_QuestionColon: - DMCompiler.Emit(WarningCode.RuntimeSearchOperator, token.Location, "Runtime search operator '?:' should be avoided; prefer typecasting and using '?.' instead"); + Compiler.Emit(WarningCode.RuntimeSearchOperator, token.Location, "Runtime search operator '?:' should be avoided; prefer typecasting and using '?.' instead"); goto case TokenType.DM_QuestionPeriod; case TokenType.DM_Period: case TokenType.DM_QuestionPeriod: @@ -2387,7 +2387,7 @@ private void BracketWhitespace() { var identifier = Identifier(); if (identifier == null) { - DMCompiler.Emit(WarningCode.BadToken, token.Location, "Identifier expected"); + Compiler.Emit(WarningCode.BadToken, token.Location, "Identifier expected"); return new DMASTConstantNull(token.Location); } @@ -2419,7 +2419,7 @@ private void BracketWhitespace() { ConsumeRightBracket(); if (index == null) { - DMCompiler.Emit(WarningCode.BadToken, token.Location, "Expression expected"); + Compiler.Emit(WarningCode.BadToken, token.Location, "Expression expected"); return new DMASTConstantNull(token.Location); } @@ -2456,7 +2456,7 @@ private void BracketWhitespace() { break; case DMASTDereference.IndexOperation: - DMCompiler.Emit(WarningCode.BadToken, token.Location, "Attempt to call an invalid l-value"); + Compiler.Emit(WarningCode.BadToken, token.Location, "Attempt to call an invalid l-value"); return new DMASTConstantNull(token.Location); default: @@ -2740,7 +2740,7 @@ private void BracketWhitespace() { if (path == null) path = pathType; else - DMCompiler.Emit(WarningCode.BadToken, CurrentLoc, + Compiler.Emit(WarningCode.BadToken, CurrentLoc, $"Only one type path can be used, ignoring {pathType}"); } @@ -2772,13 +2772,13 @@ private DMValueType SingleAsType(out DreamPath? path, bool allowPath = false) { path = Path()?.Path; if (allowPath) { if (path is null) { - DMCompiler.Emit(WarningCode.BadToken, typeToken.Location, "Expected value type or path"); + Compiler.Emit(WarningCode.BadToken, typeToken.Location, "Expected value type or path"); } return DMValueType.Path; } - DMCompiler.Emit(WarningCode.BadToken, typeToken.Location, "Expected value type"); + Compiler.Emit(WarningCode.BadToken, typeToken.Location, "Expected value type"); return 0; } diff --git a/DMCompiler/Compiler/DM/DMParserHelper.cs b/DMCompiler/Compiler/DM/DMParserHelper.cs index ca202a2770..38799187f7 100644 --- a/DMCompiler/Compiler/DM/DMParserHelper.cs +++ b/DMCompiler/Compiler/DM/DMParserHelper.cs @@ -6,7 +6,7 @@ namespace DMCompiler.Compiler.DM; -public partial class DMParser { +internal partial class DMParser { /// /// If the expression is null, emit an error and set it to a new /// @@ -44,7 +44,7 @@ protected void LocateNextTopLevel() { } if (Current().Type == TokenType.EndOfFile) break; - } while (((DMLexer)_lexer).CurrentIndentation() != 0); + } while (((DMLexer)Lexer).CurrentIndentation() != 0); Delimiter(); } @@ -53,7 +53,7 @@ private void ConsumeRightParenthesis() { // A missing right parenthesis has to subtract 1 from the lexer's bracket nesting counter // To keep indentation working correctly if (!Check(TokenType.DM_RightParenthesis)) { - ((DMLexer)_lexer).BracketNesting--; + ((DMLexer)Lexer).BracketNesting--; Emit(WarningCode.BadToken, "Expected ')'"); } } @@ -61,7 +61,7 @@ private void ConsumeRightParenthesis() { private void ConsumeRightBracket() { // Similar to ConsumeRightParenthesis() if (!Check(TokenType.DM_RightBracket)) { - ((DMLexer)_lexer).BracketNesting--; + ((DMLexer)Lexer).BracketNesting--; Emit(WarningCode.BadToken, "Expected ']'"); } } @@ -70,12 +70,12 @@ private void ConsumeRightBracket() { /// if error occurs. private bool CheckInterpolation(Location loc, bool hasSeenNonRefInterpolation, List? interpolationValues, string mack) { if (interpolationValues == null || interpolationValues.Count == 0) { - DMCompiler.Emit(WarningCode.MissingInterpolatedExpression, loc, $"Macro \"\\{mack}\" requires preceding interpolated expression"); + Compiler.Emit(WarningCode.MissingInterpolatedExpression, loc, $"Macro \"\\{mack}\" requires preceding interpolated expression"); return true; } if(!hasSeenNonRefInterpolation) { // More elaborate error for a more elaborate situation - DMCompiler.Emit(WarningCode.MissingInterpolatedExpression, loc, $"Macro \"\\{mack}\" requires preceding interpolated expression that is not a reference"); + Compiler.Emit(WarningCode.MissingInterpolatedExpression, loc, $"Macro \"\\{mack}\" requires preceding interpolated expression that is not a reference"); return true; } @@ -339,7 +339,7 @@ private DMASTExpression ExpressionFromString() { switch (currentToken.Type) { case TokenType.DM_ConstantString: // Constant singular piece of string, return here if (usedPrefixMacro != null) // FIXME: \the should not compiletime here, instead becoming a tab character followed by "he", when in parity mode - DMCompiler.Emit(WarningCode.MissingInterpolatedExpression, tokenLoc, + Compiler.Emit(WarningCode.MissingInterpolatedExpression, tokenLoc, $"Macro \"\\{usedPrefixMacro}\" requires interpolated expression"); return new DMASTConstantString(currentToken.Location, stringBuilder.ToString()); @@ -353,12 +353,12 @@ private DMASTExpression ExpressionFromString() { } else { var interpolatedExpression = Expression(); if (interpolatedExpression == null) - DMCompiler.Emit(WarningCode.MissingExpression, Current().Location, + Compiler.Emit(WarningCode.MissingExpression, Current().Location, "Expected an embedded expression"); // The next token should be the next piece of the string, error if not if (Current().Type is not TokenType.DM_StringMiddle and not TokenType.DM_StringEnd) { - DMCompiler.Emit(WarningCode.BadExpression, Current().Location, + Compiler.Emit(WarningCode.BadExpression, Current().Location, "Expected end of the embedded expression"); while (Current().Type is not TokenType.DM_StringMiddle and not TokenType.DM_StringEnd @@ -375,7 +375,7 @@ private DMASTExpression ExpressionFromString() { break; case TokenType.DM_StringEnd: // End of a string with interpolated values, return here if(currentInterpolationType != StringFormatEncoder.InterpolationDefault) { // this implies a prefix tried to modify a [] that never ended up existing after it - DMCompiler.Emit(WarningCode.MissingInterpolatedExpression, tokenLoc, + Compiler.Emit(WarningCode.MissingInterpolatedExpression, tokenLoc, $"Macro \"\\{usedPrefixMacro}\" must precede an interpolated expression"); } diff --git a/DMCompiler/Compiler/DMM/DMMParser.cs b/DMCompiler/Compiler/DMM/DMMParser.cs index bd7ffdfb91..bf089252ed 100644 --- a/DMCompiler/Compiler/DMM/DMMParser.cs +++ b/DMCompiler/Compiler/DMM/DMMParser.cs @@ -1,11 +1,11 @@ -using DMCompiler.DM; -using DMCompiler.Compiler.DM; +using DMCompiler.Compiler.DM; using DMCompiler.Compiler.DM.AST; +using DMCompiler.DM.Builders; using DMCompiler.Json; namespace DMCompiler.Compiler.DMM; -internal sealed class DMMParser(DMLexer lexer, int zOffset) : DMParser(lexer) { +internal sealed class DMMParser(DMCompiler compiler, DMLexer lexer, int zOffset) : DMParser(compiler, lexer) { private int _cellNameLength = -1; private readonly HashSet _skippedTypes = new(); @@ -55,7 +55,7 @@ public DreamMapJson ParseMap() { CellDefinitionJson cellDefinition = new CellDefinitionJson(currentToken.ValueAsString()); DMASTPath? objectType = Path(); while (objectType != null) { - if (!DMObjectTree.TryGetDMObject(objectType.Path, out var type) && _skippedTypes.Add(objectType.Path)) { + if (!Compiler.DMObjectTree.TryGetDMObject(objectType.Path, out var type) && _skippedTypes.Add(objectType.Path)) { Warning($"Skipping type '{objectType.Path}'"); } @@ -71,15 +71,16 @@ public DreamMapJson ParseMap() { } if (!varOverride.ObjectPath.Equals(DreamPath.Root)) - DMCompiler.ForcedError(statement.Location, $"Invalid var name '{varOverride.VarName}' in DMM on type {objectType.Path}"); + Compiler.ForcedError(statement.Location, $"Invalid var name '{varOverride.VarName}' in DMM on type {objectType.Path}"); - DMObjectTree.TryGetDMObject(objectType.Path, out var dmObject); - DMExpression value = DMExpression.Create(dmObject, null, varOverride.Value); - if (!value.TryAsJsonRepresentation(out var valueJson)) - DMCompiler.ForcedError(statement.Location, $"Failed to serialize value to json ({value})"); + Compiler.DMObjectTree.TryGetDMObject(objectType.Path, out var dmObject); + var exprBuilder = new DMExpressionBuilder(new(Compiler, dmObject, null)); + var value = exprBuilder.Create(varOverride.Value); + if (!value.TryAsJsonRepresentation(Compiler, out var valueJson)) + Compiler.ForcedError(statement.Location, $"Failed to serialize value to json ({value})"); if(!mapObject.AddVarOverride(varOverride.VarName, valueJson)) { - DMCompiler.ForcedWarning(statement.Location, $"Duplicate var override '{varOverride.VarName}' in DMM on type {objectType.Path}"); + Compiler.ForcedWarning(statement.Location, $"Duplicate var override '{varOverride.VarName}' in DMM on type {objectType.Path}"); } CurrentPath = DreamPath.Root; diff --git a/DMCompiler/Compiler/DMPreprocessor/DMMacro.cs b/DMCompiler/Compiler/DMPreprocessor/DMMacro.cs index 2c30753bfa..71ded24456 100644 --- a/DMCompiler/Compiler/DMPreprocessor/DMMacro.cs +++ b/DMCompiler/Compiler/DMPreprocessor/DMMacro.cs @@ -46,12 +46,13 @@ public bool HasParameters() { /// /// Takes given parameters and creates a list of tokens representing the expanded macro /// + /// The DMCompiler compiling this program /// The identifier being replaced with this macro /// Parameters for macro expansion. Null if none given. /// A list of tokens replacing the identifier /// Thrown if no parameters were given but are required // TODO: Convert this to an IEnumerator? Could cut down on allocations. - public virtual List? Expand(Token replacing, List>? parameters) { + public virtual List? Expand(DMCompiler compiler, Token replacing, List>? parameters) { if (_tokens == null) return null; @@ -138,7 +139,7 @@ token.Type is TokenType.DM_Preproc_TokenConcat or TokenType.DM_Preproc_Parameter // __LINE__ internal sealed class DMMacroLine() : DMMacro(null, null) { - public override List Expand(Token replacing, List>? parameters) { + public override List Expand(DMCompiler compiler, Token replacing, List>? parameters) { var line = replacing.Location.Line; if (line == null) throw new ArgumentException($"Token {replacing} does not have a line number", nameof(replacing)); @@ -151,7 +152,7 @@ public override List Expand(Token replacing, List>? parameter // __FILE__ internal sealed class DMMacroFile() : DMMacro(null, null) { - public override List Expand(Token replacing, List>? parameters) { + public override List Expand(DMCompiler compiler, Token replacing, List>? parameters) { string path = replacing.Location.SourceFile.Replace(@"\", @"\\"); //Escape any backwards slashes return [ @@ -162,18 +163,18 @@ public override List Expand(Token replacing, List>? parameter // DM_VERSION internal sealed class DMMacroVersion() : DMMacro(null, null) { - public override List Expand(Token replacing, List>? parameters) { + public override List Expand(DMCompiler compiler, Token replacing, List>? parameters) { return [ - new Token(TokenType.DM_Preproc_Number, DMCompiler.Settings.DMVersion, replacing.Location, null) + new Token(TokenType.DM_Preproc_Number, compiler.Settings.DMVersion, replacing.Location, null) ]; } } // DM_BUILD internal sealed class DMMacroBuild() : DMMacro(null, null) { - public override List Expand(Token replacing, List>? parameters) { + public override List Expand(DMCompiler compiler, Token replacing, List>? parameters) { return [ - new Token(TokenType.DM_Preproc_Number, DMCompiler.Settings.DMBuild, replacing.Location, null) + new Token(TokenType.DM_Preproc_Number, compiler.Settings.DMBuild, replacing.Location, null) ]; } } diff --git a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessor.cs b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessor.cs index fe3daa84fd..b30049cadb 100644 --- a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessor.cs +++ b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessor.cs @@ -10,7 +10,8 @@ namespace DMCompiler.Compiler.DMPreprocessor; /// The master class for handling DM preprocessing. /// This is an , and is usually accessed via its output in a for-loop. /// -public sealed class DMPreprocessor(bool enableDirectives) : IEnumerable { +internal sealed class DMPreprocessor(DMCompiler compiler, bool enableDirectives) : IEnumerable { + private readonly DMPreprocessorParser _dmPreprocessorParser = new(compiler); public readonly List IncludedMaps = new(8); public string? IncludedInterface; @@ -109,7 +110,7 @@ public IEnumerator GetEnumerator() { break; case TokenType.DM_Preproc_Else: if (!_lastIfEvaluations.TryPop(out bool? wasTruthy) || wasTruthy is null) - DMCompiler.Emit(WarningCode.BadDirective, token.Location, "Unexpected #else"); + compiler.Emit(WarningCode.BadDirective, token.Location, "Unexpected #else"); if (wasTruthy.Value) SkipIfBody(true); else @@ -124,7 +125,7 @@ public IEnumerator GetEnumerator() { break; case TokenType.DM_Preproc_EndIf: if (!_lastIfEvaluations.TryPop(out _)) - DMCompiler.Emit(WarningCode.BadDirective, token.Location, "Unexpected #endif"); + compiler.Emit(WarningCode.BadDirective, token.Location, "Unexpected #endif"); break; case TokenType.DM_Preproc_Identifier: { if (TryMacro(token)) { @@ -161,19 +162,19 @@ public IEnumerator GetEnumerator() { } case TokenType.Error: - DMCompiler.Emit(WarningCode.ErrorDirective, token.Location, token.ValueAsString()); + compiler.Emit(WarningCode.ErrorDirective, token.Location, token.ValueAsString()); break; default: - DMCompiler.Emit(WarningCode.BadToken, token.Location, + compiler.Emit(WarningCode.BadToken, token.Location, $"Invalid token encountered while preprocessing: {token.PrintableText} ({token.Type})"); break; } } if(_lastIfEvaluations.Any()) - DMCompiler.Emit(WarningCode.BadDirective, _lastSeenIf, $"Missing {_lastIfEvaluations.Count} #endif directive{(_lastIfEvaluations.Count != 1 ? 's' : "")}"); - DMCompiler.CheckAllPragmasWereSet(); + compiler.Emit(WarningCode.BadDirective, _lastSeenIf, $"Missing {_lastIfEvaluations.Count} #endif directive{(_lastIfEvaluations.Count != 1 ? 's' : "")}"); + compiler.CheckAllPragmasWereSet(); } IEnumerator IEnumerable.GetEnumerator() { @@ -181,7 +182,7 @@ IEnumerator IEnumerable.GetEnumerator() { } public void DefineMacro(string key, string value) { - var lexer = new DMPreprocessorLexer(null, "", value); + var lexer = new DMPreprocessorLexer(compiler, null, "", value); var list = new List(); while (lexer.NextToken() is { Type: not TokenType.EndOfFile } token) { @@ -198,16 +199,16 @@ public void IncludeFile(string includeDir, string file, bool isDMStandard, Locat filePath = filePath.Replace('\\', Path.DirectorySeparatorChar); if (_includedFiles.Contains(filePath)) { - DMCompiler.Emit(WarningCode.FileAlreadyIncluded, includedFrom ?? Location.Internal, $"File \"{filePath}\" was already included"); + compiler.Emit(WarningCode.FileAlreadyIncluded, includedFrom ?? Location.Internal, $"File \"{filePath}\" was already included"); return; } if (!File.Exists(filePath)) { - DMCompiler.Emit(WarningCode.MissingIncludedFile, includedFrom ?? Location.Internal, $"Could not find included file \"{filePath}\""); + compiler.Emit(WarningCode.MissingIncludedFile, includedFrom ?? Location.Internal, $"Could not find included file \"{filePath}\""); return; } - DMCompiler.VerbosePrint($"Including {file}"); + compiler.VerbosePrint($"Including {file}"); _includedFiles.Add(filePath); switch (Path.GetExtension(filePath)) { @@ -218,11 +219,11 @@ public void IncludeFile(string includeDir, string file, bool isDMStandard, Locat case ".dmf": if (IncludedInterface != null) { if(IncludedInterface == filePath) { - DMCompiler.Emit(WarningCode.FileAlreadyIncluded, includedFrom ?? Location.Internal, $"Interface \"{filePath}\" was already included"); + compiler.Emit(WarningCode.FileAlreadyIncluded, includedFrom ?? Location.Internal, $"Interface \"{filePath}\" was already included"); break; } - DMCompiler.Emit(WarningCode.InvalidInclusion, includedFrom ?? Location.Internal, $"Attempted to include a second interface file ({filePath}) while one was already included ({IncludedInterface})"); + compiler.Emit(WarningCode.InvalidInclusion, includedFrom ?? Location.Internal, $"Attempted to include a second interface file ({filePath}) while one was already included ({IncludedInterface})"); break; } @@ -230,7 +231,7 @@ public void IncludeFile(string includeDir, string file, bool isDMStandard, Locat break; case ".dms": // Webclient interface file. Probably never gonna be supported. - DMCompiler.UnimplementedWarning(includedFrom ?? Location.Internal, "DMS files are not supported"); + compiler.UnimplementedWarning(includedFrom ?? Location.Internal, "DMS files are not supported"); break; default: PreprocessFile(includeDir, file, isDMStandard); @@ -241,17 +242,17 @@ public void IncludeFile(string includeDir, string file, bool isDMStandard, Locat public void PreprocessFile(string includeDir, string file, bool isDMStandard) { file = file.Replace('\\', '/'); - _lexerStack.Push(new DMPreprocessorLexer(includeDir, file, isDMStandard)); + _lexerStack.Push(new DMPreprocessorLexer(compiler, includeDir, file, isDMStandard)); } private bool VerifyDirectiveUsage(Token token) { if (!enableDirectives) { - DMCompiler.Emit(WarningCode.MisplacedDirective, token.Location, "Cannot use a preprocessor directive here"); + compiler.Emit(WarningCode.MisplacedDirective, token.Location, "Cannot use a preprocessor directive here"); return false; } if (!_canUseDirective) { - DMCompiler.Emit(WarningCode.MisplacedDirective, token.Location, "There can only be whitespace before a preprocessor directive"); + compiler.Emit(WarningCode.MisplacedDirective, token.Location, "There can only be whitespace before a preprocessor directive"); return false; } @@ -264,7 +265,7 @@ private void HandleIncludeDirective(Token includeToken) { Token includedFileToken = GetNextToken(true); if (includedFileToken.Type != TokenType.DM_Preproc_ConstantString) { - DMCompiler.Emit(WarningCode.InvalidInclusion, includeToken.Location, $"\"{includedFileToken.Text}\" is not a valid include path"); + compiler.Emit(WarningCode.InvalidInclusion, includeToken.Location, $"\"{includedFileToken.Text}\" is not a valid include path"); return; } @@ -281,7 +282,7 @@ private void HandleDefineDirective(Token defineToken) { Token defineIdentifier = GetNextToken(true); if (defineIdentifier.Type != TokenType.DM_Preproc_Identifier) { - DMCompiler.Emit(WarningCode.BadDirective, defineIdentifier.Location, "Unexpected token, identifier expected for #define directive"); + compiler.Emit(WarningCode.BadDirective, defineIdentifier.Location, "Unexpected token, identifier expected for #define directive"); GetLineOfTokens(); // consume what's on this line and leave return; } @@ -297,19 +298,19 @@ private void HandleDefineDirective(Token defineToken) { }; if (dirTokenValue is null) { - DMCompiler.Emit(WarningCode.BadDirective, dirToken.Location, $"\"{dirToken.Text}\" is not a valid directory"); + compiler.Emit(WarningCode.BadDirective, dirToken.Location, $"\"{dirToken.Text}\" is not a valid directory"); return; } DMPreprocessorLexer currentLexer = _lexerStack.Peek(); string dir = Path.Combine(currentLexer.IncludeDirectory, dirTokenValue); - DMCompiler.AddResourceDirectory(dir); + compiler.AddResourceDirectory(dir); // In BYOND it goes on to set the FILE_DIR macro's value to the added directory // I don't see any reason to do that return; } else if (defineIdentifier.Text == "defined") { - DMCompiler.Emit(WarningCode.SoftReservedKeyword, defineIdentifier.Location, "Reserved keyword 'defined' cannot be used as macro name"); + compiler.Emit(WarningCode.SoftReservedKeyword, defineIdentifier.Location, "Reserved keyword 'defined' cannot be used as macro name"); } List parameters = null; @@ -327,13 +328,13 @@ private void HandleDefineDirective(Token defineToken) { case TokenType.DM_Preproc_Identifier: canConsumeComma = true; if (foundVariadic) { - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Variadic argument '{parameters.Last()}' must be the last argument"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Variadic argument '{parameters.Last()}' must be the last argument"); foundVariadic = false; // Reduces error spam if there's several arguments after it continue; } if(Check(TokenType.DM_Preproc_Punctuator_Period)) { // Check for a variadic if (!Check(TokenType.DM_Preproc_Punctuator_Period) || !Check(TokenType.DM_Preproc_Punctuator_Period)) { - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Invalid macro parameter, '{parameterToken.Text}...' expected"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Invalid macro parameter, '{parameterToken.Text}...' expected"); } parameters.Add($"{parameterToken.Text}..."); foundVariadic = true; @@ -345,11 +346,11 @@ private void HandleDefineDirective(Token defineToken) { continue; case TokenType.DM_Preproc_Punctuator_Period: // One of those "..." things, maybe? if (!Check(TokenType.DM_Preproc_Punctuator_Period) || !Check(TokenType.DM_Preproc_Punctuator_Period)) { - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Invalid macro parameter, '...' expected"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Invalid macro parameter, '...' expected"); } canConsumeComma = true; if (foundVariadic) { // Placed here so we properly consume this bogus '...' parameter if need be - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Variadic argument '{parameters.Last()}' must be the last argument"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, $"Variadic argument '{parameters.Last()}' must be the last argument"); foundVariadic = false; // Reduces error spam if there's several arguments after it continue; } @@ -357,17 +358,17 @@ private void HandleDefineDirective(Token defineToken) { continue; case TokenType.DM_Preproc_Punctuator_Comma: if(!canConsumeComma) - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Unexpected ',' in macro parameter list"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Unexpected ',' in macro parameter list"); canConsumeComma = false; continue; case TokenType.DM_Preproc_Punctuator_RightParenthesis: break; case TokenType.EndOfFile: - DMCompiler.Emit(WarningCode.BadDirective, macroToken.Location, "Missing ')' in macro definition"); // Location points to the left paren! + compiler.Emit(WarningCode.BadDirective, macroToken.Location, "Missing ')' in macro definition"); // Location points to the left paren! PushToken(parameterToken); break; default: - DMCompiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Expected a macro parameter"); + compiler.Emit(WarningCode.BadDirective, parameterToken.Location, "Expected a macro parameter"); return; } break; // If the switch gets here, the loop ends. @@ -409,10 +410,10 @@ private void HandleUndefineDirective(Token undefToken) { Token defineIdentifier = GetNextToken(true); if (defineIdentifier.Type != TokenType.DM_Preproc_Identifier) { - DMCompiler.Emit(WarningCode.BadDirective, defineIdentifier.Location, "Invalid macro identifier"); + compiler.Emit(WarningCode.BadDirective, defineIdentifier.Location, "Invalid macro identifier"); return; } else if (!_defines.ContainsKey(defineIdentifier.Text)) { - DMCompiler.Emit(WarningCode.UndefineMissingDirective, defineIdentifier.Location, $"No macro named \"{defineIdentifier.PrintableText}\""); + compiler.Emit(WarningCode.UndefineMissingDirective, defineIdentifier.Location, $"No macro named \"{defineIdentifier.PrintableText}\""); return; } @@ -470,7 +471,7 @@ private bool TryMacro(Token token) { return false; } - List? expandedTokens = macro.Expand(token, parameters); + List? expandedTokens = macro.Expand(compiler, token, parameters); if (expandedTokens != null) { for (int i = expandedTokens.Count - 1; i >= 0; i--) { Token expandedToken = expandedTokens[i]; @@ -501,13 +502,13 @@ private void HandleIfDirective(Token ifToken) { var tokens = GetLineOfTokens(); if (!tokens.Any()) { // If empty - DMCompiler.Emit(WarningCode.BadDirective, ifToken.Location, "Expression expected for #if"); + compiler.Emit(WarningCode.BadDirective, ifToken.Location, "Expression expected for #if"); HandleDegenerateIf(); return; } - float? expr = DMPreprocessorParser.ExpressionFromTokens(tokens, _defines); + float? expr = _dmPreprocessorParser.ExpressionFromTokens(tokens, _defines); if(expr is null) { - DMCompiler.Emit(WarningCode.BadDirective, ifToken.Location, "Expression is invalid"); + compiler.Emit(WarningCode.BadDirective, ifToken.Location, "Expression is invalid"); HandleDegenerateIf(); return; } @@ -524,7 +525,7 @@ private void HandleIfDefDirective(Token ifDefToken) { Token define = GetNextToken(true); if (define.Type != TokenType.DM_Preproc_Identifier) { - DMCompiler.Emit(WarningCode.BadDirective, ifDefToken.Location, "Expected a define identifier"); + compiler.Emit(WarningCode.BadDirective, ifDefToken.Location, "Expected a define identifier"); HandleDegenerateIf(); return; } @@ -542,7 +543,7 @@ private void HandleIfNDefDirective(Token ifNDefToken) { Token define = GetNextToken(true); if (define.Type != TokenType.DM_Preproc_Identifier) { - DMCompiler.Emit(WarningCode.BadDirective, ifNDefToken.Location, "Expected a define identifier"); + compiler.Emit(WarningCode.BadDirective, ifNDefToken.Location, "Expected a define identifier"); HandleDegenerateIf(); return; } @@ -556,9 +557,9 @@ private void HandleIfNDefDirective(Token ifNDefToken) { private void HandleElifDirective(Token elifToken) { if (!_lastIfEvaluations.TryPeek(out bool? wasTruthy)) - DMCompiler.Emit(WarningCode.BadDirective, elifToken.Location, "Unexpected #elif"); + compiler.Emit(WarningCode.BadDirective, elifToken.Location, "Unexpected #elif"); if (wasTruthy is null) { - DMCompiler.Emit(WarningCode.BadDirective, elifToken.Location, "Directive #elif cannot appear after #else in its flow control"); + compiler.Emit(WarningCode.BadDirective, elifToken.Location, "Directive #elif cannot appear after #else in its flow control"); SkipIfBody(); } else if (wasTruthy.Value) SkipIfBody(); @@ -572,7 +573,7 @@ private void HandleErrorOrWarningDirective(Token token) { if (!VerifyDirectiveUsage(token)) return; - DMCompiler.Emit( + compiler.Emit( token.Type == TokenType.DM_Preproc_Error ? WarningCode.ErrorDirective : WarningCode.WarningDirective, token.Location, token.Text); } @@ -605,7 +606,7 @@ private void HandlePragmaDirective() { switch(warningNameToken.Type) { case TokenType.DM_Preproc_Identifier: { if (!Enum.TryParse(warningNameToken.Text, out warningCode)) { - DMCompiler.Emit(WarningCode.InvalidWarningCode, warningNameToken.Location, $"Warning '{warningNameToken.PrintableText}' does not exist"); + compiler.Emit(WarningCode.InvalidWarningCode, warningNameToken.Location, $"Warning '{warningNameToken.PrintableText}' does not exist"); GetLineOfTokens(); // consume what's on this line and leave return; } @@ -614,7 +615,7 @@ private void HandlePragmaDirective() { } case TokenType.DM_Preproc_Number: { if (!int.TryParse(warningNameToken.Text, out var intValue)) { - DMCompiler.Emit(WarningCode.InvalidWarningCode, warningNameToken.Location, $"Warning OD{warningNameToken.PrintableText} does not exist"); + compiler.Emit(WarningCode.InvalidWarningCode, warningNameToken.Location, $"Warning OD{warningNameToken.PrintableText} does not exist"); GetLineOfTokens(); return; } @@ -623,43 +624,43 @@ private void HandlePragmaDirective() { break; } default: { - DMCompiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Invalid warning identifier '{warningNameToken.PrintableText}'"); + compiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Invalid warning identifier '{warningNameToken.PrintableText}'"); GetLineOfTokens(); return; } } if((int)warningCode < 1000) { - DMCompiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warning OD{(int)warningCode:d4} cannot be set - it must always be an error"); + compiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warning OD{(int)warningCode:d4} cannot be set - it must always be an error"); GetLineOfTokens(); return; } Token warningTypeToken = GetNextToken(true); if (warningTypeToken.Type != TokenType.DM_Preproc_Identifier) { - DMCompiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warnings can only be set to disabled, notice, warning, or error"); + compiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warnings can only be set to disabled, notice, warning, or error"); return; } switch(warningTypeToken.Text.ToLower()) { case "disabled": case "disable": - DMCompiler.SetPragma(warningCode, ErrorLevel.Disabled); + compiler.SetPragma(warningCode, ErrorLevel.Disabled); break; case "notice": case "pedantic": case "info": - DMCompiler.SetPragma(warningCode, ErrorLevel.Notice); + compiler.SetPragma(warningCode, ErrorLevel.Notice); break; case "warning": case "warn": - DMCompiler.SetPragma(warningCode, ErrorLevel.Warning); + compiler.SetPragma(warningCode, ErrorLevel.Warning); break; case "error": case "err": - DMCompiler.SetPragma(warningCode, ErrorLevel.Error); + compiler.SetPragma(warningCode, ErrorLevel.Error); break; default: - DMCompiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warnings can only be set to disabled, notice, warning, or error"); + compiler.Emit(WarningCode.BadDirective, warningNameToken.Location, $"Warnings can only be set to disabled, notice, warning, or error"); return; } } @@ -718,7 +719,7 @@ private bool SkipIfBody(bool calledByElseDirective = false) { if (ifStack != 1) break; if (calledByElseDirective) - DMCompiler.Emit(WarningCode.BadDirective, token.Location, $"Unexpected {token.PrintableText} directive"); + compiler.Emit(WarningCode.BadDirective, token.Location, $"Unexpected {token.PrintableText} directive"); _unprocessedTokens.Push(token); // Push it back onto the stack so we can interpret this later return true; default: @@ -732,7 +733,7 @@ private bool SkipIfBody(bool calledByElseDirective = false) { return false; } } - DMCompiler.Emit(WarningCode.BadDirective, Location.Unknown, "Missing #endif directive"); + compiler.Emit(WarningCode.BadDirective, Location.Unknown, "Missing #endif directive"); return false; } @@ -788,7 +789,7 @@ private bool TryGetMacroParameters(out List>? parameters) { parameters.Add(currentParameter); if (parameterToken.Type != TokenType.DM_Preproc_Punctuator_RightParenthesis) { - DMCompiler.Emit(WarningCode.BadDirective, leftParenToken.Value.Location, "Missing ')' in macro call"); + compiler.Emit(WarningCode.BadDirective, leftParenToken.Value.Location, "Missing ')' in macro call"); return false; } diff --git a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorLexer.cs b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorLexer.cs index cd702d336e..ae2f5a9e1f 100644 --- a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorLexer.cs +++ b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorLexer.cs @@ -15,13 +15,15 @@ internal sealed class DMPreprocessorLexer { public readonly string IncludeDirectory; public readonly string File; + private readonly DMCompiler _compiler; private readonly StreamReader _source; private readonly bool _isDMStandard; private char _current; private int _currentLine = 1, _currentColumn; private readonly Queue _pendingTokenQueue = new(); // TODO: Possible to remove this? - public DMPreprocessorLexer(string includeDirectory, string file, string source) { + public DMPreprocessorLexer(DMCompiler compiler, string includeDirectory, string file, string source) { + _compiler = compiler; IncludeDirectory = includeDirectory; File = file; @@ -29,7 +31,8 @@ public DMPreprocessorLexer(string includeDirectory, string file, string source) Advance(); } - public DMPreprocessorLexer(string includeDirectory, string file, bool isDMStandard) { + public DMPreprocessorLexer(DMCompiler compiler, string includeDirectory, string file, bool isDMStandard) { + _compiler = compiler; IncludeDirectory = includeDirectory; File = file; @@ -390,7 +393,7 @@ public Token NextToken(bool ignoreWhitespace = false) { string macroAttempt = text.ToLower(); if (TryMacroKeyword(macroAttempt, out var attemptKeyword)) { // if they mis-capitalized the keyword - DMCompiler.Emit(WarningCode.MiscapitalizedDirective, attemptKeyword.Value.Location, + _compiler.Emit(WarningCode.MiscapitalizedDirective, attemptKeyword.Value.Location, $"#{text} is not a valid macro keyword. Did you mean '#{macroAttempt}'?"); } diff --git a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorParser.cs b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorParser.cs index f732dba537..5130902a19 100644 --- a/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorParser.cs +++ b/DMCompiler/Compiler/DMPreprocessor/DMPreprocessorParser.cs @@ -25,14 +25,14 @@ namespace DMCompiler.Compiler.DMPreprocessor; /// An extremely simple parser that acts on a sliver of tokens that have been DM-lexed for evaluation in a preprocessor directive,
/// held separate from DMParser because of slightly different behaviour, far simpler implementation, and () possible statelessness. /// -internal static class DMPreprocessorParser { - private static List? _tokens; - private static Dictionary? _defines; - private static int _tokenIndex; - private static readonly float DegenerateValue = 0.0f; +internal class DMPreprocessorParser(DMCompiler compiler) { + private List? _tokens; + private Dictionary? _defines; + private int _tokenIndex; + private readonly float DegenerateValue = 0.0f; /// A float, because that is the only possible thing a well-formed preproc expression can evaluate to. - public static float? ExpressionFromTokens(List input, Dictionary defines) { + public float? ExpressionFromTokens(List input, Dictionary defines) { _tokens = input; _defines = defines; var ret = Expression(); @@ -42,18 +42,18 @@ internal static class DMPreprocessorParser { return ret; } - private static void Advance() { + private void Advance() { ++_tokenIndex; } - private static Token Current() { + private Token Current() { if (_tokenIndex >= _tokens!.Count) return new Token(TokenType.EndOfFile, "\0", Location.Unknown, null); return _tokens[_tokenIndex]; } - private static bool Check(TokenType type) { + private bool Check(TokenType type) { if (Current().Type == type) { Advance(); return true; @@ -62,7 +62,7 @@ private static bool Check(TokenType type) { return false; } - private static bool Check(TokenType[] types) { + private bool Check(TokenType[] types) { foreach (TokenType type in types) { if (Current().Type == type) { Advance(); @@ -73,15 +73,15 @@ private static bool Check(TokenType[] types) { return false; } - private static void Error(string msg) { - DMCompiler.Emit(WarningCode.BadDirective, Current().Location, msg); + private void Error(string msg) { + compiler.Emit(WarningCode.BadDirective, Current().Location, msg); } - private static float? Expression() { + private float? Expression() { return ExpressionOr(); } - private static float? ExpressionOr() { + private float? ExpressionOr() { float? a = ExpressionAnd(); if (a is null) return a; @@ -101,7 +101,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionAnd() { + private float? ExpressionAnd() { float? a = ExpressionComparison(); if (a is null) return a; @@ -121,7 +121,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionComparison() { + private float? ExpressionComparison() { float? a = ExpressionComparisonLtGt(); if (a is null) return a; for (Token token = Current(); Check(DMParser.ComparisonTypes); token = Current()) { @@ -150,7 +150,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionComparisonLtGt() { + private float? ExpressionComparisonLtGt() { float? a = ExpressionAdditionSubtraction(); if (a is null) return a; for (Token token = Current(); Check(DMParser.LtGtComparisonTypes); token = Current()) { @@ -179,7 +179,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionAdditionSubtraction() { + private float? ExpressionAdditionSubtraction() { float? a = ExpressionMultiplicationDivisionModulus(); if (a is null) return a; for (Token token = Current(); Check(DMParser.PlusMinusTypes); token = Current()) { @@ -202,7 +202,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionMultiplicationDivisionModulus() { + private float? ExpressionMultiplicationDivisionModulus() { float? a = ExpressionPower(); if (a is null) return a; for (Token token = Current(); Check(DMParser.MulDivModTypes); token = Current()) { @@ -228,7 +228,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionPower() { + private float? ExpressionPower() { float? a = ExpressionUnary(); if (a is null) return a; @@ -245,7 +245,7 @@ private static void Error(string msg) { return a; } - private static float? ExpressionUnary() { + private float? ExpressionUnary() { if (Check(TokenType.DM_Exclamation)) { float? expression = ExpressionUnary(); if (expression == null) { @@ -259,7 +259,7 @@ private static void Error(string msg) { return ExpressionSign(); } - private static float? ExpressionSign() { + private float? ExpressionSign() { Token token = Current(); if (Check(DMParser.PlusMinusTypes)) { @@ -278,7 +278,7 @@ private static void Error(string msg) { return ExpressionPrimary(); } - private static float? ExpressionPrimary() { + private float? ExpressionPrimary() { Token token = Current(); switch (token.Type) { case TokenType.DM_LeftParenthesis: @@ -306,7 +306,7 @@ private static void Error(string msg) { Advance(); if (!Check(TokenType.DM_RightParenthesis)) { - DMCompiler.Emit(WarningCode.DefinedMissingParen, token.Location, + compiler.Emit(WarningCode.DefinedMissingParen, token.Location, "Expected ')' to end defined() expression"); //Electing to not return a degenerate value here since "defined(x" actually isn't an ambiguous grammar; we can figure out what they meant. } @@ -328,13 +328,13 @@ private static void Error(string msg) { Advance(); if (!Check(TokenType.DM_RightParenthesis)) { - DMCompiler.Emit(WarningCode.DefinedMissingParen, token.Location, + compiler.Emit(WarningCode.DefinedMissingParen, token.Location, "Expected ')' to end fexists() expression"); } var filePath = Path.GetRelativePath(".", fExistsInner.ValueAsString().Replace('\\', '/')); - var outputDir = Path.Combine(Path.GetDirectoryName(DMCompiler.Settings.Files?[0]) ?? "/", Path.GetDirectoryName(fExistsInner.Location.SourceFile) ?? "/"); + var outputDir = Path.Combine(Path.GetDirectoryName(compiler.Settings.Files?[0]) ?? "/", Path.GetDirectoryName(fExistsInner.Location.SourceFile) ?? "/"); if (string.IsNullOrEmpty(outputDir)) outputDir = "./"; @@ -350,7 +350,7 @@ private static void Error(string msg) { } } - private static float? Constant() { + private float? Constant() { Token constantToken = Current(); switch (constantToken.Type) { diff --git a/DMCompiler/Compiler/Lexer.cs b/DMCompiler/Compiler/Lexer.cs index ce5222595b..4e336b140e 100644 --- a/DMCompiler/Compiler/Lexer.cs +++ b/DMCompiler/Compiler/Lexer.cs @@ -2,20 +2,18 @@ namespace DMCompiler.Compiler; -public class Lexer { +internal class Lexer { public Location CurrentLocation { get; protected set; } - public string SourceName { get; protected set; } - public IEnumerable Source { get; protected set; } - public bool AtEndOfSource { get; protected set; } = false; + public IEnumerable Source { get; } + public bool AtEndOfSource { get; private set; } protected Queue _pendingTokenQueue = new(); - private readonly IEnumerator _sourceEnumerator; - private SourceType _current; + private readonly IEnumerator _sourceEnumerator; + private TSourceType _current; - protected Lexer(string sourceName, IEnumerable source) { + protected Lexer(string sourceName, IEnumerable source) { CurrentLocation = new Location(sourceName, 1, 0); - SourceName = sourceName; Source = source; if (source == null) throw new FileNotFoundException("Source file could not be read: " + sourceName); @@ -49,11 +47,11 @@ protected Token CreateToken(TokenType type, char text, object? value = null) { return CreateToken(type, char.ToString(text), value); } - protected virtual SourceType GetCurrent() { + protected virtual TSourceType GetCurrent() { return _current; } - protected virtual SourceType Advance() { + protected virtual TSourceType Advance() { if (_sourceEnumerator.MoveNext()) { _current = _sourceEnumerator.Current; } else { @@ -64,56 +62,8 @@ protected virtual SourceType Advance() { } } -public class TextLexer : Lexer { - protected string _source; - protected int _currentPosition = 0; - - public TextLexer(string sourceName, string source) : base(sourceName, source) { - _source = source; - - Advance(); - } - - protected override Token ParseNextToken() { - char c = GetCurrent(); - - Token token; - switch (c) { - case '\n': token = CreateToken(TokenType.Newline, c); Advance(); break; - case '\0': token = CreateToken(TokenType.EndOfFile, c); Advance(); break; - default: token = CreateToken(TokenType.Unknown, c); break; - } - - return token; - } - - protected override char GetCurrent() { - if (AtEndOfSource) return '\0'; - else return base.GetCurrent(); - } - - protected override char Advance() { - if (GetCurrent() == '\n') { - CurrentLocation = new Location( - CurrentLocation.SourceFile, - CurrentLocation.Line + 1, - 1 - ); - } else { - CurrentLocation = new Location( - CurrentLocation.SourceFile, - CurrentLocation.Line, - CurrentLocation.Column + 1 - ); - } - - _currentPosition++; - return base.Advance(); - } -} - -public class TokenLexer : Lexer { - public TokenLexer(string sourceName, IEnumerable source) : base(sourceName, source) { +internal class TokenLexer : Lexer { + protected TokenLexer(string sourceName, IEnumerable source) : base(sourceName, source) { Advance(); } diff --git a/DMCompiler/Compiler/Parser.cs b/DMCompiler/Compiler/Parser.cs index e24de27e30..e740267f7d 100644 --- a/DMCompiler/Compiler/Parser.cs +++ b/DMCompiler/Compiler/Parser.cs @@ -1,12 +1,15 @@ namespace DMCompiler.Compiler; -public class Parser { - protected Lexer _lexer; +internal class Parser { + protected readonly Lexer Lexer; + protected readonly DMCompiler Compiler; + private Token _currentToken; private readonly Stack _tokenStack = new(1); - protected Parser(Lexer lexer) { - _lexer = lexer; + internal Parser(DMCompiler compiler, Lexer lexer) { + Compiler = compiler; + Lexer = lexer; Advance(); } @@ -22,7 +25,7 @@ protected virtual Token Advance() { if (_tokenStack.Count > 0) { _currentToken = _tokenStack.Pop(); } else { - _currentToken = _lexer.GetNextToken(); + _currentToken = Lexer.GetNextToken(); if (_currentToken.Type == TokenType.Error) { Emit(WarningCode.BadToken, _currentToken.ValueAsString()); @@ -94,12 +97,12 @@ protected TokenType Consume(TokenType[] types, string errorMessage) { /// protected void Warning(string message, Token? token = null) { token ??= _currentToken; - DMCompiler.ForcedWarning(token.Value.Location, message); + Compiler.ForcedWarning(token.Value.Location, message); } /// True if this will raise an error, false if not. You can use this return value to help improve error emission around this (depending on how permissive we're being) protected bool Emit(WarningCode code, Location location, string message) { - return DMCompiler.Emit(code, location, message); + return Compiler.Emit(code, location, message); } protected bool Emit(WarningCode code, string message) { diff --git a/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs b/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs index 4e72c00037..c4b558c95d 100644 --- a/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs +++ b/DMCompiler/DM/Builders/DMCodeTreeBuilder.cs @@ -2,78 +2,73 @@ namespace DMCompiler.DM.Builders; -internal static class DMCodeTreeBuilder { - private static DMASTFile _astFile = default!; - private static bool _leftDMStandard; +internal class DMCodeTreeBuilder(DMCompiler compiler) { + private bool _leftDMStandard; - public static void BuildCodeTree(DMASTFile astFile) { - DMCodeTree.Reset(); + private DMCodeTree CodeTree => compiler.DMCodeTree; + + public void BuildCodeTree(DMASTFile astFile) { _leftDMStandard = false; - _astFile = astFile; // Add everything in the AST to the code tree - ProcessFile(); + ProcessBlockInner(astFile.BlockInner, DreamPath.Root); // Now define everything in the code tree - DMCodeTree.DefineEverything(); - if (DMCompiler.Settings.PrintCodeTree) - DMCodeTree.Print(); + CodeTree.DefineEverything(); + if (compiler.Settings.PrintCodeTree) + CodeTree.Print(); // Create each types' initialization proc (initializes vars that aren't constants) - foreach (DMObject dmObject in DMObjectTree.AllObjects) + foreach (DMObject dmObject in compiler.DMObjectTree.AllObjects) dmObject.CreateInitializationProc(); // Compile every proc - foreach (DMProc proc in DMObjectTree.AllProcs) + foreach (DMProc proc in compiler.DMObjectTree.AllProcs) proc.Compile(); } - private static void ProcessFile() { - ProcessBlockInner(_astFile.BlockInner, DreamPath.Root); - } - - private static void ProcessBlockInner(DMASTBlockInner blockInner, DreamPath currentType) { + private void ProcessBlockInner(DMASTBlockInner blockInner, DreamPath currentType) { foreach (DMASTStatement statement in blockInner.Statements) { ProcessStatement(statement, currentType); } } - private static void ProcessStatement(DMASTStatement statement, DreamPath currentType) { + private void ProcessStatement(DMASTStatement statement, DreamPath currentType) { if (!_leftDMStandard && !statement.Location.InDMStandard) { _leftDMStandard = true; - DMCodeTree.FinishDMStandard(); + CodeTree.FinishDMStandard(); } switch (statement) { case DMASTObjectDefinition objectDefinition: - DMCodeTree.AddType(objectDefinition.Path); + CodeTree.AddType(objectDefinition.Path); if (objectDefinition.InnerBlock != null) ProcessBlockInner(objectDefinition.InnerBlock, objectDefinition.Path); break; case DMASTObjectVarDefinition varDefinition: - DMCodeTree.AddType(varDefinition.ObjectPath); - DMCodeTree.AddObjectVar(varDefinition.ObjectPath, varDefinition); + CodeTree.AddType(varDefinition.ObjectPath); + CodeTree.AddObjectVar(varDefinition.ObjectPath, varDefinition); break; case DMASTObjectVarOverride varOverride: - DMCodeTree.AddType(varOverride.ObjectPath); - DMCodeTree.AddObjectVarOverride(varOverride.ObjectPath, varOverride); + CodeTree.AddType(varOverride.ObjectPath); + CodeTree.AddObjectVarOverride(varOverride.ObjectPath, varOverride); break; case DMASTProcDefinition procDefinition: var procOwner = currentType.Combine(procDefinition.ObjectPath); - DMCodeTree.AddType(procOwner); - DMCodeTree.AddProc(procOwner, procDefinition); + CodeTree.AddType(procOwner); + CodeTree.AddProc(procOwner, procDefinition); break; case DMASTMultipleObjectVarDefinitions multipleVarDefinitions: { foreach (DMASTObjectVarDefinition varDefinition in multipleVarDefinitions.VarDefinitions) { - DMCodeTree.AddType(varDefinition.ObjectPath); - DMCodeTree.AddObjectVar(varDefinition.ObjectPath, varDefinition); + CodeTree.AddType(varDefinition.ObjectPath); + CodeTree.AddObjectVar(varDefinition.ObjectPath, varDefinition); } break; } default: - DMCompiler.ForcedError(statement.Location, $"Invalid object statement {statement.GetType()}"); + compiler.ForcedError(statement.Location, $"Invalid object statement {statement.GetType()}"); break; } } diff --git a/DMCompiler/DM/Builders/DMExpressionBuilder.cs b/DMCompiler/DM/Builders/DMExpressionBuilder.cs index 68f57ce4f2..1209be9f80 100644 --- a/DMCompiler/DM/Builders/DMExpressionBuilder.cs +++ b/DMCompiler/DM/Builders/DMExpressionBuilder.cs @@ -2,11 +2,12 @@ using Resource = DMCompiler.DM.Expressions.Resource; using DMCompiler.Compiler.DM.AST; using DMCompiler.DM.Expressions; +using static DMCompiler.DM.Builders.DMExpressionBuilder.ScopeMode; using String = DMCompiler.DM.Expressions.String; namespace DMCompiler.DM.Builders; -internal static class DMExpressionBuilder { +internal class DMExpressionBuilder(ExpressionContext ctx, DMExpressionBuilder.ScopeMode scopeMode = Normal) { public enum ScopeMode { /// All in-scope procs and vars available Normal, @@ -18,244 +19,277 @@ public enum ScopeMode { FirstPassStatic } - // TODO: Remove these terrible global flags - public static ScopeMode CurrentScopeMode = ScopeMode.Normal; + // TODO: Remove this terrible global flag public static bool ScopeOperatorEnabled = false; // Enabled on the last pass of the code tree - public static UnknownReference? EncounteredUnknownReference; - /// Don't use DMExpression.Create() inside this or anything it calls! It resets EncounteredUnknownReference - public static DMExpression BuildExpression(DMASTExpression expression, DMObject dmObject, DMProc proc, DreamPath? inferredPath = null) { + private UnknownReference? _encounteredUnknownReference; + + private DMCompiler Compiler => ctx.Compiler; + private DMObjectTree ObjectTree => ctx.ObjectTree; + + // TODO: proc and dmObject can be null, address nullability contract + public DMExpression Create(DMASTExpression expression, DreamPath? inferredPath = null) { + var expr = CreateIgnoreUnknownReference(expression, inferredPath); + if (expr is UnknownReference unknownRef) + unknownRef.EmitCompilerError(Compiler); + + return expr; + } + + public DMExpression CreateIgnoreUnknownReference(DMASTExpression expression, DreamPath? inferredPath = null) { + _encounteredUnknownReference = null; + return BuildExpression(expression, inferredPath); + } + + public void Emit(DMASTExpression expression, DreamPath? inferredPath = null) { + var expr = Create(expression, inferredPath); + expr.EmitPushValue(ctx); + } + + public bool TryConstant(DMASTExpression expression, out Constant? constant) { + var expr = Create(expression); + return expr.TryAsConstant(Compiler, out constant); + } + + /// Don't use Create() inside this or anything it calls! It resets _encounteredUnknownReference + private DMExpression BuildExpression(DMASTExpression expression, DreamPath? inferredPath = null) { DMExpression result; switch (expression) { case DMASTInvalidExpression: - // No DMCompiler.Emit() here because the parser should have emitted an error when making this + // No error emission here because the parser should have emitted an error when making this return new BadExpression(expression.Location); - case DMASTExpressionConstant constant: result = BuildConstant(constant, dmObject, proc); break; - case DMASTStringFormat stringFormat: result = BuildStringFormat(stringFormat, dmObject, proc, inferredPath); break; - case DMASTIdentifier identifier: result = BuildIdentifier(identifier, dmObject, proc, inferredPath); break; - case DMASTScopeIdentifier globalIdentifier: result = BuildScopeIdentifier(globalIdentifier, dmObject, proc, inferredPath); break; - case DMASTCallableSelf: result = new ProcSelf(expression.Location, null, proc); break; - case DMASTCallableSuper: result = new ProcSuper(expression.Location, dmObject, proc); break; - case DMASTCallableProcIdentifier procIdentifier: result = BuildCallableProcIdentifier(procIdentifier, dmObject); break; - case DMASTProcCall procCall: result = BuildProcCall(procCall, dmObject, proc, inferredPath); break; - case DMASTAssign assign: result = BuildAssign(assign, dmObject, proc, inferredPath); break; - case DMASTAssignInto assignInto: result = BuildAssignInto(assignInto, dmObject, proc, inferredPath); break; - case DMASTEqual equal: result = BuildEqual(equal, dmObject, proc, inferredPath); break; - case DMASTNotEqual notEqual: result = BuildNotEqual(notEqual, dmObject, proc, inferredPath); break; - case DMASTDereference deref: result = BuildDereference(deref, dmObject, proc, inferredPath); break; - case DMASTLocate locate: result = BuildLocate(locate, dmObject, proc, inferredPath); break; - case DMASTImplicitIsType implicitIsType: result = BuildImplicitIsType(implicitIsType, dmObject, proc, inferredPath); break; - case DMASTList list: result = BuildList(list, dmObject, proc); break; - case DMASTDimensionalList dimensionalList: result = BuildDimensionalList(dimensionalList, dmObject, proc, inferredPath); break; - case DMASTNewList newList: result = BuildNewList(newList, dmObject, proc, inferredPath); break; - case DMASTAddText addText: result = BuildAddText(addText, dmObject, proc, inferredPath); break; - case DMASTInput input: result = BuildInput(input, dmObject, proc); break; - case DMASTPick pick: result = BuildPick(pick, dmObject, proc); break; - case DMASTLog log: result = BuildLog(log, dmObject, proc, inferredPath); break; - case DMASTCall call: result = BuildCall(call, dmObject, proc, inferredPath); break; - case DMASTExpressionWrapped wrapped: result = BuildExpression(wrapped.Value, dmObject, proc, inferredPath); break; + case DMASTExpressionConstant constant: result = BuildConstant(constant); break; + case DMASTStringFormat stringFormat: result = BuildStringFormat(stringFormat, inferredPath); break; + case DMASTIdentifier identifier: result = BuildIdentifier(identifier, inferredPath); break; + case DMASTScopeIdentifier globalIdentifier: result = BuildScopeIdentifier(globalIdentifier, inferredPath); break; + case DMASTCallableSelf: result = new ProcSelf(expression.Location, ctx.Proc.ReturnTypes); break; + case DMASTCallableSuper: result = new ProcSuper(expression.Location, ctx.Type.GetProcReturnTypes(ctx.Proc.Name)); break; + case DMASTCallableProcIdentifier procIdentifier: result = BuildCallableProcIdentifier(procIdentifier, ctx.Type); break; + case DMASTProcCall procCall: result = BuildProcCall(procCall, inferredPath); break; + case DMASTAssign assign: result = BuildAssign(assign, inferredPath); break; + case DMASTAssignInto assignInto: result = BuildAssignInto(assignInto, inferredPath); break; + case DMASTEqual equal: result = BuildEqual(equal, inferredPath); break; + 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 DMASTImplicitIsType implicitIsType: result = BuildImplicitIsType(implicitIsType, inferredPath); break; + case DMASTList list: result = BuildList(list); break; + case DMASTDimensionalList dimensionalList: result = BuildDimensionalList(dimensionalList, inferredPath); break; + case DMASTNewList newList: result = BuildNewList(newList, inferredPath); break; + case DMASTAddText addText: result = BuildAddText(addText, inferredPath); break; + case DMASTInput input: result = BuildInput(input); break; + case DMASTPick pick: result = BuildPick(pick); break; + case DMASTLog log: result = BuildLog(log, inferredPath); break; + case DMASTCall call: result = BuildCall(call, inferredPath); break; + case DMASTExpressionWrapped wrapped: result = BuildExpression(wrapped.Value, inferredPath); break; case DMASTNegate negate: - result = new Negate(negate.Location, BuildExpression(negate.Value, dmObject, proc, inferredPath)); + result = new Negate(negate.Location, BuildExpression(negate.Value, inferredPath)); break; case DMASTNot not: - result = new Not(not.Location, BuildExpression(not.Value, dmObject, proc, inferredPath)); + result = new Not(not.Location, BuildExpression(not.Value, inferredPath)); break; case DMASTBinaryNot binaryNot: - result = new BinaryNot(binaryNot.Location, BuildExpression(binaryNot.Value, dmObject, proc, inferredPath)); + result = new BinaryNot(binaryNot.Location, BuildExpression(binaryNot.Value, inferredPath)); break; case DMASTAdd add: result = new Add(add.Location, - BuildExpression(add.LHS, dmObject, proc, inferredPath), - BuildExpression(add.RHS, dmObject, proc, inferredPath)); + BuildExpression(add.LHS, inferredPath), + BuildExpression(add.RHS, inferredPath)); break; case DMASTSubtract subtract: result = new Subtract(subtract.Location, - BuildExpression(subtract.LHS, dmObject, proc, inferredPath), - BuildExpression(subtract.RHS, dmObject, proc, inferredPath)); + BuildExpression(subtract.LHS, inferredPath), + BuildExpression(subtract.RHS, inferredPath)); break; case DMASTMultiply multiply: result = new Multiply(multiply.Location, - BuildExpression(multiply.LHS, dmObject, proc, inferredPath), - BuildExpression(multiply.RHS, dmObject, proc, inferredPath)); + BuildExpression(multiply.LHS, inferredPath), + BuildExpression(multiply.RHS, inferredPath)); break; case DMASTDivide divide: result = new Divide(divide.Location, - BuildExpression(divide.LHS, dmObject, proc, inferredPath), - BuildExpression(divide.RHS, dmObject, proc, inferredPath)); + BuildExpression(divide.LHS, inferredPath), + BuildExpression(divide.RHS, inferredPath)); break; case DMASTModulus modulus: result = new Modulo(modulus.Location, - BuildExpression(modulus.LHS, dmObject, proc, inferredPath), - BuildExpression(modulus.RHS, dmObject, proc, inferredPath)); + BuildExpression(modulus.LHS, inferredPath), + BuildExpression(modulus.RHS, inferredPath)); break; case DMASTModulusModulus modulusModulus: result = new ModuloModulo(modulusModulus.Location, - BuildExpression(modulusModulus.LHS, dmObject, proc, inferredPath), - BuildExpression(modulusModulus.RHS, dmObject, proc, inferredPath)); + BuildExpression(modulusModulus.LHS, inferredPath), + BuildExpression(modulusModulus.RHS, inferredPath)); break; case DMASTPower power: result = new Power(power.Location, - BuildExpression(power.LHS, dmObject, proc, inferredPath), - BuildExpression(power.RHS, dmObject, proc, inferredPath)); + BuildExpression(power.LHS, inferredPath), + BuildExpression(power.RHS, inferredPath)); break; case DMASTAppend append: result = new Append(append.Location, - BuildExpression(append.LHS, dmObject, proc, inferredPath), - BuildExpression(append.RHS, dmObject, proc, inferredPath)); + BuildExpression(append.LHS, inferredPath), + BuildExpression(append.RHS, inferredPath)); break; case DMASTCombine combine: result = new Combine(combine.Location, - BuildExpression(combine.LHS, dmObject, proc, inferredPath), - BuildExpression(combine.RHS, dmObject, proc, inferredPath)); + BuildExpression(combine.LHS, inferredPath), + BuildExpression(combine.RHS, inferredPath)); break; case DMASTRemove remove: result = new Remove(remove.Location, - BuildExpression(remove.LHS, dmObject, proc, inferredPath), - BuildExpression(remove.RHS, dmObject, proc, inferredPath)); + BuildExpression(remove.LHS, inferredPath), + BuildExpression(remove.RHS, inferredPath)); break; case DMASTMask mask: result = new Mask(mask.Location, - BuildExpression(mask.LHS, dmObject, proc, inferredPath), - BuildExpression(mask.RHS, dmObject, proc, inferredPath)); + BuildExpression(mask.LHS, inferredPath), + BuildExpression(mask.RHS, inferredPath)); break; case DMASTLogicalAndAssign lAnd: - var lAndLHS = BuildExpression(lAnd.LHS, dmObject, proc, inferredPath); - var lAndRHS = BuildExpression(lAnd.RHS, dmObject, proc, lAndLHS.NestedPath); + var lAndLHS = BuildExpression(lAnd.LHS, inferredPath); + var lAndRHS = BuildExpression(lAnd.RHS, lAndLHS.NestedPath); result = new LogicalAndAssign(lAnd.Location, lAndLHS, lAndRHS); break; case DMASTLogicalOrAssign lOr: - var lOrLHS = BuildExpression(lOr.LHS, dmObject, proc, inferredPath); - var lOrRHS = BuildExpression(lOr.RHS, dmObject, proc, lOrLHS.NestedPath); + var lOrLHS = BuildExpression(lOr.LHS, inferredPath); + var lOrRHS = BuildExpression(lOr.RHS, lOrLHS.NestedPath); result = new LogicalOrAssign(lOr.Location, lOrLHS, lOrRHS); break; case DMASTMultiplyAssign multiplyAssign: result = new MultiplyAssign(multiplyAssign.Location, - BuildExpression(multiplyAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(multiplyAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(multiplyAssign.LHS, inferredPath), + BuildExpression(multiplyAssign.RHS, inferredPath)); break; case DMASTDivideAssign divideAssign: result = new DivideAssign(divideAssign.Location, - BuildExpression(divideAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(divideAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(divideAssign.LHS, inferredPath), + BuildExpression(divideAssign.RHS, inferredPath)); break; case DMASTLeftShiftAssign leftShiftAssign: result = new LeftShiftAssign(leftShiftAssign.Location, - BuildExpression(leftShiftAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(leftShiftAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(leftShiftAssign.LHS, inferredPath), + BuildExpression(leftShiftAssign.RHS, inferredPath)); break; case DMASTRightShiftAssign rightShiftAssign: result = new RightShiftAssign(rightShiftAssign.Location, - BuildExpression(rightShiftAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(rightShiftAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(rightShiftAssign.LHS, inferredPath), + BuildExpression(rightShiftAssign.RHS, inferredPath)); break; case DMASTXorAssign xorAssign: result = new XorAssign(xorAssign.Location, - BuildExpression(xorAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(xorAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(xorAssign.LHS, inferredPath), + BuildExpression(xorAssign.RHS, inferredPath)); break; case DMASTModulusAssign modulusAssign: result = new ModulusAssign(modulusAssign.Location, - BuildExpression(modulusAssign.LHS, dmObject, proc, inferredPath), - BuildExpression(modulusAssign.RHS, dmObject, proc, inferredPath)); + BuildExpression(modulusAssign.LHS, inferredPath), + BuildExpression(modulusAssign.RHS, inferredPath)); break; case DMASTModulusModulusAssign modulusModulusAssign: - var mmAssignLHS = BuildExpression(modulusModulusAssign.LHS, dmObject, proc, inferredPath); - var mmAssignRHS = BuildExpression(modulusModulusAssign.RHS, dmObject, proc, mmAssignLHS.NestedPath); + var mmAssignLHS = BuildExpression(modulusModulusAssign.LHS, inferredPath); + var mmAssignRHS = BuildExpression(modulusModulusAssign.RHS, mmAssignLHS.NestedPath); result = new ModulusModulusAssign(modulusModulusAssign.Location, mmAssignLHS, mmAssignRHS); break; case DMASTLeftShift leftShift: result = new LeftShift(leftShift.Location, - BuildExpression(leftShift.LHS, dmObject, proc, inferredPath), - BuildExpression(leftShift.RHS, dmObject, proc, inferredPath)); + BuildExpression(leftShift.LHS, inferredPath), + BuildExpression(leftShift.RHS, inferredPath)); break; case DMASTRightShift rightShift: result = new RightShift(rightShift.Location, - BuildExpression(rightShift.LHS, dmObject, proc, inferredPath), - BuildExpression(rightShift.RHS, dmObject, proc, inferredPath)); + BuildExpression(rightShift.LHS, inferredPath), + BuildExpression(rightShift.RHS, inferredPath)); break; case DMASTBinaryAnd binaryAnd: result = new BinaryAnd(binaryAnd.Location, - BuildExpression(binaryAnd.LHS, dmObject, proc, inferredPath), - BuildExpression(binaryAnd.RHS, dmObject, proc, inferredPath)); + BuildExpression(binaryAnd.LHS, inferredPath), + BuildExpression(binaryAnd.RHS, inferredPath)); break; case DMASTBinaryXor binaryXor: result = new BinaryXor(binaryXor.Location, - BuildExpression(binaryXor.LHS, dmObject, proc, inferredPath), - BuildExpression(binaryXor.RHS, dmObject, proc, inferredPath)); + BuildExpression(binaryXor.LHS, inferredPath), + BuildExpression(binaryXor.RHS, inferredPath)); break; case DMASTBinaryOr binaryOr: result = new BinaryOr(binaryOr.Location, - BuildExpression(binaryOr.LHS, dmObject, proc, inferredPath), - BuildExpression(binaryOr.RHS, dmObject, proc, inferredPath)); + BuildExpression(binaryOr.LHS, inferredPath), + BuildExpression(binaryOr.RHS, inferredPath)); break; case DMASTEquivalent equivalent: result = new Equivalent(equivalent.Location, - BuildExpression(equivalent.LHS, dmObject, proc, inferredPath), - BuildExpression(equivalent.RHS, dmObject, proc, inferredPath)); + BuildExpression(equivalent.LHS, inferredPath), + BuildExpression(equivalent.RHS, inferredPath)); break; case DMASTNotEquivalent notEquivalent: result = new NotEquivalent(notEquivalent.Location, - BuildExpression(notEquivalent.LHS, dmObject, proc, inferredPath), - BuildExpression(notEquivalent.RHS, dmObject, proc, inferredPath)); + BuildExpression(notEquivalent.LHS, inferredPath), + BuildExpression(notEquivalent.RHS, inferredPath)); break; case DMASTGreaterThan greaterThan: result = new GreaterThan(greaterThan.Location, - BuildExpression(greaterThan.LHS, dmObject, proc, inferredPath), - BuildExpression(greaterThan.RHS, dmObject, proc, inferredPath)); + BuildExpression(greaterThan.LHS, inferredPath), + BuildExpression(greaterThan.RHS, inferredPath)); break; case DMASTGreaterThanOrEqual greaterThanOrEqual: result = new GreaterThanOrEqual(greaterThanOrEqual.Location, - BuildExpression(greaterThanOrEqual.LHS, dmObject, proc, inferredPath), - BuildExpression(greaterThanOrEqual.RHS, dmObject, proc, inferredPath)); + BuildExpression(greaterThanOrEqual.LHS, inferredPath), + BuildExpression(greaterThanOrEqual.RHS, inferredPath)); break; case DMASTLessThan lessThan: result = new LessThan(lessThan.Location, - BuildExpression(lessThan.LHS, dmObject, proc, inferredPath), - BuildExpression(lessThan.RHS, dmObject, proc, inferredPath)); + BuildExpression(lessThan.LHS, inferredPath), + BuildExpression(lessThan.RHS, inferredPath)); break; case DMASTLessThanOrEqual lessThanOrEqual: result = new LessThanOrEqual(lessThanOrEqual.Location, - BuildExpression(lessThanOrEqual.LHS, dmObject, proc, inferredPath), - BuildExpression(lessThanOrEqual.RHS, dmObject, proc, inferredPath)); + BuildExpression(lessThanOrEqual.LHS, inferredPath), + BuildExpression(lessThanOrEqual.RHS, inferredPath)); break; case DMASTOr or: result = new Or(or.Location, - BuildExpression(or.LHS, dmObject, proc, inferredPath), - BuildExpression(or.RHS, dmObject, proc, inferredPath)); + BuildExpression(or.LHS, inferredPath), + BuildExpression(or.RHS, inferredPath)); break; case DMASTAnd and: result = new And(and.Location, - BuildExpression(and.LHS, dmObject, proc, inferredPath), - BuildExpression(and.RHS, dmObject, proc, inferredPath)); + BuildExpression(and.LHS, inferredPath), + BuildExpression(and.RHS, inferredPath)); break; case DMASTTernary ternary: - result = new Ternary(ternary.Location, - BuildExpression(ternary.A, dmObject, proc, inferredPath), - BuildExpression(ternary.B, dmObject, proc, inferredPath), - BuildExpression(ternary.C ?? new DMASTConstantNull(ternary.Location), dmObject, proc, inferredPath)); + var a = BuildExpression(ternary.A, inferredPath); + var b = BuildExpression(ternary.B, inferredPath); + var c = BuildExpression(ternary.C ?? new DMASTConstantNull(ternary.Location), inferredPath); + + if (b.ValType.TypePath != null && c.ValType.TypePath != null && b.ValType.TypePath != c.ValType.TypePath) { + Compiler.Emit(WarningCode.LostTypeInfo, ternary.Location, + $"Ternary has type paths {b.ValType.TypePath} and {c.ValType.TypePath} but a value can only have one type path. Using {b.ValType.TypePath}."); + } + + result = new Ternary(ternary.Location, a, b, c); break; case DMASTNewPath newPath: - if (BuildExpression(newPath.Path, dmObject, proc, inferredPath) is not IConstantPath path) { + if (BuildExpression(newPath.Path, inferredPath) is not IConstantPath path) { result = BadExpression(WarningCode.BadExpression, newPath.Path.Location, "Expected a path expression"); break; } - result = new NewPath(newPath.Location, path, - BuildArgumentList(newPath.Location, dmObject, proc, newPath.Parameters, inferredPath)); + result = new NewPath(Compiler, newPath.Location, path, + BuildArgumentList(newPath.Location, newPath.Parameters, inferredPath)); break; case DMASTNewExpr newExpr: - result = new New(newExpr.Location, - BuildExpression(newExpr.Expression, dmObject, proc, inferredPath), - BuildArgumentList(newExpr.Location, dmObject, proc, newExpr.Parameters, inferredPath)); + result = new New(Compiler, newExpr.Location, + BuildExpression(newExpr.Expression, inferredPath), + BuildArgumentList(newExpr.Location, newExpr.Parameters, inferredPath)); break; case DMASTNewInferred newInferred: if (inferredPath is null) { @@ -263,53 +297,53 @@ public static DMExpression BuildExpression(DMASTExpression expression, DMObject break; } - var type = BuildPath(newInferred.Location, dmObject, inferredPath.Value); + var type = BuildPath(newInferred.Location, inferredPath.Value); if (type is not IConstantPath inferredType) { result = BadExpression(WarningCode.BadExpression, newInferred.Location, $"Cannot instantiate {type}"); break; } - result = new NewPath(newInferred.Location, inferredType, - BuildArgumentList(newInferred.Location, dmObject, proc, newInferred.Parameters, inferredPath)); + result = new NewPath(Compiler, newInferred.Location, inferredType, + BuildArgumentList(newInferred.Location, newInferred.Parameters, inferredPath)); break; case DMASTPreIncrement preIncrement: - result = new PreIncrement(preIncrement.Location, BuildExpression(preIncrement.Value, dmObject, proc, inferredPath)); + result = new PreIncrement(preIncrement.Location, BuildExpression(preIncrement.Value, inferredPath)); break; case DMASTPostIncrement postIncrement: - result = new PostIncrement(postIncrement.Location, BuildExpression(postIncrement.Value, dmObject, proc, inferredPath)); + result = new PostIncrement(postIncrement.Location, BuildExpression(postIncrement.Value, inferredPath)); break; case DMASTPreDecrement preDecrement: - result = new PreDecrement(preDecrement.Location, BuildExpression(preDecrement.Value, dmObject, proc, inferredPath)); + result = new PreDecrement(preDecrement.Location, BuildExpression(preDecrement.Value, inferredPath)); break; case DMASTPostDecrement postDecrement: - result = new PostDecrement(postDecrement.Location, BuildExpression(postDecrement.Value, dmObject, proc, inferredPath)); + result = new PostDecrement(postDecrement.Location, BuildExpression(postDecrement.Value, inferredPath)); break; case DMASTPointerRef pointerRef: - result = new PointerRef(pointerRef.Location, BuildExpression(pointerRef.Value, dmObject, proc, inferredPath)); + result = new PointerRef(pointerRef.Location, BuildExpression(pointerRef.Value, inferredPath)); break; case DMASTPointerDeref pointerDeref: - result = new PointerDeref(pointerDeref.Location, BuildExpression(pointerDeref.Value, dmObject, proc, inferredPath)); + result = new PointerDeref(pointerDeref.Location, BuildExpression(pointerDeref.Value, inferredPath)); break; case DMASTGradient gradient: result = new Gradient(gradient.Location, - BuildArgumentList(gradient.Location, dmObject, proc, gradient.Parameters)); + BuildArgumentList(gradient.Location, gradient.Parameters)); break; case DMASTRgb rgb: - result = new Rgb(rgb.Location, BuildArgumentList(rgb.Location, dmObject, proc, rgb.Parameters)); + result = new Rgb(rgb.Location, BuildArgumentList(rgb.Location, rgb.Parameters)); break; case DMASTLocateCoordinates locateCoordinates: result = new LocateCoordinates(locateCoordinates.Location, - BuildExpression(locateCoordinates.X, dmObject, proc, inferredPath), - BuildExpression(locateCoordinates.Y, dmObject, proc, inferredPath), - BuildExpression(locateCoordinates.Z, dmObject, proc, inferredPath)); + BuildExpression(locateCoordinates.X, inferredPath), + BuildExpression(locateCoordinates.Y, inferredPath), + BuildExpression(locateCoordinates.Z, inferredPath)); break; case DMASTIsSaved isSaved: - result = new IsSaved(isSaved.Location, BuildExpression(isSaved.Value, dmObject, proc, inferredPath)); + 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, dmObject, proc, inferredPath); + var expr = BuildExpression(isType.LHS, inferredPath); if (expr.Path is null) { result = BadExpression(WarningCode.BadExpression, isType.Location, "A type could not be inferred!"); @@ -321,43 +355,43 @@ public static DMExpression BuildExpression(DMASTExpression expression, DMObject } result = new IsType(isType.Location, - BuildExpression(isType.LHS, dmObject, proc, inferredPath), - BuildExpression(isType.RHS, dmObject, proc, inferredPath)); + BuildExpression(isType.LHS, inferredPath), + BuildExpression(isType.RHS, inferredPath)); break; } case DMASTIsNull isNull: - result = new IsNull(isNull.Location, BuildExpression(isNull.Value, dmObject, proc, inferredPath)); + result = new IsNull(isNull.Location, BuildExpression(isNull.Value, inferredPath)); break; case DMASTLength length: - result = new Length(length.Location, BuildExpression(length.Value, dmObject, proc, inferredPath)); + result = new Length(length.Location, BuildExpression(length.Value, inferredPath)); break; case DMASTGetStep getStep: result = new GetStep(getStep.Location, - BuildExpression(getStep.LHS, dmObject, proc, inferredPath), - BuildExpression(getStep.RHS, dmObject, proc, inferredPath)); + BuildExpression(getStep.LHS, inferredPath), + BuildExpression(getStep.RHS, inferredPath)); break; case DMASTGetDir getDir: result = new GetDir(getDir.Location, - BuildExpression(getDir.LHS, dmObject, proc, inferredPath), - BuildExpression(getDir.RHS, dmObject, proc, inferredPath)); + BuildExpression(getDir.LHS, inferredPath), + BuildExpression(getDir.RHS, inferredPath)); break; case DMASTProb prob: result = new Prob(prob.Location, - BuildExpression(prob.Value, dmObject, proc, inferredPath)); + BuildExpression(prob.Value, inferredPath)); break; case DMASTInitial initial: - result = new Initial(initial.Location, BuildExpression(initial.Value, dmObject, proc, inferredPath)); + result = new Initial(initial.Location, BuildExpression(initial.Value, inferredPath)); break; case DMASTNameof nameof: - result = BuildNameof(nameof, dmObject, proc, inferredPath); + result = BuildNameof(nameof, inferredPath); break; case DMASTExpressionIn expressionIn: - var exprInLHS = BuildExpression(expressionIn.LHS, dmObject, proc, inferredPath); - var exprInRHS = BuildExpression(expressionIn.RHS, dmObject, proc, inferredPath); + var exprInLHS = BuildExpression(expressionIn.LHS, inferredPath); + var exprInRHS = BuildExpression(expressionIn.RHS, inferredPath); if ((expressionIn.LHS is not DMASTExpressionWrapped && exprInLHS is UnaryOp or BinaryOp or Ternary) || (expressionIn.RHS is not DMASTExpressionWrapped && exprInRHS is BinaryOp or Ternary)) { - DMCompiler.Emit(WarningCode.AmbiguousInOrder, expressionIn.Location, + Compiler.Emit(WarningCode.AmbiguousInOrder, expressionIn.Location, "Order of operations for \"in\" may not be what is expected. Use parentheses to be more explicit."); } @@ -365,43 +399,43 @@ public static DMExpression BuildExpression(DMASTExpression expression, DMObject break; case DMASTExpressionInRange expressionInRange: result = new InRange(expressionInRange.Location, - BuildExpression(expressionInRange.Value, dmObject, proc, inferredPath), - BuildExpression(expressionInRange.StartRange, dmObject, proc, inferredPath), - BuildExpression(expressionInRange.EndRange, dmObject, proc, inferredPath)); + BuildExpression(expressionInRange.Value, inferredPath), + BuildExpression(expressionInRange.StartRange, inferredPath), + BuildExpression(expressionInRange.EndRange, inferredPath)); break; case DMASTSin sin: - result = new Sin(sin.Location, BuildExpression(sin.Value, dmObject, proc, inferredPath)); + result = new Sin(sin.Location, BuildExpression(sin.Value, inferredPath)); break; case DMASTCos cos: - result = new Cos(cos.Location, BuildExpression(cos.Value, dmObject, proc, inferredPath)); + result = new Cos(cos.Location, BuildExpression(cos.Value, inferredPath)); break; case DMASTTan tan: - result = new Tan(tan.Location, BuildExpression(tan.Value, dmObject, proc, inferredPath)); + result = new Tan(tan.Location, BuildExpression(tan.Value, inferredPath)); break; case DMASTArcsin arcSin: - result = new ArcSin(arcSin.Location, BuildExpression(arcSin.Value, dmObject, proc, inferredPath)); + result = new ArcSin(arcSin.Location, BuildExpression(arcSin.Value, inferredPath)); break; case DMASTArccos arcCos: - result = new ArcCos(arcCos.Location, BuildExpression(arcCos.Value, dmObject, proc, inferredPath)); + result = new ArcCos(arcCos.Location, BuildExpression(arcCos.Value, inferredPath)); break; case DMASTArctan arcTan: - result = new ArcTan(arcTan.Location, BuildExpression(arcTan.Value, dmObject, proc, inferredPath)); + result = new ArcTan(arcTan.Location, BuildExpression(arcTan.Value, inferredPath)); break; case DMASTArctan2 arcTan2: result = new ArcTan2(arcTan2.Location, - BuildExpression(arcTan2.LHS, dmObject, proc, inferredPath), - BuildExpression(arcTan2.RHS, dmObject, proc, inferredPath)); + BuildExpression(arcTan2.LHS, inferredPath), + BuildExpression(arcTan2.RHS, inferredPath)); break; case DMASTSqrt sqrt: - result = new Sqrt(sqrt.Location, BuildExpression(sqrt.Value, dmObject, proc, inferredPath)); + result = new Sqrt(sqrt.Location, BuildExpression(sqrt.Value, inferredPath)); break; case DMASTAbs abs: - result = new Abs(abs.Location, BuildExpression(abs.Value, dmObject, proc, inferredPath)); + result = new Abs(abs.Location, BuildExpression(abs.Value, inferredPath)); break; case DMASTVarDeclExpression varDeclExpr: var declIdentifier = new DMASTIdentifier(expression.Location, varDeclExpr.DeclPath.Path.LastElement); - result = BuildIdentifier(declIdentifier, dmObject, proc); + result = BuildIdentifier(declIdentifier); break; case DMASTVoid: result = BadExpression(WarningCode.BadExpression, expression.Location, "Attempt to use a void expression"); @@ -410,23 +444,23 @@ public static DMExpression BuildExpression(DMASTExpression expression, DMObject throw new ArgumentException($"Invalid expression {expression}", nameof(expression)); } - if (EncounteredUnknownReference != null) { - return EncounteredUnknownReference; + if (_encounteredUnknownReference != null) { + return _encounteredUnknownReference; } else { return result; } } - private static DMExpression BuildConstant(DMASTExpressionConstant constant, DMObject dmObject, DMProc proc) { + private DMExpression BuildConstant(DMASTExpressionConstant constant) { switch (constant) { case DMASTConstantNull: return new Null(constant.Location); case DMASTConstantInteger constInt: return new Number(constant.Location, constInt.Value); case DMASTConstantFloat constFloat: return new Number(constant.Location, constFloat.Value); case DMASTConstantString constString: return new String(constant.Location, constString.Value); - case DMASTConstantResource constResource: return new Resource(constant.Location, constResource.Path); - case DMASTConstantPath constPath: return BuildPath(constant.Location, dmObject, constPath.Value.Path); + case DMASTConstantResource constResource: return new Resource(Compiler, constant.Location, constResource.Path); + case DMASTConstantPath constPath: return BuildPath(constant.Location, constPath.Value.Path); case DMASTUpwardPathSearch upwardSearch: - BuildExpression(upwardSearch.Path, dmObject, proc).TryAsConstant(out var pathExpr); + BuildExpression(upwardSearch.Path).TryAsConstant(Compiler, out var pathExpr); if (pathExpr is not IConstantPath expr) return BadExpression(WarningCode.BadExpression, constant.Location, $"Cannot do an upward path search on {pathExpr}"); @@ -436,18 +470,18 @@ private static DMExpression BuildConstant(DMASTExpressionConstant constant, DMOb return UnknownReference(constant.Location, $"Cannot search on {expr}"); - DreamPath? foundPath = DMObjectTree.UpwardSearch(path.Value, upwardSearch.Search.Path); + DreamPath? foundPath = ObjectTree.UpwardSearch(path.Value, upwardSearch.Search.Path); if (foundPath == null) return UnknownReference(constant.Location, $"Could not find path {path}.{upwardSearch.Search.Path}"); - return BuildPath(constant.Location, dmObject, foundPath.Value); + return BuildPath(constant.Location, foundPath.Value); } throw new ArgumentException($"Invalid constant {constant}", nameof(constant)); } - private static StringFormat BuildStringFormat(DMASTStringFormat stringFormat, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private StringFormat BuildStringFormat(DMASTStringFormat stringFormat, DreamPath? inferredPath) { var expressions = new DMExpression[stringFormat.InterpolatedValues.Length]; for (int i = 0; i < stringFormat.InterpolatedValues.Length; i++) { @@ -456,17 +490,17 @@ private static StringFormat BuildStringFormat(DMASTStringFormat stringFormat, DM if (interpolatedValue == null) { expressions[i] = new Null(stringFormat.Location); } else { - expressions[i] = BuildExpression(interpolatedValue, dmObject, proc, inferredPath); + expressions[i] = BuildExpression(interpolatedValue, inferredPath); } } return new StringFormat(stringFormat.Location, stringFormat.Value, expressions); } - private static DMExpression BuildPath(Location location, DMObject dmObject, DreamPath path) { + private DMExpression BuildPath(Location location, DreamPath path) { // An upward search with no left-hand side if (path.Type == DreamPath.PathType.UpwardSearch) { - DreamPath? foundPath = DMCodeTree.UpwardSearch(dmObject, path); + DreamPath? foundPath = Compiler.DMCodeTree.UpwardSearch(ctx.Type, path); if (foundPath == null) return UnknownReference(location, $"Could not find path {path}"); @@ -476,14 +510,14 @@ private static DMExpression BuildPath(Location location, DMObject dmObject, Drea // /datum/proc or /datum/verb if (path.LastElement is "proc" or "verb") { DreamPath typePath = path.FromElements(0, -2); - if (!DMObjectTree.TryGetDMObject(typePath, out var stubOfType)) + if (!ObjectTree.TryGetDMObject(typePath, out var stubOfType)) return UnknownReference(location, $"Type {typePath} does not exist"); return new ConstantProcStub(location, stubOfType, path.LastElement is "verb"); } // /datum - if (DMObjectTree.TryGetDMObject(path, out var referencing)) { + if (ObjectTree.TryGetDMObject(path, out var referencing)) { return new ConstantTypeReference(location, referencing); } @@ -495,11 +529,11 @@ private static DMExpression BuildPath(Location location, DMObject dmObject, Drea DreamPath ownerPath = withoutProcElement.FromElements(0, -2); string procName = path.LastElement!; - if (!DMObjectTree.TryGetDMObject(ownerPath, out var owner)) + if (!ObjectTree.TryGetDMObject(ownerPath, out var owner)) return UnknownReference(location, $"Type {ownerPath} does not exist"); int? procId; - if (owner == DMObjectTree.Root && DMObjectTree.TryGetGlobalProc(procName, out var globalProc)) { + if (owner == ObjectTree.Root && ObjectTree.TryGetGlobalProc(procName, out var globalProc)) { procId = globalProc.Id; } else { var procs = owner.GetProcs(procName); @@ -507,64 +541,64 @@ private static DMExpression BuildPath(Location location, DMObject dmObject, Drea procId = procs?[^1]; } - if (procId == null || DMObjectTree.AllProcs.Count < procId) { + if (procId == null || ObjectTree.AllProcs.Count < procId) { return UnknownReference(location, $"Could not find proc {procName}() on {ownerPath}"); } - return new ConstantProcReference(location, path, DMObjectTree.AllProcs[procId.Value]); + return new ConstantProcReference(location, path, ObjectTree.AllProcs[procId.Value]); } return UnknownReference(location, $"Path {path} does not exist"); } - private static DMExpression BuildIdentifier(DMASTIdentifier identifier, DMObject dmObject, DMProc proc, DreamPath? inferredPath = null) { + private DMExpression BuildIdentifier(DMASTIdentifier identifier, DreamPath? inferredPath = null) { var name = identifier.Identifier; switch (name) { case "src": - return new Src(identifier.Location, dmObject.Path); + return new Src(identifier.Location, ctx.Type.Path); case "usr": return new Usr(identifier.Location); case "args": return new Args(identifier.Location); case "world": - if (CurrentScopeMode == ScopeMode.FirstPassStatic) // world is not available on the first pass + if (scopeMode == FirstPassStatic) // world is not available on the first pass return UnknownIdentifier(identifier.Location, "world"); return new World(identifier.Location); case "__TYPE__": - return new ProcOwnerType(identifier.Location, dmObject); + return new ProcOwnerType(identifier.Location, ctx.Type); case "__IMPLIED_TYPE__": if (inferredPath == null) return BadExpression(WarningCode.BadExpression, identifier.Location, "__IMPLIED_TYPE__ cannot be used here, there is no type being implied"); - return BuildPath(identifier.Location, dmObject, inferredPath.Value); + return BuildPath(identifier.Location, inferredPath.Value); case "__PROC__": // The saner alternative to "....." - var path = dmObject.Path.AddToPath("proc/" + proc.Name); + var path = ctx.Type.Path.AddToPath("proc/" + ctx.Proc.Name); - return new ConstantProcReference(identifier.Location, path, proc); + return new ConstantProcReference(identifier.Location, path, ctx.Proc); case "global": return new Global(identifier.Location); default: { - if (CurrentScopeMode == ScopeMode.Normal) { - var localVar = proc?.GetLocalVariable(name); + if (scopeMode == Normal) { + var localVar = ctx.Proc?.GetLocalVariable(name); if (localVar != null) return new Local(identifier.Location, localVar); } - var field = dmObject.GetVariable(name); - if (field != null && CurrentScopeMode == ScopeMode.Normal) { + var field = ctx.Type.GetVariable(name); + if (field != null && scopeMode == Normal) { return new Field(identifier.Location, field, field.ValType); } - var globalId = proc?.GetGlobalVariableId(name) ?? dmObject.GetGlobalVariableId(name); + var globalId = ctx.Proc?.GetGlobalVariableId(name) ?? ctx.Type.GetGlobalVariableId(name); if (globalId != null) { if (field is not null) - DMCompiler.Emit(WarningCode.AmbiguousVarStatic, identifier.Location, $"Static var definition cannot reference instance variable \"{name}\" but a global exists"); + Compiler.Emit(WarningCode.AmbiguousVarStatic, identifier.Location, $"Static var definition cannot reference instance variable \"{name}\" but a global exists"); - var globalVar = DMObjectTree.Globals[globalId.Value]; + var globalVar = ObjectTree.Globals[globalId.Value]; var global = new GlobalField(identifier.Location, globalVar.Type, globalId.Value, globalVar.ValType); return global; } @@ -574,19 +608,16 @@ private static DMExpression BuildIdentifier(DMASTIdentifier identifier, DMObject } } - private static DMExpression BuildScopeIdentifier( - DMASTScopeIdentifier scopeIdentifier, - DMObject dmObject, DMProc proc, - DreamPath? inferredPath) { + private DMExpression BuildScopeIdentifier(DMASTScopeIdentifier scopeIdentifier, DreamPath? inferredPath) { var location = scopeIdentifier.Location; var bIdentifier = scopeIdentifier.Identifier; if (scopeIdentifier.Expression == null) { // ::A, shorthand for global.A if (scopeIdentifier.IsProcRef) { // ::A(), global proc ref - if (!DMObjectTree.TryGetGlobalProc(bIdentifier, out var globalProc)) + if (!ObjectTree.TryGetGlobalProc(bIdentifier, out var globalProc)) return UnknownReference(location, $"No global proc named \"{bIdentifier}\" exists"); - var arguments = BuildArgumentList(location, dmObject, proc, scopeIdentifier.CallArguments, inferredPath); + var arguments = BuildArgumentList(location, scopeIdentifier.CallArguments, inferredPath); return new ProcCall(location, new GlobalProc(location, globalProc), arguments, DMValueType.Anything); } @@ -595,13 +626,13 @@ private static DMExpression BuildScopeIdentifier( return new GlobalVars(location); // ::A, global var ref - var globalId = DMObjectTree.Root.GetGlobalVariableId(bIdentifier); + var globalId = ObjectTree.Root.GetGlobalVariableId(bIdentifier); if (globalId == null) return UnknownIdentifier(location, bIdentifier); - var globalVar = DMObjectTree.Globals[globalId.Value]; + var globalVar = ObjectTree.Globals [globalId.Value]; return new GlobalField(location, - DMObjectTree.Globals[globalId.Value].Type, + ObjectTree.Globals[globalId.Value].Type, globalId.Value, globalVar.ValType); } @@ -616,29 +647,29 @@ private static DMExpression BuildScopeIdentifier( if (scopeIdentifier.Expression is DMASTIdentifier { Identifier: "type" or "parent_type" } identifier) { // This is the same behaviour as in BYOND, but BYOND simply raises an undefined var error. // We want to give end users an explanation at least. - if (CurrentScopeMode is ScopeMode.Normal && proc != null) + if (scopeMode is Normal && ctx.Proc != null) return BadExpression(WarningCode.BadExpression, identifier.Location, - "Use of \"type::\" and \"parent_type::\" outside of a static context is forbidden"); + "Use of \"type::\" and \"parent_type::\" outside of a context is forbidden"); if (identifier.Identifier == "parent_type") { - if (dmObject.Parent == null) + if (ctx.Type.Parent == null) return BadExpression(WarningCode.ItemDoesntExist, identifier.Location, - $"Type {dmObject.Path} does not have a parent"); + $"Type {ctx.Type.Path} does not have a parent"); - expression = BuildPath(location, dmObject, dmObject.Parent.Path); + expression = BuildPath(location, ctx.Type.Parent.Path); } else { // "type" - expression = BuildPath(location, dmObject, dmObject.Path); + expression = BuildPath(location, ctx.Type.Path); } } else { - expression = BuildExpression(scopeIdentifier.Expression, dmObject, proc, inferredPath); + expression = BuildExpression(scopeIdentifier.Expression, inferredPath); } // A needs to have a type if (expression.Path == null) return BadExpression(WarningCode.BadExpression, expression.Location, - $"Identifier \"{expression.GetNameof(dmObject)}\" does not have a type"); + $"Identifier \"{expression.GetNameof(ctx)}\" does not have a type"); - if (!DMObjectTree.TryGetDMObject(expression.Path.Value, out var owner)) { + if (!ObjectTree.TryGetDMObject(expression.Path.Value, out var owner)) { if (expression is ConstantProcReference procReference) { if (bIdentifier == "name") return new String(expression.Location, procReference.Value.Name); @@ -657,7 +688,7 @@ private static DMExpression BuildScopeIdentifier( return BadExpression(WarningCode.ItemDoesntExist, location, $"Type {owner.Path} does not have a proc named \"{bIdentifier}\""); - var referencedProc = DMObjectTree.AllProcs[procs[^1]]; + var referencedProc = ObjectTree.AllProcs[procs[^1]]; var path = owner.Path.AddToPath("proc/" + referencedProc.Name); return new ConstantProcReference(location, path, referencedProc); } else { // A::B @@ -665,7 +696,7 @@ private static DMExpression BuildScopeIdentifier( if (globalVarId != null) { // B is a static var. // This is the only case a ScopeIdentifier can be an LValue. - var globalVar = DMObjectTree.Globals[globalVarId.Value]; + var globalVar = ObjectTree.Globals [globalVarId.Value]; return new GlobalField(location, globalVar.Type, globalVarId.Value, globalVar.ValType); } @@ -673,13 +704,13 @@ private static DMExpression BuildScopeIdentifier( if (variable == null) return UnknownIdentifier(location, bIdentifier); - return new ScopeReference(location, expression, bIdentifier, variable); + return new ScopeReference(ObjectTree, location, expression, bIdentifier, variable); } } - private static DMExpression BuildCallableProcIdentifier(DMASTCallableProcIdentifier procIdentifier, DMObject dmObject) { - if (CurrentScopeMode is ScopeMode.Static or ScopeMode.FirstPassStatic) { - if (!DMObjectTree.TryGetGlobalProc(procIdentifier.Identifier, out var staticScopeGlobalProc)) + private DMExpression BuildCallableProcIdentifier(DMASTCallableProcIdentifier procIdentifier, DMObject dmObject) { + if (scopeMode is Static or FirstPassStatic) { + if (!ObjectTree.TryGetGlobalProc(procIdentifier.Identifier, out var staticScopeGlobalProc)) return UnknownReference(procIdentifier.Location, $"Type {dmObject.Path} does not have a proc named \"{procIdentifier.Identifier}\""); @@ -690,7 +721,7 @@ private static DMExpression BuildCallableProcIdentifier(DMASTCallableProcIdentif return new Proc(procIdentifier.Location, procIdentifier.Identifier); } - if (DMObjectTree.TryGetGlobalProc(procIdentifier.Identifier, out var globalProc)) { + if (ObjectTree.TryGetGlobalProc(procIdentifier.Identifier, out var globalProc)) { return new GlobalProc(procIdentifier.Location, globalProc); } @@ -698,31 +729,31 @@ private static DMExpression BuildCallableProcIdentifier(DMASTCallableProcIdentif $"Type {dmObject.Path} does not have a proc named \"{procIdentifier.Identifier}\""); } - private static DMExpression BuildProcCall(DMASTProcCall procCall, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private DMExpression BuildProcCall(DMASTProcCall procCall, DreamPath? inferredPath) { // arglist hack if (procCall.Callable is DMASTCallableProcIdentifier { Identifier: "arglist" }) { switch (procCall.Parameters.Length) { case 0: - DMCompiler.Emit(WarningCode.BadArgument, procCall.Location, "arglist() requires 1 argument"); + Compiler.Emit(WarningCode.BadArgument, procCall.Location, "arglist() requires 1 argument"); break; case 1: break; default: - DMCompiler.Emit( + Compiler.Emit( WarningCode.InvalidArgumentCount, procCall.Location, $"arglist() given {procCall.Parameters.Length} arguments, expecting 1"); break; } - var expr = BuildExpression(procCall.Parameters[0].Value, dmObject, proc, inferredPath); + var expr = BuildExpression(procCall.Parameters[0].Value, inferredPath); return new Arglist(procCall.Location, expr); } - var target = BuildExpression((DMASTExpression)procCall.Callable, dmObject, proc, inferredPath); - var args = BuildArgumentList(procCall.Location, dmObject, proc, procCall.Parameters); + var target = BuildExpression((DMASTExpression)procCall.Callable, inferredPath); + var args = BuildArgumentList(procCall.Location, procCall.Parameters); if (target is Proc targetProc) { // GlobalProc handles returnType itself - var returnType = targetProc.GetReturnType(dmObject); + var returnType = targetProc.GetReturnType(ctx.Type); return new ProcCall(procCall.Location, target, args, returnType); } @@ -730,7 +761,7 @@ private static DMExpression BuildProcCall(DMASTProcCall procCall, DMObject dmObj return new ProcCall(procCall.Location, target, args, DMValueType.Anything); } - private static ArgumentList BuildArgumentList(Location location, DMObject dmObject, DMProc proc, DMASTCallParameter[]? arguments, DreamPath? inferredPath = null) { + private ArgumentList BuildArgumentList(Location location, DMASTCallParameter[]? arguments, DreamPath? inferredPath = null) { if (arguments == null || arguments.Length == 0) return new ArgumentList(location, [], false); @@ -739,8 +770,8 @@ private static ArgumentList BuildArgumentList(Location location, DMObject dmObje int idx = 0; foreach(var arg in arguments) { - var value = BuildExpression(arg.Value, dmObject, proc, inferredPath); - var key = (arg.Key != null) ? BuildExpression(arg.Key, dmObject, proc, inferredPath) : null; + var value = BuildExpression(arg.Value, inferredPath); + var key = (arg.Key != null) ? BuildExpression(arg.Key, inferredPath) : null; int argIndex = idx++; string? name = null; @@ -753,7 +784,7 @@ private static ArgumentList BuildArgumentList(Location location, DMObject dmObje var newIdx = (int)keyNum.Value - 1; if (newIdx == argIndex) { - DMCompiler.Emit(WarningCode.PointlessPositionalArgument, key.Location, + Compiler.Emit(WarningCode.PointlessPositionalArgument, key.Location, $"The argument at index {argIndex + 1} is a positional argument with a redundant index (\"{argIndex + 1} = value\" at argument {argIndex + 1}). This does not function like a named argument and is likely a mistake."); } @@ -767,7 +798,7 @@ private static ArgumentList BuildArgumentList(Location location, DMObject dmObje default: if (key != null && key is not Expressions.UnknownReference) { - DMCompiler.Emit(WarningCode.InvalidArgumentKey, key.Location, $"Invalid argument key {key}"); + Compiler.Emit(WarningCode.InvalidArgumentKey, key.Location, $"Invalid argument key {key}"); } break; @@ -782,29 +813,29 @@ private static ArgumentList BuildArgumentList(Location location, DMObject dmObje return new ArgumentList(location, expressions, isKeyed); } - private static DMExpression BuildAssign(DMASTAssign assign, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var lhs = BuildExpression(assign.LHS, dmObject, proc, inferredPath); - var rhs = BuildExpression(assign.RHS, dmObject, proc, lhs.NestedPath); - if(lhs.TryAsConstant(out _)) { - DMCompiler.Emit(WarningCode.WriteToConstant, assign.LHS.Location, "Cannot write to const var"); + private DMExpression BuildAssign(DMASTAssign assign, DreamPath? inferredPath) { + var lhs = BuildExpression(assign.LHS, inferredPath); + var rhs = BuildExpression(assign.RHS, lhs.NestedPath); + if(lhs.TryAsConstant(Compiler, out _)) { + Compiler.Emit(WarningCode.WriteToConstant, assign.LHS.Location, "Cannot write to const var"); } return new Assignment(assign.Location, lhs, rhs); } - private static DMExpression BuildAssignInto(DMASTAssignInto assign, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var lhs = BuildExpression(assign.LHS, dmObject, proc, inferredPath); - var rhs = BuildExpression(assign.RHS, dmObject, proc, lhs.NestedPath); - if(lhs.TryAsConstant(out _)) { - DMCompiler.Emit(WarningCode.WriteToConstant, assign.LHS.Location, "Cannot write to const var"); + private DMExpression BuildAssignInto(DMASTAssignInto assign, DreamPath? inferredPath) { + var lhs = BuildExpression(assign.LHS, inferredPath); + var rhs = BuildExpression(assign.RHS, lhs.NestedPath); + if(lhs.TryAsConstant(Compiler, out _)) { + Compiler.Emit(WarningCode.WriteToConstant, assign.LHS.Location, "Cannot write to const var"); } return new AssignmentInto(assign.Location, lhs, rhs); } - private static DMExpression BuildEqual(DMASTEqual equal, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var lhs = BuildExpression(equal.LHS, dmObject, proc, inferredPath); - var rhs = BuildExpression(equal.RHS, dmObject, proc, inferredPath); + private DMExpression BuildEqual(DMASTEqual equal, DreamPath? inferredPath) { + var lhs = BuildExpression(equal.LHS, inferredPath); + var rhs = BuildExpression(equal.RHS, inferredPath); // (x == null) can be changed to isnull(x) which compiles down to an opcode // TODO: Bytecode optimizations instead @@ -814,9 +845,9 @@ private static DMExpression BuildEqual(DMASTEqual equal, DMObject dmObject, DMPr return new Equal(equal.Location, lhs, rhs); } - private static DMExpression BuildNotEqual(DMASTNotEqual notEqual, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var lhs = BuildExpression(notEqual.LHS, dmObject, proc, inferredPath); - var rhs = BuildExpression(notEqual.RHS, dmObject, proc, inferredPath); + private DMExpression BuildNotEqual(DMASTNotEqual notEqual, DreamPath? inferredPath) { + var lhs = BuildExpression(notEqual.LHS, inferredPath); + var rhs = BuildExpression(notEqual.RHS, inferredPath); // (x != null) can be changed to !isnull(x) which compiles down to two opcodes // TODO: Bytecode optimizations instead @@ -826,12 +857,12 @@ private static DMExpression BuildNotEqual(DMASTNotEqual notEqual, DMObject dmObj return new NotEqual(notEqual.Location, lhs, rhs); } - private static DMExpression BuildDereference(DMASTDereference deref, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private DMExpression BuildDereference(DMASTDereference deref, DreamPath? inferredPath) { var astOperations = deref.Operations; // The base expression and list of operations to perform on it // These may be redefined if we encounter a global access mid-operation - var expr = BuildExpression(deref.Expression, dmObject, proc, inferredPath); + var expr = BuildExpression(deref.Expression, inferredPath); var operations = new Dereference.Operation[deref.Operations.Length]; int astOperationOffset = 0; @@ -850,12 +881,11 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm switch (namedOperation) { // global.f() case DMASTDereference.CallOperation callOperation: - if (!DMObjectTree.TryGetGlobalProc(callOperation.Identifier, out var globalProc)) + if (!ObjectTree.TryGetGlobalProc(callOperation.Identifier, out var globalProc)) return UnknownReference(callOperation.Location, $"Could not find a global proc named \"{callOperation.Identifier}\""); - var argumentList = BuildArgumentList(deref.Expression.Location, dmObject, proc, - callOperation.Parameters); + var argumentList = BuildArgumentList(deref.Expression.Location, callOperation.Parameters); var globalProcExpr = new GlobalProc(expr.Location, globalProc); expr = new ProcCall(expr.Location, globalProcExpr, argumentList, DMValueType.Anything); @@ -869,11 +899,11 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm } // global.variable - var globalId = dmObject.GetGlobalVariableId(namedOperation.Identifier); + var globalId = ctx.Type.GetGlobalVariableId(namedOperation.Identifier); if (globalId == null) return UnknownIdentifier(deref.Location, $"global.{namedOperation.Identifier}"); - var property = DMObjectTree.Globals[globalId.Value]; + var property = ObjectTree.Globals [globalId.Value]; expr = new GlobalField(expr.Location, property.Type, globalId.Value, property.ValType); prevPath = property.Type; @@ -891,7 +921,7 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm operations = new Dereference.Operation[newOperationCount]; astOperationOffset = 1; } else { - DMCompiler.Emit(WarningCode.BadExpression, firstOperation.Location, + Compiler.Emit(WarningCode.BadExpression, firstOperation.Location, "Invalid dereference operation performed on global"); expr = new Null(firstOperation.Location); } @@ -911,18 +941,18 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm if (!fieldOperation.NoSearch && !pathIsFuzzy) { if (prevPath == null) return UnknownIdentifier(deref.Location, field); - if (!DMObjectTree.TryGetDMObject(prevPath.Value, out var fromObject)) + if (!ObjectTree.TryGetDMObject(prevPath.Value, out var fromObject)) return UnknownReference(fieldOperation.Location, $"Type {prevPath.Value} does not exist"); property = fromObject.GetVariable(field); if (!fieldOperation.Safe && fromObject.IsSubtypeOf(DreamPath.Client)) { - DMCompiler.Emit(WarningCode.UnsafeClientAccess, deref.Location, + Compiler.Emit(WarningCode.UnsafeClientAccess, deref.Location, "Unsafe \"client\" access. Use the \"?.\" operator instead"); } if (property == null && fromObject.GetGlobalVariableId(field) is { } globalId) { - property = DMObjectTree.Globals[globalId]; + property = ObjectTree.Globals [globalId]; expr = new GlobalField(expr.Location, property.Type, globalId, property.ValType); @@ -932,7 +962,7 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm } if (property.ValType.IsUnimplemented) { - DMCompiler.UnimplementedWarning(deref.Location, + Compiler.UnimplementedWarning(deref.Location, $"{prevPath}.{field} is not implemented and will have unexpected behavior"); } @@ -964,7 +994,7 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm operation = new Dereference.IndexOperation { // var/type1/result = new /type2()[new()] changes the inferred new to "new /type1()" // L[new()] = new() uses the type of L however - Index = BuildExpression(indexOperation.Index, dmObject, proc, inferredPath ?? prevPath), + Index = BuildExpression(indexOperation.Index, inferredPath ?? prevPath), Safe = indexOperation.Safe, Path = prevPath }; @@ -974,15 +1004,14 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm case DMASTDereference.CallOperation callOperation: { var field = callOperation.Identifier; - var argumentList = BuildArgumentList(deref.Expression.Location, dmObject, proc, - callOperation.Parameters); + var argumentList = BuildArgumentList(deref.Expression.Location, callOperation.Parameters); if (!callOperation.NoSearch && !pathIsFuzzy) { if (prevPath == null) { return UnknownIdentifier(deref.Location, field); } - if (!DMObjectTree.TryGetDMObject(prevPath.Value, out var fromObject)) + if (!ObjectTree.TryGetDMObject(prevPath.Value, out var fromObject)) return UnknownReference(callOperation.Location, $"Type {prevPath.Value} does not exist"); if (!fromObject.HasProc(field)) return UnknownIdentifier(callOperation.Location, field); @@ -1007,11 +1036,11 @@ private static DMExpression BuildDereference(DMASTDereference deref, DMObject dm } // The final value in prevPath is our expression's path! - return new Dereference(deref.Location, prevPath, expr, operations); + return new Dereference(ObjectTree, deref.Location, prevPath, expr, operations); } - private static DMExpression BuildLocate(DMASTLocate locate, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var container = locate.Container != null ? BuildExpression(locate.Container, dmObject, proc, inferredPath) : null; + private DMExpression BuildLocate(DMASTLocate locate, DreamPath? inferredPath) { + var container = locate.Container != null ? BuildExpression(locate.Container, inferredPath) : null; if (locate.Expression == null) { if (inferredPath == null) @@ -1020,12 +1049,12 @@ private static DMExpression BuildLocate(DMASTLocate locate, DMObject dmObject, D return new LocateInferred(locate.Location, inferredPath.Value, container); } - var pathExpr = BuildExpression(locate.Expression, dmObject, proc, inferredPath); + var pathExpr = BuildExpression(locate.Expression, inferredPath); return new Locate(locate.Location, pathExpr, container); } - private static DMExpression BuildImplicitIsType(DMASTImplicitIsType isType, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var expr = BuildExpression(isType.Value, dmObject, proc, inferredPath); + private DMExpression BuildImplicitIsType(DMASTImplicitIsType isType, DreamPath? inferredPath) { + var expr = BuildExpression(isType.Value, inferredPath); if (expr.Path is null) return BadExpression(WarningCode.BadExpression, isType.Location, "An inferred istype requires a type!"); @@ -1033,7 +1062,7 @@ private static DMExpression BuildImplicitIsType(DMASTImplicitIsType isType, DMOb return new IsTypeInferred(isType.Location, expr, expr.Path.Value); } - private static DMExpression BuildList(DMASTList list, DMObject dmObject, DMProc proc) { + private DMExpression BuildList(DMASTList list) { (DMExpression? Key, DMExpression Value)[] values = []; if (list.Values != null) { @@ -1041,8 +1070,8 @@ private static DMExpression BuildList(DMASTList list, DMObject dmObject, DMProc for (int i = 0; i < list.Values.Length; i++) { DMASTCallParameter value = list.Values[i]; - DMExpression? key = (value.Key != null) ? BuildExpression(value.Key, dmObject, proc) : null; - DMExpression listValue = BuildExpression(value.Value, dmObject, proc); + DMExpression? key = (value.Key != null) ? BuildExpression(value.Key) : null; + DMExpression listValue = BuildExpression(value.Value); values[i] = (key, listValue); } @@ -1051,10 +1080,10 @@ private static DMExpression BuildList(DMASTList list, DMObject dmObject, DMProc return new List(list.Location, values); } - private static DMExpression BuildDimensionalList(DMASTDimensionalList list, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private DMExpression BuildDimensionalList(DMASTDimensionalList list, DreamPath? inferredPath) { var sizes = new DMExpression[list.Sizes.Count]; for (int i = 0; i < sizes.Length; i++) { - var sizeExpr = BuildExpression(list.Sizes[i], dmObject, proc, inferredPath); + var sizeExpr = BuildExpression(list.Sizes[i], inferredPath); sizes[i] = sizeExpr; } @@ -1063,16 +1092,16 @@ private static DMExpression BuildDimensionalList(DMASTDimensionalList list, DMOb } // nameof(x) - private static DMExpression BuildNameof(DMASTNameof nameof, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var expr = BuildExpression(nameof.Value, dmObject, proc, inferredPath); - if (expr.GetNameof(dmObject) is { } name) { + private DMExpression BuildNameof(DMASTNameof nameof, DreamPath? inferredPath) { + var expr = BuildExpression(nameof.Value, inferredPath); + if (expr.GetNameof(ctx) is { } name) { return new String(nameof.Location, name); } return BadExpression(WarningCode.BadArgument, nameof.Location, "nameof() requires a var, proc reference, or type path"); } - private static DMExpression BuildNewList(DMASTNewList newList, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private DMExpression BuildNewList(DMASTNewList newList, DreamPath? inferredPath) { DMExpression[] expressions = new DMExpression[newList.Parameters.Length]; for (int i = 0; i < newList.Parameters.Length; i++) { @@ -1081,13 +1110,13 @@ private static DMExpression BuildNewList(DMASTNewList newList, DMObject dmObject return BadExpression(WarningCode.InvalidArgumentKey, parameter.Location, "newlist() does not take named arguments"); - expressions[i] = BuildExpression(parameter.Value, dmObject, proc, inferredPath); + expressions[i] = BuildExpression(parameter.Value, inferredPath); } return new NewList(newList.Location, expressions); } - private static DMExpression BuildAddText(DMASTAddText addText, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { + private DMExpression BuildAddText(DMASTAddText addText, DreamPath? inferredPath) { if (addText.Parameters.Length < 2) return BadExpression(WarningCode.InvalidArgumentCount, addText.Location, "Invalid addtext() parameter count; expected 2 or more arguments"); @@ -1095,30 +1124,30 @@ private static DMExpression BuildAddText(DMASTAddText addText, DMObject dmObject for (int i = 0; i < expArr.Length; i++) { DMASTCallParameter parameter = addText.Parameters[i]; if(parameter.Key != null) - DMCompiler.Emit(WarningCode.InvalidArgumentKey, parameter.Location, "addtext() does not take named arguments"); + Compiler.Emit(WarningCode.InvalidArgumentKey, parameter.Location, "addtext() does not take named arguments"); - expArr[i] = BuildExpression(parameter.Value, dmObject, proc, inferredPath); + expArr[i] = BuildExpression(parameter.Value, inferredPath); } return new AddText(addText.Location, expArr); } - private static DMExpression BuildInput(DMASTInput input, DMObject dmObject, DMProc proc) { + private DMExpression BuildInput(DMASTInput input) { DMExpression[] arguments = new DMExpression[input.Parameters.Length]; for (int i = 0; i < input.Parameters.Length; i++) { DMASTCallParameter parameter = input.Parameters[i]; if (parameter.Key != null) { - DMCompiler.Emit(WarningCode.InvalidArgumentKey, parameter.Location, + Compiler.Emit(WarningCode.InvalidArgumentKey, parameter.Location, "input() does not take named arguments"); } - arguments[i] = BuildExpression(parameter.Value, dmObject, proc); + arguments[i] = BuildExpression(parameter.Value); } DMExpression? list = null; if (input.List != null) { - list = BuildExpression(input.List, dmObject, proc); + list = BuildExpression(input.List); DMValueType objectTypes = DMValueType.Null |DMValueType.Obj | DMValueType.Mob | DMValueType.Turf | DMValueType.Area; @@ -1126,7 +1155,7 @@ private static DMExpression BuildInput(DMASTInput input, DMObject dmObject, DMPr // Default filter is "as anything" when there's a list input.Types ??= DMValueType.Anything; if (input.Types != DMValueType.Anything && (input.Types & objectTypes) == 0x0) { - DMCompiler.Emit(WarningCode.BadArgument, input.Location, + Compiler.Emit(WarningCode.BadArgument, input.Location, $"Invalid input() filter \"{input.Types}\". Filter must be \"{DMValueType.Anything}\" or at least one of \"{objectTypes}\""); } } else { @@ -1140,13 +1169,13 @@ private static DMExpression BuildInput(DMASTInput input, DMObject dmObject, DMPr return new Input(input.Location, arguments, input.Types.Value, list); } - private static DMExpression BuildPick(DMASTPick pick, DMObject dmObject, DMProc proc) { + private DMExpression BuildPick(DMASTPick pick) { Pick.PickValue[] pickValues = new Pick.PickValue[pick.Values.Length]; for (int i = 0; i < pickValues.Length; i++) { DMASTPick.PickValue pickValue = pick.Values[i]; - DMExpression? weight = (pickValue.Weight != null) ? BuildExpression(pickValue.Weight, dmObject, proc) : null; - DMExpression value = BuildExpression(pickValue.Value, dmObject, proc); + DMExpression? weight = (pickValue.Weight != null) ? BuildExpression(pickValue.Weight) : null; + DMExpression value = BuildExpression(pickValue.Value); if (weight is Prob prob) // pick(prob(50);x, prob(200);y) format weight = prob.P; @@ -1157,35 +1186,35 @@ private static DMExpression BuildPick(DMASTPick pick, DMObject dmObject, DMProc return new Pick(pick.Location, pickValues); } - private static DMExpression BuildLog(DMASTLog log, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var expr = BuildExpression(log.Expression, dmObject, proc, inferredPath); + private DMExpression BuildLog(DMASTLog log, DreamPath? inferredPath) { + var expr = BuildExpression(log.Expression, inferredPath); DMExpression? baseExpr = null; if (log.BaseExpression != null) { - baseExpr = BuildExpression(log.BaseExpression, dmObject, proc, inferredPath); + baseExpr = BuildExpression(log.BaseExpression, inferredPath); } return new Log(log.Location, expr, baseExpr); } - private static DMExpression BuildCall(DMASTCall call, DMObject dmObject, DMProc proc, DreamPath? inferredPath) { - var procArgs = BuildArgumentList(call.Location, dmObject, proc, call.ProcParameters, inferredPath); + private DMExpression BuildCall(DMASTCall call, DreamPath? inferredPath) { + var procArgs = BuildArgumentList(call.Location, call.ProcParameters, inferredPath); switch (call.CallParameters.Length) { default: - DMCompiler.Emit(WarningCode.InvalidArgumentCount, call.Location, "Too many arguments for call()"); + Compiler.Emit(WarningCode.InvalidArgumentCount, call.Location, "Too many arguments for call()"); goto case 2; // Fallthrough! case 2: { - var a = BuildExpression(call.CallParameters[0].Value, dmObject, proc, inferredPath); - var b = BuildExpression(call.CallParameters[1].Value, dmObject, proc, inferredPath); + var a = BuildExpression(call.CallParameters[0].Value, inferredPath); + var b = BuildExpression(call.CallParameters[1].Value, inferredPath); return new CallStatement(call.Location, a, b, procArgs); } case 1: { - var a = BuildExpression(call.CallParameters[0].Value, dmObject, proc, inferredPath); + var a = BuildExpression(call.CallParameters[0].Value, inferredPath); return new CallStatement(call.Location, a, procArgs); } case 0: - DMCompiler.Emit(WarningCode.InvalidArgumentCount, call.Location, "Not enough arguments for call()"); + Compiler.Emit(WarningCode.InvalidArgumentCount, call.Location, "Not enough arguments for call()"); return new CallStatement(call.Location, new Null(Location.Internal), procArgs); } } @@ -1193,9 +1222,9 @@ private static DMExpression BuildCall(DMASTCall call, DMObject dmObject, DMProc /// /// Emits an error and returns a
///
- private static BadExpression BadExpression(WarningCode code, Location location, string errorMessage) { - if (EncounteredUnknownReference == null) - DMCompiler.Emit(code, location, errorMessage); + private BadExpression BadExpression(WarningCode code, Location location, string errorMessage) { + if (_encounteredUnknownReference == null) + Compiler.Emit(code, location, errorMessage); return new BadExpression(location); } @@ -1203,14 +1232,14 @@ private static BadExpression BadExpression(WarningCode code, Location location, /// Creates an UnknownReference expression that should be returned at the end of the expression building.
/// Always use this to return an UnknownReference! /// - private static UnknownReference UnknownReference(Location location, string errorMessage) { - EncounteredUnknownReference = new UnknownReference(location, errorMessage); - return EncounteredUnknownReference; + private UnknownReference UnknownReference(Location location, string errorMessage) { + _encounteredUnknownReference = new UnknownReference(location, errorMessage); + return _encounteredUnknownReference; } /// /// but with a common message /// - private static UnknownReference UnknownIdentifier(Location location, string identifier) => + private UnknownReference UnknownIdentifier(Location location, string identifier) => UnknownReference(location, $"Unknown identifier \"{identifier}\""); } diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index 15b90c9727..e4fcd52a2d 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -6,7 +6,9 @@ using DMCompiler.DM.Expressions; namespace DMCompiler.DM.Builders { - internal sealed class DMProcBuilder(DMObject dmObject, DMProc proc) { + internal sealed class DMProcBuilder(DMCompiler compiler, DMObject dmObject, DMProc proc) { + private readonly DMExpressionBuilder _exprBuilder = new(new(compiler, dmObject, proc)); + /// /// BYOND currently has a ridiculous behaviour, where,
/// sometimes when a set statement has a right-hand side that is non-constant,
@@ -17,6 +19,8 @@ internal sealed class DMProcBuilder(DMObject dmObject, DMProc proc) { // Starts null; marks that we've never seen one before and should just error like normal people. private Constant? _previousSetStatementValue; + private ExpressionContext ExprContext => new(compiler, dmObject, proc); + public void ProcessProcDefinition(DMASTProcDefinition procDefinition) { if (procDefinition.Body == null) return; @@ -33,7 +37,7 @@ public void ProcessProcDefinition(DMASTProcDefinition procDefinition) { proc.JumpIfFalse(afterDefaultValueCheck); //Set default - DMExpression.Emit(dmObject, proc, parameter.Value, parameter.ObjectType); + _exprBuilder.Emit(parameter.Value, parameter.ObjectType); proc.Assign(parameterRef); proc.Pop(); @@ -62,9 +66,9 @@ private void ProcessBlockInner(DMASTProcBlockInner block, bool silenceEmptyBlock // 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"); + compiler.Emit(WarningCode.EmptyBlock,block.Location,"Empty block detected - set statements are executed outside of, before, and unconditional to, this block"); } else { - DMCompiler.Emit(WarningCode.EmptyBlock,block.Location,"Empty block detected"); + compiler.Emit(WarningCode.EmptyBlock,block.Location,"Empty block detected"); } return; @@ -115,13 +119,13 @@ public void ProcessStatement(DMASTProcStatement statement) { ProcessStatementVarDeclaration(declare); break; default: - DMCompiler.ForcedError(statement.Location, $"Invalid proc statement {statement.GetType()}"); + compiler.ForcedError(statement.Location, $"Invalid proc statement {statement.GetType()}"); break; } } public void ProcessStatementExpression(DMASTProcStatementExpression statement) { - DMExpression.Emit(dmObject, proc, statement.Expression); + _exprBuilder.Emit(statement.Expression); proc.Pop(); } @@ -170,11 +174,11 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { proc.VerbSrc = VerbSrc.InUsr; } else if (deref == "loc") { proc.VerbSrc = VerbSrc.UsrLoc; - DMCompiler.UnimplementedWarning(statementSet.Location, + compiler.UnimplementedWarning(statementSet.Location, "'set src = usr.loc' is unimplemented"); } else if (deref == "group") { proc.VerbSrc = VerbSrc.UsrGroup; - DMCompiler.UnimplementedWarning(statementSet.Location, + compiler.UnimplementedWarning(statementSet.Location, "'set src = usr.group' is unimplemented"); } else { goto default; @@ -184,10 +188,10 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { case DMASTIdentifier {Identifier: "world"}: proc.VerbSrc = statementSet.WasInKeyword ? VerbSrc.InWorld : VerbSrc.World; if (statementSet.WasInKeyword) - DMCompiler.UnimplementedWarning(statementSet.Location, + compiler.UnimplementedWarning(statementSet.Location, "'set src = world.contents' is unimplemented"); else - DMCompiler.UnimplementedWarning(statementSet.Location, + compiler.UnimplementedWarning(statementSet.Location, "'set src = world' is unimplemented"); break; case DMASTDereference {Expression: DMASTIdentifier{Identifier: "world"}, Operations: var operations}: @@ -195,7 +199,7 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { goto default; proc.VerbSrc = VerbSrc.InWorld; - DMCompiler.UnimplementedWarning(statementSet.Location, + compiler.UnimplementedWarning(statementSet.Location, "'set src = world.contents' is unimplemented"); break; case DMASTProcCall {Callable: DMASTCallableProcIdentifier {Identifier: { } viewType and ("view" or "oview")}}: @@ -214,15 +218,15 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { proc.VerbSrc = viewType == "range" ? VerbSrc.Range : VerbSrc.ORange; break; default: - DMCompiler.Emit(WarningCode.BadExpression, statementSet.Value.Location, "Invalid verb src"); + compiler.Emit(WarningCode.BadExpression, statementSet.Value.Location, "Invalid verb src"); break; } return; } - if (!DMExpression.TryConstant(dmObject, proc, statementSet.Value, out var constant)) { // If this set statement's rhs is not constant - bool didError = DMCompiler.Emit(WarningCode.InvalidSetStatement, statementSet.Location, $"'{attribute}' attribute should be a constant"); + if (!_exprBuilder.TryConstant(statementSet.Value, out var constant)) { // If this set statement's rhs is not constant + bool didError = compiler.Emit(WarningCode.InvalidSetStatement, statementSet.Location, $"'{attribute}' attribute should be a constant"); if (didError) // if this is an error return; // don't do the cursed thing @@ -233,14 +237,14 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { // oh no. if (constant is null) { - DMCompiler.Emit(WarningCode.BadExpression, statementSet.Location, $"'{attribute}' attribute must be a constant"); + compiler.Emit(WarningCode.BadExpression, statementSet.Location, $"'{attribute}' attribute must be a constant"); return; } // Check if it was 'set x in y' or whatever // (which is illegal for everything except setting src to something) if (statementSet.WasInKeyword) { - DMCompiler.Emit(WarningCode.BadToken, statementSet.Location, "Use of 'in' keyword is illegal here. Did you mean '='?"); + compiler.Emit(WarningCode.BadToken, statementSet.Location, "Use of 'in' keyword is illegal here. Did you mean '='?"); //fallthrough into normal behaviour because this error is kinda pedantic } @@ -274,7 +278,7 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { else proc.Attributes &= ~ProcAttributes.Instant; - DMCompiler.UnimplementedWarning(statementSet.Location, "set instant is not implemented"); + compiler.UnimplementedWarning(statementSet.Location, "set instant is not implemented"); break; case "background": if (constant.IsTruthy()) @@ -284,7 +288,7 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { break; case "name": if (constant is not Expressions.String nameStr) { - DMCompiler.Emit(WarningCode.BadExpression, constant.Location, "name attribute must be a string"); + compiler.Emit(WarningCode.BadExpression, constant.Location, "name attribute must be a string"); break; } @@ -296,7 +300,7 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { } else if (constant is Null) { proc.VerbCategory = null; } else { - DMCompiler.Emit(WarningCode.BadExpression, constant.Location, + compiler.Emit(WarningCode.BadExpression, constant.Location, "category attribute must be a string or null"); } @@ -304,7 +308,7 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { case "desc": // TODO: verb.desc is supposed to be printed when you type the verb name and press F1. Check the ref for details. if (constant is not Expressions.String descStr) { - DMCompiler.Emit(WarningCode.BadExpression, constant.Location, "desc attribute must be a string"); + compiler.Emit(WarningCode.BadExpression, constant.Location, "desc attribute must be a string"); break; } @@ -314,25 +318,25 @@ public void ProcessStatementSet(DMASTProcStatementSet statementSet) { // The ref says 0-101 for atoms and 0-100 for verbs // BYOND doesn't clamp the actual var value but it does seem to treat out-of-range values as their extreme if (constant is not Number invisNum) { - DMCompiler.Emit(WarningCode.BadExpression, constant.Location, "invisibility attribute must be an int"); + compiler.Emit(WarningCode.BadExpression, constant.Location, "invisibility attribute must be an int"); break; } proc.Invisibility = Convert.ToSByte(Math.Clamp(MathF.Floor(invisNum.Value), 0f, 100f)); break; case "src": - DMCompiler.UnimplementedWarning(statementSet.Location, "set src is not implemented"); + compiler.UnimplementedWarning(statementSet.Location, "set src is not implemented"); break; } } public void ProcessStatementDel(DMASTProcStatementDel statementDel) { - DMExpression.Emit(dmObject, proc, statementDel.Value); + _exprBuilder.Emit(statementDel.Value); proc.DeleteObject(); } public void ProcessStatementSpawn(DMASTProcStatementSpawn statementSpawn) { - DMExpression.Emit(dmObject, proc, statementSpawn.Delay); + _exprBuilder.Emit(statementSpawn.Delay); string afterSpawnLabel = proc.NewLabelName(); proc.Spawn(afterSpawnLabel); @@ -358,10 +362,10 @@ public void ProcessStatementVarDeclaration(DMASTProcStatementVarDeclaration varD DMExpression value; if (varDeclaration.Value != null) { - value = DMExpression.Create(dmObject, proc, varDeclaration.Value, varDeclaration.Type); + value = _exprBuilder.Create(varDeclaration.Value, varDeclaration.Type); - if (!varDeclaration.ValType.MatchesType(value.ValType)) { - DMCompiler.Emit(WarningCode.InvalidVarType, varDeclaration.Location, + if (!varDeclaration.ValType.MatchesType(compiler, value.ValType)) { + compiler.Emit(WarningCode.InvalidVarType, varDeclaration.Location, $"{varDeclaration.Name}: Invalid var value {value.ValType}, expected {varDeclaration.ValType}"); } } else { @@ -370,8 +374,8 @@ public void ProcessStatementVarDeclaration(DMASTProcStatementVarDeclaration varD bool successful; if (varDeclaration.IsConst) { - if (!value.TryAsConstant(out var constValue)) { - DMCompiler.Emit(WarningCode.HardConstContext, varDeclaration.Location, "Const var must be set to a constant"); + if (!value.TryAsConstant(compiler, out var constValue)) { + compiler.Emit(WarningCode.HardConstContext, varDeclaration.Location, "Const var must be set to a constant"); return; } @@ -381,29 +385,29 @@ public void ProcessStatementVarDeclaration(DMASTProcStatementVarDeclaration varD } if (!successful) { - DMCompiler.Emit(WarningCode.DuplicateVariable, varDeclaration.Location, $"Duplicate var {varDeclaration.Name}"); + compiler.Emit(WarningCode.DuplicateVariable, varDeclaration.Location, $"Duplicate var {varDeclaration.Name}"); return; } - value.EmitPushValue(dmObject, proc); + value.EmitPushValue(ExprContext); proc.Assign(proc.GetLocalVariableReference(varDeclaration.Name)); proc.Pop(); } public void ProcessStatementReturn(DMASTProcStatementReturn statement) { if (statement.Value != null) { - var expr = DMExpression.Create(dmObject, proc, statement.Value); + var expr = _exprBuilder.Create(statement.Value); // Don't type-check unimplemented procs if (proc.TypeChecked && (proc.Attributes & ProcAttributes.Unimplemented) == 0) { - if (expr.TryAsConstant(out var exprConst)) { + if (expr.TryAsConstant(compiler, out var exprConst)) { proc.ValidateReturnType(exprConst); } else { proc.ValidateReturnType(expr); } } - expr.EmitPushValue(dmObject, proc); + expr.EmitPushValue(ExprContext); } else { proc.PushReferenceValue(DMReference.Self); //Default return value } @@ -412,7 +416,7 @@ public void ProcessStatementReturn(DMASTProcStatementReturn statement) { } public void ProcessStatementIf(DMASTProcStatementIf statement) { - DMExpression.Emit(dmObject, proc, statement.Condition); + _exprBuilder.Emit(statement.Condition); if (statement.ElseBody == null) { string endLabel = proc.NewLabelName(); @@ -449,22 +453,22 @@ public void ProcessStatementFor(DMASTProcStatementFor statementFor) { } if (statementFor.Expression2 != null || statementFor.Expression3 != null) { - var initializer = statementFor.Expression1 != null ? DMExpression.Create(dmObject, proc, statementFor.Expression1) : null; - var comparator = statementFor.Expression2 != null ? DMExpression.Create(dmObject, proc, statementFor.Expression2) : null; - var incrementor = statementFor.Expression3 != null ? DMExpression.Create(dmObject, proc, statementFor.Expression3) : null; + var initializer = statementFor.Expression1 != null ? _exprBuilder.Create(statementFor.Expression1) : null; + var comparator = statementFor.Expression2 != null ? _exprBuilder.Create(statementFor.Expression2) : null; + var incrementor = statementFor.Expression3 != null ? _exprBuilder.Create(statementFor.Expression3) : null; ProcessStatementForStandard(initializer, comparator, incrementor, statementFor.Body); } else { switch (statementFor.Expression1) { case DMASTAssign {LHS: DMASTVarDeclExpression decl, RHS: DMASTExpressionInRange range}: { - var initializer = statementFor.Expression1 != null ? DMExpression.Create(dmObject, proc, statementFor.Expression1) : null; + var initializer = statementFor.Expression1 != null ? _exprBuilder.Create(statementFor.Expression1) : null; var identifier = new DMASTIdentifier(decl.Location, decl.DeclPath.Path.LastElement); - var outputVar = DMExpression.Create(dmObject, proc, identifier); + var outputVar = _exprBuilder.Create(identifier); - var start = DMExpression.Create(dmObject, proc, range.StartRange); - var end = DMExpression.Create(dmObject, proc, range.EndRange); + var start = _exprBuilder.Create(range.StartRange); + var end = _exprBuilder.Create(range.EndRange); var step = range.Step != null - ? DMExpression.Create(dmObject, proc, range.Step) + ? _exprBuilder.Create(range.StartRange) : new Number(range.Location, 1); ProcessStatementForRange(initializer, outputVar, start, end, step, statementFor.Body); @@ -483,22 +487,22 @@ public void ProcessStatementFor(DMASTProcStatementFor statementFor) { outputExpr = exprRange.Value; } - var outputVar = DMExpression.Create(dmObject, proc, outputExpr); + var outputVar = _exprBuilder.Create(outputExpr); - var start = DMExpression.Create(dmObject, proc, exprRange.StartRange); - var end = DMExpression.Create(dmObject, proc, exprRange.EndRange); + var start = _exprBuilder.Create(exprRange.StartRange); + var end = _exprBuilder.Create(exprRange.EndRange); var step = exprRange.Step != null - ? DMExpression.Create(dmObject, proc, exprRange.Step) + ? _exprBuilder.Create(exprRange.Step) : new Number(exprRange.Location, 1); ProcessStatementForRange(null, outputVar, start, end, step, statementFor.Body); break; } case DMASTVarDeclExpression vd: { - var initializer = statementFor.Expression1 != null ? DMExpression.Create(dmObject, proc, statementFor.Expression1) : null; + var initializer = statementFor.Expression1 != null ? _exprBuilder.Create(statementFor.Expression1) : null; var declInfo = new ProcVarDeclInfo(vd.DeclPath.Path); var identifier = new DMASTIdentifier(vd.Location, declInfo.VarName); - var outputVar = DMExpression.Create(dmObject, proc, identifier); + var outputVar = _exprBuilder.Create(identifier); ProcessStatementForType(initializer, outputVar, declInfo.TypePath, statementFor.Body); break; @@ -511,8 +515,8 @@ public void ProcessStatementFor(DMASTProcStatementFor statementFor) { outputExpr = exprIn.LHS; } - var outputVar = DMExpression.Create(dmObject, proc, outputExpr); - var list = DMExpression.Create(dmObject, proc, exprIn.RHS); + var outputVar = _exprBuilder.Create(outputExpr); + var list = _exprBuilder.Create(exprIn.RHS); if (outputVar is Local outputLocal) { outputLocal.LocalVar.ExplicitValueType = statementFor.DMTypes; @@ -522,7 +526,7 @@ public void ProcessStatementFor(DMASTProcStatementFor statementFor) { break; } default: - DMCompiler.Emit(WarningCode.BadExpression, statementFor.Location, "Invalid expression in for"); + compiler.Emit(WarningCode.BadExpression, statementFor.Location, "Invalid expression in for"); break; } } @@ -545,7 +549,7 @@ public void ProcessStatementForStandard(DMExpression? initializer, DMExpression? proc.StartScope(); { if (initializer != null) { - initializer.EmitPushValue(dmObject, proc); + initializer.EmitPushValue(ExprContext); proc.Pop(); } @@ -553,7 +557,7 @@ public void ProcessStatementForStandard(DMExpression? initializer, DMExpression? proc.LoopStart(loopLabel); { if (comparator != null) { - comparator.EmitPushValue(dmObject, proc); + comparator.EmitPushValue(ExprContext); proc.BreakIfFalse(); } @@ -561,9 +565,10 @@ public void ProcessStatementForStandard(DMExpression? initializer, DMExpression? proc.MarkLoopContinue(loopLabel); if (incrementor != null) { - incrementor.EmitPushValue(dmObject, proc); + incrementor.EmitPushValue(ExprContext); proc.Pop(); } + proc.LoopJumpToStart(loopLabel); } proc.LoopEnd(); @@ -576,7 +581,7 @@ public void ProcessLoopAssignment(LValue lValue) { string endLabel = proc.NewLabelName(); string endLabel2 = proc.NewLabelName(); - DMReference outputRef = lValue.EmitReference(dmObject, proc, endLabel, DMExpression.ShortCircuitMode.PopNull); + DMReference outputRef = lValue.EmitReference(ExprContext, endLabel, DMExpression.ShortCircuitMode.PopNull); proc.Enumerate(outputRef); proc.Jump(endLabel2); @@ -584,14 +589,14 @@ public void ProcessLoopAssignment(LValue lValue) { proc.EnumerateNoAssign(); proc.AddLabel(endLabel2); } else { - DMReference outputRef = lValue.EmitReference(dmObject, proc, null); + DMReference outputRef = lValue.EmitReference(ExprContext, null); proc.Enumerate(outputRef); } } public void ProcessStatementForList(DMExpression list, DMExpression outputVar, DMComplexValueType? dmTypes, DMASTProcBlockInner body) { if (outputVar is not LValue lValue) { - DMCompiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); + compiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); lValue = null; } @@ -605,17 +610,17 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D implicitTypeCheck = dmTypes.Value.TypePath; } else if (!dmTypes.Value.IsAnything) { // "as anything" performs no check. Other values are unimplemented. - DMCompiler.UnimplementedWarning(outputVar.Location, + compiler.UnimplementedWarning(outputVar.Location, $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); } - list.EmitPushValue(dmObject, proc); + list.EmitPushValue(ExprContext); if (implicitTypeCheck != null) { - if (DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) { + if (compiler.DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) { // Create an enumerator that will do the implicit istype() for us proc.CreateFilteredListEnumerator(filterTypeId, implicitTypeCheck.Value); } else { - DMCompiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location, + compiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location, $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist"); proc.CreateListEnumerator(); } @@ -646,22 +651,22 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D public void ProcessStatementForType(DMExpression? initializer, DMExpression outputVar, DreamPath? type, DMASTProcBlockInner body) { if (type == null) { // This shouldn't happen, just to be safe - DMCompiler.ForcedError(initializer.Location, + compiler.ForcedError(initializer.Location, "Attempted to create a type enumerator with a null type"); return; } - if (DMObjectTree.TryGetTypeId(type.Value, out var typeId)) { + if (compiler.DMObjectTree.TryGetTypeId(type.Value, out var typeId)) { proc.PushType(typeId); proc.CreateTypeEnumerator(); } else { - DMCompiler.Emit(WarningCode.ItemDoesntExist, initializer.Location, $"Type {type.Value} does not exist"); + compiler.Emit(WarningCode.ItemDoesntExist, initializer.Location, $"Type {type.Value} does not exist"); } proc.StartScope(); { if (initializer != null) { - initializer.EmitPushValue(dmObject, proc); + initializer.EmitPushValue(ExprContext); proc.Pop(); } @@ -673,7 +678,7 @@ public void ProcessStatementForType(DMExpression? initializer, DMExpression outp if (outputVar is Expressions.LValue lValue) { ProcessLoopAssignment(lValue); } else { - DMCompiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); + compiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); } ProcessBlockInner(body); @@ -686,10 +691,10 @@ public void ProcessStatementForType(DMExpression? initializer, DMExpression outp } public void ProcessStatementForRange(DMExpression? initializer, DMExpression outputVar, DMExpression start, DMExpression end, DMExpression? step, DMASTProcBlockInner body) { - start.EmitPushValue(dmObject, proc); - end.EmitPushValue(dmObject, proc); + start.EmitPushValue(ExprContext); + end.EmitPushValue(ExprContext); if (step != null) { - step.EmitPushValue(dmObject, proc); + step.EmitPushValue(ExprContext); } else { proc.PushFloat(1.0f); } @@ -698,7 +703,7 @@ public void ProcessStatementForRange(DMExpression? initializer, DMExpression out proc.StartScope(); { if (initializer != null) { - initializer.EmitPushValue(dmObject, proc); + initializer.EmitPushValue(ExprContext); proc.Pop(); } @@ -710,7 +715,7 @@ public void ProcessStatementForRange(DMExpression? initializer, DMExpression out if (outputVar is Expressions.LValue lValue) { ProcessLoopAssignment(lValue); } else { - DMCompiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); + compiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var"); } ProcessBlockInner(body); @@ -744,7 +749,7 @@ public void ProcessStatementWhile(DMASTProcStatementWhile statementWhile) { proc.LoopStart(loopLabel); { proc.MarkLoopContinue(loopLabel); - DMExpression.Emit(dmObject, proc, statementWhile.Conditional); + _exprBuilder.Emit(statementWhile.Conditional); proc.BreakIfFalse(); proc.StartScope(); @@ -766,7 +771,7 @@ public void ProcessStatementDoWhile(DMASTProcStatementDoWhile statementDoWhile) ProcessBlockInner(statementDoWhile.Body); proc.MarkLoopContinue(loopLabel); - DMExpression.Emit(dmObject, proc, statementDoWhile.Conditional); + _exprBuilder.Emit(statementDoWhile.Conditional); proc.JumpIfFalse(loopEndLabel); proc.LoopJumpToStart(loopLabel); @@ -781,15 +786,15 @@ public void ProcessStatementSwitch(DMASTProcStatementSwitch statementSwitch) { List<(string CaseLabel, DMASTProcBlockInner CaseBody)> valueCases = new(); DMASTProcBlockInner? defaultCaseBody = null; - DMExpression.Emit(dmObject, proc, statementSwitch.Value); + _exprBuilder.Emit(statementSwitch.Value); foreach (DMASTProcStatementSwitch.SwitchCase switchCase in statementSwitch.Cases) { if (switchCase is DMASTProcStatementSwitch.SwitchCaseValues switchCaseValues) { string caseLabel = proc.NewLabelName(); foreach (DMASTExpression value in switchCaseValues.Values) { Constant GetCaseValue(DMASTExpression expression) { - if (!DMExpression.TryConstant(dmObject, proc, expression, out var constant)) - DMCompiler.Emit(WarningCode.HardConstContext, expression.Location, "Expected a constant"); + if (!_exprBuilder.TryConstant(expression, out var constant)) + compiler.Emit(WarningCode.HardConstContext, expression.Location, "Expected a constant"); // Return 0 if unsuccessful so that we can continue compiling return constant ?? new Number(expression.Location, 0.0f); @@ -801,7 +806,7 @@ Constant GetCaseValue(DMASTExpression expression) { Constant CoerceBound(Constant bound) { if (bound is Null) { // We do a little null coercion, as a treat - DMCompiler.Emit(WarningCode.MalformedRange, range.RangeStart.Location, + compiler.Emit(WarningCode.MalformedRange, range.RangeStart.Location, "Malformed range, lower bound is coerced from null to 0"); return new Number(lower.Location, 0.0f); } @@ -809,7 +814,7 @@ Constant CoerceBound(Constant bound) { //DM 514.1580 does NOT care if the constants within a range are strings, and does a strange conversion to 0 or something, without warning or notice. //We are (hopefully) deviating from parity here and just calling that a Compiler error. if (bound is not Number) { - DMCompiler.Emit(WarningCode.InvalidRange, range.RangeStart.Location, + compiler.Emit(WarningCode.InvalidRange, range.RangeStart.Location, "Invalid range, lower bound is not a number"); bound = new Number(bound.Location, 0.0f); } @@ -820,13 +825,13 @@ Constant CoerceBound(Constant bound) { lower = CoerceBound(lower); upper = CoerceBound(upper); - lower.EmitPushValue(dmObject, proc); - upper.EmitPushValue(dmObject, proc); + lower.EmitPushValue(ExprContext); + upper.EmitPushValue(ExprContext); proc.SwitchCaseRange(caseLabel); } else { Constant constant = GetCaseValue(value); - constant.EmitPushValue(dmObject, proc); + constant.EmitPushValue(ExprContext); proc.SwitchCase(caseLabel); } } @@ -861,74 +866,74 @@ Constant CoerceBound(Constant bound) { } public void ProcessStatementBrowse(DMASTProcStatementBrowse statementBrowse) { - DMExpression.Emit(dmObject, proc, statementBrowse.Receiver); - DMExpression.Emit(dmObject, proc, statementBrowse.Body); - DMExpression.Emit(dmObject, proc, statementBrowse.Options); + _exprBuilder.Emit(statementBrowse.Receiver); + _exprBuilder.Emit(statementBrowse.Body); + _exprBuilder.Emit(statementBrowse.Options); proc.Browse(); } public void ProcessStatementBrowseResource(DMASTProcStatementBrowseResource statementBrowseResource) { - DMExpression.Emit(dmObject, proc, statementBrowseResource.Receiver); - DMExpression.Emit(dmObject, proc, statementBrowseResource.File); - DMExpression.Emit(dmObject, proc, statementBrowseResource.Filename); + _exprBuilder.Emit(statementBrowseResource.Receiver); + _exprBuilder.Emit(statementBrowseResource.File); + _exprBuilder.Emit(statementBrowseResource.Filename); proc.BrowseResource(); } public void ProcessStatementOutputControl(DMASTProcStatementOutputControl statementOutputControl) { - DMExpression.Emit(dmObject, proc, statementOutputControl.Receiver); - DMExpression.Emit(dmObject, proc, statementOutputControl.Message); - DMExpression.Emit(dmObject, proc, statementOutputControl.Control); + _exprBuilder.Emit(statementOutputControl.Receiver); + _exprBuilder.Emit(statementOutputControl.Message); + _exprBuilder.Emit(statementOutputControl.Control); proc.OutputControl(); } public void ProcessStatementFtp(DMASTProcStatementFtp statementFtp) { - DMExpression.Emit(dmObject, proc, statementFtp.Receiver); - DMExpression.Emit(dmObject, proc, statementFtp.File); - DMExpression.Emit(dmObject, proc, statementFtp.Name); + _exprBuilder.Emit(statementFtp.Receiver); + _exprBuilder.Emit(statementFtp.File); + _exprBuilder.Emit(statementFtp.Name); proc.Ftp(); } public void ProcessStatementOutput(DMASTProcStatementOutput statementOutput) { - DMExpression left = DMExpression.Create(dmObject, proc, statementOutput.A); - DMExpression right = DMExpression.Create(dmObject, proc, statementOutput.B); + DMExpression left = _exprBuilder.Create(statementOutput.A); + DMExpression right = _exprBuilder.Create(statementOutput.B); if (left is LValue) { // An LValue on the left needs a special opcode so that its reference can be used // This allows for special operations like "savefile[...] << ..." string endLabel = proc.NewLabelName(); - DMReference leftRef = left.EmitReference(dmObject, proc, endLabel, DMExpression.ShortCircuitMode.PopNull); - right.EmitPushValue(dmObject, proc); + DMReference leftRef = left.EmitReference(ExprContext, endLabel, DMExpression.ShortCircuitMode.PopNull); + right.EmitPushValue(ExprContext); proc.OutputReference(leftRef); proc.AddLabel(endLabel); } else { - left.EmitPushValue(dmObject, proc); - right.EmitPushValue(dmObject, proc); + left.EmitPushValue(ExprContext); + right.EmitPushValue(ExprContext); proc.Output(); } } public void ProcessStatementInput(DMASTProcStatementInput statementInput) { - DMExpression left = DMExpression.Create(dmObject, proc, statementInput.A); - DMExpression right = DMExpression.Create(dmObject, proc, statementInput.B); + DMExpression left = _exprBuilder.Create(statementInput.A); + DMExpression right = _exprBuilder.Create(statementInput.B); // The left-side value of an input operation must be an LValue // (I think? I haven't found an exception but there could be one) if (left is not LValue) { - DMCompiler.Emit(WarningCode.BadExpression, left.Location, "Left side must be an l-value"); + compiler.Emit(WarningCode.BadExpression, left.Location, "Left side must be an l-value"); return; } // The right side must also be an LValue. Because where else would the value go? if (right is not LValue) { - DMCompiler.Emit(WarningCode.BadExpression, right.Location, "Right side must be an l-value"); + compiler.Emit(WarningCode.BadExpression, right.Location, "Right side must be an l-value"); return; } string rightEndLabel = proc.NewLabelName(); string leftEndLabel = proc.NewLabelName(); - DMReference rightRef = right.EmitReference(dmObject, proc, rightEndLabel, DMExpression.ShortCircuitMode.PopNull); - DMReference leftRef = left.EmitReference(dmObject, proc, leftEndLabel, DMExpression.ShortCircuitMode.PopNull); + DMReference rightRef = right.EmitReference(ExprContext, rightEndLabel, DMExpression.ShortCircuitMode.PopNull); + DMReference leftRef = left.EmitReference(ExprContext, leftEndLabel, DMExpression.ShortCircuitMode.PopNull); proc.Input(leftRef, rightRef); @@ -944,7 +949,7 @@ public void ProcessStatementTryCatch(DMASTProcStatementTryCatch tryCatch) { var param = tryCatch.CatchParameter as DMASTProcStatementVarDeclaration; if (!proc.TryAddLocalVariable(param.Name, param.Type, param.ValType)) { - DMCompiler.Emit(WarningCode.DuplicateVariable, param.Location, $"Duplicate var {param.Name}"); + compiler.Emit(WarningCode.DuplicateVariable, param.Location, $"Duplicate var {param.Name}"); } proc.StartTry(catchLabel, proc.GetLocalVariableReference(param.Name)); @@ -964,12 +969,12 @@ public void ProcessStatementTryCatch(DMASTProcStatementTryCatch tryCatch) { ProcessBlockInner(tryCatch.CatchBody); proc.EndScope(); } - proc.AddLabel(endLabel); + proc.AddLabel(endLabel); } public void ProcessStatementThrow(DMASTProcStatementThrow statement) { - DMExpression.Emit(dmObject, proc, statement.Value); + _exprBuilder.Emit(statement.Value); proc.Throw(); } } diff --git a/DMCompiler/DM/DMCodeTree.Procs.cs b/DMCompiler/DM/DMCodeTree.Procs.cs index 86567fca4a..174ff77c9b 100644 --- a/DMCompiler/DM/DMCodeTree.Procs.cs +++ b/DMCompiler/DM/DMCodeTree.Procs.cs @@ -3,44 +3,43 @@ namespace DMCompiler.DM; -internal static partial class DMCodeTree { +internal partial class DMCodeTree { private class ProcsNode() : TypeNode("proc"); - private class ProcNode(DreamPath owner, DMASTProcDefinition procDef) : TypeNode(procDef.Name) { + private class ProcNode(DMCodeTree codeTree, DreamPath owner, DMASTProcDefinition procDef) : TypeNode(procDef.Name) { private string ProcName => procDef.Name; private bool IsOverride => procDef.IsOverride; private bool _defined; - public void DefineProc() { + public bool TryDefineProc(DMCompiler compiler) { if (_defined) - return; - if (!DMObjectTree.TryGetDMObject(owner, out var dmObject)) - return; + return true; + if (!compiler.DMObjectTree.TryGetDMObject(owner, out var dmObject)) + return false; _defined = true; - WaitingNodes.Remove(this); bool hasProc = dmObject.HasProc(ProcName); if (hasProc && !IsOverride && !dmObject.OwnsProc(ProcName) && !procDef.Location.InDMStandard) { - DMCompiler.Emit(WarningCode.DuplicateProcDefinition, procDef.Location, + compiler.Emit(WarningCode.DuplicateProcDefinition, procDef.Location, $"Type {owner} already inherits a proc named \"{ProcName}\" and cannot redefine it"); - return; // TODO: Maybe fallthrough since this error is a little pedantic? + return true; // TODO: Maybe fallthrough since this error is a little pedantic? } - DMProc proc = DMObjectTree.CreateDMProc(dmObject, procDef); + DMProc proc = compiler.DMObjectTree.CreateDMProc(dmObject, procDef); - if (dmObject == DMObjectTree.Root) { // Doesn't belong to a type, this is a global proc + if (dmObject == compiler.DMObjectTree.Root) { // Doesn't belong to a type, this is a global proc if(IsOverride) { - DMCompiler.Emit(WarningCode.InvalidOverride, procDef.Location, + compiler.Emit(WarningCode.InvalidOverride, procDef.Location, $"Global procs cannot be overridden - '{ProcName}' override will be ignored"); //Continue processing the proc anyhoo, just don't add it. } else { - DMCompiler.VerbosePrint($"Adding global proc {procDef.Name}() on pass {_currentPass}"); - DMObjectTree.AddGlobalProc(proc); + compiler.VerbosePrint($"Adding global proc {procDef.Name}() on pass {codeTree._currentPass}"); + compiler.DMObjectTree.AddGlobalProc(proc); } } else { - DMCompiler.VerbosePrint($"Adding proc {procDef.Name}() to {dmObject.Path} on pass {_currentPass}"); + compiler.VerbosePrint($"Adding proc {procDef.Name}() to {dmObject.Path} on pass {codeTree._currentPass}"); dmObject.AddProc(proc, forceFirst: procDef.Location.InDMStandard); } @@ -50,12 +49,14 @@ public void DefineProc() { var procGlobalNode = new ProcGlobalVarNode(owner, proc, varDecl); Children.Add(procGlobalNode); - WaitingNodes.Add(procGlobalNode); + codeTree._waitingNodes.Add(procGlobalNode); } if (proc.IsVerb) { dmObject.AddVerb(proc); } + + return true; } // TODO: Remove this entirely @@ -105,14 +106,14 @@ public override string ToString() { } } - public static void AddProc(DreamPath owner, DMASTProcDefinition procDef) { + public void AddProc(DreamPath owner, DMASTProcDefinition procDef) { var node = GetDMObjectNode(owner); - var procNode = new ProcNode(owner, procDef); + var procNode = new ProcNode(this, owner, procDef); if (procDef is { Name: "New", IsOverride: false }) - NewProcs[owner] = procNode; // We need to be ready to define New() as soon as the type is created + _newProcs[owner] = procNode; // We need to be ready to define New() as soon as the type is created node.AddProcsNode().Children.Add(procNode); - WaitingNodes.Add(procNode); + _waitingNodes.Add(procNode); } } diff --git a/DMCompiler/DM/DMCodeTree.Vars.cs b/DMCompiler/DM/DMCodeTree.Vars.cs index 00877b4109..95188388e4 100644 --- a/DMCompiler/DM/DMCodeTree.Vars.cs +++ b/DMCompiler/DM/DMCodeTree.Vars.cs @@ -8,53 +8,49 @@ namespace DMCompiler.DM; -internal static partial class DMCodeTree { +internal partial class DMCodeTree { public abstract class VarNode : INode { public UnknownReference? LastError; protected bool IsFirstPass => (LastError == null); - public abstract void TryDefineVar(); + public abstract bool TryDefineVar(DMCompiler compiler, int pass); - protected bool TryBuildValue(DMASTExpression ast, DreamPath? inferredType, DMObject dmObject, DMProc? proc, + protected bool TryBuildValue(ExpressionContext ctx, DMASTExpression ast, DreamPath? inferredType, ScopeMode scope, [NotNullWhen(true)] out DMExpression? value) { - try { - DMExpressionBuilder.CurrentScopeMode = scope; + var exprBuilder = new DMExpressionBuilder(ctx, scope); - value = DMExpression.CreateIgnoreUnknownReference(dmObject, proc, ast, inferredType); - if (value is UnknownReference unknownRef) { - LastError = unknownRef; - value = null; - return false; - } - - return true; - } finally { - DMExpressionBuilder.CurrentScopeMode = ScopeMode.Normal; + value = exprBuilder.CreateIgnoreUnknownReference(ast, inferredType); + if (value is UnknownReference unknownRef) { + LastError = unknownRef; + value = null; + return false; } + + return true; } - protected static void SetVariableValue(DMObject dmObject, DMVariable variable, DMExpression value, bool isOverride) { + protected void SetVariableValue(DMCompiler compiler, DMObject dmObject, DMVariable variable, DMExpression value, bool isOverride) { // Typechecking - if (!variable.ValType.MatchesType(value.ValType) && !variable.ValType.IsUnimplemented) { + if (!variable.ValType.MatchesType(compiler, value.ValType) && !variable.ValType.IsUnimplemented) { if (value is Null && !isOverride) { - DMCompiler.Emit(WarningCode.ImplicitNullType, value.Location, $"{dmObject.Path}.{variable.Name}: Variable is null but not explicitly typed as nullable, append \"|null\" to \"as\". Implicitly treating as nullable."); + compiler.Emit(WarningCode.ImplicitNullType, value.Location, $"{dmObject.Path}.{variable.Name}: Variable is null but not explicitly typed as nullable, append \"|null\" to \"as\". Implicitly treating as nullable."); variable.ValType |= DMValueType.Null; } else { - DMCompiler.Emit(WarningCode.InvalidVarType, value.Location, $"{dmObject.Path}.{variable.Name}: Invalid var value type {value.ValType}, expected {variable.ValType}"); + compiler.Emit(WarningCode.InvalidVarType, value.Location, $"{dmObject.Path}.{variable.Name}: Invalid var value type {value.ValType}, expected {variable.ValType}"); } } - if (value.TryAsConstant(out var constant)) { + if (value.TryAsConstant(compiler, out var constant)) { variable.Value = constant; return; } else if (variable.IsConst) { - DMCompiler.Emit(WarningCode.HardConstContext, value.Location, "Value of const var must be a constant"); + compiler.Emit(WarningCode.HardConstContext, value.Location, "Value of const var must be a constant"); return; } - if (!IsValidRightHandSide(dmObject, value)) { - DMCompiler.Emit(WarningCode.BadExpression, value.Location, + if (!IsValidRightHandSide(compiler, dmObject, value)) { + compiler.Emit(WarningCode.BadExpression, value.Location, $"Invalid initial value for \"{variable.Name}\""); return; } @@ -68,10 +64,10 @@ protected static void SetVariableValue(DMObject dmObject, DMVariable variable, D } /// Whether the given value can be used as an instance variable's initial value - private static bool IsValidRightHandSide(DMObject dmObject, DMExpression value) { + private bool IsValidRightHandSide(DMCompiler compiler, DMObject dmObject, DMExpression value) { return value switch { //TODO: A better way of handling procs evaluated at compile time - ProcCall procCall => procCall.GetTargetProc(dmObject).Proc?.Name switch { + ProcCall procCall => procCall.GetTargetProc(compiler, dmObject).Proc?.Name switch { "generator" => true, "matrix" => true, "icon" => true, @@ -100,22 +96,21 @@ private class ObjectVarNode(DreamPath owner, DMASTObjectVarDefinition varDef) : private bool _defined; - public override void TryDefineVar() { + public override bool TryDefineVar(DMCompiler compiler, int pass) { if (_defined) - return; - if (!DMObjectTree.TryGetDMObject(owner, out var dmObject)) - return; + return true; + if (!compiler.DMObjectTree.TryGetDMObject(owner, out var dmObject)) + return false; - if (AlreadyExists(dmObject)) { + if (AlreadyExists(compiler, dmObject)) { _defined = true; - WaitingNodes.Remove(this); - return; + return true; } if (IsStatic) { - HandleGlobalVar(dmObject); + return HandleGlobalVar(compiler, dmObject, pass); } else { - HandleInstanceVar(dmObject); + return HandleInstanceVar(compiler, dmObject); } } @@ -123,67 +118,67 @@ public override string ToString() { return varDef.IsStatic ? $"var/static/{VarName}" : $"var/{VarName}"; } - private void HandleGlobalVar(DMObject dmObject) { + private bool HandleGlobalVar(DMCompiler compiler, DMObject dmObject, int pass) { var scope = IsFirstPass ? ScopeMode.FirstPassStatic : ScopeMode.Static; - if (!TryBuildValue(varDef.Value, varDef.Type, dmObject, GlobalInitProc, scope, out var value)) - return; + if (!TryBuildValue(new(compiler, dmObject, compiler.GlobalInitProc), varDef.Value, varDef.Type, scope, out var value)) + return false; - int globalId = DMObjectTree.CreateGlobal(out DMVariable global, varDef.Type, VarName, varDef.IsConst, + int globalId = compiler.DMObjectTree.CreateGlobal(out DMVariable global, varDef.Type, VarName, varDef.IsConst, varDef.ValType); dmObject.AddGlobalVariable(global, globalId); _defined = true; - WaitingNodes.Remove(this); - if (value.TryAsConstant(out var constant)) { + if (value.TryAsConstant(compiler, out var constant)) { global.Value = constant; - return; + return true; } else if (!global.IsConst) { // Starts out as null, gets initialized by the global init proc global.Value = new Null(Location.Internal); } else { - DMCompiler.Emit(WarningCode.HardConstContext, value.Location, "Constant initializer required"); + compiler.Emit(WarningCode.HardConstContext, value.Location, "Constant initializer required"); } // Initialize its value in the global init proc - DMCompiler.VerbosePrint($"Adding {dmObject.Path}/var/static/{global.Name} to global init on pass {_currentPass}"); - GlobalInitProc.DebugSource(value.Location); - value.EmitPushValue(dmObject, GlobalInitProc); - GlobalInitProc.Assign(DMReference.CreateGlobal(globalId)); + compiler.VerbosePrint($"Adding {dmObject.Path}/var/static/{global.Name} to global init on pass {pass}"); + compiler.GlobalInitProc.DebugSource(value.Location); + value.EmitPushValue(new(compiler, dmObject, compiler.GlobalInitProc)); + compiler.GlobalInitProc.Assign(DMReference.CreateGlobal(globalId)); + return true; } - private void HandleInstanceVar(DMObject dmObject) { - if (!TryBuildValue(varDef.Value, varDef.Type, dmObject, null, ScopeMode.Normal, out var value)) - return; + private bool HandleInstanceVar(DMCompiler compiler, DMObject dmObject) { + if (!TryBuildValue(new(compiler, dmObject, null), varDef.Value, varDef.Type, ScopeMode.Normal, out var value)) + return false; var variable = new DMVariable(varDef.Type, VarName, false, varDef.IsConst, varDef.IsTmp, varDef.ValType); dmObject.AddVariable(variable); _defined = true; - WaitingNodes.Remove(this); - SetVariableValue(dmObject, variable, value, false); + SetVariableValue(compiler, dmObject, variable, value, false); + return true; } - private bool AlreadyExists(DMObject dmObject) { + private bool AlreadyExists(DMCompiler compiler, DMObject dmObject) { // "type" and "tag" can only be defined in DMStandard if (VarName is "type" or "tag" && !varDef.Location.InDMStandard) { - DMCompiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, + compiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, $"Cannot redefine built-in var \"{VarName}\""); return true; } //DMObjects store two bundles of variables; the statics in GlobalVariables and the non-statics in Variables. if (dmObject.HasGlobalVariable(VarName)) { - DMCompiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, + compiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, $"Duplicate definition of static var \"{VarName}\""); return true; } else if (dmObject.HasLocalVariable(VarName)) { if (!varDef.Location.InDMStandard) // Duplicate instance vars are not an error in DMStandard - DMCompiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, + compiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, $"Duplicate definition of var \"{VarName}\""); return true; - } else if (IsStatic && VarName == "vars" && dmObject == DMObjectTree.Root) { - DMCompiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, "Duplicate definition of global.vars"); + } else if (IsStatic && VarName == "vars" && dmObject == compiler.DMObjectTree.Root) { + compiler.Emit(WarningCode.InvalidVarDefinition, varDef.Location, "Duplicate definition of global.vars"); return true; } @@ -196,57 +191,50 @@ private class ObjectVarOverrideNode(DreamPath owner, DMASTObjectVarOverride varO private bool _finished; - public override void TryDefineVar() { + public override bool TryDefineVar(DMCompiler compiler, int pass) { if (_finished) - return; - if (!DMObjectTree.TryGetDMObject(owner, out var dmObject)) - return; + return true; + if (!compiler.DMObjectTree.TryGetDMObject(owner, out var dmObject)) + return false; DMVariable? variable = null; if (dmObject.HasLocalVariable(VarName)) { variable = dmObject.GetVariable(VarName); } else if (dmObject.HasGlobalVariable(VarName)) { - DMCompiler.Emit(WarningCode.StaticOverride, varOverride.Location, + compiler.Emit(WarningCode.StaticOverride, varOverride.Location, $"var \"{VarName}\" cannot be overridden - it is a global var"); _finished = true; - WaitingNodes.Remove(this); - return; + return true; } if (variable == null) { - return; + return false; } else if (variable.IsConst) { - DMCompiler.Emit(WarningCode.WriteToConstant, varOverride.Location, + compiler.Emit(WarningCode.WriteToConstant, varOverride.Location, $"Var \"{VarName}\" is const and cannot be modified"); _finished = true; - WaitingNodes.Remove(this); - return; + return true; } else if (variable.ValType.IsCompileTimeReadOnly) { - DMCompiler.Emit(WarningCode.WriteToConstant, varOverride.Location, + compiler.Emit(WarningCode.WriteToConstant, varOverride.Location, $"Var \"{VarName}\" is a native read-only value which cannot be modified"); _finished = true; - WaitingNodes.Remove(this); - return; + return true; } variable = new DMVariable(variable); - if (!TryBuildValue(varOverride.Value, variable.Type, dmObject, null, ScopeMode.Normal, out var value)) - return; + if (!TryBuildValue(new(compiler, dmObject, null), varOverride.Value, variable.Type, ScopeMode.Normal, out var value)) + return false; - if (VarName == "tag" && dmObject.IsSubtypeOf(DreamPath.Datum) && !DMCompiler.Settings.NoStandard) - DMCompiler.Emit(WarningCode.InvalidOverride, varOverride.Location, + if (VarName == "tag" && dmObject.IsSubtypeOf(DreamPath.Datum) && !compiler.Settings.NoStandard) + compiler.Emit(WarningCode.InvalidOverride, varOverride.Location, "var \"tag\" cannot be set to a value at compile-time"); dmObject.VariableOverrides[variable.Name] = variable; _finished = true; - WaitingNodes.Remove(this); - try { - SetVariableValue(dmObject, variable, value, true); - } finally { - DMExpressionBuilder.CurrentScopeMode = ScopeMode.Normal; - } + SetVariableValue(compiler, dmObject, variable, value, true); + return true; } public override string ToString() { @@ -257,34 +245,35 @@ public override string ToString() { private class ProcGlobalVarNode(DreamPath owner, DMProc proc, DMASTProcStatementVarDeclaration varDecl) : VarNode { private bool _defined; - public override void TryDefineVar() { + public override bool TryDefineVar(DMCompiler compiler, int pass) { if (_defined) - return; - if (!DMObjectTree.TryGetDMObject(owner, out var dmObject)) - return; + return true; + if (!compiler.DMObjectTree.TryGetDMObject(owner, out var dmObject)) + return false; DMExpression? value = null; if (varDecl.Value != null) { var scope = IsFirstPass ? ScopeMode.FirstPassStatic : ScopeMode.Static; - if (!TryBuildValue(varDecl.Value, varDecl.Type, dmObject, proc, scope, out value)) - return; + if (!TryBuildValue(new(compiler, dmObject, proc), varDecl.Value, varDecl.Type, scope, out value)) + return false; } - int globalId = DMObjectTree.CreateGlobal(out DMVariable global, varDecl.Type, varDecl.Name, varDecl.IsConst, + int globalId = compiler.DMObjectTree.CreateGlobal(out DMVariable global, varDecl.Type, varDecl.Name, varDecl.IsConst, varDecl.ValType); global.Value = new Null(Location.Internal); proc.AddGlobalVariable(global, globalId); _defined = true; - WaitingNodes.Remove(this); if (value != null) { // Initialize its value in the global init proc - DMCompiler.VerbosePrint($"Adding {dmObject.Path}/proc/{proc.Name}/var/static/{global.Name} to global init on pass {_currentPass}"); - GlobalInitProc.DebugSource(value.Location); - value.EmitPushValue(dmObject, GlobalInitProc); - GlobalInitProc.Assign(DMReference.CreateGlobal(globalId)); + compiler.VerbosePrint($"Adding {dmObject.Path}/proc/{proc.Name}/var/static/{global.Name} to global init on pass {pass}"); + compiler.GlobalInitProc.DebugSource(value.Location); + value.EmitPushValue(new(compiler, dmObject, compiler.GlobalInitProc)); + compiler.GlobalInitProc.Assign(DMReference.CreateGlobal(globalId)); } + + return true; } public override string ToString() { @@ -292,36 +281,36 @@ public override string ToString() { } } - public static void AddObjectVar(DreamPath owner, DMASTObjectVarDefinition varDef) { + public void AddObjectVar(DreamPath owner, DMASTObjectVarDefinition varDef) { var node = GetDMObjectNode(owner); var varNode = new ObjectVarNode(owner, varDef); node.Children.Add(varNode); - WaitingNodes.Add(varNode); + _waitingNodes.Add(varNode); } - public static void AddObjectVarOverride(DreamPath owner, DMASTObjectVarOverride varOverride) { + public void AddObjectVarOverride(DreamPath owner, DMASTObjectVarOverride varOverride) { var node = GetDMObjectNode(owner); // parent_type is not an actual var override, and must be applied as soon as the object is created if (varOverride.VarName == "parent_type") { - if (ParentTypes.ContainsKey(owner)) { - DMCompiler.Emit(WarningCode.InvalidOverride, varOverride.Location, + if (_parentTypes.ContainsKey(owner)) { + _compiler.Emit(WarningCode.InvalidOverride, varOverride.Location, $"{owner} already has its parent_type set. This override is ignored."); return; } if (varOverride.Value is not DMASTConstantPath parentType) { - DMCompiler.Emit(WarningCode.BadExpression, varOverride.Value.Location, "Expected a constant path"); + _compiler.Emit(WarningCode.BadExpression, varOverride.Value.Location, "Expected a constant path"); return; } - ParentTypes.Add(owner, parentType.Value.Path); + _parentTypes.Add(owner, parentType.Value.Path); return; } var varNode = new ObjectVarOverrideNode(owner, varOverride); node.Children.Add(varNode); - WaitingNodes.Add(varNode); + _waitingNodes.Add(varNode); } } diff --git a/DMCompiler/DM/DMCodeTree.cs b/DMCompiler/DM/DMCodeTree.cs index af8d85460a..a4a1459b8e 100644 --- a/DMCompiler/DM/DMCodeTree.cs +++ b/DMCompiler/DM/DMCodeTree.cs @@ -10,7 +10,7 @@ namespace DMCompiler.DM; ///
// TODO: "/var" vs "var" has a different init order (same for procs) // TODO: Path elements like /static and /global are grouped together -internal static partial class DMCodeTree { +internal partial class DMCodeTree { private interface INode; private class TypeNode(string name) : INode { @@ -38,30 +38,31 @@ public override string ToString() { } } - private class ObjectNode(string name, DreamPath type) : TypeNode(name) { + private class ObjectNode(DMCodeTree codeTree, string name, DreamPath type) : TypeNode(name) { private bool _defined; private ProcsNode? _procs; - public void DefineType() { + public bool TryDefineType(DMCompiler compiler) { if (_defined) - return; + return true; DMObject? explicitParent = null; - if (ParentTypes.TryGetValue(type, out var parentType) && - !DMObjectTree.TryGetDMObject(parentType, out explicitParent)) - return; // Parent type isn't ready yet + if (codeTree._parentTypes.TryGetValue(type, out var parentType) && + !compiler.DMObjectTree.TryGetDMObject(parentType, out explicitParent)) + return false; // Parent type isn't ready yet _defined = true; - WaitingNodes.Remove(this); - var dmObject = DMObjectTree.GetOrCreateDMObject(type); + var dmObject = compiler.DMObjectTree.GetOrCreateDMObject(type); if (explicitParent != null) { dmObject.Parent = explicitParent; - ParentTypes.Remove(type); + codeTree._parentTypes.Remove(type); } - if (NewProcs.Remove(type, out var newProcNode)) - newProcNode.DefineProc(); + if (codeTree._newProcs.Remove(type, out var newProcNode)) + newProcNode.TryDefineProc(compiler); + + return true; } public ProcsNode AddProcsNode() { @@ -74,44 +75,34 @@ public ProcsNode AddProcsNode() { } } - public static DMProc GlobalInitProc = default!; - - private static readonly HashSet WaitingNodes = new(); - private static readonly Dictionary ParentTypes = new(); - private static readonly Dictionary NewProcs = new(); - - private static ObjectNode _root = default!; - private static ObjectNode? _dmStandardRoot; - private static int _currentPass; + private readonly DMCompiler _compiler; + private readonly HashSet _waitingNodes = new(); + private readonly Dictionary _parentTypes = new(); + private readonly Dictionary _newProcs = new(); + private ObjectNode _root; + private ObjectNode? _dmStandardRoot; + private int _currentPass; - public static void Reset() { + public DMCodeTree(DMCompiler compiler) { // Yep, not _dmStandardRoot // They get switched in FinishDMStandard() - _root = new("/ (DMStandard)", DreamPath.Root); + _root = new(this, "/ (DMStandard)", DreamPath.Root); - GlobalInitProc = new DMProc(-1, DMObjectTree.Root, null); - _dmStandardRoot = null; - _currentPass = 0; - WaitingNodes.Clear(); - ParentTypes.Clear(); - NewProcs.Clear(); - - DMObjectTree.Reset(); + _compiler = compiler; } - public static void DefineEverything() { + public void DefineEverything() { if (_dmStandardRoot == null) FinishDMStandard(); - static void Pass(ObjectNode root) { + void Pass(ObjectNode root) { foreach (var node in TraverseNodes(root)) { - if (node is ObjectNode objectNode) { - objectNode.DefineType(); - } else if (node is ProcNode procNode) { - procNode.DefineProc(); - } else if (node is VarNode varNode) { - varNode.TryDefineVar(); - } + var successful = (node is ObjectNode objectNode && objectNode.TryDefineType(_compiler)) || + (node is ProcNode procNode && procNode.TryDefineProc(_compiler)) || + (node is VarNode varNode && varNode.TryDefineVar(_compiler, _currentPass)); + + if (successful) + _waitingNodes.Remove(node); } } @@ -123,11 +114,11 @@ static void Pass(ObjectNode root) { int lastCount; do { _currentPass++; - lastCount = WaitingNodes.Count; + lastCount = _waitingNodes.Count; Pass(_root); Pass(_dmStandardRoot!); - } while (WaitingNodes.Count < lastCount && WaitingNodes.Count > 0); + } while (_waitingNodes.Count < lastCount && _waitingNodes.Count > 0); // Scope operator pass DMExpressionBuilder.ScopeOperatorEnabled = true; @@ -135,29 +126,29 @@ static void Pass(ObjectNode root) { Pass(_dmStandardRoot!); // If there exists vars that didn't successfully compile, emit their errors - foreach (var node in WaitingNodes) { + foreach (var node in _waitingNodes) { if (node is not VarNode varNode) // TODO: If a type or proc fails? continue; if (varNode.LastError == null) continue; - DMCompiler.Emit(WarningCode.ItemDoesntExist, varNode.LastError.Location, + _compiler.Emit(WarningCode.ItemDoesntExist, varNode.LastError.Location, varNode.LastError.Message); } - GlobalInitProc.ResolveLabels(); + _compiler.GlobalInitProc.ResolveLabels(); } - public static void FinishDMStandard() { + public void FinishDMStandard() { _dmStandardRoot = _root; - _root = new("/", DreamPath.Root); + _root = new(this, "/", DreamPath.Root); } - public static void AddType(DreamPath type) { + public void AddType(DreamPath type) { GetDMObjectNode(type); // Add it to our tree } - public static DreamPath? UpwardSearch(DMObject start, DreamPath search) { + public DreamPath? UpwardSearch(DMObject start, DreamPath search) { var currentPath = start.Path; search.Type = DreamPath.PathType.Relative; @@ -185,13 +176,13 @@ public static void AddType(DreamPath type) { } } - public static void Print() { + public void Print() { PrintNode(_root); if (_dmStandardRoot != null) PrintNode(_dmStandardRoot); } - private static void PrintNode(INode node, int level = 0) { + private void PrintNode(INode node, int level = 0) { if (node is TypeNode typeNode) { Console.Write(new string('\t', level)); Console.WriteLine(typeNode); @@ -205,7 +196,7 @@ private static void PrintNode(INode node, int level = 0) { } } - private static ObjectNode GetDMObjectNode(DreamPath path) { + private ObjectNode GetDMObjectNode(DreamPath path) { var node = _root; for (int i = 0; i < path.Elements.Length; i++) { @@ -213,10 +204,10 @@ private static ObjectNode GetDMObjectNode(DreamPath path) { if (!node.TryGetChild(element, out var childNode)) { var creating = path.FromElements(0, i + 1); - DMCompiler.VerbosePrint($"Adding {creating} to the code tree"); - childNode = new ObjectNode(element, creating); + _compiler.VerbosePrint($"Adding {creating} to the code tree"); + childNode = new ObjectNode(this, element, creating); node.Children.Add(childNode); - WaitingNodes.Add(childNode); + _waitingNodes.Add(childNode); } if (childNode is not ObjectNode objectNode) @@ -228,7 +219,7 @@ private static ObjectNode GetDMObjectNode(DreamPath path) { return node; } - private static IEnumerable TraverseNodes(TypeNode from) { + private IEnumerable TraverseNodes(TypeNode from) { yield return from; foreach (var child in from.Children) { diff --git a/DMCompiler/DM/DMExpression.cs b/DMCompiler/DM/DMExpression.cs index 7b329da3d0..73de3895ba 100644 --- a/DMCompiler/DM/DMExpression.cs +++ b/DMCompiler/DM/DMExpression.cs @@ -1,8 +1,6 @@ using DMCompiler.Bytecode; using System.Diagnostics.CodeAnalysis; using DMCompiler.Compiler; -using DMCompiler.Compiler.DM.AST; -using DMCompiler.DM.Builders; using DMCompiler.DM.Expressions; namespace DMCompiler.DM; @@ -12,45 +10,21 @@ internal abstract class DMExpression(Location location) { public virtual DMComplexValueType ValType => DMValueType.Anything; - // TODO: proc and dmObject can be null, address nullability contract - public static DMExpression Create(DMObject dmObject, DMProc proc, DMASTExpression expression, DreamPath? inferredPath = null) { - var expr = CreateIgnoreUnknownReference(dmObject, proc, expression, inferredPath); - if (expr is UnknownReference unknownRef) - unknownRef.EmitCompilerError(); - - return expr; - } - - public static DMExpression CreateIgnoreUnknownReference(DMObject dmObject, DMProc proc, DMASTExpression expression, DreamPath? inferredPath = null) { - DMExpressionBuilder.EncounteredUnknownReference = null; - return DMExpressionBuilder.BuildExpression(expression, dmObject, proc, inferredPath); - } - - public static void Emit(DMObject dmObject, DMProc proc, DMASTExpression expression, DreamPath? inferredPath = null) { - var expr = Create(dmObject, proc, expression, inferredPath); - expr.EmitPushValue(dmObject, proc); - } - - public static bool TryConstant(DMObject dmObject, DMProc proc, DMASTExpression expression, out Expressions.Constant? constant) { - var expr = Create(dmObject, proc, expression); - return expr.TryAsConstant(out constant); - } - // Attempt to convert this expression into a Constant expression - public virtual bool TryAsConstant([NotNullWhen(true)] out Expressions.Constant? constant) { + public virtual bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { constant = null; return false; } // Attempt to create a json-serializable version of this expression - public virtual bool TryAsJsonRepresentation(out object? json) { + public virtual bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = null; return false; } // Emits code that pushes the result of this expression to the proc's stack // May throw if this expression is unable to be pushed to the stack - public abstract void EmitPushValue(DMObject dmObject, DMProc proc); + public abstract void EmitPushValue(ExpressionContext ctx); public enum ShortCircuitMode { // If a dereference is short-circuited due to a null conditional, the short-circuit label should be jumped to with null NOT on top of the stack @@ -65,8 +39,8 @@ public enum ShortCircuitMode { // Emits a reference that is to be used in an opcode that assigns/gets a value // May throw if this expression is unable to be referenced // The emitted code will jump to endLabel after pushing `null` to the stack in the event of a short-circuit - public virtual DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "attempt to reference r-value"); + public virtual DMReference EmitReference(ExpressionContext ctx, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "attempt to reference r-value"); return DMReference.Invalid; } @@ -74,7 +48,7 @@ public virtual DMReference EmitReference(DMObject dmObject, DMProc proc, string /// Gets the canonical name of the expression if it exists. /// /// The name of the expression, or null if it does not have one. - public virtual string? GetNameof(DMObject dmObject) => null; + public virtual string? GetNameof(ExpressionContext ctx) => null; /// /// Determines whether the expression returns an ambiguous path. @@ -94,16 +68,16 @@ internal sealed class ArgumentList(Location location, (string? Name, DMExpressio public int Length => Expressions.Length; public Location Location = location; - public (DMCallArgumentsType Type, int StackSize) EmitArguments(DMObject dmObject, DMProc proc, DMProc? targetProc) { + public (DMCallArgumentsType Type, int StackSize) EmitArguments(ExpressionContext ctx, DMProc? targetProc) { if (Expressions.Length == 0) { return (DMCallArgumentsType.None, 0); } if (Expressions[0].Expr is Arglist arglist) { if (Expressions[0].Name != null) - DMCompiler.Emit(WarningCode.BadArgument, arglist.Location, "arglist cannot be a named argument"); + ctx.Compiler.Emit(WarningCode.BadArgument, arglist.Location, "arglist cannot be a named argument"); - arglist.EmitPushArglist(dmObject, proc); + arglist.EmitPushArglist(ctx); return (DMCallArgumentsType.FromArgumentList, 1); } @@ -113,24 +87,24 @@ internal sealed class ArgumentList(Location location, (string? Name, DMExpressio (string? name, DMExpression expr) = Expressions[index]; if (targetProc != null) - VerifyArgType(targetProc, index, name, expr); + VerifyArgType(ctx.Compiler, targetProc, index, name, expr); if (isKeyed) { if (name != null) { - proc.PushString(name); + ctx.Proc.PushString(name); } else { - proc.PushNull(); + ctx.Proc.PushNull(); } } - expr.EmitPushValue(dmObject, proc); + expr.EmitPushValue(ctx); stackCount += isKeyed ? 2 : 1; } return (isKeyed ? DMCallArgumentsType.FromStackKeyed : DMCallArgumentsType.FromStack, stackCount); } - private static void VerifyArgType(DMProc targetProc, int index, string? name, DMExpression expr) { + private void VerifyArgType(DMCompiler compiler, DMProc targetProc, int index, string? name, DMExpression expr) { // TODO: See if the static typechecking can be improved // Also right now we don't care if the arg is Anything // TODO: Make a separate "UnsetStaticType" pragma for whether we should care if it's Anything @@ -148,7 +122,7 @@ private static void VerifyArgType(DMProc targetProc, int index, string? name, DM if (param == null) { // TODO: Remove this check once variadic args are properly supported if (targetProc.Name != "animate" && index < targetProc.Parameters.Count) { - DMCompiler.Emit(WarningCode.InvalidVarType, expr.Location, + compiler.Emit(WarningCode.InvalidVarType, expr.Location, $"{targetProc.Name}(...): Unknown argument {(name is null ? $"at index {index}" : $"\"{name}\"")}, typechecking failed"); } @@ -157,9 +131,17 @@ private static void VerifyArgType(DMProc targetProc, int index, string? name, DM DMComplexValueType paramType = param.ExplicitValueType ?? DMValueType.Anything; - if (!expr.ValType.IsAnything && !paramType.MatchesType(expr.ValType)) { - DMCompiler.Emit(WarningCode.InvalidVarType, expr.Location, + if (!expr.ValType.IsAnything && !paramType.MatchesType(compiler, expr.ValType)) { + compiler.Emit(WarningCode.InvalidVarType, expr.Location, $"{targetProc.Name}(...) argument \"{param.Name}\": Invalid var value type {expr.ValType}, expected {paramType}"); } } } + +internal readonly struct ExpressionContext(DMCompiler compiler, DMObject type, DMProc proc) { + public readonly DMCompiler Compiler = compiler; + public readonly DMObject Type = type; + public readonly DMProc Proc = proc; + + public DMObjectTree ObjectTree => Compiler.DMObjectTree; +} diff --git a/DMCompiler/DM/DMObject.cs b/DMCompiler/DM/DMObject.cs index 74bc3e352f..d48d84eb6f 100644 --- a/DMCompiler/DM/DMObject.cs +++ b/DMCompiler/DM/DMObject.cs @@ -8,7 +8,7 @@ namespace DMCompiler.DM; /// but rather stores the compile-time information necessary to describe a certain object definition,
/// including its procs, vars, path, parent, etc. /// -internal sealed class DMObject(int id, DreamPath path, DMObject? parent) { +internal sealed class DMObject(DMCompiler compiler, int id, DreamPath path, DMObject? parent) { public readonly int Id = id; public DreamPath Path = path; public DMObject? Parent = parent; @@ -95,12 +95,12 @@ public bool OwnsProc(string name) { } public DMComplexValueType? GetProcReturnTypes(string name) { - if (this == DMObjectTree.Root && DMObjectTree.TryGetGlobalProc(name, out var globalProc)) + if (this == compiler.DMObjectTree.Root && compiler.DMObjectTree.TryGetGlobalProc(name, out var globalProc)) return globalProc.RawReturnTypes; if (GetProcs(name) is not { } procs) return Parent?.GetProcReturnTypes(name); - var proc = DMObjectTree.AllProcs[procs[0]]; + var proc = compiler.DMObjectTree.AllProcs[procs[0]]; if ((proc.Attributes & ProcAttributes.IsOverride) != 0) return Parent?.GetProcReturnTypes(name) ?? DMValueType.Anything; @@ -108,7 +108,7 @@ public bool OwnsProc(string name) { } public void AddVerb(DMProc verb) { - if (!DMCompiler.Settings.NoStandard && !IsSubtypeOf(DreamPath.Atom) && !IsSubtypeOf(DreamPath.Client)) + if (!compiler.Settings.NoStandard && !IsSubtypeOf(DreamPath.Atom) && !IsSubtypeOf(DreamPath.Client)) return; _verbs ??= []; @@ -145,29 +145,23 @@ public void AddGlobalVariable(DMVariable global, int id) { return Parent?.GetGlobalVariableId(name); } - public DMVariable? GetGlobalVariable(string name) { - int? id = GetGlobalVariableId(name); - - return (id == null) ? null : DMObjectTree.Globals[id.Value]; - } - public DMComplexValueType GetReturnType(string name) { var procId = GetProcs(name)?[^1]; - return procId is null ? DMValueType.Anything : DMObjectTree.AllProcs[procId.Value].ReturnTypes; + return procId is null ? DMValueType.Anything : compiler.DMObjectTree.AllProcs[procId.Value].ReturnTypes; } public void CreateInitializationProc() { if (InitializationProcExpressions.Count <= 0 || InitializationProc != null) return; - var init = DMObjectTree.CreateDMProc(this, null); + var init = compiler.DMObjectTree.CreateDMProc(this, null); InitializationProc = init.Id; init.Call(DMReference.SuperProc, DMCallArgumentsType.None, 0); foreach (DMExpression expression in InitializationProcExpressions) { init.DebugSource(expression.Location); - expression.EmitPushValue(this, init); + expression.EmitPushValue(new(compiler, this, init)); } } @@ -181,14 +175,14 @@ public DreamTypeJson CreateJsonRepresentation() { typeJson.Variables = new Dictionary(); foreach (KeyValuePair variable in Variables) { - if (!variable.Value.TryAsJsonRepresentation(out var valueJson)) + if (!variable.Value.TryAsJsonRepresentation(compiler, out var valueJson)) throw new Exception($"Failed to serialize {Path}.{variable.Key}"); typeJson.Variables.Add(variable.Key, valueJson); } foreach (KeyValuePair variable in VariableOverrides) { - if (!variable.Value.TryAsJsonRepresentation(out var valueJson)) + if (!variable.Value.TryAsJsonRepresentation(compiler, out var valueJson)) throw new Exception($"Failed to serialize {Path}.{variable.Key}"); typeJson.Variables[variable.Key] = valueJson; diff --git a/DMCompiler/DM/DMObjectTree.cs b/DMCompiler/DM/DMObjectTree.cs index 064bb892db..3dad425614 100644 --- a/DMCompiler/DM/DMObjectTree.cs +++ b/DMCompiler/DM/DMObjectTree.cs @@ -5,59 +5,36 @@ namespace DMCompiler.DM; -internal static class DMObjectTree { - public static readonly List AllObjects = new(); - public static readonly List AllProcs = new(); +internal class DMObjectTree(DMCompiler compiler) { + public readonly List AllObjects = new(); + public readonly List AllProcs = new(); //TODO: These don't belong in the object tree - public static readonly List Globals = new(); - public static readonly Dictionary GlobalProcs = new(); - public static readonly List StringTable = new(); - public static readonly HashSet Resources = new(); + public readonly List Globals = new(); + public readonly Dictionary GlobalProcs = new(); + public readonly List StringTable = new(); + public readonly HashSet Resources = new(); - public static DMObject Root => GetOrCreateDMObject(DreamPath.Root); + public DMObject Root => GetOrCreateDMObject(DreamPath.Root); - private static readonly Dictionary StringToStringId = new(); + private readonly Dictionary _stringToStringId = new(); + private readonly Dictionary _pathToTypeId = new(); + private int _dmObjectIdCounter; + private int _dmProcIdCounter; - private static readonly Dictionary _pathToTypeId = new(); - private static int _dmObjectIdCounter; - private static int _dmProcIdCounter; - - static DMObjectTree() { - Reset(); - } - - /// - /// A thousand curses upon you if you add a new member to this thing without deleting it here. - /// - public static void Reset() { - AllObjects.Clear(); - AllProcs.Clear(); - - Globals.Clear(); - GlobalProcs.Clear(); - StringTable.Clear(); - StringToStringId.Clear(); - Resources.Clear(); - - _pathToTypeId.Clear(); - _dmObjectIdCounter = 0; - _dmProcIdCounter = 0; - } - - public static int AddString(string value) { - if (!StringToStringId.TryGetValue(value, out var stringId)) { + public int AddString(string value) { + if (!_stringToStringId.TryGetValue(value, out var stringId)) { stringId = StringTable.Count; StringTable.Add(value); - StringToStringId.Add(value, stringId); + _stringToStringId.Add(value, stringId); } return stringId; } - public static DMProc CreateDMProc(DMObject dmObject, DMASTProcDefinition? astDefinition) { - DMProc dmProc = new DMProc(_dmProcIdCounter++, dmObject, astDefinition); + public DMProc CreateDMProc(DMObject dmObject, DMASTProcDefinition? astDefinition) { + DMProc dmProc = new DMProc(compiler, _dmProcIdCounter++, dmObject, astDefinition); AllProcs.Add(dmProc); return dmProc; @@ -67,7 +44,7 @@ public static DMProc CreateDMProc(DMObject dmObject, DMASTProcDefinition? astDef /// Returns the "New()" DMProc for a given object type ID ///
/// - public static DMProc? GetNewProc(int id) { + public DMProc? GetNewProc(int id) { var obj = AllObjects[id]; var procs = obj.GetProcs("New"); @@ -77,7 +54,7 @@ public static DMProc CreateDMProc(DMObject dmObject, DMASTProcDefinition? astDef return null; } - public static DMObject GetOrCreateDMObject(DreamPath path) { + public DMObject GetOrCreateDMObject(DreamPath path) { if (TryGetDMObject(path, out var dmObject)) return dmObject; @@ -94,7 +71,7 @@ public static DMObject GetOrCreateDMObject(DreamPath path) { parent = GetOrCreateDMObject(DreamPath.Root); break; default: - parent = GetOrCreateDMObject(DMCompiler.Settings.NoStandard ? DreamPath.Root : DreamPath.Datum); + parent = GetOrCreateDMObject(compiler.Settings.NoStandard ? DreamPath.Root : DreamPath.Datum); break; } } @@ -102,13 +79,13 @@ public static DMObject GetOrCreateDMObject(DreamPath path) { if (path != DreamPath.Root && parent == null) // Parent SHOULD NOT be null here! (unless we're root lol) throw new Exception($"Type {path} did not have a parent"); - dmObject = new DMObject(_dmObjectIdCounter++, path, parent); + dmObject = new DMObject(compiler, _dmObjectIdCounter++, path, parent); AllObjects.Add(dmObject); _pathToTypeId[path] = dmObject.Id; return dmObject; } - public static bool TryGetDMObject(DreamPath path, [NotNullWhen(true)] out DMObject? dmObject) { + public bool TryGetDMObject(DreamPath path, [NotNullWhen(true)] out DMObject? dmObject) { if (_pathToTypeId.TryGetValue(path, out int typeId)) { dmObject = AllObjects[typeId]; return true; @@ -118,7 +95,7 @@ public static bool TryGetDMObject(DreamPath path, [NotNullWhen(true)] out DMObje return false; } - public static bool TryGetGlobalProc(string name, [NotNullWhen(true)] out DMProc? proc) { + public bool TryGetGlobalProc(string name, [NotNullWhen(true)] out DMProc? proc) { if (!GlobalProcs.TryGetValue(name, out var id)) { proc = null; return false; @@ -129,12 +106,12 @@ public static bool TryGetGlobalProc(string name, [NotNullWhen(true)] out DMProc? } /// True if the path exists, false if not. Keep in mind though that we may just have not found this object path yet while walking in ObjectBuilder. - public static bool TryGetTypeId(DreamPath path, out int typeId) { + public bool TryGetTypeId(DreamPath path, out int typeId) { return _pathToTypeId.TryGetValue(path, out typeId); } // TODO: This is all so snowflake and needs redone - public static DreamPath? UpwardSearch(DreamPath path, DreamPath search) { + public DreamPath? UpwardSearch(DreamPath path, DreamPath search) { bool requireProcElement = search.Type == DreamPath.PathType.Absolute; string? searchingProcName = null; @@ -185,7 +162,7 @@ public static bool TryGetTypeId(DreamPath path, out int typeId) { return null; } - public static int CreateGlobal(out DMVariable global, DreamPath? type, string name, bool isConst, DMComplexValueType valType) { + public int CreateGlobal(out DMVariable global, DreamPath? type, string name, bool isConst, DMComplexValueType valType) { int id = Globals.Count; global = new DMVariable(type, name, true, isConst, false, valType); @@ -193,16 +170,16 @@ public static int CreateGlobal(out DMVariable global, DreamPath? type, string na return id; } - public static void AddGlobalProc(DMProc proc) { + public void AddGlobalProc(DMProc proc) { if (GlobalProcs.ContainsKey(proc.Name)) { - DMCompiler.Emit(WarningCode.DuplicateProcDefinition, proc.Location, $"Global proc {proc.Name} is already defined"); + compiler.Emit(WarningCode.DuplicateProcDefinition, proc.Location, $"Global proc {proc.Name} is already defined"); return; } GlobalProcs[proc.Name] = proc.Id; } - public static (DreamTypeJson[], ProcDefinitionJson[]) CreateJsonRepresentation() { + public (DreamTypeJson[], ProcDefinitionJson[]) CreateJsonRepresentation() { DreamTypeJson[] types = new DreamTypeJson[AllObjects.Count]; ProcDefinitionJson[] procs = new ProcDefinitionJson[AllProcs.Count]; diff --git a/DMCompiler/DM/DMProc.cs b/DMCompiler/DM/DMProc.cs index 45a55dd73d..376fc8a2bd 100644 --- a/DMCompiler/DM/DMProc.cs +++ b/DMCompiler/DM/DMProc.cs @@ -29,7 +29,7 @@ public sealed class LocalConstVariable(string name, int id, DreamPath? type, Con } public class CodeLabel { - private static int _idCounter = 0; + private static int _idCounter; public readonly long AnnotatedByteOffset; public readonly int Id; public readonly string Name; @@ -76,6 +76,7 @@ public DMProcScope(DMProcScope? parentScope) { public string? VerbDesc; public sbyte Invisibility; + private readonly DMCompiler _compiler; private readonly DMObject _dmObject; private readonly DMASTProcDefinition? _astDefinition; private readonly Stack _pendingLabelReferences = new(); @@ -96,11 +97,13 @@ public DMProcScope(DMProcScope? parentScope) { public DMComplexValueType ReturnTypes => _dmObject.GetProcReturnTypes(Name) ?? DMValueType.Anything; public long Position => AnnotatedBytecode.Position; - public AnnotatedByteCodeWriter AnnotatedBytecode = new(); + public readonly AnnotatedByteCodeWriter AnnotatedBytecode; private Location _writerLocation; - public DMProc(int id, DMObject dmObject, DMASTProcDefinition? astDefinition) { + public DMProc(DMCompiler compiler, int id, DMObject dmObject, DMASTProcDefinition? astDefinition) { + AnnotatedBytecode = new(compiler); + _compiler = compiler; Id = id; _dmObject = dmObject; _astDefinition = astDefinition; @@ -129,10 +132,10 @@ private void DeallocLocalVariables(int amount) { } public void Compile() { - DMCompiler.VerbosePrint($"Compiling proc {_dmObject?.Path.ToString() ?? "Unknown"}.{Name}()"); + _compiler.VerbosePrint($"Compiling proc {_dmObject?.Path.ToString() ?? "Unknown"}.{Name}()"); if (_astDefinition is not null) { // It's null for initialization procs - new DMProcBuilder(_dmObject, this).ProcessProcDefinition(_astDefinition); + new DMProcBuilder(_compiler, _dmObject, this).ProcessProcDefinition(_astDefinition); } } @@ -140,37 +143,37 @@ public void ValidateReturnType(DMExpression expr) { var type = expr.ValType; var returnTypes = _dmObject.GetProcReturnTypes(Name)!.Value; if ((returnTypes.Type & (DMValueType.Color | DMValueType.File | DMValueType.Message)) != 0) { - DMCompiler.Emit(WarningCode.UnsupportedTypeCheck, expr.Location, "color, message, and file return types are currently unsupported."); + _compiler.Emit(WarningCode.UnsupportedTypeCheck, expr.Location, "color, message, and file return types are currently unsupported."); return; } var splitter = _astDefinition?.IsOverride ?? false ? "/" : "/proc/"; // We couldn't determine the expression's return type for whatever reason if (type.IsAnything) { - if (DMCompiler.Settings.SkipAnythingTypecheck) + if (_compiler.Settings.SkipAnythingTypecheck) return; switch (expr) { case ProcCall: - DMCompiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Called proc does not have a return type set, expected {ReturnTypes}."); + _compiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Called proc does not have a return type set, expected {ReturnTypes}."); break; case Local: - DMCompiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Cannot determine return type of non-constant expression, expected {ReturnTypes}. Consider making this variable constant or adding an explicit \"as {ReturnTypes}\""); + _compiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Cannot determine return type of non-constant expression, expected {ReturnTypes}. Consider making this variable constant or adding an explicit \"as {ReturnTypes}\""); break; default: - DMCompiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Cannot determine return type of expression \"{expr}\", expected {ReturnTypes}. Consider reporting this as a bug on OpenDream's GitHub."); + _compiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}.{Name}(): Cannot determine return type of expression \"{expr}\", expected {ReturnTypes}. Consider reporting this as a bug on OpenDream's GitHub."); break; } - } else if (!ReturnTypes.MatchesType(type)) { // We could determine the return types but they don't match - DMCompiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}{splitter}{Name}(): Invalid return type {type}, expected {ReturnTypes}"); + } else if (!ReturnTypes.MatchesType(_compiler, type)) { // We could determine the return types but they don't match + _compiler.Emit(WarningCode.InvalidReturnType, expr.Location, $"{_dmObject?.Path.ToString() ?? "Unknown"}{splitter}{Name}(): Invalid return type {type}, expected {ReturnTypes}"); } } public ProcDefinitionJson GetJsonRepresentation() { var optimizer = new BytecodeOptimizer(); - var serializer = new AnnotatedBytecodeSerializer(); + var serializer = new AnnotatedBytecodeSerializer(_compiler); - optimizer.Optimize(AnnotatedBytecode.GetAnnotatedBytecode()); + optimizer.Optimize(_compiler, AnnotatedBytecode.GetAnnotatedBytecode()); List? arguments = null; if (_parameters.Count > 0) { @@ -182,7 +185,7 @@ public ProcDefinitionJson GetJsonRepresentation() { if (parameter.Type is not { } typePath) { argumentType = DMValueType.Anything; } else { - DMObjectTree.TryGetDMObject(typePath, out var type); + _compiler.DMObjectTree.TryGetDMObject(typePath, out var type); argumentType = type?.GetDMValueType() ?? DMValueType.Anything; } } @@ -242,7 +245,7 @@ public void AddGlobalVariable(DMVariable global, int id) { public void AddParameter(string name, DMComplexValueType? valueType, DreamPath? type) { if (_parameters.ContainsKey(name)) { - DMCompiler.Emit(WarningCode.DuplicateVariable, _astDefinition.Location, $"Duplicate argument \"{name}\""); + _compiler.Emit(WarningCode.DuplicateVariable, _astDefinition.Location, $"Duplicate argument \"{name}\""); } else { Parameters.Add(name); _parameters.Add(name, new LocalVariable(name, _parameters.Count, true, type, valueType)); @@ -267,7 +270,7 @@ public bool TryGetParameterAtIndex(int index, [NotNullWhen(true)] out LocalVaria public CodeLabel? TryAddCodeLabel(string name) { if (_scopes.Peek().LocalCodeLabels.ContainsKey(name)) { - DMCompiler.Emit(WarningCode.DuplicateVariable, Location, $"A label with the name \"{name}\" already exists"); + _compiler.Emit(WarningCode.DuplicateVariable, Location, $"A label with the name \"{name}\" already exists"); return null; } @@ -329,7 +332,7 @@ public void DebugSource(Location location) { // Only write the source file if it has changed if (_lastSourceFile != sourceFile) { - sourceInfo.File = DMObjectTree.AddString(sourceFile); + sourceInfo.File = _compiler.DMObjectTree.AddString(sourceFile); } else if (_sourceInfo.Count > 0 && sourceInfo.Line == _sourceInfo[^1].Line) { // Don't need to write this source info if it's the same source & line as the last return; @@ -372,7 +375,7 @@ public void Enumerate(DMReference reference) { WriteReference(reference); WriteLabel($"{peek}_end"); } else { - DMCompiler.ForcedError(Location, "Cannot peek empty loop stack"); + _compiler.ForcedError(Location, "Cannot peek empty loop stack"); } } @@ -382,7 +385,7 @@ public void EnumerateNoAssign() { WriteEnumeratorId(_enumeratorIdCounter - 1); WriteLabel($"{peek}_end"); } else { - DMCompiler.ForcedError(Location, "Cannot peek empty loop stack"); + _compiler.ForcedError(Location, "Cannot peek empty loop stack"); } } @@ -429,8 +432,8 @@ public void BackgroundSleep() { // TODO This seems like a bad way to handle background, doesn't it? if ((Attributes & ProcAttributes.Background) == ProcAttributes.Background) { - if (!DMObjectTree.TryGetGlobalProc("sleep", out var sleepProc)) { - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, "Cannot do a background sleep without a sleep proc"); + if (!_compiler.DMObjectTree.TryGetGlobalProc("sleep", out var sleepProc)) { + _compiler.Emit(WarningCode.ItemDoesntExist, Location, "Cannot do a background sleep without a sleep proc"); return; } @@ -449,7 +452,7 @@ public void LoopEnd() { if (_loopStack?.TryPop(out var pop) ?? false) { AddLabel(pop + "_end"); } else { - DMCompiler.ForcedError(Location, "Cannot pop empty loop stack"); + _compiler.ForcedError(Location, "Cannot pop empty loop stack"); } EndScope(); @@ -505,14 +508,14 @@ public void Break(DMASTIdentifier? label = null) { if (label is not null) { var codeLabel = (GetCodeLabel(label.Identifier, _scopes.Peek())?.LabelName ?? label.Identifier + "_codelabel"); if (!LabelExists(codeLabel)) { - DMCompiler.Emit(WarningCode.ItemDoesntExist, label.Location, $"Unknown label {label.Identifier}"); + _compiler.Emit(WarningCode.ItemDoesntExist, label.Location, $"Unknown label {label.Identifier}"); } Jump(codeLabel + "_end"); } else if (_loopStack?.TryPeek(out var peek) ?? false) { Jump(peek + "_end"); } else { - DMCompiler.ForcedError(Location, "Cannot peek empty loop stack"); + _compiler.ForcedError(Location, "Cannot peek empty loop stack"); } } @@ -520,7 +523,7 @@ public void BreakIfFalse() { if (_loopStack?.TryPeek(out var peek) ?? false) { JumpIfFalse($"{peek}_end"); } else { - DMCompiler.ForcedError(Location, "Cannot peek empty loop stack"); + _compiler.ForcedError(Location, "Cannot peek empty loop stack"); } } @@ -533,7 +536,7 @@ public void Continue(DMASTIdentifier? label = null) { label.Identifier + "_codelabel" ); if (!LabelExists(codeLabel)) { - DMCompiler.Emit(WarningCode.ItemDoesntExist, label.Location, $"Unknown label {label.Identifier}"); + _compiler.Emit(WarningCode.ItemDoesntExist, label.Location, $"Unknown label {label.Identifier}"); } var labelList = GetLabels().Keys.ToList(); @@ -553,7 +556,7 @@ public void Continue(DMASTIdentifier? label = null) { if (_loopStack?.TryPeek(out var peek) ?? false) { Jump(peek + "_continue"); } else { - DMCompiler.ForcedError(Location, "Cannot peek empty loop stack"); + _compiler.ForcedError(Location, "Cannot peek empty loop stack"); } } } diff --git a/DMCompiler/DM/DMValueType.cs b/DMCompiler/DM/DMValueType.cs index 705867b077..09c1b0b157 100644 --- a/DMCompiler/DM/DMValueType.cs +++ b/DMCompiler/DM/DMValueType.cs @@ -56,9 +56,9 @@ public bool MatchesType(DMValueType type) { return IsAnything || (Type & type) != 0; } - public bool MatchesType(DMComplexValueType type) { + internal bool MatchesType(DMCompiler compiler, DMComplexValueType type) { if (IsPath && type.IsPath) { - if (DMObjectTree.TryGetDMObject(type.TypePath!.Value, out var dmObject) && + if (compiler.DMObjectTree.TryGetDMObject(type.TypePath!.Value, out var dmObject) && dmObject.IsSubtypeOf(TypePath!.Value)) // Allow subtypes return true; } diff --git a/DMCompiler/DM/DMVariable.cs b/DMCompiler/DM/DMVariable.cs index 7140703e90..ece888fe82 100644 --- a/DMCompiler/DM/DMVariable.cs +++ b/DMCompiler/DM/DMVariable.cs @@ -34,7 +34,7 @@ public DMVariable(DMVariable copyFrom) { ValType = copyFrom.ValType; } - public bool TryAsJsonRepresentation([NotNullWhen(true)] out object? valueJson) { - return Value.TryAsJsonRepresentation(out valueJson); + public bool TryAsJsonRepresentation(DMCompiler compiler, [NotNullWhen(true)] out object? valueJson) { + return Value.TryAsJsonRepresentation(compiler, out valueJson); } } diff --git a/DMCompiler/DM/Expressions/Binary.cs b/DMCompiler/DM/Expressions/Binary.cs index 1b05d20317..0d9420faeb 100644 --- a/DMCompiler/DM/Expressions/Binary.cs +++ b/DMCompiler/DM/Expressions/Binary.cs @@ -13,10 +13,11 @@ internal abstract class BinaryOp(Location location, DMExpression lhs, DMExpressi } #region Simple + // x + y internal sealed class Add(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -33,17 +34,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Add(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Add(); } } // x - y internal sealed class Subtract(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -58,17 +59,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Subtract(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Subtract(); } } // x * y internal sealed class Multiply(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -83,17 +84,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Multiply(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Multiply(); } } // x / y internal sealed class Divide(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -108,17 +109,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Divide(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Divide(); } } // x % y internal sealed class Modulo(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -133,17 +134,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Modulus(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Modulus(); } } // x %% y internal sealed class ModuloModulo(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -161,17 +162,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.ModulusModulus(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.ModulusModulus(); } } // x ** y internal sealed class Power(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -186,17 +187,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Power(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Power(); } } // x << y internal sealed class LeftShift(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -211,17 +212,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.BitShiftLeft(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.BitShiftLeft(); } } // x >> y internal sealed class RightShift(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -236,10 +237,10 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.BitShiftRight(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.BitShiftRight(); } } @@ -247,8 +248,8 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class BinaryAnd(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { public override bool PathIsFuzzy => true; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -263,17 +264,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.BinaryAnd(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.BinaryAnd(); } } // x ^ y internal sealed class BinaryXor(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -288,17 +289,17 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.BinaryXor(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.BinaryXor(); } } // x | y internal sealed class BinaryOr(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -313,53 +314,53 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.BinaryOr(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.BinaryOr(); } } // x == y internal sealed class Equal(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Equal(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Equal(); } } // x != y internal sealed class NotEqual(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.NotEqual(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.NotEqual(); } } // x ~= y internal sealed class Equivalent(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.Equivalent(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.Equivalent(); } } // x ~! y internal sealed class NotEquivalent(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.NotEquivalent(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.NotEquivalent(); } } // x > y internal sealed class GreaterThan(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -376,23 +377,23 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.GreaterThan(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.GreaterThan(); } } // x >= y internal sealed class GreaterThanOrEqual(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.GreaterThanOrEqual(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.GreaterThanOrEqual(); } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -410,17 +411,16 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { } } - // x < y internal sealed class LessThan(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.LessThan(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.LessThan(); } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -440,14 +440,14 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { // x <= y internal sealed class LessThanOrEqual(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.LessThanOrEqual(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.LessThanOrEqual(); } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!LHS.TryAsConstant(out var lhs) || !RHS.TryAsConstant(out var rhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!LHS.TryAsConstant(compiler, out var lhs) || !RHS.TryAsConstant(compiler, out var rhs)) { constant = null; return false; } @@ -467,14 +467,14 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { // x || y internal sealed class Or(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (LHS.TryAsConstant(out var lhs)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (LHS.TryAsConstant(compiler, out var lhs)) { if (lhs.IsTruthy()) { constant = lhs; return true; } - if (RHS.TryAsConstant(out var rhs)) { + if (RHS.TryAsConstant(compiler, out var rhs)) { constant = rhs; return true; } @@ -484,25 +484,25 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return false; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushValue(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - LHS.EmitPushValue(dmObject, proc); - proc.BooleanOr(endLabel); - RHS.EmitPushValue(dmObject, proc); - proc.AddLabel(endLabel); + LHS.EmitPushValue(ctx); + ctx.Proc.BooleanOr(endLabel); + RHS.EmitPushValue(ctx); + ctx.Proc.AddLabel(endLabel); } } // x && y internal sealed class And(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (LHS.TryAsConstant(out var lhs) && !lhs.IsTruthy()) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (LHS.TryAsConstant(compiler, out var lhs) && !lhs.IsTruthy()) { constant = lhs; return true; } - if (RHS.TryAsConstant(out var rhs)) { + if (RHS.TryAsConstant(compiler, out var rhs)) { constant = rhs; return true; } @@ -511,43 +511,47 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return false; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushValue(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - LHS.EmitPushValue(dmObject, proc); - proc.BooleanAnd(endLabel); - RHS.EmitPushValue(dmObject, proc); - proc.AddLabel(endLabel); + LHS.EmitPushValue(ctx); + ctx.Proc.BooleanAnd(endLabel); + RHS.EmitPushValue(ctx); + ctx.Proc.AddLabel(endLabel); } } // x in y internal sealed class In(Location location, DMExpression expr, DMExpression container) : BinaryOp(location, expr, container) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - LHS.EmitPushValue(dmObject, proc); - RHS.EmitPushValue(dmObject, proc); - proc.IsInList(); + public override void EmitPushValue(ExpressionContext ctx) { + LHS.EmitPushValue(ctx); + RHS.EmitPushValue(ctx); + ctx.Proc.IsInList(); } } + #endregion #region Compound Assignment + internal abstract class AssignmentBinaryOp(Location location, DMExpression lhs, DMExpression rhs) : BinaryOp(location, lhs, rhs) { /// /// Generic interface for emitting the assignment operation. Has its conditionality and reference generation already handled. /// /// You should always make use of the reference argument, unless you totally override AssignmentBinaryOp's EmitPushValue method. - /// A reference to the LHS emitted via - protected abstract void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel); + /// + /// A reference to the LHS emitted via + /// + protected abstract void EmitOp(ExpressionContext ctx, DMReference reference, string endLabel); - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushValue(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - DMReference reference = LHS.EmitReference(dmObject, proc, endLabel); - EmitOp(dmObject, proc, reference, endLabel); + DMReference reference = LHS.EmitReference(ctx, endLabel); + EmitOp(ctx, reference, endLabel); - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } } @@ -555,15 +559,15 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class Assignment(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { public override DreamPath? Path => LHS.Path; - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.Assign(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.Assign(reference); - if (!LHS.ValType.MatchesType(RHS.ValType) && !LHS.ValType.IsUnimplemented) { - if (DMCompiler.Settings.SkipAnythingTypecheck && RHS.ValType.IsAnything) + if (!LHS.ValType.MatchesType(ctx.Compiler, RHS.ValType) && !LHS.ValType.IsUnimplemented) { + if (ctx.Compiler.Settings.SkipAnythingTypecheck && RHS.ValType.IsAnything) return; - DMCompiler.Emit(WarningCode.InvalidVarType, Location, + ctx.Compiler.Emit(WarningCode.InvalidVarType, Location, $"Invalid var type {RHS.ValType}, expected {LHS.ValType}"); } } @@ -573,115 +577,130 @@ protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference refer internal sealed class AssignmentInto(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { public override DreamPath? Path => LHS.Path; - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.AssignInto(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.AssignInto(reference); } } // x += y internal sealed class Append(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.Append(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.Append(reference); } } // x |= y internal sealed class Combine(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.Combine(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.Combine(reference); } } // x -= y internal sealed class Remove(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.Remove(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.Remove(reference); } } // x &= y internal sealed class Mask(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.Mask(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.Mask(reference); } } // x &&= y internal sealed class LogicalAndAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - proc.JumpIfFalseReference(reference, endLabel); - RHS.EmitPushValue(dmObject, proc); - proc.Assign(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + ctx.Proc.JumpIfFalseReference(reference, endLabel); + RHS.EmitPushValue(ctx); + ctx.Proc.Assign(reference); } } // x ||= y internal sealed class LogicalOrAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - proc.JumpIfTrueReference(reference, endLabel); - RHS.EmitPushValue(dmObject, proc); - proc.Assign(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + ctx.Proc.JumpIfTrueReference(reference, endLabel); + RHS.EmitPushValue(ctx); + ctx.Proc.Assign(reference); } } // x *= y internal sealed class MultiplyAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.MultiplyReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.MultiplyReference(reference); } } // x /= y internal sealed class DivideAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.DivideReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.DivideReference(reference); } } // x <<= y internal sealed class LeftShiftAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.BitShiftLeftReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.BitShiftLeftReference(reference); } } // x >>= y internal sealed class RightShiftAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.BitShiftRightReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.BitShiftRightReference(reference); } } // x ^= y internal sealed class XorAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.BinaryXorReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.BinaryXorReference(reference); } } // x %= y internal sealed class ModulusAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.ModulusReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.ModulusReference(reference); } } // x %%= y internal sealed class ModulusModulusAssign(Location location, DMExpression lhs, DMExpression rhs) : AssignmentBinaryOp(location, lhs, rhs) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { - RHS.EmitPushValue(dmObject, proc); - proc.ModulusModulusReference(reference); + protected override void EmitOp(ExpressionContext ctx, DMReference reference, + string endLabel) { + RHS.EmitPushValue(ctx); + ctx.Proc.ModulusModulusReference(reference); } } + #endregion diff --git a/DMCompiler/DM/Expressions/Builtins.cs b/DMCompiler/DM/Expressions/Builtins.cs index 5c0629bbf7..4b7ec230b6 100644 --- a/DMCompiler/DM/Expressions/Builtins.cs +++ b/DMCompiler/DM/Expressions/Builtins.cs @@ -10,26 +10,26 @@ namespace DMCompiler.DM.Expressions; /// /// Emit an error code before creating! internal sealed class BadExpression(Location location) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { // It's normal to have this expression exist when there are errors in the code // But in the runtime we say it's a compiler bug because the compiler should never have output it - proc.PushString("Encountered a bad expression (compiler bug!)"); - proc.Throw(); + ctx.Proc.PushString("Encountered a bad expression (compiler bug!)"); + ctx.Proc.Throw(); } } internal sealed class UnknownReference(Location location, string message) : DMExpression(location) { public string Message => message; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { // It's normal to have this expression exist when there's out-of-order definitions in the code // But in the runtime we say it's a compiler bug because the compiler should never have output it - proc.PushString("Encountered an unknown reference expression (compiler bug!)"); - proc.Throw(); + ctx.Proc.PushString("Encountered an unknown reference expression (compiler bug!)"); + ctx.Proc.Throw(); } - public void EmitCompilerError() { - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, message); + public void EmitCompilerError(DMCompiler compiler) { + compiler.Emit(WarningCode.ItemDoesntExist, Location, message); } } @@ -37,67 +37,68 @@ public void EmitCompilerError() { internal sealed class StringFormat(Location location, string value, DMExpression[] expressions) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Text; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { foreach (DMExpression expression in expressions) { - expression.EmitPushValue(dmObject, proc); + expression.EmitPushValue(ctx); } - proc.FormatString(value); + ctx.Proc.FormatString(value); } } // arglist(...) internal sealed class Arglist(Location location, DMExpression expr) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "invalid use of arglist"); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "invalid use of arglist"); } - public void EmitPushArglist(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); + public void EmitPushArglist(ExpressionContext ctx) { + expr.EmitPushValue(ctx); } } // new x (...) -internal sealed class New(Location location, DMExpression expr, ArgumentList arguments) : DMExpression(location) { +internal sealed class New(DMCompiler compiler, Location location, DMExpression expr, ArgumentList arguments) : DMExpression(location) { + public override DreamPath? Path => expr.Path; public override bool PathIsFuzzy => Path == null; - public override DMComplexValueType ValType => !expr.ValType.IsAnything ? expr.ValType : (Path?.GetAtomType() ?? DMValueType.Anything); + public override DMComplexValueType ValType => !expr.ValType.IsAnything ? expr.ValType : (Path?.GetAtomType(compiler) ?? DMValueType.Anything); - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - var argumentInfo = arguments.EmitArguments(dmObject, proc, null); + public override void EmitPushValue(ExpressionContext ctx) { + var argumentInfo = arguments.EmitArguments(ctx, null); - expr.EmitPushValue(dmObject, proc); - proc.CreateObject(argumentInfo.Type, argumentInfo.StackSize); + expr.EmitPushValue(ctx); + ctx.Proc.CreateObject(argumentInfo.Type, argumentInfo.StackSize); } } // new /x/y/z (...) -internal sealed class NewPath(Location location, IConstantPath create, ArgumentList arguments) : DMExpression(location) { +internal sealed class NewPath(DMCompiler compiler, Location location, IConstantPath create, ArgumentList arguments) : DMExpression(location) { public override DreamPath? Path => (create is ConstantTypeReference typeReference) ? typeReference.Path : null; - public override DMComplexValueType ValType => Path?.GetAtomType() ?? DMValueType.Anything; + public override DMComplexValueType ValType => Path?.GetAtomType(compiler) ?? DMValueType.Anything; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { DMCallArgumentsType argumentsType; int stackSize; switch (create) { case ConstantTypeReference typeReference: - // TODO: This might give us null depending on how definition order goes - var newProc = DMObjectTree.GetNewProc(typeReference.Value.Id); + // ctx: This might give us null depending on how definition order goes + var newProc = ctx.ObjectTree.GetNewProc(typeReference.Value.Id); - (argumentsType, stackSize) = arguments.EmitArguments(dmObject, proc, newProc); - proc.PushType(typeReference.Value.Id); + (argumentsType, stackSize) = arguments.EmitArguments(ctx, newProc); + ctx.Proc.PushType(typeReference.Value.Id); break; case ConstantProcReference procReference: // "new /proc/new_verb(Destination)" is a thing - (argumentsType, stackSize) = arguments.EmitArguments(dmObject, proc, DMObjectTree.AllProcs[procReference.Value.Id]); - proc.PushProc(procReference.Value.Id); + (argumentsType, stackSize) = arguments.EmitArguments(ctx, ctx.ObjectTree.AllProcs[procReference.Value.Id]); + ctx.Proc.PushProc(procReference.Value.Id); break; default: - DMCompiler.Emit(WarningCode.BadExpression, Location, $"Cannot instantiate {create}"); - proc.PushNull(); + ctx.Compiler.Emit(WarningCode.BadExpression, Location, $"Cannot instantiate {create}"); + ctx.Proc.PushNull(); return; } - proc.CreateObject(argumentsType, stackSize); + ctx.Proc.CreateObject(argumentsType, stackSize); } } @@ -105,28 +106,28 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class LocateInferred(Location location, DreamPath path, DMExpression? container) : DMExpression(location) { public override DMComplexValueType ValType => path; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - if (!DMObjectTree.TryGetTypeId(path, out var typeId)) { - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, $"Type {path} does not exist"); + 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; } - proc.PushType(typeId); + ctx.Proc.PushType(typeId); if (container != null) { - container.EmitPushValue(dmObject, proc); + container.EmitPushValue(ctx); } else { - if (DMCompiler.Settings.NoStandard) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "Implicit locate() container is not available with --no-standard"); - proc.Error(); + if (ctx.Compiler.Settings.NoStandard) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Implicit locate() container is not available with --no-standard"); + ctx.Proc.Error(); return; } - proc.PushReferenceValue(DMReference.World); + ctx.Proc.PushReferenceValue(DMReference.World); } - proc.Locate(); + ctx.Proc.Locate(); } } @@ -134,22 +135,22 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class Locate(Location location, DMExpression path, DMExpression? container) : DMExpression(location) { public override bool PathIsFuzzy => true; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - path.EmitPushValue(dmObject, proc); + public override void EmitPushValue(ExpressionContext ctx) { + path.EmitPushValue(ctx); if (container != null) { - container.EmitPushValue(dmObject, proc); + container.EmitPushValue(ctx); } else { - if (DMCompiler.Settings.NoStandard) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "Implicit locate() container is not available with --no-standard"); - proc.Error(); + if (ctx.Compiler.Settings.NoStandard) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Implicit locate() container is not available with --no-standard"); + ctx.Proc.Error(); return; } - proc.PushReferenceValue(DMReference.World); + ctx.Proc.PushReferenceValue(DMReference.World); } - proc.Locate(); + ctx.Proc.Locate(); } } @@ -157,22 +158,22 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class LocateCoordinates(Location location, DMExpression x, DMExpression y, DMExpression z) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Turf; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - x.EmitPushValue(dmObject, proc); - y.EmitPushValue(dmObject, proc); - z.EmitPushValue(dmObject, proc); - proc.LocateCoordinates(); + public override void EmitPushValue(ExpressionContext ctx) { + x.EmitPushValue(ctx); + y.EmitPushValue(ctx); + z.EmitPushValue(ctx); + ctx.Proc.LocateCoordinates(); } } // gradient(Gradient, index) // gradient(Item1, Item2, ..., index) internal sealed class Gradient(Location location, ArgumentList arguments) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMObjectTree.TryGetGlobalProc("gradient", out var dmProc); - var argInfo = arguments.EmitArguments(dmObject, proc, dmProc); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.ObjectTree.TryGetGlobalProc("gradient", out var dmProc); + var argInfo = arguments.EmitArguments(ctx, dmProc); - proc.Gradient(argInfo.Type, argInfo.StackSize); + ctx.Proc.Gradient(argInfo.Type, argInfo.StackSize); } } @@ -181,11 +182,11 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { /// rgb(x, y, z, space) /// rgb(x, y, z, a, space) internal sealed class Rgb(Location location, ArgumentList arguments) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMObjectTree.TryGetGlobalProc("rgb", out var dmProc); - var argInfo = arguments.EmitArguments(dmObject, proc, dmProc); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.ObjectTree.TryGetGlobalProc("rgb", out var dmProc); + var argInfo = arguments.EmitArguments(ctx, dmProc); - proc.Rgb(argInfo.Type, argInfo.StackSize); + ctx.Proc.Rgb(argInfo.Type, argInfo.StackSize); } } @@ -198,7 +199,7 @@ public struct PickValue(DMExpression? weight, DMExpression value) { public readonly DMExpression Value = value; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { bool weighted = false; foreach (PickValue pickValue in values) { if (pickValue.Weight != null) { @@ -209,31 +210,31 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { if (weighted) { if (values.Length == 1) { - DMCompiler.ForcedWarning(Location, "Weighted pick() with one argument"); + ctx.Compiler.ForcedWarning(Location, "Weighted pick() with one argument"); } - DMCompiler.Emit(WarningCode.PickWeightedSyntax, Location, "Use of weighted pick() syntax"); + ctx.Compiler.Emit(WarningCode.PickWeightedSyntax, Location, "Use of weighted pick() syntax"); foreach (PickValue pickValue in values) { DMExpression weight = pickValue.Weight ?? new Number(Location.Internal, 100); //Default of 100 - weight.EmitPushValue(dmObject, proc); - pickValue.Value.EmitPushValue(dmObject, proc); + weight.EmitPushValue(ctx); + pickValue.Value.EmitPushValue(ctx); } - proc.PickWeighted(values.Length); + ctx.Proc.PickWeighted(values.Length); } else { foreach (PickValue pickValue in values) { if (pickValue.Value is Arglist args) { // This will just push a list which pick() accepts // Really hacky and won't verify that the value is actually a list - args.EmitPushArglist(dmObject, proc); + args.EmitPushArglist(ctx); } else { - pickValue.Value.EmitPushValue(dmObject, proc); + pickValue.Value.EmitPushValue(ctx); } } - proc.PickUnweighted(values.Length); + ctx.Proc.PickUnweighted(values.Length); } } } @@ -243,15 +244,15 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class AddText(Location location, DMExpression[] paras) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Text; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { //We don't have to do any checking of our parameters since that was already done by VisitAddText(), hopefully. :) //Push addtext()'s arguments foreach (DMExpression parameter in paras) { - parameter.EmitPushValue(dmObject, proc); + parameter.EmitPushValue(ctx); } - proc.MassConcatenation(paras.Length); + ctx.Proc.MassConcatenation(paras.Length); } } @@ -261,9 +262,9 @@ internal sealed class Prob(Location location, DMExpression p) : DMExpression(loc public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - P.EmitPushValue(dmObject, proc); - proc.Prob(); + public override void EmitPushValue(ExpressionContext ctx) { + P.EmitPushValue(ctx); + ctx.Proc.Prob(); } } @@ -271,20 +272,20 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class IsSaved(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { switch (expr) { case Dereference deref: - deref.EmitPushIsSaved(dmObject, proc); + deref.EmitPushIsSaved(ctx); return; case Field field: - field.EmitPushIsSaved(proc); + field.EmitPushIsSaved(ctx.Proc); return; case Local: - proc.PushFloat(0); + ctx.Proc.PushFloat(0); return; default: - DMCompiler.Emit(WarningCode.BadArgument, expr.Location, $"can't get saved value of {expr}"); - proc.Error(); + ctx.Compiler.Emit(WarningCode.BadArgument, expr.Location, $"can't get saved value of {expr}"); + ctx.Proc.Error(); return; } } @@ -294,10 +295,10 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class IsType(Location location, DMExpression expr, DMExpression path) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - path.EmitPushValue(dmObject, proc); - proc.IsType(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + path.EmitPushValue(ctx); + ctx.Proc.IsType(); } } @@ -305,16 +306,16 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class IsTypeInferred(Location location, DMExpression expr, DreamPath path) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - if (!DMObjectTree.TryGetTypeId(path, out var typeId)) { - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, $"Type {path} does not exist"); + 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(dmObject, proc); - proc.PushType(typeId); - proc.IsType(); + expr.EmitPushValue(ctx); + ctx.Proc.PushType(typeId); + ctx.Proc.IsType(); } } @@ -323,9 +324,9 @@ internal sealed class IsNull(Location location, DMExpression value) : DMExpressi public override bool PathIsFuzzy => true; public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - value.EmitPushValue(dmObject, proc); - proc.IsNull(); + public override void EmitPushValue(ExpressionContext ctx) { + value.EmitPushValue(ctx); + ctx.Proc.IsNull(); } } @@ -334,9 +335,9 @@ internal sealed class Length(Location location, DMExpression value) : DMExpressi public override bool PathIsFuzzy => true; public override DMComplexValueType ValType => DMValueType.Num; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - value.EmitPushValue(dmObject, proc); - proc.Length(); + public override void EmitPushValue(ExpressionContext ctx) { + value.EmitPushValue(ctx); + ctx.Proc.Length(); } } @@ -344,10 +345,10 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class GetStep(Location location, DMExpression refValue, DMExpression dir) : DMExpression(location) { public override bool PathIsFuzzy => true; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - refValue.EmitPushValue(dmObject, proc); - dir.EmitPushValue(dmObject, proc); - proc.GetStep(); + public override void EmitPushValue(ExpressionContext ctx) { + refValue.EmitPushValue(ctx); + dir.EmitPushValue(ctx); + ctx.Proc.GetStep(); } } @@ -355,10 +356,10 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class GetDir(Location location, DMExpression loc1, DMExpression loc2) : DMExpression(location) { public override bool PathIsFuzzy => true; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - loc1.EmitPushValue(dmObject, proc); - loc2.EmitPushValue(dmObject, proc); - proc.GetDir(); + public override void EmitPushValue(ExpressionContext ctx) { + loc1.EmitPushValue(ctx); + loc2.EmitPushValue(ctx); + ctx.Proc.GetDir(); } } @@ -382,38 +383,38 @@ public List(Location location, (DMExpression? Key, DMExpression Value)[] values) } } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { foreach (var value in _values) { if (_isAssociative) { if (value.Key == null) { - proc.PushNull(); + ctx.Proc.PushNull(); } else { - value.Key.EmitPushValue(dmObject, proc); + value.Key.EmitPushValue(ctx); } } - value.Value.EmitPushValue(dmObject, proc); + value.Value.EmitPushValue(ctx); } if (_isAssociative) { - proc.CreateAssociativeList(_values.Length); + ctx.Proc.CreateAssociativeList(_values.Length); } else { - proc.CreateList(_values.Length); + ctx.Proc.CreateList(_values.Length); } } - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { List values = new(); foreach (var value in _values) { - if (!value.Value.TryAsJsonRepresentation(out var jsonValue)) { + if (!value.Value.TryAsJsonRepresentation(compiler, out var jsonValue)) { json = null; return false; } if (value.Key != null) { // Null key is not supported here - if (!value.Key.TryAsJsonRepresentation(out var jsonKey) || jsonKey == null) { + if (!value.Key.TryAsJsonRepresentation(compiler, out var jsonKey) || jsonKey == null) { json = null; return false; } @@ -438,13 +439,13 @@ public override bool TryAsJsonRepresentation(out object? json) { // Value of var/list/L[1][2][3] internal sealed class DimensionalList(Location location, DMExpression[] sizes) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { foreach (var size in sizes) { - size.EmitPushValue(dmObject, proc); + size.EmitPushValue(ctx); } // Should be equivalent to new /list(1, 2, 3) - proc.CreateMultidimensionalList(sizes.Length); + ctx.Proc.CreateMultidimensionalList(sizes.Length); } } @@ -452,19 +453,19 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class NewList(Location location, DMExpression[] parameters) : DMExpression(location) { public override DMComplexValueType ValType => DreamPath.List; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { foreach (DMExpression parameter in parameters) { - parameter.EmitPushValue(dmObject, proc); - proc.CreateObject(DMCallArgumentsType.None, 0); + parameter.EmitPushValue(ctx); + ctx.Proc.CreateObject(DMCallArgumentsType.None, 0); } - proc.CreateList(parameters.Length); + ctx.Proc.CreateList(parameters.Length); } - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = null; - DMCompiler.UnimplementedWarning(Location, "DMM overrides for newlist() are not implemented"); - return true; //TODO + compiler.UnimplementedWarning(Location, "DMM overrides for newlist() are not implemented"); + return true; //ctx } } @@ -473,24 +474,24 @@ internal sealed class Input(Location location, DMExpression[] arguments, DMValue : DMExpression(location) { public override DMComplexValueType ValType => types; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { // Push input's four arguments, pushing null for the missing ones for (int i = 3; i >= 0; i--) { if (i < arguments.Length) { - arguments[i].EmitPushValue(dmObject, proc); + arguments[i].EmitPushValue(ctx); } else { - proc.PushNull(); + ctx.Proc.PushNull(); } } // The list of values to be selected from (or null for none) if (list != null) { - list.EmitPushValue(dmObject, proc); + list.EmitPushValue(ctx); } else { - proc.PushNull(); + ctx.Proc.PushNull(); } - proc.Prompt(types); + ctx.Proc.Prompt(types); } } @@ -498,14 +499,14 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal class Initial(Location location, DMExpression expr) : DMExpression(location) { protected DMExpression Expression { get; } = expr; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { if (Expression is LValue lValue) { - lValue.EmitPushInitial(dmObject, proc); + lValue.EmitPushInitial(ctx); return; } - DMCompiler.Emit(WarningCode.BadArgument, Expression.Location, $"can't get initial value of {Expression}"); - proc.Error(); + ctx.Compiler.Emit(WarningCode.BadArgument, Expression.Location, $"can't get initial value of {Expression}"); + ctx.Proc.Error(); } } @@ -526,12 +527,12 @@ public CallStatement(Location location, DMExpression a, DMExpression b, Argument _procArgs = procArgs; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - var argumentInfo = _procArgs.EmitArguments(dmObject, proc, null); + public override void EmitPushValue(ExpressionContext ctx) { + var argumentInfo = _procArgs.EmitArguments(ctx, null); - _b?.EmitPushValue(dmObject, proc); - _a.EmitPushValue(dmObject, proc); - proc.CallStatement(argumentInfo.Type, argumentInfo.StackSize); + _b?.EmitPushValue(ctx); + _a.EmitPushValue(ctx); + ctx.Proc.CallStatement(argumentInfo.Type, argumentInfo.StackSize); } } @@ -541,21 +542,21 @@ internal sealed class ProcOwnerType(Location location, DMObject owner) : DMExpre public override DMComplexValueType ValType => (OwnerPath != null) ? OwnerPath.Value : DMValueType.Null; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { // BYOND returns null if this is called in a global proc - if (dmObject.Path == DreamPath.Root) { - proc.PushNull(); + if (ctx.Type.Path == DreamPath.Root) { + ctx.Proc.PushNull(); } else { - proc.PushType(dmObject.Id); + ctx.Proc.PushType(ctx.Type.Id); } } - public override string? GetNameof(DMObject dmObject) { - if (dmObject.Path.LastElement != null) { - return dmObject.Path.LastElement; + public override string? GetNameof(ExpressionContext ctx) { + if (ctx.Type.Path.LastElement != null) { + return ctx.Type.Path.LastElement; } - DMCompiler.Emit(WarningCode.BadArgument, Location, "Attempt to get nameof(__TYPE__) in global proc"); + ctx.Compiler.Emit(WarningCode.BadArgument, Location, "Attempt to get nameof(__TYPE__) in global proc"); return null; } } @@ -563,15 +564,15 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class Sin(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, sin(0) will always be 0"); } @@ -579,24 +580,24 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.Sin(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.Sin(); } } internal sealed class Cos(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, cos(0) will always be 1"); } @@ -604,24 +605,24 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.Cos(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.Cos(); } } internal sealed class Tan(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, tan(0) will always be 0"); } @@ -629,29 +630,29 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.Tan(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.Tan(); } } internal sealed class ArcSin(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, arcsin(0) will always be 0"); } if (x is < -1 or > 1) { - DMCompiler.Emit(WarningCode.BadArgument, expr.Location, $"Invalid value {x}, must be >= -1 and <= 1"); + compiler.Emit(WarningCode.BadArgument, expr.Location, $"Invalid value {x}, must be >= -1 and <= 1"); x = 0; } @@ -659,29 +660,29 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.ArcSin(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.ArcSin(); } } internal sealed class ArcCos(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, arccos(0) will always be 1"); } if (x is < -1 or > 1) { - DMCompiler.Emit(WarningCode.BadArgument, expr.Location, $"Invalid value {x}, must be >= -1 and <= 1"); + compiler.Emit(WarningCode.BadArgument, expr.Location, $"Invalid value {x}, must be >= -1 and <= 1"); x = 0; } @@ -689,24 +690,24 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.ArcCos(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.ArcCos(); } } internal sealed class ArcTan(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var a}) { a = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, arctan(0) will always be 0"); } @@ -714,59 +715,59 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.ArcTan(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.ArcTan(); } } internal sealed class ArcTan2(Location location, DMExpression xExpr, DMExpression yExpr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!xExpr.TryAsConstant(out var xConst) || !yExpr.TryAsConstant(out var yConst)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!xExpr.TryAsConstant(compiler, out var xConst) || !yExpr.TryAsConstant(compiler, out var yConst)) { constant = null; return false; } if (xConst is not Number {Value: var x}) { x = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, xExpr.Location, "Invalid x value treated as 0"); + compiler.Emit(WarningCode.FallbackBuiltinArgument, xExpr.Location, "Invalid x value treated as 0"); } if (yConst is not Number {Value: var y}) { y = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, xExpr.Location, "Invalid y value treated as 0"); + compiler.Emit(WarningCode.FallbackBuiltinArgument, xExpr.Location, "Invalid y value treated as 0"); } constant = new Number(Location, SharedOperations.ArcTan(x, y)); return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - xExpr.EmitPushValue(dmObject, proc); - yExpr.EmitPushValue(dmObject, proc); - proc.ArcTan2(); + public override void EmitPushValue(ExpressionContext ctx) { + xExpr.EmitPushValue(ctx); + yExpr.EmitPushValue(ctx); + ctx.Proc.ArcTan2(); } } internal sealed class Sqrt(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var a}) { a = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, sqrt(0) will always be 0"); } if (a < 0) { - DMCompiler.Emit(WarningCode.BadArgument, expr.Location, + compiler.Emit(WarningCode.BadArgument, expr.Location, $"Cannot get the square root of a negative number ({a})"); } @@ -774,9 +775,9 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.Sqrt(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.Sqrt(); } } @@ -784,15 +785,15 @@ internal sealed class Log(Location location, DMExpression expr, DMExpression? ba : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var value} || value <= 0) { value = 1; - DMCompiler.Emit(WarningCode.BadArgument, expr.Location, + compiler.Emit(WarningCode.BadArgument, expr.Location, "Invalid value, must be a number greater than 0"); } @@ -801,14 +802,14 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - if (!baseExpr.TryAsConstant(out var baseConstant)) { + if (!baseExpr.TryAsConstant(compiler, out var baseConstant)) { constant = null; return false; } if (baseConstant is not Number {Value: var baseValue} || baseValue <= 0) { baseValue = 10; - DMCompiler.Emit(WarningCode.BadArgument, baseExpr.Location, + compiler.Emit(WarningCode.BadArgument, baseExpr.Location, "Invalid base, must be a number greater than 0"); } @@ -816,13 +817,13 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); if (baseExpr == null) { - proc.LogE(); + ctx.Proc.LogE(); } else { - baseExpr.EmitPushValue(dmObject, proc); - proc.Log(); + baseExpr.EmitPushValue(ctx); + ctx.Proc.Log(); } } } @@ -830,15 +831,15 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { internal sealed class Abs(Location location, DMExpression expr) : DMExpression(location) { public override DMComplexValueType ValType => DMValueType.Num; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!expr.TryAsConstant(out constant)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!expr.TryAsConstant(compiler, out constant)) { constant = null; return false; } if (constant is not Number {Value: var a}) { a = 0; - DMCompiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, + compiler.Emit(WarningCode.FallbackBuiltinArgument, expr.Location, "Invalid value treated as 0, abs(0) will always be 0"); } @@ -846,8 +847,8 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - expr.EmitPushValue(dmObject, proc); - proc.Abs(); + public override void EmitPushValue(ExpressionContext ctx) { + expr.EmitPushValue(ctx); + ctx.Proc.Abs(); } } diff --git a/DMCompiler/DM/Expressions/Constant.cs b/DMCompiler/DM/Expressions/Constant.cs index 64fbb5da23..d9f6b256a9 100644 --- a/DMCompiler/DM/Expressions/Constant.cs +++ b/DMCompiler/DM/Expressions/Constant.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using DMCompiler.Compiler; @@ -7,7 +6,7 @@ namespace DMCompiler.DM.Expressions; internal abstract class Constant(Location location) : DMExpression(location) { - public sealed override bool TryAsConstant(out Constant constant) { + public sealed override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { constant = this; return true; } @@ -19,13 +18,13 @@ public sealed override bool TryAsConstant(out Constant constant) { internal sealed class Null(Location location) : Constant(location) { public override DMComplexValueType ValType => DMValueType.Null; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushNull(); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushNull(); } public override bool IsTruthy() => false; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = null; return true; } @@ -45,13 +44,13 @@ public Number(Location location, float value) : base(location) { Value = value; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushFloat(Value); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushFloat(Value); } public override bool IsTruthy() => Value != 0; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { // Positive/Negative infinity cannot be represented in JSON and need a special value if (float.IsPositiveInfinity(Value)) { json = new Dictionary() { @@ -75,13 +74,13 @@ internal sealed class String(Location location, string value) : Constant(locatio public override DMComplexValueType ValType => DMValueType.Text; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushString(Value); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushString(Value); } public override bool IsTruthy() => Value.Length != 0; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = Value; return true; } @@ -102,12 +101,12 @@ internal sealed class Resource : Constant { private readonly string _filePath; private bool _isAmbiguous; - public Resource(Location location, string filePath) : base(location) { + public Resource(DMCompiler compiler, Location location, string filePath) : base(location) { // Treat backslashes as forward slashes on Linux // Also remove "." and ".." from the directory path filePath = System.IO.Path.GetRelativePath(".", filePath.Replace('\\', '/')); - var outputDir = System.IO.Path.GetDirectoryName(DMCompiler.Settings.Files?[0]) ?? "/"; + var outputDir = System.IO.Path.GetDirectoryName(compiler.Settings.Files?[0]) ?? "/"; if (string.IsNullOrEmpty(outputDir)) outputDir = "./"; @@ -117,7 +116,7 @@ public Resource(Location location, string filePath) : base(location) { var fileDir = System.IO.Path.GetDirectoryName(filePath) ?? string.Empty; // Search every defined FILE_DIR - foreach (string resourceDir in DMCompiler.ResourceDirectories) { + foreach (string resourceDir in compiler.ResourceDirectories) { var directory = FindDirectory(resourceDir, fileDir); if (directory != null) { @@ -142,11 +141,11 @@ public Resource(Location location, string filePath) : base(location) { _filePath = System.IO.Path.GetRelativePath(outputDir, finalFilePath); if (_isAmbiguous) { - DMCompiler.Emit(WarningCode.AmbiguousResourcePath, Location, + compiler.Emit(WarningCode.AmbiguousResourcePath, Location, $"Resource {filePath} has multiple case-insensitive matches, using {_filePath}"); } } else { - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, $"Cannot find file '{filePath}'"); + compiler.Emit(WarningCode.ItemDoesntExist, Location, $"Cannot find file '{filePath}'"); _filePath = filePath; } @@ -154,16 +153,16 @@ public Resource(Location location, string filePath) : base(location) { // Compile-time resources always use forward slashes _filePath = _filePath.Replace('\\', '/'); - DMObjectTree.Resources.Add(_filePath); + compiler.DMObjectTree.Resources.Add(_filePath); } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushResource(_filePath); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushResource(_filePath); } public override bool IsTruthy() => true; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = new Dictionary() { { "type", JsonVariableType.Resource }, { "resourcePath", _filePath } @@ -233,15 +232,15 @@ internal class ConstantTypeReference(Location location, DMObject dmObject) : Con public override DreamPath? Path => Value.Path; public override DMComplexValueType ValType => Value.Path; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushType(Value.Id); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushType(Value.Id); } - public override string? GetNameof(DMObject dmObject) => Value.Path.LastElement; + public override string? GetNameof(ExpressionContext ctx) => Value.Path.LastElement; public override bool IsTruthy() => true; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = new Dictionary { { "type", JsonVariableType.Type }, { "value", Value.Id } @@ -260,15 +259,15 @@ internal sealed class ConstantProcReference(Location location, DreamPath path, D public override DreamPath? Path => path; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushProc(Value.Id); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushProc(Value.Id); } - public override string GetNameof(DMObject dmObject) => Value.Name; + public override string GetNameof(ExpressionContext ctx) => Value.Name; public override bool IsTruthy() => true; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = new Dictionary { { "type", JsonVariableType.Proc }, { "value", Value.Id } @@ -288,14 +287,14 @@ internal sealed class ConstantProcStub(Location location, DMObject onObject, boo public override DreamPath? Path => onObject.Path.AddToPath(isVerb ? "verb" : "proc"); - public override void EmitPushValue(DMObject dmObject, DMProc proc) { + public override void EmitPushValue(ExpressionContext ctx) { // /datum/proc and /datum/verb just compile down to strings lmao - proc.PushString(_str); + ctx.Proc.PushString(_str); } public override bool IsTruthy() => true; - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = _str; return true; } diff --git a/DMCompiler/DM/Expressions/Dereference.cs b/DMCompiler/DM/Expressions/Dereference.cs index 6c3c72224c..e350f13a52 100644 --- a/DMCompiler/DM/Expressions/Dereference.cs +++ b/DMCompiler/DM/Expressions/Dereference.cs @@ -45,17 +45,17 @@ public sealed class CallOperation : NamedOperation { public required ArgumentList Parameters { get; init; } } - public readonly DMExpression Expression; public override DreamPath? Path { get; } public override DreamPath? NestedPath { get; } public override bool PathIsFuzzy => Path == null; public override DMComplexValueType ValType { get; } + private readonly DMExpression _expression; private readonly Operation[] _operations; - public Dereference(Location location, DreamPath? path, DMExpression expression, Operation[] operations) + public Dereference(DMObjectTree objectTree, Location location, DreamPath? path, DMExpression expression, Operation[] operations) : base(location, null) { - Expression = expression; + _expression = expression; Path = path; _operations = operations; @@ -64,16 +64,16 @@ public Dereference(Location location, DreamPath? path, DMExpression expression, } NestedPath = _operations[^1].Path; - ValType = DetermineValType(); + ValType = DetermineValType(objectTree); } - private DMComplexValueType DetermineValType() { - var type = Expression.ValType; + private DMComplexValueType DetermineValType(DMObjectTree objectTree) { + var type = _expression.ValType; var i = 0; while (!type.IsAnything && i < _operations.Length) { var operation = _operations[i++]; - if (type.TypePath is null || !DMObjectTree.TryGetDMObject(type.TypePath.Value, out var dmObject)) { + if (type.TypePath is null || !objectTree.TryGetDMObject(type.TypePath.Value, out var dmObject)) { // We're dereferencing something without a type-path, this could be anything type = DMValueType.Anything; break; @@ -103,30 +103,30 @@ private void ShortCircuitHandler(DMProc proc, string endLabel, ShortCircuitMode } } - private void EmitOperation(DMObject dmObject, DMProc proc, Operation operation, string endLabel, ShortCircuitMode shortCircuitMode) { + private void EmitOperation(ExpressionContext ctx, Operation operation, string endLabel, ShortCircuitMode shortCircuitMode) { if (operation.Safe) { - ShortCircuitHandler(proc, endLabel, shortCircuitMode); + ShortCircuitHandler(ctx.Proc, endLabel, shortCircuitMode); } switch (operation) { case FieldOperation fieldOperation: - proc.DereferenceField(fieldOperation.Identifier); + ctx.Proc.DereferenceField(fieldOperation.Identifier); break; case IndexOperation indexOperation: if (NestedPath is not null) { - if (DMObjectTree.TryGetDMObject(NestedPath.Value, out var obj) && obj.IsSubtypeOf(DreamPath.Datum) && !obj.HasProc("operator[]")) { - DMCompiler.Emit(WarningCode.InvalidIndexOperation, Location, "Invalid index operation. datum[] index operations are not valid starting in BYOND 515.1641"); + if (ctx.ObjectTree.TryGetDMObject(NestedPath.Value, out var obj) && obj.IsSubtypeOf(DreamPath.Datum) && !obj.HasProc("operator[]")) { + ctx.Compiler.Emit(WarningCode.InvalidIndexOperation, Location, "Invalid index operation. datum[] index operations are not valid starting in BYOND 515.1641"); } } - indexOperation.Index.EmitPushValue(dmObject, proc); - proc.DereferenceIndex(); + indexOperation.Index.EmitPushValue(ctx); + ctx.Proc.DereferenceIndex(); break; case CallOperation callOperation: - var (argumentsType, argumentStackSize) = callOperation.Parameters.EmitArguments(dmObject, proc, null); - proc.DereferenceCall(callOperation.Identifier, argumentsType, argumentStackSize); + var (argumentsType, argumentStackSize) = callOperation.Parameters.EmitArguments(ctx, null); + ctx.Proc.DereferenceCall(callOperation.Identifier, argumentsType, argumentStackSize); break; default: @@ -134,28 +134,29 @@ private void EmitOperation(DMObject dmObject, DMProc proc, Operation operation, } } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushValue(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - Expression.EmitPushValue(dmObject, proc); + _expression.EmitPushValue(ctx); foreach (var operation in _operations) { - EmitOperation(dmObject, proc, operation, endLabel, ShortCircuitMode.KeepNull); + EmitOperation(ctx, operation, endLabel, ShortCircuitMode.KeepNull); } - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } public override bool CanReferenceShortCircuit() { return _operations.Any(operation => operation.Safe); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - Expression.EmitPushValue(dmObject, proc); + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + _expression.EmitPushValue(ctx); // Perform all except for our last operation for (int i = 0; i < _operations.Length - 1; i++) { - EmitOperation(dmObject, proc, _operations[i], endLabel, shortCircuitMode); + EmitOperation(ctx, _operations[i], endLabel, shortCircuitMode); } var operation = _operations[^1]; @@ -163,27 +164,27 @@ public override DMReference EmitReference(DMObject dmObject, DMProc proc, string switch (operation) { case FieldOperation fieldOperation: if (fieldOperation.Safe) { - ShortCircuitHandler(proc, endLabel, shortCircuitMode); + ShortCircuitHandler(ctx.Proc, endLabel, shortCircuitMode); } return DMReference.CreateField(fieldOperation.Identifier); case IndexOperation indexOperation: if (NestedPath is not null) { - if (DMObjectTree.TryGetDMObject(NestedPath.Value, out var obj) && obj.IsSubtypeOf(DreamPath.Datum) && !obj.HasProc("operator[]=")) { - DMCompiler.Emit(WarningCode.InvalidIndexOperation, Location, "Invalid index operation. datum[] index operations are not valid starting in BYOND 515.1641"); + if (ctx.ObjectTree.TryGetDMObject(NestedPath.Value, out var obj) && obj.IsSubtypeOf(DreamPath.Datum) && !obj.HasProc("operator[]=")) { + ctx.Compiler.Emit(WarningCode.InvalidIndexOperation, Location, "Invalid index operation. datum[] index operations are not valid starting in BYOND 515.1641"); } } if (indexOperation.Safe) { - ShortCircuitHandler(proc, endLabel, shortCircuitMode); + ShortCircuitHandler(ctx.Proc, endLabel, shortCircuitMode); } - indexOperation.Index.EmitPushValue(dmObject, proc); + indexOperation.Index.EmitPushValue(ctx); return DMReference.ListIndex; case CallOperation: - DMCompiler.Emit(WarningCode.BadExpression, Location, + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Expected field or index as reference, got proc call result"); return default; @@ -192,19 +193,19 @@ public override DMReference EmitReference(DMObject dmObject, DMProc proc, string } } - public override void EmitPushInitial(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushInitial(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - if (Expression is LValue exprLValue) { + if (_expression is LValue exprLValue) { // We don't want this instead pushing the constant value if it's const - exprLValue.EmitPushValueNoConstant(dmObject, proc); + exprLValue.EmitPushValueNoConstant(ctx); } else { - Expression.EmitPushValue(dmObject, proc); + _expression.EmitPushValue(ctx); } // Perform all except for our last operation for (int i = 0; i < _operations.Length - 1; i++) { - EmitOperation(dmObject, proc, _operations[i], endLabel, ShortCircuitMode.KeepNull); + EmitOperation(ctx, _operations[i], endLabel, ShortCircuitMode.KeepNull); } var operation = _operations[^1]; @@ -212,22 +213,24 @@ public override void EmitPushInitial(DMObject dmObject, DMProc proc) { switch (operation) { case FieldOperation fieldOperation: if (fieldOperation.Safe) { - proc.JumpIfNullNoPop(endLabel); + ctx.Proc.JumpIfNullNoPop(endLabel); } - proc.PushString(fieldOperation.Identifier); - proc.Initial(); + + ctx.Proc.PushString(fieldOperation.Identifier); + ctx.Proc.Initial(); break; case IndexOperation indexOperation: if (indexOperation.Safe) { - proc.JumpIfNullNoPop(endLabel); + ctx.Proc.JumpIfNullNoPop(endLabel); } - indexOperation.Index.EmitPushValue(dmObject, proc); - proc.Initial(); + + indexOperation.Index.EmitPushValue(ctx); + ctx.Proc.Initial(); break; case CallOperation: - DMCompiler.Emit(WarningCode.BadExpression, Location, + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Expected field or index for initial(), got proc call result"); break; @@ -235,22 +238,22 @@ public override void EmitPushInitial(DMObject dmObject, DMProc proc) { throw new InvalidOperationException("Unimplemented dereference operation"); } - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } - public void EmitPushIsSaved(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public void EmitPushIsSaved(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - if (Expression is LValue exprLValue) { + if (_expression is LValue exprLValue) { // We don't want this instead pushing the constant value if it's const - exprLValue.EmitPushValueNoConstant(dmObject, proc); + exprLValue.EmitPushValueNoConstant(ctx); } else { - Expression.EmitPushValue(dmObject, proc); + _expression.EmitPushValue(ctx); } // Perform all except for our last operation for (int i = 0; i < _operations.Length - 1; i++) { - EmitOperation(dmObject, proc, _operations[i], endLabel, ShortCircuitMode.KeepNull); + EmitOperation(ctx, _operations[i], endLabel, ShortCircuitMode.KeepNull); } var operation = _operations[^1]; @@ -258,22 +261,24 @@ public void EmitPushIsSaved(DMObject dmObject, DMProc proc) { switch (operation) { case FieldOperation fieldOperation: if (fieldOperation.Safe) { - proc.JumpIfNullNoPop(endLabel); + ctx.Proc.JumpIfNullNoPop(endLabel); } - proc.PushString(fieldOperation.Identifier); - proc.IsSaved(); + + ctx.Proc.PushString(fieldOperation.Identifier); + ctx.Proc.IsSaved(); break; case IndexOperation indexOperation: if (indexOperation.Safe) { - proc.JumpIfNullNoPop(endLabel); + ctx.Proc.JumpIfNullNoPop(endLabel); } - indexOperation.Index.EmitPushValue(dmObject, proc); - proc.IsSaved(); + + indexOperation.Index.EmitPushValue(ctx); + ctx.Proc.IsSaved(); break; case CallOperation: - DMCompiler.Emit(WarningCode.BadExpression, Location, + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "Expected field or index for issaved(), got proc call result"); break; @@ -281,28 +286,28 @@ public void EmitPushIsSaved(DMObject dmObject, DMProc proc) { throw new InvalidOperationException("Unimplemented dereference operation"); } - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } // BYOND says the nameof is invalid if the chain is not purely field operations - public override string? GetNameof(DMObject dmObject) { + public override string? GetNameof(ExpressionContext ctx) { return _operations.All(op => op is FieldOperation) ? ((FieldOperation)_operations[^1]).Identifier : null; } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - var prevPath = _operations.Length == 1 ? Expression.Path : _operations[^2].Path; + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + var prevPath = _operations.Length == 1 ? _expression.Path : _operations[^2].Path; var operation = _operations[^1]; - if (operation is FieldOperation fieldOperation && prevPath is not null && DMObjectTree.TryGetDMObject(prevPath.Value, out var obj)) { + if (operation is FieldOperation fieldOperation && prevPath is not null && compiler.DMObjectTree.TryGetDMObject(prevPath.Value, out var obj)) { var variable = obj.GetVariable(fieldOperation.Identifier); if (variable != null) { if (variable.IsConst) - return variable.Value.TryAsConstant(out constant); + return variable.Value.TryAsConstant(compiler, out constant); if (variable.ValType.IsCompileTimeReadOnly) { - variable.Value.TryAsConstant(out constant!); + variable.Value.TryAsConstant(compiler, out constant!); return true; // MUST be true. } } @@ -315,8 +320,8 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { // expression::identifier // Same as initial(expression?.identifier) except this keeps its type -internal sealed class ScopeReference(Location location, DMExpression expression, string identifier, DMVariable dmVar) - : Initial(location, new Dereference(location, dmVar.Type, expression, // Just a little hacky +internal sealed class ScopeReference(DMObjectTree objectTree, Location location, DMExpression expression, string identifier, DMVariable dmVar) + : Initial(location, new Dereference(objectTree, location, dmVar.Type, expression, // Just a little hacky [ new Dereference.FieldOperation { Identifier = identifier, @@ -327,14 +332,14 @@ internal sealed class ScopeReference(Location location, DMExpression expression, ) { public override DreamPath? Path => Expression.Path; - public override string GetNameof(DMObject dmObject) => dmVar.Name; + public override string GetNameof(ExpressionContext ctx) => dmVar.Name; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { if (expression is not IConstantPath) { constant = null; return false; } - return dmVar.Value!.TryAsConstant(out constant); + return dmVar.Value!.TryAsConstant(compiler, out constant); } } diff --git a/DMCompiler/DM/Expressions/LValue.cs b/DMCompiler/DM/Expressions/LValue.cs index 4ab6aa6513..b454833f35 100644 --- a/DMCompiler/DM/Expressions/LValue.cs +++ b/DMCompiler/DM/Expressions/LValue.cs @@ -7,34 +7,35 @@ namespace DMCompiler.DM.Expressions; internal abstract class LValue(Location location, DreamPath? path) : DMExpression(location) { public override DreamPath? Path { get; } = path; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - if (TryAsConstant(out var constant)) { // BYOND also seems to push consts instead of references when possible - constant.EmitPushValue(dmObject, proc); + public override void EmitPushValue(ExpressionContext ctx) { + if (TryAsConstant(ctx.Compiler, out var constant)) { // BYOND also seems to push consts instead of references when possible + constant.EmitPushValue(ctx); return; } - EmitPushValueNoConstant(dmObject, proc); + EmitPushValueNoConstant(ctx); } - public void EmitPushValueNoConstant(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public void EmitPushValueNoConstant(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - DMReference reference = EmitReference(dmObject, proc, endLabel); - proc.PushReferenceValue(reference); + DMReference reference = EmitReference(ctx, endLabel); + ctx.Proc.PushReferenceValue(reference); - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } - public virtual void EmitPushInitial(DMObject dmObject, DMProc proc) { - DMCompiler.Emit(WarningCode.BadExpression, Location, $"Can't get initial value of {this}"); - proc.Error(); + public virtual void EmitPushInitial(ExpressionContext ctx) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, $"Can't get initial value of {this}"); + ctx.Proc.Error(); } } // global internal class Global(Location location) : LValue(location, null) { - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "attempt to use `global` as a reference"); + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "attempt to use `global` as a reference"); return DMReference.Invalid; } } @@ -43,11 +44,12 @@ public override DMReference EmitReference(DMObject dmObject, DMProc proc, string internal sealed class Src(Location location, DreamPath? path) : LValue(location, path) { public override DMComplexValueType ValType => DMValueType.Anything; - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.Src; } - public override string GetNameof(DMObject dmObject) => "src"; + public override string GetNameof(ExpressionContext ctx) => "src"; } // usr @@ -55,29 +57,32 @@ internal sealed class Usr(Location location) : LValue(location, DreamPath.Mob) { //According to the docs, Usr is a mob. But it will get set to null by coders to clear refs. public override DMComplexValueType ValType => (DMValueType.Mob | DMValueType.Null); - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.Usr; } - public override string GetNameof(DMObject dmObject) => "usr"; + public override string GetNameof(ExpressionContext ctx) => "usr"; } // args internal sealed class Args(Location location) : LValue(location, DreamPath.List) { - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.Args; } - public override string GetNameof(DMObject dmObject) => "args"; + public override string GetNameof(ExpressionContext ctx) => "args"; } // world internal sealed class World(Location location) : LValue(location, DreamPath.World) { - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.World; } - public override string GetNameof(DMObject dmObject) => "world"; + public override string GetNameof(ExpressionContext ctx) => "world"; } // Identifier of local variable @@ -87,7 +92,8 @@ internal sealed class Local(Location location, DMProc.LocalVariable localVar) : // TODO: non-const local var static typing public override DMComplexValueType ValType => LocalVar.ExplicitValueType ?? DMValueType.Anything; - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { if (LocalVar.IsParameter) { return DMReference.CreateArgument(LocalVar.Id); } else { @@ -95,7 +101,7 @@ public override DMReference EmitReference(DMObject dmObject, DMProc proc, string } } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { if (LocalVar is DMProc.LocalConstVariable constVar) { constant = constVar.Value; return true; @@ -105,23 +111,23 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { return false; } - public override void EmitPushInitial(DMObject dmObject, DMProc proc) { + public override void EmitPushInitial(ExpressionContext ctx) { // This happens silently in BYOND - DMCompiler.Emit(WarningCode.PointlessBuiltinCall, Location, "calling initial() on a local variable returns the current value"); - EmitPushValue(dmObject, proc); + ctx.Compiler.Emit(WarningCode.PointlessBuiltinCall, Location, "calling initial() on a local variable returns the current value"); + EmitPushValue(ctx); } - public override string GetNameof(DMObject dmObject) => LocalVar.Name; + public override string GetNameof(ExpressionContext ctx) => LocalVar.Name; } // Identifier of field internal sealed class Field(Location location, DMVariable variable, DMComplexValueType valType) : LValue(location, variable.Type) { public override DMComplexValueType ValType => valType; - public override void EmitPushInitial(DMObject dmObject, DMProc proc) { - proc.PushReferenceValue(DMReference.Src); - proc.PushString(variable.Name); - proc.Initial(); + public override void EmitPushInitial(ExpressionContext ctx) { + ctx.Proc.PushReferenceValue(DMReference.Src); + ctx.Proc.PushString(variable.Name); + ctx.Proc.Initial(); } public void EmitPushIsSaved(DMProc proc) { @@ -130,15 +136,16 @@ public void EmitPushIsSaved(DMProc proc) { proc.IsSaved(); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.CreateSrcField(variable.Name); } - public override string GetNameof(DMObject dmObject) => variable.Name; + public override string GetNameof(ExpressionContext ctx) => variable.Name; - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { if (variable is { IsConst: true, Value: not null }) { - return variable.Value.TryAsConstant(out constant); + return variable.Value.TryAsConstant(compiler, out constant); } constant = null; @@ -156,25 +163,26 @@ internal sealed class GlobalField(Location location, DreamPath? path, int id, D public override DMComplexValueType ValType => valType; - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.CreateGlobal(Id); } - public override void EmitPushInitial(DMObject dmObject, DMProc proc) { + public override void EmitPushInitial(ExpressionContext ctx) { // This happens silently in BYOND - DMCompiler.Emit(WarningCode.PointlessBuiltinCall, Location, "calling initial() on a global returns the current value"); - EmitPushValue(dmObject, proc); + ctx.Compiler.Emit(WarningCode.PointlessBuiltinCall, Location, "calling initial() on a global returns the current value"); + EmitPushValue(ctx); } - public override string GetNameof(DMObject dmObject) { - DMVariable global = DMObjectTree.Globals[Id]; + public override string GetNameof(ExpressionContext ctx) { + DMVariable global = ctx.ObjectTree.Globals[Id]; return global.Name; } - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - DMVariable global = DMObjectTree.Globals[Id]; + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + DMVariable global = compiler.DMObjectTree.Globals[Id]; if (global.IsConst) { - return global.Value.TryAsConstant(out constant); + return global.Value.TryAsConstant(compiler, out constant); } constant = null; @@ -183,9 +191,9 @@ public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { } internal sealed class GlobalVars(Location location) : LValue(location, null) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - proc.PushGlobalVars(); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Proc.PushGlobalVars(); } - public override string GetNameof(DMObject dmObject) => "vars"; + public override string GetNameof(ExpressionContext ctx) => "vars"; } diff --git a/DMCompiler/DM/Expressions/Procs.cs b/DMCompiler/DM/Expressions/Procs.cs index 0ec87f4d69..ba5105dba0 100644 --- a/DMCompiler/DM/Expressions/Procs.cs +++ b/DMCompiler/DM/Expressions/Procs.cs @@ -6,26 +6,27 @@ namespace DMCompiler.DM.Expressions; // x() (only the identifier) internal sealed class Proc(Location location, string identifier) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMCompiler.Emit(WarningCode.BadExpression, Location, "attempt to use proc as value"); - proc.Error(); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Compiler.Emit(WarningCode.BadExpression, Location, "attempt to use proc as value"); + ctx.Proc.Error(); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - if (dmObject.HasProc(identifier)) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + if (ctx.Type.HasProc(identifier)) { return DMReference.CreateSrcProc(identifier); - } else if (DMObjectTree.TryGetGlobalProc(identifier, out var globalProc)) { + } else if (ctx.ObjectTree.TryGetGlobalProc(identifier, out var globalProc)) { return DMReference.CreateGlobalProc(globalProc.Id); } - DMCompiler.Emit(WarningCode.ItemDoesntExist, Location, $"Type {dmObject.Path} does not have a proc named \"{identifier}\""); + ctx.Compiler.Emit(WarningCode.ItemDoesntExist, Location, $"Type {ctx.Type.Path} does not have a proc named \"{identifier}\""); //Just... pretend there is one for the sake of argument. return DMReference.CreateSrcProc(identifier); } - public DMProc? GetProc(DMObject dmObject) { + public DMProc? GetProc(DMCompiler compiler, DMObject dmObject) { var procId = dmObject.GetProcs(identifier)?[^1]; - return procId is null ? null : DMObjectTree.AllProcs[procId.Value]; + return procId is null ? null : compiler.DMObjectTree.AllProcs[procId.Value]; } public DMComplexValueType GetReturnType(DMObject dmObject) { @@ -33,20 +34,21 @@ public DMComplexValueType GetReturnType(DMObject dmObject) { } } -internal sealed class GlobalProc(Location location, DMProc proc) : DMExpression(location) { +internal sealed class GlobalProc(Location location, DMProc globalProc) : DMExpression(location) { public override DMComplexValueType ValType => Proc.ReturnTypes; - public DMProc Proc => proc; + public DMProc Proc => globalProc; public override string ToString() { - return $"{proc.Name}()"; + return $"{globalProc.Name}()"; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMCompiler.Emit(WarningCode.InvalidReference, Location, $"Attempt to use proc \"{this}\" as value"); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Compiler.Emit(WarningCode.InvalidReference, Location, $"Attempt to use proc \"{this}\" as value"); } - public override DMReference EmitReference(DMObject dmObject, DMProc callingProc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.CreateGlobalProc(Proc.Id); } } @@ -55,27 +57,28 @@ public override DMReference EmitReference(DMObject dmObject, DMProc callingProc, /// .
/// This is an LValue _and_ a proc! /// -internal sealed class ProcSelf(Location location, DreamPath? path, DMProc proc) : LValue(location, path) { - public override DMComplexValueType ValType => proc.ReturnTypes; +internal sealed class ProcSelf(Location location, DMComplexValueType valType) : LValue(location, null) { + public override DMComplexValueType ValType => valType; - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { return DMReference.Self; } } // .. -internal sealed class ProcSuper(Location location, DMObject _dmObject, DMProc _proc) : DMExpression(location) { - public override DMComplexValueType ValType => _dmObject.GetProcReturnTypes(_proc.Name) ?? DMValueType.Anything; +internal sealed class ProcSuper(Location location, DMComplexValueType? valType) : DMExpression(location) { + public override DMComplexValueType ValType => valType ?? DMValueType.Anything; - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - DMCompiler.Emit(WarningCode.InvalidReference, Location, $"Attempt to use proc \"..\" as value"); + public override void EmitPushValue(ExpressionContext ctx) { + ctx.Compiler.Emit(WarningCode.InvalidReference, Location, $"Attempt to use proc \"..\" as value"); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - if ((proc.Attributes & ProcAttributes.IsOverride) != ProcAttributes.IsOverride) { + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + if ((ctx.Proc.Attributes & ProcAttributes.IsOverride) != ProcAttributes.IsOverride) { // Don't emit if lateral proc overrides exist - if (dmObject.GetProcs(proc.Name)!.Count == 1) { - DMCompiler.Emit(WarningCode.PointlessParentCall, Location, + if (ctx.Type.GetProcs(ctx.Proc.Name)!.Count == 1) { + ctx.Compiler.Emit(WarningCode.PointlessParentCall, Location, "Calling parents via ..() in a proc definition does nothing"); } } @@ -90,9 +93,9 @@ internal sealed class ProcCall(Location location, DMExpression target, ArgumentL public override bool PathIsFuzzy => Path == null; public override DMComplexValueType ValType => valType.IsAnything ? target.ValType : valType; - public (DMObject? ProcOwner, DMProc? Proc) GetTargetProc(DMObject dmObject) { + public (DMObject? ProcOwner, DMProc? Proc) GetTargetProc(DMCompiler compiler, DMObject dmObject) { return target switch { - Proc procTarget => (dmObject, procTarget.GetProc(dmObject)), + Proc procTarget => (dmObject, procTarget.GetProc(compiler, dmObject)), GlobalProc procTarget => (null, procTarget.Proc), _ => (null, null) }; @@ -102,14 +105,14 @@ public override string ToString() { return target.ToString()!; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - (DMObject? procOwner, DMProc? targetProc) = GetTargetProc(dmObject); - DoCompileTimeLinting(procOwner, targetProc); + public override void EmitPushValue(ExpressionContext ctx) { + (DMObject? procOwner, DMProc? targetProc) = GetTargetProc(ctx.Compiler, ctx.Type); + DoCompileTimeLinting(ctx.Compiler, procOwner, targetProc); if ((targetProc?.Attributes & ProcAttributes.Unimplemented) == ProcAttributes.Unimplemented) { - DMCompiler.UnimplementedWarning(Location, $"{procOwner?.Path.ToString() ?? "/"}.{targetProc.Name}() is not implemented"); + ctx.Compiler.UnimplementedWarning(Location, $"{procOwner?.Path.ToString() ?? "/"}.{targetProc.Name}() is not implemented"); } - string endLabel = proc.NewLabelName(); + string endLabel = ctx.Proc.NewLabelName(); DMCallArgumentsType argumentsType; int argumentStackSize; @@ -117,20 +120,20 @@ public override void EmitPushValue(DMObject dmObject, DMProc proc) { argumentsType = DMCallArgumentsType.FromProcArguments; argumentStackSize = 0; } else { - (argumentsType, argumentStackSize) = arguments.EmitArguments(dmObject, proc, targetProc); + (argumentsType, argumentStackSize) = arguments.EmitArguments(ctx, targetProc); } - DMReference procRef = target.EmitReference(dmObject, proc, endLabel); + DMReference procRef = target.EmitReference(ctx, endLabel); - proc.Call(procRef, argumentsType, argumentStackSize); - proc.AddLabel(endLabel); + ctx.Proc.Call(procRef, argumentsType, argumentStackSize); + ctx.Proc.AddLabel(endLabel); } /// /// This is a good place to do some compile-time linting of any native procs that require it, /// such as native procs that check ahead of time if the number of arguments is correct (like matrix() or sin()) /// - private void DoCompileTimeLinting(DMObject? procOwner, DMProc? targetProc) { + private void DoCompileTimeLinting(DMCompiler compiler, DMObject? procOwner, DMProc? targetProc) { if(procOwner is null || procOwner.Path == DreamPath.Root) { if (targetProc is null) return; @@ -144,9 +147,9 @@ private void DoCompileTimeLinting(DMObject? procOwner, DMProc? targetProc) { case 3: // These imply that they're trying to use the undocumented matrix signatures. case 4: // The lint is to just check that the last argument is a numeric constant that is a valid matrix "opcode." var lastArg = arguments.Expressions.Last().Expr; - if(lastArg.TryAsConstant(out var constant)) { + if(lastArg.TryAsConstant(compiler, out var constant)) { if(constant is not Number opcodeNumber) { - DMCompiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, + compiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, "Arguments for matrix() are invalid - either opcode is invalid or not enough arguments"); break; } @@ -159,18 +162,18 @@ private void DoCompileTimeLinting(DMObject? procOwner, DMProc? targetProc) { //NOTE: This still does let some certain weird opcodes through, //like a MODIFY with no other operation present. //Not sure if that is a parity behaviour or not! - DMCompiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, + compiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, "Arguments for matrix() are invalid - either opcode is invalid or not enough arguments"); } } break; case 5: // BYOND always runtimes but DOES compile, here - DMCompiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, - $"Calling matrix() with 5 arguments will always error when called at runtime"); + compiler.Emit(WarningCode.SuspiciousMatrixCall, arguments.Location, + "Calling matrix() with 5 arguments will always error when called at runtime"); break; default: // BYOND always compiletimes here - DMCompiler.Emit(WarningCode.InvalidArgumentCount, arguments.Location, + compiler.Emit(WarningCode.InvalidArgumentCount, arguments.Location, $"Too many arguments to matrix() - got {arguments.Length} arguments, expecting 6 or less"); break; } @@ -178,9 +181,9 @@ private void DoCompileTimeLinting(DMObject? procOwner, DMProc? targetProc) { } } - public override bool TryAsJsonRepresentation(out object? json) { + public override bool TryAsJsonRepresentation(DMCompiler compiler, out object? json) { json = null; - DMCompiler.UnimplementedWarning(Location, $"DMM overrides for expression {GetType()} are not implemented"); + compiler.UnimplementedWarning(Location, $"DMM overrides for expression {GetType()} are not implemented"); return true; //TODO } } diff --git a/DMCompiler/DM/Expressions/Ternary.cs b/DMCompiler/DM/Expressions/Ternary.cs index 27bc106657..c5b874a89b 100644 --- a/DMCompiler/DM/Expressions/Ternary.cs +++ b/DMCompiler/DM/Expressions/Ternary.cs @@ -1,61 +1,46 @@ using System.Diagnostics.CodeAnalysis; -using DMCompiler.Compiler; namespace DMCompiler.DM.Expressions; // x ? y : z -internal sealed class Ternary : DMExpression { - private readonly DMExpression _a, _b, _c; - +internal sealed class Ternary(Location location, DMExpression a, DMExpression b, DMExpression c) + : DMExpression(location) { public override bool PathIsFuzzy => true; - public override DMComplexValueType ValType { get; } - - public Ternary(Location location, DMExpression a, DMExpression b, DMExpression c) : base(location) { - _a = a; - _b = b; - _c = c; - - if (b.ValType.TypePath != null && c.ValType.TypePath != null && b.ValType.TypePath != c.ValType.TypePath) { - DMCompiler.Emit(WarningCode.LostTypeInfo, Location, - $"Ternary has type paths {b.ValType.TypePath} and {c.ValType.TypePath} but a value can only have one type path. Using {b.ValType.TypePath}."); - } - - ValType = new(b.ValType.Type | c.ValType.Type, b.ValType.TypePath ?? c.ValType.TypePath); - } + public override DMComplexValueType ValType { get; } = new(b.ValType.Type | c.ValType.Type, b.ValType.TypePath ?? c.ValType.TypePath); - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!_a.TryAsConstant(out var constant1)) { + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!a.TryAsConstant(compiler, out var constant1)) { constant = null; return false; } if (constant1.IsTruthy()) { - return _b.TryAsConstant(out constant); + return b.TryAsConstant(compiler, out constant); } - return _c.TryAsConstant(out constant); + return c.TryAsConstant(compiler, out constant); } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string cLabel = proc.NewLabelName(); - string endLabel = proc.NewLabelName(); - - _a.EmitPushValue(dmObject, proc); - proc.JumpIfFalse(cLabel); - _b.EmitPushValue(dmObject, proc); - proc.Jump(endLabel); - proc.AddLabel(cLabel); - _c.EmitPushValue(dmObject, proc); - proc.AddLabel(endLabel); + public override void EmitPushValue(ExpressionContext ctx) { + string cLabel = ctx.Proc.NewLabelName(); + string endLabel = ctx.Proc.NewLabelName(); + + a.EmitPushValue(ctx); + ctx.Proc.JumpIfFalse(cLabel); + b.EmitPushValue(ctx); + ctx.Proc.Jump(endLabel); + ctx.Proc.AddLabel(cLabel); + c.EmitPushValue(ctx); + ctx.Proc.AddLabel(endLabel); } } // var in x to y internal sealed class InRange(Location location, DMExpression var, DMExpression start, DMExpression end) : DMExpression(location) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - var.EmitPushValue(dmObject, proc); - start.EmitPushValue(dmObject, proc); - end.EmitPushValue(dmObject, proc); - proc.IsInRange(); + public override void EmitPushValue(ExpressionContext ctx) { + var.EmitPushValue(ctx); + start.EmitPushValue(ctx); + end.EmitPushValue(ctx); + ctx.Proc.IsInRange(); } } diff --git a/DMCompiler/DM/Expressions/Unary.cs b/DMCompiler/DM/Expressions/Unary.cs index 3f53b2f716..f6b07c94fb 100644 --- a/DMCompiler/DM/Expressions/Unary.cs +++ b/DMCompiler/DM/Expressions/Unary.cs @@ -9,67 +9,67 @@ internal abstract class UnaryOp(Location location, DMExpression expr) : DMExpres // -x internal sealed class Negate(Location location, DMExpression expr) : UnaryOp(location, expr) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!Expr.TryAsConstant(out constant) || constant is not Number number) + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!Expr.TryAsConstant(compiler, out constant) || constant is not Number number) return false; constant = new Number(Location, -number.Value); return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - Expr.EmitPushValue(dmObject, proc); - proc.Negate(); + public override void EmitPushValue(ExpressionContext ctx) { + Expr.EmitPushValue(ctx); + ctx.Proc.Negate(); } } // !x internal sealed class Not(Location location, DMExpression expr) : UnaryOp(location, expr) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!Expr.TryAsConstant(out constant)) return false; + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!Expr.TryAsConstant(compiler, out constant)) return false; constant = new Number(Location, constant.IsTruthy() ? 0 : 1); return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - Expr.EmitPushValue(dmObject, proc); - proc.Not(); + public override void EmitPushValue(ExpressionContext ctx) { + Expr.EmitPushValue(ctx); + ctx.Proc.Not(); } } // ~x internal sealed class BinaryNot(Location location, DMExpression expr) : UnaryOp(location, expr) { - public override bool TryAsConstant([NotNullWhen(true)] out Constant? constant) { - if (!Expr.TryAsConstant(out constant) || constant is not Number constantNum) + public override bool TryAsConstant(DMCompiler compiler, [NotNullWhen(true)] out Constant? constant) { + if (!Expr.TryAsConstant(compiler, out constant) || constant is not Number constantNum) return false; constant = new Number(Location, ~(int)constantNum.Value); return true; } - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - Expr.EmitPushValue(dmObject, proc); - proc.BinaryNot(); + public override void EmitPushValue(ExpressionContext ctx) { + Expr.EmitPushValue(ctx); + ctx.Proc.BinaryNot(); } } internal abstract class AssignmentUnaryOp(Location location, DMExpression expr) : UnaryOp(location, expr) { - protected abstract void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel); + protected abstract void EmitOp(DMProc proc, DMReference reference); - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - string endLabel = proc.NewLabelName(); + public override void EmitPushValue(ExpressionContext ctx) { + string endLabel = ctx.Proc.NewLabelName(); - DMReference reference = Expr.EmitReference(dmObject, proc, endLabel); - EmitOp(dmObject, proc, reference, endLabel); + DMReference reference = Expr.EmitReference(ctx, endLabel); + EmitOp(ctx.Proc, reference); - proc.AddLabel(endLabel); + ctx.Proc.AddLabel(endLabel); } } // ++x internal sealed class PreIncrement(Location location, DMExpression expr) : AssignmentUnaryOp(location, expr) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { + protected override void EmitOp(DMProc proc, DMReference reference) { proc.PushFloat(1); proc.Append(reference); } @@ -77,14 +77,14 @@ protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference refer // x++ internal sealed class PostIncrement(Location location, DMExpression expr) : AssignmentUnaryOp(location, expr) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { + protected override void EmitOp(DMProc proc, DMReference reference) { proc.Increment(reference); } } // --x internal sealed class PreDecrement(Location location, DMExpression expr) : AssignmentUnaryOp(location, expr) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { + protected override void EmitOp(DMProc proc, DMReference reference) { proc.PushFloat(1); proc.Remove(reference); } @@ -92,31 +92,33 @@ protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference refer // x-- internal sealed class PostDecrement(Location location, DMExpression expr) : AssignmentUnaryOp(location, expr) { - protected override void EmitOp(DMObject dmObject, DMProc proc, DMReference reference, string endLabel) { + protected override void EmitOp(DMProc proc, DMReference reference) { proc.Decrement(reference); } } // &x internal sealed class PointerRef(Location location, DMExpression expr) : UnaryOp(location, expr) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - Expr.EmitPushValue(dmObject, proc); - DMCompiler.UnimplementedWarning(Location, "Pointers are currently unimplemented and identifiers will be treated as normal variables."); + public override void EmitPushValue(ExpressionContext ctx) { + Expr.EmitPushValue(ctx); + ctx.Compiler.UnimplementedWarning(Location, "Pointers are currently unimplemented and identifiers will be treated as normal variables."); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - return Expr.EmitReference(dmObject, proc, endLabel, shortCircuitMode); + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + return Expr.EmitReference(ctx, endLabel, shortCircuitMode); } } // *x internal sealed class PointerDeref(Location location, DMExpression expr) : UnaryOp(location, expr) { - public override void EmitPushValue(DMObject dmObject, DMProc proc) { - Expr.EmitPushValue(dmObject, proc); - DMCompiler.UnimplementedWarning(Location, "Pointers are currently unimplemented and identifiers will be treated as normal variables."); + public override void EmitPushValue(ExpressionContext ctx) { + Expr.EmitPushValue(ctx); + ctx.Compiler.UnimplementedWarning(Location, "Pointers are currently unimplemented and identifiers will be treated as normal variables."); } - public override DMReference EmitReference(DMObject dmObject, DMProc proc, string endLabel, ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { - return Expr.EmitReference(dmObject, proc, endLabel, shortCircuitMode); + public override DMReference EmitReference(ExpressionContext ctx, string endLabel, + ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) { + return Expr.EmitReference(ctx, endLabel, shortCircuitMode); } } diff --git a/DMCompiler/DMCompiler.cs b/DMCompiler/DMCompiler.cs index dac43cfca0..65cac0c3b9 100644 --- a/DMCompiler/DMCompiler.cs +++ b/DMCompiler/DMCompiler.cs @@ -17,19 +17,31 @@ namespace DMCompiler; -//TODO: Make this not a static class -public static class DMCompiler { - public static int ErrorCount; - public static int WarningCount; - public static HashSet UniqueEmissions = new(); - public static DMCompilerSettings Settings; - public static IReadOnlyList ResourceDirectories => _resourceDirectories; - - private static readonly DMCompilerConfiguration Config = new(); - private static readonly List _resourceDirectories = new(); - private static DateTime _compileStartTime; - - public static bool Compile(DMCompilerSettings settings) { +public class DMCompiler { + public int ErrorCount; + public int WarningCount; + public HashSet UniqueEmissions = new(); + public DMCompilerSettings Settings; + public IReadOnlyList ResourceDirectories => _resourceDirectories; + + private readonly DMCompilerConfiguration Config = new(); + private readonly List _resourceDirectories = new(); + private DateTime _compileStartTime; + + internal readonly DMCodeTree DMCodeTree; + internal readonly DMObjectTree DMObjectTree; + internal readonly DMProc GlobalInitProc; + + public DMCompiler() { + DMCodeTree = new(this); + DMObjectTree = new(this); + GlobalInitProc = new(this, -1, DMObjectTree.Root, null); + } + + public bool Compile(DMCompilerSettings settings) { + if (_compileStartTime != default) + throw new Exception("Create a new DMCompiler to compile again"); + ErrorCount = 0; WarningCount = 0; UniqueEmissions.Clear(); @@ -51,13 +63,13 @@ public static bool Compile(DMCompilerSettings settings) { ForcedWarning("Unimplemented proc & var warnings are currently suppressed"); } - DMPreprocessor preprocessor = Preprocess(settings.Files, settings.MacroDefines); + DMPreprocessor preprocessor = Preprocess(this, settings.Files, settings.MacroDefines); bool successfulCompile = preprocessor is not null && Compile(preprocessor); if (successfulCompile) { //Output file is the first file with the extension changed to .json string outputFile = Path.ChangeExtension(settings.Files[0], "json"); - List maps = ConvertMaps(preprocessor.IncludedMaps); + List maps = ConvertMaps(this, preprocessor.IncludedMaps); if (ErrorCount > 0) { successfulCompile = false; @@ -82,15 +94,15 @@ public static bool Compile(DMCompilerSettings settings) { return successfulCompile; } - public static void AddResourceDirectory(string dir) { + public void AddResourceDirectory(string dir) { dir = dir.Replace('\\', Path.DirectorySeparatorChar); _resourceDirectories.Add(dir); } - private static DMPreprocessor? Preprocess(List files, Dictionary? macroDefines) { + private DMPreprocessor? Preprocess(DMCompiler compiler, List files, Dictionary? macroDefines) { DMPreprocessor? Build() { - DMPreprocessor preproc = new DMPreprocessor(true); + DMPreprocessor preproc = new DMPreprocessor(compiler, true); if (macroDefines != null) { foreach (var (key, value) in macroDefines) { preproc.DefineMacro(key, value); @@ -158,9 +170,9 @@ public static void AddResourceDirectory(string dir) { return Build(); } - private static bool Compile(IEnumerable preprocessedTokens) { + private bool Compile(IEnumerable preprocessedTokens) { DMLexer dmLexer = new DMLexer(null, preprocessedTokens); - DMParser dmParser = new DMParser(dmLexer); + DMParser dmParser = new DMParser(this, dmLexer); VerbosePrint("Parsing"); DMASTFile astFile = dmParser.File(); @@ -169,12 +181,13 @@ private static bool Compile(IEnumerable preprocessedTokens) { VerbosePrint("Constant folding"); astSimplifier.FoldAst(astFile); - DMCodeTreeBuilder.BuildCodeTree(astFile); + DMCodeTreeBuilder dmCodeTreeBuilder = new(this); + dmCodeTreeBuilder.BuildCodeTree(astFile); return ErrorCount == 0; } - public static void Emit(CompilerEmission emission) { + public void Emit(CompilerEmission emission) { switch (emission.Level) { case ErrorLevel.Disabled: return; @@ -196,7 +209,7 @@ public static void Emit(CompilerEmission emission) { /// Emits the given warning, according to its ErrorLevel as set in our config. /// True if the warning was an error, false if not. - public static bool Emit(WarningCode code, Location loc, string message) { + public bool Emit(WarningCode code, Location loc, string message) { ErrorLevel level = Config.ErrorConfig[code]; Emit(new CompilerEmission(level, code, loc, message)); return level == ErrorLevel.Error; @@ -206,12 +219,12 @@ public static bool Emit(WarningCode code, Location loc, string message) { /// To be used when the compiler MUST ALWAYS give an error.
/// Completely ignores the warning configuration. Use wisely! /// - public static void ForcedError(string message) { + public void ForcedError(string message) { ForcedError(Location.Internal, message); } /// - public static void ForcedError(Location loc, string message) { + public void ForcedError(Location loc, string message) { Console.WriteLine(new CompilerEmission(ErrorLevel.Error, loc, message).ToString()); ErrorCount++; } @@ -220,43 +233,43 @@ public static void ForcedError(Location loc, string message) { /// To be used when the compiler MUST ALWAYS give a warning.
/// Completely ignores the warning configuration. Use wisely! /// - public static void ForcedWarning(string message) { + public void ForcedWarning(string message) { Console.WriteLine(new CompilerEmission(ErrorLevel.Warning, Location.Internal, message).ToString()); WarningCount++; } /// - public static void ForcedWarning(Location loc, string message) { + public void ForcedWarning(Location loc, string message) { Console.WriteLine(new CompilerEmission(ErrorLevel.Warning, loc, message).ToString()); WarningCount++; } - public static void UnimplementedWarning(Location loc, string message) { + public void UnimplementedWarning(Location loc, string message) { if (Settings.SuppressUnimplementedWarnings) return; Emit(WarningCode.UnimplementedAccess, loc, message); } - public static void VerbosePrint(string message) { + public void VerbosePrint(string message) { if (!Settings.Verbose) return; TimeSpan duration = DateTime.Now - _compileStartTime; Console.WriteLine($"{duration.ToString(@"mm\:ss\.fffffff")}: {message}"); } - private static List ConvertMaps(List mapPaths) { + private List ConvertMaps(DMCompiler compiler, List mapPaths) { List maps = new(); int zOffset = 0; foreach (string mapPath in mapPaths) { VerbosePrint($"Converting map {mapPath}"); - DMPreprocessor preprocessor = new DMPreprocessor(false); + DMPreprocessor preprocessor = new DMPreprocessor(compiler, false); preprocessor.PreprocessFile(Path.GetDirectoryName(mapPath), Path.GetFileName(mapPath), false); DMLexer lexer = new DMLexer(mapPath, preprocessor); - DMMParser parser = new DMMParser(lexer, zOffset); + DMMParser parser = new DMMParser(this, lexer, zOffset); DreamMapJson map = parser.ParseMap(); zOffset = Math.Max(zOffset + 1, map.MaxZ); @@ -266,7 +279,7 @@ private static List ConvertMaps(List mapPaths) { return maps; } - private static string SaveJson(List maps, string interfaceFile, string outputFile) { + private string SaveJson(List maps, string interfaceFile, string outputFile) { var jsonRep = DMObjectTree.CreateJsonRepresentation(); var compiledDream = new DreamCompiledJson { Metadata = new DreamCompiledJsonMetadata { Version = OpcodeVerifier.GetOpcodesHash() }, @@ -280,8 +293,8 @@ private static string SaveJson(List maps, string interfaceFile, st Procs = jsonRep.Item2 }; - if (DMCodeTree.GlobalInitProc.AnnotatedBytecode.GetLength() > 0) - compiledDream.GlobalInitProc = DMCodeTree.GlobalInitProc.GetJsonRepresentation(); + if (GlobalInitProc.AnnotatedBytecode.GetLength() > 0) + compiledDream.GlobalInitProc = GlobalInitProc.GetJsonRepresentation(); if (DMObjectTree.Globals.Count > 0) { GlobalListJson globalListJson = new GlobalListJson { @@ -299,7 +312,7 @@ private static string SaveJson(List maps, string interfaceFile, st DMVariable global = DMObjectTree.Globals[i]; globalListJson.Names.Add(global.Name); - if (!global.TryAsJsonRepresentation(out var globalJson)) + if (!global.TryAsJsonRepresentation(this, out var globalJson)) ForcedError(global.Value.Location, $"Failed to serialize global {global.Name}"); if (globalJson != null) { @@ -331,7 +344,7 @@ private static string SaveJson(List maps, string interfaceFile, st return string.Empty; } - public static void DefineFatalErrors() { + public void DefineFatalErrors() { foreach (WarningCode code in Enum.GetValues()) { if((int)code < 1_000) { Config.ErrorConfig[code] = ErrorLevel.Error; @@ -342,7 +355,7 @@ public static void DefineFatalErrors() { /// /// This method also enforces the rule that all emissions with codes less than 1000 are mandatory errors. /// - public static void CheckAllPragmasWereSet() { + public void CheckAllPragmasWereSet() { foreach(WarningCode code in Enum.GetValues()) { if (!Config.ErrorConfig.ContainsKey(code)) { ForcedWarning($"Warning #{(int)code:d4} '{code.ToString()}' was never declared as error, warning, notice, or disabled."); @@ -351,11 +364,11 @@ public static void CheckAllPragmasWereSet() { } } - public static void SetPragma(WarningCode code, ErrorLevel level) { + public void SetPragma(WarningCode code, ErrorLevel level) { Config.ErrorConfig[code] = level; } - public static ErrorLevel CodeToLevel(WarningCode code) { + public ErrorLevel CodeToLevel(WarningCode code) { if (!Config.ErrorConfig.TryGetValue(code, out var ret)) throw new Exception($"Failed to find error level for code {code}"); diff --git a/DMCompiler/DreamPath.cs b/DMCompiler/DreamPath.cs index c458aee163..1812e745a3 100644 --- a/DMCompiler/DreamPath.cs +++ b/DMCompiler/DreamPath.cs @@ -94,8 +94,8 @@ public DreamPath(PathType type, string[] elements) { Normalize(true); } - public DMValueType GetAtomType() { - if (!DMObjectTree.TryGetDMObject(this, out var dmType)) + internal DMValueType GetAtomType(DMCompiler compiler) { + if (!compiler.DMObjectTree.TryGetDMObject(this, out var dmType)) return DMValueType.Anything; if (dmType.IsSubtypeOf(Obj)) diff --git a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs index 4319bc2d7f..7b5b3631a0 100644 --- a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs +++ b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs @@ -8,7 +8,7 @@ namespace DMCompiler.Optimizer; * Provides a wrapper about BinaryWriter that stores information about the bytecode * for optimization and debugging. */ -internal class AnnotatedByteCodeWriter { +internal class AnnotatedByteCodeWriter(DMCompiler compiler) { private readonly List _annotatedBytecode = new(250); // 1/6th of max size for bytecode in tgstation @@ -34,7 +34,7 @@ public List GetAnnotatedBytecode() { public void WriteOpcode(DreamProcOpcode opcode, Location location) { _location = location; if (_requiredArgs.Count > 0) { - DMCompiler.ForcedError(location, "Expected argument"); + compiler.ForcedError(location, "Expected argument"); } var metadata = OpcodeMetadataCache.GetMetadata(opcode); @@ -62,11 +62,11 @@ public void WriteOpcode(DreamProcOpcode opcode, Location location) { public void WriteFloat(float val, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.Float) { - DMCompiler.ForcedError(location, "Expected floating argument"); + compiler.ForcedError(location, "Expected floating argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeFloat(val, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeFloat(val, location)); } /// @@ -77,11 +77,11 @@ public void WriteFloat(float val, Location location) { public void WriteArgumentType(DMCallArgumentsType argType, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.ArgType) { - DMCompiler.ForcedError(location, "Expected argument type argument"); + compiler.ForcedError(location, "Expected argument type argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeArgumentType(argType, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeArgumentType(argType, location)); } /// @@ -92,11 +92,11 @@ public void WriteArgumentType(DMCallArgumentsType argType, Location location) { public void WriteStackDelta(int delta, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.StackDelta) { - DMCompiler.ForcedError(location, "Expected stack delta argument"); + compiler.ForcedError(location, "Expected stack delta argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeStackDelta(delta, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeStackDelta(delta, location)); } /// @@ -106,12 +106,12 @@ public void WriteStackDelta(int delta, Location location) { /// The location of the type in the source code public void WriteType(DMValueType type, Location location) { if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.TypeId) { - DMCompiler.ForcedError(location, "Expected type argument"); + compiler.ForcedError(location, "Expected type argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeType(type, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeType(type, location)); } /// @@ -122,12 +122,12 @@ public void WriteType(DMValueType type, Location location) { public void WriteString(string value, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.String) { - DMCompiler.ForcedError(location, "Expected string argument"); + compiler.ForcedError(location, "Expected string argument"); } _requiredArgs.Pop(); - int stringId = DMObjectTree.AddString(value); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeString(stringId, location)); + int stringId = compiler.DMObjectTree.AddString(value); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeString(stringId, location)); } /// @@ -142,12 +142,12 @@ public void WriteFilterId(int filterTypeId, DreamPath filterPath, Location locat _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.FilterId) { - DMCompiler.ForcedError(location, "Expected filter argument"); + compiler.ForcedError(location, "Expected filter argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeFilter(filterTypeId, filterPath, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeFilter(filterTypeId, filterPath, location)); } /// @@ -158,15 +158,15 @@ public void WriteFilterId(int filterTypeId, DreamPath filterPath, Location locat public void WriteListSize(int value, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.ListSize) { - DMCompiler.ForcedError(location, "Expected list size argument"); + compiler.ForcedError(location, "Expected list size argument"); } if (value < 0) { - DMCompiler.ForcedError(location, "List size cannot be negative"); + compiler.ForcedError(location, "List size cannot be negative"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeListSize(value, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeListSize(value, location)); } /// @@ -177,10 +177,10 @@ public void WriteListSize(int value, Location location) { public void WriteLabel(string s, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Pop() != OpcodeArgType.Label) { - DMCompiler.ForcedError(location, "Expected label argument"); + compiler.ForcedError(location, "Expected label argument"); } - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeLabel(s, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeLabel(s, location)); _unresolvedLabelsInAnnotatedBytecode.Add((_annotatedBytecode.Count - 1, s)); } @@ -191,7 +191,7 @@ public void ResolveCodeLabelReferences(Stack pendingL // Failed to find the label in the given context if (label == null) { - DMCompiler.Emit( + compiler.Emit( WarningCode.ItemDoesntExist, reference.Location, $"Label \"{reference.Identifier}\" unreachable from scope or does not exist" @@ -199,7 +199,7 @@ public void ResolveCodeLabelReferences(Stack pendingL // Not cleaning away the placeholder will emit another compiler error // let's not do that _unresolvedLabelsInAnnotatedBytecode.RemoveAt( - _unresolvedLabelsInAnnotatedBytecode.FindIndex(((long Position, string LabelName) o) => + _unresolvedLabelsInAnnotatedBytecode.FindIndex(o => o.LabelName == reference.Placeholder) ); continue; @@ -239,7 +239,7 @@ public void ResizeStack(int sizeDelta) { _maxStackSize = Math.Max(_currentStackSize, _maxStackSize); if (_currentStackSize < 0 && !_negativeStackSizeError) { _negativeStackSizeError = true; - DMCompiler.ForcedError(_location, "Negative stack size"); + compiler.ForcedError(_location, "Negative stack size"); } } @@ -253,109 +253,109 @@ public int GetMaxStackSize() { public void WriteResource(string value, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.Resource) { - DMCompiler.ForcedError(location, "Expected resource argument"); + compiler.ForcedError(location, "Expected resource argument"); } _requiredArgs.Pop(); - int stringId = DMObjectTree.AddString(value); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeResource(stringId, location)); + int stringId = compiler.DMObjectTree.AddString(value); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeResource(stringId, location)); } public void WriteTypeId(int typeId, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.TypeId) { - DMCompiler.ForcedError(location, "Expected TypeID argument"); + compiler.ForcedError(location, "Expected TypeID argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeTypeId(typeId, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeTypeId(typeId, location)); } public void WriteProcId(int procId, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.ProcId) { - DMCompiler.ForcedError(location, "Expected ProcID argument"); + compiler.ForcedError(location, "Expected ProcID argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeProcId(procId, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeProcId(procId, location)); } public void WriteEnumeratorId(int enumeratorId, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.EnumeratorId) { - DMCompiler.ForcedError(location, "Expected EnumeratorID argument"); + compiler.ForcedError(location, "Expected EnumeratorID argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeEnumeratorId(enumeratorId, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeEnumeratorId(enumeratorId, location)); } public void WriteFormatCount(int formatCount, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.FormatCount) { - DMCompiler.ForcedError(location, "Expected format count argument"); + compiler.ForcedError(location, "Expected format count argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeFormatCount(formatCount, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeFormatCount(formatCount, location)); } public void WritePickCount(int count, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.PickCount) { - DMCompiler.ForcedError(location, "Expected pick count argument"); + compiler.ForcedError(location, "Expected pick count argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodePickCount(count, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodePickCount(count, location)); } public void WriteConcatCount(int count, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.ConcatCount) { - DMCompiler.ForcedError(location, "Expected concat count argument"); + compiler.ForcedError(location, "Expected concat count argument"); } _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeConcatCount(count, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeConcatCount(count, location)); } public void WriteReference(DMReference reference, Location location, bool affectStack = true) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Pop() != OpcodeArgType.Reference) { - DMCompiler.ForcedError(location, "Expected reference argument"); + compiler.ForcedError(location, "Expected reference argument"); } switch (reference.RefType) { case DMReference.Type.Argument: case DMReference.Type.Local: _annotatedBytecode[^1] - .AddArg(new AnnotatedBytecodeReference(reference.RefType, reference.Index, location)); + .AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, reference.Index, location)); break; case DMReference.Type.Global: case DMReference.Type.GlobalProc: _annotatedBytecode[^1] - .AddArg(new AnnotatedBytecodeReference(reference.RefType, reference.Index, location)); + .AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, reference.Index, location)); break; case DMReference.Type.Field: - int fieldId = DMObjectTree.AddString(reference.Name); + int fieldId = compiler.DMObjectTree.AddString(reference.Name); _annotatedBytecode[^1] - .AddArg(new AnnotatedBytecodeReference(reference.RefType, fieldId, location)); + .AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, fieldId, location)); ResizeStack(affectStack ? -1 : 0); break; case DMReference.Type.SrcProc: case DMReference.Type.SrcField: - fieldId = DMObjectTree.AddString(reference.Name); + fieldId = compiler.DMObjectTree.AddString(reference.Name); _annotatedBytecode[^1] - .AddArg(new AnnotatedBytecodeReference(reference.RefType, fieldId, location)); + .AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, fieldId, location)); break; case DMReference.Type.ListIndex: - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeReference(reference.RefType, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, location)); ResizeStack(affectStack ? -2 : 0); break; @@ -366,11 +366,11 @@ public void WriteReference(DMReference reference, Location location, bool affect case DMReference.Type.World: case DMReference.Type.Usr: case DMReference.Type.Invalid: - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeReference(reference.RefType, location)); + _annotatedBytecode[^1].AddArg(compiler, new AnnotatedBytecodeReference(reference.RefType, location)); break; default: - DMCompiler.ForcedError(_location, $"Encountered unknown reference type {reference.RefType}"); + compiler.ForcedError(_location, $"Encountered unknown reference type {reference.RefType}"); break; } } diff --git a/DMCompiler/Optimizer/AnnotatedBytecode.cs b/DMCompiler/Optimizer/AnnotatedBytecode.cs index cad59387d0..4231539f36 100644 --- a/DMCompiler/Optimizer/AnnotatedBytecode.cs +++ b/DMCompiler/Optimizer/AnnotatedBytecode.cs @@ -4,8 +4,8 @@ namespace DMCompiler.Optimizer; -public interface IAnnotatedBytecode { - public void AddArg(IAnnotatedBytecode arg); +internal interface IAnnotatedBytecode { + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg); void SetLocation(IAnnotatedBytecode location); void SetLocation(Location location); public Location GetLocation(); @@ -108,7 +108,7 @@ private bool MatchArgs(OpcodeArgType requiredArg, IAnnotatedBytecode arg) { } } - public void AddArg(IAnnotatedBytecode arg) { + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { _args.Add(arg); } @@ -157,8 +157,8 @@ public AnnotatedBytecodeVariable(int popOff, Location location) { Location = location; } - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a variable"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a variable"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -174,17 +174,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeInteger : IAnnotatedBytecode { - public Location Location; - public int Value; - - public AnnotatedBytecodeInteger(int value, Location location) { - Value = value; - Location = location; - } +internal sealed class AnnotatedBytecodeInteger(int value, Location location) : IAnnotatedBytecode { + public Location Location = location; + public int Value = value; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to an integer"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to an integer"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -200,17 +195,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeFloat : IAnnotatedBytecode { - public Location Location; - public float Value; - - public AnnotatedBytecodeFloat(float value, Location location) { - Value = value; - Location = location; - } +internal sealed class AnnotatedBytecodeFloat(float value, Location location) : IAnnotatedBytecode { + public Location Location = location; + public float Value = value; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a float"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a float"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -226,17 +216,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeString : IAnnotatedBytecode { - public int Id; - public Location Location; - - public AnnotatedBytecodeString(int id, Location location) { - Id = id; - Location = location; - } +internal sealed class AnnotatedBytecodeString(int id, Location location) : IAnnotatedBytecode { + public int Id = id; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a string"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a string"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -252,17 +237,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeArgumentType : IAnnotatedBytecode { - public Location Location; - public DMCallArgumentsType Value; - - public AnnotatedBytecodeArgumentType(DMCallArgumentsType value, Location location) { - Value = value; - Location = location; - } +internal sealed class AnnotatedBytecodeArgumentType(DMCallArgumentsType value, Location location) : IAnnotatedBytecode { + public Location Location = location; + public DMCallArgumentsType Value = value; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to an argument type"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to an argument type"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -278,17 +258,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeType : IAnnotatedBytecode { - public Location Location; - public DMValueType Value; - - public AnnotatedBytecodeType(DMValueType value, Location location) { - Value = value; - Location = location; - } +internal sealed class AnnotatedBytecodeType(DMValueType value, Location location) : IAnnotatedBytecode { + public Location Location = location; + public DMValueType Value = value; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a type"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a type"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -304,17 +279,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeTypeId : IAnnotatedBytecode { - public Location Location; - public int TypeId; - - public AnnotatedBytecodeTypeId(int typeId, Location location) { - TypeId = typeId; - Location = location; - } +internal sealed class AnnotatedBytecodeTypeId(int typeId, Location location) : IAnnotatedBytecode { + public Location Location = location; + public int TypeId = typeId; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a type"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a type"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -334,8 +304,8 @@ internal sealed class AnnotatedBytecodeProcId(int procId, Location location) : I public Location Location = location; public int ProcId = procId; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a type"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a type"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -355,8 +325,8 @@ internal sealed class AnnotatedBytecodeEnumeratorId(int enumeratorId, Location l public Location Location = location; public int EnumeratorId = enumeratorId; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a type"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a type"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -372,17 +342,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeFormatCount : IAnnotatedBytecode { - public int Count; - public Location Location; - - public AnnotatedBytecodeFormatCount(int count, Location location) { - Count = count; - Location = location; - } +internal sealed class AnnotatedBytecodeFormatCount(int count, Location location) : IAnnotatedBytecode { + public int Count = count; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a format count"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a format count"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -398,17 +363,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeStackDelta : IAnnotatedBytecode { - public int Delta; - public Location Location; - - public AnnotatedBytecodeStackDelta(int delta, Location location) { - Delta = delta; - Location = location; - } +internal sealed class AnnotatedBytecodeStackDelta(int delta, Location location) : IAnnotatedBytecode { + public int Delta = delta; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a stack delta"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a stack delta"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -424,17 +384,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeListSize : IAnnotatedBytecode { - public Location Location; - public int Size; - - public AnnotatedBytecodeListSize(int size, Location location) { - Size = size; - Location = location; - } +internal sealed class AnnotatedBytecodeListSize(int size, Location location) : IAnnotatedBytecode { + public Location Location = location; + public int Size = size; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a list size"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a list size"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -450,17 +405,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodePickCount : IAnnotatedBytecode { - public int Count; - public Location Location; - - public AnnotatedBytecodePickCount(int count, Location location) { - Count = count; - Location = location; - } +internal sealed class AnnotatedBytecodePickCount(int count, Location location) : IAnnotatedBytecode { + public int Count = count; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a pick count"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a pick count"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -476,17 +426,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeConcatCount : IAnnotatedBytecode { - public int Count; - public Location Location; - - public AnnotatedBytecodeConcatCount(int count, Location location) { - Count = count; - Location = location; - } +internal sealed class AnnotatedBytecodeConcatCount(int count, Location location) : IAnnotatedBytecode { + public int Count = count; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a concat count"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a concat count"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -502,17 +447,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeResource : IAnnotatedBytecode { - public Location Location; - public int ResourceId; - - public AnnotatedBytecodeResource(int rid, Location location) { - ResourceId = rid; - Location = location; - } +internal sealed class AnnotatedBytecodeResource(int rid, Location location) : IAnnotatedBytecode { + public Location Location = location; + public int ResourceId = rid; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a resource"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a resource"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -528,17 +468,12 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeLabel : IAnnotatedBytecode { - public string LabelName; - public Location Location; - - public AnnotatedBytecodeLabel(string labelName, Location location) { - LabelName = labelName; - Location = location; - } +internal sealed class AnnotatedBytecodeLabel(string labelName, Location location) : IAnnotatedBytecode { + public string LabelName = labelName; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a label"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a label"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -554,19 +489,14 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeFilter : IAnnotatedBytecode { - public DreamPath FilterPath; - public int FilterTypeId; - public Location Location; - - public AnnotatedBytecodeFilter(int filterTypeId, DreamPath filterPath, Location location) { - FilterTypeId = filterTypeId; - FilterPath = filterPath; - Location = location; - } +internal sealed class AnnotatedBytecodeFilter(int filterTypeId, DreamPath filterPath, Location location) + : IAnnotatedBytecode { + public DreamPath FilterPath = filterPath; + public int FilterTypeId = filterTypeId; + public Location Location = location; - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a filter"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a filter"); } public void SetLocation(IAnnotatedBytecode loc) { @@ -582,24 +512,17 @@ public Location GetLocation() { } } -internal sealed class AnnotatedBytecodeReference : IAnnotatedBytecode { - public int Index; - public Location Location; - public DMReference.Type RefType; - - public AnnotatedBytecodeReference(DMReference.Type refType, int index, Location location) { - RefType = refType; - Index = index; - Location = location; - } +internal sealed class AnnotatedBytecodeReference(DMReference.Type refType, int index, Location location) + : IAnnotatedBytecode { + public int Index = index; + public Location Location = location; + public DMReference.Type RefType = refType; - public AnnotatedBytecodeReference(DMReference.Type refType, Location location) { - RefType = refType; - Location = location; + public AnnotatedBytecodeReference(DMReference.Type refType, Location location) : this(refType, 0, location) { } - public void AddArg(IAnnotatedBytecode arg) { - DMCompiler.ForcedError(Location, "Cannot add args to a reference"); + public void AddArg(DMCompiler compiler, IAnnotatedBytecode arg) { + compiler.ForcedError(Location, "Cannot add args to a reference"); } public void SetLocation(IAnnotatedBytecode loc) { diff --git a/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs b/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs index 1bd4000427..497a31d558 100644 --- a/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs +++ b/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs @@ -1,14 +1,13 @@ using System.IO; using DMCompiler.Bytecode; using DMCompiler.Compiler; -using DMCompiler.DM; using DMCompiler.Json; namespace DMCompiler.Optimizer; -internal class AnnotatedBytecodeSerializer { +internal class AnnotatedBytecodeSerializer(DMCompiler compiler) { private readonly List _localVariables = new(); - private BinaryWriter _bytecodeWriter; + private BinaryWriter? _bytecodeWriter; private Dictionary _labels = new(); private List<(long Position, string LabelName)> _unresolvedLabels = new(); private int _lastFileId = -1; @@ -18,11 +17,8 @@ internal class AnnotatedBytecodeSerializer { public List SourceInfo = new(); - public AnnotatedBytecodeSerializer() { - _bytecodeWriter = new BinaryWriter(Bytecode); - } - public byte[]? Serialize(List annotatedBytecode) { + _bytecodeWriter ??= new BinaryWriter(Bytecode); foreach (IAnnotatedBytecode bytecodeChunk in annotatedBytecode) { if (bytecodeChunk is AnnotatedBytecodeInstruction instruction) { SerializeInstruction(instruction); @@ -58,8 +54,9 @@ public AnnotatedBytecodeSerializer() { } private void SerializeInstruction(AnnotatedBytecodeInstruction instruction) { + _bytecodeWriter ??= new BinaryWriter(Bytecode); if (instruction.Location.Line != null && (_location == null || instruction.Location.Line != _location?.Line)) { - int sourceFileId = DMObjectTree.AddString(instruction.Location.SourceFile); + int sourceFileId = compiler.DMObjectTree.AddString(instruction.Location.SourceFile); if (_lastFileId != sourceFileId) { _lastFileId = sourceFileId; SourceInfo.Add(new SourceInfoJson { @@ -149,12 +146,13 @@ private void SerializeLabel(AnnotatedBytecodeLabel label) { } private void ResolveLabels() { + _bytecodeWriter ??= new BinaryWriter(Bytecode); foreach ((long position, string labelName) in _unresolvedLabels) { if (_labels.TryGetValue(labelName, out int labelPosition)) { _bytecodeWriter.Seek((int)position, SeekOrigin.Begin); _bytecodeWriter.Write((int)labelPosition); } else { - DMCompiler.Emit(WarningCode.BadLabel, Location.Internal, + compiler.Emit(WarningCode.BadLabel, Location.Internal, "Label \"" + labelName + "\" could not be resolved"); } } @@ -164,6 +162,7 @@ private void ResolveLabels() { } public void WriteReference(AnnotatedBytecodeReference reference) { + _bytecodeWriter ??= new BinaryWriter(Bytecode); _bytecodeWriter.Write((byte)reference.RefType); switch (reference.RefType) { case DMReference.Type.Argument: @@ -196,7 +195,7 @@ public void WriteReference(AnnotatedBytecodeReference reference) { break; default: - DMCompiler.ForcedError(_location ?? Location.Unknown, $"Encountered unknown reference type {reference.RefType}"); + compiler.ForcedError(_location ?? Location.Unknown, $"Encountered unknown reference type {reference.RefType}"); break; } } diff --git a/DMCompiler/Optimizer/BytecodeOptimizer.cs b/DMCompiler/Optimizer/BytecodeOptimizer.cs index 8b1eca71d0..be97db7c19 100644 --- a/DMCompiler/Optimizer/BytecodeOptimizer.cs +++ b/DMCompiler/Optimizer/BytecodeOptimizer.cs @@ -3,16 +3,14 @@ namespace DMCompiler.Optimizer; public class BytecodeOptimizer { - public List Optimize(List input) { - if (input.Count == 0) { - return input; - } + internal void Optimize(DMCompiler compiler, List input) { + if (input.Count == 0) + return; RemoveUnreferencedLabels(input); JoinAndForwardLabels(input); RemoveUnreferencedLabels(input); - PeepholeOptimizer.RunPeephole(input); - return input; + PeepholeOptimizer.RunPeephole(compiler, input); } private static void RemoveUnreferencedLabels(List input) { diff --git a/DMCompiler/Optimizer/PeepholeOptimizations.cs b/DMCompiler/Optimizer/PeepholeOptimizations.cs index dff3823bf1..395e0dc38e 100644 --- a/DMCompiler/Optimizer/PeepholeOptimizations.cs +++ b/DMCompiler/Optimizer/PeepholeOptimizations.cs @@ -13,7 +13,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Bytecode index is outside the bounds of the input list."); } @@ -37,7 +37,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Bytecode index is outside the bounds of the input list."); } @@ -61,7 +61,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { // Ensure that we have at least two elements from the starting index to avoid out-of-bound errors if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); @@ -90,7 +90,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -117,7 +117,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); AnnotatedBytecodeReference pushVal = firstInstruction.GetArg(0); input.RemoveRange(index, 2); @@ -136,7 +136,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal); IPeepholeOptimization.ReplaceInstructions(input, index, 2, new AnnotatedBytecodeInstruction(DreamProcOpcode.ReturnFloat, [new AnnotatedBytecodeFloat(pushVal, firstInstruction.Location)])); @@ -154,7 +154,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -182,7 +182,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { int count = 0; int stackDelta = 0; @@ -217,7 +217,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { int count = 0; int stackDelta = 0; @@ -252,7 +252,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { int count = 0; int stackDelta = 0; @@ -288,7 +288,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -340,7 +340,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -366,7 +366,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -393,7 +393,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { int count = 0; int stackDelta = 0; while (index + count < input.Count && @@ -439,7 +439,7 @@ public bool CheckPreconditions(List input, int index) { return pushVal1 == pushVal2; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -480,7 +480,7 @@ public bool CheckPreconditions(List input, int index) { return pushVal1 == pushVal2; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -521,7 +521,7 @@ public bool CheckPreconditions(List input, int index) { return pushVal1 == pushVal2; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -560,7 +560,7 @@ public bool CheckPreconditions(List input, int index) { return pushVal1 == pushVal2; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Bytecode index is outside the bounds of the input list."); } @@ -588,7 +588,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { input.RemoveAt(index + 1); } } @@ -604,7 +604,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { if (index + 1 >= input.Count) { throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); } @@ -630,7 +630,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); var args = new List(1) {new AnnotatedBytecodeFloat(((~(int)pushVal1) & 0xFFFFFF), firstInstruction.Location)}; @@ -653,7 +653,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -678,7 +678,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -703,7 +703,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -728,7 +728,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -755,7 +755,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -780,7 +780,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -807,7 +807,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -834,7 +834,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -877,7 +877,7 @@ public bool CheckPreconditions(List input, int index) { return assignTarget.Equals(pushTarget); } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { // We check the input bounds in CheckPreconditions, so we can skip doing it again here AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); @@ -917,7 +917,7 @@ public bool CheckPreconditions(List input, int index) { return appendTarget.Equals(pushTarget); } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { // We check the input bounds in CheckPreconditions, so we can skip doing it again here AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); @@ -941,7 +941,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); @@ -967,7 +967,7 @@ public ReadOnlySpan GetOpcodes() { ]; } - public void Apply(List input, int index) { + public void Apply(DMCompiler compiler, List input, int index) { var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); diff --git a/DMCompiler/Optimizer/PeepholeOptimizer.cs b/DMCompiler/Optimizer/PeepholeOptimizer.cs index a7f8588389..e49852bae0 100644 --- a/DMCompiler/Optimizer/PeepholeOptimizer.cs +++ b/DMCompiler/Optimizer/PeepholeOptimizer.cs @@ -5,7 +5,7 @@ namespace DMCompiler.Optimizer; internal interface IPeepholeOptimization { public ReadOnlySpan GetOpcodes(); - public void Apply(List input, int index); + public void Apply(DMCompiler compiler, List input, int index); public bool CheckPreconditions(List input, int index) { return true; @@ -37,7 +37,7 @@ private class OptimizationTreeEntry { private static readonly Dictionary OptimizationTrees = new(); /// Setup - static PeepholeOptimizer() { + private PeepholeOptimizer(DMCompiler compiler) { var possibleTypes = typeof(PeepholeOptimizer).Assembly.GetTypes(); var optimizationTypes = new List(possibleTypes.Length); foreach (var type in possibleTypes) { @@ -53,7 +53,7 @@ static PeepholeOptimizer() { var opt = (IPeepholeOptimization)(Activator.CreateInstance(optType))!; var opcodes = opt.GetOpcodes(); if (opcodes.Length < 2) { - DMCompiler.ForcedError(Location.Internal, $"Peephole optimization {optType} must have at least 2 opcodes"); + compiler.ForcedError(Location.Internal, $"Peephole optimization {optType} must have at least 2 opcodes"); continue; } @@ -81,7 +81,7 @@ static PeepholeOptimizer() { } } - public static void RunPeephole(List input) { + public static void RunPeephole(DMCompiler compiler, List input) { OptimizationTreeEntry? currentOpt = null; int optSize = 0; @@ -92,7 +92,7 @@ int AttemptCurrentOpt(int i) { int offset; if (currentOpt.Optimization?.CheckPreconditions(input, i - optSize) is true) { - currentOpt.Optimization.Apply(input, i - optSize); + currentOpt.Optimization.Apply(compiler, input, i - optSize); offset = (optSize + 2); // Run over the new opcodes for potential further optimization } else { // This chain of opcodes did not lead to a valid optimization. diff --git a/DMCompiler/Program.cs b/DMCompiler/Program.cs index 9947983554..384f9441c1 100644 --- a/DMCompiler/Program.cs +++ b/DMCompiler/Program.cs @@ -13,12 +13,13 @@ internal struct Argument { internal static class Program { private static void Main(string[] args) { - if (!TryParseArguments(args, out DMCompilerSettings settings)) { + DMCompiler compiler = new DMCompiler(); + if (!TryParseArguments(compiler, args, out DMCompilerSettings settings)) { Environment.Exit(1); return; } - if (!DMCompiler.Compile(settings)) { + if (!compiler.Compile(settings)) { //Compile errors, exit with an error code Environment.Exit(1); } @@ -78,7 +79,7 @@ private static void PrintHelp() { Console.WriteLine("--pragma-config [file].dm : Configure the error/warning/notice/ignore level of compiler messages"); } - private static bool TryParseArguments(string[] args, out DMCompilerSettings settings) { + private static bool TryParseArguments(DMCompiler compiler, string[] args, out DMCompilerSettings settings) { settings = new DMCompilerSettings { Files = new List() }; @@ -114,7 +115,7 @@ private static bool TryParseArguments(string[] args, out DMCompilerSettings sett case "pragma-config": { if(arg.Value is null || !HasValidDMExtension(arg.Value)) { if(skipBad) { - DMCompiler.ForcedWarning($"Compiler arg 'pragma-config' requires filename of valid DM file, skipping"); + compiler.ForcedWarning($"Compiler arg 'pragma-config' requires filename of valid DM file, skipping"); continue; } @@ -128,7 +129,7 @@ private static bool TryParseArguments(string[] args, out DMCompilerSettings sett case "version": { if(arg.Value is null) { if(skipBad) { - DMCompiler.ForcedWarning("Compiler arg 'version' requires a full BYOND build (e.g. --version=514.1584), skipping"); + compiler.ForcedWarning("Compiler arg 'version' requires a full BYOND build (e.g. --version=514.1584), skipping"); continue; } @@ -139,7 +140,7 @@ private static bool TryParseArguments(string[] args, out DMCompilerSettings sett var split = arg.Value.Split('.', StringSplitOptions.RemoveEmptyEntries); if (split.Length != 2 || !int.TryParse(split[0], out _) || !int.TryParse(split[1], out _)) { // We want to make sure that they *are* ints but the preprocessor takes strings if(skipBad) { - DMCompiler.ForcedWarning("Compiler arg 'version' requires a full BYOND build (e.g. --version=514.1584), skipping"); + compiler.ForcedWarning("Compiler arg 'version' requires a full BYOND build (e.g. --version=514.1584), skipping"); continue; } @@ -160,7 +161,7 @@ private static bool TryParseArguments(string[] args, out DMCompilerSettings sett } if (skipBad) { - DMCompiler.ForcedWarning($"Invalid compiler arg '{arg.Value}', skipping"); + compiler.ForcedWarning($"Invalid compiler arg '{arg.Value}', skipping"); } else { Console.WriteLine($"Invalid arg '{arg}'"); return false; @@ -170,7 +171,7 @@ private static bool TryParseArguments(string[] args, out DMCompilerSettings sett } default: { if (skipBad) { - DMCompiler.ForcedWarning($"Unknown compiler arg '{arg.Name}', skipping"); + compiler.ForcedWarning($"Unknown compiler arg '{arg.Name}', skipping"); break; }