Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add targeted function definitions. #10

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Core/EVIL.Grammar/AST/Statements/FnTargetedStatement.cs
Original file line number Diff line number Diff line change
@@ -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<AttributeNode> Attributes { get; }

public FnTargetedStatement(
IdentifierNode primaryIdentifier,
IdentifierNode secondaryIdentifier,
ParameterList parameterList,
Statement statement,
List<AttributeNode> attributes)
{
PrimaryIdentifier = primaryIdentifier;
SecondaryIdentifier = secondaryIdentifier;
ParameterList = parameterList;
Statement = statement;
Attributes = attributes;

Reparent(PrimaryIdentifier);
Reparent(SecondaryIdentifier);
Reparent(ParameterList);
Reparent(Statement);
Reparent(Attributes);
}
}
}
2 changes: 1 addition & 1 deletion Core/EVIL.Grammar/EVIL.Grammar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<LangVersion>11.0</LangVersion>

<Version>3.1.0</Version>
<Version>3.2.0</Version>
<AssemblyName>EVIL.Grammar</AssemblyName>
<AssemblyTitle>EVIL.Grammar</AssemblyTitle>
</PropertyGroup>
Expand Down
50 changes: 41 additions & 9 deletions Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
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;

namespace EVIL.Grammar.Parsing
{
public partial class Parser
{
private FnStatement FnStatement()
private Statement FnStatement()
{
var attributes = new List<AttributeNode>();

Expand Down Expand Up @@ -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;
Expand All @@ -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 };
}
}
}
}
2 changes: 2 additions & 0 deletions Core/EVIL.Grammar/Traversal/AstVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) },
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion VirtualMachine/Ceres/Ceres.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<LangVersion>11.0</LangVersion>

<Version>5.0.0</Version>
<Version>5.1.0</Version>
<AssemblyName>Ceres</AssemblyName>
<AssemblyTitle>Ceres</AssemblyTitle>

Expand Down Expand Up @@ -173,5 +173,8 @@
<Compile Update="TranslationEngine\Compiler.Expression.Error.cs">
<DependentUpon>Compiler.cs</DependentUpon>
</Compile>
<Compile Update="TranslationEngine\Compiler.Statement.FnTargeted.cs">
<DependentUpon>Compiler.cs</DependentUpon>
</Compile>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
<None Update="tests\023_try.vil">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="tests\024_fn_targeted.vil">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
110 changes: 110 additions & 0 deletions VirtualMachine/Tests/Ceres.LanguageTests/tests/024_fn_targeted.vil
Original file line number Diff line number Diff line change
@@ -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);
}

Loading