mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Add inline XML to assembler and disassembler
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user