Add inline XML to assembler and disassembler

This commit is contained in:
Yoshi Askharoun
2024-02-12 20:56:51 -06:00
parent f0f712f9d9
commit 03859bec6e
5 changed files with 135 additions and 39 deletions
+14 -3
View File
@@ -209,17 +209,28 @@ internal class AsmMarkupLoader
object constantValue;
var constantTypeSchema = ResolveTypeFromQualifiedName(constant.TypeName);
if (constantTypeSchema.SupportsTypeConversion(stringTypeSchema))
if (constant is EncodedConstantDirective encodedConstant)
{
var parseResult = constantTypeSchema.TypeConverter(constant.Content, stringTypeSchema, out constantValue);
var parseResult = constantTypeSchema.TypeConverter(encodedConstant.Content, stringTypeSchema, out constantValue);
if (parseResult.Failed)
{
ReportError($"Failed to create an instance of '{constant.TypeName}' from '{constant.Content}'", constant);
ReportError($"Failed to create an instance of '{constant.TypeName}' from '{encodedConstant.Content}'", constant);
continue;
}
}
else
{
var xmlElem = System.Xml.Linq.XElement.Parse(constant.Constructor);
constantValue = constantTypeSchema.ConstructDefault();
foreach (var attr in xmlElem.Attributes())
{
var propName = attr.Name.LocalName;
var prop = constantTypeSchema.FindProperty(propName);
prop.SetValue(ref constantValue, attr.Value);
}
ReportError($"'{constant.TypeName}' cannot be constructed from a string, and UIXA does not yet support XML construction.", constant);
continue;
}
+53 -6
View File
@@ -4,6 +4,7 @@ using Microsoft.Iris.Markup;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Microsoft.Iris.Asm;
@@ -148,7 +149,7 @@ public class Disassembler
var persistedConstant = persistedList[c];
var typeSchema = persistedConstant.Type;
constants.Add((c, typeSchema, persistedConstant));
constants.Add((c, typeSchema, persistedConstant.Data));
}
}
else
@@ -173,15 +174,54 @@ public class Disassembler
}
}
var stringTypeSchema = UIXTypes.MapIDToType(UIXTypeID.String);
foreach (var (c, typeSchema, constantValue) in constants)
{
string encodedValue = constantValue is IStringEncodable encodable
? encodable.EncodeString()
: constantValue.ToString();
var constantName = $"const{c:D}";
QualifiedTypeName qualifiedTypeName = GetQualifiedName(typeSchema);
if (constantValue is IStringEncodable encodable)
{
var encodedValue = encodable.EncodeString();
yield return new EncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
}
else if (typeSchema.SupportsTypeConversion(stringTypeSchema))
{
var encodedValue = constantValue.ToString();
yield return new EncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
}
else
{
XName constElemName = qualifiedTypeName.NamespacePrefix is null
? qualifiedTypeName.TypeName
: XName.Get(qualifiedTypeName.TypeName, qualifiedTypeName.NamespacePrefix);
XElement constElem = new(constElemName);
yield return new ConstantDirective($"const{c:D}", qualifiedTypeName, encodedValue);
var defaultConstantValue = typeSchema.ConstructDefault();
foreach (var prop in typeSchema.Properties)
{
// No need to serialize properties that can't be set
if (!prop.CanWrite)
continue;
var defaultPropValue = prop.GetValue(defaultConstantValue);
var propValue = prop.GetValue(constantValue);
var encodedPropValue = EncodeSimpleConstant(propValue);
var encodedDefaultPropValue = EncodeSimpleConstant(defaultPropValue);
// No need to serialize properties that are at their default value
if (encodedPropValue == encodedDefaultPropValue)
continue;
constElem.SetAttributeValue(prop.Name, encodedPropValue);
}
var constructor = constElem.ToString(SaveOptions.DisableFormatting);
yield return new ConstantDirective(constantName, qualifiedTypeName, constructor);
}
}
}
@@ -272,4 +312,11 @@ public class Disassembler
labels = _offsetLabelMap[offset] = new(1);
labels.Add(new(labelName));
}
private static string EncodeSimpleConstant(object value)
{
return value is IStringEncodable encodable
? encodable.EncodeString()
: value.ToString();
}
}
+47 -24
View File
@@ -55,31 +55,54 @@ partial class Lexer
input = Parse.Char('=').Token()(input).Remainder;
var typeNameResult = QualifiedTypeName.Token()(input);
input = typeNameResult.Remainder;
if (!typeNameResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
var openBracketResult = Parse.Char('(').Token()(input);
input = openBracketResult.Remainder;
if (!openBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '('"]);
var contentResult = Parse.CharExcept(')').AtLeastOnce().Text().Token()(input);
input = contentResult.Remainder;
if (!contentResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected constant value"]);
var closeBracketResult = Parse.Char(')').Token()(input);
input = closeBracketResult.Remainder;
if (!closeBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected ')'"]);
directive = new ConstantDirective(constNameResult.Value, typeNameResult.Value, contentResult.Value)
// Types that can't be encoded as simple strings, such as FlowLayout,
// are defined inline using XML elements.
bool isInlineXml = Parse.Char('<')(input).WasSuccessful;
if (isInlineXml)
{
Line = line,
Column = column,
};
// Find the end of the XML tag
var xmlPartResult = Parse.AnyChar.Until(Parse.String("/>")).Text()(input);
input = xmlPartResult.Remainder;
if (!xmlPartResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected valid XML"]);
var xmlElem = System.Xml.Linq.XElement.Parse($"{xmlPartResult.Value}/>");
QualifiedTypeName typeName = new(xmlElem.Name.NamespaceName, xmlElem.Name.LocalName);
var content = xmlElem.ToString(System.Xml.Linq.SaveOptions.DisableFormatting);
directive = new ConstantDirective(constNameResult.Value, typeName, content)
{
Line = line,
};
}
else
{
var typeNameResult = QualifiedTypeName.Token()(input);
input = typeNameResult.Remainder;
if (!typeNameResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
var openBracketResult = Parse.Char('(').Token()(input);
input = openBracketResult.Remainder;
if (!openBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '('"]);
var contentResult = Parse.CharExcept(')').AtLeastOnce().Text().Token()(input);
input = contentResult.Remainder;
if (!contentResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected constant value"]);
var closeBracketResult = Parse.Char(')').Token()(input);
input = closeBracketResult.Remainder;
if (!closeBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected ')'"]);
directive = new EncodedConstantDirective(constNameResult.Value, typeNameResult.Value, contentResult.Value)
{
Line = line,
Column = column,
};
}
break;
case "EXPORT":
+16 -3
View File
@@ -14,18 +14,31 @@ public record SectionDirective : Directive
public record ConstantDirective : Directive
{
public ConstantDirective(string name, QualifiedTypeName typeName, string content) : base("constant")
public ConstantDirective(string name, QualifiedTypeName typeName, string constructor) : base("constant")
{
Name = name;
TypeName = typeName;
Content = content;
Constructor = constructor;
}
public string Name { get; }
public QualifiedTypeName TypeName { get; }
public string Constructor { get; }
public override string ToString() => $"{base.ToString()} {Name} = {Constructor}";
}
public record EncodedConstantDirective : ConstantDirective
{
public EncodedConstantDirective(string name, QualifiedTypeName typeName, string content)
: base(name, typeName, $"{typeName}({content})")
{
Content = content;
}
public string Content { get; }
public override string ToString() => $"{base.ToString()} {Name} = {TypeName}({Content})";
public override string ToString() => base.ToString();
}
public record ExportDirective : Directive
+5 -3
View File
@@ -29,12 +29,14 @@ public class Assembly(ITestOutputHelper output)
.import-type Color
.import-type Font
.constant const0 = Color(A=255, R=255, G=0, B=0)
.constant const0 = Color(255, 255, 0, 0)
.constant const1 = String(Howdy from Microsoft.Iris!)
.constant const2 = String(SelectCommand)
.constant const3 = Color(A=255, R=0, G=0, B=255)
.constant const3 = Color(255, 0, 0, 255)
.constant const4 = Font(JetBrains Mono)
.constant const5 = String(This is some blue text)
.constant xmlTest = <FlowLayout Orientation="Vertical" Spacing="50,0" />
.section object
Default_cont:
@@ -67,7 +69,7 @@ Alt_locl:
output.WriteLine(ast.ToString());
Assert.NotNull(ast);
Assert.Equal(18, ast.Directives.Count());
Assert.Equal(19, ast.Directives.Count());
Assert.Equal(24, ast.Code.Count());
}