Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ExtendedJavaScriptSubset.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,5 @@
<Project Path="tests/HydraScript.UnitTests/HydraScript.UnitTests.csproj" />
<File Path="tests/coverage-exclude.xml" />
<File Path="tests/Directory.Build.props" />
<File Path="tests/Directory.Packages.props" />
</Folder>
</Solution>
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,39 +10,45 @@ internal class TypeDeclarationsResolver(
ISymbolTableStorage symbolTables,
IVisitor<TypeValue, Type> typeBuilder) : ITypeDeclarationsResolver
{
private readonly Queue<TypeDeclaration> _declarationsToResolve = [];
private readonly List<TypeDeclaration> _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<TypeSymbol>()
.Except(defaults);

foreach (var referenceSymbol in resolvingCandidates)
{
typeSymbol.Type.ResolveReference(
referenceSymbol.Type,
referenceSymbol.Name);
typeSymbol.Resolve(referenceSymbol);
}
}

_declarationsToResolve.Clear();
}

public IHydraScriptTypesService TypesService { get; } = typesService;
Expand Down
5 changes: 5 additions & 0 deletions src/Domain/HydraScript.Domain.IR/Impl/Symbols/TypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Тесты <see cref="TypeDeclarationsResolver"/>
/// </summary>
public class TypeDeclarationsResolverTests
{
/// <summary>
/// https://github.com/Stepami/hydrascript/issues/231
/// </summary>
[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));
}
}
Loading