Add constants and qualified type names to lexer, and use instruction schema to decode UIB instrctions

This commit is contained in:
Yoshi Askharoun
2024-02-11 20:38:19 -06:00
parent 03f8974ac1
commit 4056dc9f4f
9 changed files with 216 additions and 106 deletions
+31 -31
View File
@@ -119,31 +119,31 @@ internal static class InstructionSet
[OpCode.ReturnValue] = [],
[OpCode.ReturnVoid] = [],
[OpCode.ConstructObject] = Inst_UInt16,
[OpCode.ConstructObject] = [LiteralDataType.TypeIndex],
[OpCode.ConstructObjectIndirect] = Inst_UInt16,
[OpCode.InitializeInstance] = Inst_UInt16,
[OpCode.LookupSymbol] = Inst_UInt16,
[OpCode.WriteSymbol] = Inst_UInt16,
[OpCode.WriteSymbolPeek] = Inst_UInt16,
[OpCode.ClearSymbol] = Inst_UInt16,
[OpCode.PropertyInitialize] = Inst_UInt16,
[OpCode.PropertyInitializeIndirect] = Inst_UInt16,
[OpCode.PropertyListAdd] = Inst_UInt16,
[OpCode.PropertyAssign] = Inst_UInt16,
[OpCode.PropertyAssignStatic] = Inst_UInt16,
[OpCode.PropertyGet] = Inst_UInt16,
[OpCode.PropertyGetPeek] = Inst_UInt16,
[OpCode.PropertyGetStatic] = Inst_UInt16,
[OpCode.MethodInvoke] = Inst_UInt16,
[OpCode.MethodInvokePeek] = Inst_UInt16,
[OpCode.MethodInvokeStatic] = Inst_UInt16,
[OpCode.MethodInvokePushLastParam] = Inst_UInt16,
[OpCode.MethodInvokeStaticPushLastParam] = Inst_UInt16,
[OpCode.VerifyTypeCast] = Inst_UInt16,
[OpCode.IsCheck] = Inst_UInt16,
[OpCode.As] = Inst_UInt16,
[OpCode.TypeOf] = Inst_UInt16,
[OpCode.PushConstant] = Inst_UInt16,
[OpCode.InitializeInstance] = [LiteralDataType.TypeIndex],
[OpCode.LookupSymbol] = [LiteralDataType.SymbolRefIndex],
[OpCode.WriteSymbol] = [LiteralDataType.SymbolRefIndex],
[OpCode.WriteSymbolPeek] = [LiteralDataType.SymbolRefIndex],
[OpCode.ClearSymbol] = [LiteralDataType.SymbolRefIndex],
[OpCode.PropertyInitialize] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyInitializeIndirect] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyListAdd] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyAssign] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyAssignStatic] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyGet] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyGetPeek] = [LiteralDataType.PropertyIndex],
[OpCode.PropertyGetStatic] = [LiteralDataType.PropertyIndex],
[OpCode.MethodInvoke] = [LiteralDataType.MethodIndex],
[OpCode.MethodInvokePeek] = [LiteralDataType.MethodIndex],
[OpCode.MethodInvokeStatic] = [LiteralDataType.MethodIndex],
[OpCode.MethodInvokePushLastParam] = [LiteralDataType.MethodIndex],
[OpCode.MethodInvokeStaticPushLastParam] = [LiteralDataType.MethodIndex],
[OpCode.VerifyTypeCast] = [LiteralDataType.TypeIndex],
[OpCode.IsCheck] = [LiteralDataType.TypeIndex],
[OpCode.As] = [LiteralDataType.TypeIndex],
[OpCode.TypeOf] = [LiteralDataType.TypeIndex],
[OpCode.PushConstant] = [LiteralDataType.ConstantIndex],
[OpCode.ConstructListenerStorage] = Inst_UInt16,
[OpCode.JumpIfFalse] = Inst_UInt32,
@@ -154,16 +154,16 @@ internal static class InstructionSet
[OpCode.EnterDebugState] = [LiteralDataType.Int32],
[OpCode.ConstructObjectParam] = Inst_UInt16x2,
[OpCode.ConstructFromString] = Inst_UInt16x2,
[OpCode.PropertyDictionaryAdd] = Inst_UInt16x2,
[OpCode.ConvertType] = Inst_UInt16x2,
[OpCode.ConstructObjectParam] = [LiteralDataType.TypeIndex, LiteralDataType.UInt16],
[OpCode.ConstructFromString] = [LiteralDataType.TypeIndex, LiteralDataType.ConstantIndex],
[OpCode.PropertyDictionaryAdd] = [LiteralDataType.TypeIndex, LiteralDataType.ConstantIndex],
[OpCode.ConvertType] = [LiteralDataType.TypeIndex, LiteralDataType.TypeIndex],
[OpCode.JumpIfDictionaryContains] = [LiteralDataType.UInt16, LiteralDataType.UInt16, LiteralDataType.UInt32],
[OpCode.JumpIfDictionaryContains] = [LiteralDataType.PropertyIndex, LiteralDataType.UInt16, LiteralDataType.UInt32],
[OpCode.ConstructFromBinary] = [LiteralDataType.UInt16, LiteralDataType.Bytes],
[OpCode.ConstructFromBinary] = [LiteralDataType.TypeIndex, LiteralDataType.Bytes],
[OpCode.Operation] = [LiteralDataType.UInt16, LiteralDataType.Byte],
[OpCode.Operation] = [LiteralDataType.OpHostIndex, LiteralDataType.Byte],
[OpCode.Listen] = [LiteralDataType.UInt16, LiteralDataType.Byte, LiteralDataType.UInt16, LiteralDataType.UInt32],
[OpCode.DestructiveListen] = [LiteralDataType.UInt16, LiteralDataType.Byte, LiteralDataType.UInt16, LiteralDataType.UInt32, LiteralDataType.UInt32],
+39 -11
View File
@@ -44,7 +44,7 @@ partial class Lexer
}
else
{
List<OperandLiteral> operands = new();
Operand[] operands = null;
var endOfInstructionResult = StatementEnd(input);
input = endOfInstructionResult.Remainder;
@@ -52,18 +52,41 @@ partial class Lexer
{
input = ConsumeWhitespace(input);
var operandsResult = Parse.Ref(() => AlphanumericText).DelimitedBy(Parse.Char(',').Token())(input);
input = operandsResult.Remainder;
var opCode = InstructionSet.MnemonicToOpCode(identifier);
var schema = InstructionSet.InstructionSchema[opCode];
operands = new Operand[schema.Length];
int schemaIndex = 0;
foreach (var operandContent in operandsResult.Value)
for (int operandIndex = 0; operandIndex < schema.Length; operandIndex++)
{
var operandType = schema[schemaIndex++];
Operand operand;
var operandType = schema[operandIndex];
object operandValue = operandType switch
var operandConstPrefixResult = Parse.Char('@')(input);
input = operandConstPrefixResult.Remainder;
if (operandConstPrefixResult.WasSuccessful)
{
if (!OperandLiteral.IsIndex(operandType))
return Result.Failure<IBodyItem>(input, "Invalid instruction", [$"Constant references are not allowed for this operand, expected a {operandType}"]);
var operandConstResult = Identifier(input);
input = operandConstResult.Remainder;
if (!operandConstResult.WasSuccessful)
return Result.Failure<IBodyItem>(input, "Invalid instruction", ["Invalid constant name"]);
operand = new OperandReference(operandConstResult.Value)
{
Line = line,
};
}
else
{
var operandContentResult = AlphanumericText(input);
input = operandContentResult.Remainder;
if (!operandContentResult.WasSuccessful)
return Result.Failure<IBodyItem>(input, "Invalid instruction", ["Invalid operand"]);
var operandContent = operandContentResult.Value;
object operandValue = OperandLiteral.ReduceDataType(operandType) switch
{
LiteralDataType.Byte => byte.Parse(operandContent),
LiteralDataType.UInt16 => ushort.Parse(operandContent),
@@ -72,14 +95,19 @@ partial class Lexer
LiteralDataType.Bytes or _ => operandContent,
};
operands.Add(new(operandValue, operandType, operandContent)
operand = new OperandLiteral(operandValue, operandType, operandContent)
{
Line = line,
});
};
}
operands[operandIndex] = operand;
input = Parse.Char(',').Token()(input).Remainder;
}
}
bodyItem = new Instruction(identifier, operands)
bodyItem = new Instruction(identifier, operands ?? [])
{
Line = line,
Column = column,
+46 -8
View File
@@ -30,12 +30,12 @@ partial class Lexer
case "SECTION":
if (StatementEnd(input).WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid section directive", ["Expected a section name"]);
return Result.Failure<IDirective>(input, "Invalid section directive", ["Expected a section name"]);
var sectionNameResult = Parse.Letter.AtLeastOnce().Token().Text()(input);
input = sectionNameResult.Remainder;
if (!sectionNameResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid section name", ["Expected a section name containing only letters"]);
return Result.Failure<IDirective>(input, "Invalid section name", ["Expected a section name containing only letters"]);
directive = new SectionDirective(sectionNameResult.Value)
{
@@ -44,27 +44,65 @@ partial class Lexer
};
break;
case "CONSTANT":
if (StatementEnd(input).WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected a consant declaration"]);
var constNameResult = Identifier.Token()(input);
input = constNameResult.Remainder;
if (!constNameResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected a name for the constant"]);
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)
{
Line = line,
Column = column,
};
break;
case "EXPORT":
if (StatementEnd(input).WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid export directive", ["Expected export information"]);
return Result.Failure<IDirective>(input, "Invalid export directive", ["Expected export information"]);
var labelPrefixResult = Identifier.Token()(input);
input = labelPrefixResult.Remainder;
if (!labelPrefixResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid export directive", ["Expected prefix of labels to export"]);
return Result.Failure<IDirective>(input, "Invalid export directive", ["Expected prefix of labels to export"]);
var listenerCountResult = WholeNumber.Token()(input);
input = listenerCountResult.Remainder;
if (!listenerCountResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid export directive", ["Expected listener count"]);
return Result.Failure<IDirective>(input, "Invalid export directive", ["Expected listener count"]);
if (!uint.TryParse(listenerCountResult.Value, out var listenerCount))
return Result.Failure<IImportDirective>(input, "Invalid export directive", ["Expected export listener count to be an unsigned integer"]);
return Result.Failure<IDirective>(input, "Invalid export directive", ["Expected export listener count to be an unsigned integer"]);
var baseTypeNameResult = AlphanumericText.Token()(input);
input = baseTypeNameResult.Remainder;
if (!baseTypeNameResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid export directive", ["Expected base type name"]);
return Result.Failure<IDirective>(input, "Invalid export directive", ["Expected base type name"]);
var labelPrefix = labelPrefixResult.Value;
var baseTypeName = baseTypeNameResult.Value;
@@ -76,7 +114,7 @@ partial class Lexer
break;
default:
return Result.Failure<IImportDirective>(input, $"Unknown import type '{directiveIdResult.Value}'", ["Expected 'export', 'import', or 'section'"]);
return Result.Failure<IDirective>(input, $"Unknown import type '{directiveIdResult.Value}'", ["Expected 'export', 'import', or 'section'"]);
}
return Result.Success(directive, input);
+5 -23
View File
@@ -48,36 +48,18 @@ partial class Lexer
break;
case "TYPE":
var typePrefixResult = Identifier.Token()(input);
input = typePrefixResult.Remainder;
if (!typePrefixResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid type import", ["Expected a valid namespace prefix"]);
input = ConsumeWhitespace(input);
var typeNamespaceDelimitterResult = Parse.Char(':')(input);
input = typeNamespaceDelimitterResult.Remainder;
string typeName, typePrefix;
if (typeNamespaceDelimitterResult.WasSuccessful)
{
var typeNameResult = Identifier(input);
var typeNameResult = ParseQualifiedTypeName(input);
input = typeNameResult.Remainder;
if (!typeNameResult.WasSuccessful)
return Result.Failure<IImportDirective>(input, "Invalid type import", ["Expected a valid type name"]);
return Result.Failure<IImportDirective>(input, "Invalid type import", ["Expected qualified type name"]);
typePrefix = typePrefixResult.Value;
typeName = typeNameResult.Value;
}
else
{
typePrefix = null;
typeName = typePrefixResult.Value;
}
import = new TypeImport(typePrefix, typeName);
import = new TypeImport(typeNameResult.Value);
break;
default:
return Result.Failure<IImportDirective>(input, $"Unknown import type '{importTypeResult.Value}'", ["Expected 'ns'"]);
return Result.Failure<IImportDirective>(input, $"Unknown import type '{importTypeResult.Value}'", ["Expected 'ns', 'type'"]);
}
return Result.Success(import, input);
+33
View File
@@ -18,6 +18,8 @@ public static partial class Lexer
public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator);
public static readonly Parser<QualifiedTypeName> QualifiedTypeName = ParseQualifiedTypeName;
public static readonly Parser<IImportDirective> Import = ParseImport;
public static readonly Parser<IDirective> Directive = ParseDirective;
@@ -29,4 +31,35 @@ public static partial class Lexer
select new Program(body);
private static IInput ConsumeWhitespace(IInput input) => Parse.WhiteSpace.Many()(input).Remainder;
private static IResult<QualifiedTypeName> ParseQualifiedTypeName(IInput input)
{
var typePrefixResult = Identifier(input);
input = typePrefixResult.Remainder;
if (!typePrefixResult.WasSuccessful)
return Result.Failure<QualifiedTypeName>(input, "Invalid type name", ["Expected a valid namespace prefix"]);
var typeNamespaceDelimitterResult = Parse.Char(':')(input);
input = typeNamespaceDelimitterResult.Remainder;
string typeName, typePrefix;
if (typeNamespaceDelimitterResult.WasSuccessful)
{
var typeNameResult = Identifier(input);
input = typeNameResult.Remainder;
if (!typeNameResult.WasSuccessful)
return Result.Failure<QualifiedTypeName>(input, "Invalid type name", ["Expected a valid type name"]);
typePrefix = typePrefixResult.Value;
typeName = typeNameResult.Value;
}
else
{
typePrefix = null;
typeName = typePrefixResult.Value;
}
QualifiedTypeName qualifiedName = new(typePrefix, typeName);
return Result.Success(qualifiedName, input);
}
}
+6 -4
View File
@@ -14,16 +14,18 @@ public record SectionDirective : Directive
public record ConstantDirective : Directive
{
public ConstantDirective(string name, string typeName, string content) : base("constant")
public ConstantDirective(string name, QualifiedTypeName typeName, string content) : base("constant")
{
Name = name;
TypeName = typeName;
ValueString = content;
Content = content;
}
public string Name { get; }
public string TypeName { get; }
public string ValueString { get; }
public QualifiedTypeName TypeName { get; }
public string Content { get; }
public override string ToString() => $"{base.ToString()} {Name} = {TypeName}({Content})";
}
public record ExportDirective : Directive
+4 -12
View File
@@ -16,20 +16,12 @@ public record NamespaceImport : ImportDirective
public record TypeImport : ImportDirective
{
public TypeImport(string namespacePrefix, string name) : base("type")
public TypeImport(QualifiedTypeName qualifiedName) : base("type")
{
NamespacePrefix = namespacePrefix;
Name = name;
QualifiedName = qualifiedName;
}
public string NamespacePrefix { get; init; }
public string Name { get; init; }
public QualifiedTypeName QualifiedName { get; init; }
public override string ToString()
{
if (NamespacePrefix is null)
return $"{base.ToString()} {Name}";
else
return $"{base.ToString()} {NamespacePrefix}:{Name}";
}
public override string ToString() => $"{base.ToString()} {QualifiedName}";
}
+29 -8
View File
@@ -1,12 +1,26 @@
namespace Microsoft.Iris.Asm.Models;
/// <summary>
/// The data type of a literal expression.
/// </summary>
/// <remarks>
/// If bit 4 is set, the literal is meant to be an index.
/// </remarks>
public enum LiteralDataType : byte
{
Byte,
UInt16,
UInt32,
Int32,
Bytes,
Byte = 0b0000,
UInt16 = 0b0001,
UInt32 = 0b0010,
Int32 = 0b0011,
Bytes = 0b0100,
TypeIndex = 0b1000,
PropertyIndex = 0b1001,
MethodIndex = 0b1010,
ConstantIndex = 0b1011,
SymbolRefIndex = 0b1100,
OpHostIndex = 0b1101,
}
public abstract record Operand(object Value, string Content = null) : AsmItem
@@ -24,15 +38,22 @@ public record OperandLiteral : Operand
public LiteralDataType DataType { get; init; }
public override string ToString() => base.ToString();
public static LiteralDataType ReduceDataType(LiteralDataType dataType)
{
return IsIndex(dataType) ? LiteralDataType.UInt16 : dataType;
}
public static bool IsIndex(LiteralDataType dataType) => ((byte)dataType & (1 << 3)) != 0;
}
public record OperandReference : Operand
{
public OperandReference(string labelName) : base(labelName, labelName)
public OperandReference(string constantName) : base(constantName, constantName)
{
}
public string LabelName => Content;
public string ConstantName => Content;
public override string ToString() => base.ToString();
public override string ToString() => $"@{base.ToString()}";
}
+14
View File
@@ -11,6 +11,17 @@ public record Label(string Name) : CodeItem
public override string ToString() => $"{Name}:";
}
public record QualifiedTypeName(string NamespacePrefix, string TypeName) : AsmItem
{
public override string ToString()
{
if (NamespacePrefix is null)
return TypeName;
else
return $"{NamespacePrefix}:{TypeName}";
}
}
public record Program
{
public Program(IEnumerable<IBodyItem> body)
@@ -44,6 +55,9 @@ public record Program
sb.AppendJoin(lineEnding, Imports.Select(i => i.ToString()));
sb.Append(lineEnding);
sb.Append(lineEnding);
sb.AppendJoin(lineEnding, Directives.Where(d => d is not (IImportDirective or ExportDirective)).Select(i => i.ToString()));
sb.Append(lineEnding);
sb.Append(lineEnding);
foreach (var bodyItem in Code)
{