using Microsoft.Iris.Asm.Models; using Sprache; using System.Linq; namespace Microsoft.Iris.Asm; partial class Lexer { private static IResult ParseDirective(IInput input) { input = ConsumeWhitespace(input); var line = input.Line; var column = input.Column; var directiveBeginResult = Parse.Char('.')(input); input = directiveBeginResult.Remainder; if (!directiveBeginResult.WasSuccessful) return Result.Failure(input, "Invalid directive", ["Expected '.', followed by an identifier"]); var directiveIdResult = WordText(input); input = directiveIdResult.Remainder; if (!directiveIdResult.WasSuccessful) return Result.Failure(input, "Invalid directive", ["Expected a valid directive name"]); IDirective directive; switch (directiveIdResult.Value.ToUpperInvariant()) { case "IMPORT": return ParseImportAsDirective(input); case "SECTION": if (StatementEnd(input).WasSuccessful) return Result.Failure(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(input, "Invalid section name", ["Expected a section name containing only letters"]); directive = new SectionDirective(sectionNameResult.Value) { Line = line, Column = column, }; break; case "CONSTANT": if (StatementEnd(input).WasSuccessful) return Result.Failure(input, "Invalid constant directive", ["Expected a consant declaration"]); var constNameResult = Identifier.Token()(input); input = constNameResult.Remainder; if (!constNameResult.WasSuccessful) return Result.Failure(input, "Invalid constant directive", ["Expected a name for the constant"]); input = Parse.Char('=').Token()(input).Remainder; // 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) { // 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(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(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(input, "Invalid constant directive", ["Expected '('"]); var contentResult = Parse.CharExcept(')').AtLeastOnce().Text().Token()(input); input = contentResult.Remainder; if (!contentResult.WasSuccessful) return Result.Failure(input, "Invalid constant directive", ["Expected constant value"]); var closeBracketResult = Parse.Char(')').Token()(input); input = closeBracketResult.Remainder; if (!closeBracketResult.WasSuccessful) return Result.Failure(input, "Invalid constant directive", ["Expected ')'"]); directive = new EncodedConstantDirective(constNameResult.Value, typeNameResult.Value, contentResult.Value) { Line = line, Column = column, }; } break; case "EXPORT": if (StatementEnd(input).WasSuccessful) return Result.Failure(input, "Invalid export directive", ["Expected export information"]); var labelPrefixResult = Identifier.Token()(input); input = labelPrefixResult.Remainder; if (!labelPrefixResult.WasSuccessful) return Result.Failure(input, "Invalid export directive", ["Expected prefix of labels to export"]); var listenerCountResult = WholeNumber.Token()(input); input = listenerCountResult.Remainder; if (!listenerCountResult.WasSuccessful) return Result.Failure(input, "Invalid export directive", ["Expected listener count"]); if (!uint.TryParse(listenerCountResult.Value, out var listenerCount)) return Result.Failure(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(input, "Invalid export directive", ["Expected base type name"]); var labelPrefix = labelPrefixResult.Value; var baseTypeName = baseTypeNameResult.Value; directive = new ExportDirective(labelPrefix, listenerCount, baseTypeName) { Line = line, Column = column, }; break; default: return Result.Failure(input, $"Unknown import type '{directiveIdResult.Value}'", ["Expected 'export', 'import', or 'section'"]); } return Result.Success(directive, input); } }