From 593b564f871a0da385bed1ec1277d4b61b90e5d4 Mon Sep 17 00:00:00 2001 From: Lukeuke Date: Thu, 23 Jan 2025 20:16:46 +0100 Subject: [PATCH] Added ability to assign default value to a struct field Added constructor struct init --- Yail.Shared/Objects/StructObj.cs | 14 +++++ Yail.Shared/ValueObj.cs | 9 +++ Yail.Tests/StructTests.cs | 94 ++++++++++++++++++++++++++++++++ Yail/ExpressionsVisitor.cs | 35 ++++++++++-- Yail/Grammar/Expressions.g4 | 8 ++- 5 files changed, 153 insertions(+), 7 deletions(-) diff --git a/Yail.Shared/Objects/StructObj.cs b/Yail.Shared/Objects/StructObj.cs index c77c3b8..baea950 100644 --- a/Yail.Shared/Objects/StructObj.cs +++ b/Yail.Shared/Objects/StructObj.cs @@ -1,4 +1,5 @@ using Yail.Shared.Abstract; +using Yail.Shared.Helpers; namespace Yail.Shared.Objects; @@ -24,6 +25,19 @@ public void Set(string variableName, ValueObj value) Value = list; } + public void Update(string variableName, ValueObj valueObj) + { + var fields = Get(); + + if (!fields.TryGetValue(variableName, out var prevValue)) + ExceptionHelper.PrintError($"Variable '{variableName}' is not defined in struct '{Name}'"); + + if (!prevValue.ValueEquals(valueObj)) + ExceptionHelper.PrintError($"Data types must be equal."); + + fields[variableName] = valueObj; + } + public Dictionary Get() { return Value as Dictionary; diff --git a/Yail.Shared/ValueObj.cs b/Yail.Shared/ValueObj.cs index 3cd23f2..ae347fd 100644 --- a/Yail.Shared/ValueObj.cs +++ b/Yail.Shared/ValueObj.cs @@ -77,4 +77,13 @@ public virtual void Print(bool newLine = false) Console.Write(val); } + + public bool ValueEquals(object? obj) + { + if (obj is ValueObj other) + { + return Value.GetType() == other.Value.GetType() && DataType == other.DataType; + } + return false; + } } \ No newline at end of file diff --git a/Yail.Tests/StructTests.cs b/Yail.Tests/StructTests.cs index 856f6c2..3afa1e9 100644 --- a/Yail.Tests/StructTests.cs +++ b/Yail.Tests/StructTests.cs @@ -177,4 +177,98 @@ pub struct Point { Assert.Pass(); } } + + [Test] + public void CreateStructWithCtor() + { + var code = @" + package main + + pub struct Point { + var x i32; + var z i32; + var y i32; + } + + var p = new Point() { + y = 2; + }; + print(p.y); + "; + + var actual = RunCode(code); + + Assert.That(actual, Is.EqualTo("2")); + } + + [Test] + public void CreateStructWithCtorAndAssignValue() + { + var code = @" + package main + + pub struct Point { + var x i32; + var z i32; + var y i32; + } + + var p = new Point() { + y = 2; + }; + println(p.y); + p.y = 3; + print(p.y); + "; + + var actual = RunCode(code); + + Assert.That(actual, Is.EqualTo("2\n3")); + } + + [Test] + public void CreateStructWithDefaultValue() + { + var code = @" + package main + + pub struct Point { + var x i32; + var z i32; + var y i32 = 3; + } + + var p = new Point(); + + print(p.y); + "; + + var actual = RunCode(code); + + Assert.That(actual, Is.EqualTo("3")); + } + + [Test] + public void CreateStructWithDefaultValue_ConstructorAssign() + { + var code = @" + package main + + pub struct Point { + var x i32; + var z i32; + var y i32 = 3; + } + + var p = new Point() { + y = 5; + }; + + print(p.y); + "; + + var actual = RunCode(code); + + Assert.That(actual, Is.EqualTo("5")); + } } \ No newline at end of file diff --git a/Yail/ExpressionsVisitor.cs b/Yail/ExpressionsVisitor.cs index e79d687..55024af 100644 --- a/Yail/ExpressionsVisitor.cs +++ b/Yail/ExpressionsVisitor.cs @@ -51,11 +51,18 @@ public sealed class ExpressionsVisitor : ExpressionsBaseVisitor return null; } + if (_currentStruct is not null) + { + if (!_instances.TryGetValue(_currentStruct, out var currStruct)) + ExceptionHelper.PrintError($"Struct '{_currentStruct}' is not defined."); + + (currStruct as StructObj)!.Update(variableName, value); + return null; + } + if (!_variables.TryGetValue(variableName, out var prevVal)) { - Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine($"Variable '${variableName}' is not defined."); - Environment.Exit(1); + ExceptionHelper.PrintError($"Variable '${variableName}' is not defined."); } if (prevVal is IAccessible accessible) @@ -1027,7 +1034,18 @@ public override ValueObj VisitStructBlock(ExpressionsParser.StructBlockContext c var varName = structLine.variableDefine().IDENTIFIER().GetText(); var dataType = structLine.variableDefine().DATA_TYPES().GetText().ToDataType(); - structObj.Set(varName, new ValueObj(dataType)); + var expr = structLine.variableDefine().expression(); + + if (expr is not null) + { + var defaultVal = Visit(expr); + + structObj.Set(varName, defaultVal); + } + else + { + structObj.Set(varName, new ValueObj(dataType)); + } } if (!_instances.TryAdd(structObj.Name, structObj)) @@ -1038,6 +1056,7 @@ public override ValueObj VisitStructBlock(ExpressionsParser.StructBlockContext c return structObj; } + private string? _currentStruct; public override ValueObj? VisitInstanceCreateExpr(ExpressionsParser.InstanceCreateExprContext context) { var instanceName = string.Empty; @@ -1052,7 +1071,13 @@ public override ValueObj VisitStructBlock(ExpressionsParser.StructBlockContext c { instanceName = context.instanceCreate().IDENTIFIER(0).GetText(); } - + + if (context.instanceCreate().instanceBody() is not null) + { + _currentStruct = $"{packageName}::{instanceName}"; + Visit(context.instanceCreate().instanceBody()); + _currentStruct = null; + } var valueObj = _instances[$"{packageName}::{instanceName}"]; diff --git a/Yail/Grammar/Expressions.g4 b/Yail/Grammar/Expressions.g4 index edcdfaf..a0c1a68 100644 --- a/Yail/Grammar/Expressions.g4 +++ b/Yail/Grammar/Expressions.g4 @@ -24,6 +24,10 @@ block: '{' line* '}'; structBody: '{' structLine* '}'; structLine: variableDefine ';'; +// structs new calling 'new' +instanceBody: '{' instanceLine* '}'; +instanceLine: assignment ';'; + directive: '#' 'use' USE_IDENTIFIERS; multiplyOp: '*' | '/' | '%'; @@ -67,7 +71,7 @@ packageDeclaration: 'package' IDENTIFIER; usingDirective: 'using' IDENTIFIER; variableDeclaration: 'var' IDENTIFIER '=' REFERENCE? expression; -variableDefine: 'var' IDENTIFIER DATA_TYPES; +variableDefine: 'var' IDENTIFIER DATA_TYPES ('=' expression)?; functionDeclaration: (accessLevels)? 'funky' IDENTIFIER '(' (parameterList)? ')' DATA_TYPES block; parameterList: parameter (',' parameter)*; @@ -97,6 +101,6 @@ foreachBlock: FOREACH '(' 'var' IDENTIFIER 'in' expression ')' block; structBlock: accessLevels? 'struct' IDENTIFIER structBody; -instanceCreate: 'new' (IDENTIFIER '::')? IDENTIFIER '(' (expression (',' expression)*)? ')'; +instanceCreate: 'new' (IDENTIFIER '::')? IDENTIFIER '(' (expression (',' expression)*)? ')' instanceBody?; instancePropAssign: IDENTIFIER '.' IDENTIFIER '=' expression; instancePropCall: IDENTIFIER '.' IDENTIFIER; \ No newline at end of file