mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Added initial implementation of UIXA parser
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using Microsoft.Iris.Markup;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UIX.Asm;
|
||||
|
||||
public interface IBodyItem { }
|
||||
|
||||
public record Instruction(string Mnemonic, IEnumerable<Operand> Operands) : IBodyItem
|
||||
{
|
||||
public OpCode OpCode => LexerMaps.MnemonicToOpCode(Mnemonic);
|
||||
public OperationType? OperationType => LexerMaps.TryOperationMnemonicToType(Mnemonic);
|
||||
|
||||
public override string ToString() => $"{Mnemonic} {string.Join(", ", Operands)}";
|
||||
}
|
||||
|
||||
public record Label(string Name) : IBodyItem
|
||||
{
|
||||
public override string ToString() => $"{Name}:";
|
||||
}
|
||||
|
||||
public record Operand(string Content)
|
||||
{
|
||||
public override string ToString() => Content;
|
||||
}
|
||||
|
||||
public record Import(string Uri, string Name)
|
||||
{
|
||||
public override string ToString() => $".import {Uri} as {Name}";
|
||||
}
|
||||
|
||||
public record Program(IEnumerable<Import> Imports, IEnumerable<IBodyItem> Body)
|
||||
{
|
||||
public override string ToString() => string.Join("\r\n", Imports.Select(i => i.ToString()).Concat(Body.Select(b => b.ToString())));
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Sprache;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UIX.Asm;
|
||||
|
||||
public static class Lexer
|
||||
{
|
||||
public static readonly Parser<string> AlphanumericText = Parse.LetterOrDigit.AtLeastOnce().Text();
|
||||
|
||||
public static readonly Parser<string> Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text();
|
||||
|
||||
public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator);
|
||||
|
||||
public static readonly Parser<Import> Import =
|
||||
from _ in Parse.String(".import").Token()
|
||||
from uri in Uri.Token()
|
||||
from __ in Parse.String("as").Token()
|
||||
from name in AlphanumericText
|
||||
from end in StatementEnd
|
||||
select new Import(uri, name);
|
||||
|
||||
public static readonly Parser<string> Mnemonic = Parse.Letter.AtLeastOnce().Text().Token();
|
||||
|
||||
public static readonly Parser<Label> Label =
|
||||
from label in AlphanumericText
|
||||
from _ in Parse.Char(':')
|
||||
select new Label(label);
|
||||
|
||||
public static readonly Parser<Operand> Operand =
|
||||
from op in AlphanumericText
|
||||
select new Operand(op);
|
||||
|
||||
public static readonly Parser<Instruction> Instruction2 =
|
||||
from mnemonic in Mnemonic
|
||||
from operands in Parse.Ref(() => Operand).DelimitedBy(Parse.Char(',').Token())
|
||||
from end in StatementEnd
|
||||
select new Instruction(mnemonic, operands);
|
||||
|
||||
public static readonly Parser<IBodyItem> BodyItem = ParseBodyItem;
|
||||
|
||||
public static readonly Parser<Program> Program =
|
||||
from imports in Import.Token().Many()
|
||||
from body in BodyItem.Many()
|
||||
select new Program(imports, body);
|
||||
|
||||
private static IResult<IBodyItem> ParseBodyItem(IInput input)
|
||||
{
|
||||
var trimWhitespaceResult = Parse.WhiteSpace.Many()(input);
|
||||
input = trimWhitespaceResult.Remainder;
|
||||
|
||||
var identifierResult = Parse.Letter.AtLeastOnce().Text().Invoke(input);
|
||||
if (!identifierResult.WasSuccessful)
|
||||
return Result.Failure<Instruction>(input, "Invalid code", System.Array.Empty<string>());
|
||||
|
||||
input = identifierResult.Remainder;
|
||||
var identifier = identifierResult.Value;
|
||||
IBodyItem bodyItem;
|
||||
|
||||
if (!input.AtEnd && input.Current == ':')
|
||||
{
|
||||
input = input.Advance();
|
||||
bodyItem = new Label(identifier);
|
||||
input = StatementEnd(input).Remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Operand> operands = new();
|
||||
var endOfInstructionResult = StatementEnd(input);
|
||||
input = endOfInstructionResult.Remainder;
|
||||
|
||||
if (!endOfInstructionResult.WasSuccessful)
|
||||
{
|
||||
input = Parse.WhiteSpace.Many()(input).Remainder;
|
||||
|
||||
var operandsResult = Parse.Ref(() => AlphanumericText).DelimitedBy(Parse.Char(',').Token())(input);
|
||||
operands.AddRange(operandsResult.Value.Select(s => new Operand(s)));
|
||||
input = operandsResult.Remainder;
|
||||
}
|
||||
|
||||
bodyItem = new Instruction(identifier, operands);
|
||||
}
|
||||
|
||||
return Result.Success(bodyItem, input);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using Microsoft.Iris.Markup;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UIX.Asm;
|
||||
|
||||
internal static class LexerMaps
|
||||
{
|
||||
public static OperationType OperationMnemonicToType(string mnemonic) => OperationMnemonicMap[mnemonic.ToUpperInvariant()];
|
||||
public static OperationType? TryOperationMnemonicToType(string mnemonic)
|
||||
{
|
||||
return OperationMnemonicMap.TryGetValue(mnemonic.ToUpperInvariant(), out var type)
|
||||
? type : null;
|
||||
}
|
||||
|
||||
public static OpCode MnemonicToOpCode(string mnemonic) => MnemonicMap[mnemonic.ToUpperInvariant()];
|
||||
|
||||
internal static readonly IDictionary<string, OperationType> OperationMnemonicMap = new Dictionary<string, OperationType>
|
||||
{
|
||||
["ADD"] = OperationType.MathAdd,
|
||||
["SUB"] = OperationType.MathSubtract,
|
||||
["MUL"] = OperationType.MathMultiply,
|
||||
["DIV"] = OperationType.MathDivide,
|
||||
["MOD"] = OperationType.MathModulus,
|
||||
["NEG"] = OperationType.MathModulus,
|
||||
["AND"] = OperationType.LogicalAnd,
|
||||
["ORR"] = OperationType.LogicalOr,
|
||||
["NOT"] = OperationType.LogicalNot,
|
||||
["REQ"] = OperationType.RelationalEquals,
|
||||
["RNE"] = OperationType.RelationalNotEquals,
|
||||
["RLT"] = OperationType.RelationalLessThan,
|
||||
["RGT"] = OperationType.RelationalGreaterThan,
|
||||
["RLE"] = OperationType.RelationalLessThanEquals,
|
||||
["RGE"] = OperationType.RelationalGreaterThanEquals,
|
||||
["RIS"] = OperationType.RelationalIs,
|
||||
["INC"] = OperationType.PostIncrement,
|
||||
["DEC"] = OperationType.PostDecrement,
|
||||
};
|
||||
|
||||
internal static readonly IDictionary<string, OpCode> MnemonicMap = new Dictionary<string, OpCode>
|
||||
{
|
||||
["COBJ"] = OpCode.ConstructObject,
|
||||
["COBI"] = OpCode.ConstructObjectIndirect,
|
||||
["COBP"] = OpCode.ConstructObjectParam,
|
||||
["CSTR"] = OpCode.ConstructFromString,
|
||||
["CBIN"] = OpCode.ConstructFromBinary,
|
||||
["INIT"] = OpCode.InitializeInstance,
|
||||
["INID"] = OpCode.InitializeInstanceIndirect,
|
||||
["LSYM"] = OpCode.LookupSymbol,
|
||||
["WSYM"] = OpCode.WriteSymbol,
|
||||
["WSYP"] = OpCode.WriteSymbolPeek,
|
||||
["CSYM"] = OpCode.ClearSymbol,
|
||||
["PINI"] = OpCode.PropertyInitialize,
|
||||
["PINII"] = OpCode.PropertyInitializeIndirect,
|
||||
["PLAD"] = OpCode.PropertyListAdd,
|
||||
["PDAD"] = OpCode.PropertyDictionaryAdd,
|
||||
["PASS"] = OpCode.PropertyAssign,
|
||||
["PASST"] = OpCode.PropertyAssignStatic,
|
||||
["PGET"] = OpCode.PropertyGet,
|
||||
["PGETP"] = OpCode.PropertyGetPeek,
|
||||
["PGETT"] = OpCode.PropertyGetStatic,
|
||||
["MINV"] = OpCode.MethodInvoke,
|
||||
["MINVP"] = OpCode.MethodInvokePeek,
|
||||
["MINVT"] = OpCode.MethodInvokeStatic,
|
||||
["MINVA"] = OpCode.MethodInvokePushLastParam,
|
||||
["MINVAT"] = OpCode.MethodInvokeStaticPushLastParam, // Avoid using "LT" as suffix
|
||||
["VTC"] = OpCode.VerifyTypeCast,
|
||||
["CON"] = OpCode.ConvertType,
|
||||
["OPR"] = OpCode.Operation, // Generic operation, allow dynamic invocations of operators
|
||||
["ADD"] = OpCode.Operation,
|
||||
["SUB"] = OpCode.Operation,
|
||||
["MUL"] = OpCode.Operation,
|
||||
["DIV"] = OpCode.Operation,
|
||||
["MOD"] = OpCode.Operation,
|
||||
["NEG"] = OpCode.Operation,
|
||||
["AND"] = OpCode.Operation,
|
||||
["ORR"] = OpCode.Operation,
|
||||
["NOT"] = OpCode.Operation,
|
||||
["REQ"] = OpCode.Operation,
|
||||
["RNE"] = OpCode.Operation,
|
||||
["RLT"] = OpCode.Operation,
|
||||
["RGT"] = OpCode.Operation,
|
||||
["RLE"] = OpCode.Operation,
|
||||
["RGE"] = OpCode.Operation,
|
||||
["RIS"] = OpCode.Operation,
|
||||
["INC"] = OpCode.Operation,
|
||||
["DEC"] = OpCode.Operation,
|
||||
["ISC"] = OpCode.IsCheck,
|
||||
["ASC"] = OpCode.As,
|
||||
["TYP"] = OpCode.TypeOf,
|
||||
["PSHN"] = OpCode.PushNull,
|
||||
["PSHC"] = OpCode.PushConstant,
|
||||
["PSHT"] = OpCode.PushThis,
|
||||
["DIS"] = OpCode.DiscardValue,
|
||||
["RET"] = OpCode.ReturnValue,
|
||||
["RETV"] = OpCode.ReturnVoid,
|
||||
["JMPF"] = OpCode.JumpIfFalse,
|
||||
["JMPFP"] = OpCode.JumpIfFalsePeek,
|
||||
["JMPTP"] = OpCode.JumpIfTruePeek,
|
||||
["JMPD"] = OpCode.JumpIfDictionaryContains,
|
||||
["JMPNP"] = OpCode.JumpIfNullPeek,
|
||||
["JMP"] = OpCode.Jump,
|
||||
["CLIS"] = OpCode.ConstructListenerStorage,
|
||||
["LIS"] = OpCode.Listen,
|
||||
["DLS"] = OpCode.DestructiveListen,
|
||||
["DBG"] = OpCode.EnterDebugState,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MicrosoftIris\UIX\UIX.csproj" />
|
||||
<PackageReference Include="Sprache" Version="2.3.1" />
|
||||
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,32 @@
|
||||
using Sprache;
|
||||
using UIX.Asm;
|
||||
|
||||
namespace UIX.Test;
|
||||
|
||||
public class Assembly
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
const string code =
|
||||
"""
|
||||
.import Me as me
|
||||
.import assembly://UIX/Microsoft.Iris as iris
|
||||
|
||||
main:
|
||||
COBJ 2
|
||||
PSHC 0
|
||||
PINI 1
|
||||
PSHC 1
|
||||
PINI 2
|
||||
PINI 0
|
||||
RETV
|
||||
RETV
|
||||
""";
|
||||
|
||||
var ast = Lexer.Program.Parse(code);
|
||||
Assert.NotNull(ast);
|
||||
Assert.Equal(2, ast.Imports.Count());
|
||||
Assert.Equal(9, ast.Body.Count());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UIX.Asm\UIX.Asm.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user