From 622bf1279611b8f9577d183a50ed70ff051ea619 Mon Sep 17 00:00:00 2001 From: vddCore Date: Fri, 5 Apr 2024 20:33:57 +0200 Subject: [PATCH 1/2] Add targeted function definitions. --- .../AST/Statements/FnTargetedStatement.cs | 35 ++++++ .../Parsing/Statements/Parser.FnStatement.cs | 50 ++++++-- Core/EVIL.Grammar/Traversal/AstVisitor.cs | 2 + VirtualMachine/Ceres/Ceres.csproj | 3 + .../Compiler.Statement.FnTargeted.cs | 56 +++++++++ .../Diagnostics/EvilMessageCode.cs | 1 + .../Ceres.LanguageTests.csproj | 3 + .../tests/024_fn_targeted.vil | 110 ++++++++++++++++++ 8 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 Core/EVIL.Grammar/AST/Statements/FnTargetedStatement.cs create mode 100644 VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.FnTargeted.cs create mode 100644 VirtualMachine/Tests/Ceres.LanguageTests/tests/024_fn_targeted.vil diff --git a/Core/EVIL.Grammar/AST/Statements/FnTargetedStatement.cs b/Core/EVIL.Grammar/AST/Statements/FnTargetedStatement.cs new file mode 100644 index 0000000..836caa8 --- /dev/null +++ b/Core/EVIL.Grammar/AST/Statements/FnTargetedStatement.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using EVIL.Grammar.AST.Base; +using EVIL.Grammar.AST.Miscellaneous; + +namespace EVIL.Grammar.AST.Statements +{ + public class FnTargetedStatement : Statement + { + public IdentifierNode PrimaryIdentifier { get; } + public IdentifierNode SecondaryIdentifier { get; } + public ParameterList ParameterList { get; } + public Statement Statement { get; } + public List Attributes { get; } + + public FnTargetedStatement( + IdentifierNode primaryIdentifier, + IdentifierNode secondaryIdentifier, + ParameterList parameterList, + Statement statement, + List attributes) + { + PrimaryIdentifier = primaryIdentifier; + SecondaryIdentifier = secondaryIdentifier; + ParameterList = parameterList; + Statement = statement; + Attributes = attributes; + + Reparent(PrimaryIdentifier); + Reparent(SecondaryIdentifier); + Reparent(ParameterList); + Reparent(Statement); + Reparent(Attributes); + } + } +} \ No newline at end of file diff --git a/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs b/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs index e93c155..fe9e267 100644 --- a/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs +++ b/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using EVIL.Grammar.AST.Base; using EVIL.Grammar.AST.Miscellaneous; +using EVIL.Grammar.AST.Statements; using EVIL.Grammar.AST.Statements.TopLevel; using EVIL.Lexical; @@ -8,7 +9,7 @@ namespace EVIL.Grammar.Parsing { public partial class Parser { - private FnStatement FnStatement() + private Statement FnStatement() { var attributes = new List(); @@ -38,8 +39,26 @@ private FnStatement FnStatement() (line, col) ); } + + IdentifierNode? primaryIdentifier = null; + IdentifierNode? secondaryIdentifier = null; + + primaryIdentifier = Identifier(); + + if (CurrentToken.Type == TokenType.DoubleColon) + { + if (!isLocalDefinition) + { + throw new ParserException( + "`loc' specifier is required for targeted definitions.", + (line, col) + ); + } + + Match(Token.DoubleColon); + secondaryIdentifier = Identifier(); + } - var functionIdentifier = Identifier(); var parameterList = ParameterList(); Statement statement; @@ -60,13 +79,26 @@ private FnStatement FnStatement() ); } - return new FnStatement( - functionIdentifier, - parameterList, - statement, - attributes, - isLocalDefinition - ) { Line = line, Column = col }; + if (secondaryIdentifier == null) + { + return new FnStatement( + primaryIdentifier, + parameterList, + statement, + attributes, + isLocalDefinition + ) { Line = line, Column = col }; + } + else + { + return new FnTargetedStatement( + primaryIdentifier, + secondaryIdentifier, + parameterList, + statement, + attributes + ) { Line = line, Column = col }; + } } } } \ No newline at end of file diff --git a/Core/EVIL.Grammar/Traversal/AstVisitor.cs b/Core/EVIL.Grammar/Traversal/AstVisitor.cs index c24a70f..24d4a9d 100644 --- a/Core/EVIL.Grammar/Traversal/AstVisitor.cs +++ b/Core/EVIL.Grammar/Traversal/AstVisitor.cs @@ -36,6 +36,7 @@ protected AstVisitor() { typeof(SymbolReferenceExpression), (n) => Visit((SymbolReferenceExpression)n) }, { typeof(ValStatement), (n) => Visit((ValStatement)n) }, { typeof(FnStatement), (n) => Visit((FnStatement)n) }, + { typeof(FnTargetedStatement), (n) => Visit((FnTargetedStatement)n) }, { typeof(InvocationExpression), (n) => Visit((InvocationExpression)n) }, { typeof(IfStatement), (n) => Visit((IfStatement)n) }, { typeof(ForStatement), (n) => Visit((ForStatement)n) }, @@ -102,6 +103,7 @@ public virtual void Visit(AstNode node) public abstract void Visit(SymbolReferenceExpression symbolReferenceExpression); public abstract void Visit(ValStatement valStatement); public abstract void Visit(FnStatement fnStatement); + public abstract void Visit(FnTargetedStatement fnTargetedStatement); public abstract void Visit(InvocationExpression invocationExpression); public abstract void Visit(IfStatement ifStatement); public abstract void Visit(ForStatement forStatement); diff --git a/VirtualMachine/Ceres/Ceres.csproj b/VirtualMachine/Ceres/Ceres.csproj index 61fa5f7..59c335b 100644 --- a/VirtualMachine/Ceres/Ceres.csproj +++ b/VirtualMachine/Ceres/Ceres.csproj @@ -173,5 +173,8 @@ Compiler.cs + + Compiler.cs + \ No newline at end of file diff --git a/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.FnTargeted.cs b/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.FnTargeted.cs new file mode 100644 index 0000000..01b5dfd --- /dev/null +++ b/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.FnTargeted.cs @@ -0,0 +1,56 @@ +using Ceres.ExecutionEngine.Diagnostics; +using Ceres.TranslationEngine.Diagnostics; +using EVIL.Grammar.AST.Statements; + +namespace Ceres.TranslationEngine +{ + public partial class Compiler + { + public override void Visit(FnTargetedStatement fnTargetedStatement) + { + var id = InNamedSubChunkDo( + $"{fnTargetedStatement.PrimaryIdentifier.Name}::{fnTargetedStatement.SecondaryIdentifier.Name}", + () => + { + InNewClosedScopeDo(() => + { + Chunk.DebugDatabase.DefinedOnLine = fnTargetedStatement.Line; + Chunk.MarkSelfAware(); + /* implicit `self' */ Chunk.AllocateParameter(); + + Visit(fnTargetedStatement.ParameterList); + Visit(fnTargetedStatement.Statement); + + FinalizeChunk(); + }); + }, out var wasExistingReplaced, out var replacedChunk + ); + + if (wasExistingReplaced) + { + Log.EmitWarning( + $"Targeted function '{replacedChunk.Name}' defined on line {fnTargetedStatement.Line} is " + + $"re-defining a previously defined function of the same name in {replacedChunk.DebugDatabase.DefinedInFile} on line {fnTargetedStatement.Line}.", + CurrentFileName, + EvilMessageCode.FnTargetedStatementRedefinedExistingChunk, + fnTargetedStatement.Line, + fnTargetedStatement.Column + ); + } + + Chunk.CodeGenerator.Emit( + OpCode.LDCNK, + id + ); + + EmitVarGet(fnTargetedStatement.PrimaryIdentifier.Name); + + Chunk.CodeGenerator.Emit( + OpCode.LDSTR, + (int)Chunk.StringPool.FetchOrAdd(fnTargetedStatement.SecondaryIdentifier.Name) + ); + + Chunk.CodeGenerator.Emit(OpCode.ELSET); + } + } +} \ No newline at end of file diff --git a/VirtualMachine/Ceres/TranslationEngine/Diagnostics/EvilMessageCode.cs b/VirtualMachine/Ceres/TranslationEngine/Diagnostics/EvilMessageCode.cs index 60beca8..c5ba639 100644 --- a/VirtualMachine/Ceres/TranslationEngine/Diagnostics/EvilMessageCode.cs +++ b/VirtualMachine/Ceres/TranslationEngine/Diagnostics/EvilMessageCode.cs @@ -17,5 +17,6 @@ public static class EvilMessageCode public const int TooManyInitializersForConstSizeArray = 0013; public const int NoDefaultByArm = 0014; public const int MissingErrorInformation = 0015; + public const int FnTargetedStatementRedefinedExistingChunk = 0016; } } \ No newline at end of file diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj b/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj index 8b19dea..155b207 100644 --- a/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj +++ b/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj @@ -105,6 +105,9 @@ Always + + Always + diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/tests/024_fn_targeted.vil b/VirtualMachine/Tests/Ceres.LanguageTests/tests/024_fn_targeted.vil new file mode 100644 index 0000000..fb47eeb --- /dev/null +++ b/VirtualMachine/Tests/Ceres.LanguageTests/tests/024_fn_targeted.vil @@ -0,0 +1,110 @@ +override(TestClass = {})::__invoke(instance_name) { + val Object = { }; + + Object.__name = instance_name; + + loc fn Object::set_vars(a, b) { + self.a = a; + self.b = b; + } + + loc fn Object::add(a, b) { + self::set_vars(a, b); + ret self.result = self.a + self.b; + } + + loc fn Object::sub(a, b) { + self::set_vars(a, b); + ret self.result = self.a - self.b; + } + + loc fn Object::mul(a, b) { + self::set_vars(a, b); + ret self.result = self.a * self.b; + } + + loc fn Object::div(a, b) { + self::set_vars(a, b); + + if (self.b == 0) { + throw error("i could return a NaN here but fuck your input."); + } else -> self.result = self.a / self.b; + } + + ret Object; +} + +#[test] +fn targeted_fn_self_invocations() { + val instance = TestClass("instance_name_uwu"); + + assert.equal(instance.__name, "instance_name_uwu"); + + val add_result = instance::add(21, 37); + assert.equal(instance.a, 21); + assert.equal(instance.b, 37); + assert.equal(instance.result, 58); + assert.equal(instance.result, add_result); + + val sub_result = instance::sub(37, 21); + assert.equal(instance.a, 37); + assert.equal(instance.b, 21); + assert.equal(instance.result, 16); + assert.equal(instance.result, sub_result); + + val mul_result = instance::mul(21, 37); + assert.equal(instance.a, 21); + assert.equal(instance.b, 37); + assert.equal(instance.result, 777); + assert.equal(instance.result, mul_result); + + val div_result = instance::div(600, 50); + assert.equal(instance.a, 600); + assert.equal(instance.b, 50); + assert.equal(instance.result, 12); + assert.equal(instance.result, div_result); + + try { + instance::div(20, 0); + } catch (err) { + assert.equal( + err.msg, + "i could return a NaN here but fuck your input." + ); + } +} + +#[test] +fn targeted_fn_in_local() { + val Object = {}; + + loc fn Object::meow(x) { + self.result = "meow! $x"; + ret x; + } + + val result = Object::meow(2137); + + assert.equal(Object.result, "meow! 2137"); + assert.equal(result, 2137); +} + +#[test] +fn targeted_fn_nested() { + val Object = {}; + + loc fn Object::thing(x) { + val Object = {}; + + loc fn Object::jp2gmd() { + self.uwu = x; + ret self; + } + + ret Object; + } + + val inner_ref = Object::thing(222)::jp2gmd(); + assert.equal(inner_ref.uwu, 222); +} + From 0edb85d996e913e516b10c181e8a89813accb15f Mon Sep 17 00:00:00 2001 From: vddCore Date: Fri, 5 Apr 2024 20:39:48 +0200 Subject: [PATCH 2/2] bump module versions --- Core/EVIL.Grammar/EVIL.Grammar.csproj | 2 +- VirtualMachine/Ceres/Ceres.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/EVIL.Grammar/EVIL.Grammar.csproj b/Core/EVIL.Grammar/EVIL.Grammar.csproj index e1931db..101c927 100644 --- a/Core/EVIL.Grammar/EVIL.Grammar.csproj +++ b/Core/EVIL.Grammar/EVIL.Grammar.csproj @@ -4,7 +4,7 @@ enable 11.0 - 3.1.0 + 3.2.0 EVIL.Grammar EVIL.Grammar diff --git a/VirtualMachine/Ceres/Ceres.csproj b/VirtualMachine/Ceres/Ceres.csproj index 59c335b..7ed4754 100644 --- a/VirtualMachine/Ceres/Ceres.csproj +++ b/VirtualMachine/Ceres/Ceres.csproj @@ -4,7 +4,7 @@ enable 11.0 - 5.0.0 + 5.1.0 Ceres Ceres