From 0fbf57f560c7d1aa3ff4411b127205aaf6aabbdf Mon Sep 17 00:00:00 2001 From: rameel Date: Wed, 12 Feb 2025 20:58:56 +0500 Subject: [PATCH 1/6] Add support for comments in Tiny-C --- samples/TinyC/TinyCParser.cs | 105 +++++++++++++++++++----------- samples/TinyC/examples/example1.c | 11 +++- 2 files changed, 75 insertions(+), 41 deletions(-) diff --git a/samples/TinyC/TinyCParser.cs b/samples/TinyC/TinyCParser.cs index a8990e1..ab32967 100644 --- a/samples/TinyC/TinyCParser.cs +++ b/samples/TinyC/TinyCParser.cs @@ -13,51 +13,78 @@ public static class TinyCParser [SuppressMessage("ReSharper", "InconsistentNaming")] private static Parser CreateParser() { - var keyword = Seq( - OneOf("while", "do", "if", "else"), - Not(Set("\\w"))); + var keyword = + Seq( + OneOf("while", "do", "if", "else"), + Not(Set("\\w"))); + + var number = + Set("0-9") + .OneOrMore() + .Map(Node (m) => Node.Number(int.Parse(m))) + .As("number"); - var number = Set("0-9") - .OneOrMore() - .Map(Node (m) => Node.Number(int.Parse(m))) - .As("number"); + var variable = + Seq( + Not(keyword), + Set("a-z"), + Set("a-zA-Z_0-9").ZeroOrMore() + ).Map(Identifier).As("variable"); - var variable = Seq( - Not(keyword), - Set("a-z"), - Set("a-zA-Z_0-9").ZeroOrMore() - ).Map(Identifier).As("variable"); + var block_comment = + Seq( + L("/*"), + Choice( + Any, + Eof.Then(Error("'*/'")) + ).Until(L("*/")), + L("*/") + ).Void(); + + var line_comment = + Seq( + L("//"), + Any.Until(Eol), + Eol + ).Void(); + + var ws = + Choice( + block_comment, + line_comment, + S + ).ZeroOrMore().Void(); var semicolon = - Seq(L(';'), S).Void(); + Seq(L(';'), ws).Void(); var eq = - Seq(L('='), S).Void(); + Seq(L('='), ws).Void(); var if_keyword = - Seq(L("if"), S).Void(); + Seq(L("if"), ws).Void(); var else_keyword = - Seq(L("else"), S).Void(); + Seq(L("else"), ws).Void(); var while_keyword = - Seq(L("while"), S).Void(); + Seq(L("while"), ws).Void(); var do_keyword = - Seq(L("do"), S).Void(); + Seq(L("do"), ws).Void(); var expr = Deferred(); var number_expr = - number.ThenIgnore(S); + number.ThenIgnore(ws); var var_expr = - variable.ThenIgnore(S); + variable.ThenIgnore(ws); var parenthesis = expr.Between( - Seq(L('('), S), - Seq(L(')'), S)); + Seq(L('('), ws), + Seq(L(')'), ws)); var primary_expr = Choice( @@ -67,48 +94,48 @@ private static Parser CreateParser() var unary_expr = Seq( OneOf("-+~!").Optional(), - S, + ws, primary_expr ).Do(CreateUnary); var mul_expr = unary_expr.Fold( - OneOf("*/%").ThenIgnore(S), + OneOf("*/%").ThenIgnore(ws), CreateBinary); var sum_expr = mul_expr.Fold( - OneOf("+-").ThenIgnore(S), + OneOf("+-").ThenIgnore(ws), CreateBinary); var shift_expr = sum_expr.Fold( - OneOf("<<", ">>").ThenIgnore(S), + OneOf("<<", ">>").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var relational_expr = shift_expr.Fold( - OneOf("<", "<=", ">", ">=").ThenIgnore(S), + OneOf("<", "<=", ">", ">=").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var eq_expr = relational_expr.Fold( - OneOf("==", "!=").ThenIgnore(S), + OneOf("==", "!=").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var binary_and_expr = eq_expr.Fold( - L('&').ThenIgnore(S), + L('&').ThenIgnore(ws), (l, r, _) => Node.Binary("&", l, r)); var exclusive_or_expr = binary_and_expr.Fold( - L('^').ThenIgnore(S), + L('^').ThenIgnore(ws), (l, r, _) => Node.Binary("^", l, r)); var inclusive_or_expr = exclusive_or_expr.Fold( - L('|').ThenIgnore(S), + L('|').ThenIgnore(ws), (l, r, _) => Node.Binary("|", l, r)); var and_expr = inclusive_or_expr.Fold( - L("&&").ThenIgnore(S), + L("&&").ThenIgnore(ws), (l, r, _) => Node.Binary("&&", l, r)); var or_expr = and_expr.Fold( - L("||").ThenIgnore(S), + L("||").ThenIgnore(ws), (l, r, _) => Node.Binary("||", l, r)); var ternary_expr = Deferred(); @@ -116,8 +143,8 @@ private static Parser CreateParser() Seq( or_expr, Seq( - L('?'), S, expr, - L(':'), S, ternary_expr + L('?'), ws, expr, + L(':'), ws, ternary_expr ).Optional()) .Do(CreateTernary); @@ -150,12 +177,12 @@ private static Parser CreateParser() statement .ZeroOrMore() .Between( - Seq(L('{'), S), - Seq(L('}'), S)) + Seq(L('{'), ws), + Seq(L('}'), ws)) .Do(CreateBlock); var empty_statement = - Seq(L(';'), S + Seq(L(';'), ws ).Map(_ => Node.Empty()); var while_statement = @@ -187,7 +214,7 @@ private static Parser CreateParser() ); return statement - .Between(S, Eof); + .Between(ws, Eof); static Node Identifier(Match m) => Node.Variable(m.ToString()); diff --git a/samples/TinyC/examples/example1.c b/samples/TinyC/examples/example1.c index 92d4f10..b7c7209 100644 --- a/samples/TinyC/examples/example1.c +++ b/samples/TinyC/examples/example1.c @@ -1,4 +1,11 @@ -{ +/* + * Tiny-C: Example 1. + */ + +{ + ///////////////////// + ///// Factorial ///// + ///////////////////// x = 5; fact = 1; @@ -8,7 +15,7 @@ { fact = fact * x; x = x - 1; - } + } while (x != 0); } } From d47fa2ac95fab74a21ed8405455eeea3bab39df9 Mon Sep 17 00:00:00 2001 From: rameel Date: Thu, 13 Feb 2025 06:57:48 +0500 Subject: [PATCH 2/6] Rename AddError to ReportExpected --- src/Ramstack.Parsing/ErrorSet.cs | 26 +++++------ .../Literal.EscapeSequence.cs | 2 +- src/Ramstack.Parsing/Literal.Number.cs | 2 +- src/Ramstack.Parsing/Literal.String.cs | 2 +- .../Literal.UnicodeEscapeSequence.cs | 2 +- src/Ramstack.Parsing/ParseContext.cs | 46 +++++++++---------- src/Ramstack.Parsing/Parser.Any.cs | 2 +- src/Ramstack.Parsing/Parser.As.cs | 2 +- src/Ramstack.Parsing/Parser.Char.cs | 2 +- src/Ramstack.Parsing/Parser.Choice.cs | 4 +- src/Ramstack.Parsing/Parser.Eof.cs | 2 +- src/Ramstack.Parsing/Parser.Eol.cs | 2 +- src/Ramstack.Parsing/Parser.Error.cs | 14 +++--- src/Ramstack.Parsing/Parser.Fail.cs | 9 ++-- src/Ramstack.Parsing/Parser.Regex.cs | 2 +- src/Ramstack.Parsing/Parser.Repeat.cs | 8 ++-- src/Ramstack.Parsing/Parser.Set.cs | 8 +++- src/Ramstack.Parsing/Parser.String.cs | 12 ++--- 18 files changed, 76 insertions(+), 71 deletions(-) diff --git a/src/Ramstack.Parsing/ErrorSet.cs b/src/Ramstack.Parsing/ErrorSet.cs index 5231df4..73376e8 100644 --- a/src/Ramstack.Parsing/ErrorSet.cs +++ b/src/Ramstack.Parsing/ErrorSet.cs @@ -1,7 +1,7 @@ namespace Ramstack.Parsing; /// -/// Represents a set of parsing errors and the positions at which they occurred in the source text. +/// Represents a collection of parsing errors in the source text. /// internal struct ErrorSet { @@ -9,7 +9,7 @@ internal struct ErrorSet private int _index; /// - /// Gets the position in the source where the parsing failed or an unmatched sequence was encountered. + /// Gets the position in the source text where a parsing error occurred. /// [SuppressMessage("ReSharper", "ConvertToAutoPropertyWithPrivateSetter")] public readonly int Index => _index; @@ -21,12 +21,12 @@ public ErrorSet() => _expectations = new ArrayBuilder(); /// - /// Adds an error message describing an expected sequence at the specified source position. + /// Reports a missing expected sequence or rule at the specified position. /// - /// The position in the source where the error occurred. - /// A string describing the expected sequence. + /// The position where the error occurred. + /// The expected sequence or rule. [MethodImpl(MethodImplOptions.NoInlining)] - public void AddError(int index, string error) + public void ReportExpected(int index, string expected) { if (index >= _index) { @@ -36,16 +36,16 @@ public void AddError(int index, string error) _expectations.Clear(); } - _expectations.Add(error); + _expectations.Add(expected); } } /// - /// Adds multiple error messages describing expected sequences at the specified source position. + /// Reports multiple missing expected sequences or rules at the specified position. /// /// The position in the source where the errors occurred. - /// An array of strings describing the expected sequences. - public void AddErrors(int index, string[] errors) + /// An array of expected sequences or rules. + public void ReportExpected(int index, string[] expected) { if (index >= _index) { @@ -55,15 +55,15 @@ public void AddErrors(int index, string[] errors) _expectations.Clear(); } - _expectations.AddRange(errors); + _expectations.AddRange(expected); } } /// - /// Returns a formatted string representing the list of expected sequences that were not matched. + /// Returns a formatted string describing the expected sequences that were not found. /// /// - /// A string describing the expected sequences, formatted appropriately. + /// A formatted string listing the expected sequences. /// public readonly override string ToString() { diff --git a/src/Ramstack.Parsing/Literal.EscapeSequence.cs b/src/Ramstack.Parsing/Literal.EscapeSequence.cs index 31f6c79..1038cc7 100644 --- a/src/Ramstack.Parsing/Literal.EscapeSequence.cs +++ b/src/Ramstack.Parsing/Literal.EscapeSequence.cs @@ -182,7 +182,7 @@ public override bool TryParse(ref ParseContext context, out T value) } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Literal.Number.cs b/src/Ramstack.Parsing/Literal.Number.cs index 615e617..7e4e972 100644 --- a/src/Ramstack.Parsing/Literal.Number.cs +++ b/src/Ramstack.Parsing/Literal.Number.cs @@ -296,7 +296,7 @@ ref MemoryMarshal.GetReference(context.Source), } context.RestorePosition(bookmark); - context.AddError(Name); + context.ReportExpected(Name); value = default; return false; } diff --git a/src/Ramstack.Parsing/Literal.String.cs b/src/Ramstack.Parsing/Literal.String.cs index 58fccce..266d14a 100644 --- a/src/Ramstack.Parsing/Literal.String.cs +++ b/src/Ramstack.Parsing/Literal.String.cs @@ -42,7 +42,7 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out var count = TryParseImpl(context.Remaining, out value); if (count == 0) { - context.AddError(Name); + context.ReportExpected(Name); } else { diff --git a/src/Ramstack.Parsing/Literal.UnicodeEscapeSequence.cs b/src/Ramstack.Parsing/Literal.UnicodeEscapeSequence.cs index c9de9f2..20d814c 100644 --- a/src/Ramstack.Parsing/Literal.UnicodeEscapeSequence.cs +++ b/src/Ramstack.Parsing/Literal.UnicodeEscapeSequence.cs @@ -78,7 +78,7 @@ public override bool TryParse(ref ParseContext context, out T value) } } - context.AddError(Name); + context.ReportExpected(Name); value = default; return false; } diff --git a/src/Ramstack.Parsing/ParseContext.cs b/src/Ramstack.Parsing/ParseContext.cs index a624df0..94d0841 100644 --- a/src/Ramstack.Parsing/ParseContext.cs +++ b/src/Ramstack.Parsing/ParseContext.cs @@ -7,7 +7,7 @@ public ref struct ParseContext { private readonly ReadOnlySpan _source; private (int Index, int Length) _match; - private ErrorSet _errors; + private ErrorSet _diagnostics; private int _position; /// @@ -58,7 +58,7 @@ ref MemoryMarshal.GetReference(source), public ParseContext(ReadOnlySpan source) { _source = source; - _errors = new ErrorSet(); + _diagnostics = new ErrorSet(); } /// @@ -117,37 +117,37 @@ public void SetMatched(int index, int length) } /// - /// Adds an error message for an expected sequence at the current position. - /// This error is added only if diagnostics are enabled. + /// Reports a missing expected sequence or rule at the current position. + /// The expected sequence or rule is added only if diagnostics are enabled. /// - /// The error message to add. - public void AddError(string? error) + /// The expected sequence or rule. + public void ReportExpected(string? expected) { - if (DiagnosticState == DiagnosticState.Normal && error is not null) - _errors.AddError(_position, error); + if (DiagnosticState == DiagnosticState.Normal && expected is not null) + _diagnostics.ReportExpected(_position, expected); } /// - /// Adds an error message for an expected sequence at the specified position. - /// This error is added only if diagnostics are enabled. + /// Reports a missing expected sequence or rule at the specified position. + /// The expected sequence or rule is added only if diagnostics are enabled. /// - /// The error message to add. /// The position in the source text where the error occurred. - internal void AddError(string? error, int index) + /// The expected sequence or rule. + internal void ReportExpected(int index, string? expected) { - if (DiagnosticState == DiagnosticState.Normal && error is not null) - _errors.AddError(index, error); + if (DiagnosticState == DiagnosticState.Normal && expected is not null) + _diagnostics.ReportExpected(index, expected); } /// - /// Adds multiple error messages for expected sequences at the current position. - /// These errors are added only if diagnostics are enabled. + /// Reports multiple missing expected sequences or rules at the current position. + /// The expected sequences or rules are added only if diagnostics are enabled. /// - /// An array of error messages to add. - public void AddErrors(string[] errors) + /// An array of expected sequences or rules. + public void ReportExpected(string[] expected) { if (DiagnosticState == DiagnosticState.Normal) - _errors.AddErrors(_position, errors); + _diagnostics.ReportExpected(_position, expected); } /// @@ -185,8 +185,8 @@ public DiagnosticState SuppressDiagnostics() /// /// Consider, for example, a grammar for parsing a float value: /// - /// // ('+' / '-')? [0-9]+ (.[0-9]+)? - /// // sign integer fraction + /// // ('+' / '-')? [0-9]+ (.[0-9]+)? + /// // [ sign ] [int ] [fraction] /// /// var parser = Seq( /// Set("+-").Optional(), // sign @@ -195,7 +195,7 @@ public DiagnosticState SuppressDiagnostics() /// L('.'), /// Set("0-9").OneOrMore() /// ).Optional() // fraction - /// ).Name("float"); + /// ).As("float"); /// /// /// We can visualize this parser, where float is the top-level @@ -263,7 +263,7 @@ public void RestorePosition(PositionBookmark bookmark) /// public readonly override string ToString() => - GenerateErrorMessage(_source, _errors.Index, _errors.ToString()); + GenerateErrorMessage(_source, _diagnostics.Index, _diagnostics.ToString()); /// /// Generates a formatted error message that includes line and column information. diff --git a/src/Ramstack.Parsing/Parser.Any.cs b/src/Ramstack.Parsing/Parser.Any.cs index 94af2c9..51263a6 100644 --- a/src/Ramstack.Parsing/Parser.Any.cs +++ b/src/Ramstack.Parsing/Parser.Any.cs @@ -34,7 +34,7 @@ public override bool TryParse(ref ParseContext context, out T value) return true; } - context.AddError(Name); + context.ReportExpected(Name); value = default; return false; diff --git a/src/Ramstack.Parsing/Parser.As.cs b/src/Ramstack.Parsing/Parser.As.cs index d0eb23d..1e95753 100644 --- a/src/Ramstack.Parsing/Parser.As.cs +++ b/src/Ramstack.Parsing/Parser.As.cs @@ -38,7 +38,7 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } context.RestoreDiagnosticState(state); - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Parser.Char.cs b/src/Ramstack.Parsing/Parser.Char.cs index bd8c68c..b8d2ccf 100644 --- a/src/Ramstack.Parsing/Parser.Char.cs +++ b/src/Ramstack.Parsing/Parser.Char.cs @@ -66,7 +66,7 @@ public override bool TryParse(ref ParseContext context, out T value) } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Parser.Choice.cs b/src/Ramstack.Parsing/Parser.Choice.cs index 88a7ebb..4992eeb 100644 --- a/src/Ramstack.Parsing/Parser.Choice.cs +++ b/src/Ramstack.Parsing/Parser.Choice.cs @@ -199,11 +199,11 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out switch (Name) { case not null: - context.AddError(Name); + context.ReportExpected(Name); break; default: - context.AddErrors(_expected); + context.ReportExpected(_expected); break; } diff --git a/src/Ramstack.Parsing/Parser.Eof.cs b/src/Ramstack.Parsing/Parser.Eof.cs index 1079ea7..40e03a7 100644 --- a/src/Ramstack.Parsing/Parser.Eof.cs +++ b/src/Ramstack.Parsing/Parser.Eof.cs @@ -25,7 +25,7 @@ public override bool TryParse(ref ParseContext context, out Unit value) return true; } - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Parser.Eol.cs b/src/Ramstack.Parsing/Parser.Eol.cs index 73eb28a..02aaf84 100644 --- a/src/Ramstack.Parsing/Parser.Eol.cs +++ b/src/Ramstack.Parsing/Parser.Eol.cs @@ -40,7 +40,7 @@ public override bool TryParse(ref ParseContext context, out Unit value) break; default: - context.AddError(Name); + context.ReportExpected(Name); return false; } } diff --git a/src/Ramstack.Parsing/Parser.Error.cs b/src/Ramstack.Parsing/Parser.Error.cs index 065b355..078f8f6 100644 --- a/src/Ramstack.Parsing/Parser.Error.cs +++ b/src/Ramstack.Parsing/Parser.Error.cs @@ -3,20 +3,20 @@ namespace Ramstack.Parsing; partial class Parser { /// - /// Creates a parser that always fails with the specified error message. + /// Creates a parser that always fails with a report of a missing expected sequence or rule. /// /// The type of the value produced by the parser. - /// The error message that this parser will produce upon failure. + /// The expected sequence or rule. /// - /// A parser that always fails with the specified error message. + /// A parser that always fails with a report of a missing expected sequence or rule. /// - public static Parser Error(string message) => - new ErrorParser { Name = message }; + public static Parser Error(string expected) => + new ErrorParser { Name = expected }; #region Inner type: ErrorParser /// - /// Represents a parser that always fails with the specified error message. + /// Represents a parser that always fails with a report of a missing expected sequence or rule. /// /// The type of the value produced by the parser. private sealed class ErrorParser : Parser @@ -25,7 +25,7 @@ private sealed class ErrorParser : Parser public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) { value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Parser.Fail.cs b/src/Ramstack.Parsing/Parser.Fail.cs index cc2ea3f..3eeef46 100644 --- a/src/Ramstack.Parsing/Parser.Fail.cs +++ b/src/Ramstack.Parsing/Parser.Fail.cs @@ -13,7 +13,7 @@ partial class Parser public static Parser Fail(string message) { Argument.ThrowIfNullOrEmpty(message); - return new FailParser { Name = message }; + return new FailParser(message); } /// @@ -31,15 +31,16 @@ public static void FatalError(string message) => /// with the specified error message when invoked. /// /// The type of value produced by the parser. - private sealed class FailParser : Parser + /// The error message to be thrown. + private sealed class FailParser(string message) : Parser { /// public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out T? value) => - throw new FatalErrorException(Name!); + throw new FatalErrorException(message); /// protected internal override Parser ToVoidParser() => - new FailParser { Name = Name }; + new FailParser(message); } #endregion diff --git a/src/Ramstack.Parsing/Parser.Regex.cs b/src/Ramstack.Parsing/Parser.Regex.cs index 8d14695..5c72c2a 100644 --- a/src/Ramstack.Parsing/Parser.Regex.cs +++ b/src/Ramstack.Parsing/Parser.Regex.cs @@ -62,7 +62,7 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } diff --git a/src/Ramstack.Parsing/Parser.Repeat.cs b/src/Ramstack.Parsing/Parser.Repeat.cs index b7dec99..9d483fd 100644 --- a/src/Ramstack.Parsing/Parser.Repeat.cs +++ b/src/Ramstack.Parsing/Parser.Repeat.cs @@ -463,7 +463,7 @@ ref Unsafe.Add(ref MemoryMarshal.GetReference(s), (nint)(uint)index), if (count >= _min) { if (count < _max) - context.AddError(Name, context.Position + count); + context.ReportExpected(context.Position + count, Name); count = Math.Min(count, _max); context.Advance(count); @@ -472,7 +472,7 @@ ref Unsafe.Add(ref MemoryMarshal.GetReference(s), (nint)(uint)index), } else { - context.AddError(Name, context.Position + count); + context.ReportExpected(context.Position + count, Name); } return count >= _min; @@ -547,14 +547,14 @@ ref Unsafe.Add(ref MemoryMarshal.GetReference(s), (nint)(uint)index), if (count >= _min) { if (count < _max) - context.AddError(Name, context.Position + count); + context.ReportExpected(context.Position + count, Name); count = Math.Min(count, _max); context.Advance(count); } else { - context.AddError(Name, context.Position + count); + context.ReportExpected(context.Position + count, Name); } return count >= _min; diff --git a/src/Ramstack.Parsing/Parser.Set.cs b/src/Ramstack.Parsing/Parser.Set.cs index 1e936c7..e72c21d 100644 --- a/src/Ramstack.Parsing/Parser.Set.cs +++ b/src/Ramstack.Parsing/Parser.Set.cs @@ -256,7 +256,7 @@ public override bool TryParse(ref ParseContext context, out T value) } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } @@ -324,7 +324,7 @@ public override bool TryParse(ref ParseContext context, out T value) } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } @@ -634,6 +634,10 @@ private static bool ContainsCore(char ch, ushort[] ranges) } } } + else + { + throw new NotSupportedException(); + } return false; } diff --git a/src/Ramstack.Parsing/Parser.String.cs b/src/Ramstack.Parsing/Parser.String.cs index 67c901a..518b6ff 100644 --- a/src/Ramstack.Parsing/Parser.String.cs +++ b/src/Ramstack.Parsing/Parser.String.cs @@ -137,7 +137,7 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } @@ -187,7 +187,7 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out } value = default; - context.AddError(Name); + context.ReportExpected(Name); return false; } @@ -258,10 +258,10 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out switch (Name) { case not null: - context.AddError(Name); + context.ReportExpected(Name); break; default: - context.AddErrors(_expected); + context.ReportExpected(_expected); break; } @@ -337,10 +337,10 @@ public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out switch (Name) { case not null: - context.AddError(Name); + context.ReportExpected(Name); break; default: - context.AddErrors(_expected); + context.ReportExpected(_expected); break; } From 5c45d983e23e23911eba30726e06a03b19821fd7 Mon Sep 17 00:00:00 2001 From: rameel Date: Thu, 13 Feb 2025 06:59:55 +0500 Subject: [PATCH 3/6] Rename comment parsing variables for clarity --- samples/TinyC/TinyCParser.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/TinyC/TinyCParser.cs b/samples/TinyC/TinyCParser.cs index ab32967..051a481 100644 --- a/samples/TinyC/TinyCParser.cs +++ b/samples/TinyC/TinyCParser.cs @@ -31,7 +31,7 @@ private static Parser CreateParser() Set("a-zA-Z_0-9").ZeroOrMore() ).Map(Identifier).As("variable"); - var block_comment = + var multiline_comment = Seq( L("/*"), Choice( @@ -41,7 +41,7 @@ private static Parser CreateParser() L("*/") ).Void(); - var line_comment = + var single_comment = Seq( L("//"), Any.Until(Eol), @@ -50,8 +50,8 @@ private static Parser CreateParser() var ws = Choice( - block_comment, - line_comment, + single_comment, + multiline_comment, S ).ZeroOrMore().Void(); From 0fd723602e950fa18c6b75c4409dd7c01d435d1a Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 15 Feb 2025 22:57:37 +0500 Subject: [PATCH 4/6] Clean up and formatting --- samples/CalcExpr/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/CalcExpr/README.md b/samples/CalcExpr/README.md index cebaa03..e8b2859 100644 --- a/samples/CalcExpr/README.md +++ b/samples/CalcExpr/README.md @@ -6,7 +6,7 @@ This project implements a simple mathematical expression parser. ```sh start - = sum_expr EOF + = S sum_expr EOF ; sum_expr @@ -37,7 +37,7 @@ S = [ \t\r\n]* ; -EOF: +EOF = $ ; ``` From 42a232398f2d3e0589c5999a89ca54caa4d3497a Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 15 Feb 2025 22:58:28 +0500 Subject: [PATCH 5/6] Clean up and formatting Tiny-C parser --- samples/TinyC/Node.cs | 8 +-- samples/TinyC/README.md | 32 +++++---- samples/TinyC/TinyCParser.cs | 130 +++++++++++++++++++---------------- 3 files changed, 91 insertions(+), 79 deletions(-) diff --git a/samples/TinyC/Node.cs b/samples/TinyC/Node.cs index b9ab3ff..1b79e6e 100644 --- a/samples/TinyC/Node.cs +++ b/samples/TinyC/Node.cs @@ -7,15 +7,15 @@ public abstract record Node public static Node Empty() => new BlockNode([]); public static Node Number(int value) => new NumberNode(value); public static Node Variable(string name) => new VariableNode(name); - public static Node If(Node test, Node ifTrue, Node ifFalse) => new IfNode(test, Wrap(ifTrue), Wrap(ifFalse)); + public static Node If(Node test, Node ifTrue, Node ifFalse) => new IfNode(test, Block(ifTrue), Block(ifFalse)); public static Node Ternary(Node test, Node ifTrue, Node ifFalse) => new TernaryNode(test, ifTrue, ifFalse); - public static Node WhileLoop(Node test, Node body) => new WhileLoopNode(test, Wrap(body)); - public static Node DoWhileLoop(Node test, Node body) => new DoWhileLoopNode(test, Wrap(body)); + public static Node WhileLoop(Node test, Node body) => new WhileLoopNode(test, Block(body)); + public static Node DoWhileLoop(Node test, Node body) => new DoWhileLoopNode(test, Block(body)); public static Node Unary(char op, Node operand) => new UnaryNode(op, operand); public static Node Binary(string op, Node left, Node right) => new BinaryNode(op, left, right); public static Node Assign(Node variable, Node value) => Binary("=", variable, value); public static Node Block(IReadOnlyList statements) => new BlockNode(statements); - private static Node Wrap(Node statement) => statement is BlockNode ? statement : new BlockNode([statement]); + public static Node Block(Node statement) => statement is BlockNode ? statement : new BlockNode([statement]); public sealed override string ToString() { diff --git a/samples/TinyC/README.md b/samples/TinyC/README.md index 1493dc1..e5e0033 100644 --- a/samples/TinyC/README.md +++ b/samples/TinyC/README.md @@ -1,4 +1,4 @@ -# Tiny-C +# Tiny-C This project implements a parser for the [Tiny-C](http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c) language, a highly simplified version of `C` designed as an educational tool for learning about compilers. @@ -11,7 +11,7 @@ The main differences from the original `Tiny-C` are: ## Tiny-C Grammar ```sh -start: +start = S statement EOF ; @@ -20,10 +20,12 @@ keyword ; number - = [0-9]+; + = [0-9]+ + ; variable - = !keyword [a-zA-Z_][a-zA-Z0-9_]*; + = !keyword [a-zA-Z_][a-zA-Z0-9_]* + ; S = [ \t\n\r]* @@ -43,34 +45,34 @@ number_expr expr = assigment_expr + / ternary_expr ; assigment_expr = var_expr "=" S expr - / ternary_expr ; ternary_expr - = or_expr ("?" S expr ":" S ternary_expr)? + = logical_or_expr ("?" S expr ":" S ternary_expr)? ; -or_expr - = and_expr ("||" S and_expr)* +logical_or_expr + = logical_and_expr ("||" S logical_and_expr)* ; -and_expr - = inclusive_or_expr ("&&" S inclusive_or_expr)* +logical_and_expr + = bitwise_or_expr ("&&" S bitwise_or_expr)* ; -inclusive_or_expr - = exclusive_or_expr ("|" S exclusive_or_expr)* +bitwise_or_expr + = bitwise_xor_expr ("|" S bitwise_xor_expr)* ; -exclusive_or_expr - = binary_and_expr ("^" S binary_and_expr)* +bitwise_xor_expr + = bitwise_and_expr ("^" S bitwise_and_expr)* ; -binary_and_expr +bitwise_and_expr = eq_expr ("&" S eq_expr)* ; diff --git a/samples/TinyC/TinyCParser.cs b/samples/TinyC/TinyCParser.cs index 051a481..5e63749 100644 --- a/samples/TinyC/TinyCParser.cs +++ b/samples/TinyC/TinyCParser.cs @@ -73,7 +73,8 @@ private static Parser CreateParser() var do_keyword = Seq(L("do"), ws).Void(); - var expr = Deferred(); + var expr = + Deferred(); var number_expr = number.ThenIgnore(ws); @@ -92,69 +93,78 @@ private static Parser CreateParser() number_expr, var_expr); - var unary_expr = Seq( - OneOf("-+~!").Optional(), - ws, - primary_expr + var unary_expr = + Seq( + OneOf("-+~!").Optional(), + ws, + primary_expr ).Do(CreateUnary); var mul_expr = unary_expr.Fold( OneOf("*/%").ThenIgnore(ws), CreateBinary); - var sum_expr = mul_expr.Fold( - OneOf("+-").ThenIgnore(ws), - CreateBinary); - - var shift_expr = sum_expr.Fold( - OneOf("<<", ">>").ThenIgnore(ws), - (l, r, o) => Node.Binary(o, l, r)); - - var relational_expr = shift_expr.Fold( - OneOf("<", "<=", ">", ">=").ThenIgnore(ws), - (l, r, o) => Node.Binary(o, l, r)); - - var eq_expr = relational_expr.Fold( - OneOf("==", "!=").ThenIgnore(ws), - (l, r, o) => Node.Binary(o, l, r)); - - var binary_and_expr = eq_expr.Fold( - L('&').ThenIgnore(ws), - (l, r, _) => Node.Binary("&", l, r)); - - var exclusive_or_expr = binary_and_expr.Fold( - L('^').ThenIgnore(ws), - (l, r, _) => Node.Binary("^", l, r)); - - var inclusive_or_expr = exclusive_or_expr.Fold( - L('|').ThenIgnore(ws), - (l, r, _) => Node.Binary("|", l, r)); - - var and_expr = inclusive_or_expr.Fold( - L("&&").ThenIgnore(ws), - (l, r, _) => Node.Binary("&&", l, r)); - - var or_expr = and_expr.Fold( - L("||").ThenIgnore(ws), - (l, r, _) => Node.Binary("||", l, r)); + var sum_expr = + mul_expr.Fold( + OneOf("+-").ThenIgnore(ws), + CreateBinary); + + var shift_expr = + sum_expr.Fold( + OneOf("<<", ">>").ThenIgnore(ws), + (l, r, o) => Node.Binary(o, l, r)); + + var relational_expr = + shift_expr.Fold( + OneOf("<", "<=", ">", ">=").ThenIgnore(ws), + (l, r, o) => Node.Binary(o, l, r)); + + var eq_expr = + relational_expr.Fold( + OneOf("==", "!=").ThenIgnore(ws), + (l, r, o) => Node.Binary(o, l, r)); + + var bitwise_and_expr = + eq_expr.Fold( + L('&').ThenIgnore(ws), + (l, r, _) => Node.Binary("&", l, r)); + + var bitwise_xor_expr = + bitwise_and_expr.Fold( + L('^').ThenIgnore(ws), + (l, r, _) => Node.Binary("^", l, r)); + + var bitwise_or_expr = + bitwise_xor_expr.Fold( + L('|').ThenIgnore(ws), + (l, r, _) => Node.Binary("|", l, r)); + + var logical_and_expr = + bitwise_or_expr.Fold( + L("&&").ThenIgnore(ws), + (l, r, _) => Node.Binary("&&", l, r)); + + var logical_or_expr = + logical_and_expr.Fold( + L("||").ThenIgnore(ws), + (l, r, _) => Node.Binary("||", l, r)); var ternary_expr = Deferred(); ternary_expr.Parser = Seq( - or_expr, + logical_or_expr, Seq( L('?'), ws, expr, - L(':'), ws, ternary_expr - ).Optional()) - .Do(CreateTernary); + L(':'), ws, ternary_expr).Optional() + ).Do(CreateTernary); var assignment_expr = - Choice( - Seq(var_expr, eq, expr).Do(CreateAssign), - ternary_expr); + Seq(var_expr, eq, expr).Do(CreateAssign); expr.Parser = - assignment_expr; + Choice( + assignment_expr, + ternary_expr); var statement = Deferred(); @@ -163,7 +173,7 @@ private static Parser CreateParser() Seq( else_keyword, statement - ).Do((_, s) => s).DefaultOnFail(Node.Empty()); + ).Do((_, s) => s).DefaultOnFail(Node.Empty()); var if_statement = Seq( @@ -171,7 +181,7 @@ private static Parser CreateParser() parenthesis, statement, else_clause - ).Do(CreateIf); + ).Do(CreateIf); var block_statement = statement @@ -190,7 +200,7 @@ private static Parser CreateParser() while_keyword, parenthesis, statement - ).Do(CreateWhile); + ).Do(CreateWhile); var do_while_statement = Seq( @@ -199,19 +209,19 @@ private static Parser CreateParser() while_keyword, parenthesis, semicolon - ).Do(CreateDoWhile); + ).Do(CreateDoWhile); var expr_statement = expr.ThenIgnore(semicolon); - statement.Parser = Choice( - if_statement, - while_statement, - do_while_statement, - block_statement, - expr_statement, - empty_statement - ); + statement.Parser = + Choice( + if_statement, + while_statement, + do_while_statement, + block_statement, + expr_statement, + empty_statement); return statement .Between(ws, Eof); From c617e17aa9629f9f288f670cf7741cae208e2c74 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 15 Feb 2025 23:02:41 +0500 Subject: [PATCH 6/6] Use ArrayList`1 instead of List`1 for consistency --- src/Ramstack.Parsing/Parser.Separated.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ramstack.Parsing/Parser.Separated.cs b/src/Ramstack.Parsing/Parser.Separated.cs index bf5df24..a0be11b 100644 --- a/src/Ramstack.Parsing/Parser.Separated.cs +++ b/src/Ramstack.Parsing/Parser.Separated.cs @@ -15,7 +15,7 @@ partial class Parser /// /// /// A parser that repeatedly applies the main parser, interleaved with the specified separator. /// - public static Parser> Separated(this Parser parser, Parser separator, bool allowTrailing = false, int min = 0, int max = int.MaxValue) => + public static Parser> Separated(this Parser parser, Parser separator, bool allowTrailing = false, int min = 0, int max = int.MaxValue) => new SeparatedParser(parser, separator.Void(), allowTrailing, min, max); #region Inner type: SeparatedParser @@ -29,12 +29,12 @@ public static Parser> Separated(this Parser parser, Pa /// if trailing separator is allowed; otherwise, . /// The minimum number of repetitions. /// The maximum number of repetitions. - private sealed class SeparatedParser(Parser parser, Parser separator, bool allowTrailing, int min, int max) : Parser> + private sealed class SeparatedParser(Parser parser, Parser separator, bool allowTrailing, int min, int max) : Parser> { /// - public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out List? value) + public override bool TryParse(ref ParseContext context, [NotNullWhen(true)] out ArrayList? value) { - var list = new List(); + var list = new ArrayList(); var bookmark = context.BookmarkPosition(); var separatorBookmark = bookmark;