diff --git a/Sources/Benchmarks/.gitignore b/Sources/Benchmarks/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/Sources/Examples/.gitignore b/Sources/Examples/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/Sources/Props/Package.Build.props b/Sources/Props/Package.Build.props index 3df2dacc..2fb0f9e1 100644 --- a/Sources/Props/Package.Build.props +++ b/Sources/Props/Package.Build.props @@ -22,7 +22,7 @@ - + True diff --git a/Sources/Props/SourceGenerator.Build.props b/Sources/Props/SourceGenerator.Build.props index c61d9745..08d6ff98 100644 --- a/Sources/Props/SourceGenerator.Build.props +++ b/Sources/Props/SourceGenerator.Build.props @@ -36,12 +36,12 @@ - - + + - - + + diff --git a/Sources/Shared/Libraries/Collections/Collections.csproj b/Sources/Shared/Libraries/Collections/Collections.csproj index 1d5beb8c..e796b9c7 100644 --- a/Sources/Shared/Libraries/Collections/Collections.csproj +++ b/Sources/Shared/Libraries/Collections/Collections.csproj @@ -1,5 +1,5 @@ - + diff --git a/Sources/Shared/Libraries/Collections/RingBuffer.cs b/Sources/Shared/Libraries/Collections/RingBuffer.cs index 2b54bc5f..e2ed90db 100644 --- a/Sources/Shared/Libraries/Collections/RingBuffer.cs +++ b/Sources/Shared/Libraries/Collections/RingBuffer.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Text; namespace Yoakke.Collections; @@ -57,13 +58,21 @@ public int Capacity /// public int Tail => (this.Head + this.Count) % this.Capacity; + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowOutOfRange() + { + throw new ArgumentOutOfRangeException("frog"); + } + /// public T this[int index] { get { - if (index < 0 || index >= this.Count) throw new ArgumentOutOfRangeException(nameof(index)); - return this.storage[(this.Head + index) % this.Capacity]; + if ((uint)index >= this.Count) ThrowOutOfRange(); + var nd = Head + index; + if (nd >= this.Capacity) nd -= this.Capacity; + return this.storage[nd]; } set diff --git a/Sources/Shared/Libraries/Streams/Streams.csproj b/Sources/Shared/Libraries/Streams/Streams.csproj index 12364259..6a76f2a6 100644 --- a/Sources/Shared/Libraries/Streams/Streams.csproj +++ b/Sources/Shared/Libraries/Streams/Streams.csproj @@ -1,6 +1,6 @@ - - + + diff --git a/Sources/Shared/Tests/Collections.Tests/Collections.Tests.csproj b/Sources/Shared/Tests/Collections.Tests/Collections.Tests.csproj index 6ac7bd2e..e5f3b231 100644 --- a/Sources/Shared/Tests/Collections.Tests/Collections.Tests.csproj +++ b/Sources/Shared/Tests/Collections.Tests/Collections.Tests.csproj @@ -1,5 +1,5 @@ - + diff --git a/Sources/Shared/Tests/Streams.Tests/Streams.Tests.csproj b/Sources/Shared/Tests/Streams.Tests/Streams.Tests.csproj index 61a0fdf4..1ba933b3 100644 --- a/Sources/Shared/Tests/Streams.Tests/Streams.Tests.csproj +++ b/Sources/Shared/Tests/Streams.Tests/Streams.Tests.csproj @@ -1,5 +1,5 @@ - + diff --git a/Sources/SynKit/Benchmarks/.gitignore b/Sources/SynKit/Benchmarks/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/Grammatics.cs b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Grammatics.cs new file mode 100644 index 00000000..c81a3f8e --- /dev/null +++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Grammatics.cs @@ -0,0 +1,71 @@ +using System; +using Yoakke.SynKit.Lexer; +using Yoakke.SynKit.Lexer.Attributes; +using Yoakke.SynKit.Parser.Attributes; + +namespace Yoakke.SynKit.Parser.Benchmarks; + +public enum TokenType +{ + [Error] Error, + [End] End, + [Ignore] [Regex(Regexes.Whitespace)] Whitespace, + + [Token("(")] OpenParen, + [Token(")")] CloseParen, + + [Token("+")] Add, + [Token("-")] Sub, + [Token("*")] Mul, + [Token("/")] Div, + [Token("%")] Mod, + [Token("^")] Exp, + + [Token(";")] Semicol, + + [Regex(Regexes.IntLiteral)] IntLit, +} + +[Lexer(typeof(TokenType))] +public partial class Lexer +{ + public List> LexAll() + { + var list = new List>(); + while (true) + { + var token = Next(); + list.Add(token); + if (token.Kind == TokenType.End) break; + } + return list; + } +} + +[Parser(typeof(TokenType))] +public partial class Parser +{ + [Rule("program: expression ';'")] + public static int Program(int n, IToken _) => n; + + [Right("^")] + [Left("*", "/", "%")] + [Left("+", "-")] + [Rule("expression")] + public static int BinOp(int a, IToken op, int b) => op.Text switch + { + "^" => (int)Math.Pow(a, b), + "*" => a * b, + "/" => a / b, + "%" => a % b, + "+" => a + b, + "-" => a - b, + _ => throw new NotImplementedException(), + }; + + [Rule("expression : '(' expression ')'")] + public static int Grouping(IToken _1, int n, IToken _2) => n; + + [Rule("expression : IntLit")] + public static int IntLit(IToken token) => int.Parse(token.Text); +} \ No newline at end of file diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/Parser.Benchmarks.csproj b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Parser.Benchmarks.csproj new file mode 100644 index 00000000..a07cc31b --- /dev/null +++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Parser.Benchmarks.csproj @@ -0,0 +1,22 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs new file mode 100644 index 00000000..723f984c --- /dev/null +++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs @@ -0,0 +1,47 @@ +// See https://aka.ms/new-console-template for more information + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Yoakke.SynKit.Lexer; +using Yoakke.SynKit.Parser; +using Yoakke.SynKit.Parser.Benchmarks; + +var a = new SimpleBench(); +for (int i = 0; i < 10_000_000; i++) + a.Parse(); +// BenchmarkRunner.Run(); + + + // UNCOMMENT FOR DEBUG +/* +var result = new SimpleBench().Parse(); +if (result.IsOk) +{ + Console.WriteLine($" => {result.Ok.Value}"); +} +else +{ + var err = result.Error; + foreach (var element in err.Elements.Values) + { + Console.WriteLine($" expected {string.Join(" or ", element.Expected)} while parsing {element.Context}"); + } + Console.WriteLine($" but got {(err.Got == null ? "end of input" : ((IToken)err.Got).Text)}"); +}*/ + +public class SimpleBench +{ + private static string source = "1+1/2+6^8-1-15*2;"; + + [Benchmark] + public ParseResult Parse() + { + return new Parser(new Lexer(source)).ParseProgram(); + } + + [Benchmark] + public List> Lex() + { + return new Lexer(source).LexAll(); + } +} diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/key.snk b/Sources/SynKit/Benchmarks/Parser.Benchmarks/key.snk new file mode 100644 index 00000000..a6294a52 Binary files /dev/null and b/Sources/SynKit/Benchmarks/Parser.Benchmarks/key.snk differ diff --git a/Sources/SynKit/Libraries/Automata/Automata.csproj b/Sources/SynKit/Libraries/Automata/Automata.csproj index 12364259..37c9a5b1 100644 --- a/Sources/SynKit/Libraries/Automata/Automata.csproj +++ b/Sources/SynKit/Libraries/Automata/Automata.csproj @@ -1,6 +1,6 @@ - - + + diff --git a/Sources/SynKit/Libraries/C.Syntax/C.Syntax.csproj b/Sources/SynKit/Libraries/C.Syntax/C.Syntax.csproj index aaa2f0ae..4d488c7e 100644 --- a/Sources/SynKit/Libraries/C.Syntax/C.Syntax.csproj +++ b/Sources/SynKit/Libraries/C.Syntax/C.Syntax.csproj @@ -1,6 +1,6 @@ - - + + diff --git a/Sources/SynKit/Libraries/Lexer.Generator/Lexer.Generator.csproj b/Sources/SynKit/Libraries/Lexer.Generator/Lexer.Generator.csproj index 2b3d102b..4fb02c9c 100644 --- a/Sources/SynKit/Libraries/Lexer.Generator/Lexer.Generator.csproj +++ b/Sources/SynKit/Libraries/Lexer.Generator/Lexer.Generator.csproj @@ -2,10 +2,10 @@ - - + + - - + + diff --git a/Sources/SynKit/Libraries/Lexer/Lexer.csproj b/Sources/SynKit/Libraries/Lexer/Lexer.csproj index f13be0b0..be8da499 100644 --- a/Sources/SynKit/Libraries/Lexer/Lexer.csproj +++ b/Sources/SynKit/Libraries/Lexer/Lexer.csproj @@ -1,8 +1,8 @@ - - - - + + + + diff --git a/Sources/SynKit/Libraries/Parser/ParseError.cs b/Sources/SynKit/Libraries/Parser/ParseError.cs index 9756a1b2..83bedad6 100644 --- a/Sources/SynKit/Libraries/Parser/ParseError.cs +++ b/Sources/SynKit/Libraries/Parser/ParseError.cs @@ -14,10 +14,12 @@ namespace Yoakke.SynKit.Parser; /// public class ParseError { + private static readonly Dictionary plug = new(); + /// /// The error cases in different parse contexts. /// - public IReadOnlyDictionary Elements { get; } + public IReadOnlyDictionary Elements { get; } = plug; /// /// The item that was found, if any. @@ -37,7 +39,7 @@ public class ParseError /// The position where the error occurred. /// The context in which the error occurred. public ParseError(object expected, object? got, IComparable position, string context) - : this(new Dictionary { { context, new ParseErrorElement(expected, context) } }, got, position) + : this(plug, got, position) { } @@ -56,6 +58,7 @@ private ParseError(IReadOnlyDictionary elements, obje /// The error that represents both of them properly. public static ParseError? operator |(ParseError? first, ParseError? second) { + return first; // Check nullities if (first is null && second is null) return null; if (first is null) return second!; diff --git a/Sources/SynKit/Libraries/Parser/ParseResult.cs b/Sources/SynKit/Libraries/Parser/ParseResult.cs index dd488d19..d9ed09f6 100644 --- a/Sources/SynKit/Libraries/Parser/ParseResult.cs +++ b/Sources/SynKit/Libraries/Parser/ParseResult.cs @@ -10,32 +10,33 @@ namespace Yoakke.SynKit.Parser; /// The parsed value type. public readonly struct ParseResult { - private readonly object value; + private readonly ParseOk ok; + private readonly ParseError error; /// /// True, if the result is a success. /// - public bool IsOk => this.value is ParseOk; + public bool IsOk { get; } /// /// True, if the result is an error. /// - public bool IsError => this.value is ParseError; + public bool IsError => !IsOk; /// /// Retrieves the parse as a success. /// - public ParseOk Ok => (ParseOk)this.value; + public ParseOk Ok => ok; /// /// Retrieves the parse as an error. /// - public ParseError Error => (ParseError)this.value; + public ParseError Error => error; /// /// Retrieves the furthest error for this result. /// - public ParseError? FurthestError => this.value is ParseOk ok ? ok.FurthestError : (ParseError)this.value; + public ParseError? FurthestError => IsOk ? ok.FurthestError : error; /// /// Initializes a new instance of the struct. @@ -43,7 +44,9 @@ public readonly struct ParseResult /// The successful parse description. public ParseResult(ParseOk ok) { - this.value = ok; + this.ok = ok; + IsOk = true; + error = default!; } /// @@ -52,7 +55,9 @@ public ParseResult(ParseOk ok) /// The error description. public ParseResult(ParseError error) { - this.value = error; + this.error = error; + IsOk = false; + this.ok = default!; } /// @@ -75,6 +80,7 @@ public ParseResult(ParseError error) /// The constructed from the alternatives. public static ParseResult operator |(ParseResult first, ParseResult second) { + return first.IsOk ? first.Ok : (second.IsOk ? second.Ok : second.Error); if (first.IsOk && second.IsOk) return first.Ok | second.Ok; if (first.IsOk) return first.Ok | second.Error; if (second.IsOk) return second.Ok | first.Error; diff --git a/Sources/SynKit/Libraries/Parser/Parser.csproj b/Sources/SynKit/Libraries/Parser/Parser.csproj index f4f59b06..3547d517 100644 --- a/Sources/SynKit/Libraries/Parser/Parser.csproj +++ b/Sources/SynKit/Libraries/Parser/Parser.csproj @@ -1,6 +1,6 @@  - + diff --git a/Sources/SynKit/Libraries/Reporting/Reporting.csproj b/Sources/SynKit/Libraries/Reporting/Reporting.csproj index 48754982..81996d2e 100644 --- a/Sources/SynKit/Libraries/Reporting/Reporting.csproj +++ b/Sources/SynKit/Libraries/Reporting/Reporting.csproj @@ -1,6 +1,6 @@ - - + + diff --git a/Sources/SynKit/Libraries/Text/Text.csproj b/Sources/SynKit/Libraries/Text/Text.csproj index 1d5beb8c..29741f3f 100644 --- a/Sources/SynKit/Libraries/Text/Text.csproj +++ b/Sources/SynKit/Libraries/Text/Text.csproj @@ -1,5 +1,5 @@ - + diff --git a/Sources/Yoakke.sln b/Sources/Yoakke.sln index 0b40a144..a3c5cfb9 100644 --- a/Sources/Yoakke.sln +++ b/Sources/Yoakke.sln @@ -84,6 +84,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{F51DEFFD EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{55692E6E-198F-4699-B6D3-CBA49D22A2D3}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{E49D0FC6-6660-4E49-B9DC-187479C623FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser.Benchmarks", "SynKit\Benchmarks\Parser.Benchmarks\Parser.Benchmarks.csproj", "{AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -194,6 +198,10 @@ Global {CCD5F778-B722-414A-A376-B99E0734C3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {CCD5F778-B722-414A-A376-B99E0734C3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {CCD5F778-B722-414A-A376-B99E0734C3C7}.Release|Any CPU.Build.0 = Release|Any CPU + {AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -228,6 +236,8 @@ Global {3743B1A5-DF69-4C44-8BC0-C967D0BB4AD6} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660} {F51DEFFD-626F-4040-9227-A59BF67F4F53} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660} {55692E6E-198F-4699-B6D3-CBA49D22A2D3} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660} + {E49D0FC6-6660-4E49-B9DC-187479C623FA} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660} + {AF917EB7-FAB8-466C-AA9C-4DE2F83EA2EF} = {E49D0FC6-6660-4E49-B9DC-187479C623FA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1EDE8F07-AE3A-4556-AEF0-3AC518F18F92}