From 3e0aec7d686427e9aa4c69b9cb0cd75892c1998d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD?= Date: Sun, 23 Nov 2025 03:07:29 +0300 Subject: [PATCH 01/25] optimization simplifying refactoring: (#210) - func decl process - return analysis --- .../Visitors/InstructionProvider.cs | 5 +-- .../Visitors/DeclarationVisitor.cs | 44 +++++++------------ .../Visitors/ReturnAnalyzer.cs | 43 +++++++++++------- .../Visitors/ReturnAnalyzerResult.cs | 22 ---------- .../Visitors/SemanticChecker.cs | 8 ++-- .../Impl/Instructions/PopParameter.cs | 2 +- .../FunctionDeclaration.cs | 5 ++- .../AfterTypesAreLoaded/IFunctionArgument.cs | 4 ++ .../Parser/Impl/TopDownParser.cs | 6 ++- .../Impl/Symbols/FunctionSymbol.cs | 6 +-- .../Impl/Symbols/Ids/FunctionSymbolId.cs | 2 +- .../Impl/Symbols/Ids/TypeSymbolId.cs | 4 +- ...FunctionWithUndefinedReturnStorageTests.cs | 17 ++++--- .../Application/ReturnAnalyzerTests.cs | 3 +- .../Domain/BackEnd/InstructionsData.cs | 2 +- .../Domain/BackEnd/VirtualMachineTests.cs | 2 +- .../Domain/FrontEnd/AstNodeTests.cs | 3 +- 17 files changed, 86 insertions(+), 92 deletions(-) delete mode 100644 src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs index 4fb64d7f..b7b64bbe 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs @@ -132,10 +132,7 @@ public AddressedInstructions Visit(FunctionDeclaration visitable) for (var i = 0; i < visitable.Arguments.Count; i++) { var arg = visitable.Arguments[i]; - if (arg is DefaultValueArgument @default) - result.Add(new PopParameter(arg.Name, @default.Info.Value)); - else - result.Add(new PopParameter(arg.Name)); + result.Add(new PopParameter(arg.Name, arg.Info.Value)); } result.AddRange(visitable.Statements.Accept(This)); diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs index be503f12..0f0bbc64 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs @@ -7,7 +7,6 @@ using HydraScript.Domain.IR.Impl.Symbols; using HydraScript.Domain.IR.Impl.Symbols.Ids; using HydraScript.Domain.IR.Types; -using ZLinq; namespace HydraScript.Application.StaticAnalysis.Visitors; @@ -79,16 +78,16 @@ public VisitUnit Visit(FunctionDeclaration visitable) visitable.AllCodePathsEndedWithReturn = returnAnalyzerResult.CodePathEndedWithReturn; var parentTable = _symbolTables[visitable.Parent.Scope]; - var indexOfFirstDefaultArgument = visitable.Arguments.AsValueEnumerable() - .Select((x, i) => new { Argument = x, Index = i }) - .FirstOrDefault(pair => pair.Argument is DefaultValueArgument)?.Index ?? -1; - var parameters = visitable.Arguments.AsValueEnumerable() - .Select(x => - new VariableSymbol( - x.Name, - x.TypeValue.Accept(_typeBuilder))).ToList(); - var functionSymbolId = new FunctionSymbolId(visitable.Name, parameters.Select(x => x.Type)); + var parameters = new List(); + for (var i = 0; i < visitable.Arguments.Count; i++) + { + parameters.Add(visitable.Arguments[i].TypeValue.Accept(_typeBuilder)); + var arg = new VariableSymbol(visitable.Arguments[i].Name, parameters[i]); + arg.Initialize(); + _symbolTables[visitable.Scope].AddSymbol(arg); + } + var functionSymbolId = new FunctionSymbolId(visitable.Name, parameters); _ambiguousInvocations.Clear(functionSymbolId); visitable.ComputedFunctionAddress = functionSymbolId.ToString(); var functionSymbol = new FunctionSymbol( @@ -99,18 +98,8 @@ public VisitUnit Visit(FunctionDeclaration visitable) if (functionSymbolId.Equals(parentTable.FindSymbol(functionSymbolId)?.Id)) throw new OverloadAlreadyExists(visitable.Name, functionSymbolId); - for (var i = 0; i < parameters.Count; i++) - { - var arg = parameters[i]; - arg.Initialize(); - _symbolTables[visitable.Scope].AddSymbol(arg); - } - - var isMethod = - parameters is [{ Type: ObjectType }, ..] && - visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..]; - if (isMethod) - _methodStorage.BindMethod((parameters[0].Type as ObjectType)!, functionSymbol, functionSymbolId); + if (parameters is [ObjectType methodOwner, ..] && visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..]) + _methodStorage.BindMethod(methodOwner, functionSymbol, functionSymbolId); Type undefined = "undefined"; if (functionSymbol.Type.Equals(undefined)) @@ -122,23 +111,22 @@ public VisitUnit Visit(FunctionDeclaration visitable) } parentTable.AddSymbol(functionSymbol); - for (var i = indexOfFirstDefaultArgument; i < visitable.Arguments.Count; i++) + for (var i = visitable.IndexOfFirstDefaultArgument; i < visitable.Arguments.Count; i++) { - if (i is -1) break; - if (visitable.Arguments[i] is not DefaultValueArgument) + if (visitable.Arguments[i].Info.Type is ValueDtoType.Name) throw new NamedArgumentAfterDefaultValueArgument( visitable.Segment, function: visitable.Name, visitable.Arguments[i]); - var overload = new FunctionSymbolId(visitable.Name, parameters[..i].Select(x => x.Type)); + var overload = new FunctionSymbolId(visitable.Name, parameters[..i]); var existing = parentTable.FindSymbol(overload); var functionToAdd = existing is not null && existing < functionSymbol ? existing : functionSymbol; parentTable.AddSymbol(functionToAdd, overload); - if (isMethod) - _methodStorage.BindMethod((parameters[0].Type as ObjectType)!, functionToAdd, overload); + if (parameters is [ObjectType overloadOwner, ..] && visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..]) + _methodStorage.BindMethod(overloadOwner, functionToAdd, overload); if (existing is not null && !existing.Id.Equals(overload)) { diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs index 30b4f91c..94f8fa7b 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs @@ -4,42 +4,53 @@ namespace HydraScript.Application.StaticAnalysis.Visitors; -internal class ReturnAnalyzer : VisitorBase, +internal class ReturnAnalyzer : VisitorBase, IVisitor, - IVisitor, - IVisitor + IVisitor, + IVisitor { + private readonly List _returnStatements = []; + public ReturnAnalyzerResult Visit(FunctionDeclaration visitable) { IAbstractSyntaxTreeNode astNode = visitable; - return Visit(astNode); + var codePathEndedWithReturn= Visit(astNode); + var returnStatements = new List(_returnStatements); + ReturnAnalyzerResult result = new(codePathEndedWithReturn, returnStatements); + _returnStatements.Clear(); + return result; } - public override ReturnAnalyzerResult Visit(IAbstractSyntaxTreeNode visitable) + public override bool Visit(IAbstractSyntaxTreeNode visitable) { - var result = ReturnAnalyzerResult.AdditiveIdentity; for (var i = 0; i < visitable.Count; i++) { var visitableResult = visitable[i].Accept(This); - if (visitableResult.CodePathEndedWithReturn) - return visitableResult * result; - result += visitableResult; + if (visitableResult) + return true; } - return result; + return false; } - public ReturnAnalyzerResult Visit(IfStatement visitable) + public bool Visit(IfStatement visitable) { var thenReturns = visitable.Then.Accept(This); if (visitable.Else is null) - return thenReturns + ReturnAnalyzerResult.AdditiveIdentity; + return false; var elseReturns = visitable.Else.Accept(This); - return thenReturns + elseReturns; + return thenReturns && elseReturns; + } + + public bool Visit(ReturnStatement visitable) + { + _returnStatements.Add(visitable); + return true; } +} - public ReturnAnalyzerResult Visit(ReturnStatement visitable) => - new(CodePathEndedWithReturn: true, ReturnStatements: [visitable]); -} \ No newline at end of file +public sealed record ReturnAnalyzerResult( + bool CodePathEndedWithReturn, + IReadOnlyList ReturnStatements); \ No newline at end of file diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs deleted file mode 100644 index f9c2fb43..00000000 --- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Numerics; -using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Statements; - -namespace HydraScript.Application.StaticAnalysis.Visitors; - -public sealed record ReturnAnalyzerResult(bool CodePathEndedWithReturn, IReadOnlyList ReturnStatements) : - IAdditiveIdentity, - IAdditionOperators, - IMultiplyOperators -{ - public static ReturnAnalyzerResult operator +(ReturnAnalyzerResult left, ReturnAnalyzerResult right) => - new( - left.CodePathEndedWithReturn && right.CodePathEndedWithReturn, - ReturnStatements: [..left.ReturnStatements, ..right.ReturnStatements]); - - public static ReturnAnalyzerResult AdditiveIdentity { get; } = new(CodePathEndedWithReturn: false, ReturnStatements: []); - - public static ReturnAnalyzerResult operator *(ReturnAnalyzerResult left, ReturnAnalyzerResult right) => - new( - left.CodePathEndedWithReturn || right.CodePathEndedWithReturn, - ReturnStatements: [..left.ReturnStatements, ..right.ReturnStatements]); -} \ No newline at end of file diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs index a0990dcf..c52c97fd 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs @@ -466,9 +466,9 @@ public Type Visit(CallExpression visitable) .Zip(parameters).Zip(functionSymbol.Parameters.AsValueEnumerable().Skip(methodCall ? 1 : 0)) .ToList().ForEach(pair => { - var ((expr, actualType), expected) = pair; - if (!actualType.Equals(expected.Type)) - throw new WrongTypeOfArgument(expr.Segment, expected.Type, actualType); + var ((expr, actualType), expectedType) = pair; + if (!actualType.Equals(expectedType)) + throw new WrongTypeOfArgument(expr.Segment, expectedType, actualType); }); Type undefined = "undefined"; @@ -486,7 +486,7 @@ public Type Visit(CallExpression visitable) public Type Visit(FunctionDeclaration visitable) { - var parameters = visitable.Arguments.Select(x => x.TypeValue.Accept(_typeBuilder)); + var parameters = visitable.Arguments.Select(x => x.TypeValue.Accept(_typeBuilder)).ToList(); var symbol = _symbolTables[visitable.Scope].FindSymbol(new FunctionSymbolId(visitable.Name, parameters))!; _functionStorage.RemoveIfPresent(symbol); visitable.Statements.Accept(This); diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs index 17ae4363..3ebd3e50 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs @@ -1,6 +1,6 @@ namespace HydraScript.Domain.BackEnd.Impl.Instructions; -public class PopParameter(string parameter, object? defaultValue = null) : Instruction +public class PopParameter(string parameter, object? defaultValue) : Instruction { public override IAddress? Execute(IExecuteParams executeParams) { diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/FunctionDeclaration.cs b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/FunctionDeclaration.cs index 54ddf2ba..aff96ccb 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/FunctionDeclaration.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/FunctionDeclaration.cs @@ -14,6 +14,7 @@ public partial class FunctionDeclaration : AfterTypesAreLoadedDeclaration public IdentifierReference Name { get; } public TypeValue ReturnTypeValue { get; } public IReadOnlyList Arguments => _arguments; + public int IndexOfFirstDefaultArgument { get; } public BlockStatement Statements { get; } public bool IsEmpty => Statements.Count == 0; @@ -28,11 +29,13 @@ public FunctionDeclaration( IdentifierReference name, TypeValue returnTypeValue, List arguments, - BlockStatement blockStatement) + BlockStatement blockStatement, + int indexOfFirstDefaultArgument) { Name = name; ReturnTypeValue = returnTypeValue; _arguments = arguments; + IndexOfFirstDefaultArgument = indexOfFirstDefaultArgument; Statements = blockStatement; Statements.Parent = this; diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/IFunctionArgument.cs b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/IFunctionArgument.cs index bcafe8b0..5a37e3fd 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/IFunctionArgument.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Declarations/AfterTypesAreLoaded/IFunctionArgument.cs @@ -7,6 +7,8 @@ public interface IFunctionArgument public string Name { get; } public TypeValue TypeValue { get; } + + public ValueDto Info { get; } } public record NamedArgument( @@ -15,6 +17,8 @@ public record NamedArgument( { public override string ToString() => $"{Name}: {TypeValue}"; + + public ValueDto Info { get; } = ValueDto.NameDto(Name); } public record DefaultValueArgument : IFunctionArgument diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs index cc30703b..18df9bcc 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs @@ -340,6 +340,7 @@ private FunctionDeclaration FunctionDeclaration() Expect("LeftParen"); var args = new List(); + var indexOfFirstDefaultArgument = int.MaxValue; while (CurrentIs("Ident")) { var arg = Expect("Ident").Value; @@ -353,6 +354,9 @@ private FunctionDeclaration FunctionDeclaration() { Expect("Assign"); var value = Literal(); + indexOfFirstDefaultArgument = args.Count < indexOfFirstDefaultArgument + ? args.Count + : indexOfFirstDefaultArgument; args.Add(new DefaultValueArgument(arg, value)); } @@ -373,7 +377,7 @@ private FunctionDeclaration FunctionDeclaration() } var name = new IdentifierReference(ident.Value) { Segment = ident.Segment }; - return new FunctionDeclaration(name, returnType, args, BlockStatement()) + return new FunctionDeclaration(name, returnType, args, BlockStatement(), indexOfFirstDefaultArgument) { Segment = ident.Segment }; } diff --git a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/FunctionSymbol.cs b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/FunctionSymbol.cs index 33ceefd1..f93e640e 100644 --- a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/FunctionSymbol.cs +++ b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/FunctionSymbol.cs @@ -6,7 +6,7 @@ namespace HydraScript.Domain.IR.Impl.Symbols; public class FunctionSymbol( string name, - IReadOnlyList parameters, + IReadOnlyList parameters, Type type, bool isEmpty) : Symbol(name, type) { @@ -17,9 +17,9 @@ public class FunctionSymbol( /// /// Перегрузка функции /// - public override FunctionSymbolId Id { get; } = new(name, parameters.Select(x => x.Type)); + public override FunctionSymbolId Id { get; } = new(name, parameters); - public IReadOnlyList Parameters { get; } = parameters; + public IReadOnlyList Parameters { get; } = parameters; public bool IsEmpty { get; } = isEmpty; public void DefineReturnType(Type returnType) => diff --git a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/FunctionSymbolId.cs b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/FunctionSymbolId.cs index 6e582c32..78574110 100644 --- a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/FunctionSymbolId.cs +++ b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/FunctionSymbolId.cs @@ -4,7 +4,7 @@ namespace HydraScript.Domain.IR.Impl.Symbols.Ids; public class FunctionSymbolId( string name, - IEnumerable parameters) : SymbolId + IReadOnlyList parameters) : SymbolId { protected override string Value { get; } = $"function {name}({ZString.Join(", ", parameters)})"; diff --git a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/TypeSymbolId.cs b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/TypeSymbolId.cs index b7a67f23..cb19ad7f 100644 --- a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/TypeSymbolId.cs +++ b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/Ids/TypeSymbolId.cs @@ -1,6 +1,8 @@ +using Cysharp.Text; + namespace HydraScript.Domain.IR.Impl.Symbols.Ids; public class TypeSymbolId(string name) : SymbolId { - protected override string Value { get; } = $"type {name}"; + protected override string Value { get; } = ZString.Concat("type", ' ', name); } \ No newline at end of file diff --git a/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs b/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs index 9115da6f..c7f573e5 100644 --- a/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs +++ b/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs @@ -27,7 +27,8 @@ public void StorageIsEmptyAfterFlushTest() name: new IdentifierReference(FunctionName), returnTypeValue: Substitute.For(), arguments: [], - new BlockStatement([])); + new BlockStatement([]), + indexOfFirstDefaultArgument: int.MaxValue); storage.Save(symbol, decl); @@ -44,26 +45,30 @@ public void StorageIsCorrectOrderTest() name: new IdentifierReference(FunctionName + "1"), returnTypeValue: Substitute.For(), arguments: [], - new BlockStatement([])), + new BlockStatement([]), + indexOfFirstDefaultArgument: int.MaxValue), new( name: new IdentifierReference(FunctionName + "2"), returnTypeValue: Substitute.For(), arguments: [], - new BlockStatement([])), + new BlockStatement([]), + indexOfFirstDefaultArgument: int.MaxValue), new( name: new IdentifierReference(FunctionName + "3"), returnTypeValue: Substitute.For(), arguments: [], - new BlockStatement([])), + new BlockStatement([]), + indexOfFirstDefaultArgument: int.MaxValue), new( name: new IdentifierReference(FunctionName + "4"), returnTypeValue: Substitute.For(), arguments: [], - new BlockStatement([]))]; - + new BlockStatement([]), + indexOfFirstDefaultArgument: int.MaxValue)]; + IFunctionWithUndefinedReturnStorage storage = new FunctionWithUndefinedReturnStorage(); var removable = new FunctionSymbol( diff --git a/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs b/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs index c88175d8..c2b846a9 100644 --- a/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs +++ b/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs @@ -29,7 +29,8 @@ public void Visit_FunctionWithMissingReturn_CodePathEndedWithReturnIsFalse() new IdentifierReference("b"), new ReturnStatement( new Literal(new TypeIdentValue(new IdentifierReference("number")), 1, "segment"))) - ])); + ]), + indexOfFirstDefaultArgument: int.MaxValue); // Act var result = new ReturnAnalyzer().Visit(functionDeclaration); diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs index 773dfbc6..1daaccaf 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs @@ -98,7 +98,7 @@ public IEnumerator GetEnumerator() ]; yield return [ - new PopParameter("param"), + new PopParameter("param", defaultValue: null), "PopParameter param" ]; yield return diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs index 3d4a312e..c5879e99 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs @@ -61,7 +61,7 @@ public void VirtualMachineHandlesRecursionTest(VirtualMachine vm) { new Goto(factorial.End), { new BeginBlock(BlockType.Function, blockId: factorial.ToString()), factorial.Start.Name }, - new PopParameter("n"), + new PopParameter("n", defaultValue: null), new Simple("_t2", (new Name("n"), new Constant(2)), "<"), new IfNotGoto(new Name("_t2"), new Label("5")), new Return(new Name("n")), diff --git a/tests/HydraScript.UnitTests/Domain/FrontEnd/AstNodeTests.cs b/tests/HydraScript.UnitTests/Domain/FrontEnd/AstNodeTests.cs index 29834283..efc113ed 100644 --- a/tests/HydraScript.UnitTests/Domain/FrontEnd/AstNodeTests.cs +++ b/tests/HydraScript.UnitTests/Domain/FrontEnd/AstNodeTests.cs @@ -20,7 +20,8 @@ public void PrecedenceTest() TypeId: new IdentifierReference( name: Guid.NewGuid().ToString())), arguments: [], - new BlockStatement(stmtItemList)); + new BlockStatement(stmtItemList), + indexOfFirstDefaultArgument: int.MaxValue); _ = new ScriptBody([func]); From 619abd7c60465bb499490bc2c5f8eaa09980f3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD?= Date: Sun, 23 Nov 2025 10:47:35 +0300 Subject: [PATCH 02/25] lexer optimizing refactoring (#211) --- .../Lexer/ILexer.cs | 2 +- .../Lexer/Impl/RegexLexer.cs | 29 ++++--------------- .../Parser/Impl/TokensStream.cs | 28 ------------------ .../Parser/Impl/TopDownParser.cs | 13 ++++----- .../Dumping/DumpingLexer.cs | 7 +++-- .../Domain/FrontEnd/RegexLexerTests.cs | 10 +++---- 6 files changed, 21 insertions(+), 68 deletions(-) delete mode 100644 src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TokensStream.cs diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Lexer/ILexer.cs b/src/Domain/HydraScript.Domain.FrontEnd/Lexer/ILexer.cs index bb694326..faf4c1f4 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Lexer/ILexer.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Lexer/ILexer.cs @@ -4,5 +4,5 @@ public interface ILexer { public IStructure Structure { get; } - public List GetTokens(string text); + public IEnumerable GetTokens(string text); } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Lexer/Impl/RegexLexer.cs b/src/Domain/HydraScript.Domain.FrontEnd/Lexer/Impl/RegexLexer.cs index 388d111a..c97181b0 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Lexer/Impl/RegexLexer.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Lexer/Impl/RegexLexer.cs @@ -1,28 +1,16 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; -using Cysharp.Text; namespace HydraScript.Domain.FrontEnd.Lexer.Impl; -public class RegexLexer(IStructure structure, ITextCoordinateSystemComputer computer) : ILexer, IEnumerable +public class RegexLexer(IStructure structure, ITextCoordinateSystemComputer computer) : ILexer { - private IReadOnlyList _lines = []; - private string _text = ""; - public IStructure Structure { get; } = structure; - public List GetTokens(string text) + public IEnumerable GetTokens(string text) { - _text = text; - _lines = computer.GetLines(_text); - - return this.ToList(); - } + var lines = computer.GetLines(text); - public IEnumerator GetEnumerator() - { - foreach (Match match in Structure.Regex.Matches(_text)) + foreach (Match match in Structure.Regex.Matches(text)) { for (var i = 0; i < Structure.Count; i++) { @@ -33,8 +21,8 @@ public IEnumerator GetEnumerator() var value = group.Value; var segment = new Segment( - computer.GetCoordinates(group.Index, _lines), - computer.GetCoordinates(absoluteIndex: group.Index + group.Length, _lines)); + computer.GetCoordinates(group.Index, lines), + computer.GetCoordinates(absoluteIndex: group.Index + group.Length, lines)); var token = new Token(type, segment, value); if (type.Error()) throw new LexerException(token); @@ -45,9 +33,4 @@ public IEnumerator GetEnumerator() yield return new EndToken(); } - - [ExcludeFromCodeCoverage] - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public override string ToString() => ZString.Join('\n', this); } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TokensStream.cs b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TokensStream.cs deleted file mode 100644 index a258c818..00000000 --- a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TokensStream.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections; -using HydraScript.Domain.FrontEnd.Lexer; - -namespace HydraScript.Domain.FrontEnd.Parser.Impl; - -public class TokensStream : IEnumerator -{ - private readonly IEnumerator _inner; - - private TokensStream(IEnumerator enumerator) - { - _inner = enumerator; - _inner.MoveNext(); - } - - public bool MoveNext() => _inner.MoveNext(); - - public void Reset() => _inner.Reset(); - - public Token Current => _inner.Current; - - object IEnumerator.Current => Current; - - public void Dispose() => _inner.Dispose(); - - public static implicit operator TokensStream(List tokens) => - new (tokens.GetEnumerator()); -} \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs index 18df9bcc..27b09975 100644 --- a/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs +++ b/src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/TopDownParser.cs @@ -14,17 +14,14 @@ namespace HydraScript.Domain.FrontEnd.Parser.Impl; -public class TopDownParser : IParser +public class TopDownParser(ILexer lexer) : IParser { - private TokensStream _tokens = new List(); - private readonly ILexer _lexer; - - public TopDownParser(ILexer lexer) => - _lexer = lexer; + private IEnumerator _tokens = Enumerable.Empty().GetEnumerator(); public IAbstractSyntaxTree Parse(string text) { - _tokens = _lexer.GetTokens(text); + _tokens = lexer.GetTokens(text).GetEnumerator(); + _tokens.MoveNext(); var root = Script(); Expect(Eop.Tag); @@ -45,7 +42,7 @@ private Token Expect(string expectedTag, string? expectedValue = null) } private bool CurrentIs(string tag) => - _tokens.Current.Type == _lexer.Structure.FindByTag(tag); + _tokens.Current.Type == lexer.Structure.FindByTag(tag); private bool CurrentIsLiteral() => CurrentIs("NullLiteral") || diff --git a/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs index e9b53573..3d1136bf 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs +++ b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Cysharp.Text; using HydraScript.Domain.FrontEnd.Lexer; using Microsoft.Extensions.DependencyInjection; @@ -12,10 +13,10 @@ internal class DumpingLexer( [ExcludeFromCodeCoverage] public IStructure Structure => lexer.Structure; - public List GetTokens(string text) + public IEnumerable GetTokens(string text) { - var tokens = lexer.GetTokens(text); - dumpingService.Dump(lexer.ToString(), "tokens"); + var tokens = lexer.GetTokens(text).ToList(); + dumpingService.Dump(ZString.Join('\n', tokens), "tokens"); return tokens; } } \ No newline at end of file diff --git a/tests/HydraScript.UnitTests/Domain/FrontEnd/RegexLexerTests.cs b/tests/HydraScript.UnitTests/Domain/FrontEnd/RegexLexerTests.cs index 119a3d5e..bcbe7767 100644 --- a/tests/HydraScript.UnitTests/Domain/FrontEnd/RegexLexerTests.cs +++ b/tests/HydraScript.UnitTests/Domain/FrontEnd/RegexLexerTests.cs @@ -20,15 +20,15 @@ public void LexerDoesNotThrowTest(string text) => [Theory] [ClassData(typeof(LexerFailData))] public void LexerThrowsErrorTest(string text) => - Assert.Throws(() => _regexLexer.GetTokens(text)); + Assert.Throws(() => _regexLexer.GetTokens(text).ToList()); [Fact] public void LexerToStringCorrectTest() { const string text = "8"; - var tokens = _regexLexer.GetTokens(text); - Assert.Contains("EOP", _regexLexer.ToString()); - Assert.Equal("IntegerLiteral (1, 1)-(1, 2): 8", tokens.First().ToString()); + var tokens = _regexLexer.GetTokens(text).ToList(); + Assert.Contains("EOP", tokens[^1].ToString()); + Assert.Equal("IntegerLiteral (1, 1)-(1, 2): 8", tokens[0].ToString()); } [Fact] @@ -80,7 +80,7 @@ public void GetTokens_MockedRegex_ValidOutput( structure.Count.Returns(tokenTypes.Count); structure[Arg.Any()].Returns(callInfo => tokenTypes[callInfo.Arg()]); - var tokens = lexer.GetTokens(input.ToString()); + var tokens = lexer.GetTokens(input.ToString()).ToList(); for (var i = 0; i < input.Count; i++) { output.WriteLine(tokens[i].ToString()); From 31b0da64dd9bc47573db7a21fc3b9342b859a862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD?= Date: Mon, 15 Dec 2025 16:01:35 +0300 Subject: [PATCH 03/25] Refactoring/frame (#215) * remove return address from Frame.cs * use concrete type * Left string -> Name * update pull_request_template.md --- .github/pull_request_template.md | 25 ++---- .../Visitors/ExpressionInstructionProvider.cs | 46 +++++----- .../Visitors/InstructionProvider.cs | 14 +-- .../AddressedInstructions.cs | 4 +- src/Domain/HydraScript.Domain.BackEnd/Call.cs | 3 +- .../HydraScript.Domain.BackEnd/Frame.cs | 4 +- .../Impl/Instructions/PopParameter.cs | 13 +-- .../Impl/Instructions/RemoveFromArray.cs | 6 +- .../Impl/Instructions/Return.cs | 9 +- .../Instructions/WithAssignment/AsString.cs | 3 +- .../WithAssignment/CallFunction.cs | 2 +- .../ComplexData/Create/CreateArray.cs | 8 +- .../ComplexData/Create/CreateObject.cs | 8 +- .../ComplexData/Read/DotRead.cs | 8 +- .../ComplexData/Read/IndexRead.cs | 2 +- .../ComplexData/Write/DotAssignment.cs | 6 +- .../ComplexData/Write/IndexAssignment.cs | 6 +- .../Instructions/WithAssignment/Simple.cs | 86 ++++++++++--------- .../Impl/Values/Name.cs | 3 + .../Impl/VirtualMachine.cs | 2 +- .../Application/WithExpressionData.cs | 8 +- .../BackEnd/AddressedInstructionsTests.cs | 8 +- .../Domain/BackEnd/AsStringTests.cs | 2 +- .../Domain/BackEnd/InstructionsData.cs | 20 ++--- .../Domain/BackEnd/VirtualMachineTests.cs | 30 +++---- 25 files changed, 165 insertions(+), 161 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3b58b380..11555aec 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,21 +1,8 @@ -### Description - - +**Is your pull request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +**Describe the solution you've done** +Key points of your tech and arch decisions. -### Related Issues - - - -### References - - - - -### Checklist: - - -- [ ] I have followed the [contribution guidelines](../CONTRIBUTING.md) and code style for this project. -- [ ] I have added tests covering my contributions. -- [ ] I have updated the documentation accordingly. -- [ ] I have added corresponding labels with respect to the part of the interpreter i've worked on. +**Related Issues** +If it fixes an open issue, please link to the issue here. diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs index 82802b41..9cf2b501 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs @@ -46,7 +46,7 @@ public AddressedInstructions Visit(ArrayLiteral visitable) { var arraySize = visitable.Expressions.Count; - var arrayName = visitable.Id; + var arrayName = new Name(visitable.Id); var createArray = new CreateArray(arrayName, arraySize); var result = new AddressedInstructions { createArray }; @@ -64,7 +64,7 @@ public AddressedInstructions Visit(ArrayLiteral visitable) else { result.AddRange(expression.Accept(This)); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new IndexAssignment(arrayName, index, last)); } } @@ -75,7 +75,7 @@ public AddressedInstructions Visit(ArrayLiteral visitable) public AddressedInstructions Visit(ObjectLiteral visitable) { var objectId = visitable.Id; - var createObject = new CreateObject(objectId); + var createObject = new CreateObject(new Name(objectId)); var result = new AddressedInstructions { createObject }; @@ -89,7 +89,7 @@ public AddressedInstructions Visit(ObjectLiteral visitable) public AddressedInstructions Visit(Property visitable) { - var objectId = visitable.Object.Id; + var objectId = new Name(visitable.Object.Id); var (id, expression) = visitable; var propertyId = new Constant(id); @@ -101,7 +101,7 @@ public AddressedInstructions Visit(Property visitable) _valueDtoConverter.Convert(primary.ToValueDto()))]; var instructions = expression.Accept(This); - var last = new Name(instructions.OfType().Last().Left!); + var last = instructions.OfType().Last().Left!; instructions.Add(new DotAssignment(objectId, propertyId, last)); return instructions; @@ -113,7 +113,7 @@ public AddressedInstructions Visit(UnaryExpression visitable) return [new Simple(visitable.Operator, _valueDtoConverter.Convert(primary.ToValueDto()))]; var result = visitable.Expression.Accept(This); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new Simple(visitable.Operator, last)); return result; @@ -122,7 +122,7 @@ public AddressedInstructions Visit(UnaryExpression visitable) public AddressedInstructions Visit(BinaryExpression visitable) { if (visitable is { Left: IdentifierReference arr, Right: PrimaryExpression primary, Operator: "::" }) - return [new RemoveFromArray(arr.Name, index: _valueDtoConverter.Convert(primary.ToValueDto()))]; + return [new RemoveFromArray(new Name(arr), index: _valueDtoConverter.Convert(primary.ToValueDto()))]; var result = new AddressedInstructions(); IValue left, right; @@ -132,7 +132,7 @@ public AddressedInstructions Visit(BinaryExpression visitable) else { result.AddRange(visitable.Left.Accept(This)); - left = new Name(result.OfType().Last().Left!); + left = result.OfType().Last().Left!; } if (visitable.Right is PrimaryExpression primaryRight) @@ -140,7 +140,7 @@ public AddressedInstructions Visit(BinaryExpression visitable) else { result.AddRange(visitable.Right.Accept(This)); - right = new Name(result.OfType().Last().Left!); + right = result.OfType().Last().Left!; } result.Add(new Simple(left, visitable.Operator, right)); @@ -154,7 +154,7 @@ public AddressedInstructions Visit(CastAsExpression visitable) return [new AsString(_valueDtoConverter.Convert(primary.ToValueDto()))]; var result = visitable.Expression.Accept(This); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new AsString(last)); return result; @@ -162,7 +162,7 @@ public AddressedInstructions Visit(CastAsExpression visitable) public AddressedInstructions Visit(WithExpression visitable) { - var objectId = visitable.ObjectLiteral.Id; + var objectId = new Name(visitable.ObjectLiteral.Id); var createObject = new CreateObject(objectId); var result = new AddressedInstructions { createObject }; @@ -188,7 +188,7 @@ public AddressedInstructions Visit(WithExpression visitable) var copyFrom = visitable.Expression is IdentifierReference objectIdent ? new Name(objectIdent) - : new Name(result.OfType().Last().Left!); + : result.OfType().Last().Left!; for (var i = 0; i < visitable.ComputedCopiedProperties.Count; i++) { @@ -214,7 +214,7 @@ public AddressedInstructions Visit(ConditionalExpression visitable) else { result.AddRange(visitable.Test.Accept(This)); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new IfNotGoto(last, startBlockLabel)); } @@ -227,7 +227,7 @@ public AddressedInstructions Visit(ConditionalExpression visitable) result.OfType().Last().Left = temp; result.Add(new EndBlock(BlockType.Condition, blockId), endBlockLabel.Name); - result.Add(new Simple(new Name(temp))); + result.Add(new Simple(temp)); return result; } @@ -241,14 +241,14 @@ public AddressedInstructions Visit(AssignmentExpression visitable) if (last is IWriteToComplexData assignment) result.Add(assignment.ToSimple()); else - result.Add(new Simple(new Name(last.Left!))); + result.Add(new Simple(last.Left!)); } if (visitable.Destination.Empty()) - result.OfType().Last().Left = visitable.Destination.Id; + result.OfType().Last().Left = new Name(visitable.Destination.Id); else { - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.AddRange(visitable.Destination.Accept(This)); var lastRead = result.OfType().Last(); result.Replace(lastRead.ToInstruction(), lastRead.ToAssignment(last)); @@ -270,7 +270,7 @@ public AddressedInstructions Visit(DotAccess visitable) return [new DotRead(new Name(lhs.Id), right)]; var result = visitable.Prev?.Accept(This) ?? []; - var left = new Name(result.OfType().Last().Left!); + var left = result.OfType().Last().Left!; result.Add(new DotRead(left, right)); return result; @@ -287,7 +287,7 @@ public AddressedInstructions Visit(IndexAccess visitable) else { result.AddRange(visitable.Index.Accept(This)); - right = new Name(result.OfType().Last().Left!); + right = result.OfType().Last().Left!; } if (!visitable.HasPrev() && visitable.Parent is LeftHandSideExpression lhs) @@ -295,7 +295,7 @@ public AddressedInstructions Visit(IndexAccess visitable) else { result.AddRange(visitable.Prev?.Accept(This) ?? []); - var left = new Name(result.OfType().Last().Left!); + var left = result.OfType().Last().Left!; result.Add(new IndexRead(left, right)); } @@ -320,8 +320,8 @@ public AddressedInstructions Visit(CallExpression visitable) if (methodCall) { - var caller = result.Any() ? result.OfType().Last().Left! : visitable.Id; - result.Add(new PushParameter(new Name(caller))); + var caller = result.Count > 0 ? result.OfType().Last().Left! : new Name(visitable.Id); + result.Add(new PushParameter(caller)); } for (var i = 0; i < visitable.Parameters.Count; i++) @@ -333,7 +333,7 @@ public AddressedInstructions Visit(CallExpression visitable) { result.AddRange(expr.Accept(This)); var id = result.OfType().Last().Left!; - result.Add(new PushParameter(new Name(id))); + result.Add(new PushParameter(id)); } } diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs index b7b64bbe..682f649f 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs @@ -107,7 +107,7 @@ public AddressedInstructions Visit(ReturnStatement visitable) } var result = visitable.Expression.Accept(_expressionVisitor); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new Return(last)); return result; @@ -115,7 +115,7 @@ public AddressedInstructions Visit(ReturnStatement visitable) public AddressedInstructions Visit(FunctionDeclaration visitable) { - if (!visitable.Statements.Any()) + if (visitable.IsEmpty) return []; var functionInfo = new FunctionInfo(visitable.ComputedFunctionAddress); @@ -132,7 +132,7 @@ public AddressedInstructions Visit(FunctionDeclaration visitable) for (var i = 0; i < visitable.Arguments.Count; i++) { var arg = visitable.Arguments[i]; - result.Add(new PopParameter(arg.Name, arg.Info.Value)); + result.Add(new PopParameter(new Name(arg.Name), arg.Info.Value)); } result.AddRange(visitable.Statements.Accept(This)); @@ -160,7 +160,7 @@ public AddressedInstructions Visit(WhileStatement visitable) else { result.AddRange(visitable.Condition.Accept(_expressionVisitor)); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new IfNotGoto(last, endBlockLabel)); } @@ -204,7 +204,7 @@ public AddressedInstructions Visit(IfStatement visitable) else { result.AddRange(visitable.Test.Accept(_expressionVisitor)); - var last = new Name(result.OfType().Last().Left!); + var last = result.OfType().Last().Left!; result.Add(new IfNotGoto(last, visitable.HasElseBlock() ? startBlockLabel @@ -239,11 +239,11 @@ public AddressedInstructions Visit(PrintStatement visitable) else { result.AddRange(visitable.Expression.Accept(_expressionVisitor)); - var name = new Name(result.OfType().Last().Left!); + var name = result.OfType().Last().Left!; result.Add(new AsString(name)); } - result.Add(new Print(new Name((result[result.End] as AsString)!.Left!))); + result.Add(new Print((result[result.End] as AsString)!.Left!)); return result; } diff --git a/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs b/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs index 95620e6a..2a974ddd 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs @@ -4,7 +4,7 @@ namespace HydraScript.Domain.BackEnd; -public class AddressedInstructions : IEnumerable +public class AddressedInstructions : IReadOnlyCollection { private readonly LinkedList _addresses = new(); private readonly Dictionary> _addressToNode = new(); @@ -85,6 +85,8 @@ public IEnumerator GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public int Count => _addresses.Count; + public override string ToString() => ZString.Join('\n', this); } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Call.cs b/src/Domain/HydraScript.Domain.BackEnd/Call.cs index c3cc4f61..7dd16697 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Call.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Call.cs @@ -1,11 +1,12 @@ using HydraScript.Domain.BackEnd.Impl.Addresses; +using HydraScript.Domain.BackEnd.Impl.Values; namespace HydraScript.Domain.BackEnd; public record Call( IAddress From, FunctionInfo To, - string? Where = null) + Name? Where = null) { public override string ToString() => $"{From}: {Where} => {To.Start}: {To.Id}"; diff --git a/src/Domain/HydraScript.Domain.BackEnd/Frame.cs b/src/Domain/HydraScript.Domain.BackEnd/Frame.cs index e3e5d963..f378ea9b 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Frame.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Frame.cs @@ -1,11 +1,9 @@ namespace HydraScript.Domain.BackEnd; -public class Frame(IAddress returnAddress, Frame? parentFrame = null) +public class Frame(Frame? parentFrame = null) { private readonly Dictionary _variables = new(); - public IAddress ReturnAddress { get; } = returnAddress; - public object? this[string id] { get => _variables.TryGetValue(id, out var value) diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs index 3ebd3e50..cb26c044 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/PopParameter.cs @@ -1,14 +1,17 @@ +using HydraScript.Domain.BackEnd.Impl.Values; + namespace HydraScript.Domain.BackEnd.Impl.Instructions; -public class PopParameter(string parameter, object? defaultValue) : Instruction +public class PopParameter(Name parameter, object? defaultValue) : Instruction { public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - if (executeParams.Arguments.TryDequeue(out var argument)) - frame[parameter] = argument; - else - frame[parameter] = defaultValue; + parameter.Set( + frame, + executeParams.Arguments.TryDequeue(out var argument) + ? argument + : defaultValue); return Address.Next; } diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/RemoveFromArray.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/RemoveFromArray.cs index b6acb8a8..83b1ff6d 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/RemoveFromArray.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/RemoveFromArray.cs @@ -1,11 +1,13 @@ +using HydraScript.Domain.BackEnd.Impl.Values; + namespace HydraScript.Domain.BackEnd.Impl.Instructions; -public class RemoveFromArray(string id, IValue index) : Instruction +public class RemoveFromArray(Name id, IValue index) : Instruction { public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - if (frame[id] is List list) + if (id.Get(frame) is List list) { list.RemoveAt(Convert.ToInt32(index.Get(frame))); } diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/Return.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/Return.cs index a30abf38..c304c7e5 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/Return.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/Return.cs @@ -2,16 +2,17 @@ namespace HydraScript.Domain.BackEnd.Impl.Instructions; public class Return(IValue? value = null) : Instruction { - public override IAddress Execute(IExecuteParams executeParams) + public override IAddress? Execute(IExecuteParams executeParams) { - var frame = executeParams.Frames.Pop(); + var callFrame = executeParams.Frames.Pop(); var call = executeParams.CallStack.Pop(); if (call.Where != null && value != null) { - executeParams.Frames.Peek()[call.Where] = value.Get(frame); + var frame = executeParams.Frames.Peek(); + call.Where?.Set(frame, value.Get(callFrame)); } - return frame.ReturnAddress; + return call.From.Next; } protected override string ToStringInternal() => diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/AsString.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/AsString.cs index d4014455..e8144eed 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/AsString.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/AsString.cs @@ -10,11 +10,12 @@ public partial class AsString(IValue value) : Simple(value) { var frame = executeParams.Frames.Peek(); var value = Right.right!.Get(frame); - frame[Left!] = value is string + var valueAsString = value is string ? value : JsonSerializer.Serialize( value: Right.right!.Get(frame)!, AsStringSerializationContext.Default.Object); + Left?.Set(frame, valueAsString); return Address.Next; } diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/CallFunction.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/CallFunction.cs index 8d3a0559..10d37cb0 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/CallFunction.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/CallFunction.cs @@ -12,7 +12,7 @@ protected override void OnSetOfAddress(IAddress address) public override IAddress Execute(IExecuteParams executeParams) { - var frame = new Frame(Address.Next!, executeParams.Frames.Peek()); + var frame = new Frame(executeParams.Frames.Peek()); executeParams.CallStack.Push(new Call(Address, function, Left)); executeParams.Frames.Push(frame); return function.Start; diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateArray.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateArray.cs index 5513794c..30f61eff 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateArray.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateArray.cs @@ -1,8 +1,10 @@ +using HydraScript.Domain.BackEnd.Impl.Values; + namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Create; -public class CreateArray(string id, int size) : Simple(id) +public class CreateArray(Name id, int size) : Simple(id) { - private readonly string _id = id; + private readonly Name _id = id; public override IAddress? Execute(IExecuteParams executeParams) { @@ -10,7 +12,7 @@ public class CreateArray(string id, int size) : Simple(id) var list = new List(size * 2); for (var i = 0; i < size; i++) list.Add(null!); - frame[_id] = list; + _id.Set(frame, list); return Address.Next; } diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateObject.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateObject.cs index 2155a156..b95550ce 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateObject.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Create/CreateObject.cs @@ -1,13 +1,15 @@ +using HydraScript.Domain.BackEnd.Impl.Values; + namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Create; -public class CreateObject(string id) : Simple(id) +public class CreateObject(Name id) : Simple(id) { - private readonly string _id = id; + private readonly Name _id = id; public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - frame[_id] = new Dictionary(); + _id.Set(frame, new Dictionary()); return Address.Next; } diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/DotRead.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/DotRead.cs index 0ccc1df0..510d81b7 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/DotRead.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/DotRead.cs @@ -3,18 +3,16 @@ namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Read; -public class DotRead(Name @object, IValue property) : Simple( +public class DotRead(Name @object, Constant property) : Simple( leftValue: @object, binaryOperator: ".", rightValue: property), IReadFromComplexData { - private readonly IValue _property = property; - public Simple ToAssignment(IValue value) => - new DotAssignment(@object.ToString(), _property, value); + new DotAssignment(@object, property, value); public IExecutableInstruction ToInstruction() => this; protected override string ToStringInternal() => - $"{Left} = {@object}.{_property}"; + $"{Left} = {@object}.{property}"; } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/IndexRead.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/IndexRead.cs index af34e7db..64c73aa3 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/IndexRead.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Read/IndexRead.cs @@ -11,7 +11,7 @@ public class IndexRead(Name array, IValue index) : Simple( private readonly IValue _index = index; public Simple ToAssignment(IValue value) => - new IndexAssignment(array.ToString(), _index, value); + new IndexAssignment(array, _index, value); public IExecutableInstruction ToInstruction() => this; diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/DotAssignment.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/DotAssignment.cs index 8bd8f720..1a09ed66 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/DotAssignment.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/DotAssignment.cs @@ -3,13 +3,13 @@ namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Write; -public class DotAssignment(string @object, IValue property, IValue value) +public class DotAssignment(Name @object, Constant property, IValue value) : Simple(left: @object, (property, value), "."), IWriteToComplexData { public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - if (frame[Left!] is Dictionary obj) + if (Left?.Get(frame) is Dictionary obj) { var field = (string?)Right.left?.Get(frame) ?? string.Empty; obj[field] = Right.right!.Get(frame)!; @@ -19,7 +19,7 @@ public class DotAssignment(string @object, IValue property, IValue value) } public Simple ToSimple() => - new DotRead(new Name(Left!), Right.left!); + new DotRead(Left!, property); protected override string ToStringInternal() => $"{Left}.{Right.left} = {Right.right}"; diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/IndexAssignment.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/IndexAssignment.cs index 1a1996a5..e4fda764 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/IndexAssignment.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/ComplexData/Write/IndexAssignment.cs @@ -3,13 +3,13 @@ namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Write; -public class IndexAssignment(string array, IValue index, IValue value) +public class IndexAssignment(Name array, IValue index, IValue value) : Simple(left: array, right: (index, value), "[]"), IWriteToComplexData { public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - if (frame[Left!] is List list) + if (Left?.Get(frame) is List list) { var index = Convert.ToInt32(Right.left!.Get(frame)); list[index] = Right.right!.Get(frame)!; @@ -19,7 +19,7 @@ public class IndexAssignment(string array, IValue index, IValue value) } public Simple ToSimple() => - new IndexRead(new Name(Left!), Right.left!); + new IndexRead(Left!, Right.left!); protected override string ToStringInternal() => $"{Left}[{Right.left}] = {Right.right}"; diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/Simple.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/Simple.cs index fa0b6aea..7b0009ab 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/Simple.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Instructions/WithAssignment/Simple.cs @@ -1,18 +1,19 @@ using Cysharp.Text; +using HydraScript.Domain.BackEnd.Impl.Values; namespace HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment; public class Simple : Instruction { - public string? Left { get; set; } + public Name? Left { get; set; } protected readonly (IValue? left, IValue? right) Right; private readonly string _operator = string.Empty; - protected Simple(string? left) => Left = left; + protected Simple(Name? left) => Left = left; public Simple( - string? left, + Name? left, (IValue? left, IValue? right) right, string @operator) { @@ -47,52 +48,55 @@ public Simple( } protected override void OnSetOfAddress(IAddress address) => - Left ??= address.Name; + Left ??= new Name(address.Name); public override IAddress? Execute(IExecuteParams executeParams) { var frame = executeParams.Frames.Peek(); - if (Right.left == null) - { - var value = Right.right!.Get(frame); - frame[Left!] = _operator switch - { - "-" => -Convert.ToDouble(value), - "!" => !Convert.ToBoolean(value), - "~" => ((List)value!).Count, - "" => value, - _ => throw new NotSupportedException($"_operator {_operator} is not supported") - }; - } - else - { - object? lValue = Right.left.Get(frame), rValue = Right.right!.Get(frame); - frame[Left!] = _operator switch - { - "+" when lValue is string => ZString.Concat(lValue, rValue), - "+" => Convert.ToDouble(lValue) + Convert.ToDouble(rValue), - "-" => Convert.ToDouble(lValue) - Convert.ToDouble(rValue), - "*" => Convert.ToDouble(lValue) * Convert.ToDouble(rValue), - "/" => Convert.ToDouble(lValue) / Convert.ToDouble(rValue), - "%" => Convert.ToDouble(lValue) % Convert.ToDouble(rValue), - "||" => Convert.ToBoolean(lValue) || Convert.ToBoolean(rValue), - "&&" => Convert.ToBoolean(lValue) && Convert.ToBoolean(rValue), - "==" => Equals(lValue, rValue), - "!=" => !Equals(lValue, rValue), - ">" => Convert.ToDouble(lValue) > Convert.ToDouble(rValue), - ">=" => Convert.ToDouble(lValue) >= Convert.ToDouble(rValue), - "<" => Convert.ToDouble(lValue) < Convert.ToDouble(rValue), - "<=" => Convert.ToDouble(lValue) <= Convert.ToDouble(rValue), - "." => ((Dictionary)lValue!)[rValue!.ToString()!], - "[]" => ((List)lValue!)[Convert.ToInt32(rValue)], - "++" => ((List)lValue!).Concat((List)rValue!).ToList(), - _ => throw new NotSupportedException($"_operator {_operator} is not supported") - }; - } + Left?.Set(frame, Right.left is null ? GetUnaryResult(frame) : GetBinaryResult(frame)); return Address.Next; } + private object? GetUnaryResult(Frame frame) + { + var value = Right.right!.Get(frame); + return _operator switch + { + "-" => -Convert.ToDouble(value), + "!" => !Convert.ToBoolean(value), + "~" => ((List)value!).Count, + "" => value, + _ => throw new NotSupportedException($"_operator {_operator} is not supported") + }; + } + + private object? GetBinaryResult(Frame frame) + { + object? lValue = Right.left!.Get(frame), rValue = Right.right!.Get(frame); + return _operator switch + { + "+" when lValue is string => ZString.Concat(lValue, rValue), + "+" => Convert.ToDouble(lValue) + Convert.ToDouble(rValue), + "-" => Convert.ToDouble(lValue) - Convert.ToDouble(rValue), + "*" => Convert.ToDouble(lValue) * Convert.ToDouble(rValue), + "/" => Convert.ToDouble(lValue) / Convert.ToDouble(rValue), + "%" => Convert.ToDouble(lValue) % Convert.ToDouble(rValue), + "||" => Convert.ToBoolean(lValue) || Convert.ToBoolean(rValue), + "&&" => Convert.ToBoolean(lValue) && Convert.ToBoolean(rValue), + "==" => Equals(lValue, rValue), + "!=" => !Equals(lValue, rValue), + ">" => Convert.ToDouble(lValue) > Convert.ToDouble(rValue), + ">=" => Convert.ToDouble(lValue) >= Convert.ToDouble(rValue), + "<" => Convert.ToDouble(lValue) < Convert.ToDouble(rValue), + "<=" => Convert.ToDouble(lValue) <= Convert.ToDouble(rValue), + "." => ((Dictionary)lValue!)[rValue!.ToString()!], + "[]" => ((List)lValue!)[Convert.ToInt32(rValue)], + "++" => ((List)lValue!).Concat((List)rValue!).ToList(), + _ => throw new NotSupportedException($"_operator {_operator} is not supported") + }; + } + protected override string ToStringInternal() => Right.left == null ? $"{Left} = {_operator}{Right.right}" diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/Values/Name.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/Values/Name.cs index 0ce77de1..bcd60244 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/Values/Name.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/Values/Name.cs @@ -6,6 +6,9 @@ public class Name(string id) : IValue public object? Get(Frame? frame) => frame![_id]; + public void Set(Frame? frame, object? value) => + frame?[_id] = value; + public override string ToString() => _id; public bool Equals(IValue? other) => diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs index 09dfaf83..a2fffa32 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs @@ -6,7 +6,7 @@ public class VirtualMachine(IOutputWriter writer) : IVirtualMachine public void Run(AddressedInstructions instructions) { - ExecuteParams.Frames.Push(new Frame(instructions.Start)); + ExecuteParams.Frames.Push(new Frame()); var address = instructions.Start; while (address is not null) diff --git a/tests/HydraScript.UnitTests/Application/WithExpressionData.cs b/tests/HydraScript.UnitTests/Application/WithExpressionData.cs index 9a1aa247..083f33e0 100644 --- a/tests/HydraScript.UnitTests/Application/WithExpressionData.cs +++ b/tests/HydraScript.UnitTests/Application/WithExpressionData.cs @@ -29,8 +29,8 @@ public WithExpressionData() ]), new ObjectLiteral([])))), [ - new CreateObject("obj"), - new DotAssignment("obj", new Constant("x"), new Constant(1)) + new CreateObject(new Name("obj")), + new DotAssignment(new Name("obj"), new Constant("x"), new Constant(1)) ]); // let copyFrom = {x: 0;} @@ -51,8 +51,8 @@ public WithExpressionData() ComputedCopiedProperties = [] })), [ - new CreateObject("obj"), - new DotAssignment("obj", new Constant("x"), new Constant(1)) + new CreateObject(new Name("obj")), + new DotAssignment(new Name("obj"), new Constant("x"), new Constant(1)) ]); } } \ No newline at end of file diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/AddressedInstructionsTests.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/AddressedInstructionsTests.cs index 1e40cab2..7953f2cc 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/AddressedInstructionsTests.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/AddressedInstructionsTests.cs @@ -15,7 +15,7 @@ public void EnumerationPreservedAfterRemovalTest() [ new AsString(new Constant(2)) { - Left = "s" + Left = new Name("s") }, new Print(new Name("s")), new Halt() @@ -44,17 +44,17 @@ public void ReplacementPreservesOrderTest() { var instructions = new AddressedInstructions { - new Simple("a", (new Constant(1), new Constant(2)), "-"), + new Simple(new Name("a"), (new Constant(1), new Constant(2)), "-"), { new AsString(new Constant(true)) - { Left = "s" }, + { Left = new Name("s") }, "as_str" }, new Print(new Name("s")) }; var old = instructions[new Label("as_str")]; - var @new = new AsString(new Name("a")) { Left = "s" }; + var @new = new AsString(new Name("a")) { Left = new Name("s") }; instructions.Replace(old, @new); var prev = instructions.First(); diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/AsStringTests.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/AsStringTests.cs index 716296f2..96f5f78f 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/AsStringTests.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/AsStringTests.cs @@ -12,7 +12,7 @@ public void Execute_String_NoQuotes(VirtualMachine vm) { // Arrange AddressedInstructions program = [new AsString(new Constant("string"))]; - vm.ExecuteParams.Frames.Push(new Frame(program.Start)); + vm.ExecuteParams.Frames.Push(new Frame()); // Act program[program.Start].Execute(vm.ExecuteParams); diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs index 1daaccaf..490b1dd1 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/InstructionsData.cs @@ -18,7 +18,7 @@ public IEnumerator GetEnumerator() [ new AsString(new Name("num")) { - Left = "str" + Left = new Name("str") }, "str = num as string" ]; @@ -39,23 +39,23 @@ public IEnumerator GetEnumerator() [ new CallFunction(new FunctionInfo("func"), true) { - Left = "ret" + Left = new Name("ret") }, "ret = Call func" ]; yield return [ - new CreateArray("arr", 5), + new CreateArray(new Name("arr"), 5), "array arr = [5]" ]; yield return [ - new CreateObject("obj"), + new CreateObject(new Name("obj")), "object obj = {}" ]; yield return [ - new DotAssignment("obj", new Constant("prop"), new Constant(3)), + new DotAssignment(new Name("obj"), new Constant("prop"), new Constant(3)), "obj.prop = 3" ]; yield return @@ -83,7 +83,7 @@ public IEnumerator GetEnumerator() ]; yield return [ - new IndexAssignment("arr", new Constant(1), new Constant(1)), + new IndexAssignment(new Name("arr"), new Constant(1), new Constant(1)), "arr[1] = 1" ]; yield return @@ -98,12 +98,12 @@ public IEnumerator GetEnumerator() ]; yield return [ - new PopParameter("param", defaultValue: null), + new PopParameter(new Name("param"), defaultValue: null), "PopParameter param" ]; yield return [ - new RemoveFromArray("arr", new Constant(0)), + new RemoveFromArray(new Name("arr"), new Constant(0)), "RemoveFrom arr at 0" ]; yield return @@ -118,12 +118,12 @@ public IEnumerator GetEnumerator() ]; yield return [ - new Simple("a", (new Name("b"), new Name("c")), "+"), + new Simple(new Name("a"), (new Name("b"), new Name("c")), "+"), "a = b + c" ]; yield return [ - new Simple("b", (null, new Name("c")), "-"), + new Simple(new Name("b"), (null, new Name("c")), "-"), "b = -c" ]; } diff --git a/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs b/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs index c5879e99..0fab3cf2 100644 --- a/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs +++ b/tests/HydraScript.UnitTests/Domain/BackEnd/VirtualMachineTests.cs @@ -17,7 +17,7 @@ public class VirtualMachineTests public void CorrectPrintToOutTest([Frozen] IOutputWriter writer, IExecuteParams exParams) { exParams.CallStack.Returns(new Stack()); - exParams.Frames.Returns(new Stack([new Frame(new HashAddress(0))])); + exParams.Frames.Returns(new Stack([new Frame()])); exParams.Arguments.Returns(new Queue()); var print = new Print(new Constant(223)) @@ -40,10 +40,10 @@ public void VirtualMachineFramesClearedAfterExecutionTest(VirtualMachine vm) { AddressedInstructions program = [ - new Simple("a", (new Constant(1), new Constant(2)), "+"), + new Simple(new Name("a"), (new Constant(1), new Constant(2)), "+"), new AsString(new Name("a")) { - Left = "s" + Left = new Name("s") }, new Halt() ]; @@ -61,23 +61,23 @@ public void VirtualMachineHandlesRecursionTest(VirtualMachine vm) { new Goto(factorial.End), { new BeginBlock(BlockType.Function, blockId: factorial.ToString()), factorial.Start.Name }, - new PopParameter("n", defaultValue: null), - new Simple("_t2", (new Name("n"), new Constant(2)), "<"), + new PopParameter(new Name("n"), defaultValue: null), + new Simple(new Name("_t2"), (new Name("n"), new Constant(2)), "<"), new IfNotGoto(new Name("_t2"), new Label("5")), new Return(new Name("n")), - { new Simple("_t5", (new Name("n"), new Constant(1)), "-"), "5" }, + { new Simple(new Name("_t5"), (new Name("n"), new Constant(1)), "-"), "5" }, new PushParameter(new Name("_t5")), new CallFunction(factorial, true) { - Left = "f" + Left = new Name("f") }, - new Simple("_t8", (new Name("n"), new Name("f")), "*"), + new Simple(new Name("_t8"), (new Name("n"), new Name("f")), "*"), new Return(new Name("_t8")), { new EndBlock(BlockType.Function, blockId: factorial.ToString()), factorial.End.Name }, new PushParameter(new Constant(6)), new CallFunction(factorial, true) { - Left = "fa6" + Left = new Name("fa6") }, halt }; @@ -95,23 +95,23 @@ public void VirtualMachineHandlesRecursionTest(VirtualMachine vm) [Theory, AutoHydraScriptData] public void CreateArrayReservesCertainSpaceTest(ExecuteParams vm) { - vm.Frames.Push(new Frame(new HashAddress(0))); + vm.Frames.Push(new Frame()); - var createArray = new CreateArray("arr", 6) + var createArray = new CreateArray(new Name("arr"), 6) { Address = new HashAddress(1) }; createArray.Execute(vm); Assert.Equal(6, ((List)vm.Frames.Peek()["arr"]!).Count); - var indexAssignment = new IndexAssignment("arr", new Constant(0), new Constant(0)) + var indexAssignment = new IndexAssignment(new Name("arr"), new Constant(0), new Constant(0)) { Address = new HashAddress(2) }; indexAssignment.Execute(vm); Assert.Equal(0, ((List)vm.Frames.Peek()["arr"]!)[0]); - var removeFromArray = new RemoveFromArray("arr", new Constant(5)) + var removeFromArray = new RemoveFromArray(new Name("arr"), new Constant(5)) { Address = new HashAddress(3) }; @@ -125,8 +125,8 @@ public void ObjectCreationTest(VirtualMachine vm) var halt = HaltTrackable(); AddressedInstructions program = [ - new CreateObject("obj"), - new DotAssignment("obj", new Constant("prop"), new Constant(null, "null")), + new CreateObject(new Name("obj")), + new DotAssignment(new Name("obj"), new Constant("prop"), new Constant(null, "null")), halt ]; From 91bd366ca833f5441a17def71ffc6d01986454be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD?= Date: Tue, 16 Dec 2025 16:05:03 +0300 Subject: [PATCH 04/25] =?UTF-8?q?#201=20-=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=D1=80=D0=B5=D0=B4=D1=8B?= =?UTF-8?q?=20(#214)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #201 - install env package * #201 - integrate env into vm * #201 - fix env di + testing * #201 - update lex * #201 - parser refactor * #201 - refactoring * #201 - rename tests * #201 - fix * #201 - rm dead code * #201 - literal refactoring * #201 - env var node parsing * #201 - parsing tests * #201 - refactoring + tests * #201 - fix * add src props * update pull_request_template.md * #201 - fix after merge * #201 - remove env from ExecuteParams.cs * #201 - add IFrame abstraction * #201 - add EnvFrame.cs * #201 - move envframe to impl * #201 - frame context && CurrentFrame.cs * fix * #201 - frame refactoring * IDefaultValueForTypeCalculator * DefaultValueForTypeCalculator restore * #201 - fix ImplicitLiteral.cs * #201 - codegen + static analysis * #201 - return not null if no env is present * restore test * #201 - fix + tests * small refac * #201 - refac IValueFactory.cs --- Directory.Packages.props | 1 + ExtendedJavaScriptSubset.slnx | 3 +- .../IValueDtoConverter.cs | 9 - .../IValueFactory.cs | 14 ++ .../Impl/ValueDtoConverter.cs | 18 -- .../Impl/ValueFactory.cs | 42 +++++ .../ServiceCollectionExtensions.cs | 2 +- .../Visitors/ExpressionInstructionProvider.cs | 52 +++--- .../Visitors/InstructionProvider.cs | 19 +-- .../Impl/DefaultValueForTypeCalculator.cs | 2 +- .../Visitors/SemanticChecker.cs | 18 +- .../HydraScript.Domain.BackEnd.csproj | 1 + .../IExecuteParams.cs | 5 +- .../HydraScript.Domain.BackEnd/IFrame.cs | 6 + .../IFrameContext.cs | 10 ++ .../HydraScript.Domain.BackEnd/IValue.cs | 2 +- .../IVirtualMachine.cs | 1 + .../Impl/ExecuteParams.cs | 13 +- .../Impl/FrameContext.cs | 17 ++ .../Impl/Frames/CurrentFrame.cs | 10 ++ .../Impl/Frames/EnvFrame.cs | 10 ++ .../{ => Impl/Frames}/Frame.cs | 4 +- .../Impl/Instructions/Halt.cs | 2 +- .../Impl/Instructions/PopParameter.cs | 2 - .../Impl/Instructions/Print.cs | 2 +- .../Impl/Instructions/PushParameter.cs | 3 +- .../Impl/Instructions/RemoveFromArray.cs | 5 +- .../Impl/Instructions/Return.cs | 10 +- .../Instructions/WithAssignment/AsString.cs | 11 +- .../WithAssignment/CallFunction.cs | 6 +- .../ComplexData/Create/CreateArray.cs | 6 +- .../ComplexData/Create/CreateObject.cs | 8 +- .../ComplexData/Write/DotAssignment.cs | 15 +- .../ComplexData/Write/IndexAssignment.cs | 13 +- .../Instructions/WithAssignment/Simple.cs | 24 ++- .../Impl/Instructions/WithJump/IfNotGoto.cs | 3 +- .../Impl/Values/Constant.cs | 2 +- .../Impl/Values/EnvName.cs | 12 ++ .../Impl/Values/Name.cs | 27 ++- .../Impl/VirtualMachine.cs | 8 +- .../TokenTypes.cs | 4 +- .../Lexer/Token.cs | 18 +- .../Impl/Ast/Nodes/Declarations/TypeValue.cs | 8 +- .../ComplexLiterals/ArrayLiteral.cs | 6 +- .../ComplexLiterals/ComplexLiteral.cs | 3 +- .../ComplexLiterals/ObjectLiteral.cs | 6 +- .../PrimaryExpressions/AbstractLiteral.cs | 3 +- .../PrimaryExpressions/EnvVarReference.cs | 12 ++ .../PrimaryExpressions/ImplicitLiteral.cs | 22 ++- .../Expressions/PrimaryExpressions/Literal.cs | 12 ++ .../PrimaryExpressions/ValueDto.cs | 10 +- .../Impl/Ast/Nodes/Statements/IfStatement.cs | 6 +- .../Parser/Impl/TopDownParser.cs | 107 ++++++------ .../Parser/grammar.txt | 3 +- .../ServiceCollectionExtensions.cs | 2 + .../Samples/defaultarray.js | 3 +- .../Samples/env.js | 2 + .../Samples/jump.js | 4 +- .../Samples/xxx.js | 3 +- .../TestHostFixture.cs | 10 +- .../ExpressionInstructionProviderTests.cs | 4 +- .../Application/ReturnAnalyzerTests.cs | 7 +- .../Application/WithExpressionData.cs | 15 +- .../AutoHydraScriptDataAttribute.cs | 6 + .../BackEnd/AddressedInstructionsTests.cs | 15 +- .../Domain/BackEnd/AsStringTests.cs | 10 +- .../Domain/BackEnd/InstructionsData.cs | 157 +++++++----------- .../Domain/BackEnd/InstructionsTests.cs | 4 +- .../Domain/BackEnd/TestVirtualMachine.cs | 13 ++ .../Domain/BackEnd/ValuesTests.cs | 9 +- .../Domain/BackEnd/VirtualMachineTests.cs | 79 +++++---- .../Domain/FrontEnd/AstNodeTests.cs | 9 +- .../Domain/FrontEnd/ComplexLiteralTests.cs | 2 +- .../Domain/FrontEnd/ImplicitLiteralTests.cs | 32 ++++ .../Domain/FrontEnd/LexerData.cs | 2 +- .../Domain/FrontEnd/ParserData.cs | 60 +++++-- .../Domain/FrontEnd/TopDownParserTests.cs | 17 +- 77 files changed, 659 insertions(+), 444 deletions(-) delete mode 100644 src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs create mode 100644 src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs delete mode 100644 src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs create mode 100644 src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs create mode 100644 src/Domain/HydraScript.Domain.BackEnd/IFrame.cs create mode 100644 src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs create mode 100644 src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs create mode 100644 src/Domain/HydraScript.Domain.BackEnd/Impl/Frames/CurrentFrame.cs create mode 100644 src/Domain/HydraScript.Domain.BackEnd/Impl/Frames/EnvFrame.cs rename src/Domain/HydraScript.Domain.BackEnd/{ => Impl/Frames}/Frame.cs (70%) create mode 100644 src/Domain/HydraScript.Domain.BackEnd/Impl/Values/EnvName.cs create mode 100644 src/Domain/HydraScript.Domain.FrontEnd/Parser/Impl/Ast/Nodes/Expressions/PrimaryExpressions/EnvVarReference.cs create mode 100644 tests/HydraScript.IntegrationTests/Samples/env.js create mode 100644 tests/HydraScript.UnitTests/Domain/BackEnd/TestVirtualMachine.cs create mode 100644 tests/HydraScript.UnitTests/Domain/FrontEnd/ImplicitLiteralTests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index c1782766..84754d44 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,6 +1,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ExtendedJavaScriptSubset.slnx b/ExtendedJavaScriptSubset.slnx index f5a7e5c2..f0736330 100644 --- a/ExtendedJavaScriptSubset.slnx +++ b/ExtendedJavaScriptSubset.slnx @@ -1,6 +1,6 @@ - + @@ -28,6 +28,7 @@ + diff --git a/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs b/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs deleted file mode 100644 index 36d70216..00000000 --- a/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs +++ /dev/null @@ -1,9 +0,0 @@ -using HydraScript.Domain.BackEnd; -using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions; - -namespace HydraScript.Application.CodeGeneration; - -public interface IValueDtoConverter -{ - public IValue Convert(ValueDto dto); -} \ No newline at end of file diff --git a/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs b/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs new file mode 100644 index 00000000..4af6ba61 --- /dev/null +++ b/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs @@ -0,0 +1,14 @@ +using HydraScript.Domain.BackEnd; +using HydraScript.Domain.BackEnd.Impl.Values; +using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions; + +namespace HydraScript.Application.CodeGeneration; + +public interface IValueFactory +{ + public IValue Create(ValueDto dto); + + public Name CreateName(IdentifierReference id); + + public Name CreateName(string id); +} \ No newline at end of file diff --git a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs deleted file mode 100644 index ee3f919b..00000000 --- a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using HydraScript.Domain.BackEnd; -using HydraScript.Domain.BackEnd.Impl.Values; -using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions; - -namespace HydraScript.Application.CodeGeneration.Impl; - -internal class ValueDtoConverter : IValueDtoConverter -{ - public IValue Convert(ValueDto dto) => - dto switch - { - { Type: ValueDtoType.Constant, Label: not null } => - new Constant(dto.Value, dto.Label), - { Type: ValueDtoType.Name, Name: not null } => - new Name(dto.Name), - _ => throw new ArgumentOutOfRangeException(nameof(dto)) - }; -} \ No newline at end of file diff --git a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs new file mode 100644 index 00000000..f722f8c6 --- /dev/null +++ b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs @@ -0,0 +1,42 @@ +using HydraScript.Domain.BackEnd; +using HydraScript.Domain.BackEnd.Impl.Frames; +using HydraScript.Domain.BackEnd.Impl.Values; +using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions; + +namespace HydraScript.Application.CodeGeneration.Impl; + +internal class ValueFactory( + IFrameContext frameContext, + IEnvironmentVariableProvider provider) : IValueFactory +{ + public IValue Create(ValueDto dto) => + dto switch + { + { Type: ValueDtoType.Constant, Label: not null } => + new Constant(dto.Value, dto.Label), + { Type: ValueDtoType.Name, Name: not null } => + new Name(dto.Name, CurrentFrame), + { Type: ValueDtoType.Env, Name: not null } => + new EnvName(dto.Name, EnvFrame), + _ => throw new ArgumentOutOfRangeException(nameof(dto)) + }; + + public Name CreateName(IdentifierReference id) + { + var dto = id.ToValueDto(); + return dto switch + { + { Type: ValueDtoType.Name, Name: not null } => + new Name(dto.Name, CurrentFrame), + { Type: ValueDtoType.Env, Name: not null } => + new EnvName(dto.Name, EnvFrame), + _ => throw new ArgumentOutOfRangeException(nameof(dto)) + }; + } + + public Name CreateName(string id) => new(id, CurrentFrame); + + private CurrentFrame CurrentFrame { get; } = new(frameContext); + + private EnvFrame EnvFrame { get; } = new(provider); +} \ No newline at end of file diff --git a/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs b/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs index b79c4be5..bd3254d8 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddCodeGeneration(this IServiceCollection services) { - services.AddSingleton(); + services.AddSingleton(); services.AddKeyedSingleton< IVisitor, InstructionProvider>(CodeGeneratorType.General); diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs index 9cf2b501..f0aec001 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs @@ -32,21 +32,21 @@ internal class ExpressionInstructionProvider : VisitorBase, IVisitor { - private readonly IValueDtoConverter _valueDtoConverter; + private readonly IValueFactory _valueFactory; - public ExpressionInstructionProvider(IValueDtoConverter valueDtoConverter) => - _valueDtoConverter = valueDtoConverter; + public ExpressionInstructionProvider(IValueFactory valueFactory) => + _valueFactory = valueFactory; public override AddressedInstructions Visit(IAbstractSyntaxTreeNode visitable) => []; public AddressedInstructions Visit(PrimaryExpression visitable) => - [new Simple(_valueDtoConverter.Convert(visitable.ToValueDto()))]; + [new Simple(_valueFactory.Create(visitable.ToValueDto()))]; public AddressedInstructions Visit(ArrayLiteral visitable) { var arraySize = visitable.Expressions.Count; - var arrayName = new Name(visitable.Id); + var arrayName = _valueFactory.CreateName(visitable.Id); var createArray = new CreateArray(arrayName, arraySize); var result = new AddressedInstructions { createArray }; @@ -60,7 +60,7 @@ public AddressedInstructions Visit(ArrayLiteral visitable) result.Add(new IndexAssignment( arrayName, index, - _valueDtoConverter.Convert(primary.ToValueDto()))); + _valueFactory.Create(primary.ToValueDto()))); else { result.AddRange(expression.Accept(This)); @@ -74,8 +74,8 @@ public AddressedInstructions Visit(ArrayLiteral visitable) public AddressedInstructions Visit(ObjectLiteral visitable) { - var objectId = visitable.Id; - var createObject = new CreateObject(new Name(objectId)); + var objectId = _valueFactory.CreateName(visitable.Id); + var createObject = new CreateObject(objectId); var result = new AddressedInstructions { createObject }; @@ -89,7 +89,7 @@ public AddressedInstructions Visit(ObjectLiteral visitable) public AddressedInstructions Visit(Property visitable) { - var objectId = new Name(visitable.Object.Id); + var objectId = _valueFactory.CreateName(visitable.Object.Id); var (id, expression) = visitable; var propertyId = new Constant(id); @@ -98,7 +98,7 @@ public AddressedInstructions Visit(Property visitable) return [new DotAssignment( objectId, propertyId, - _valueDtoConverter.Convert(primary.ToValueDto()))]; + _valueFactory.Create(primary.ToValueDto()))]; var instructions = expression.Accept(This); var last = instructions.OfType().Last().Left!; @@ -110,7 +110,7 @@ public AddressedInstructions Visit(Property visitable) public AddressedInstructions Visit(UnaryExpression visitable) { if (visitable.Expression is PrimaryExpression primary) - return [new Simple(visitable.Operator, _valueDtoConverter.Convert(primary.ToValueDto()))]; + return [new Simple(visitable.Operator, _valueFactory.Create(primary.ToValueDto()))]; var result = visitable.Expression.Accept(This); var last = result.OfType().Last().Left!; @@ -122,13 +122,13 @@ public AddressedInstructions Visit(UnaryExpression visitable) public AddressedInstructions Visit(BinaryExpression visitable) { if (visitable is { Left: IdentifierReference arr, Right: PrimaryExpression primary, Operator: "::" }) - return [new RemoveFromArray(new Name(arr), index: _valueDtoConverter.Convert(primary.ToValueDto()))]; + return [new RemoveFromArray(_valueFactory.CreateName(arr), index: _valueFactory.Create(primary.ToValueDto()))]; var result = new AddressedInstructions(); IValue left, right; if (visitable.Left is PrimaryExpression primaryLeft) - left = _valueDtoConverter.Convert(primaryLeft.ToValueDto()); + left = _valueFactory.Create(primaryLeft.ToValueDto()); else { result.AddRange(visitable.Left.Accept(This)); @@ -136,7 +136,7 @@ public AddressedInstructions Visit(BinaryExpression visitable) } if (visitable.Right is PrimaryExpression primaryRight) - right = _valueDtoConverter.Convert(primaryRight.ToValueDto()); + right = _valueFactory.Create(primaryRight.ToValueDto()); else { result.AddRange(visitable.Right.Accept(This)); @@ -151,7 +151,7 @@ public AddressedInstructions Visit(BinaryExpression visitable) public AddressedInstructions Visit(CastAsExpression visitable) { if (visitable.Expression is PrimaryExpression primary) - return [new AsString(_valueDtoConverter.Convert(primary.ToValueDto()))]; + return [new AsString(_valueFactory.Create(primary.ToValueDto()))]; var result = visitable.Expression.Accept(This); var last = result.OfType().Last().Left!; @@ -162,7 +162,7 @@ public AddressedInstructions Visit(CastAsExpression visitable) public AddressedInstructions Visit(WithExpression visitable) { - var objectId = new Name(visitable.ObjectLiteral.Id); + var objectId = _valueFactory.CreateName(visitable.ObjectLiteral.Id); var createObject = new CreateObject(objectId); var result = new AddressedInstructions { createObject }; @@ -187,7 +187,7 @@ public AddressedInstructions Visit(WithExpression visitable) result.AddRange(visitable.Expression is PrimaryExpression ? [] : visitable.Expression.Accept(This)); var copyFrom = visitable.Expression is IdentifierReference objectIdent - ? new Name(objectIdent) + ? _valueFactory.CreateName(objectIdent) : result.OfType().Last().Left!; for (var i = 0; i < visitable.ComputedCopiedProperties.Count; i++) @@ -195,7 +195,7 @@ public AddressedInstructions Visit(WithExpression visitable) var property = new Constant(visitable.ComputedCopiedProperties[i]); result.Add(new DotRead(copyFrom, property)); var read = result[result.End].Address.Name; - result.Add(new DotAssignment(objectId, property, new Name(read))); + result.Add(new DotAssignment(objectId, property, _valueFactory.CreateName(read))); } return result; @@ -210,7 +210,7 @@ public AddressedInstructions Visit(ConditionalExpression visitable) var result = new AddressedInstructions(); if (visitable.Test is PrimaryExpression primary) - result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), startBlockLabel)); + result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), startBlockLabel)); else { result.AddRange(visitable.Test.Accept(This)); @@ -245,7 +245,7 @@ public AddressedInstructions Visit(AssignmentExpression visitable) } if (visitable.Destination.Empty()) - result.OfType().Last().Left = new Name(visitable.Destination.Id); + result.OfType().Last().Left = _valueFactory.CreateName(visitable.Destination.Id); else { var last = result.OfType().Last().Left!; @@ -267,7 +267,7 @@ public AddressedInstructions Visit(DotAccess visitable) var right = new Constant(visitable.Property.Name); if (!visitable.HasPrev() && visitable.Parent is LeftHandSideExpression lhs) - return [new DotRead(new Name(lhs.Id), right)]; + return [new DotRead(_valueFactory.CreateName(lhs.Id), right)]; var result = visitable.Prev?.Accept(This) ?? []; var left = result.OfType().Last().Left!; @@ -283,7 +283,7 @@ public AddressedInstructions Visit(IndexAccess visitable) IValue right; if (visitable.Index is PrimaryExpression primary) - right = _valueDtoConverter.Convert(primary.ToValueDto()); + right = _valueFactory.Create(primary.ToValueDto()); else { result.AddRange(visitable.Index.Accept(This)); @@ -291,7 +291,7 @@ public AddressedInstructions Visit(IndexAccess visitable) } if (!visitable.HasPrev() && visitable.Parent is LeftHandSideExpression lhs) - result.Add(new IndexRead(new Name(lhs.Id), right)); + result.Add(new IndexRead(_valueFactory.CreateName(lhs.Id), right)); else { result.AddRange(visitable.Prev?.Accept(This) ?? []); @@ -320,7 +320,9 @@ public AddressedInstructions Visit(CallExpression visitable) if (methodCall) { - var caller = result.Count > 0 ? result.OfType().Last().Left! : new Name(visitable.Id); + var caller = result.Count > 0 + ? result.OfType().Last().Left! + : _valueFactory.CreateName(visitable.Id); result.Add(new PushParameter(caller)); } @@ -328,7 +330,7 @@ public AddressedInstructions Visit(CallExpression visitable) { var expr = visitable.Parameters[i]; if (expr is PrimaryExpression primary) - result.Add(new PushParameter(_valueDtoConverter.Convert(primary.ToValueDto()))); + result.Add(new PushParameter(_valueFactory.Create(primary.ToValueDto()))); else { result.AddRange(expr.Accept(This)); diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs index 682f649f..53729b86 100644 --- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs +++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs @@ -3,7 +3,6 @@ using HydraScript.Domain.BackEnd.Impl.Instructions; using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment; using HydraScript.Domain.BackEnd.Impl.Instructions.WithJump; -using HydraScript.Domain.BackEnd.Impl.Values; using HydraScript.Domain.Constants; using HydraScript.Domain.FrontEnd.Parser; using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes; @@ -26,15 +25,15 @@ internal class InstructionProvider : VisitorBase, IVisitor { - private readonly IValueDtoConverter _valueDtoConverter; + private readonly IValueFactory _valueFactory; private readonly IVisitor _expressionVisitor; public InstructionProvider( - IValueDtoConverter valueDtoConverter, + IValueFactory valueFactory, [FromKeyedServices(CodeGeneratorType.Expression)] IVisitor expressionVisitor) { - _valueDtoConverter = valueDtoConverter; + _valueFactory = valueFactory; _expressionVisitor = expressionVisitor; } @@ -103,7 +102,7 @@ public AddressedInstructions Visit(ReturnStatement visitable) case null: return [new Return()]; case PrimaryExpression primary: - return [new Return(_valueDtoConverter.Convert(primary.ToValueDto()))]; + return [new Return(_valueFactory.Create(primary.ToValueDto()))]; } var result = visitable.Expression.Accept(_expressionVisitor); @@ -132,7 +131,7 @@ public AddressedInstructions Visit(FunctionDeclaration visitable) for (var i = 0; i < visitable.Arguments.Count; i++) { var arg = visitable.Arguments[i]; - result.Add(new PopParameter(new Name(arg.Name), arg.Info.Value)); + result.Add(new PopParameter(_valueFactory.CreateName(arg.Name), arg.Info.Value)); } result.AddRange(visitable.Statements.Accept(This)); @@ -156,7 +155,7 @@ public AddressedInstructions Visit(WhileStatement visitable) }; if (visitable.Condition is PrimaryExpression primary) - result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), endBlockLabel)); + result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), endBlockLabel)); else { result.AddRange(visitable.Condition.Accept(_expressionVisitor)); @@ -190,7 +189,7 @@ public AddressedInstructions Visit(WhileStatement visitable) public AddressedInstructions Visit(IfStatement visitable) { - if (visitable.Empty()) + if (visitable.Empty) return []; var blockId = $"if_else_{visitable.GetHashCode()}"; @@ -200,7 +199,7 @@ public AddressedInstructions Visit(IfStatement visitable) var result = new AddressedInstructions(); if (visitable.Test is PrimaryExpression primary) - result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), startBlockLabel)); + result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), startBlockLabel)); else { result.AddRange(visitable.Test.Accept(_expressionVisitor)); @@ -235,7 +234,7 @@ public AddressedInstructions Visit(PrintStatement visitable) AddressedInstructions result = []; if (visitable.Expression is PrimaryExpression prim) - result.Add(new AsString(_valueDtoConverter.Convert(prim.ToValueDto()))); + result.Add(new AsString(_valueFactory.Create(prim.ToValueDto()))); else { result.AddRange(visitable.Expression.Accept(_expressionVisitor)); diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs index d42b560a..2ede746e 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs @@ -26,7 +26,7 @@ internal class DefaultValueForTypeCalculator : IDefaultValueForTypeCalculator return null; if (type is ArrayType) return new List(); - + return new object(); } } \ No newline at end of file diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs index c52c97fd..0cd2cf62 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs @@ -24,6 +24,7 @@ internal class SemanticChecker : VisitorBase, IVisitor, IVisitor, IVisitor, + IVisitor, IVisitor, IVisitor, IVisitor, @@ -154,13 +155,20 @@ public Type Visit(IdentifierReference visitable) return symbol?.Type ?? throw new UnknownIdentifierReference(visitable); } + public Type Visit(EnvVarReference visitable) => "string"; + public Type Visit(Literal visitable) => visitable.Type.Accept(_typeBuilder); public Type Visit(ImplicitLiteral visitable) { var type = visitable.Type.Accept(_typeBuilder); - visitable.ComputedDefaultValue = _calculator.GetDefaultValueForType(type); + if (!visitable.IsDefined) + { + var definedValue = _calculator.GetDefaultValueForType(type); + visitable.SetValue(definedValue); + } + return type; } @@ -336,9 +344,11 @@ public Type Visit(AssignmentExpression visitable) return destinationType; } - var symbol = - _symbolTables[visitable.Scope].FindSymbol(new VariableSymbolId(visitable.Destination.Id)) ?? - throw new UnknownIdentifierReference(visitable.Destination.Id); + // здесь может быть переменная программы, а может быть переменная среды + var symbol = visitable.Destination.Id.ToValueDto().Type is ValueDtoType.Name + ? _symbolTables[visitable.Scope].FindSymbol(new VariableSymbolId(visitable.Destination.Id)) ?? + throw new UnknownIdentifierReference(visitable.Destination.Id) + : new VariableSymbol(visitable.Destination.Id, "string"); if (symbol.ReadOnly) throw new AssignmentToConst(visitable.Destination.Id); diff --git a/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj b/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj index 77e6f58d..5e816d07 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj +++ b/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs index 8c58b105..91fe61a8 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs @@ -3,7 +3,10 @@ namespace HydraScript.Domain.BackEnd; public interface IExecuteParams { public Stack CallStack { get; } - public Stack Frames { get; } + public Queue Arguments { get; } + public IOutputWriter Writer { get; } + + public IFrameContext FrameContext { get; } } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs b/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs new file mode 100644 index 00000000..2486bbc1 --- /dev/null +++ b/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs @@ -0,0 +1,6 @@ +namespace HydraScript.Domain.BackEnd; + +public interface IFrame +{ + object? this[string id] { get; set; } +} \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs b/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs new file mode 100644 index 00000000..b8d42c73 --- /dev/null +++ b/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs @@ -0,0 +1,10 @@ +namespace HydraScript.Domain.BackEnd; + +public interface IFrameContext +{ + IFrame Current { get; } + + void StepIn(); + + void StepOut(); +} \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IValue.cs b/src/Domain/HydraScript.Domain.BackEnd/IValue.cs index 847ebd44..01187554 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/IValue.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/IValue.cs @@ -2,5 +2,5 @@ namespace HydraScript.Domain.BackEnd; public interface IValue : IEquatable { - object? Get(Frame? frame); + object? Get(); } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs b/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs index e8263e32..194d14c1 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs @@ -3,5 +3,6 @@ namespace HydraScript.Domain.BackEnd; public interface IVirtualMachine { public IExecuteParams ExecuteParams { get; } + public void Run(AddressedInstructions instructions); } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs index 19b4eeeb..ef0ef3e5 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs @@ -1,9 +1,14 @@ namespace HydraScript.Domain.BackEnd.Impl; -public class ExecuteParams(IOutputWriter textWriter) : IExecuteParams +public class ExecuteParams( + IOutputWriter textWriter, + IFrameContext frameContext) : IExecuteParams { - public Stack CallStack { get; } = new(); - public Stack Frames { get; } = new(); - public Queue Arguments { get; } = new(); + public Stack CallStack { get; } = []; + + public Queue Arguments { get; } = []; + public IOutputWriter Writer { get; } = textWriter; + + public IFrameContext FrameContext { get; } = frameContext; } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs new file mode 100644 index 00000000..0f464c74 --- /dev/null +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs @@ -0,0 +1,17 @@ +using HydraScript.Domain.BackEnd.Impl.Frames; + +namespace HydraScript.Domain.BackEnd.Impl; + +public sealed class FrameContext : IFrameContext +{ + private readonly Stack