diff --git a/libs/UIX.Asm/AsmMarkupLoader.cs b/libs/UIX.Asm/AsmMarkupLoader.cs index 3f77f53..71f5b28 100644 --- a/libs/UIX.Asm/AsmMarkupLoader.cs +++ b/libs/UIX.Asm/AsmMarkupLoader.cs @@ -161,7 +161,7 @@ internal class AsmMarkupLoader foreach (var typeImport in Program.Imports.OfType()) { var typeSchema = ResolveTypeFromQualifiedName(typeImport.QualifiedName); - _importTables.ImportedTypes.Add(typeSchema); + TrackImportedType(typeSchema); } foreach (var nsImport in Program.Imports.OfType()) @@ -215,7 +215,7 @@ internal class AsmMarkupLoader foreach (var constant in Program.Body.OfType()) { - object constantValue; + object constantValue, persistData = null; var constantTypeSchema = ResolveTypeFromQualifiedName(constant.TypeName); if (constant is EncodedConstantDirective encodedConstant) @@ -226,6 +226,8 @@ internal class AsmMarkupLoader ReportError($"Failed to create an instance of '{constant.TypeName}' from '{encodedConstant.Content}'", constant); continue; } + + persistData = encodedConstant.Content; } else { @@ -249,11 +251,18 @@ internal class AsmMarkupLoader } } - var mode = constantTypeSchema.SupportsBinaryEncoding - ? MarkupConstantPersistMode.Binary - : MarkupConstantPersistMode.FromString; + MarkupConstantPersistMode mode; + if (constantTypeSchema.SupportsBinaryEncoding) + { + mode = MarkupConstantPersistMode.Binary; + persistData = constantValue; + } + else + { + mode = MarkupConstantPersistMode.FromString; + } - var constantIndex = (ushort)constantsTable.Add(constantTypeSchema, constantValue, mode); + var constantIndex = (ushort)constantsTable.Add(constantTypeSchema, constantValue, mode, persistData); _constants.Add(constant.Name, constantIndex); } @@ -308,8 +317,30 @@ internal class AsmMarkupLoader private LoadResult[] PrepareDependenciesTable() { - // TODO - return []; + LoadResult[] loadResultArray = LoadResult.EmptyList; + int length = 0; + if (_importTables != null) + { + foreach (LoadResult importedLoadResult in _importTables.ImportedLoadResults) + { + if (importedLoadResult != _loadResult) + ++length; + } + } + if (length != 0) + { + loadResultArray = new LoadResult[length]; + int index = 0; + foreach (LoadResult importedLoadResult in _importTables.ImportedLoadResults) + { + if (importedLoadResult != _loadResult) + { + loadResultArray[index] = importedLoadResult; + ++index; + } + } + } + return loadResultArray; } private MarkupDataMapping[] PrepareDataMappingTable() diff --git a/libs/UIX.Asm/Disassembler.cs b/libs/UIX.Asm/Disassembler.cs index 56602ab..4e92f5e 100644 --- a/libs/UIX.Asm/Disassembler.cs +++ b/libs/UIX.Asm/Disassembler.cs @@ -131,6 +131,40 @@ public class Disassembler yield return new TypeImport(new(namespacePrefix, typeImport.Name)); }; + + foreach (var constructorImport in _loadResult.ImportTables.ConstructorImports) + { + var typeName = GetQualifiedName(constructorImport.Owner); + var parameterTypeNames = constructorImport.ParameterTypes.Select(GetQualifiedName); + + yield return new ConstructorImport(typeName, parameterTypeNames); + } + + foreach (var methodImport in _loadResult.ImportTables.MethodImports) + { + var typeName = GetQualifiedName(methodImport.Owner); + var parameterTypeNames = methodImport.ParameterTypes.Select(GetQualifiedName); + + yield return new MethodImport(typeName, methodImport.Name, parameterTypeNames); + } + + Dictionary> namedMemberImports = new(); + void GroupMemberImport(TypeSchema owner, string memberName) + { + var typeName = GetQualifiedName(owner); + if (!namedMemberImports.TryGetValue(typeName, out var currentTypeImports)) + currentTypeImports = namedMemberImports[typeName] = new(); + currentTypeImports.Add(memberName); + } + + foreach (var propertyImport in _loadResult.ImportTables.PropertyImports) + GroupMemberImport(propertyImport.Owner, propertyImport.Name); + + foreach (var eventImport in _loadResult.ImportTables.EventImports) + GroupMemberImport(eventImport.Owner, eventImport.Name); + + foreach (var groupedImport in namedMemberImports) + yield return new NamedMemberImport(groupedImport.Key, groupedImport.Value); } public IEnumerable GetConstants() diff --git a/libs/UIX.Asm/Lexer.Imports.cs b/libs/UIX.Asm/Lexer.Imports.cs index 5bb968d..6eacd80 100644 --- a/libs/UIX.Asm/Lexer.Imports.cs +++ b/libs/UIX.Asm/Lexer.Imports.cs @@ -58,6 +58,73 @@ partial class Lexer import = new TypeImport(typeNameResult.Value); break; + case "MBRS": + input = ConsumeWhitespace(input); + + var memberTypeNameResult = ParseQualifiedTypeName(input); + input = memberTypeNameResult.Remainder; + if (!memberTypeNameResult.WasSuccessful) + return Result.Failure(input, "Invalid member import", ["Expected qualified type name"]); + + input = Parse.Char('{')(input).Remainder; + + var memberNamesResult = Parse.Ref(() => Identifier).DelimitedBy(Parse.Char(',').Token())(input); + input = memberNamesResult.Remainder; + if (!memberNamesResult.WasSuccessful) + return Result.Failure(input, "Invalid member import", ["Expected list of members to import"]); + + input = Parse.Char('}')(input).Remainder; + + import = new NamedMemberImport(memberTypeNameResult.Value, memberNamesResult.Value); + break; + + case "CTOR": + input = ConsumeWhitespace(input); + + var ctorMemberTypeNameResult = ParseQualifiedTypeName(input); + input = ctorMemberTypeNameResult.Remainder; + if (!ctorMemberTypeNameResult.WasSuccessful) + return Result.Failure(input, "Invalid constructor import", ["Expected qualified type name"]); + + input = Parse.Char('(')(input).Remainder; + + var ctorParameterTypesResult = Parse.Ref(() => QualifiedTypeName).DelimitedBy(Parse.Char(',').Token())(input); + input = ctorParameterTypesResult.Remainder; + if (!ctorParameterTypesResult.WasSuccessful) + return Result.Failure(input, "Invalid constructor import", ["Expected constructor parameter types"]); + + input = Parse.Char(')')(input).Remainder; + + import = new ConstructorImport(ctorMemberTypeNameResult.Value, ctorParameterTypesResult.Value); + break; + + case "MTHD": + input = ConsumeWhitespace(input); + + var mthdMemberTypeNameResult = ParseQualifiedTypeName(input); + input = mthdMemberTypeNameResult.Remainder; + if (!mthdMemberTypeNameResult.WasSuccessful) + return Result.Failure(input, "Invalid method import", ["Expected qualified type name"]); + + input = Parse.Char('.')(input).Remainder; + + var mthdNameResult = Identifier(input); + input = mthdNameResult.Remainder; + if (!mthdNameResult.WasSuccessful) + return Result.Failure(input, "Invalid method import", ["Expected method name"]); + + input = Parse.Char('(')(input).Remainder; + + var mthdParameterTypesResult = Parse.Ref(() => QualifiedTypeName).DelimitedBy(Parse.Char(',').Token())(input); + input = mthdParameterTypesResult.Remainder; + if (!mthdParameterTypesResult.WasSuccessful) + return Result.Failure(input, "Invalid constructor import", ["Expected constructor parameter types"]); + + input = Parse.Char(')')(input).Remainder; + + import = new MethodImport(mthdMemberTypeNameResult.Value, mthdNameResult.Value, mthdParameterTypesResult.Value); + break; + default: return Result.Failure(input, $"Unknown import type '{importTypeResult.Value}'", ["Expected 'ns', 'type'"]); } diff --git a/libs/UIX.Asm/Models/Imports.cs b/libs/UIX.Asm/Models/Imports.cs index ebb8526..8dfde84 100644 --- a/libs/UIX.Asm/Models/Imports.cs +++ b/libs/UIX.Asm/Models/Imports.cs @@ -1,4 +1,7 @@ -namespace Microsoft.Iris.Asm.Models; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Iris.Asm.Models; public record NamespaceImport : ImportDirective { @@ -25,3 +28,54 @@ public record TypeImport : ImportDirective public override string ToString() => $"{base.ToString()} {QualifiedName}"; } + +/// +/// Imports many members that can be specified by solely their name. +/// +public record NamedMemberImport : ImportDirective +{ + public NamedMemberImport(QualifiedTypeName qualifiedName, IEnumerable memberNames) : base("mbrs") + { + QualifiedName = qualifiedName; + MemberNames = memberNames.ToList(); + } + + public QualifiedTypeName QualifiedName { get; init; } + + public List MemberNames { get; init; } + + public override string ToString() => $"{base.ToString()} {QualifiedName}{{{string.Join(", ", MemberNames)}}}"; +} + +public record ConstructorImport : ImportDirective +{ + public ConstructorImport(QualifiedTypeName qualifiedName, IEnumerable parameterTypes) : base("ctor") + { + QualifiedName = qualifiedName; + ParameterTypes = parameterTypes.ToList(); + } + + public QualifiedTypeName QualifiedName { get; init; } + + public IEnumerable ParameterTypes { get; init; } + + public override string ToString() => $"{base.ToString()} {QualifiedName}.({string.Join(", ", ParameterTypes)})"; +} + +public record MethodImport : ImportDirective +{ + public MethodImport(QualifiedTypeName qualifiedName, string methodName, IEnumerable parameterTypes) : base("mthd") + { + QualifiedName = qualifiedName; + MethodName = methodName; + ParameterTypes = parameterTypes.ToList(); + } + + public QualifiedTypeName QualifiedName { get; init; } + + public string MethodName { get; init; } + + public IEnumerable ParameterTypes { get; init; } + + public override string ToString() => $"{base.ToString()} {QualifiedName}.{MethodName}({string.Join(", ", ParameterTypes)})"; +} diff --git a/libs/UIX.Test/Assembly.cs b/libs/UIX.Test/Assembly.cs index 4e4f617..9aa5418 100644 --- a/libs/UIX.Test/Assembly.cs +++ b/libs/UIX.Test/Assembly.cs @@ -28,6 +28,8 @@ public class Assembly(ITestOutputHelper output) .import-type Text .import-type Color .import-type Font +.import-mbrs UI{Locals, Content} +.import-mbrs Text{Color, Content, Font} .constant const0 = Color(255, 255, 0, 0) .constant const1 = String(Howdy from Microsoft.Iris!) @@ -35,8 +37,6 @@ public class Assembly(ITestOutputHelper output) .constant const3 = Color(255, 0, 0, 255) .constant const4 = Font(JetBrains Mono) .constant const5 = String(This is some blue text) -.constant xmlTest = - .section object Default_cont: @@ -69,7 +69,7 @@ Alt_locl: output.WriteLine(ast.ToString()); Assert.NotNull(ast); - Assert.Equal(19, ast.Directives.Count()); + Assert.Equal(20, ast.Directives.Count()); Assert.Equal(24, ast.Code.Count()); }