diff --git a/Sources/Props/Benchmark.Build.props b/Sources/Props/Benchmark.Build.props
index a18f50c5..41efffba 100644
--- a/Sources/Props/Benchmark.Build.props
+++ b/Sources/Props/Benchmark.Build.props
@@ -1,15 +1,20 @@
+
+ false
+
- net6.0
+ net8.0
+ enable
+ enable
Exe
false
false
-
+
diff --git a/Sources/SynKit/Benchmarks/.gitignore b/Sources/SynKit/Benchmarks/.gitignore
index e69de29b..91a99327 100644
--- a/Sources/SynKit/Benchmarks/.gitignore
+++ b/Sources/SynKit/Benchmarks/.gitignore
@@ -0,0 +1 @@
+BenchmarkDotNet.Artifacts/
diff --git a/Sources/SynKit/Benchmarks/Directory.Build.props b/Sources/SynKit/Benchmarks/Directory.Build.props
new file mode 100644
index 00000000..91415dd9
--- /dev/null
+++ b/Sources/SynKit/Benchmarks/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+
+
+
+ Yoakke.SynKit.$(MSBuildProjectName)
+
+
diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/ExpressionBenchmarks.cs b/Sources/SynKit/Benchmarks/Parser.Benchmarks/ExpressionBenchmarks.cs
new file mode 100644
index 00000000..3930f600
--- /dev/null
+++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/ExpressionBenchmarks.cs
@@ -0,0 +1,94 @@
+// Copyright (c) 2021-2022 Yoakke.
+// Licensed under the Apache License, Version 2.0.
+// Source repository: https://github.com/LanguageDev/Yoakke
+
+using BenchmarkDotNet.Attributes;
+using Yoakke.SynKit.Lexer;
+using Yoakke.SynKit.Parser;
+using Yoakke.SynKit.Lexer.Attributes;
+using Yoakke.SynKit.Parser.Attributes;
+
+namespace Yoakke.SynKit.Parser.Benchmarks;
+
+public partial class ExpressionBenchmarks
+{
+ private static string source = "(((((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))))+(((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))))))))+((((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))))+(((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))))))))) + (((((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))))+(((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))))))))+((((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))))+(((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))) + ((((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))+(((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))) + ((((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1))) + (((2 + 2) + (1 + 1)) + ((2 + 2) + (1 + 1)))))))))";
+
+ [Benchmark]
+ public ParseResult Parse()
+ {
+ return new Parser(new Lexer(source)).ParseProgram();
+ }
+
+ [Benchmark]
+ public List> Lex()
+ {
+ return new Lexer(source).LexAll();
+ }
+
+}
+
+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);
+}
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..43369c59
--- /dev/null
+++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Parser.Benchmarks.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs
new file mode 100644
index 00000000..b9a3713a
--- /dev/null
+++ b/Sources/SynKit/Benchmarks/Parser.Benchmarks/Program.cs
@@ -0,0 +1,8 @@
+// Copyright (c) 2021-2022 Yoakke.
+// Licensed under the Apache License, Version 2.0.
+// Source repository: https://github.com/LanguageDev/Yoakke
+
+using BenchmarkDotNet.Running;
+
+BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
+
diff --git a/Sources/SynKit/Libraries/Lexer.Generator/LexerSourceGenerator.cs b/Sources/SynKit/Libraries/Lexer.Generator/LexerSourceGenerator.cs
index 7f2e6333..a1bab260 100644
--- a/Sources/SynKit/Libraries/Lexer.Generator/LexerSourceGenerator.cs
+++ b/Sources/SynKit/Libraries/Lexer.Generator/LexerSourceGenerator.cs
@@ -358,7 +358,7 @@ int MakeState()
Name = lexerModel.LexerType.Name,
GenericArgs = lexerModel.LexerType.TypeArguments.Select(t => t.Name).ToList(),
},
- TokenType = lexerModel.TokenType.ToDisplayString(),
+ TokenType = lexerModel.TokenType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
ImplicitConstructor = lexerModel.LexerType.HasNoUserDefinedCtors() && lexerModel.SourceField is null,
SourceName = lexerModel.SourceField?.Name ?? "CharStream",
EndTokenName = lexerModel.EndVariant.Name,
diff --git a/Sources/Yoakke.sln b/Sources/Yoakke.sln
index 6c54b85d..e10b49c5 100644
--- a/Sources/Yoakke.sln
+++ b/Sources/Yoakke.sln
@@ -86,6 +86,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{55
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser.Generator.Tests", "SynKit\Tests\Parser.Generator.Tests\Parser.Generator.Tests.csproj", "{CCD7B565-36BB-F60A-17DC-BD62000BD0C5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser.Benchmarks", "SynKit\Benchmarks\Parser.Benchmarks\Parser.Benchmarks.csproj", "{6A52197D-7F3F-46F5-B549-A4DFEDD48C48}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -200,6 +204,10 @@ Global
{CCD7B565-36BB-F60A-17DC-BD62000BD0C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCD7B565-36BB-F60A-17DC-BD62000BD0C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCD7B565-36BB-F60A-17DC-BD62000BD0C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6A52197D-7F3F-46F5-B549-A4DFEDD48C48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A52197D-7F3F-46F5-B549-A4DFEDD48C48}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A52197D-7F3F-46F5-B549-A4DFEDD48C48}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A52197D-7F3F-46F5-B549-A4DFEDD48C48}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -235,6 +243,8 @@ Global
{F51DEFFD-626F-4040-9227-A59BF67F4F53} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660}
{55692E6E-198F-4699-B6D3-CBA49D22A2D3} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660}
{CCD7B565-36BB-F60A-17DC-BD62000BD0C5} = {F51DEFFD-626F-4040-9227-A59BF67F4F53}
+ {6A52197D-7F3F-46F5-B549-A4DFEDD48C48} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {807C54A7-C3E0-4F31-95A8-C9FD3F4D5660}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1EDE8F07-AE3A-4556-AEF0-3AC518F18F92}