diff --git a/ExtendedJavaScriptSubset.slnx b/ExtendedJavaScriptSubset.slnx index 4932071c..94a939ee 100644 --- a/ExtendedJavaScriptSubset.slnx +++ b/ExtendedJavaScriptSubset.slnx @@ -51,6 +51,5 @@ - \ No newline at end of file diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs index 6c2e1bd0..73dc5b08 100644 --- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs +++ b/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs @@ -1,5 +1,6 @@ using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Declarations; using HydraScript.Domain.IR.Impl.Symbols; +using HydraScript.Domain.IR.Impl.Symbols.Ids; using ZLinq; namespace HydraScript.Application.StaticAnalysis.Impl; @@ -9,39 +10,45 @@ internal class TypeDeclarationsResolver( ISymbolTableStorage symbolTables, IVisitor typeBuilder) : ITypeDeclarationsResolver { - private readonly Queue _declarationsToResolve = []; + private readonly List _declarationsToResolve = []; public void Store(TypeDeclaration declaration) => - _declarationsToResolve.Enqueue(declaration); + _declarationsToResolve.Add(declaration); public void Resolve() { - var defaults = TypesService.GetDefaultTypes() - .AsValueEnumerable() - .Select(x => new TypeSymbol(x)) - .ToList(); - - while (_declarationsToResolve.Count != 0) + // build phase + for (var i = 0; i < _declarationsToResolve.Count; i++) { - var declarationToResolve = _declarationsToResolve.Dequeue(); + var declarationToResolve = _declarationsToResolve[i]; var typeSymbol = new TypeSymbol( declarationToResolve.TypeValue.Accept(typeBuilder), declarationToResolve.TypeId); symbolTables[declarationToResolve.Scope].AddSymbol(typeSymbol); + } - var resolvingCandidates = symbolTables[declarationToResolve.Scope] - .GetAvailableSymbols() + var defaults = TypesService.GetDefaultTypes() + .AsValueEnumerable() + .Select(x => new TypeSymbol(x)) + .ToList(); + + // resolve phase + for (var i = 0; i < _declarationsToResolve.Count; i++) + { + var symbolTable = symbolTables[_declarationsToResolve[i].Scope]; + var typeSymbol = symbolTable.FindSymbol(new TypeSymbolId(_declarationsToResolve[i].TypeId))!; + var resolvingCandidates = symbolTable.GetAvailableSymbols() .AsValueEnumerable() .OfType() .Except(defaults); foreach (var referenceSymbol in resolvingCandidates) { - typeSymbol.Type.ResolveReference( - referenceSymbol.Type, - referenceSymbol.Name); + typeSymbol.Resolve(referenceSymbol); } } + + _declarationsToResolve.Clear(); } public IHydraScriptTypesService TypesService { get; } = typesService; diff --git a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/TypeSymbol.cs b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/TypeSymbol.cs index bf6a0818..ef6646f1 100644 --- a/src/Domain/HydraScript.Domain.IR/Impl/Symbols/TypeSymbol.cs +++ b/src/Domain/HydraScript.Domain.IR/Impl/Symbols/TypeSymbol.cs @@ -7,6 +7,11 @@ public class TypeSymbol(Type type, string? name = null) : { public override TypeSymbolId Id { get; } = new(name ?? type.ToString()); + public void Resolve(TypeSymbol referenceSymbol) => + Type.ResolveReference( + referenceSymbol.Type, + referenceSymbol.Name); + public override bool Equals(object? obj) => obj is TypeSymbol typeSymbol && Name == typeSymbol.Name && Type.Equals(typeSymbol.Type); diff --git a/tests/HydraScript.UnitTests/Application/TypeDeclarationsResolverTests.cs b/tests/HydraScript.UnitTests/Application/TypeDeclarationsResolverTests.cs new file mode 100644 index 00000000..2e9b2d9d --- /dev/null +++ b/tests/HydraScript.UnitTests/Application/TypeDeclarationsResolverTests.cs @@ -0,0 +1,85 @@ +using HydraScript.Application.StaticAnalysis.Impl; +using HydraScript.Application.StaticAnalysis.Visitors; +using HydraScript.Domain.FrontEnd.Parser; +using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes; +using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Declarations; +using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions; +using HydraScript.Domain.IR.Impl; +using HydraScript.Domain.IR.Impl.Symbols; +using HydraScript.Domain.IR.Impl.Symbols.Ids; +using HydraScript.Domain.IR.Types; + +namespace HydraScript.UnitTests.Application; + +/// +/// Тесты +/// +public class TypeDeclarationsResolverTests +{ + /// + /// https://github.com/Stepami/hydrascript/issues/231 + /// + [Fact] + public void Resolve_Issue231_Success() + { + // Arrange + const string itemTypeName = "QueryStringParseResultItem"; + const string resultTypeName = "QueryStringParseResult"; + + var scope = new Scope(); + var symbolTable = new SymbolTable(); + symbolTable.AddSymbol(new TypeSymbol(itemTypeName)); + symbolTable.AddSymbol(new TypeSymbol(resultTypeName)); + + var symbolTables = new SymbolTableStorage(); + symbolTables.Init(scope, symbolTable); + + var resolver = new TypeDeclarationsResolver( + new HydraScriptTypesService(), + symbolTables, + new TypeBuilder(symbolTables)); + + foreach (var defaultType in resolver.TypesService.GetDefaultTypes()) + { + symbolTable.AddSymbol(new TypeSymbol(defaultType)); + } + + var resultDeclaration = new TypeDeclaration( + new IdentifierReference(resultTypeName), + new ObjectTypeValue( + [ + new PropertyTypeValue( + "result", + new ArrayTypeValue( + new TypeIdentValue( + new IdentifierReference(itemTypeName)))), + ])); + + var itemDeclaration = new TypeDeclaration( + new IdentifierReference(itemTypeName), + new ObjectTypeValue( + [ + new PropertyTypeValue("name", TypeIdentValue.String), + new PropertyTypeValue("value", TypeIdentValue.String), + ])); + + var root = new ScriptBody([resultDeclaration, itemDeclaration]); + root.InitScope(scope); + itemDeclaration.InitScope(); + resultDeclaration.InitScope(); + + // Act + resolver.Store(resultDeclaration); + resolver.Store(itemDeclaration); + + resolver.Resolve(); + + // Assert + var itemType = symbolTable.FindSymbol(new TypeSymbolId(itemTypeName))?.Type; + var resultType = symbolTable.FindSymbol(new TypeSymbolId(resultTypeName))?.Type as ObjectType; + + itemType.Should().NotBeNull(); + resultType.Should().NotBeNull(); + resultType["result"].Should().Be(new ArrayType(itemType)); + } +} \ No newline at end of file