using Sprache; using System.Collections.Generic; using System.Linq; namespace Microsoft.Iris.Asm; public static class Lexer { public static readonly Parser AlphanumericText = Parse.LetterOrDigit.AtLeastOnce().Text(); public static readonly Parser Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text(); public static readonly Parser StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator); public static readonly Parser Import = ParseImport; public static readonly Parser BodyItem = ParseBodyItem; public static readonly Parser Program = from imports in Import.Token().Many() from body in BodyItem.Many() select new Program(imports, body); private static IResult ParseBodyItem(IInput input) { input = ConsumeWhitespace(input); var identifierResult = Parse.Letter.AtLeastOnce().Text().Invoke(input); if (!identifierResult.WasSuccessful) return Result.Failure(input, "Invalid code", System.Array.Empty()); 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 operands = new(); var endOfInstructionResult = StatementEnd(input); input = endOfInstructionResult.Remainder; if (!endOfInstructionResult.WasSuccessful) { input = ConsumeWhitespace(input); 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); } private static IResult ParseImport(IInput input) { input = ConsumeWhitespace(input); var importDirectiveResult = Parse.String(".import-")(input); input = importDirectiveResult.Remainder; if (!importDirectiveResult.WasSuccessful) return Result.Failure(input, "Invalid import directive", null); var importTypeResult = Parse.Letter.AtLeastOnce().Text()(input); input = importTypeResult.Remainder; if (!importTypeResult.WasSuccessful) return Result.Failure(input, "Invalid import type", null); IImport import; switch (importTypeResult.Value.ToUpperInvariant()) { case "NS": var uriResult = Uri.Token()(input); input = uriResult.Remainder; if (!uriResult.WasSuccessful) return Result.Failure(input, "Invalid URI", null); input = Parse.String("as").Token()(input).Remainder; var nameResult = AlphanumericText(input); input = nameResult.Remainder; if (!nameResult.WasSuccessful) return Result.Failure(input, "Invalid namespace alias", null); import = new NamespaceImport(uriResult.Value, nameResult.Value); break; default: return Result.Failure(input, $"Unknown import type '{importTypeResult.Value}'", null); } return Result.Success(import, input); } private static IInput ConsumeWhitespace(IInput input) => Parse.WhiteSpace.Many()(input).Remainder; }