From cf19f8197ebeff5cff4357ca6457bbf661884554 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 18 Aug 2024 12:01:27 +0800 Subject: [PATCH] feat: Grammar Parser (#3) Reviewed-on: https://git.bupt-hpc.cn/jackfiled/CanonSharp/pulls/3 Co-authored-by: jackfiled Co-committed-by: jackfiled --- .../Abstractions/FailedResult.cs | 49 --- .../Abstractions/IFailedResult.cs | 45 ++ .../{ParseResult.cs => IParseResult.cs} | 19 +- .../Abstractions/{Parser.cs => IParser.cs} | 12 +- .../Abstractions/ISuccessfulResult.cs | 29 ++ .../Abstractions/SuccessfulResult.cs | 33 -- .../Extensions/ParserExtensions.cs | 140 +++---- CanonSharp.Combinator/ParseResultBuilder.cs | 8 +- CanonSharp.Combinator/ParserBuilder.cs | 41 +- .../Parsers/Bases/AlternativeParser.cs | 9 +- .../Parsers/Bases/BindParser.cs | 9 +- .../Parsers/Bases/FixParser.cs | 20 +- .../Parsers/Bases/MapParser.cs | 9 +- .../Parsers/Bases/NextParser.cs | 11 +- .../Parsers/Bases/ResumeParser.cs | 9 +- .../Parsers/ModifiedParser.cs | 16 +- .../Parsers/Modifiers/DoParser.cs | 10 +- .../Parsers/Modifiers/LookAheadParser.cs | 8 +- .../Parsers/Modifiers/ReverseParser.cs | 10 +- .../Parsers/Modifiers/SuccessfulMapParser.cs | 12 +- .../Parsers/Modifiers/TryParser.cs | 8 +- .../Parsers/PrimitiveParser.cs | 9 +- .../Parsers/Primitives/FailedParser.cs | 8 +- .../Parsers/Primitives/PureParser.cs | 4 +- .../Parsers/Primitives/SatisfyParser.cs | 2 +- .../Parsers/Primitives/SkipParser.cs | 2 +- .../Parsers/Primitives/TakeParser.cs | 2 +- .../Results/FailedResultWithError.cs | 10 +- .../Results/FailedResultWithException.cs | 13 +- .../Results/FailedResultWithMessage.cs | 11 +- .../Results/InternalSuccessfulResult.cs | 12 +- CanonSharp.Combinator/Text/StringParser.cs | 2 +- .../Text/TextParserBuilder.cs | 28 +- .../Text/TextParserExtensions.cs | 4 +- .../CanonSharp.Pascal.csproj | 1 + CanonSharp.Pascal/Parser/GrammarParser.cs | 392 ++++++++++++++++++ CanonSharp.Pascal/Parser/GrammarParserBase.cs | 70 ++++ .../Parser/LexicalTokenReadState.cs | 53 +++ .../Scanner/LexicalScanner.cs | 49 ++- .../Scanner/LexicalToken.cs | 2 +- .../Scanner/StringReadState.cs | 2 +- CanonSharp.Pascal/SyntaxTree/AssignNode.cs | 10 + .../SyntaxTree/BinaryOperatorNode.cs | 33 ++ CanonSharp.Pascal/SyntaxTree/BlockNode.cs | 8 + .../SyntaxTree/BooleanValueNode.cs | 8 + CanonSharp.Pascal/SyntaxTree/CharValueNode.cs | 8 + CanonSharp.Pascal/SyntaxTree/ConstantNode.cs | 12 + .../SyntaxTree/FloatValueNode.cs | 8 + CanonSharp.Pascal/SyntaxTree/ForNode.cs | 20 + CanonSharp.Pascal/SyntaxTree/IfNode.cs | 26 ++ .../SyntaxTree/IntegerValueNode.cs | 8 + CanonSharp.Pascal/SyntaxTree/Parameter.cs | 14 + .../SyntaxTree/ProcedureCallNode.cs | 18 + CanonSharp.Pascal/SyntaxTree/Program.cs | 10 + CanonSharp.Pascal/SyntaxTree/ProgramBody.cs | 17 + CanonSharp.Pascal/SyntaxTree/ProgramHead.cs | 10 + CanonSharp.Pascal/SyntaxTree/Subprogram.cs | 10 + .../SyntaxTree/SubprogramBody.cs | 12 + .../SyntaxTree/SubprogramHead.cs | 28 ++ .../SyntaxTree/SyntaxNodeBase.cs | 43 ++ CanonSharp.Pascal/SyntaxTree/TypeNode.cs | 24 ++ .../SyntaxTree/UnaryOperatorNode.cs | 17 + .../SyntaxTree/VariableDeclarationNode.cs | 12 + CanonSharp.Pascal/SyntaxTree/VariableNode.cs | 23 + CanonSharp.Pascal/SyntaxTree/WhileNode.cs | 10 + CanonSharp.Tests/CanonSharp.Tests.csproj | 2 +- .../CombinatorsTests/BasicParsersTests.cs | 12 +- .../CombinatorsTests/CombinatorParserTests.cs | 45 +- .../CombinatorsTests/LinqTests.cs | 4 +- .../CombinatorsTests/ModifierParserTests.cs | 8 +- .../CombinatorsTests/PrimitiveParserTests.cs | 25 +- .../ParserTests/ConstDeclarationTests.cs | 62 +++ .../ParserTests/ExpressionParserTests.cs | 297 +++++++++++++ .../ParserTests/ProgramParserTests.cs | 229 ++++++++++ .../ParserTests/StatementParserTests.cs | 190 +++++++++ .../ParserTests/SubprogramParserTests.cs | 83 ++++ .../ParserTests/VariableDeclarationTests.cs | 82 ++++ .../ReaderTests/StringReadStateTests.cs | 4 +- .../LexicalParserTests.cs | 19 +- .../LexicalTokenParserTest.cs | 36 +- CanonSharp.Tests/TextTests/TextParserTests.cs | 14 +- .../Utils/GrammarParserTestBase.cs | 31 ++ CanonSharp.Tests/Utils/LexicalTestBase.cs | 20 +- CanonSharp.Tests/Utils/ParserTestsBase.cs | 16 +- CanonSharp.sln | 2 +- 85 files changed, 2340 insertions(+), 413 deletions(-) delete mode 100644 CanonSharp.Combinator/Abstractions/FailedResult.cs create mode 100644 CanonSharp.Combinator/Abstractions/IFailedResult.cs rename CanonSharp.Combinator/Abstractions/{ParseResult.cs => IParseResult.cs} (70%) rename CanonSharp.Combinator/Abstractions/{Parser.cs => IParser.cs} (68%) create mode 100644 CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs delete mode 100644 CanonSharp.Combinator/Abstractions/SuccessfulResult.cs rename CanonSharp.Common/CanonSharp.Common.csproj => CanonSharp.Pascal/CanonSharp.Pascal.csproj (89%) create mode 100644 CanonSharp.Pascal/Parser/GrammarParser.cs create mode 100644 CanonSharp.Pascal/Parser/GrammarParserBase.cs create mode 100644 CanonSharp.Pascal/Parser/LexicalTokenReadState.cs rename {CanonSharp.Common => CanonSharp.Pascal}/Scanner/LexicalScanner.cs (74%) rename {CanonSharp.Common => CanonSharp.Pascal}/Scanner/LexicalToken.cs (95%) rename {CanonSharp.Common => CanonSharp.Pascal}/Scanner/StringReadState.cs (97%) create mode 100644 CanonSharp.Pascal/SyntaxTree/AssignNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/BlockNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/CharValueNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/ConstantNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/ForNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/IfNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/Parameter.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/Program.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/ProgramBody.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/ProgramHead.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/Subprogram.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/TypeNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/VariableNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/WhileNode.cs create mode 100644 CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs create mode 100644 CanonSharp.Tests/ParserTests/ExpressionParserTests.cs create mode 100644 CanonSharp.Tests/ParserTests/ProgramParserTests.cs create mode 100644 CanonSharp.Tests/ParserTests/StatementParserTests.cs create mode 100644 CanonSharp.Tests/ParserTests/SubprogramParserTests.cs create mode 100644 CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs rename CanonSharp.Tests/{ScannerTest => ScannerTests}/LexicalParserTests.cs (90%) rename CanonSharp.Tests/{ScannerTest => ScannerTests}/LexicalTokenParserTest.cs (76%) create mode 100644 CanonSharp.Tests/Utils/GrammarParserTestBase.cs diff --git a/CanonSharp.Combinator/Abstractions/FailedResult.cs b/CanonSharp.Combinator/Abstractions/FailedResult.cs deleted file mode 100644 index 9fd8a42..0000000 --- a/CanonSharp.Combinator/Abstractions/FailedResult.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace CanonSharp.Combinator.Abstractions; - -/// -/// 失败解析结果基类 -/// -/// 输入流类型 -/// 解析结果类型 -public abstract class FailedResult : ParseResult -{ - public override T Value => throw Exception; - - /// - /// 当前读取到的状态 - /// - public abstract IReadState State { get; } - - /// - /// 解析失败的消息 - /// - public abstract string Message { get; } - - /// - /// 解析失败的异常 - /// - public virtual ParseException Exception => new(ToString()); - - /// - /// 转换该失败结果的类型 - /// - /// 转换之后的结果类型 - /// 转换之后的失败解析类型 - public abstract FailedResult Convert(); - - internal override ParseResult Next(Func> nextParser, - Func, ParseResult> continuation) - => continuation(Convert()); - - public override ParseResult Map(Func map) - => Convert(); - - public override TResult CaseOf(Func, TResult> successfulHandler, - Func, TResult> failedHandler) - => failedHandler(this); - - public override string ToString() - { - return $"Parse Failed: {Message}."; - } -} diff --git a/CanonSharp.Combinator/Abstractions/IFailedResult.cs b/CanonSharp.Combinator/Abstractions/IFailedResult.cs new file mode 100644 index 0000000..607cf84 --- /dev/null +++ b/CanonSharp.Combinator/Abstractions/IFailedResult.cs @@ -0,0 +1,45 @@ +namespace CanonSharp.Combinator.Abstractions; + +/// +/// 失败解析结果基类 +/// +/// 输入流类型 +/// 解析结果类型 +public interface IFailedResult : IParseResult +{ + T IParseResult.Value => throw Exception; + + /// + /// 当前读取到的状态 + /// + public IReadState State { get; } + + /// + /// 解析失败的消息 + /// + public string Message { get; } + + /// + /// 解析失败的异常 + /// + public ParseException Exception => new(Message); + + /// + /// 转换该失败结果的类型 + /// + /// 转换之后的结果类型 + /// 转换之后的失败解析类型 + public IFailedResult Convert(); + + IParseResult IParseResult.Next( + Func> nextParser, + Func, IParseResult> continuation) + => continuation(Convert()); + + IParseResult IParseResult.Map(Func map) + => Convert(); + + TResult IParseResult.CaseOf(Func, TResult> successfulHandler, + Func, TResult> failedHandler) + => failedHandler(this); +} diff --git a/CanonSharp.Combinator/Abstractions/ParseResult.cs b/CanonSharp.Combinator/Abstractions/IParseResult.cs similarity index 70% rename from CanonSharp.Combinator/Abstractions/ParseResult.cs rename to CanonSharp.Combinator/Abstractions/IParseResult.cs index 7f58852..331f021 100644 --- a/CanonSharp.Combinator/Abstractions/ParseResult.cs +++ b/CanonSharp.Combinator/Abstractions/IParseResult.cs @@ -5,17 +5,12 @@ namespace CanonSharp.Combinator.Abstractions; /// /// 输入流类型 /// 实际结果类型 -public abstract class ParseResult +public interface IParseResult { /// /// 实际结果对象 /// - public abstract T Value { get; } - - protected ParseResult() - { - - } + public T Value { get; } /// /// 在当前结果上应用下一个解析器 @@ -25,8 +20,8 @@ public abstract class ParseResult /// 下一个解析器函数返回的解析结果类型 /// 最终的解析结果类型 /// - internal abstract ParseResult Next(Func> nextParser, - Func, ParseResult> continuation); + internal IParseResult Next(Func> nextParser, + Func, IParseResult> continuation); /// /// 映射结果 @@ -34,7 +29,7 @@ public abstract class ParseResult /// 映射结果的函数 /// 映射结果函数返回解析结果的类型 /// 最终的解析结果 - public abstract ParseResult Map(Func map); + public IParseResult Map(Func map); /// /// 在成功或者失败解析结果上应用不同的后继函数 @@ -43,6 +38,6 @@ public abstract class ParseResult /// 在失败解析结构上应用的函数 /// 最后返回解析结果的类型 /// 最后的解析结果 - public abstract TResult CaseOf(Func, TResult> successfulHandler, - Func, TResult> failedHandler); + public TResult CaseOf(Func, TResult> successfulHandler, + Func, TResult> failedHandler); } diff --git a/CanonSharp.Combinator/Abstractions/Parser.cs b/CanonSharp.Combinator/Abstractions/IParser.cs similarity index 68% rename from CanonSharp.Combinator/Abstractions/Parser.cs rename to CanonSharp.Combinator/Abstractions/IParser.cs index 2159a95..edeb2e3 100644 --- a/CanonSharp.Combinator/Abstractions/Parser.cs +++ b/CanonSharp.Combinator/Abstractions/IParser.cs @@ -7,7 +7,7 @@ namespace CanonSharp.Combinator.Abstractions; /// /// 输入流类型 /// 解析结果类型 -public abstract class Parser +public interface IParser { /// /// 解析器运行函数 @@ -17,16 +17,16 @@ public abstract class Parser /// 输入流状态类型 /// 后继函数运行之后的解析结果类型 /// - internal abstract ParseResult Run(TState state, - Func, ParseResult> continuation) + internal IParseResult Run(TState state, + Func, IParseResult> continuation) where TState : IReadState; - public ParseResult Parse(TState state) where TState : IReadState + public IParseResult Parse(TState state) where TState : IReadState { return Run(state); } - private ParseResult Run(TState state) where TState : IReadState + private IParseResult Run(TState state) where TState : IReadState { try { @@ -38,6 +38,6 @@ public abstract class Parser } } - public static Parser operator |(Parser a, Parser b) + public static IParser operator |(IParser a, IParser b) => a.Alternative(b); } diff --git a/CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs b/CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs new file mode 100644 index 0000000..766880c --- /dev/null +++ b/CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs @@ -0,0 +1,29 @@ +namespace CanonSharp.Combinator.Abstractions; + +/// +/// 成功解析结果基类 +/// +/// 输入流类型 +/// 实际的解析结果类型 +public interface ISuccessfulResult : IParseResult +{ + /// + /// 运行下一个解析器 + /// + /// 下一个解析器 + /// 处理解析结果的后继函数 + /// 下一个解析器返回的结果类型 + /// 最终的结果类型 + /// 最终的结果 + protected IParseResult RunNext(IParser parser, + Func, IParseResult> continuation); + + IParseResult IParseResult.Next( + Func> nextParser, + Func, IParseResult> continuation) + => RunNext(nextParser(Value), continuation); + + TResult IParseResult.CaseOf(Func, TResult> successfulHandler, + Func, TResult> failedHandler) + => successfulHandler(this); +} diff --git a/CanonSharp.Combinator/Abstractions/SuccessfulResult.cs b/CanonSharp.Combinator/Abstractions/SuccessfulResult.cs deleted file mode 100644 index fc76084..0000000 --- a/CanonSharp.Combinator/Abstractions/SuccessfulResult.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace CanonSharp.Combinator.Abstractions; - -/// -/// 成功解析结果基类 -/// -/// 实际的解析结果 -/// 输入流类型 -/// 实际的解析结果类型 -public abstract class SuccessfulResult(T value) : ParseResult -{ - public override T Value => value; - - /// - /// 运行下一个解析器 - /// - /// 下一个解析器 - /// 处理解析结果的后继函数 - /// 下一个解析器返回的结果类型 - /// 最终的结果类型 - /// 最终的结果 - protected abstract ParseResult RunNext(Parser parser, - Func, ParseResult> continuation); - - internal override ParseResult Next(Func> nextParser, - Func, ParseResult> continuation) - => RunNext(nextParser(Value), continuation); - - public override TResult CaseOf(Func, TResult> successfulHandler, - Func, TResult> failedHandler) - => successfulHandler(this); - - public override string ToString() => Value?.ToString() ?? string.Empty; -} diff --git a/CanonSharp.Combinator/Extensions/ParserExtensions.cs b/CanonSharp.Combinator/Extensions/ParserExtensions.cs index 1493e2b..dc8c3dc 100644 --- a/CanonSharp.Combinator/Extensions/ParserExtensions.cs +++ b/CanonSharp.Combinator/Extensions/ParserExtensions.cs @@ -19,7 +19,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Alternative(this Parser first, Parser second) + public static IParser Alternative(this IParser first, IParser second) => new AlternativeParser(first, second); /// @@ -32,8 +32,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Alternative(this Parser parser, - Func, Parser> resume) + public static IParser Alternative(this IParser parser, + Func, IParser> resume) => new ResumeParser(parser, resume); /// @@ -46,8 +46,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Bind(this Parser parser, - Func> next) + public static IParser Bind(this IParser parser, + Func> next) => new BindParser(parser, next); /// @@ -60,7 +60,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Map(this Parser parser, Func map) + public static IParser Map(this IParser parser, Func map) => new MapParser(parser, map); /// @@ -73,7 +73,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Map(this Parser parser, TResult result) + public static IParser Map(this IParser parser, TResult result) => parser.Map(_ => result); /// @@ -87,9 +87,9 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, - Func> next, - Func, Parser> failedNext) + public static IParser Next(this IParser parser, + Func> next, + Func, IParser> failedNext) => new NextParser(parser, next, failedNext); /// @@ -103,8 +103,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, - Func> next, Func, TResult> failedHandler) + public static IParser Next(this IParser parser, + Func> next, Func, TResult> failedHandler) => parser.Next(next, failedResult => Pure(failedHandler(failedResult))); /// @@ -118,8 +118,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, - Func> next, TResult failedResult) + public static IParser Next(this IParser parser, + Func> next, TResult failedResult) => parser.Next(next, _ => Pure(failedResult)); /// @@ -133,8 +133,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, - Func nextResult, Func, Parser> failedResume) + public static IParser Next(this IParser parser, + Func nextResult, Func, IParser> failedResume) => parser.Next(x => Pure(nextResult(x)), failedResume); /// @@ -148,8 +148,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, - Func nextResult, Func, TResult> failedResult) + public static IParser Next(this IParser parser, + Func nextResult, Func, TResult> failedResult) => new SuccessfulMapParser(parser, nextResult, failedResult); /// @@ -163,7 +163,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Next(this Parser parser, + public static IParser Next(this IParser parser, Func successfulHandler, TResult failedResult) => parser.Next(successfulHandler, _ => failedResult); @@ -181,8 +181,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Do(this Parser parser, Action successfulAction, - Action> failedAction) + public static IParser Do(this IParser parser, Action successfulAction, + Action> failedAction) => new DoParser(parser, successfulAction, failedAction); /// @@ -194,7 +194,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Do(this Parser parser, Action successfulAction) + public static IParser Do(this IParser parser, Action successfulAction) => parser.Do(successfulAction, _ => { }); /// @@ -206,7 +206,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser LookAhead(this Parser parser) + public static IParser LookAhead(this IParser parser) => new LookAheadParser(parser); /// @@ -219,7 +219,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Not(this Parser parser, T result) + public static IParser Not(this IParser parser, T result) => new ReverseParser(parser, result); /// @@ -231,21 +231,21 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Not(this Parser parser) + public static IParser Not(this IParser parser) => parser.Not(Unit.Instance); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Try(this Parser parser, - Func, T> resume) + public static IParser Try(this IParser parser, + Func, T> resume) => new TryParser(parser, resume); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Try(this Parser parser, T result) + public static IParser Try(this IParser parser, T result) => parser.Try(_ => result); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Try(this Parser parser) + public static IParser Try(this IParser parser) => parser.Next(_ => true, false).Try(false); #endregion @@ -262,8 +262,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Left(this Parser left, - Parser right) + public static IParser Left(this IParser left, + IParser right) => left.Bind(right.Map); /// @@ -276,12 +276,12 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Right(this Parser left, - Parser right) + public static IParser Right(this IParser left, + IParser right) => left.Bind(_ => right); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Parser> ManyRecursively(this Parser parser, + private static IParser> ManyRecursively(this IParser parser, IEnumerable result) => parser.Next(x => parser.ManyRecursively(result.Append(x)), result); @@ -293,7 +293,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Many(this Parser parser) + public static IParser> Many(this IParser parser) => parser.ManyRecursively([]); /// @@ -304,7 +304,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Many1(this Parser parser) + public static IParser> Many1(this IParser parser) => parser.Bind(x => parser.ManyRecursively([x])); /// @@ -317,7 +317,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser SkipMany(this Parser parser) + public static IParser SkipMany(this IParser parser) => Fix(self => parser.Next(_ => self, Unit.Instance)); /// @@ -330,11 +330,11 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser SkipMany1(this Parser parser) + public static IParser SkipMany1(this IParser parser) => parser.Right(parser.SkipMany()); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Parser ChainRecursively(Func> chain, T value) + private static IParser ChainRecursively(Func> chain, T value) => chain(value).Next(x => ChainRecursively(chain, x), value); /// @@ -347,12 +347,12 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Chain(this Parser parser, Func> chain) + public static IParser Chain(this IParser parser, Func> chain) => parser.Bind(x => ChainRecursively(chain, x)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Parser> ManyTillRecursively(this Parser parser, - Parser terminator, IEnumerable result) + private static IParser> ManyTillRecursively(this IParser parser, + IParser terminator, IEnumerable result) => terminator.Next(_ => Pure>(result), _ => parser.Bind(x => parser.ManyTillRecursively(terminator, result.Append(x)))); @@ -367,8 +367,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> ManyTill(this Parser parser, - Parser terminator) + public static IParser> ManyTill(this IParser parser, + IParser terminator) => parser.ManyTillRecursively(terminator, []); /// @@ -382,8 +382,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Many1Till(this Parser parser, - Parser terminator) + public static IParser> Many1Till(this IParser parser, + IParser terminator) => parser.Bind(x => parser.ManyTillRecursively(terminator, [x])); /// @@ -397,8 +397,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser SkipTill(this Parser parser, - Parser terminator) + public static IParser SkipTill(this IParser parser, + IParser terminator) => Fix(self => terminator | parser.Right(self)); /// @@ -412,8 +412,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Skip1Till(this Parser parser, - Parser terminator) + public static IParser Skip1Till(this IParser parser, + IParser terminator) => parser.Right(parser.SkipTill(terminator)); /// @@ -424,7 +424,7 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Match(this Parser parser) + public static IParser Match(this IParser parser) => SkipTill(Any(), parser); /// @@ -440,8 +440,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Quote(this Parser parser, - Parser left, Parser right) + public static IParser> Quote(this IParser parser, + IParser left, IParser right) => left.Right(parser.ManyTill(right)); /// @@ -454,8 +454,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Quote(this Parser parser, - Parser quotedParser) + public static IParser> Quote(this IParser parser, + IParser quotedParser) => parser.Quote(quotedParser, quotedParser); /// @@ -470,8 +470,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> SeparatedBy1(this Parser parser, - Parser separator) + public static IParser> SeparatedBy1(this IParser parser, + IParser separator) => parser.Bind(x => separator.Right(parser).ManyRecursively([x])); /// @@ -486,8 +486,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> SeparatedBy(this Parser parser, - Parser separator) + public static IParser> SeparatedBy(this IParser parser, + IParser separator) => parser.SeparatedBy1(separator).Try([]); /// @@ -502,8 +502,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> EndBy(this Parser parser, - Parser separator) + public static IParser> EndBy(this IParser parser, + IParser separator) => parser.Many().Left(separator); /// @@ -518,8 +518,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> EndBy1(this Parser parser, - Parser separator) + public static IParser> EndBy1(this IParser parser, + IParser separator) => parser.Many1().Left(separator); /// @@ -534,8 +534,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> SeparatedOrEndBy1(this Parser parser, - Parser separator) + public static IParser> SeparatedOrEndBy1(this IParser parser, + IParser separator) => parser.SeparatedBy1(separator).Left(separator.Try()); /// @@ -550,8 +550,8 @@ public static class ParserExtensions /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> SeparatedOrEndBy(this Parser parser, - Parser separator) + public static IParser> SeparatedOrEndBy(this IParser parser, + IParser separator) => parser.SeparatedOrEndBy1(separator).Try([]); #endregion @@ -559,13 +559,13 @@ public static class ParserExtensions #region LINQ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Select(this Parser parser, + public static IParser Select(this IParser parser, Func selector) => parser.Map(selector); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser SelectMany(this Parser parser, - Func> selector, Func projector) + public static IParser SelectMany(this IParser parser, + Func> selector, Func projector) => parser.Bind(x => selector(x).Map(y => projector(x, y))); #endregion diff --git a/CanonSharp.Combinator/ParseResultBuilder.cs b/CanonSharp.Combinator/ParseResultBuilder.cs index 3f09e0b..b6c616c 100644 --- a/CanonSharp.Combinator/ParseResultBuilder.cs +++ b/CanonSharp.Combinator/ParseResultBuilder.cs @@ -19,7 +19,7 @@ public static class ParseResultBuilder /// 解析成功的对象类型 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ParseResult Succeed(T value, TState state) + public static IParseResult Succeed(T value, TState state) where TState : IReadState => new InternalSuccessfulResult(value, state); @@ -32,7 +32,7 @@ public static class ParseResultBuilder /// 解析成功的对象类型 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ParseResult Fail(TState state) + public static IParseResult Fail(TState state) where TState : IReadState => new FailedResultWithError(state); @@ -47,7 +47,7 @@ public static class ParseResultBuilder /// 解析成功的对象类型 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ParseResult Fail(string message, TState state) + public static IParseResult Fail(string message, TState state) where TState : IReadState => new FailedResultWithMessage(message, state); @@ -61,7 +61,7 @@ public static class ParseResultBuilder /// 解析成功的对象类型 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ParseResult Fail(Exception exception, TState state) + public static IParseResult Fail(Exception exception, TState state) where TState : IReadState => new FailedResultWithException(exception, state); } diff --git a/CanonSharp.Combinator/ParserBuilder.cs b/CanonSharp.Combinator/ParserBuilder.cs index 5d84120..f2ce356 100644 --- a/CanonSharp.Combinator/ParserBuilder.cs +++ b/CanonSharp.Combinator/ParserBuilder.cs @@ -20,7 +20,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Pure(T value) + public static IParser Pure(T value) => new PureParser(value); /// @@ -31,7 +31,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Pure(Func, T> valueFunc) + public static IParser Pure(Func, T> valueFunc) => new DelayedPureParser(valueFunc); /// @@ -40,7 +40,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Null() => Pure(Unit.Instance); + public static IParser Null() => Pure(Unit.Instance); /// /// 失败的解析器 @@ -49,7 +49,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Fail() => new FailedParser(); + public static IParser Fail() => new FailedParser(); /// /// 失败的解析器 @@ -59,7 +59,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Fail(string message) => new FailedParserWithMessage(message); + public static IParser Fail(string message) => new FailedParserWithMessage(message); /// /// 失败的解析器 @@ -69,7 +69,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Fail(Func, string> messageFunc) => + public static IParser Fail(Func, string> messageFunc) => new FailedParserWithDelayedMessage(messageFunc); /// @@ -80,7 +80,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Fail(Exception exception) => + public static IParser Fail(Exception exception) => new FailedParserWithException(exception); /// @@ -90,7 +90,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Satisfy(Func predicate) + public static IParser Satisfy(Func predicate) => new SatisfyParser(predicate); /// @@ -99,7 +99,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Any() => Satisfy(_ => true); + public static IParser Any() => Satisfy(_ => true); /// /// 识别指定输入元素的解析器 @@ -108,7 +108,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Token(TToken token) + public static IParser Token(TToken token) => Satisfy(t => EqualityComparer.Default.Equals(t, token)); /// @@ -118,7 +118,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Skip(int count) => new SkipParser(count); + public static IParser Skip(int count) => new SkipParser(count); /// /// 识别指定数量输入元素的解析器 @@ -127,7 +127,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Take(int count) => new TakeParser(count); + public static IParser> Take(int count) => new TakeParser(count); #endregion @@ -141,7 +141,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Fix(Func, Parser> parserFix) + public static IParser Fix(Func, IParser> parserFix) => new FixParser(parserFix); #endregion @@ -156,8 +156,8 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Choice(IEnumerable> parsers) - => parsers.Reverse().Aggregate((next, parser) => parser.Alternative(next)); + public static IParser Choice(IEnumerable> parsers) + => parsers.Aggregate((result, parser) => result.Alternative(parser)); /// /// 按照给定的解析器组依次尝试 @@ -167,7 +167,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Choice(params Parser[] parsers) + public static IParser Choice(params IParser[] parsers) => Choice(parsers.AsEnumerable()); /// @@ -178,7 +178,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Sequence(IEnumerable> parsers) + public static IParser> Sequence(IEnumerable> parsers) => parsers.Reverse().Aggregate(Pure>([]), (next, parser) => parser.Bind( x => next.Map(result => result.Prepend(x)))); @@ -191,7 +191,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Sequence(params Parser[] parsers) + public static IParser> Sequence(params IParser[] parsers) => Sequence(parsers.AsEnumerable()); /// @@ -203,7 +203,7 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> TakeTill(Parser terminator) + public static IParser> TakeTill(IParser terminator) => Any().ManyTill(terminator); /// @@ -215,9 +215,8 @@ public static class ParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser> Take1Till(Parser termintor) + public static IParser> Take1Till(IParser termintor) => Any().Many1Till(termintor); - #endregion } diff --git a/CanonSharp.Combinator/Parsers/Bases/AlternativeParser.cs b/CanonSharp.Combinator/Parsers/Bases/AlternativeParser.cs index af68952..f4cfe1c 100644 --- a/CanonSharp.Combinator/Parsers/Bases/AlternativeParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/AlternativeParser.cs @@ -10,11 +10,12 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// 第二个解析器 /// 输入流类型 /// 解析器结果类型 -internal sealed class AlternativeParser(Parser first, Parser second) - : Parser +internal sealed class AlternativeParser(IParser first, IParser second) + : IParser { - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState { return first.Run(state, result => result.CaseOf(continuation, _ => second.Run(state, continuation))); } diff --git a/CanonSharp.Combinator/Parsers/Bases/BindParser.cs b/CanonSharp.Combinator/Parsers/Bases/BindParser.cs index 10be255..e6b42a1 100644 --- a/CanonSharp.Combinator/Parsers/Bases/BindParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/BindParser.cs @@ -11,10 +11,11 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// 上游解析器结果类型 /// 下游解析器结果类型 internal sealed class BindParser( - Parser parser, - Func> next) : Parser + IParser parser, + Func> next) : IParser { - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState => parser.Run(state, result => result.Next(next, continuation)); } diff --git a/CanonSharp.Combinator/Parsers/Bases/FixParser.cs b/CanonSharp.Combinator/Parsers/Bases/FixParser.cs index a8f3b3a..4edbc05 100644 --- a/CanonSharp.Combinator/Parsers/Bases/FixParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/FixParser.cs @@ -9,25 +9,17 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// /// /// -internal sealed class FixParser : Parser +internal sealed class FixParser : IParser { - private readonly Parser _parser; + private readonly IParser _parser; - public FixParser(Func, Parser> func) + public FixParser(Func, IParser> func) { _parser = func(this); } - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState => _parser.Run(state, continuation); } - -internal sealed class FixParser( - Func>, TParameter, Parser> func, - TParameter parameter) : Parser -{ - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) - => func(p => new FixParser(func, p), parameter).Run(state, continuation); -} diff --git a/CanonSharp.Combinator/Parsers/Bases/MapParser.cs b/CanonSharp.Combinator/Parsers/Bases/MapParser.cs index 0a94072..81ac998 100644 --- a/CanonSharp.Combinator/Parsers/Bases/MapParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/MapParser.cs @@ -12,10 +12,11 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// /// internal sealed class MapParser( - Parser parser, - Func func) : Parser + IParser parser, + Func func) : IParser { - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState => parser.Run(state, result => continuation(result.Map(func))); } diff --git a/CanonSharp.Combinator/Parsers/Bases/NextParser.cs b/CanonSharp.Combinator/Parsers/Bases/NextParser.cs index 67b4242..687a134 100644 --- a/CanonSharp.Combinator/Parsers/Bases/NextParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/NextParser.cs @@ -12,12 +12,13 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// 上游解析器结果类型 /// 最终解析结果类型 internal sealed class NextParser( - Parser parser, - Func> successfulParser, - Func, Parser> failedParser) : Parser + IParser parser, + Func> successfulParser, + Func, IParser> failedParser) : IParser { - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState { return parser.Run(state, result => result.CaseOf( successfulResult => successfulResult.Next(successfulParser, continuation), diff --git a/CanonSharp.Combinator/Parsers/Bases/ResumeParser.cs b/CanonSharp.Combinator/Parsers/Bases/ResumeParser.cs index 9cf4d76..f9c8c75 100644 --- a/CanonSharp.Combinator/Parsers/Bases/ResumeParser.cs +++ b/CanonSharp.Combinator/Parsers/Bases/ResumeParser.cs @@ -11,11 +11,12 @@ namespace CanonSharp.Combinator.Parsers.Bases; /// 输入令牌类型 /// 解析结果类型 internal sealed class ResumeParser( - Parser parser, - Func, Parser> failedHandler) : Parser + IParser parser, + Func, IParser> failedHandler) : IParser { - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState { return parser.Run(state, result => result.CaseOf(continuation, diff --git a/CanonSharp.Combinator/Parsers/ModifiedParser.cs b/CanonSharp.Combinator/Parsers/ModifiedParser.cs index 6a70c79..7b5e4fb 100644 --- a/CanonSharp.Combinator/Parsers/ModifiedParser.cs +++ b/CanonSharp.Combinator/Parsers/ModifiedParser.cs @@ -9,18 +9,20 @@ namespace CanonSharp.Combinator.Parsers; /// 输入流类型 /// 需要修改结果的解析器 /// 最终返回的解析结果 -public abstract class ModifiedParser(Parser parser) : Parser +public abstract class ModifiedParser(IParser parser) + : IParser { - protected abstract ParseResult Fail(TState state, - FailedResult failedResult) + protected abstract IParseResult Fail(TState state, + IFailedResult failedResult) where TState : IReadState; - protected abstract ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected abstract IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) where TState : IReadState; - internal override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState => parser.Run(state, result => result.CaseOf( success => continuation(Succeed(state, success)), failure => continuation(Fail(state, failure)))); diff --git a/CanonSharp.Combinator/Parsers/Modifiers/DoParser.cs b/CanonSharp.Combinator/Parsers/Modifiers/DoParser.cs index 332bb87..0af8683 100644 --- a/CanonSharp.Combinator/Parsers/Modifiers/DoParser.cs +++ b/CanonSharp.Combinator/Parsers/Modifiers/DoParser.cs @@ -11,18 +11,18 @@ namespace CanonSharp.Combinator.Parsers.Modifiers; /// 输入流类型 /// 解析结果类型 internal sealed class DoParser( - Parser parser, + IParser parser, Action succeed, - Action> fail) : ModifiedParser(parser) + Action> fail) : ModifiedParser(parser) { - protected override ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected override IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) { succeed(successfulResult.Value); return successfulResult; } - protected override ParseResult Fail(TState state, FailedResult failedResult) + protected override IParseResult Fail(TState state, IFailedResult failedResult) { fail(failedResult); return failedResult; diff --git a/CanonSharp.Combinator/Parsers/Modifiers/LookAheadParser.cs b/CanonSharp.Combinator/Parsers/Modifiers/LookAheadParser.cs index 7210a6c..5ee592c 100644 --- a/CanonSharp.Combinator/Parsers/Modifiers/LookAheadParser.cs +++ b/CanonSharp.Combinator/Parsers/Modifiers/LookAheadParser.cs @@ -10,12 +10,12 @@ namespace CanonSharp.Combinator.Parsers.Modifiers; /// 需要向前看的解析器 /// 输入流令牌 /// 返回的解析结果类型 -internal sealed class LookAheadParser(Parser parser) : ModifiedParser(parser) +internal sealed class LookAheadParser(IParser parser) : ModifiedParser(parser) { - protected override ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected override IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) => ParseResultBuilder.Succeed(successfulResult.Value, state); - protected override ParseResult Fail(TState state, FailedResult failedResult) + protected override IParseResult Fail(TState state, IFailedResult failedResult) => ParseResultBuilder.Fail($"Failed when looking ahead: {failedResult}", state); } diff --git a/CanonSharp.Combinator/Parsers/Modifiers/ReverseParser.cs b/CanonSharp.Combinator/Parsers/Modifiers/ReverseParser.cs index 14655f5..c6518bc 100644 --- a/CanonSharp.Combinator/Parsers/Modifiers/ReverseParser.cs +++ b/CanonSharp.Combinator/Parsers/Modifiers/ReverseParser.cs @@ -12,15 +12,15 @@ namespace CanonSharp.Combinator.Parsers.Modifiers; /// 输入流的类型 /// 上游解析器结果类型 /// 最终的返回结果 -internal sealed class ReverseParser(Parser parser, T result) +internal sealed class ReverseParser(IParser parser, T result) : ModifiedParser(parser) { - protected override ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected override IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) => ParseResultBuilder.Fail($"Unexpected successful result: {successfulResult.Value}", state); - protected override ParseResult Fail(TState state, - FailedResult failedResult) + protected override IParseResult Fail(TState state, + IFailedResult failedResult) => ParseResultBuilder.Succeed(result, state); } diff --git a/CanonSharp.Combinator/Parsers/Modifiers/SuccessfulMapParser.cs b/CanonSharp.Combinator/Parsers/Modifiers/SuccessfulMapParser.cs index cfb4e0c..eb38a4d 100644 --- a/CanonSharp.Combinator/Parsers/Modifiers/SuccessfulMapParser.cs +++ b/CanonSharp.Combinator/Parsers/Modifiers/SuccessfulMapParser.cs @@ -12,15 +12,15 @@ namespace CanonSharp.Combinator.Parsers.Modifiers; /// 上游解析器解析结果类型 /// 最终的解析结果类型 internal sealed class SuccessfulMapParser( - Parser parser, + IParser parser, Func successfulHandler, - Func, T> failedHandler) : ModifiedParser(parser) + Func, T> failedHandler) : ModifiedParser(parser) { - protected override ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected override IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) => successfulResult.Map(successfulHandler); - protected override ParseResult Fail(TState state, - FailedResult failedResult) + protected override IParseResult Fail(TState state, + IFailedResult failedResult) => ParseResultBuilder.Succeed(failedHandler(failedResult), state); } diff --git a/CanonSharp.Combinator/Parsers/Modifiers/TryParser.cs b/CanonSharp.Combinator/Parsers/Modifiers/TryParser.cs index 88c0ab6..cc87e13 100644 --- a/CanonSharp.Combinator/Parsers/Modifiers/TryParser.cs +++ b/CanonSharp.Combinator/Parsers/Modifiers/TryParser.cs @@ -11,13 +11,13 @@ namespace CanonSharp.Combinator.Parsers.Modifiers; /// 处理失败结果的恢复函数 /// 输入流令牌 /// 解析器返回结果类型 -internal sealed class TryParser(Parser parser, Func, T> resume) +internal sealed class TryParser(IParser parser, Func, T> resume) : ModifiedParser(parser) { - protected override ParseResult Succeed(TState state, - SuccessfulResult successfulResult) + protected override IParseResult Succeed(TState state, + ISuccessfulResult successfulResult) => successfulResult; - protected override ParseResult Fail(TState state, FailedResult failedResult) + protected override IParseResult Fail(TState state, IFailedResult failedResult) => ParseResultBuilder.Succeed(resume(failedResult), state); } diff --git a/CanonSharp.Combinator/Parsers/PrimitiveParser.cs b/CanonSharp.Combinator/Parsers/PrimitiveParser.cs index c53fb82..10c5b1c 100644 --- a/CanonSharp.Combinator/Parsers/PrimitiveParser.cs +++ b/CanonSharp.Combinator/Parsers/PrimitiveParser.cs @@ -8,7 +8,7 @@ namespace CanonSharp.Combinator.Parsers; /// /// 输入流类型 /// 解析结果的类型 -public abstract class PrimitiveParser : Parser +public abstract class PrimitiveParser : IParser { /// /// 运行解析器 返回解析结果 @@ -16,10 +16,11 @@ public abstract class PrimitiveParser : Parser /// 当前输入流的状态 /// 输入流状态的类型 /// 解析结果 - protected abstract ParseResult Run(TState state) + protected abstract IParseResult Run(TState state) where TState : IReadState; - internal sealed override ParseResult Run(TState state, - Func, ParseResult> continuation) + public IParseResult Run(TState state, + Func, IParseResult> continuation) + where TState : IReadState => continuation(Run(state)); } diff --git a/CanonSharp.Combinator/Parsers/Primitives/FailedParser.cs b/CanonSharp.Combinator/Parsers/Primitives/FailedParser.cs index 4477e9c..7f746c1 100644 --- a/CanonSharp.Combinator/Parsers/Primitives/FailedParser.cs +++ b/CanonSharp.Combinator/Parsers/Primitives/FailedParser.cs @@ -9,7 +9,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives; /// 解析结果的类型 internal sealed class FailedParser : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Fail(state); } @@ -21,7 +21,7 @@ internal sealed class FailedParser : PrimitiveParser /// 解析结果的类型 internal sealed class FailedParserWithMessage(string message) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Fail(message, state); } @@ -34,7 +34,7 @@ internal sealed class FailedParserWithMessage(string message) : Primi internal sealed class FailedParserWithDelayedMessage(Func, string> messageFunc) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Fail(messageFunc(state), state); } @@ -46,6 +46,6 @@ internal sealed class FailedParserWithDelayedMessage(Func解析结果的类型 internal sealed class FailedParserWithException(Exception e) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Fail(e, state); } diff --git a/CanonSharp.Combinator/Parsers/Primitives/PureParser.cs b/CanonSharp.Combinator/Parsers/Primitives/PureParser.cs index 6ded706..dcead7a 100644 --- a/CanonSharp.Combinator/Parsers/Primitives/PureParser.cs +++ b/CanonSharp.Combinator/Parsers/Primitives/PureParser.cs @@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives; /// 解析成功返回值的类型 internal sealed class PureParser(T value) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Succeed(value, state); } @@ -22,6 +22,6 @@ internal sealed class PureParser(T value) : PrimitiveParser解析成功返回值的类型 internal sealed class DelayedPureParser(Func, T> valueFunc) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) => ParseResultBuilder.Succeed(valueFunc(state), state); } diff --git a/CanonSharp.Combinator/Parsers/Primitives/SatisfyParser.cs b/CanonSharp.Combinator/Parsers/Primitives/SatisfyParser.cs index 25ec82c..37805f6 100644 --- a/CanonSharp.Combinator/Parsers/Primitives/SatisfyParser.cs +++ b/CanonSharp.Combinator/Parsers/Primitives/SatisfyParser.cs @@ -9,7 +9,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives; /// 输入流类型 internal sealed class SatisfyParser(Func predicate) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) { return state.HasValue && predicate(state.Current) ? ParseResultBuilder.Succeed(state.Current, state.Next) diff --git a/CanonSharp.Combinator/Parsers/Primitives/SkipParser.cs b/CanonSharp.Combinator/Parsers/Primitives/SkipParser.cs index ffa1061..bb82c81 100644 --- a/CanonSharp.Combinator/Parsers/Primitives/SkipParser.cs +++ b/CanonSharp.Combinator/Parsers/Primitives/SkipParser.cs @@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives; /// 输入流类型 internal sealed class SkipParser(int count) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) { List result = state.AsEnumerable().Take(count).ToList(); diff --git a/CanonSharp.Combinator/Parsers/Primitives/TakeParser.cs b/CanonSharp.Combinator/Parsers/Primitives/TakeParser.cs index 670c7c0..fe876d0 100644 --- a/CanonSharp.Combinator/Parsers/Primitives/TakeParser.cs +++ b/CanonSharp.Combinator/Parsers/Primitives/TakeParser.cs @@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives; /// 输入流类型 internal sealed class TakeParser(int count) : PrimitiveParser> { - protected override ParseResult> Run(TState state) + protected override IParseResult> Run(TState state) { List result = state.AsEnumerable().Take(count).ToList(); diff --git a/CanonSharp.Combinator/Results/FailedResultWithError.cs b/CanonSharp.Combinator/Results/FailedResultWithError.cs index 14efb87..16a6321 100644 --- a/CanonSharp.Combinator/Results/FailedResultWithError.cs +++ b/CanonSharp.Combinator/Results/FailedResultWithError.cs @@ -8,15 +8,17 @@ namespace CanonSharp.Combinator.Results; /// 输入流的类型 /// 输入流的读取类型 /// 实际的结果类型 -internal sealed class FailedResultWithError(TState state) : FailedResult +internal sealed class FailedResultWithError(TState state) : IFailedResult where TState : IReadState { - public override IReadState State => state; + public IReadState State => state; - public override string Message => $"Unexpected state: {state}."; + public string Message => $"Unexpected state: {state}."; - public override FailedResult Convert() + public IFailedResult Convert() { return new FailedResultWithError(state); } + + public override string ToString() => $"Parse failed: {Message}."; } diff --git a/CanonSharp.Combinator/Results/FailedResultWithException.cs b/CanonSharp.Combinator/Results/FailedResultWithException.cs index 85005c5..8c228d0 100644 --- a/CanonSharp.Combinator/Results/FailedResultWithException.cs +++ b/CanonSharp.Combinator/Results/FailedResultWithException.cs @@ -10,15 +10,18 @@ namespace CanonSharp.Combinator.Results; /// 输入流类型 /// 当前输入流状态的类型 /// 解析结果的类型 -public class FailedResultWithException(Exception exception, TState state) : FailedResult +internal sealed class FailedResultWithException(Exception exception, TState state) + : IFailedResult where TState : IReadState { - public override IReadState State => state; + public IReadState State => state; - public override ParseException Exception => new(ToString(), exception); + public ParseException Exception => new(Message, exception); - public override string Message => $"Exception occured: {exception}."; + public string Message => $"Exception occured: {exception}."; - public override FailedResult Convert() + public IFailedResult Convert() => new FailedResultWithException(exception, state); + + public override string ToString() => $"Parse failed: {Message}."; } diff --git a/CanonSharp.Combinator/Results/FailedResultWithMessage.cs b/CanonSharp.Combinator/Results/FailedResultWithMessage.cs index d28bc2f..dd4f829 100644 --- a/CanonSharp.Combinator/Results/FailedResultWithMessage.cs +++ b/CanonSharp.Combinator/Results/FailedResultWithMessage.cs @@ -10,15 +10,18 @@ namespace CanonSharp.Combinator.Results; /// 输入流的类型 /// 读取状态类型 /// 解析结果的类型 -internal sealed class FailedResultWithMessage(string message, TState state) : FailedResult +internal sealed class FailedResultWithMessage(string message, TState state) + : IFailedResult where TState : IReadState { - public override IReadState State => state; + public IReadState State => state; - public override string Message => message; + public string Message => message; - public override FailedResult Convert() + public IFailedResult Convert() { return new FailedResultWithMessage(message, state); } + + public override string ToString() => $"Parse failed: {Message}."; } diff --git a/CanonSharp.Combinator/Results/InternalSuccessfulResult.cs b/CanonSharp.Combinator/Results/InternalSuccessfulResult.cs index 3934453..c6c1ce2 100644 --- a/CanonSharp.Combinator/Results/InternalSuccessfulResult.cs +++ b/CanonSharp.Combinator/Results/InternalSuccessfulResult.cs @@ -11,13 +11,17 @@ namespace CanonSharp.Combinator.Results; /// 输入流的状态类型 /// 解析结果的类型 internal sealed class InternalSuccessfulResult(T result, TState state) - : SuccessfulResult(result) + : ISuccessfulResult where TState : IReadState { - protected override ParseResult RunNext(Parser parser, - Func, ParseResult> continuation) + public T Value => result; + + public IParseResult RunNext(IParser parser, + Func, IParseResult> continuation) => parser.Run(state, continuation); - public override ParseResult Map(Func map) + public IParseResult Map(Func map) => new InternalSuccessfulResult(map(Value), state); + + public override string ToString() => Value?.ToString() ?? string.Empty; } diff --git a/CanonSharp.Combinator/Text/StringParser.cs b/CanonSharp.Combinator/Text/StringParser.cs index 1d20dd8..9d67174 100644 --- a/CanonSharp.Combinator/Text/StringParser.cs +++ b/CanonSharp.Combinator/Text/StringParser.cs @@ -11,7 +11,7 @@ namespace CanonSharp.Combinator.Text; /// 字符串比较模式 public class StringParser(string except, StringComparison comparison) : PrimitiveParser { - protected override ParseResult Run(TState state) + protected override IParseResult Run(TState state) { TState[] states = state.AsEnumerable().Take(except.Length).ToArray(); string actual = new(states.Select(x => x.Current).ToArray()); diff --git a/CanonSharp.Combinator/Text/TextParserBuilder.cs b/CanonSharp.Combinator/Text/TextParserBuilder.cs index 2ca83db..921df85 100644 --- a/CanonSharp.Combinator/Text/TextParserBuilder.cs +++ b/CanonSharp.Combinator/Text/TextParserBuilder.cs @@ -13,7 +13,7 @@ public static class TextParserBuilder /// 识别的单个字符 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Char(char token) => Satisfy(x => x == token); + public static IParser Char(char token) => Satisfy(x => x == token); /// /// 忽略大小写识别单个字符 @@ -21,7 +21,7 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser CharIgnoreCase(char token) => + public static IParser CharIgnoreCase(char token) => Satisfy(x => char.ToUpperInvariant(x) == char.ToUpperInvariant(token)); /// @@ -30,7 +30,7 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser OneOf(string candidate) => Satisfy(candidate.Contains); + public static IParser OneOf(string candidate) => Satisfy(candidate.Contains); /// /// 忽略大小写识别字符串中的一个字符 @@ -38,7 +38,7 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser OneOfIgnoreCase(string candidate) => + public static IParser OneOfIgnoreCase(string candidate) => Satisfy(x => candidate.Contains(x, StringComparison.OrdinalIgnoreCase)); /// @@ -48,7 +48,7 @@ public static class TextParserBuilder /// 字符串比较方法 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser String(string except, StringComparison comparison) => + public static IParser String(string except, StringComparison comparison) => new StringParser(except, comparison); /// @@ -57,7 +57,7 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser String(string except) => String(except, StringComparison.Ordinal); + public static IParser String(string except) => String(except, StringComparison.Ordinal); /// /// 忽略大小写识别一个字符串 @@ -65,7 +65,7 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser StringIgnoreCase(string except) => + public static IParser StringIgnoreCase(string except) => String(except, StringComparison.OrdinalIgnoreCase); /// @@ -75,28 +75,28 @@ public static class TextParserBuilder /// 包括的终止字符 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Range(char start, char end) => Satisfy(x => x >= start && x <= end); + public static IParser Range(char start, char end) => Satisfy(x => x >= start && x <= end); /// /// 识别Unicode字符类别的解析器 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Letter() => Satisfy(char.IsLetter); + public static IParser Letter() => Satisfy(char.IsLetter); /// /// 识别Unicode数字类别的解析器 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Digit() => Satisfy(char.IsDigit); + public static IParser Digit() => Satisfy(char.IsDigit); /// /// 识别ASCII字符类别的解析器 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser AsciiLetter() => + public static IParser AsciiLetter() => Satisfy(x => x is >= 'a' and <= 'z' or >= 'A' and <= 'Z'); /// @@ -104,20 +104,20 @@ public static class TextParserBuilder /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser AsciiDigit() => Satisfy(x => x is >= '0' and <= '9'); + public static IParser AsciiDigit() => Satisfy(x => x is >= '0' and <= '9'); /// /// 识别Unicode空白类型的字符 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser Space() => Satisfy(char.IsWhiteSpace); + public static IParser Space() => Satisfy(char.IsWhiteSpace); /// /// 识别所有的换行符 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Parser LineBreak() => + public static IParser LineBreak() => OneOf("\u000D\u000A\u0085\u2028\u2029\n").Map(x => x.ToString()) | String("\r\n"); } diff --git a/CanonSharp.Combinator/Text/TextParserExtensions.cs b/CanonSharp.Combinator/Text/TextParserExtensions.cs index 77df384..09516aa 100644 --- a/CanonSharp.Combinator/Text/TextParserExtensions.cs +++ b/CanonSharp.Combinator/Text/TextParserExtensions.cs @@ -6,9 +6,9 @@ namespace CanonSharp.Combinator.Text; public static class TextParserExtensions { - public static Parser SkipSpaces(this Parser parser) + public static IParser SkipSpaces(this IParser parser) => Space().SkipTill(parser); - public static Parser SkipSpaceAndLineBreak(this Parser parser) + public static IParser SkipSpaceAndLineBreak(this IParser parser) => (Space().Map(x => x.ToString()) | LineBreak()).SkipTill(parser); } diff --git a/CanonSharp.Common/CanonSharp.Common.csproj b/CanonSharp.Pascal/CanonSharp.Pascal.csproj similarity index 89% rename from CanonSharp.Common/CanonSharp.Common.csproj rename to CanonSharp.Pascal/CanonSharp.Pascal.csproj index fbfc2bd..f2b8c9f 100644 --- a/CanonSharp.Common/CanonSharp.Common.csproj +++ b/CanonSharp.Pascal/CanonSharp.Pascal.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + CanonSharp.Common diff --git a/CanonSharp.Pascal/Parser/GrammarParser.cs b/CanonSharp.Pascal/Parser/GrammarParser.cs new file mode 100644 index 0000000..78bd12e --- /dev/null +++ b/CanonSharp.Pascal/Parser/GrammarParser.cs @@ -0,0 +1,392 @@ +using CanonSharp.Combinator.Abstractions; +using CanonSharp.Combinator.Extensions; +using static CanonSharp.Combinator.ParserBuilder; +using CanonSharp.Pascal.Scanner; +using CanonSharp.Pascal.SyntaxTree; + +namespace CanonSharp.Pascal.Parser; + +public sealed class GrammarParser : GrammarParserBuilder +{ + public static IParser FactorParser() + { + // factor -> - factor | + factor + IParser minusParser = + from _ in Operator("-") + from node in FactorParser() + select new UnaryOperatorNode(UnaryOperatorType.Minus, node); + + IParser plusParser = + from _ in Operator("+") + from node in FactorParser() + select new UnaryOperatorNode(UnaryOperatorType.Plus, node); + + IParser notParser = + from _ in Keyword("not") + from node in FactorParser() + select new UnaryOperatorNode(UnaryOperatorType.Not, node); + + IParser parenthesisParser = + from _1 in Delimiter("(") + from node in ExpressionParser() + from _2 in Delimiter(")") + select node; + + IParser procedureCallParser = + from identifier in IdentifierParser() + from _ in Delimiter("(") + from expressions in ExpressionParser().SeparatedBy(Delimiter(",")) + from _1 in Delimiter(")") + select new ProcedureCallNode(identifier, expressions); + + return Choice( + TrueParser(), + FalseParser(), + NumberParser(), + minusParser, + plusParser, + notParser, + procedureCallParser, + VariableParser(), + parenthesisParser + ); + } + + private static IParser TermRecursively(SyntaxNodeBase left) + { + // MultiplyOperator -> * | / | div | mod | and + IParser multiplyOperator = Choice( + from _ in Operator("*") + select BinaryOperatorType.Multiply, + from _ in Operator("/") + select BinaryOperatorType.Divide, + from _ in Keyword("div") + select BinaryOperatorType.IntegerDivide, + from _ in Keyword("mod") + select BinaryOperatorType.Mod, + from _ in Keyword("and") + select BinaryOperatorType.And); + + return multiplyOperator.Next(op => + { + return FactorParser().Map(right => new BinaryOperatorNode(op, left, right)) + .Bind(TermRecursively); + }, left); + } + + public static IParser TermParser() + { + // Term -> Factor | Term MultiplyOperator Factor + // 消除左递归为 + // Term -> Factor Term' + // Term' -> MultiplyOperator Factor Term' | ε + return FactorParser().Bind(TermRecursively); + } + + private static IParser SimpleExpressionRecursively(SyntaxNodeBase left) + { + // AddOperator -> + | - | or + IParser addOperator = Choice( + from _ in Operator("+") + select BinaryOperatorType.Add, + from _ in Operator("-") + select BinaryOperatorType.Subtract, + from _ in Keyword("or") + select BinaryOperatorType.Or); + + return addOperator.Next(op => + { + return TermParser().Map(right => new BinaryOperatorNode(op, left, right)) + .Bind(SimpleExpressionRecursively); + }, left); + } + + public static IParser SimpleExpressionParser() + { + // SimpleExpression -> Term | SimpleExpression AddOperator Term + // 消除左递归为 + // SimpleExpression -> Term SimpleExpression' + // SimpleExpression' -> AddOperator Term SimpleExpression' | ε + return TermParser().Bind(SimpleExpressionRecursively); + } + + public static IParser ExpressionParser() + { + // RelationOperator -> = | <> | < | <= | > | >= + IParser relationOperator = Choice( + from _ in Operator("=") + select BinaryOperatorType.Equal, + from _ in Operator("<>") + select BinaryOperatorType.NotEqual, + from _ in Operator("<") + select BinaryOperatorType.Less, + from _ in Operator("<=") + select BinaryOperatorType.LessEqual, + from _ in Operator(">") + select BinaryOperatorType.Greater, + from _ in Operator(">=") + select BinaryOperatorType.GreaterEqual); + + // Expression -> SimpleExpression | SimpleExpression RelationOperator SimpleExpression + return Choice( + from left in SimpleExpressionParser() + from op in relationOperator + from right in SimpleExpressionParser() + select new BinaryOperatorNode(op, left, right), + SimpleExpressionParser() + ); + } + + /// + /// ExpressionList Parser + /// ExpressionList -> Expression | ExpressionList , Expression + /// + /// + public static IParser> ExpressionsParser() + => ExpressionParser().SeparatedBy1(Delimiter(",")); + + public static IParser VariableParser() + { + return Choice( + from token in IdentifierParser() + from _ in Delimiter("[") + from expressions in ExpressionsParser() + from _1 in Delimiter("]") + select new VariableNode(token, expressions), + from token in IdentifierParser() + select new VariableNode(token) + ); + } + + public static IParser IfParser() + { + IParser commonPart = from _ in Keyword("if") + from condition in ExpressionParser() + from _1 in Keyword("then") + from statement in StatementParser() + select new IfNode(condition, statement); + + return Choice( + from common in commonPart + from _ in Keyword("else") + from elseStatement in StatementParser() + select new IfNode(common.Condition, common.Statement, elseStatement), + commonPart + ); + } + + public static IParser ForParser() + { + return from _ in Keyword("for") + from identifier in IdentifierParser() + from _1 in Operator(":=") + from left in ExpressionParser() + from _2 in Keyword("to") + from right in ExpressionParser() + from _3 in Keyword("do") + from statement in StatementParser() + select new ForNode(identifier, left, right, statement); + } + + public static IParser WhileParser() + { + return from _ in Keyword("while") + from condition in ExpressionParser() + from _1 in Keyword("do") + from statement in StatementParser() + select new WhileNode(condition, statement); + } + + public static IParser ProcedureCallParser() + { + return Choice( + from identifier in IdentifierParser() + from _ in Delimiter("(") + from expressions in ExpressionParser().SeparatedBy(Delimiter(",")) + from _1 in Delimiter(")") + select new ProcedureCallNode(identifier, expressions), + from identifier in IdentifierParser() + select new ProcedureCallNode(identifier, []) + ); + } + + public static IParser StatementParser() + { + return Choice( + from variable in VariableParser() + from _ in Operator(":=") + from expression in ExpressionParser() + select new AssignNode(variable, expression), + ProcedureCallParser(), + IfParser(), + ForParser(), + WhileParser(), + CompoundStatementParser() + ); + } + + public static IParser CompoundStatementParser() + { + return from _1 in Keyword("begin") + from statements in StatementParser().SeparatedOrEndBy(Delimiter(";")) + from _2 in Keyword("end") + select new BlockNode(statements); + } + + public static IParser ConstValueParser() + { + return Choice( + from _ in Operator("-") + from num in NumberParser() + select new UnaryOperatorNode(UnaryOperatorType.Minus, num), + from _ in Operator("+") + from num in NumberParser() + select new UnaryOperatorNode(UnaryOperatorType.Plus, num), + NumberParser(), + CharParser(), + TrueParser(), + FalseParser() + ); + } + + public static IParser ConstDeclarationParser() + { + return from identifier in Satisfy(token => + token.TokenType == LexicalTokenType.Identifier) + from _ in Operator("=") + from node in ConstValueParser() + select new ConstantNode(identifier, node); + } + + public static IParser ConstDeclarationsParser() + { + return (from _ in Keyword("const") + from tokens in ConstDeclarationParser().SeparatedOrEndBy1(Delimiter(";")) + select new BlockNode(tokens)).Try(new BlockNode([])); + } + + public static IParser ArrayTypeParser() + { + IParser> arrayRangeParser = ( + from left in IntegerParser() + from _ in Delimiter("..") + from right in IntegerParser() + select new ArrayRange(left.Value, right.Value)).SeparatedBy1(Delimiter(",")); + + return from _ in Keyword("array") + from _1 in Delimiter("[") + from ranges in arrayRangeParser + from _2 in Delimiter("]") + from _3 in Keyword("of") + from typeToken in BasicTypeParser() + select new TypeNode(typeToken, ranges); + } + + public static IParser TypeParser() + { + return Choice(ArrayTypeParser(), + from token in BasicTypeParser() + select new TypeNode(token)); + } + + public static IParser VariableDeclarationParser() + { + return from tokens in Satisfy( + token => token.TokenType == LexicalTokenType.Identifier).SeparatedBy1(Delimiter(",")) + from _1 in Delimiter(":") + from type in TypeParser() + select new VariableDeclarationNode(tokens, type.Convert()); + } + + public static IParser VariableDeclarationsParser() + { + return (from _ in Keyword("var") + from nodes in VariableDeclarationParser().SeparatedOrEndBy1(Delimiter(";")) + select new BlockNode(nodes)).Try(new BlockNode([])); + } + + public static IParser> ParameterParser() + { + return Choice( + from _ in Keyword("var") + from tokens in IdentifierParser().SeparatedBy1(Delimiter(",")) + from _1 in Delimiter(":") + from typeToken in TypeParser() + select tokens.Select(x => new Parameter(true, x, typeToken)), + from tokens in IdentifierParser().SeparatedBy1(Delimiter(",")) + from _ in Delimiter(":") + from typeToken in TypeParser() + select tokens.Select(x => new Parameter(false, x, typeToken)) + ); + } + + public static IParser> FormalParameterParser() + { + return (from _ in Delimiter("(") + from parameters in ParameterParser().SeparatedBy(Delimiter(";")) + from _1 in Delimiter(")") + select parameters.Aggregate(new List(), (result, array) => + { + result.AddRange(array); + return result; + })).Try([]); + } + + public static IParser SubprogramHeadParser() + { + return Choice( + from _ in Keyword("procedure") + from identifier in IdentifierParser() + from parameters in FormalParameterParser() + select new SubprogramHead(identifier, parameters), + from _ in Keyword("function") + from identifier in IdentifierParser() + from parameters in FormalParameterParser() + from _1 in Delimiter(":") + from typeToken in TypeParser() + select new SubprogramHead(identifier, parameters, typeToken) + ); + } + + public static IParser SubprogramBodyParser() + { + return from constant in ConstDeclarationsParser() + from variables in VariableDeclarationsParser() + from block in CompoundStatementParser() + select new SubprogramBody(constant, variables, block); + } + + public static IParser SubprogramParser() + { + return from head in SubprogramHeadParser() + from _ in Delimiter(";") + from body in SubprogramBodyParser() + select new Subprogram(head, body); + } + + public static IParser ProgramBodyParser() + { + return from constant in ConstDeclarationsParser() + from variables in VariableDeclarationsParser() + from subprograms in SubprogramParser().SeparatedOrEndBy(Delimiter(";")) + .Map(x => new BlockNode(x)) + from block in CompoundStatementParser() + select new ProgramBody(constant, variables, subprograms, block); + } + + public static IParser ProgramHeadParser() + { + return from _ in Keyword("program") + from token in Satisfy(token => token.TokenType == LexicalTokenType.Identifier) + select new ProgramHead(token); + } + + public static IParser ProgramParser() + { + return from head in ProgramHeadParser() + from _1 in Delimiter(";") + from body in ProgramBodyParser() + from _2 in Delimiter(".") + select new Program(head, body); + } +} diff --git a/CanonSharp.Pascal/Parser/GrammarParserBase.cs b/CanonSharp.Pascal/Parser/GrammarParserBase.cs new file mode 100644 index 0000000..c3be8a6 --- /dev/null +++ b/CanonSharp.Pascal/Parser/GrammarParserBase.cs @@ -0,0 +1,70 @@ +using CanonSharp.Combinator.Abstractions; +using CanonSharp.Combinator.Extensions; +using CanonSharp.Pascal.Scanner; +using CanonSharp.Pascal.SyntaxTree; +using static CanonSharp.Combinator.ParserBuilder; + +namespace CanonSharp.Pascal.Parser; + +public abstract class GrammarParserBuilder +{ + protected static IParser Keyword(string value) + => Satisfy(token => + token.TokenType == LexicalTokenType.Keyword && + token.LiteralValue.Equals(value, StringComparison.OrdinalIgnoreCase)); + + protected static IParser Operator(string value) + => Satisfy(token => token.TokenType == LexicalTokenType.Operator && token.LiteralValue == value); + + protected static IParser Delimiter(string value) + => Satisfy(token => token.TokenType == LexicalTokenType.Delimiter && token.LiteralValue == value); + + protected static IParser TrueParser() + { + return from _ in Keyword("true") + select new BooleanValueNode(true); + } + + protected static IParser FalseParser() + { + return from _ in Keyword("false") + select new BooleanValueNode(false); + } + + protected static IParser IntegerParser() + { + return from token in Satisfy(token => token.TokenType == LexicalTokenType.ConstInteger) + select new IntegerValueNode(int.Parse(token.LiteralValue)); + } + + protected static IParser NumberParser() + { + return Choice( + from token in Satisfy(x => x.TokenType == LexicalTokenType.ConstInteger) + select new IntegerValueNode(int.Parse(token.LiteralValue)), + from token in Satisfy(x => x.TokenType == LexicalTokenType.ConstFloat) + select new FloatValueNode(double.Parse(token.LiteralValue)) + ); + } + + protected static IParser CharParser() + { + return Satisfy(token => token.TokenType == LexicalTokenType.Character) + .Map(x => new CharValueNode(char.Parse(x.LiteralValue))); + } + + protected static IParser BasicTypeParser() + { + return Choice( + Keyword("integer"), + Keyword("real"), + Keyword("boolean"), + Keyword("char") + ); + } + + protected static IParser IdentifierParser() + { + return Satisfy(token => token.TokenType == LexicalTokenType.Identifier); + } +} diff --git a/CanonSharp.Pascal/Parser/LexicalTokenReadState.cs b/CanonSharp.Pascal/Parser/LexicalTokenReadState.cs new file mode 100644 index 0000000..85be52e --- /dev/null +++ b/CanonSharp.Pascal/Parser/LexicalTokenReadState.cs @@ -0,0 +1,53 @@ +using CanonSharp.Combinator.Abstractions; +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.Parser; + +public sealed class LexicalTokenReadState : IReadState +{ + private readonly List _tokens; + private readonly int _pos; + + public LexicalToken Current => _tokens[_pos]; + + public bool HasValue => _pos < _tokens.Count; + + public LexicalTokenReadState Next => new(_tokens, _pos + 1); + + private LexicalTokenReadState(List tokens, int pos) + { + _tokens = tokens; + _pos = pos; + } + + public LexicalTokenReadState(IEnumerable tokens) + { + _tokens = tokens.ToList(); + _pos = 0; + } + + public bool Equals(LexicalTokenReadState? other) + { + if (other is null) + { + return false; + } + + if (_tokens.Count != other._tokens.Count) + { + return false; + } + + foreach ((LexicalToken first, LexicalToken second) in _tokens.Zip(other._tokens)) + { + if (!first.Equals(second)) + { + return false; + } + } + + return _pos == other._pos; + } + + public override string ToString() => HasValue ? Current.ToString() : "End of input stream."; +} diff --git a/CanonSharp.Common/Scanner/LexicalScanner.cs b/CanonSharp.Pascal/Scanner/LexicalScanner.cs similarity index 74% rename from CanonSharp.Common/Scanner/LexicalScanner.cs rename to CanonSharp.Pascal/Scanner/LexicalScanner.cs index 4ad4c1e..6bf869f 100644 --- a/CanonSharp.Common/Scanner/LexicalScanner.cs +++ b/CanonSharp.Pascal/Scanner/LexicalScanner.cs @@ -4,11 +4,11 @@ using CanonSharp.Combinator.Extensions; using static CanonSharp.Combinator.Text.TextParserBuilder; using static CanonSharp.Combinator.ParserBuilder; -namespace CanonSharp.Common.Scanner; +namespace CanonSharp.Pascal.Scanner; public sealed class LexicalScanner { - private readonly Parser> _parser = PascalParser(); + private readonly IParser> _parser = PascalParser(); public IEnumerable Tokenize(TState state) where TState : IReadState @@ -16,7 +16,7 @@ public sealed class LexicalScanner return _parser.Parse(state).Value; } - public static Parser KeywordParser() + public static IParser KeywordParser() { return from value in Choice(StringIgnoreCase("program"), StringIgnoreCase("const"), @@ -37,7 +37,7 @@ public sealed class LexicalScanner StringIgnoreCase("real"), StringIgnoreCase("boolean"), StringIgnoreCase("char"), - StringIgnoreCase("divide"), + StringIgnoreCase("div"), StringIgnoreCase("not"), StringIgnoreCase("mod"), StringIgnoreCase("and"), @@ -49,16 +49,16 @@ public sealed class LexicalScanner select new LexicalToken(LexicalTokenType.Keyword, value); } - public static Parser DelimiterParser() + public static IParser DelimiterParser() { - Parser semicolonParser = from token in Char(':') + IParser semicolonParser = from token in Char(':') from _ in Char('=').LookAhead().Not() select new LexicalToken(LexicalTokenType.Delimiter, token.ToString()); - Parser periodParser = from token in Char('.') + IParser periodParser = from token in Char('.') from _ in Char('.').LookAhead().Not() select new LexicalToken(LexicalTokenType.Delimiter, "."); - Parser singleCharTokenParser = from token in Choice( + IParser singleCharTokenParser = from token in Choice( String(","), String(";"), String("("), @@ -71,17 +71,17 @@ public sealed class LexicalScanner return singleCharTokenParser | semicolonParser | periodParser; } - public static Parser OperatorParser() + public static IParser OperatorParser() { - Parser lessParser = from token in Char('<') + IParser lessParser = from token in Char('<') from _ in Char('=').LookAhead().Not() select new LexicalToken(LexicalTokenType.Operator, "<"); - Parser greaterParser = from token in Char('>') + IParser greaterParser = from token in Char('>') from _ in Char('=').LookAhead().Not() select new LexicalToken(LexicalTokenType.Operator, ">"); - Parser otherParsers = from token in Choice( + IParser otherParsers = from token in Choice( String("="), String("!="), String("<="), @@ -96,14 +96,19 @@ public sealed class LexicalScanner return otherParsers | lessParser | greaterParser; } - public static Parser ConstIntegerParser() + public static IParser ConstIntegerParser() { - return from nums in AsciiDigit().Many1() + return Choice( + from nums in AsciiDigit().Many1() + from _ in String("..").LookAhead() + select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray())), + from nums in AsciiDigit().Many1() from _ in Char('.').LookAhead().Not() - select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray())); + select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray())) + ); } - public static Parser ConstFloatParser() + public static IParser ConstFloatParser() { return from integer in AsciiDigit().Many1() from _ in Char('.') @@ -112,24 +117,24 @@ public sealed class LexicalScanner new string(integer.ToArray()) + '.' + new string(fraction.ToArray())); } - public static Parser IdentifierParser() + public static IParser IdentifierParser() { return from first in AsciiLetter() | Char('_') from second in (AsciiLetter() | AsciiDigit() | Char('_')).Many() select new LexicalToken(LexicalTokenType.Identifier, first + new string(second.ToArray())); } - public static Parser CommentParser() + public static IParser CommentParser() { return Any().Quote(Char('{'), Char('}')).Map(_ => Unit.Instance); } - public static Parser JunkParser() + public static IParser JunkParser() { return Space().Map(_ => Unit.Instance) | LineBreak().Map(_ => Unit.Instance) | CommentParser(); } - public static Parser CharParser() + public static IParser CharParser() { return from str in Any().Quote(Char('\'')).Map(x => new string(x.ToArray())) select str.Length <= 1 @@ -137,13 +142,13 @@ public sealed class LexicalScanner : new LexicalToken(LexicalTokenType.String, str); } - public static Parser> PascalParser() + public static IParser> PascalParser() { return JunkParser().SkipTill(Choice(KeywordParser(), DelimiterParser(), OperatorParser(), - ConstIntegerParser(), ConstFloatParser(), + ConstIntegerParser(), CharParser(), IdentifierParser())).Many(); } diff --git a/CanonSharp.Common/Scanner/LexicalToken.cs b/CanonSharp.Pascal/Scanner/LexicalToken.cs similarity index 95% rename from CanonSharp.Common/Scanner/LexicalToken.cs rename to CanonSharp.Pascal/Scanner/LexicalToken.cs index 1733ade..28e9f6c 100644 --- a/CanonSharp.Common/Scanner/LexicalToken.cs +++ b/CanonSharp.Pascal/Scanner/LexicalToken.cs @@ -1,4 +1,4 @@ -namespace CanonSharp.Common.Scanner; +namespace CanonSharp.Pascal.Scanner; public enum LexicalTokenType { diff --git a/CanonSharp.Common/Scanner/StringReadState.cs b/CanonSharp.Pascal/Scanner/StringReadState.cs similarity index 97% rename from CanonSharp.Common/Scanner/StringReadState.cs rename to CanonSharp.Pascal/Scanner/StringReadState.cs index 15ce837..0475996 100644 --- a/CanonSharp.Common/Scanner/StringReadState.cs +++ b/CanonSharp.Pascal/Scanner/StringReadState.cs @@ -1,6 +1,6 @@ using CanonSharp.Combinator.Abstractions; -namespace CanonSharp.Common.Scanner; +namespace CanonSharp.Pascal.Scanner; /// /// 字符串输入流状态 diff --git a/CanonSharp.Pascal/SyntaxTree/AssignNode.cs b/CanonSharp.Pascal/SyntaxTree/AssignNode.cs new file mode 100644 index 0000000..87f8958 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/AssignNode.cs @@ -0,0 +1,10 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class AssignNode(VariableNode variable, SyntaxNodeBase expression) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Assign; + + public VariableNode Variable => variable; + + public SyntaxNodeBase Expression => expression; +} diff --git a/CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs b/CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs new file mode 100644 index 0000000..6e95b56 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs @@ -0,0 +1,33 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public enum BinaryOperatorType +{ + Add, + Subtract, + Multiply, + + // Pascal特有的整数除法关键词div + IntegerDivide, + Divide, + Mod, + And, + Or, + Equal, + NotEqual, + Greater, + GreaterEqual, + Less, + LessEqual +} + +public sealed class BinaryOperatorNode(BinaryOperatorType operatorType, SyntaxNodeBase left, SyntaxNodeBase right) + : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.BinaryOperator; + + public BinaryOperatorType OperatorType => operatorType; + + public SyntaxNodeBase Left => left; + + public SyntaxNodeBase Right => right; +} diff --git a/CanonSharp.Pascal/SyntaxTree/BlockNode.cs b/CanonSharp.Pascal/SyntaxTree/BlockNode.cs new file mode 100644 index 0000000..6359271 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/BlockNode.cs @@ -0,0 +1,8 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class BlockNode(IEnumerable statements) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Block; + + public IList Statements => statements.ToList(); +} diff --git a/CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs b/CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs new file mode 100644 index 0000000..d259abf --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs @@ -0,0 +1,8 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class BooleanValueNode(bool value) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.BooleanValue; + + public bool Value => value; +} diff --git a/CanonSharp.Pascal/SyntaxTree/CharValueNode.cs b/CanonSharp.Pascal/SyntaxTree/CharValueNode.cs new file mode 100644 index 0000000..d1bef14 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/CharValueNode.cs @@ -0,0 +1,8 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class CharValueNode(char value) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.CharValue; + + public char Value => value; +} diff --git a/CanonSharp.Pascal/SyntaxTree/ConstantNode.cs b/CanonSharp.Pascal/SyntaxTree/ConstantNode.cs new file mode 100644 index 0000000..c44c4da --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ConstantNode.cs @@ -0,0 +1,12 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ConstantNode(LexicalToken identifier, SyntaxNodeBase value) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Constant; + + public LexicalToken Identifier = identifier; + + public SyntaxNodeBase Value = value; +} diff --git a/CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs b/CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs new file mode 100644 index 0000000..215c006 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs @@ -0,0 +1,8 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class FloatValueNode(double value) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.FloatValue; + + public double Value => value; +} diff --git a/CanonSharp.Pascal/SyntaxTree/ForNode.cs b/CanonSharp.Pascal/SyntaxTree/ForNode.cs new file mode 100644 index 0000000..50be740 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ForNode.cs @@ -0,0 +1,20 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ForNode( + LexicalToken identifier, + SyntaxNodeBase leftCondition, + SyntaxNodeBase rightCondition, + SyntaxNodeBase statement) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.For; + + public LexicalToken Identifier => identifier; + + public SyntaxNodeBase LeftCondition => leftCondition; + + public SyntaxNodeBase RightCondition => rightCondition; + + public SyntaxNodeBase Statement => statement; +} diff --git a/CanonSharp.Pascal/SyntaxTree/IfNode.cs b/CanonSharp.Pascal/SyntaxTree/IfNode.cs new file mode 100644 index 0000000..4019e37 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/IfNode.cs @@ -0,0 +1,26 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class IfNode : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.If; + + public SyntaxNodeBase Condition { get; } + + public SyntaxNodeBase Statement { get; } + + public SyntaxNodeBase? ElseStatement { get; } + + public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement) + { + Condition = condition; + Statement = statement; + ElseStatement = null; + } + + public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement, SyntaxNodeBase elseStatement) + { + Condition = condition; + Statement = statement; + ElseStatement = elseStatement; + } +} diff --git a/CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs b/CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs new file mode 100644 index 0000000..0d34243 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs @@ -0,0 +1,8 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class IntegerValueNode(int value) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.IntegerValue; + + public int Value => value; +} diff --git a/CanonSharp.Pascal/SyntaxTree/Parameter.cs b/CanonSharp.Pascal/SyntaxTree/Parameter.cs new file mode 100644 index 0000000..3674f29 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/Parameter.cs @@ -0,0 +1,14 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class Parameter(bool isReference, LexicalToken identifier, SyntaxNodeBase typeNode) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Parameter; + + public bool IsReference => isReference; + + public LexicalToken Identifier => identifier; + + public SyntaxNodeBase TypeNode => typeNode; +} diff --git a/CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs b/CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs new file mode 100644 index 0000000..9824e3e --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs @@ -0,0 +1,18 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ProcedureCallNode : SyntaxNodeBase +{ + public ProcedureCallNode(LexicalToken identifier, IEnumerable parameters) + { + Identifier = identifier; + Parameters.AddRange(parameters); + } + + public override SyntaxNodeType NodeType => SyntaxNodeType.ProcedureCall; + + public LexicalToken Identifier { get; } + + public List Parameters { get; } = []; +} diff --git a/CanonSharp.Pascal/SyntaxTree/Program.cs b/CanonSharp.Pascal/SyntaxTree/Program.cs new file mode 100644 index 0000000..fa773b1 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/Program.cs @@ -0,0 +1,10 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class Program(ProgramHead head, ProgramBody body) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Program; + + public ProgramHead Head => head; + + public ProgramBody Body => body; +} diff --git a/CanonSharp.Pascal/SyntaxTree/ProgramBody.cs b/CanonSharp.Pascal/SyntaxTree/ProgramBody.cs new file mode 100644 index 0000000..cb42698 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ProgramBody.cs @@ -0,0 +1,17 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variableDeclarations, + BlockNode subprograms, + BlockNode mainBlock) + : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody; + + public BlockNode ConstantDeclarations => constantDeclarations; + + public BlockNode VariableDeclarations => variableDeclarations; + + public BlockNode Subprograms => subprograms; + + public BlockNode MainBlock => mainBlock; +} diff --git a/CanonSharp.Pascal/SyntaxTree/ProgramHead.cs b/CanonSharp.Pascal/SyntaxTree/ProgramHead.cs new file mode 100644 index 0000000..a20ce24 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ProgramHead.cs @@ -0,0 +1,10 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ProgramHead(LexicalToken programName) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramHead; + + public LexicalToken ProgramName => programName; +} diff --git a/CanonSharp.Pascal/SyntaxTree/Subprogram.cs b/CanonSharp.Pascal/SyntaxTree/Subprogram.cs new file mode 100644 index 0000000..97b5cdc --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/Subprogram.cs @@ -0,0 +1,10 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class Subprogram(SubprogramHead subprogramHead, SubprogramBody subprogramBody) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.SubProgram; + + public SubprogramHead Head => subprogramHead; + + public SubprogramBody Body => subprogramBody; +} diff --git a/CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs b/CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs new file mode 100644 index 0000000..f580864 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs @@ -0,0 +1,12 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class SubprogramBody(BlockNode constDeclarations, BlockNode variableDeclarations, BlockNode mainBlock) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramBody; + + public BlockNode ConstDeclarations => constDeclarations; + + public BlockNode VariableDeclarations => variableDeclarations; + + public BlockNode MainBlock => mainBlock; +} diff --git a/CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs b/CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs new file mode 100644 index 0000000..27d50bb --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs @@ -0,0 +1,28 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class SubprogramHead : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramHead; + + public LexicalToken Identifier { get; } + + public List Parameters { get; } = []; + + public SyntaxNodeBase? TypeToken { get; } + + public SubprogramHead(LexicalToken identifier, IEnumerable parameters) + { + Identifier = identifier; + Parameters.AddRange(parameters); + TypeToken = null; + } + + public SubprogramHead(LexicalToken identifier, IEnumerable parameters, SyntaxNodeBase typeToken) : this( + identifier, parameters) + { + TypeToken = typeToken; + } + +} diff --git a/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs b/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs new file mode 100644 index 0000000..d57266e --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs @@ -0,0 +1,43 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public enum SyntaxNodeType +{ + BinaryOperator, + UnaryOperator, + IntegerValue, + FloatValue, + BooleanValue, + CharValue, + Variable, + Type, + Assign, + Block, + Constant, + VariableDeclaration, + If, + While, + For, + ProcedureCall, + Parameter, + SubprogramHead, + SubprogramBody, + SubProgram, + ProgramBody, + ProgramHead, + Program +} + +public abstract class SyntaxNodeBase +{ + public abstract SyntaxNodeType NodeType { get; } + + public T Convert() where T : SyntaxNodeBase + { + if (this is not T result) + { + throw new InvalidCastException($"Can't convert {NodeType} to target node."); + } + + return result; + } +} diff --git a/CanonSharp.Pascal/SyntaxTree/TypeNode.cs b/CanonSharp.Pascal/SyntaxTree/TypeNode.cs new file mode 100644 index 0000000..b0e2bed --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/TypeNode.cs @@ -0,0 +1,24 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public record struct ArrayRange(int Left, int Right); + +public sealed class TypeNode : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Type; + + public LexicalToken TypeToken { get; } + + public List ArrayRanges { get; } = []; + + public TypeNode(LexicalToken typeToken) + { + TypeToken = typeToken; + } + + public TypeNode(LexicalToken typeToken, IEnumerable arrayRanges) : this(typeToken) + { + ArrayRanges.AddRange(arrayRanges); + } +} diff --git a/CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs b/CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs new file mode 100644 index 0000000..2a5fb4b --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs @@ -0,0 +1,17 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public enum UnaryOperatorType +{ + Plus, + Minus, + Not +} + +public sealed class UnaryOperatorNode(UnaryOperatorType operatorType, SyntaxNodeBase node) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.UnaryOperator; + + public UnaryOperatorType OperatorType => operatorType; + + public SyntaxNodeBase Node => node; +} diff --git a/CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs b/CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs new file mode 100644 index 0000000..579a311 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs @@ -0,0 +1,12 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class VariableDeclarationNode(IEnumerable identifiers, TypeNode typeNode) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.VariableDeclaration; + + public IList Identifiers => identifiers.ToList(); + + public TypeNode TypeNode => typeNode; +} diff --git a/CanonSharp.Pascal/SyntaxTree/VariableNode.cs b/CanonSharp.Pascal/SyntaxTree/VariableNode.cs new file mode 100644 index 0000000..513e8ec --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/VariableNode.cs @@ -0,0 +1,23 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class VariableNode : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.Variable; + + public LexicalToken Identifier { get; } + + public List Indexers { get; } = []; + + public VariableNode(LexicalToken identifier) + { + Identifier = identifier; + } + + public VariableNode(LexicalToken identifier, IEnumerable expressions) + { + Identifier = identifier; + Indexers.AddRange(expressions); + } +} diff --git a/CanonSharp.Pascal/SyntaxTree/WhileNode.cs b/CanonSharp.Pascal/SyntaxTree/WhileNode.cs new file mode 100644 index 0000000..c3df1da --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/WhileNode.cs @@ -0,0 +1,10 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class WhileNode(SyntaxNodeBase condition, SyntaxNodeBase statement) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.While; + + public SyntaxNodeBase Condition => condition; + + public SyntaxNodeBase Statement => statement; +} diff --git a/CanonSharp.Tests/CanonSharp.Tests.csproj b/CanonSharp.Tests/CanonSharp.Tests.csproj index d5d6a25..19c1429 100644 --- a/CanonSharp.Tests/CanonSharp.Tests.csproj +++ b/CanonSharp.Tests/CanonSharp.Tests.csproj @@ -23,7 +23,7 @@ - + diff --git a/CanonSharp.Tests/CombinatorsTests/BasicParsersTests.cs b/CanonSharp.Tests/CombinatorsTests/BasicParsersTests.cs index 8abb6e7..fe4d3b9 100644 --- a/CanonSharp.Tests/CombinatorsTests/BasicParsersTests.cs +++ b/CanonSharp.Tests/CombinatorsTests/BasicParsersTests.cs @@ -11,7 +11,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void AlternativeTest() { - Parser parser = Token('a') | Token('b'); + IParser parser = Token('a') | Token('b'); ValidateSuccessfulResult(parser, 'a', "abc"); ValidateSuccessfulResult(parser, 'b', "bcd"); @@ -26,7 +26,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void BindTest() { - Parser parser = Token('a').Bind(_ => Token('b')).Bind(_ => Token('c')); + IParser parser = Token('a').Bind(_ => Token('b')).Bind(_ => Token('c')); ValidateSuccessfulResult(parser, 'c', "abc"); ValidateFailedResult(parser, "acd"); @@ -36,7 +36,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void MapTest() { - Parser parser = Token('a').Map(c => $"{c}"); + IParser parser = Token('a').Map(c => $"{c}"); ValidateSuccessfulResult(parser, "a", "abc"); parser = Token('a').Map("test"); @@ -46,7 +46,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void NextTest() { - Parser parser = Token('a').Next(_ => Token('a'), _ => Token('b')); + IParser parser = Token('a').Next(_ => Token('a'), _ => Token('b')); ValidateSuccessfulResult(parser, 'a', "aaa"); ValidateSuccessfulResult(parser, 'b', "bbb"); @@ -61,7 +61,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void NextTest2() { - Parser parser = Token('a').Next(_ => "123", _ => Pure("456")); + IParser parser = Token('a').Next(_ => "123", _ => Pure("456")); ValidateSuccessfulResult(parser, "123", "aaa"); ValidateSuccessfulResult(parser, "456", "bbb"); @@ -77,7 +77,7 @@ public class BasicParsersTests : ParserTestsBase [Fact] public void FixTest() { - Parser parser = Fix(self => Token('a').Next(_ => self, Unit.Instance)) + IParser parser = Fix(self => Token('a').Next(_ => self, Unit.Instance)) .Bind(_ => Token('b')); ValidateSuccessfulResult(parser, 'b', "aaaaab"); } diff --git a/CanonSharp.Tests/CombinatorsTests/CombinatorParserTests.cs b/CanonSharp.Tests/CombinatorsTests/CombinatorParserTests.cs index 25c4eed..927f310 100644 --- a/CanonSharp.Tests/CombinatorsTests/CombinatorParserTests.cs +++ b/CanonSharp.Tests/CombinatorsTests/CombinatorParserTests.cs @@ -11,7 +11,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void ChoiceTest() { - Parser parser = Choice(Token('a'), Token('b'), Token('c')); + IParser parser = Choice(Token('a'), Token('b'), Token('c')); ValidateSuccessfulResult(parser, 'a', "abc"); ValidateSuccessfulResult(parser, 'b', "bcd"); ValidateSuccessfulResult(parser, 'c', "cde"); @@ -25,17 +25,18 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void SequenceTest() { - Parser> parser = Sequence(Token('a'), Token('b'), Token('c')); + IParser> parser = Sequence(Token('a'), Token('b'), Token('c')); ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc"); - parser = Sequence([Token('a'), Token('b'), Token('c')]); + IEnumerable> parsers = [Token('a'), Token('b'), Token('c')]; + parser = Sequence(parsers); ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc"); } [Fact] public void LeftRightTest() { - Parser parser = Token('a').Left(Token('b')); + IParser parser = Token('a').Left(Token('b')); ValidateSuccessfulResult(parser, 'a', "ab"); parser = Token('a').Right(Token('b')); @@ -45,7 +46,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void ManyTest() { - Parser> parser = Token('a').Many(); + IParser> parser = Token('a').Many(); ValidateSuccessfulResult(parser, [], "bbb"); ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "aaa"); @@ -57,7 +58,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void SkipManyTest() { - Parser parser = Token('a').SkipMany().Right(Token('b')); + IParser parser = Token('a').SkipMany().Right(Token('b')); ValidateSuccessfulResult(parser, 'b', "aaaab"); ValidateSuccessfulResult(parser, 'b', "bbbb"); @@ -71,7 +72,7 @@ public class CombinatorParserTests : ParserTestsBase { // 等效于Many1 // 但是不返回中间结果 - Parser parser = Token('a').Chain(Token); + IParser parser = Token('a').Chain(Token); ValidateSuccessfulResult(parser, 'a', "aa"); ValidateFailedResult(parser, "bb"); @@ -83,7 +84,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void ManyTillTest() { - Parser> parser = Token('a').ManyTill(Token('b').LookAhead()); + IParser> parser = Token('a').ManyTill(Token('b').LookAhead()); ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "aaab"); ValidateSuccessfulResult(parser, [], "b"); @@ -95,19 +96,33 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void SkipTillTest() { - Parser parser = Token('a').SkipTill(Token('b')); + IParser parser = Token('a').SkipTill(Token('b')); ValidateSuccessfulResult(parser, 'b', "aaab"); ValidateSuccessfulResult(parser, 'b', "b"); parser = Token('a').Skip1Till(Token('b')); ValidateSuccessfulResult(parser, 'b', "aaab"); ValidateFailedResult(parser, "b"); + + parser = Token('a').SkipTill(Choice( + Token('b'), Token('c'))); + + ValidateSuccessfulResult(parser, 'b', "aaab"); + ValidateSuccessfulResult(parser, 'c', "ac"); + } + + [Fact] + public void SkipTillManyTest() + { + IParser> parser = Token('a').SkipTill(Token('b')).Many(); + + ValidateSuccessfulResult(parser, ['b', 'b'], "aaabaab"); } [Fact] public void TakeTillTest() { - Parser> parser = TakeTill(Token('b').LookAhead()); + IParser> parser = TakeTill(Token('b').LookAhead()); ValidateSuccessfulResult(parser, ['a', 'a'], "aab"); ValidateSuccessfulResult(parser, [], "b"); @@ -119,7 +134,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void MatchTest() { - Parser parser = Token('b').Match(); + IParser parser = Token('b').Match(); ValidateSuccessfulResult(parser, 'b', "asdfasdfasdfasdfb"); ValidateSuccessfulResult(parser, 'b', "b"); } @@ -127,7 +142,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void QuoteTest() { - Parser> parser = Any().Quote(Token('['), Token(']')); + IParser> parser = Any().Quote(Token('['), Token(']')); ValidateSuccessfulResult(parser, ['1', '2', '3'], "[123]"); parser = Any().Quote(Token('\'')); @@ -137,7 +152,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void SeparatedByTest() { - Parser> parser = Token('a').SeparatedBy(Token(',')); + IParser> parser = Token('a').SeparatedBy(Token(',')); ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "a,a,a"); ValidateSuccessfulResult(parser, ['a'], "a"); ValidateSuccessfulResult(parser, [], ""); @@ -151,7 +166,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void EndByTest() { - Parser> parser = Satisfy(char.IsLetter).EndBy(Token('.')); + IParser> parser = Satisfy(char.IsLetter).EndBy(Token('.')); ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc."); ValidateSuccessfulResult(parser, [], "."); @@ -163,7 +178,7 @@ public class CombinatorParserTests : ParserTestsBase [Fact] public void SeparatedOrEndByTest() { - Parser> parser = Satisfy(char.IsLetter).SeparatedOrEndBy1(Token(',')); + IParser> parser = Satisfy(char.IsLetter).SeparatedOrEndBy1(Token(',')); ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "a,b,c,"); ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "a,b,c"); ValidateFailedResult(parser, ""); diff --git a/CanonSharp.Tests/CombinatorsTests/LinqTests.cs b/CanonSharp.Tests/CombinatorsTests/LinqTests.cs index 8aaffa0..d6fde40 100644 --- a/CanonSharp.Tests/CombinatorsTests/LinqTests.cs +++ b/CanonSharp.Tests/CombinatorsTests/LinqTests.cs @@ -10,7 +10,7 @@ public class LinqTests : ParserTestsBase [Fact] public void SelectTest1() { - Parser parser = from token in Char('a') + IParser parser = from token in Char('a') select token.ToString(); ValidateSuccessfulResult(parser, "a", "a"); ValidateFailedResult(parser, "b"); @@ -19,7 +19,7 @@ public class LinqTests : ParserTestsBase [Fact] public void SelectManyTest1() { - Parser parser = from _1 in Char('a') + IParser parser = from _1 in Char('a') from _2 in Char('b') from _3 in Char('c') select 123; diff --git a/CanonSharp.Tests/CombinatorsTests/ModifierParserTests.cs b/CanonSharp.Tests/CombinatorsTests/ModifierParserTests.cs index 498fcda..1f47bc7 100644 --- a/CanonSharp.Tests/CombinatorsTests/ModifierParserTests.cs +++ b/CanonSharp.Tests/CombinatorsTests/ModifierParserTests.cs @@ -12,7 +12,7 @@ public class ModifierParserTests : ParserTestsBase [Fact] public void DoTest() { - Parser parser = Token('a').Do(x => Assert.Equal('a', x)).Do(x => Assert.Equal('a', x), + IParser parser = Token('a').Do(x => Assert.Equal('a', x)).Do(x => Assert.Equal('a', x), failedResult => Assert.ThrowsAny(() => failedResult.Value)); ValidateSuccessfulResult(parser, 'a', "abc"); @@ -22,7 +22,7 @@ public class ModifierParserTests : ParserTestsBase [Fact] public void LookAheadTest() { - Parser parser = Token('a').LookAhead().Next(_ => Token('a'), _ => Token('b')); + IParser parser = Token('a').LookAhead().Next(_ => Token('a'), _ => Token('b')); ValidateSuccessfulResult(parser, 'a', "abc"); ValidateSuccessfulResult(parser, 'b', "bcd"); } @@ -30,7 +30,7 @@ public class ModifierParserTests : ParserTestsBase [Fact] public void NotTest() { - Parser parser = Token('a').Not('b'); + IParser parser = Token('a').Not('b'); ValidateSuccessfulResult(parser, 'b', "bcd"); parser = Token('a').Not().Bind(_ => Token('b')); @@ -40,7 +40,7 @@ public class ModifierParserTests : ParserTestsBase [Fact] public void TryTest() { - Parser parser = String("abc").Try("cde"); + IParser parser = String("abc").Try("cde"); ValidateSuccessfulResult(parser, "abc", "abc"); ValidateSuccessfulResult(parser, "cde", "cde"); diff --git a/CanonSharp.Tests/CombinatorsTests/PrimitiveParserTests.cs b/CanonSharp.Tests/CombinatorsTests/PrimitiveParserTests.cs index 504af07..6702458 100644 --- a/CanonSharp.Tests/CombinatorsTests/PrimitiveParserTests.cs +++ b/CanonSharp.Tests/CombinatorsTests/PrimitiveParserTests.cs @@ -11,7 +11,7 @@ public class PrimitiveParserTests : ParserTestsBase [Fact] public void PureTest() { - Parser parser = Pure('a'); + IParser parser = Pure('a'); ValidateSuccessfulResult(parser, 'a', "abc"); parser = Pure(_ => 'a'); @@ -21,18 +21,18 @@ public class PrimitiveParserTests : ParserTestsBase [Fact] public void NullTest() { - Parser parser = Null(); + IParser parser = Null(); ValidateSuccessfulResult(parser, Unit.Instance, "abc"); } [Fact] public void FailTest() { - Parser parser = Fail(); + IParser parser = Fail(); ValidateFailedResult(parser, "abc"); parser = Fail("Failed message"); - FailedResult result = ValidateFailedResult(parser, "abc"); + IFailedResult result = ValidateFailedResult(parser, "abc"); Assert.Equal("Failed message", result.Message); parser = Fail(x => $"{x}"); @@ -47,29 +47,36 @@ public class PrimitiveParserTests : ParserTestsBase [Fact] public void SatisfyTest() { - Parser parser = Satisfy(char.IsLetter); + IParser parser = Satisfy(char.IsLetter); ValidateSuccessfulResult(parser, 'a', "abc"); ValidateFailedResult(parser, "123"); } + [Fact] + public void SatisfyFailedTest() + { + IParser parser = Satisfy(char.IsLetter); + ValidateFailedResult(parser, ""); + } + [Fact] public void AnyTest() { - Parser parser = Any(); + IParser parser = Any(); ValidateSuccessfulResult(parser, '1', "123"); } [Fact] public void TokenTest() { - Parser parser = Token('a'); + IParser parser = Token('a'); ValidateSuccessfulResult(parser, 'a', "abc"); } [Fact] public void TakeTest() { - Parser> parser = Take(5); + IParser> parser = Take(5); ValidateSuccessfulResult(parser, ['h', 'e', 'l', 'l', 'o'], "hello"); ValidateFailedResult(parser, "abc"); } @@ -77,7 +84,7 @@ public class PrimitiveParserTests : ParserTestsBase [Fact] public void SkipTest() { - Parser parser = Skip(5).Bind(_ => Token(',')); + IParser parser = Skip(5).Bind(_ => Token(',')); ValidateSuccessfulResult(parser, ',', "hello,world."); ValidateFailedResult(parser, "abc"); } diff --git a/CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs b/CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs new file mode 100644 index 0000000..5e29d1b --- /dev/null +++ b/CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs @@ -0,0 +1,62 @@ +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public class ConstDeclarationTests : GrammarParserTestBase +{ + [Fact] + public void BooleanConstValueTest() + { + BooleanValueNode node = RunParser(GrammarParser.ConstValueParser(), "true"); + Assert.True(node.Value); + + node = RunParser(GrammarParser.ConstValueParser(), "false"); + Assert.False(node.Value); + } + + [Fact] + public void CharConstValueTest() + { + CharValueNode node = RunParser(GrammarParser.ConstValueParser(), "'a'"); + Assert.Equal('a', node.Value); + } + + [Fact] + public void NumberConstValueTest() + { + IntegerValueNode integerValueNode = RunParser(GrammarParser.ConstValueParser(), "250"); + Assert.Equal(250, integerValueNode.Value); + + FloatValueNode floatValueNode = RunParser(GrammarParser.ConstValueParser(), "100.5"); + Assert.Equal(100.5, floatValueNode.Value); + + UnaryOperatorNode node = RunParser(GrammarParser.ConstValueParser(), "+250"); + Assert.Equal(UnaryOperatorType.Plus, node.OperatorType); + node = RunParser(GrammarParser.ConstValueParser(), "-100.5"); + Assert.Equal(UnaryOperatorType.Minus, node.OperatorType); + } + + [Fact] + public void ConstDeclarationTest() + { + ConstantNode node = RunParser(GrammarParser.ConstDeclarationParser(), "a = true"); + + Assert.Equal("a", node.Identifier.LiteralValue); + Assert.True(node.Value.Convert().Value); + } + + [Theory] + [InlineData("", 0)] + [InlineData("const INFINITE = 100;", 1)] + [InlineData("const a = true; b = false", 2)] + [InlineData("const code1 = 100.4;code2 = 'a'", 2)] + public void ConstDeclarationsCountTest(string input, int count) + { + BlockNode blockNode = RunParser(GrammarParser.ConstDeclarationsParser(), input); + List constantNodes = blockNode.Statements.Select(node => node.Convert()).ToList(); + + Assert.Equal(count, constantNodes.Count); + } +} diff --git a/CanonSharp.Tests/ParserTests/ExpressionParserTests.cs b/CanonSharp.Tests/ParserTests/ExpressionParserTests.cs new file mode 100644 index 0000000..16af0a3 --- /dev/null +++ b/CanonSharp.Tests/ParserTests/ExpressionParserTests.cs @@ -0,0 +1,297 @@ +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public sealed class ExpressionParserTests : GrammarParserTestBase +{ + [Fact] + public void BoolTest() + { + BooleanValueNode node = RunParser(GrammarParser.FactorParser(), "false"); + Assert.False(node.Value); + + node = RunParser(GrammarParser.FactorParser(), "true"); + Assert.True(node.Value); + } + + [Fact] + public void NumberTest() + { + IntegerValueNode integerValueNode = RunParser(GrammarParser.FactorParser(), "123456"); + Assert.Equal(123456, integerValueNode.Value); + + FloatValueNode floatValueNode = RunParser(GrammarParser.FactorParser(), "123.456"); + Assert.Equal(123.456, floatValueNode.Value); + } + + [Fact] + public void UnaryOperatorTest() + { + UnaryOperatorNode node = RunParser(GrammarParser.FactorParser(), "- 123"); + Assert.Equal(UnaryOperatorType.Minus, node.OperatorType); + Assert.Equal(123, node.Node.Convert().Value); + + node = RunParser(GrammarParser.FactorParser(), "+ 100.5"); + Assert.Equal(UnaryOperatorType.Plus, node.OperatorType); + Assert.Equal(100.5, node.Node.Convert().Value); + } + + [Fact] + public void IdentifierTest() + { + VariableNode node = RunParser(GrammarParser.VariableParser(), "temp"); + Assert.Equal("temp", node.Identifier.LiteralValue); + } + + [Fact] + public void ArrayIdentifierTest() + { + VariableNode node = RunParser(GrammarParser.VariableParser(), "a[0]"); + Assert.Equal("a", node.Identifier.LiteralValue); + Assert.Single(node.Indexers); + Assert.Equal(0, node.Indexers.First().Convert().Value); + + node = RunParser(GrammarParser.VariableParser(), "a[i + 1]"); + Assert.Equal("a", node.Identifier.LiteralValue); + Assert.Single(node.Indexers); + BinaryOperatorNode binaryOperatorNode = node.Indexers.First().Convert(); + Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType); + Assert.Equal("i", binaryOperatorNode.Left.Convert().Identifier.LiteralValue); + Assert.Equal(1, binaryOperatorNode.Right.Convert().Value); + + node = RunParser(GrammarParser.VariableParser(), "a[b[i + 1] - 10, c]"); + Assert.Equal("a", node.Identifier.LiteralValue); + Assert.Equal(2, node.Indexers.Count); + + VariableNode secondNode = node.Indexers[1].Convert(); + Assert.Equal("c", secondNode.Identifier.LiteralValue); + Assert.Empty(secondNode.Indexers); + + BinaryOperatorNode firstNode = node.Indexers[0].Convert(); + Assert.Equal(BinaryOperatorType.Subtract, firstNode.OperatorType); + Assert.Equal(10, firstNode.Right.Convert().Value); + + VariableNode variableNode = firstNode.Left.Convert(); + Assert.Equal("b", variableNode.Identifier.LiteralValue); + Assert.Single(variableNode.Indexers); + binaryOperatorNode = variableNode.Indexers[0].Convert(); + Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType); + Assert.Equal("i", binaryOperatorNode.Left.Convert().Identifier.LiteralValue); + Assert.Equal(1, binaryOperatorNode.Right.Convert().Value); + } + + [Fact] + public void ProcedureCallTest() + { + ProcedureCallNode node = RunParser(GrammarParser.FactorParser(), "test(1)"); + Assert.Equal("test", node.Identifier.LiteralValue); + Assert.Single(node.Parameters); + Assert.Equal(1, node.Parameters[0].Convert().Value); + + node = RunParser(GrammarParser.FactorParser(), "test()"); + Assert.Equal("test", node.Identifier.LiteralValue); + Assert.Empty(node.Parameters); + + VariableNode variableNode = RunParser(GrammarParser.FactorParser(), "test"); + Assert.Equal("test", variableNode.Identifier.LiteralValue); + Assert.Empty(variableNode.Indexers); + } + + [Fact] + public void SingleTermTest() + { + VariableNode node = RunParser(GrammarParser.TermParser(), "temp"); + Assert.Equal("temp", node.Identifier.LiteralValue); + + UnaryOperatorNode unaryOperatorNode = RunParser(GrammarParser.TermParser(), "- 123"); + Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); + Assert.Equal(123, unaryOperatorNode.Node.Convert().Value); + + unaryOperatorNode = RunParser(GrammarParser.TermParser(), "+ 100.5"); + Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); + Assert.Equal(100.5, unaryOperatorNode.Node.Convert().Value); + } + + [Fact] + public void MultiplyTermTest1() + { + BinaryOperatorNode node = RunParser(GrammarParser.TermParser(), "10 / 2"); + + Assert.Equal(BinaryOperatorType.Divide, node.OperatorType); + Assert.Equal(10, node.Left.Convert().Value); + Assert.Equal(2, node.Right.Convert().Value); + } + + [Fact] + public void MultiplyTermTest2() + { + BinaryOperatorNode node = RunParser(GrammarParser.TermParser(), "10 div 2"); + + Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType); + Assert.Equal(10, node.Left.Convert().Value); + Assert.Equal(2, node.Right.Convert().Value); + } + + [Fact] + public void MultiplyTermTest3() + { + BinaryOperatorNode node = RunParser(GrammarParser.TermParser(), "temp * 2 div 3"); + + Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType); + Assert.Equal(3, node.Right.Convert().Value); + BinaryOperatorNode leftNode = node.Left.Convert(); + Assert.Equal(BinaryOperatorType.Multiply, leftNode.OperatorType); + Assert.Equal("temp", leftNode.Left.Convert().Identifier.LiteralValue); + Assert.Equal(2, leftNode.Right.Convert().Value); + } + + [Fact] + public void SimpleExpressionTest1() + { + VariableNode node = RunParser(GrammarParser.SimpleExpressionParser(), "temp"); + Assert.Equal("temp", node.Identifier.LiteralValue); + + UnaryOperatorNode unaryOperatorNode = + RunParser(GrammarParser.SimpleExpressionParser(), "- 123"); + Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); + Assert.Equal(123, unaryOperatorNode.Node.Convert().Value); + + unaryOperatorNode = RunParser(GrammarParser.SimpleExpressionParser(), "+ 100.5"); + Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); + Assert.Equal(100.5, unaryOperatorNode.Node.Convert().Value); + } + + [Fact] + public void SimpleExpressionTest2() + { + BinaryOperatorNode node = RunParser(GrammarParser.SimpleExpressionParser(), "1 + 1"); + + Assert.Equal(BinaryOperatorType.Add, node.OperatorType); + Assert.Equal(1, node.Left.Convert().Value); + Assert.Equal(1, node.Right.Convert().Value); + + node = RunParser(GrammarParser.SimpleExpressionParser(), "1 + 1 - 2"); + + Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); + Assert.Equal(2, node.Right.Convert().Value); + + BinaryOperatorNode leftNode = node.Left.Convert(); + Assert.Equal(BinaryOperatorType.Add, leftNode.OperatorType); + Assert.Equal(1, leftNode.Left.Convert().Value); + Assert.Equal(1, leftNode.Right.Convert().Value); + } + + [Fact] + public void SimpleExpressionTest3() + { + BinaryOperatorNode node = RunParser(GrammarParser.SimpleExpressionParser(), "1 - 2 * 5"); + + Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); + Assert.Equal(1, node.Left.Convert().Value); + + BinaryOperatorNode rightNode = node.Right.Convert(); + Assert.Equal(BinaryOperatorType.Multiply, rightNode.OperatorType); + Assert.Equal(2, rightNode.Left.Convert().Value); + Assert.Equal(5, rightNode.Right.Convert().Value); + } + + [Fact] + public void SimpleExpressionTest4() + { + BinaryOperatorNode node = RunParser(GrammarParser.SimpleExpressionParser(), "1 + +1"); + + Assert.Equal(BinaryOperatorType.Add, node.OperatorType); + Assert.Equal(1, node.Left.Convert().Value); + UnaryOperatorNode unaryOperatorNode = node.Right.Convert(); + Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); + Assert.Equal(1, unaryOperatorNode.Node.Convert().Value); + + node = RunParser(GrammarParser.SimpleExpressionParser(), "1 - -1"); + + Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); + Assert.Equal(1, node.Left.Convert().Value); + unaryOperatorNode = node.Right.Convert(); + Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); + Assert.Equal(1, unaryOperatorNode.Node.Convert().Value); + + node = RunParser(GrammarParser.SimpleExpressionParser(), "+ 1 - - 1"); + + Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); + unaryOperatorNode = node.Left.Convert(); + Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); + Assert.Equal(1, unaryOperatorNode.Node.Convert().Value); + unaryOperatorNode = node.Right.Convert(); + Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); + Assert.Equal(1, unaryOperatorNode.Node.Convert().Value); + } + + [Fact] + public void SimpleExpressionTest5() + { + BinaryOperatorNode node = RunParser(GrammarParser.SimpleExpressionParser(), + "true and temp or temp and false"); + + Assert.Equal(BinaryOperatorType.Or, node.OperatorType); + + BinaryOperatorNode left = node.Left.Convert(); + Assert.Equal(BinaryOperatorType.And, left.OperatorType); + BinaryOperatorNode right = node.Right.Convert(); + Assert.Equal(BinaryOperatorType.And, right.OperatorType); + } + + [Fact] + public void ExpressionTest1() + { + BinaryOperatorNode node = RunParser(GrammarParser.ExpressionParser(), + "true and temp or temp and false"); + + Assert.Equal(BinaryOperatorType.Or, node.OperatorType); + + BinaryOperatorNode left = node.Left.Convert(); + Assert.Equal(BinaryOperatorType.And, left.OperatorType); + BinaryOperatorNode right = node.Right.Convert(); + Assert.Equal(BinaryOperatorType.And, right.OperatorType); + } + + [Fact] + public void ExpressionTest2() + { + BinaryOperatorNode node = RunParser(GrammarParser.ExpressionParser(), "2 >= 1"); + Assert.Equal(BinaryOperatorType.GreaterEqual, node.OperatorType); + Assert.Equal(2, node.Left.Convert().Value); + Assert.Equal(1, node.Right.Convert().Value); + } + + [Fact] + public void ExpressionTest3() + { + BinaryOperatorNode node = RunParser(GrammarParser.ExpressionParser(), "(1 + 1) * 2"); + + Assert.Equal(BinaryOperatorType.Multiply, node.OperatorType); + Assert.Equal(2, node.Right.Convert().Value); + + node = node.Left.Convert(); + Assert.Equal(BinaryOperatorType.Add, node.OperatorType); + Assert.Equal(1, node.Left.Convert().Value); + Assert.Equal(1, node.Right.Convert().Value); + } + + [Fact] + public void ExpressionsTest1() + { + List nodes = + RunParser(GrammarParser.ExpressionsParser(), "1 + 1, 2 * 3"); + + Assert.Equal(BinaryOperatorType.Add, nodes[0].OperatorType); + Assert.Equal(BinaryOperatorType.Multiply, nodes[1].OperatorType); + } + + [Fact] + public void VariableTest1() + { + VariableNode node = RunParser(GrammarParser.VariableParser(), "temp"); + Assert.Equal("temp", node.Identifier.LiteralValue); + } +} diff --git a/CanonSharp.Tests/ParserTests/ProgramParserTests.cs b/CanonSharp.Tests/ParserTests/ProgramParserTests.cs new file mode 100644 index 0000000..1dad715 --- /dev/null +++ b/CanonSharp.Tests/ParserTests/ProgramParserTests.cs @@ -0,0 +1,229 @@ +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public class ProgramParserTests : GrammarParserTestBase +{ + [Fact] + public void ProgramParseTest1() + { + Program program = ProgramParse(""" + program main; + begin + end. + """); + + Assert.Equal("main", program.Head.ProgramName.LiteralValue); + Assert.Empty(program.Body.MainBlock.Statements); + } + + [Fact] + public void ProgramParseTest2() + { + Program program = ProgramParse(""" + program main; + begin + temp := 1 + 1; + end. + """); + + Assert.Equal("main", program.Head.ProgramName.LiteralValue); + + AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert(); + Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue); + + BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert(); + Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType); + Assert.Equal(1, binaryOperatorNode.Left.Convert().Value); + Assert.Equal(1, binaryOperatorNode.Right.Convert().Value); + } + + [Fact] + public void ProgramParseTest3() + { + Program program = ProgramParse(""" + program main; + const a = 1; b = true; + begin + temp := 1 + 1; + end. + """); + + Assert.Equal("main", program.Head.ProgramName.LiteralValue); + + List constantNodes = program.Body.ConstantDeclarations.Statements + .Select(x => x.Convert()).ToList(); + Assert.Equal(2, constantNodes.Count); + Assert.Equal("a", constantNodes[0].Identifier.LiteralValue); + Assert.Equal("b", constantNodes[1].Identifier.LiteralValue); + + AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert(); + Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue); + + BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert(); + Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType); + Assert.Equal(1, binaryOperatorNode.Left.Convert().Value); + Assert.Equal(1, binaryOperatorNode.Right.Convert().Value); + } + + [Fact] + public void ProgramParseTest4() + { + Program program = ProgramParse(""" + program main; + var a : integer; + begin + a := 1 + 1; + end. + """); + + IList variableDeclarationNodes = program.Body.VariableDeclarations.Statements + .Select(x => x.Convert()).ToList(); + Assert.Single(variableDeclarationNodes); + Assert.Single(variableDeclarationNodes.First().Identifiers); + Assert.Equal("a", variableDeclarationNodes.First().Identifiers.First().LiteralValue); + } + + [Theory] + [InlineData(""" + program DoNothing; + begin + end. + """)] + [InlineData(""" + program Add; + var a : Integer; + begin + a := 1 + 1 + end. + """)] + [InlineData(""" + program varTest; + var a : integer; + begin + a := 9 div 1; + end. + """)] + [InlineData(""" + program main; + var a : integer; + begin + a := 1 + +1; + a := 1 - -1; + a := 1 + -1; + a := 1 - +1; + end. + """)] + [InlineData(""" + program main; + const a = true; b = false; + var c, d : boolean; + begin + c := true; + d := false; + end. + """)] + [InlineData(""" + program arrayTest; + var a : array [0..10] of integer; + begin + a[0] := 1; + end. + """)] + [InlineData(""" + program arrayTest; + var a : array [10..100, 0..10] of integer; + begin + a[10,0] := 1; + end. + """)] + [InlineData(""" + program main; + begin + begin + end + end. + """)] + [InlineData(""" + program main; + var i : integer; + begin + while i < 10 do + i := i + 1; + end. + """)] + [InlineData(""" + program main; + procedure test; + begin + end; + begin + end. + """)] + [InlineData(""" + program main; + function test(a, b : integer) : integer; + begin + test := 1; + end; + begin + end. + """)] + [InlineData(""" + program main; + var i : integer; + begin + for i := 1 to 100 do + begin + doSomething(i); + end; + end. + """)] + [InlineData(""" + program main; + begin + if 1 = 2 then + begin + test1; + test2 := a; + end + else + begin + doSomething; + end; + end. + """)] + [InlineData(""" + program main; + var a : integer; + function test : integer; + begin + end; + begin + test; + a := test; + test(); + a := test(); + end. + """)] + [InlineData(""" + program main; + procedure test(); + begin + end; + begin + test(); + end. + """)] + [InlineData(""" + program main; + begin + test + end. + """)] + public void ProgramParseTest(string input) + { + ProgramParse(input); + } +} diff --git a/CanonSharp.Tests/ParserTests/StatementParserTests.cs b/CanonSharp.Tests/ParserTests/StatementParserTests.cs new file mode 100644 index 0000000..b4bc86b --- /dev/null +++ b/CanonSharp.Tests/ParserTests/StatementParserTests.cs @@ -0,0 +1,190 @@ +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public class StatementParserTests : GrammarParserTestBase +{ + [Fact] + public void StatementTest1() + { + AssignNode node = RunParser(GrammarParser.StatementParser(), "temp := 1"); + + Assert.Equal("temp", node.Variable.Identifier.LiteralValue); + Assert.Equal(1, node.Expression.Convert().Value); + } + + [Fact] + public void IfTest1() + { + IfNode node = RunParser(GrammarParser.IfParser(), """ + if i = 1 then + a := a + 1 + else + a := a + 2 + """); + Assert.NotNull(node.ElseStatement); + + BinaryOperatorNode condition = node.Condition.Convert(); + Assert.Equal(BinaryOperatorType.Equal, condition.OperatorType); + + AssignNode statement = node.Statement.Convert(); + Assert.Equal("a", statement.Variable.Identifier.LiteralValue); + + AssignNode elseStatement = node.Statement.Convert(); + Assert.Equal("a", elseStatement.Variable.Identifier.LiteralValue); + } + + [Fact] + public void IfTest2() + { + IfNode node = RunParser(GrammarParser.IfParser(), """ + if i = 1 then + if i = 2 then + a := a + 1 + else + a := a + 2 + """); + + Assert.Null(node.ElseStatement); + + IfNode subIfNode = node.Statement.Convert(); + Assert.NotNull(subIfNode.ElseStatement); + } + + [Fact] + public void ForTest1() + { + ForNode node = RunParser(GrammarParser.ForParser(), """ + for i := 1 to 10 do + a := a + i + """); + Assert.Equal("i", node.Identifier.LiteralValue); + Assert.Equal(1, node.LeftCondition.Convert().Value); + Assert.Equal(10, node.RightCondition.Convert().Value); + + AssignNode assignNode = node.Statement.Convert(); + Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue); + } + + [Fact] + public void WhileTest1() + { + WhileNode node = RunParser(GrammarParser.WhileParser(), """ + while c >= 1 do + a := a + 1; + """); + + BinaryOperatorNode binaryOperatorNode = node.Condition.Convert(); + Assert.Equal(BinaryOperatorType.GreaterEqual, binaryOperatorNode.OperatorType); + + AssignNode assignNode = node.Statement.Convert(); + Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue); + } + + [Fact] + public void ProcedureCallTest1() + { + ProcedureCallNode node = RunParser(GrammarParser.ProcedureCallParser(), "test()"); + Assert.Equal("test", node.Identifier.LiteralValue); + Assert.Empty(node.Parameters); + + node = RunParser(GrammarParser.ProcedureCallParser(), "test"); + Assert.Equal("test", node.Identifier.LiteralValue); + Assert.Empty(node.Parameters); + } + + [Fact] + public void ProcedureCallTest2() + { + ProcedureCallNode node = RunParser(GrammarParser.ProcedureCallParser(), + "test(1 + 1, true)"); + + Assert.Equal(2, node.Parameters.Count); + + BinaryOperatorNode firstParameter = node.Parameters[0].Convert(); + Assert.Equal(BinaryOperatorType.Add, firstParameter.OperatorType); + + BooleanValueNode secondParameter = node.Parameters[1].Convert(); + Assert.True(secondParameter.Value); + } + + [Fact] + public void ProcedureCallTest3() + { + ProcedureCallNode node = + RunParser(GrammarParser.ProcedureCallParser(), "test(1 * + 1, test2[0])"); + + Assert.Equal(2, node.Parameters.Count); + + VariableNode secondParameter = node.Parameters[1].Convert(); + Assert.Equal("test2", secondParameter.Identifier.LiteralValue); + Assert.Single(secondParameter.Indexers); + } + + [Theory] + [InlineData(""" + begin + temp := 1 + 1; + flag := true and false; + end + """)] + [InlineData(""" + begin + temp := 1 + 1; + flag := true and false + end + """)] + public void CompoundStatementTest1(string input) + { + BlockNode node = RunParser(GrammarParser.CompoundStatementParser(), input); + + Assert.Equal(2, node.Statements.Count); + AssignNode assignNode = node.Statements[0].Convert(); + Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue); + + assignNode = node.Statements[1].Convert(); + Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue); + } + + [Fact] + public void CompoundStatementTest2() + { + BlockNode node = RunParser(GrammarParser.CompoundStatementParser(), "begin end"); + Assert.Empty(node.Statements); + } + + [Fact] + public void ProgramHeadTest1() + { + ProgramHead head = RunParser(GrammarParser.ProgramHeadParser(), "program main"); + + Assert.Equal("main", head.ProgramName.LiteralValue); + } + + [Theory] + [InlineData(""" + begin + temp := 1 + 1; + flag := true and false; + end + """)] + [InlineData(""" + begin + temp := 1 + 1; + flag := true and false + end + """)] + public void ProgramBodyTest1(string input) + { + BlockNode node = RunParser(GrammarParser.ProgramBodyParser(), input).MainBlock; + + Assert.Equal(2, node.Statements.Count); + AssignNode assignNode = node.Statements[0].Convert(); + Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue); + + assignNode = node.Statements[1].Convert(); + Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue); + } +} diff --git a/CanonSharp.Tests/ParserTests/SubprogramParserTests.cs b/CanonSharp.Tests/ParserTests/SubprogramParserTests.cs new file mode 100644 index 0000000..c5fa723 --- /dev/null +++ b/CanonSharp.Tests/ParserTests/SubprogramParserTests.cs @@ -0,0 +1,83 @@ +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public class SubprogramParserTests : GrammarParserTestBase +{ + [Fact] + public void ParameterTest1() + { + List parameters = RunParser(GrammarParser.ParameterParser(), "a ,b : integer"); + + Assert.Equal(2, parameters.Count); + Assert.All(parameters, p => + { + Assert.False(p.IsReference); + Assert.Equal("integer", p.TypeNode.Convert().TypeToken.LiteralValue); + }); + + parameters = RunParser(GrammarParser.ParameterParser(), "var a, b,c: real"); + Assert.Equal(3, parameters.Count); + Assert.All(parameters, p => + { + Assert.True(p.IsReference); + Assert.Equal("real", p.TypeNode.Convert().TypeToken.LiteralValue); + }); + } + + [Theory] + [InlineData("(var a, b : integer; c, d : real)", 4)] + [InlineData("(a : boolean; var c, d, e : char)", 4)] + [InlineData("(a : integer)", 1)] + [InlineData("(var b ,c : real)", 2)] + public void FormalParameterTest(string input, int count) + { + List parameters = RunParser(GrammarParser.FormalParameterParser(), input); + Assert.Equal(count, parameters.Count); + } + + [Theory] + [InlineData("procedure test", 0)] + [InlineData("procedure test()", 0)] + [InlineData("procedure test(a : integer; var b : real)", 2)] + [InlineData("procedure test(a,b, c : real; var e, f : boolean)", 5)] + [InlineData("function test : integer", 0)] + [InlineData("function test() : real", 0)] + [InlineData("function test(a : integer; var b : real) : boolean", 2)] + [InlineData("function test(a,b, c : real; var e, f : boolean) : char", 5)] + public void SubprogramHeadTest(string input, int count) + { + SubprogramHead head = RunParser(GrammarParser.SubprogramHeadParser(), input); + Assert.Equal(count, head.Parameters.Count); + } + + [Fact] + public void SubprogramHeadTest1() + { + SubprogramHead head = + RunParser(GrammarParser.SubprogramHeadParser(), "function test(a : integer) : real"); + + Assert.Equal("test", head.Identifier.LiteralValue); + Assert.Single(head.Parameters); + Assert.NotNull(head.TypeToken); + Assert.Equal("real", head.TypeToken.Convert().TypeToken.LiteralValue); + } + + [Fact] + public void SubprogramBodyTest1() + { + SubprogramBody body = RunParser(GrammarParser.SubprogramBodyParser(), """ + const a = 3; + var c, d : integer; e, f :real; + begin + c := a + d; + end + """); + + Assert.Single(body.ConstDeclarations.Statements); + Assert.Equal(2, body.VariableDeclarations.Statements.Count); + Assert.Equal(1, body.MainBlock.Statements.Count); + } +} diff --git a/CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs b/CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs new file mode 100644 index 0000000..49693a5 --- /dev/null +++ b/CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs @@ -0,0 +1,82 @@ +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.SyntaxTree; +using CanonSharp.Tests.Utils; + +namespace CanonSharp.Tests.ParserTests; + +public class VariableDeclarationTests : GrammarParserTestBase +{ + [Theory] + [InlineData("boolean", "boolean")] + [InlineData("integer", "integer")] + [InlineData("char", "char")] + [InlineData("real", "real")] + public void BasicTypeParseTest(string input, string value) + { + TypeNode node = RunParser(GrammarParser.TypeParser(), input); + Assert.Equal(value, node.TypeToken.LiteralValue); + } + + [Fact] + public void ArrayTypeParseTest() + { + TypeNode node = RunParser(GrammarParser.ArrayTypeParser(), "array [0..9] of integer"); + + Assert.Equal("integer", node.TypeToken.LiteralValue); + Assert.Single(node.ArrayRanges); + Assert.Equal(new ArrayRange(0, 9), node.ArrayRanges.First()); + + node = RunParser(GrammarParser.ArrayTypeParser(), "array [0..10,0..10] of char"); + Assert.Equal("char", node.TypeToken.LiteralValue); + Assert.Equal(2, node.ArrayRanges.Count); + Assert.Equal(new ArrayRange(0, 10), node.ArrayRanges[0]); + Assert.Equal(new ArrayRange(0, 10), node.ArrayRanges[1]); + } + + [Fact] + public void VariableDeclarationTest() + { + VariableDeclarationNode node = RunParser(GrammarParser.VariableDeclarationParser(), + "a : integer"); + + Assert.Contains(node.Identifiers, token => token.LiteralValue == "a"); + Assert.Equal("integer", node.TypeNode.TypeToken.LiteralValue); + + node = RunParser(GrammarParser.VariableDeclarationParser(), + "a, b, c : boolean"); + + Assert.Contains(node.Identifiers, token => token.LiteralValue == "a"); + Assert.Contains(node.Identifiers, token => token.LiteralValue == "b"); + Assert.Contains(node.Identifiers, token => token.LiteralValue == "c"); + Assert.Equal("boolean", node.TypeNode.TypeToken.LiteralValue); + } + + [Fact] + public void ArrayVariableDeclarationTest() + { + VariableDeclarationNode node = RunParser(GrammarParser.VariableDeclarationParser(), + "test_array: array [0..9,10..20] of real"); + + Assert.Single(node.Identifiers); + Assert.Contains(node.Identifiers, token => token.LiteralValue == "test_array"); + Assert.Equal("real", node.TypeNode.TypeToken.LiteralValue); + + Assert.Equal(2, node.TypeNode.ArrayRanges.Count); + Assert.Equal(new ArrayRange(0, 9), node.TypeNode.ArrayRanges[0]); + Assert.Equal(new ArrayRange(10, 20), node.TypeNode.ArrayRanges[1]); + } + + [Theory] + [InlineData("var a : integer;", 1)] + [InlineData("var a : integer; b : boolean;", 2)] + [InlineData("var a : integer; b,c : boolean;" + + "d : char", 3)] + [InlineData("var a,d,e : integer; b : boolean; c: real;", 3)] + [InlineData("var a : array [0..5] of integer", 1)] + [InlineData("var a,b,c: array [0..9,10..20] of integer; c,d : boolean", 2)] + public void VariableDeclarationsTest(string input, int count) + { + BlockNode node = RunParser(GrammarParser.VariableDeclarationsParser(), input); + Assert.Equal(count, node.Statements.Count); + } +} diff --git a/CanonSharp.Tests/ReaderTests/StringReadStateTests.cs b/CanonSharp.Tests/ReaderTests/StringReadStateTests.cs index 4aeeb8d..beaaead 100644 --- a/CanonSharp.Tests/ReaderTests/StringReadStateTests.cs +++ b/CanonSharp.Tests/ReaderTests/StringReadStateTests.cs @@ -1,7 +1,7 @@ using CanonSharp.Combinator.Extensions; -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; -namespace CanonSharp.Tests.LexicalAnalyzerTests; +namespace CanonSharp.Tests.ReaderTests; public class StringReadStateTests { diff --git a/CanonSharp.Tests/ScannerTest/LexicalParserTests.cs b/CanonSharp.Tests/ScannerTests/LexicalParserTests.cs similarity index 90% rename from CanonSharp.Tests/ScannerTest/LexicalParserTests.cs rename to CanonSharp.Tests/ScannerTests/LexicalParserTests.cs index 4bd0d84..0935e40 100644 --- a/CanonSharp.Tests/ScannerTest/LexicalParserTests.cs +++ b/CanonSharp.Tests/ScannerTests/LexicalParserTests.cs @@ -1,7 +1,7 @@ -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; using CanonSharp.Tests.Utils; -namespace CanonSharp.Tests.ScannerTest; +namespace CanonSharp.Tests.ScannerTests; public class LexicalParserTests : LexicalTestBase { @@ -131,6 +131,21 @@ public class LexicalParserTests : LexicalTestBase ]); } + [Fact] + public void LexicalParserTest4() + { + ValidateLexicalTokens(LexicalScanner.PascalParser(), "array [0..9] of integer", [ + (LexicalTokenType.Keyword, "array"), + (LexicalTokenType.Delimiter, "["), + (LexicalTokenType.ConstInteger, "0"), + (LexicalTokenType.Delimiter, ".."), + (LexicalTokenType.ConstInteger, "9"), + (LexicalTokenType.Delimiter, "]"), + (LexicalTokenType.Keyword, "of"), + (LexicalTokenType.Keyword, "integer") + ]); + } + [Theory] [InlineData(""" program exFunction; diff --git a/CanonSharp.Tests/ScannerTest/LexicalTokenParserTest.cs b/CanonSharp.Tests/ScannerTests/LexicalTokenParserTest.cs similarity index 76% rename from CanonSharp.Tests/ScannerTest/LexicalTokenParserTest.cs rename to CanonSharp.Tests/ScannerTests/LexicalTokenParserTest.cs index 9f939d2..26d94dc 100644 --- a/CanonSharp.Tests/ScannerTest/LexicalTokenParserTest.cs +++ b/CanonSharp.Tests/ScannerTests/LexicalTokenParserTest.cs @@ -1,10 +1,10 @@ using CanonSharp.Combinator; using CanonSharp.Combinator.Abstractions; using CanonSharp.Combinator.Extensions; -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; using CanonSharp.Tests.Utils; -namespace CanonSharp.Tests.ScannerTest; +namespace CanonSharp.Tests.ScannerTests; public class LexicalTokenParserTest : LexicalTestBase { @@ -27,9 +27,14 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData("true")] [InlineData("false")] [InlineData("while")] + [InlineData("div")] + [InlineData("not")] + [InlineData("mod")] + [InlineData("and")] + [InlineData("or")] public void KeywordParserTest(string literalValue) { - Parser keyword = LexicalScanner.KeywordParser(); + IParser keyword = LexicalScanner.KeywordParser(); ValidateSuccessfulParser(keyword, LexicalTokenType.Keyword, literalValue, literalValue); } @@ -39,7 +44,7 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData("todo")] public void FailedKeywordParserTest(string input) { - Parser keyword = LexicalScanner.KeywordParser(); + IParser keyword = LexicalScanner.KeywordParser(); ValidateFailedParser(keyword, input); } @@ -55,7 +60,7 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData("..")] public void DelimiterParserTest(string literalValue) { - Parser delimiter = LexicalScanner.DelimiterParser(); + IParser delimiter = LexicalScanner.DelimiterParser(); ValidateSuccessfulParser(delimiter, LexicalTokenType.Delimiter, literalValue, literalValue); } @@ -63,7 +68,7 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData(":=")] public void FailedDelimiterParserTest(string input) { - Parser delimiter = LexicalScanner.DelimiterParser(); + IParser delimiter = LexicalScanner.DelimiterParser(); ValidateFailedParser(delimiter, input); } @@ -81,7 +86,7 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData(":=")] public void OperatorParserTest(string literalValue) { - Parser operatorParser = LexicalScanner.OperatorParser(); + IParser operatorParser = LexicalScanner.OperatorParser(); ValidateSuccessfulParser(operatorParser, LexicalTokenType.Operator, literalValue, literalValue); } @@ -94,7 +99,7 @@ public class LexicalTokenParserTest : LexicalTestBase [InlineData("andand")] public void IdentifierParserTest(string literalValue) { - Parser identifier = LexicalScanner.IdentifierParser(); + IParser identifier = LexicalScanner.IdentifierParser(); ValidateSuccessfulParser(identifier, LexicalTokenType.Identifier, literalValue, literalValue); } @@ -102,10 +107,11 @@ public class LexicalTokenParserTest : LexicalTestBase [Theory] [InlineData(123, "123")] [InlineData(0, "0")] + [InlineData(10, "10..20")] public void ConstIntegerTest(int value, string input) { StringReadState state = new(input); - ParseResult result = LexicalScanner.ConstIntegerParser().Parse(state); + IParseResult result = LexicalScanner.ConstIntegerParser().Parse(state); Assert.Equal(LexicalTokenType.ConstInteger, result.Value.TokenType); Assert.Equal(value, int.Parse(result.Value.LiteralValue)); @@ -117,7 +123,7 @@ public class LexicalTokenParserTest : LexicalTestBase public void ConstFloatTest(double value, string input) { StringReadState state = new(input); - ParseResult result = LexicalScanner.ConstFloatParser().Parse(state); + IParseResult result = LexicalScanner.ConstFloatParser().Parse(state); Assert.Equal(LexicalTokenType.ConstFloat, result.Value.TokenType); Assert.Equal(value, double.Parse(result.Value.LiteralValue)); @@ -129,7 +135,7 @@ public class LexicalTokenParserTest : LexicalTestBase public void CharTest(char value, string input) { StringReadState state = new(input); - ParseResult result = LexicalScanner.CharParser().Parse(state); + IParseResult result = LexicalScanner.CharParser().Parse(state); Assert.Equal(LexicalTokenType.Character, result.Value.TokenType); Assert.Equal(value, char.Parse(result.Value.LiteralValue)); @@ -140,7 +146,7 @@ public class LexicalTokenParserTest : LexicalTestBase public void StringTest(string value, string input) { StringReadState state = new(input); - ParseResult result = LexicalScanner.CharParser().Parse(state); + IParseResult result = LexicalScanner.CharParser().Parse(state); Assert.Equal(LexicalTokenType.String, result.Value.TokenType); Assert.Equal(value, result.Value.LiteralValue); @@ -152,7 +158,7 @@ public class LexicalTokenParserTest : LexicalTestBase public void CommentTest(string input) { StringReadState state = new(input); - ParseResult result = LexicalScanner.CommentParser().Parse(state); + IParseResult result = LexicalScanner.CommentParser().Parse(state); Assert.Equal(Unit.Instance, result.Value); } @@ -168,8 +174,8 @@ public class LexicalTokenParserTest : LexicalTestBase public void JunkTest(string input) { StringReadState state = new(input); - Parser parser = LexicalScanner.JunkParser().SkipTill(LexicalScanner.KeywordParser()); - ParseResult result = parser.Parse(state); + IParser parser = LexicalScanner.JunkParser().SkipTill(LexicalScanner.KeywordParser()); + IParseResult result = parser.Parse(state); Assert.Equal(LexicalTokenType.Keyword, result.Value.TokenType); Assert.Equal("program", result.Value.LiteralValue); diff --git a/CanonSharp.Tests/TextTests/TextParserTests.cs b/CanonSharp.Tests/TextTests/TextParserTests.cs index d39f5d7..00bb448 100644 --- a/CanonSharp.Tests/TextTests/TextParserTests.cs +++ b/CanonSharp.Tests/TextTests/TextParserTests.cs @@ -1,7 +1,7 @@ using CanonSharp.Combinator.Abstractions; using CanonSharp.Combinator.Extensions; using CanonSharp.Combinator.Text; -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; using CanonSharp.Tests.Utils; using static CanonSharp.Combinator.Text.TextParserBuilder; @@ -24,7 +24,7 @@ public class TextParserTests : ParserTestsBase [InlineData('d', "d")] public void OneOfTest(char except, string input) { - Parser parser = OneOf("abcd"); + IParser parser = OneOf("abcd"); ValidateSuccessfulResult(parser, except, input); } @@ -39,7 +39,7 @@ public class TextParserTests : ParserTestsBase [InlineData('D', "D")] public void OneOfIgnoreCaseTest(char except, string input) { - Parser parser = OneOfIgnoreCase("abcd"); + IParser parser = OneOfIgnoreCase("abcd"); ValidateSuccessfulResult(parser, except, input); } @@ -50,7 +50,7 @@ public class TextParserTests : ParserTestsBase [InlineData("Hello,World.")] public void StringIgnoreCaseTest(string literalValue) { - Parser parser = StringIgnoreCase("hello,world."); + IParser parser = StringIgnoreCase("hello,world."); ValidateSuccessfulResult(parser, literalValue, literalValue); } @@ -60,7 +60,7 @@ public class TextParserTests : ParserTestsBase [InlineData('9')] public void RangeTest(char except) { - Parser parser = Range('0', '9'); + IParser parser = Range('0', '9'); ValidateSuccessfulResult(parser, except, except.ToString()); ValidateFailedResult(parser, "abc"); @@ -161,8 +161,8 @@ public class TextParserTests : ParserTestsBase test """); - Parser> parser = StringIgnoreCase("test").SkipSpaceAndLineBreak().Many(); - ParseResult> result = parser.Parse(state); + IParser> parser = StringIgnoreCase("test").SkipSpaceAndLineBreak().Many(); + IParseResult> result = parser.Parse(state); Assert.All(result.Value, x => Assert.Equal("test", x.ToLower())); } diff --git a/CanonSharp.Tests/Utils/GrammarParserTestBase.cs b/CanonSharp.Tests/Utils/GrammarParserTestBase.cs new file mode 100644 index 0000000..dbba9b9 --- /dev/null +++ b/CanonSharp.Tests/Utils/GrammarParserTestBase.cs @@ -0,0 +1,31 @@ +using CanonSharp.Combinator.Abstractions; +using CanonSharp.Pascal.Parser; +using CanonSharp.Pascal.Scanner; +using CanonSharp.Pascal.SyntaxTree; + +namespace CanonSharp.Tests.Utils; + +public abstract class GrammarParserTestBase +{ + protected static T RunParser(IParser parser, string input) where T : SyntaxNodeBase + { + LexicalScanner scanner = new(); + LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input))); + IParseResult parseResult = parser.Parse(state); + + return parseResult.Value.Convert(); + } + + protected static List RunParser(IParser> parser, + string input) where T : SyntaxNodeBase + { + LexicalScanner scanner = new(); + LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input))); + IParseResult> parseResult = parser.Parse(state); + + return parseResult.Value.Select(node => node.Convert()).ToList(); + } + + protected static Program ProgramParse(string input) + => RunParser(GrammarParser.ProgramParser(), input); +} diff --git a/CanonSharp.Tests/Utils/LexicalTestBase.cs b/CanonSharp.Tests/Utils/LexicalTestBase.cs index fdb9657..b50f8c1 100644 --- a/CanonSharp.Tests/Utils/LexicalTestBase.cs +++ b/CanonSharp.Tests/Utils/LexicalTestBase.cs @@ -1,36 +1,38 @@ using CanonSharp.Combinator; using CanonSharp.Combinator.Abstractions; -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; namespace CanonSharp.Tests.Utils; public abstract class LexicalTestBase { - protected static void ValidateSuccessfulParser(Parser parser, LexicalTokenType exceptedType, + protected static void ValidateSuccessfulParser(IParser parser, LexicalTokenType exceptedType, string literalValue, string input) { StringReadState state = new(input); - ParseResult result = parser.Parse(state); + IParseResult result = parser.Parse(state); Assert.Equal(exceptedType, result.Value.TokenType); Assert.Equal(literalValue, result.Value.LiteralValue); } - protected static void ValidateFailedParser(Parser parser, string input) + protected static void ValidateFailedParser(IParser parser, string input) { StringReadState state = new(input); - ParseResult result = parser.Parse(state); + IParseResult result = parser.Parse(state); Assert.ThrowsAny(() => result.Value); } - protected static void ValidateLexicalTokens(Parser> parser, string input, - IEnumerable<(LexicalTokenType, string)> exceptedResult) + protected static void ValidateLexicalTokens(IParser> parser, string input, + List<(LexicalTokenType, string)> exceptedResult) { StringReadState state = new(input); - ParseResult> result = parser.Parse(state); + IParseResult> result = parser.Parse(state); + List actualResult = result.Value.ToList(); + Assert.Equal(exceptedResult.Count, actualResult.Count); foreach (((LexicalTokenType exceptedType, string exceptedValue), LexicalToken token) in exceptedResult.Zip( - result.Value)) + actualResult)) { Assert.Equal(exceptedType, token.TokenType); Assert.Equal(exceptedValue, token.LiteralValue); diff --git a/CanonSharp.Tests/Utils/ParserTestsBase.cs b/CanonSharp.Tests/Utils/ParserTestsBase.cs index 49949ff..31f8e14 100644 --- a/CanonSharp.Tests/Utils/ParserTestsBase.cs +++ b/CanonSharp.Tests/Utils/ParserTestsBase.cs @@ -1,26 +1,26 @@ using CanonSharp.Combinator; using CanonSharp.Combinator.Abstractions; -using CanonSharp.Common.Scanner; +using CanonSharp.Pascal.Scanner; namespace CanonSharp.Tests.Utils; public abstract class ParserTestsBase { - protected static void ValidateSuccessfulResult(Parser parser, T value, string source) + protected static void ValidateSuccessfulResult(IParser parser, T value, string source) { StringReadState state = new(source); - ParseResult result = parser.Parse(state); + IParseResult result = parser.Parse(state); Assert.Equal(value, result.Value); } protected static void ValidateSuccessfulResult( - Parser> parser, + IParser> parser, IEnumerable values, string source) { StringReadState state = new(source); - ParseResult> result = parser.Parse(state); + IParseResult> result = parser.Parse(state); foreach ((T actual, T except) in result.Value.Zip(values)) { @@ -28,16 +28,16 @@ public abstract class ParserTestsBase } } - protected static FailedResult ValidateFailedResult(Parser parser, string source) + protected static IFailedResult ValidateFailedResult(IParser parser, string source) { StringReadState state = new(source); - ParseResult result = parser.Parse(state); + IParseResult result = parser.Parse(state); Assert.ThrowsAny(() => { _ = result.Value; }); - return (FailedResult)result; + return (IFailedResult)result; } } diff --git a/CanonSharp.sln b/CanonSharp.sln index fea8a09..592439b 100644 --- a/CanonSharp.sln +++ b/CanonSharp.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Tests", "CanonSharp.Tests\CanonSharp.Tests.csproj", "{5A28EF92-805F-44E9-8E13-EA9A5BB623BB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Common", "CanonSharp.Common\CanonSharp.Common.csproj", "{288943FE-E3E6-49E2-8C6F-9B5B9242266C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Pascal", "CanonSharp.Pascal\CanonSharp.Pascal.csproj", "{288943FE-E3E6-49E2-8C6F-9B5B9242266C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".gitea", ".gitea", "{B97A35A0-4616-4B3F-8F73-A66827BC98BA}" EndProject