├── .config └── dotnet-tools.json ├── .editorconfig ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── Compilers ├── AssemblyInfo.cs ├── CompileException.cs ├── CompilerExceptions.cs ├── Cyjb.Compilers.csproj ├── LICENSE.txt ├── Lexers │ ├── AcceptState.cs │ ├── CharClass.cs │ ├── CharClassCollection.cs │ ├── CharClassMapBuilder.cs │ ├── CharClassSet.cs │ ├── Dfa.cs │ ├── DfaData.cs │ ├── DfaDataBuilder.cs │ ├── DfaState.cs │ ├── EofBuilder`2.cs │ ├── ITerminalBuilder`2.cs │ ├── LexerContext.cs │ ├── LexerSymbolAttrInfo.cs │ ├── Lexer`1.cs │ ├── Lexer`2.cs │ ├── Nfa.cs │ ├── NfaBuildResult.cs │ ├── NfaState.cs │ ├── NfaStateType.cs │ ├── TerminalBuilder`2.cs │ ├── TerminalMatch.cs │ └── Terminal`1.cs ├── Output.cs ├── OutputTableColumn.cs ├── Parsers │ ├── AcceptAction.cs │ ├── Action.cs │ ├── ActionCollection`1.cs │ ├── AssociativeType.cs │ ├── Associativity.cs │ ├── CandidateAction`1.cs │ ├── LRItemCollection`1.cs │ ├── LRItem`1.cs │ ├── LRState`1.cs │ ├── ParserConflict`1.cs │ ├── Parser`1.cs │ ├── Parser`2.Build.cs │ ├── Parser`2.cs │ ├── ProductionBuilder`2.cs │ ├── Production`1.cs │ ├── ReduceAction.cs │ ├── ShiftAction`1.cs │ ├── StartSymbol`1.cs │ ├── SymbolStartType.cs │ ├── SymbolType.cs │ └── Symbol`1.cs ├── README.md ├── RegularExpressions │ ├── AlternationExp.cs │ ├── AnchorExp.cs │ ├── CharClassExp.cs │ ├── ConcatenationExp.cs │ ├── EndOfFileExp.cs │ ├── LexRegex.cs │ ├── LiteralExp.cs │ ├── QuantifierExp.cs │ ├── RegexCharClass.cs │ └── RegexParser.cs ├── Resources.cs ├── Resources.resx ├── Resources.tt └── Resources.zh-Hans.resx ├── Cyjb.Compilers.sln ├── Design ├── CompilerTemplate.t4 ├── Cyjb.Compilers.Design.csproj ├── LICENSE.txt ├── Lexers │ ├── LexerContextAttribute.cs │ ├── LexerInclusiveContextAttribute.cs │ ├── LexerRegexAttribute.cs │ ├── LexerRejectableAttribute.cs │ └── LexerSymbolAttribute.cs ├── Parsers │ ├── ParserLeftAssociateAttribute.cs │ ├── ParserNonAssociateAttribute.cs │ ├── ParserProductionAttribute.cs │ ├── ParserRightAssociateAttribute.cs │ └── ParserStartAttribute.cs ├── README.md └── Tools │ ├── Cyjb.Compilers.Design.dll │ ├── Cyjb.Compilers.Runtime.dll │ ├── Cyjb.Compilers.dll │ ├── Cyjb.dll │ ├── Generator.deps.json │ ├── Generator.dll │ ├── Generator.runtimeconfig.json │ ├── Microsoft.CodeAnalysis.CSharp.dll │ ├── Microsoft.CodeAnalysis.dll │ ├── System.Collections.Immutable.dll │ ├── System.Reflection.Metadata.dll │ └── zh-Hans │ ├── Cyjb.Compilers.Runtime.resources.dll │ ├── Cyjb.Compilers.resources.dll │ ├── Cyjb.resources.dll │ ├── Generator.resources.dll │ ├── Microsoft.CodeAnalysis.CSharp.resources.dll │ └── Microsoft.CodeAnalysis.resources.dll ├── Generator ├── CodeAnalysis.CSharp │ ├── AttributeArguments.cs │ ├── AttributeModel.cs │ ├── AttributeParameterInfo.cs │ ├── AttributeSyntaxUtil.cs │ ├── Builders │ │ ├── ArgumentBuilder.cs │ │ ├── ArgumentListBuilder.cs │ │ ├── AttributeArgumentBuilder.cs │ │ ├── AttributeArgumentListBuilder.cs │ │ ├── AttributeBuilder.cs │ │ ├── AttributeListBuilder.cs │ │ ├── Declarations │ │ │ ├── BaseMethodDeclarationBuilder.cs │ │ │ ├── ClassDeclarationBuilder.cs │ │ │ ├── ConstructorDeclarationBuilder.cs │ │ │ ├── FieldDeclarationBuilder.cs │ │ │ ├── MemberDeclarationBuilder.cs │ │ │ └── MethodDeclarationBuilder.cs │ │ ├── DocumentationCommentTriviaBuilder.cs │ │ ├── Expressions │ │ │ ├── ArrayCreationExpressionBuilder.cs │ │ │ ├── AssignmentExpressionBuilder.cs │ │ │ ├── BinaryExpressionBuilder.cs │ │ │ ├── CastExpressionBuilder.cs │ │ │ ├── ElementAccessExpressionBuilder.cs │ │ │ ├── ExpressionBuilder.cs │ │ │ ├── InitializerExpressionBuilder.cs │ │ │ ├── InvocationExpressionBuilder.cs │ │ │ ├── LambdaExpressionBuilder.cs │ │ │ ├── MemberAccessExpressionBuilder.cs │ │ │ ├── NameBuilder.cs │ │ │ ├── ObjectCreationExpressionBuilder.cs │ │ │ ├── ParenthesizedExpressionBuilder.cs │ │ │ ├── PrefixUnaryExpressionBuilder.cs │ │ │ ├── TypeBuilder.cs │ │ │ └── TypeOfExpressionBuilder.cs │ │ ├── ModifierBuilder.cs │ │ ├── ParameterBuilder.cs │ │ ├── ParameterListBuilder.cs │ │ ├── Pattern │ │ │ ├── BinaryPatternBuilder.cs │ │ │ ├── PatternBuilder.cs │ │ │ └── RelationalPatternBuilder.cs │ │ ├── SeparatedListUtils.cs │ │ └── Statements │ │ │ ├── BlockBuilder.cs │ │ │ ├── BreakStatementBuilder.cs │ │ │ ├── ExpressionStatementBuilder.cs │ │ │ ├── GotoStatementBuilder.cs │ │ │ ├── IfStatementBuilder.cs │ │ │ ├── LabeledStatementBuilder.cs │ │ │ ├── LocalDeclarationStatementBuilder.cs │ │ │ ├── ReturnStatementBuilder.cs │ │ │ ├── StatementBuilder.cs │ │ │ ├── SwitchSectionBuilder.cs │ │ │ ├── SwitchStatementBuilder.cs │ │ │ └── WhileStatementBuilder.cs │ ├── CSharpException.cs │ ├── ExpressionSyntaxUtil.cs │ ├── IndentDetector.cs │ ├── NameSyntaxUtil.cs │ ├── NamespaceRewriter.cs │ ├── ParameterSyntaxUtil.cs │ ├── SyntaxBuilder.cs │ ├── SyntaxFormat.cs │ ├── SyntaxNodeUtils.cs │ ├── SyntaxTokenUtils.cs │ └── XmlNodeSyntaxOrString.cs ├── Controller.cs ├── ControllerVisitor.cs ├── Cyjb.Compilers.Generator.csproj ├── LICENSE.txt ├── Lexers │ ├── LexerController.CharClass.cs │ ├── LexerController.Contexts.cs │ ├── LexerController.Terminals.cs │ ├── LexerController.cs │ └── LexerSymbolAttrInfo.cs ├── Parsers │ ├── ParserController.Goto.cs │ ├── ParserController.KindMap.cs │ ├── ParserController.Productions.cs │ ├── ParserController.States.cs │ └── ParserController.cs ├── Program.cs ├── Properties │ ├── PublishProfiles │ │ └── Publish.pubxml │ └── launchSettings.json ├── README.md ├── Resources.cs ├── Resources.resx ├── Resources.tt ├── Resources.zh-Hans.resx ├── SymbolKind.cs ├── TransformationContext.cs └── tests │ ├── TestCalcLexer.cs │ ├── TestEscapeStrController.cs │ ├── TestEscapeStrController.second.cs │ ├── TestProductionController.cs │ ├── TestProductionParser.cs │ ├── TestProductionParser.designed.cs │ ├── TestProductionParser.tt │ ├── TestSymbolValueLexer.cs │ └── UnitTestTemplateDiagnostics.template.cs ├── README.md ├── Runtime ├── AssemblyInfo.cs ├── Cyjb.Compilers.Runtime.csproj ├── LICENSE.txt ├── Lexers │ ├── CharClassMap.cs │ ├── ContextData.cs │ ├── Core │ │ ├── BasicCore`1.cs │ │ ├── BasicDebugCore.cs │ │ ├── FixedTrailingCore`1.cs │ │ ├── FixedTrailingDebugCore`1.cs │ │ ├── LexerCore`1.cs │ │ ├── LexerStateInfo.cs │ │ ├── RejectableCore`1.cs │ │ ├── RejectableDebugCore`1.cs │ │ ├── RejectableTrailingCore`1.cs │ │ └── RejectableTrailingDebugCore`1.cs │ ├── DfaStateData.cs │ ├── ILexerFactory`1.cs │ ├── LexerController`1.cs │ ├── LexerData`1.cs │ ├── LexerFactory`1.cs │ ├── LexerFactory`2.cs │ ├── LexerRunner`1.cs │ ├── LexerTokenizer`1.cs │ ├── RejectOptions.cs │ ├── TerminalData`1.cs │ └── TrailingType.cs ├── Parsers │ ├── EmptyTokenizer`1.cs │ ├── IParserFactory`1.cs │ ├── InsertTokenCandidate.cs │ ├── LRParser`1.cs │ ├── ParseOptions.cs │ ├── ParserAction.cs │ ├── ParserActionType.cs │ ├── ParserController`1.cs │ ├── ParserData.cs │ ├── ParserData`1.cs │ ├── ParserFactory`1.cs │ ├── ParserFactory`2.cs │ ├── ParserStateData`1.cs │ ├── ProductionAction.cs │ ├── ProductionData`1.cs │ └── SymbolOptions.cs ├── README.md ├── Resources.cs ├── Resources.resx ├── Resources.tt ├── Resources.zh-Hans.resx └── Text │ ├── EnumerableTokenizer`1.cs │ ├── ITokenParser`1.cs │ ├── ITokenizer`1.cs │ ├── MissingTokenError.cs │ ├── ParseStatus.cs │ ├── ParserNode`1.cs │ ├── SourceReader │ ├── PartialSourceReader.cs │ ├── SourceReader.cs │ ├── StringSourceReader.cs │ └── StringViewSourceReader.cs │ ├── TokenCollection`1.cs │ ├── TokenDisplayNameAttribute.cs │ ├── TokenParseError.cs │ ├── TokenSpanComparer`1.cs │ ├── Token`1.DisplayName.cs │ ├── Token`1.cs │ ├── TokenizeError.cs │ ├── UnexpectedTokenError.cs │ └── Utils.cs ├── TestCompilers ├── Calc.cs ├── CompilerTemplate.t4 ├── Lexers │ ├── LexerRunnerContext.cs │ ├── Shortest │ │ ├── TestShortestLexer1.cs │ │ ├── TestShortestLexer1.designed.cs │ │ ├── TestShortestLexer1.tt │ │ ├── TestShortestLexer2.cs │ │ ├── TestShortestLexer2.designed.cs │ │ ├── TestShortestLexer2.tt │ │ ├── TestShortestLexer3.cs │ │ ├── TestShortestLexer3.designed.cs │ │ ├── TestShortestLexer3.tt │ │ ├── TestShortestLexer4.cs │ │ ├── TestShortestLexer4.designed.cs │ │ ├── TestShortestLexer4.tt │ │ └── UnitTestShortestLexer.cs │ ├── TestCalcLexer.cs │ ├── TestCalcLexer.designed.cs │ ├── TestCalcLexer.tt │ ├── TestCalcRunnerLexer.cs │ ├── TestCalcRunnerLexer.designed.cs │ ├── TestCalcRunnerLexer.tt │ ├── TestEscapeStrLexer.cs │ ├── TestEscapeStrLexer.designed.cs │ ├── TestEscapeStrLexer.tt │ ├── TestProductionLexer.cs │ ├── TestProductionLexer.designed.cs │ ├── TestProductionLexer.tt │ ├── TestStrLexer.cs │ ├── TestStrLexer.designed.cs │ ├── TestStrLexer.tt │ ├── TestSymbolValueLexer.cs │ ├── TestSymbolValueLexer.designed.cs │ ├── TestSymbolValueLexer.tt │ ├── Trailing │ │ ├── TestTrailingLexer1.cs │ │ ├── TestTrailingLexer1.designed.cs │ │ ├── TestTrailingLexer1.tt │ │ ├── TestTrailingLexer2.cs │ │ ├── TestTrailingLexer2.designed.cs │ │ ├── TestTrailingLexer2.tt │ │ ├── TestTrailingLexer3.cs │ │ ├── TestTrailingLexer3.designed.cs │ │ ├── TestTrailingLexer3.tt │ │ ├── TestTrailingLexerEOL.cs │ │ ├── TestTrailingLexerEOL.designed.cs │ │ ├── TestTrailingLexerEOL.tt │ │ └── UnitTestTrailingLexer.cs │ ├── UnitTestCharClassCollection.cs │ ├── UnitTestCharClassMap.cs │ ├── UnitTestLexer.cancel.cs │ ├── UnitTestLexer.cs │ ├── UnitTestLexer.merge.cs │ ├── UnitTestLexer.rejectable.cs │ ├── UnitTestLexer.token.cs │ ├── UnitTestLexerRunner.cs │ ├── UnitTestNFA.cs │ ├── UnitTestTemplateDiagnostics.cs │ └── UnitTestTemplateDiagnostics.template.cs ├── Parsers │ ├── TestCalcParser.cs │ ├── TestCalcParser.designed.cs │ ├── TestCalcParser.tt │ ├── TestProductionParser.cs │ ├── TestProductionParser.designed.cs │ ├── TestProductionParser.tt │ ├── UnitTestParser.cancel.cs │ ├── UnitTestParser.cs │ ├── UnitTestParser.option.cs │ ├── UnitTestParser.recover.cs │ └── UnitTestParser.start.cs ├── ProductionKind.cs ├── RegularExpressions │ ├── UnitTestLexRegex.cs │ └── UnitTestRegexCharClass.cs ├── TestCompilers.csproj ├── TestKind.cs └── Text │ ├── UnitTestSourceReader.cs │ └── UnitTestToken`1.cs └── generate-code-coverage-report.ps1 /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-reportgenerator-globaltool": { 6 | "version": "5.1.18", 7 | "commands": [ 8 | "reportgenerator" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] # 对于所有文件 2 | # 缩进风格 3 | indent_style = tab 4 | # 缩进宽度 5 | tab_width = 4 6 | # 文件编码格式 7 | charset = utf-8 8 | # 行尾格式 9 | end_of_line = crlf 10 | # 文件结尾添加换行符 11 | insert_final_newline = true 12 | 13 | [*.cs] 14 | # IDE0180: 使用元组交换值 15 | csharp_style_prefer_tuple_swap = false 16 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: windows-latest 16 | strategy: 17 | matrix: 18 | dotnet-version: [ '6.0.x' ] 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Setup .NET ${{ matrix.dotnet-version }} 23 | uses: actions/setup-dotnet@v3 24 | with: 25 | dotnet-version: ${{ matrix.dotnet-version }} 26 | - name: Restore dependencies 27 | run: dotnet restore 28 | - name: Build 29 | run: dotnet build --no-restore 30 | - name: Test 31 | run: dotnet test --no-build --verbosity normal --logger trx --results-directory "TestResults-${{ matrix.dotnet-version }}" 32 | - name: Upload test results 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: dotnet-results-${{ matrix.dotnet-version }} 36 | path: TestResults-${{ matrix.dotnet-version }} 37 | # Use always() to always run this step to publish test results when there are test failures 38 | if: ${{ always() }} 39 | - name: Upload coverage reports to Codecov 40 | uses: codecov/codecov-action@v3 41 | with: 42 | token: ${{ secrets.CODECOV_TOKEN }} 43 | fail_ci_if_error: true 44 | paths: ./**/coverage.opencover.xml 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders 2 | bin 3 | obj 4 | *.pdb 5 | .vs 6 | 7 | # mstest test results 8 | TestResults 9 | CodeCoverage 10 | coverage.cobertura.xml 11 | 12 | # test project 13 | ConsoleTest 14 | 15 | # files 16 | *.suo 17 | *.snk 18 | *.vspx 19 | *.user 20 | update-version.ps1 21 | publish.ps1 22 | -------------------------------------------------------------------------------- /Compilers/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // 有关程序集的常规信息通过以下 5 | // 特性集控制。更改这些特性值可修改 6 | // 与程序集关联的信息。 7 | [assembly: AssemblyTrademark("CYJB")] 8 | [assembly: AssemblyCulture("")] 9 | [assembly: CLSCompliant(false)] 10 | 11 | // 将 ComVisible 设置为 false 使此程序集中的类型 12 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 13 | // 则将该类型上的 ComVisible 特性设置为 true。 14 | [assembly: ComVisible(false)] 15 | 16 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 17 | [assembly: Guid("12f9eef0-249a-41be-a68f-72c7b6f426b1")] 18 | 19 | -------------------------------------------------------------------------------- /Compilers/CompileException.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers; 2 | 3 | /// 4 | /// 表示编译相关的异常。 5 | /// 6 | public sealed class CompileException : Exception 7 | { 8 | /// 9 | /// 使用指定的错误消息初始化 类的新实例。 10 | /// 11 | /// 错误消息。 12 | public CompileException(string message) : base(message) { } 13 | } 14 | -------------------------------------------------------------------------------- /Compilers/CompilerExceptions.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers; 2 | 3 | /// 4 | /// 提供用于异常处理的辅助方法。 5 | /// 6 | internal static class CompilerExceptions 7 | { 8 | /// 9 | /// 返回重复的词法分析上下文的异常。 10 | /// 11 | /// 词法分析上下文名称。 12 | /// 对象。 13 | public static ArgumentException DuplicateLexerContext(string? context) 14 | { 15 | return new ArgumentException(Resources.DuplicateLexerContext(context)); 16 | } 17 | 18 | /// 19 | /// 返回产生式的优先级必须是一个终结符的异常。 20 | /// 21 | /// 异常的符号名称。 22 | /// 对象。 23 | public static InvalidOperationException PrecedenceMustBeTerminal(string name) 24 | { 25 | return new InvalidOperationException(Resources.PrecedenceMustBeTerminal(name)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Compilers/Cyjb.Compilers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | True 7 | True 8 | https://github.com/CYJB/Cyjb.Compilers 9 | 提供编译相关功能 10 | Copyright (c) 2022, CYJB 11 | README.md 12 | LICENSE.txt 13 | en 14 | $(VersionPrefix) 15 | 1.0.23 16 | Cyjb.Compilers 17 | CYJB 18 | true 19 | snupkg 20 | 21 | 22 | ../CYJB_Code_Key.snk 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | True 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | True 40 | True 41 | Resources.tt 42 | 43 | 44 | 45 | 46 | TextTemplatingFileGenerator 47 | Resources.cs 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Compilers/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, CYJB 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Compilers/Lexers/AcceptState.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示一个接受状态。 5 | /// 6 | internal struct AcceptState 7 | { 8 | /// 9 | /// 被接受的符号标识符。 10 | /// 11 | public IList SymbolIndex; 12 | /// 13 | /// 当前的源文件索引。 14 | /// 15 | public int Index; 16 | 17 | /// 18 | /// 使用被接受的符号标识符和源文件索引初始化 结构的新实例。 19 | /// 20 | /// 被接受的符号标识符。 21 | /// 当前的源文件索引。 22 | internal AcceptState(IList symbolIndex, int index) 23 | { 24 | SymbolIndex = symbolIndex; 25 | Index = index; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Compilers/Lexers/CharClass.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Collections; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | /// 6 | /// 表示一个字符类。 7 | /// 8 | public class CharClass 9 | { 10 | /// 11 | /// 使用指定的字符集合初始化。 12 | /// 13 | /// 当前字符类的索引。 14 | /// 当前字符类包含的字符集合。 15 | internal CharClass(int index, CharSet chars) 16 | { 17 | Index = index; 18 | Chars = chars; 19 | Containers = new HashSet(); 20 | } 21 | 22 | /// 23 | /// 获取当前字符类的索引。 24 | /// 25 | public int Index { get; internal set; } 26 | /// 27 | /// 获取当前字符类包含的字符集合。 28 | /// 29 | public CharSet Chars { get; } 30 | /// 31 | /// 获取包含当前字符类的字符类集合。 32 | /// 33 | internal ISet Containers { get; } 34 | 35 | /// 36 | /// 分割当前字符类。 37 | /// 38 | /// 新字符类的索引。 39 | /// 新字符类包含的字符集合。 40 | /// 新的字符类。 41 | internal CharClass Split(int index, CharSet chars) 42 | { 43 | // 1. 剔除被分割的部分。 44 | Chars.ExceptWith(chars); 45 | // 2. 创建新字符类。 46 | CharClass newItem = new(index, chars); 47 | if (chars.Count > 1) 48 | { 49 | // 复制所属字符类集合。 50 | newItem.Containers.UnionWith(Containers); 51 | } 52 | // 3. 更新现有字符类。 53 | foreach (CharClassSet charClass in Containers) 54 | { 55 | charClass.Add(newItem); 56 | } 57 | // 4. 如果分割后的现有字符类只包含一个字符,无法继续分割,那么可以移除 Containers 58 | if (Chars.Count == 1) 59 | { 60 | Containers.Clear(); 61 | } 62 | return newItem; 63 | } 64 | 65 | /// 66 | /// 返回当前对象的字符串表示形式。 67 | /// 68 | /// 当前对象的字符串表示形式。 69 | public override string ToString() 70 | { 71 | return Chars.ToString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Compilers/Lexers/DfaData.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// DFA 的数据。 5 | /// 6 | public sealed class DfaData 7 | { 8 | /// 9 | /// 使用指定的数据初始化 类的新实例。 10 | /// 11 | /// DFA 的状态列表。 12 | /// DFA 的状态转移。 13 | public DfaData(int[] states, int[] trans) 14 | { 15 | States = states; 16 | Trans = trans; 17 | } 18 | 19 | /// 20 | /// 获取 DFA 的状态列表。 21 | /// 22 | public int[] States { get; } 23 | /// 24 | /// 获取 DFA 的状态转移。 25 | /// 26 | /// 使用 trans[i] 表示 checktrans[i+1] 表示 next 27 | public int[] Trans { get; } 28 | } 29 | -------------------------------------------------------------------------------- /Compilers/Lexers/ITerminalBuilder`2.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 词法分析的终结符构造器。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 词法分析控制器的类型。 8 | public interface ITerminalBuilder 9 | where T : struct 10 | where TController : LexerController, new() 11 | { 12 | /// 13 | /// 设置正则表达式的上下文。 14 | /// 15 | /// 正则表达式的上下文。 16 | /// 终结符的构造器。 17 | ITerminalBuilder Context(params string[] contexts); 18 | 19 | /// 20 | /// 设置终结符的词法单元类型。 21 | /// 22 | /// 词法单元的类型。 23 | /// 终结符的构造器。 24 | ITerminalBuilder Kind(T kind); 25 | 26 | /// 27 | /// 设置终结符的值。 28 | /// 29 | /// 词法单元的值。 30 | /// 终结符的构造器。 31 | ITerminalBuilder Value(object? value); 32 | 33 | /// 34 | /// 设置使用终结符的最短匹配。 35 | /// 36 | /// 终结符的构造器。 37 | /// 默认都会使用正则表达式的最长匹配,允许指定为使用最短匹配, 38 | /// 会在遇到第一个匹配时立即返回结果。 39 | ITerminalBuilder UseShortest(); 40 | 41 | /// 42 | /// 设置终结符的动作。 43 | /// 44 | /// 终结符的动作。 45 | /// 终结符的构造器。 46 | void Action(Action action); 47 | } 48 | -------------------------------------------------------------------------------- /Compilers/Lexers/LexerContext.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 词法分析器上下文的类型。 5 | /// 6 | internal enum LexerContextType 7 | { 8 | /// 9 | /// 包含型上下文,默认上下文的规则也会有效。 10 | /// 11 | Inclusive, 12 | /// 13 | /// 排除型上下文,默认上下文的规则无效。 14 | /// 15 | Exclusive 16 | } 17 | 18 | /// 19 | /// 表示词法分析器的上下文。 20 | /// 21 | internal sealed class LexerContext 22 | { 23 | /// 24 | /// 使用指定的上下文信息初始化 的新实例。 25 | /// 26 | /// 上下文的索引。 27 | /// 上下文的标签。 28 | /// 上下文的类型。 29 | public LexerContext(int index, string label, LexerContextType type) 30 | { 31 | Index = index; 32 | Label = label; 33 | Type = type; 34 | } 35 | 36 | /// 37 | /// 获取上下文的索引。 38 | /// 39 | public int Index { get; } 40 | /// 41 | /// 获取上下文的标签。 42 | /// 43 | public string Label { get; } 44 | /// 45 | /// 获取上下文的类型。 46 | /// 47 | public LexerContextType Type { get; } 48 | /// 49 | /// 上下文的 EOF 动作。 50 | /// 51 | public Delegate? EofAction { get; set; } 52 | /// 53 | /// 上下文的 EOF 动作的值。 54 | /// 55 | public object? EofValue { get; set; } 56 | 57 | /// 58 | /// 返回词法分析器的上下文数据。 59 | /// 60 | /// 词法分析器的上下文数据。 61 | public ContextData GetData() 62 | { 63 | return new ContextData(Index, Label, EofAction, EofValue); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Compilers/Lexers/Lexer`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析规则。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 8 | /// 泛型参数 一般是一个枚举类型,用于标识词法单元。 9 | /// 对于词法分析中的冲突,总是选择最长的词素。如果最长的词素可以与多个模式匹配, 10 | /// 则选择最先被定义的模式。关于词法分析的相关信息,请参考我的系列博文 11 | /// 12 | /// 《C# 词法分析器(一)词法分析介绍》,词法分析器的使用指南请参见 13 | /// 14 | /// 《C# 词法分析器(七)总结》 15 | /// 16 | /// 下面简单的构造一个数学算式的词法分析器: 17 | /// 18 | /// enum Calc { Id, Add, Sub, Mul, Div, Pow, LBrace, RBrace } 19 | /// Lexer<Calc> lexer = new Lexer<Calc>(); 20 | /// // 终结符的定义。 21 | /// lexer.DefineSymbol("[0-9]+").Kind(Calc.Id).Action(c => c.Accept(int.Parse(c.Text))); 22 | /// lexer.DefineSymbol("\\+").Kind(Calc.Add); 23 | /// lexer.DefineSymbol("\\-").Kind(Calc.Sub); 24 | /// lexer.DefineSymbol("\\*").Kind(Calc.Mul); 25 | /// lexer.DefineSymbol("\\/").Kind(Calc.Div); 26 | /// lexer.DefineSymbol("\\^").Kind(Calc.Pow); 27 | /// lexer.DefineSymbol("\\(").Kind(Calc.LBrace); 28 | /// lexer.DefineSymbol("\\)").Kind(Calc.RBrace); 29 | /// // 吃掉所有空白。 30 | /// lexer.DefineSymbol("\\s"); 31 | /// ILexerFactory<Calc> lexerFactory = lexer.GetFactory(); 32 | /// // 要分析的源文件。 33 | /// string source = "1 + 20 * 3 / 4*(5+6)"; 34 | /// ITokenizer<Calc> tokenizer = lexerFactory.CreateTokenizer(source); 35 | /// // 构造词法分析器。 36 | /// foreach (Token<Calc> token in tokenizer) 37 | /// { 38 | /// Console.WriteLine(token); 39 | /// } 40 | /// 41 | /// 42 | /// 43 | /// 《C# 词法分析器(一)词法分析介绍》 44 | /// 45 | /// 《C# 词法分析器(七)总结》 46 | public sealed class Lexer : Lexer> 47 | where T : struct 48 | { } 49 | -------------------------------------------------------------------------------- /Compilers/Lexers/NfaBuildResult.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// NFA 的构造结果。 5 | /// 6 | public struct NfaBuildResult 7 | { 8 | /// 9 | /// 结果的首状态。 10 | /// 11 | public NfaState Head; 12 | /// 13 | /// 结果的尾状态。 14 | /// 15 | public NfaState Tail; 16 | /// 17 | /// 是否是行首限定的。 18 | /// 19 | public bool BeginningOfLine; 20 | } 21 | -------------------------------------------------------------------------------- /Compilers/Lexers/NfaStateType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示 NFA 状态的类型。 5 | /// 6 | public enum NfaStateType 7 | { 8 | /// 9 | /// 普通状态。 10 | /// 11 | Normal, 12 | /// 13 | /// 向前看状态的头。 14 | /// 15 | TrailingHead, 16 | /// 17 | /// 向前看状态。 18 | /// 19 | Trailing 20 | } 21 | -------------------------------------------------------------------------------- /Compilers/Lexers/TerminalMatch.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.RegularExpressions; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | /// 6 | /// 表示词法分析中的终结符的匹配信息。 7 | /// 8 | internal sealed class TerminalMatch 9 | { 10 | /// 11 | /// 使用终结符的正则表达式初始化 类的新实例。 12 | /// 13 | /// 终结符对应的正则表达式。 14 | public TerminalMatch(LexRegex regex) 15 | { 16 | RegularExpression = regex; 17 | Context = new HashSet(); 18 | } 19 | 20 | /// 21 | /// 获取当前终结符对应的正则表达式。 22 | /// 23 | public LexRegex RegularExpression { get; } 24 | /// 25 | /// 获取定义当前终结符的上下文。 26 | /// 27 | /// 定义当前终结符的上下文。 28 | public HashSet Context { get; } 29 | } 30 | -------------------------------------------------------------------------------- /Compilers/Output.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cyjb.Compilers; 4 | 5 | /// 6 | /// 提供文本输出的工具方法。 7 | /// 8 | internal class Output 9 | { 10 | /// 11 | /// 返回索引的宽度。 12 | /// 13 | /// 索引的总个数。 14 | /// 索引位置的宽度。 15 | public static int GetIndexWidth(int count) 16 | { 17 | if (count <= 10) 18 | { 19 | return 1; 20 | } 21 | return (int)Math.Log10(count - 1) + 1; 22 | } 23 | 24 | /// 25 | /// 输出指定的表格。 26 | /// 27 | /// 要输出到的文本。 28 | /// 表格的列定义。 29 | /// 表格数据。 30 | public static void AppendTable(StringBuilder text, IList columns, IList table) 31 | { 32 | int rowCount = table.Count; 33 | int indexWidth = GetIndexWidth(rowCount); 34 | text.Append(new string(' ', indexWidth)); 35 | int columnCount = columns.Count; 36 | int[] widths = new int[columnCount]; 37 | for (int i = 0; i < columnCount; i++) 38 | { 39 | OutputTableColumn column = columns[i]; 40 | string name = column.Name; 41 | int width = 0; 42 | if (column.IsFixedWidth) 43 | { 44 | width = table.Select((row) => row[i].Length).Max(); 45 | if (width < name.Length) 46 | { 47 | width = name.Length; 48 | } 49 | // 使用两个空格分割列。 50 | width += 2; 51 | widths[i] = width; 52 | } 53 | text.AppendFormat(name.PadLeft(width)); 54 | } 55 | text.AppendLine(); 56 | for (int i = 0; i < rowCount; i++) 57 | { 58 | string[] row = table[i]; 59 | text.AppendFormat(i.ToString().PadLeft(indexWidth)); 60 | for (int j = 0; j < columnCount; j++) 61 | { 62 | text.AppendFormat(row[j].PadLeft(widths[j])); 63 | } 64 | text.AppendLine(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Compilers/OutputTableColumn.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers; 2 | 3 | /// 4 | /// 文本输出的列定义。 5 | /// 6 | internal class OutputTableColumn 7 | { 8 | /// 9 | /// 使用列的名称初始化 类的新实例。 10 | /// 11 | /// 列的名称。 12 | /// 列是否是固定宽度。 13 | public OutputTableColumn(string name, bool isFixedWidth = true) 14 | { 15 | Name = name; 16 | IsFixedWidth = isFixedWidth; 17 | } 18 | 19 | /// 20 | /// 获取列的名称。 21 | /// 22 | public string Name { get; } 23 | /// 24 | /// 获取列是否是固定宽度。 25 | /// 26 | public bool IsFixedWidth { get;private set; } 27 | } 28 | -------------------------------------------------------------------------------- /Compilers/Parsers/AcceptAction.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示 LR 语法分析器的接受动作。 5 | /// 6 | internal class AcceptAction : Action 7 | { 8 | /// 9 | /// 高优先级接受动作的实例。 10 | /// 11 | public static readonly AcceptAction HighPriority = new(true); 12 | /// 13 | /// 低优先级接受动作的实例。 14 | /// 15 | public static readonly AcceptAction LowPriority = new(false); 16 | 17 | private AcceptAction(bool isHighPriority) 18 | { 19 | IsHighPriority = isHighPriority; 20 | } 21 | 22 | /// 23 | /// 获取当前动作是否是成功动作。 24 | /// 25 | public override bool IsSuccessAction => true; 26 | /// 27 | /// 获取当前动作是否是高优先级的。 28 | /// 29 | public bool IsHighPriority { get; } 30 | 31 | /// 32 | /// 返回当前动作对应的 。 33 | /// 34 | /// 当前动作对应的 35 | public override ParserAction ToParserAction() 36 | { 37 | return ParserAction.Accept; 38 | } 39 | 40 | /// 41 | /// 返回当前对象是否等于同一类型的另一对象。 42 | /// 43 | /// 要比较的对象。 44 | /// 如果当前对象等于 ,则为 true;否则为 false 45 | public override bool Equals(Action? other) 46 | { 47 | return other is AcceptAction; 48 | } 49 | 50 | /// 51 | /// 返回当前对象的哈希值。 52 | /// 53 | /// 当前对象的哈希值。 54 | public override int GetHashCode() 55 | { 56 | return 38189501; 57 | } 58 | 59 | /// 60 | /// 返回当前对象的字符串表示。 61 | /// 62 | /// 当前对象的字符串表示。 63 | public override string ToString() 64 | { 65 | return "acc"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Compilers/Parsers/AssociativeType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示终结符结合性的类型。 5 | /// 6 | public enum AssociativeType 7 | { 8 | /// 9 | /// 非结合的。 10 | /// 11 | NonAssociate, 12 | /// 13 | /// 左结合的。 14 | /// 15 | Left, 16 | /// 17 | /// 右结合的。 18 | /// 19 | Right 20 | } 21 | -------------------------------------------------------------------------------- /Compilers/Parsers/CandidateAction`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示候选动作。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal class CandidateAction 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的候选信息初始化 类的新实例。 12 | /// 13 | /// 候选项。 14 | /// 候选动作。 15 | public CandidateAction(LRItem item, Action action) 16 | { 17 | Item = item; 18 | Action = action; 19 | } 20 | 21 | /// 22 | /// 获取候选项。 23 | /// 24 | public LRItem Item { get; } 25 | /// 26 | /// 获取候选动作。 27 | /// 28 | public Action Action { get; } 29 | } 30 | -------------------------------------------------------------------------------- /Compilers/Parsers/ProductionBuilder`2.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示文法的产生式构造器。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 语法分析控制器的类型。 8 | public sealed class ProductionBuilder 9 | where T : struct 10 | where TController : ParserController, new() 11 | { 12 | /// 13 | /// 关联到的语法分析器。 14 | /// 15 | private readonly Parser parser; 16 | /// 17 | /// 关联到的产生式。 18 | /// 19 | private readonly Production production; 20 | 21 | /// 22 | /// 使用语法分析器和产生式初始化 类的新实例。 23 | /// 24 | /// 关联到的语法分析器。 25 | /// 关联到的产生式。 26 | internal ProductionBuilder(Parser parser, Production production) 27 | { 28 | this.parser = parser; 29 | this.production = production; 30 | } 31 | 32 | /// 33 | /// 设置当前产生式的动作。 34 | /// 35 | /// 当前产生式体的动作。 36 | /// 当前产生式。 37 | public ProductionBuilder Action(Func action) 38 | { 39 | production.Action = action; 40 | return this; 41 | } 42 | 43 | /// 44 | /// 设置当前产生式的优先级与指定的非终结符相同。 45 | /// 46 | /// 非终结符的标识符。 47 | /// 当前产生式。 48 | public ProductionBuilder Prec(T kind) 49 | { 50 | production.Precedence = parser.GetOrCreateSymbol(kind); 51 | return this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Compilers/Parsers/Production`1.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示文法的产生式。 7 | /// 8 | /// 词法单元标识符的类型,一般是一个枚举类型。 9 | internal sealed class Production 10 | where T : struct 11 | { 12 | /// 13 | /// 使用产生式的索引和内容初始化 类的新实例。 14 | /// 15 | /// 产生式的索引。 16 | /// 产生式头。 17 | /// 产生式体。 18 | internal Production(int index, Symbol head, params Symbol[] body) 19 | { 20 | Index = index; 21 | Head = head; 22 | Body = body; 23 | } 24 | 25 | /// 26 | /// 获取产生式的索引。 27 | /// 28 | /// 产生式的索引。 29 | internal int Index { get; } 30 | /// 31 | /// 获取产生式头。 32 | /// 33 | /// 产生式的头。 34 | internal Symbol Head { get; } 35 | /// 36 | /// 获取产生式体。 37 | /// 38 | /// 产生式体。 39 | internal Symbol[] Body { get; } 40 | /// 41 | /// 获取或设置产生式的动作。 42 | /// 43 | /// 产生式的动作。 44 | internal Delegate? Action { get; set; } 45 | /// 46 | /// 获取或设置表示当前产生式的结合性的非终结符。 47 | /// 48 | /// 表示当前产生式的结合性的非终结符的标识符。 49 | internal Symbol? Precedence { get; set; } 50 | 51 | /// 52 | /// 返回当前产生式的数据。 53 | /// 54 | /// 当前产生式的数据。 55 | public ProductionData GetData() 56 | { 57 | return new ProductionData(Head.Index, Head.Kind, Action, Body.Select(s => s.Kind).ToArray()); 58 | } 59 | 60 | /// 61 | /// 返回当前对象的字符串表示形式。 62 | /// 63 | /// 当前对象的字符串表示形式。 64 | public override string ToString() 65 | { 66 | StringBuilder text = new(); 67 | text.Append(Head.Name); 68 | text.Append(" ->"); 69 | if (Body.Length == 0) 70 | { 71 | text.Append(" ε"); 72 | } 73 | else 74 | { 75 | foreach (Symbol item in Body) 76 | { 77 | text.Append(' '); 78 | text.Append(item); 79 | } 80 | } 81 | return text.ToString(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Compilers/Parsers/ReduceAction.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示 LR 语法分析器的规约动作。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal class ReduceAction : Action 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的产生式初始化 类的新实例。 12 | /// 13 | /// 规约时用到的产生式。 14 | public ReduceAction(Production production) 15 | { 16 | Production = production; 17 | } 18 | 19 | /// 20 | /// 规约时用到的产生式。 21 | /// 22 | public Production Production { get; } 23 | 24 | /// 25 | /// 获取当前动作是否是成功动作。 26 | /// 27 | public override bool IsSuccessAction => true; 28 | 29 | /// 30 | /// 返回当前动作对应的 。 31 | /// 32 | /// 当前动作对应的 33 | public override ParserAction ToParserAction() 34 | { 35 | return ParserAction.Reduce(Production.Index); 36 | } 37 | 38 | /// 39 | /// 返回当前对象是否等于同一类型的另一对象。 40 | /// 41 | /// 要比较的对象。 42 | /// 如果当前对象等于 ,则为 true;否则为 false 43 | public override bool Equals(Action? other) 44 | { 45 | return other is ReduceAction action && Production == action.Production; 46 | } 47 | 48 | /// 49 | /// 返回当前对象的哈希值。 50 | /// 51 | /// 当前对象的哈希值。 52 | public override int GetHashCode() 53 | { 54 | return Production.GetHashCode(); 55 | } 56 | 57 | /// 58 | /// 返回当前对象的字符串表示。 59 | /// 60 | /// 当前对象的字符串表示。 61 | public override string ToString() 62 | { 63 | return $"r{Production.Index}"; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Compilers/Parsers/ShiftAction`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示 LR 语法分析器的移入动作。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal class ShiftAction : Action 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的状态初始化 类的新实例。 12 | /// 13 | /// 要移入的状态。 14 | public ShiftAction(LRState state) 15 | { 16 | State = state; 17 | } 18 | 19 | /// 20 | /// 要移入的状态。 21 | /// 22 | public LRState State { get; } 23 | 24 | /// 25 | /// 获取当前动作是否是成功动作。 26 | /// 27 | public override bool IsSuccessAction => true; 28 | 29 | /// 30 | /// 返回当前动作对应的 。 31 | /// 32 | /// 当前动作对应的 33 | public override ParserAction ToParserAction() 34 | { 35 | return ParserAction.Shift(State.Index); 36 | } 37 | 38 | /// 39 | /// 返回当前对象是否等于同一类型的另一对象。 40 | /// 41 | /// 要比较的对象。 42 | /// 如果当前对象等于 ,则为 true;否则为 false 43 | public override bool Equals(Action? other) 44 | { 45 | return other is ShiftAction action && State == action.State; 46 | } 47 | 48 | /// 49 | /// 返回当前对象的哈希值。 50 | /// 51 | /// 当前对象的哈希值。 52 | public override int GetHashCode() 53 | { 54 | return State.GetHashCode(); 55 | } 56 | 57 | /// 58 | /// 返回当前对象的字符串表示。 59 | /// 60 | /// 当前对象的字符串表示。 61 | public override string ToString() 62 | { 63 | return $"s{State.Index}"; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Compilers/Parsers/StartSymbol`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示起始非终结符。 5 | /// 6 | /// 7 | internal class StartSymbol 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的非终结符类型和增广起始符号初始化 类的新实例。 12 | /// 13 | /// 非终结符的类型。 14 | /// 增广起始符号。 15 | /// 分析选项。 16 | public StartSymbol(T kind, Symbol augmentedStartSymbol, ParseOptions option) 17 | { 18 | Kind = kind; 19 | AugmentedStartSymbol = augmentedStartSymbol; 20 | Option = option; 21 | } 22 | 23 | /// 24 | /// 获取非终结符的类型。 25 | /// 26 | public T Kind { get; } 27 | /// 28 | /// 获取增广起始符号。 29 | /// 30 | public Symbol AugmentedStartSymbol { get; } 31 | /// 32 | /// 获取分析选项。 33 | /// 34 | public ParseOptions Option { get; } 35 | } 36 | -------------------------------------------------------------------------------- /Compilers/Parsers/SymbolStartType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 符号的起始类型。 5 | /// 6 | internal enum SymbolStartType 7 | { 8 | /// 9 | /// 非起始符号。 10 | /// 11 | NotStart, 12 | /// 13 | /// 起始符号。 14 | /// 15 | Start, 16 | /// 17 | /// 增广起始符号(高接受优先级)。 18 | /// 19 | AugmentedStartHighPriority, 20 | /// 21 | /// 增广起始符号(低接受优先级)。 22 | /// 23 | AugmentedStartLowPriority, 24 | } 25 | -------------------------------------------------------------------------------- /Compilers/Parsers/SymbolType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 语法分析中的符号类型。 5 | /// 6 | internal enum SymbolType 7 | { 8 | /// 9 | /// 未知类型。 10 | /// 11 | Unknown, 12 | /// 13 | /// 终结符。 14 | /// 15 | Terminal, 16 | /// 17 | /// 非终结符。 18 | /// 19 | NonTerminal, 20 | /// 21 | /// 错误标记符。 22 | /// 23 | Error, 24 | /// 25 | /// 空串标记符。 26 | /// 27 | Epsilon, 28 | /// 29 | /// 传递标记符。 30 | /// 31 | Spread, 32 | /// 33 | /// 文件结束标记符。 34 | /// 35 | EndOfFile, 36 | } 37 | -------------------------------------------------------------------------------- /Compilers/Parsers/Symbol`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示语法分析中的符号。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal sealed class Symbol 8 | where T : struct 9 | { 10 | /// 11 | /// 文件结束标记符。 12 | /// 13 | public static readonly Symbol EndOfFile = new(GenericConvert.ChangeType(-1), "EOF") 14 | { 15 | Type = SymbolType.EndOfFile 16 | }; 17 | /// 18 | /// 错误标记符。 19 | /// 20 | public static readonly Symbol Error = new(GenericConvert.ChangeType(-2), "ERROR") 21 | { 22 | Type = SymbolType.Error 23 | }; 24 | /// 25 | /// 空串标记符。 26 | /// 27 | public static readonly Symbol Epsilon = new(GenericConvert.ChangeType(-3), "ε") 28 | { 29 | Type = SymbolType.Epsilon 30 | }; 31 | /// 32 | /// 传递标记符。 33 | /// 34 | public static readonly Symbol Spread = new(GenericConvert.ChangeType(-4), "#") 35 | { 36 | Type = SymbolType.Spread 37 | }; 38 | 39 | /// 40 | /// 使用指定的符号索引初始化 类的新实例。 41 | /// 42 | /// 符号的类型。 43 | /// 当前符号的名称。 44 | public Symbol(T kind, string name) 45 | { 46 | Index = -1; 47 | Kind = kind; 48 | Name = name; 49 | } 50 | 51 | /// 52 | /// 获取或设置当前符号的索引。 53 | /// 54 | /// 当前符号的索引。 55 | public int Index { get; set; } 56 | /// 57 | /// 获取当前符号的类型。 58 | /// 59 | /// 当前符号的类型。 60 | public T Kind { get; } 61 | /// 62 | /// 获取当前符号的名称。 63 | /// 64 | /// 当前符号的名称索引。 65 | public string Name { get; } 66 | /// 67 | /// 获取或设置当前符号的类型。 68 | /// 69 | public SymbolType Type { get; set; } 70 | /// 71 | /// 获取或设置起始符号类型。 72 | /// 73 | public SymbolStartType StartType { get; set; } = SymbolStartType.NotStart; 74 | /// 75 | /// 获取当前非终结符的所有产生式。 76 | /// 77 | /// 当前非终结符的所有产生式。 78 | public List> Productions { get; } = new(); 79 | 80 | /// 81 | /// 返回当前对象的字符串表示形式。 82 | /// 83 | /// 当前对象的字符串表示形式。 84 | public override string ToString() 85 | { 86 | return Name; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Compilers/RegularExpressions/EndOfFileExp.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cyjb.Compilers.RegularExpressions; 4 | 5 | /// 6 | /// 表示文件结束的正则表达式,只能用于先前看表达式中。 7 | /// 8 | public sealed class EndOfFileExp : LexRegex 9 | { 10 | /// 11 | /// 初始化 类的新实例。 12 | /// 13 | internal EndOfFileExp() { } 14 | 15 | /// 16 | /// 获取当前正则表达式匹配的字符串长度。 17 | /// 18 | /// 当前正则表达式匹配的字符串长度。如果可以匹配不同长度的字符串,则为 null 19 | public override int? Length => 0; 20 | 21 | /// 22 | /// 返回当前对象是否等于同一类型的另一对象。 23 | /// 24 | /// 要比较的对象。 25 | /// 如果当前对象等于 ,则为 true;否则为 false 26 | public override bool Equals(LexRegex? other) 27 | { 28 | return (other is EndOfFileExp); 29 | } 30 | 31 | /// 32 | /// 返回当前对象的哈希值。 33 | /// 34 | /// 当前对象的哈希值。 35 | public override int GetHashCode() 36 | { 37 | return typeof(EndOfFileExp).GetHashCode(); 38 | } 39 | 40 | /// 41 | /// 返回当前对象的字符串表示形式。 42 | /// 43 | /// 字符串构造器。 44 | internal override void ToString(StringBuilder builder) 45 | { 46 | builder.Append("{EOF}"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Compilers/Resources.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Compilers/Resources.cs -------------------------------------------------------------------------------- /Compilers/Resources.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="$(PkgCyjb)\content\ResourcesTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /Design/CompilerTemplate.t4: -------------------------------------------------------------------------------- 1 | <# 2 | /** 3 | * 生成词法分析器或语法分析器的实现。 4 | */ 5 | #> 6 | <#@ template language="C#" hostspecific="true" #> 7 | <#@output extension=".designed.cs" encoding="UTF-8"#> 8 | <#@ assembly name="System.Core" #> 9 | <#@ assembly name="System.Runtime" #> 10 | <#@ assembly name="EnvDTE" #> 11 | <#@ assembly name="VSLangProj" #> 12 | <#@ import namespace="EnvDTE" #> 13 | <#@ import namespace="System" #> 14 | <#@ import namespace="System.CodeDom.Compiler" #> 15 | <#@ import namespace="System.Diagnostics" #> 16 | <#@ import namespace="System.IO" #> 17 | <#@ import namespace="System.Text.RegularExpressions" #> 18 | <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> 19 | <# 20 | string toolPath = Host.ResolveAssemblyReference(@"$(PkgCyjb_Compilers_Design)\Tools\Generator.dll"); 21 | string filePath = Host.TemplateFile.Replace(".tt", ".cs"); 22 | try 23 | { 24 | using System.Diagnostics.Process myProcess = new(); 25 | myProcess.StartInfo.UseShellExecute = false; 26 | myProcess.StartInfo.RedirectStandardOutput = true; 27 | myProcess.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 28 | myProcess.StartInfo.FileName = "dotnet"; 29 | myProcess.StartInfo.Arguments = $"\"{Path.GetFullPath(toolPath)}\" \"{Path.GetFullPath(filePath)}\""; 30 | myProcess.StartInfo.CreateNoWindow = true; 31 | myProcess.Start(); 32 | string content = myProcess.StandardOutput.ReadToEnd(); 33 | myProcess.WaitForExit(); 34 | 35 | if (content.StartsWith(Path.GetFileName(filePath))) 36 | { 37 | // 包含异常 38 | string[] lines = content.Trim().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); 39 | Regex errorRegex = new Regex(@"^.+\((\d+),(\d+)\): (.+)$"); 40 | foreach(string line in lines) 41 | { 42 | Match match = errorRegex.Match(line); 43 | if (match.Success) { 44 | string lineNum = match.Groups[1].Captures[0].Value; 45 | string colNum = match.Groups[2].Captures[0].Value; 46 | string message = match.Groups[3].Captures[0].Value; 47 | Errors.Add(new CompilerError(filePath, int.Parse(lineNum), int.Parse(colNum), "", message)); 48 | } 49 | } 50 | Write("// 生成异常"); 51 | } else { 52 | Write(content); 53 | } 54 | } 55 | catch (Exception e) 56 | { 57 | Error($"执行 {toolPath} 失败:{e.Message}"); 58 | } 59 | #> 60 | 61 | -------------------------------------------------------------------------------- /Design/Cyjb.Compilers.Design.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | False 7 | True 8 | https://github.com/CYJB/Cyjb.Compilers 9 | 提供编译器的设计时生成功能 10 | Copyright (c) 2022, CYJB 11 | README.md 12 | LICENSE.txt 13 | en 14 | $(VersionPrefix) 15 | 1.0.23 16 | Cyjb.Compilers 17 | Library 18 | true 19 | CYJB 20 | Compiler, T4 21 | 22 | 23 | ../CYJB_Code_Key.snk 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | $(DefineConstants);DEBUG 32 | 33 | 34 | 35 | true 36 | true 37 | tools\ 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Design/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, CYJB 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Design/Lexers/LexerContextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | /// 6 | /// 表示词法分析器的上下文。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class LexerContextAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的上下文标签初始化 类的新实例。 17 | /// 18 | /// 上下文的标签。 19 | public LexerContextAttribute(string label) { } 20 | 21 | #pragma warning restore IDE0060 // 删除未使用的参数 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Design/Lexers/LexerInclusiveContextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | /// 6 | /// 表示词法分析器的包含型上下文。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class LexerInclusiveContextAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的上下文标签初始化 类的新实例。 17 | /// 18 | /// 上下文的标签。 19 | public LexerInclusiveContextAttribute(string label) { } 20 | 21 | #pragma warning restore IDE0060 // 删除未使用的参数 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Design/Lexers/LexerRegexAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Cyjb.Compilers.Lexers; 5 | 6 | /// 7 | /// 表示词法分析的正则表达式定义。 8 | /// 9 | [Conditional("DEBUG")] 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 11 | public sealed class LexerRegexAttribute : Attribute 12 | { 13 | 14 | #pragma warning disable IDE0060 // 删除未使用的参数 15 | 16 | /// 17 | /// 使用指定的正则表达式信息初始化 类的新实例。 18 | /// 19 | /// 正则表达式的名称。 20 | /// 定义的正则表达式。 21 | /// 正则表达式的选项。 22 | public LexerRegexAttribute(string name, string regex, RegexOptions options = RegexOptions.None) { } 23 | 24 | #pragma warning restore IDE0060 // 删除未使用的参数 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Design/Lexers/LexerRejectableAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | /// 6 | /// 表示词法分析是否用到了 Reject 动作。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public sealed class LexerRejectableAttribute : Attribute 11 | { 12 | /// 13 | /// 初始化 类的新实例。 14 | /// 15 | public LexerRejectableAttribute() { } 16 | } 17 | -------------------------------------------------------------------------------- /Design/Lexers/LexerSymbolAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Cyjb.Compilers.Lexers; 5 | 6 | /// 7 | /// 表示词法分析的终结符定义。 8 | /// 9 | [Conditional("DEBUG")] 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 11 | public sealed class LexerSymbolAttribute : Attribute 12 | { 13 | 14 | #pragma warning disable IDE0060 // 删除未使用的参数 15 | 16 | /// 17 | /// 使用指定的终结符信息初始化 类的新实例。 18 | /// 19 | /// 终结符的正则表达式。 20 | /// 正则表达式的选项。 21 | public LexerSymbolAttribute(string regex, RegexOptions options = RegexOptions.None) { } 22 | 23 | #pragma warning restore IDE0060 // 删除未使用的参数 24 | 25 | /// 26 | /// 获取或设置终结符的词法单元类型。 27 | /// 28 | public object? Kind { get; set; } 29 | 30 | /// 31 | /// 获取或设置终结符的词法单元值。 32 | /// 33 | public object? Value { get; set; } 34 | 35 | /// 36 | /// 获取或设置是否使用终结符的最短匹配。 37 | /// 38 | /// 默认都会使用正则表达式的最长匹配,允许指定为使用最短匹配, 39 | /// 会在遇到第一个匹配时立即返回结果。 40 | public bool UseShortest { get; set; } 41 | } 42 | -------------------------------------------------------------------------------- /Design/Parsers/ParserLeftAssociateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示语法分析器的左结合符号。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class ParserLeftAssociateAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的左结合符号初始化 类的新实例。 17 | /// 18 | /// 左结合符号。 19 | public ParserLeftAssociateAttribute(params object[] kinds) { } 20 | 21 | #pragma warning restore IDE0060 // 删除未使用的参数 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Design/Parsers/ParserNonAssociateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示语法分析器的非结合符号。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class ParserNonAssociateAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的非结合符号初始化 类的新实例。 17 | /// 18 | /// 非结合符号。 19 | public ParserNonAssociateAttribute(params object[] kinds) { } 20 | 21 | #pragma warning restore IDE0060 // 删除未使用的参数 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Design/Parsers/ParserProductionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示语法分析器的产生式。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 10 | public sealed class ParserProductionAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的产生式所属的非终结符和内容初始化 类的新实例。 17 | /// 18 | /// 产生式所属的非终结符。 19 | /// 产生式的内容。 20 | public ParserProductionAttribute(object kind, params object[] body) { } 21 | 22 | #pragma warning restore IDE0060 // 删除未使用的参数 23 | 24 | /// 25 | /// 获取或设置设置当前产生式的优先级与指定的非终结符相同。 26 | /// 27 | public object? Prec { get; set; } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Design/Parsers/ParserRightAssociateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示语法分析器的右结合符号。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class ParserRightAssociateAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的右结合符号初始化 类的新实例。 17 | /// 18 | /// 右结合符号。 19 | public ParserRightAssociateAttribute(params object[] kinds) { } 20 | 21 | #pragma warning restore IDE0060 // 删除未使用的参数 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Design/Parsers/ParserStartAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | /// 6 | /// 表示语法分析器的起始符号。 7 | /// 8 | [Conditional("DEBUG")] 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 10 | public sealed class ParserStartAttribute : Attribute 11 | { 12 | 13 | #pragma warning disable IDE0060 // 删除未使用的参数 14 | 15 | /// 16 | /// 使用指定的起始符号初始化 类的新实例。 17 | /// 18 | /// 语法分析器的起始符号。 19 | public ParserStartAttribute(object kind) { } 20 | 21 | /// 22 | /// 使用指定的起始符号和分析选项初始化 类的新实例。 23 | /// 24 | /// 语法分析器的起始符号。 25 | /// 语法分析器的分析选项。 26 | public ParserStartAttribute(object kind, ParseOptions option) { } 27 | 28 | #pragma warning restore IDE0060 // 删除未使用的参数 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Design/Tools/Cyjb.Compilers.Design.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Cyjb.Compilers.Design.dll -------------------------------------------------------------------------------- /Design/Tools/Cyjb.Compilers.Runtime.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Cyjb.Compilers.Runtime.dll -------------------------------------------------------------------------------- /Design/Tools/Cyjb.Compilers.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Cyjb.Compilers.dll -------------------------------------------------------------------------------- /Design/Tools/Cyjb.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Cyjb.dll -------------------------------------------------------------------------------- /Design/Tools/Generator.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Generator.dll -------------------------------------------------------------------------------- /Design/Tools/Generator.runtimeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeOptions": { 3 | "tfm": "net6.0", 4 | "framework": { 5 | "name": "Microsoft.NETCore.App", 6 | "version": "6.0.0" 7 | }, 8 | "configProperties": { 9 | "System.Reflection.Metadata.MetadataUpdater.IsSupported": false 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Design/Tools/Microsoft.CodeAnalysis.CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Microsoft.CodeAnalysis.CSharp.dll -------------------------------------------------------------------------------- /Design/Tools/Microsoft.CodeAnalysis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/Microsoft.CodeAnalysis.dll -------------------------------------------------------------------------------- /Design/Tools/System.Collections.Immutable.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/System.Collections.Immutable.dll -------------------------------------------------------------------------------- /Design/Tools/System.Reflection.Metadata.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/System.Reflection.Metadata.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Cyjb.Compilers.Runtime.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Cyjb.Compilers.Runtime.resources.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Cyjb.Compilers.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Cyjb.Compilers.resources.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Cyjb.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Cyjb.resources.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Generator.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Generator.resources.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll -------------------------------------------------------------------------------- /Design/Tools/zh-Hans/Microsoft.CodeAnalysis.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Design/Tools/zh-Hans/Microsoft.CodeAnalysis.resources.dll -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/AttributeArguments.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Syntax; 2 | 3 | namespace Cyjb.CodeAnalysis.CSharp; 4 | 5 | /// 6 | /// 特性的参数信息。 7 | /// 8 | internal class AttributeArguments 9 | { 10 | /// 11 | /// 参数值的字典。 12 | /// 13 | private readonly Dictionary arguments; 14 | 15 | /// 16 | /// 使用特性的参数信息初始化 类的新实例。 17 | /// 18 | /// 参数值的字典。 19 | /// params 参数值。 20 | public AttributeArguments(Dictionary arguments, ExpressionSyntax[] paramsArgument) 21 | { 22 | this.arguments = arguments; 23 | ParamsArgument = paramsArgument; 24 | } 25 | 26 | /// 27 | /// 获取指定名称参数的值。 28 | /// 29 | /// 要获取值的参数名称。 30 | /// 指定名称参数的值,如果不存在则返回 null 31 | public ExpressionSyntax? this[string name] 32 | { 33 | get 34 | { 35 | if (arguments.TryGetValue(name, out var value)) 36 | { 37 | return value; 38 | } 39 | return null; 40 | } 41 | } 42 | 43 | /// 44 | /// params 参数的值。 45 | /// 46 | public ExpressionSyntax[] ParamsArgument { get; } 47 | } 48 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/AttributeParameterInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Cyjb.Reflection; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 特性的参数信息。 8 | /// 9 | internal struct AttributeParameterInfo 10 | { 11 | /// 12 | /// 参数名称。 13 | /// 14 | public string Name; 15 | /// 16 | /// 是否是可选参数。 17 | /// 18 | public bool IsOptional; 19 | /// 20 | /// 是否是 params 参数。 21 | /// 22 | public bool IsParamArray; 23 | 24 | /// 25 | /// 使用指定的 初始化。 26 | /// 27 | /// 参数信息。 28 | public AttributeParameterInfo(ParameterInfo param) 29 | { 30 | Name = param.Name!; 31 | IsOptional = param.IsOptional; 32 | IsParamArray = param.IsParamArray(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/AttributeSyntaxUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 提供 相关的扩展方法。 8 | /// 9 | internal static class AttributeSyntaxUtil 10 | { 11 | /// 12 | /// 返回当前特性列表中的所有特性。 13 | /// 14 | /// 特性列表。 15 | /// 特性列表中包含的每个特性。 16 | public static IEnumerable GetAttributes(this SyntaxList attrList) 17 | { 18 | foreach (AttributeListSyntax list in attrList) 19 | { 20 | foreach (AttributeSyntax attr in list.Attributes) 21 | { 22 | yield return attr; 23 | } 24 | } 25 | } 26 | 27 | /// 28 | /// 返回当前特性的完整名称(包含 Attribute 后缀)。 29 | /// 30 | /// 要检查的特性。 31 | /// 特性的完整名称。 32 | public static string GetFullName(this AttributeSyntax attr) 33 | { 34 | if (attr.Name is NameSyntax nameSyntax) 35 | { 36 | string name = nameSyntax.GetSimpleName().ToString(); 37 | if (!name.EndsWith("Attribute")) 38 | { 39 | name += "Attribute"; 40 | } 41 | return name; 42 | } 43 | else 44 | { 45 | // 在正确的语法里,特性好像不太走到这里。 46 | return attr.Name.ToString(); 47 | } 48 | } 49 | 50 | /// 51 | /// 返回当前特性的参数列表。 52 | /// 53 | /// 要解析的特性。 54 | /// 特性的模型。 55 | /// 特性参数。 56 | /// 未能正确解析特性参数。 57 | public static AttributeArguments GetArguments(this AttributeSyntax attr, AttributeModel model) 58 | { 59 | return model.GetArguments(attr); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/ArgumentBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 实参的构造器。 9 | /// 10 | internal sealed class ArgumentBuilder 11 | { 12 | /// 13 | /// 参数的表达式。 14 | /// 15 | private readonly ExpressionBuilder expression; 16 | /// 17 | /// 参数的名称。 18 | /// 19 | private string? name; 20 | /// 21 | /// 参数引用的类型。 22 | /// 23 | private SyntaxKind refKind = SyntaxKind.None; 24 | 25 | /// 26 | /// 使用指定的表达式初始化 类的新实例。 27 | /// 28 | /// 参数的表达式。 29 | public ArgumentBuilder(ExpressionBuilder expression) 30 | { 31 | this.expression = expression; 32 | } 33 | 34 | /// 35 | /// 设置参数的名称。 36 | /// 37 | /// 参数的名称。 38 | /// 当前方法实参的构造器。 39 | public ArgumentBuilder Name(string name) 40 | { 41 | this.name = name; 42 | return this; 43 | } 44 | 45 | /// 46 | /// 设置参数的引用。 47 | /// 48 | /// 参数的引用。 49 | /// 当前方法实参的构造器。 50 | public ArgumentBuilder Ref(SyntaxKind kind) 51 | { 52 | refKind = kind; 53 | return this; 54 | } 55 | 56 | /// 57 | /// 构造实参语法节点。 58 | /// 59 | /// 语法的格式信息。 60 | /// 实参语法节点。 61 | public ArgumentSyntax GetSyntax(SyntaxFormat format) 62 | { 63 | ArgumentSyntax syntax = SyntaxFactory.Argument(expression.GetSyntax(format)); 64 | if (name != null) 65 | { 66 | syntax = syntax.WithNameColon(SyntaxFactory.NameColon(name) 67 | .WithTrailingTrivia(SyntaxFactory.Space)); 68 | } 69 | if (refKind != SyntaxKind.None) 70 | { 71 | syntax = syntax.WithRefKindKeyword(SyntaxFactory.Token(refKind) 72 | .WithTrailingTrivia(SyntaxFactory.Space)); 73 | } 74 | return syntax; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/AttributeArgumentBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 特性参数的构造器。 9 | /// 10 | internal sealed class AttributeArgumentBuilder 11 | { 12 | /// 13 | /// 参数的表达式。 14 | /// 15 | private readonly ExpressionBuilder expression; 16 | /// 17 | /// 参数的名称。 18 | /// 19 | private string? name; 20 | /// 21 | /// 参数是否是特性成员。 22 | /// 23 | private bool isMember = false; 24 | 25 | /// 26 | /// 使用指定的表达式初始化 类的新实例。 27 | /// 28 | /// 参数的表达式。 29 | public AttributeArgumentBuilder(ExpressionBuilder expression) 30 | { 31 | this.expression = expression; 32 | } 33 | 34 | /// 35 | /// 设置参数的名称。 36 | /// 37 | /// 参数的名称。 38 | /// 参数是否是特性成员。 39 | /// 当前特性参数的构造器。 40 | public AttributeArgumentBuilder Name(string name, bool isMember = false) 41 | { 42 | this.name = name; 43 | this.isMember = isMember; 44 | return this; 45 | } 46 | 47 | /// 48 | /// 构造特性参数语法节点。 49 | /// 50 | /// 语法的格式信息。 51 | /// 特性参数语法节点。 52 | public AttributeArgumentSyntax GetSyntax(SyntaxFormat format) 53 | { 54 | var syntax = SyntaxFactory.AttributeArgument(expression.GetSyntax(format)); 55 | if (name != null) 56 | { 57 | if (isMember) 58 | { 59 | syntax = syntax.WithNameEquals(SyntaxFactory.NameEquals(name) 60 | .WithLeadingTrivia(SyntaxFactory.Space) 61 | .WithTrailingTrivia(SyntaxFactory.Space)); 62 | } 63 | else 64 | { 65 | syntax = syntax.WithNameColon(SyntaxFactory.NameColon(name) 66 | .WithTrailingTrivia(SyntaxFactory.Space)); 67 | } 68 | } 69 | return syntax; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/AttributeArgumentListBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 特性参数列表的构造器。 8 | /// 9 | internal sealed class AttributeArgumentListBuilder 10 | { 11 | /// 12 | /// 特性参数列表。 13 | /// 14 | private readonly List AttributeArguments = new(); 15 | /// 16 | /// 特性参数的换行信息,0 表示不换行。 17 | /// 18 | private int wrap = 0; 19 | 20 | /// 21 | /// 参数的个数。 22 | /// 23 | public int Count => AttributeArguments.Count; 24 | 25 | /// 26 | /// 添加指定的特性参数。 27 | /// 28 | /// 参数表达式。 29 | /// 当前特性参数列表构造器。 30 | public AttributeArgumentListBuilder Add(ExpressionBuilder expression) 31 | { 32 | AttributeArguments.Add(new AttributeArgumentBuilder(expression)); 33 | return this; 34 | } 35 | 36 | /// 37 | /// 添加指定的特性参数。 38 | /// 39 | /// 参数表达式。 40 | /// 参数的名称。 41 | /// 参数是否是特性成员。 42 | /// 当前特性参数列表构造器。 43 | public AttributeArgumentListBuilder Add(ExpressionBuilder expression, string name, bool isMember = false) 44 | { 45 | AttributeArguments.Add(new AttributeArgumentBuilder(expression).Name(name, isMember)); 46 | return this; 47 | } 48 | 49 | /// 50 | /// 设置参数的换行情况,默认为 0 表示不换行。 51 | /// 52 | /// 换行情况。 53 | /// 当前特性参数列表构造器。 54 | public AttributeArgumentListBuilder Wrap(int wrap) 55 | { 56 | if (wrap >= 0) 57 | { 58 | this.wrap = wrap; 59 | } 60 | return this; 61 | } 62 | 63 | /// 64 | /// 构造特性参数的列表。 65 | /// 66 | /// 语法的格式信息。 67 | /// 特性参数列表。 68 | public AttributeArgumentListSyntax GetSyntax(SyntaxFormat format) 69 | { 70 | return SyntaxFactory.AttributeArgumentList(SyntaxBuilder.SeparatedList( 71 | AttributeArguments.Select(arg => arg.GetSyntax(format)), format.IncDepth(), wrap)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/AttributeBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 特性构造器。 8 | /// 9 | internal sealed class AttributeBuilder 10 | { 11 | /// 12 | /// 特性的名称。 13 | /// 14 | private readonly NameBuilder name; 15 | /// 16 | /// 特性的参数。 17 | /// 18 | private readonly AttributeArgumentListBuilder arguments = new(); 19 | 20 | /// 21 | /// 使用指定的名称初始化 类的新实例。 22 | /// 23 | /// 特性的名称。 24 | public AttributeBuilder(NameBuilder name) 25 | { 26 | this.name = name; 27 | } 28 | 29 | /// 30 | /// 添加特性的参数。 31 | /// 32 | /// 特性的参数。 33 | /// 当前特性构造器。 34 | public AttributeBuilder Argument(ExpressionBuilder argument) 35 | { 36 | arguments.Add(argument); 37 | return this; 38 | } 39 | 40 | /// 41 | /// 添加特性的参数。 42 | /// 43 | /// 特性的参数。 44 | /// 参数的名称。 45 | /// 参数是否是特性成员。 46 | /// 当前特性构造器。 47 | public AttributeBuilder Argument(ExpressionBuilder argument, string name, bool isMember = false) 48 | { 49 | arguments.Add(argument, name, isMember); 50 | return this; 51 | } 52 | 53 | /// 54 | /// 构造特性语法节点。 55 | /// 56 | /// 语法的格式信息。 57 | /// 特性语法节点。 58 | public AttributeSyntax GetSyntax(SyntaxFormat format) 59 | { 60 | var syntax = SyntaxFactory.Attribute(name.GetSyntax(format)); 61 | if (arguments.Count > 0) 62 | { 63 | syntax = syntax.WithArgumentList(arguments.GetSyntax(format)); 64 | } 65 | return syntax; 66 | } 67 | 68 | /// 69 | /// 构造特性列表语法节点。 70 | /// 71 | /// 语法的格式信息。 72 | /// 特性列表语法节点。 73 | public AttributeListSyntax GetListSyntax(SyntaxFormat format) 74 | { 75 | return SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(GetSyntax(format))); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Declarations/BaseMethodDeclarationBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.CodeAnalysis.CSharp; 2 | 3 | /// 4 | /// 方法声明的基构造器。 5 | /// 6 | internal abstract class BaseMethodDeclarationBuilder : MemberDeclarationBuilder 7 | { 8 | /// 9 | /// 方法的参数列表。 10 | /// 11 | internal protected readonly ParameterListBuilder parameters = new(); 12 | /// 13 | /// 方法体构造器。 14 | /// 15 | internal protected BlockBuilder? block; 16 | } 17 | 18 | /// 19 | /// 提供方法声明基构造器的辅助功能。 20 | /// 21 | internal static class BaseMethodDeclarationBuilderUtil 22 | { 23 | /// 24 | /// 添加方法声明的参数。 25 | /// 26 | /// 方法声明的类型。 27 | /// 参数的名称。 28 | /// 参数的类型。 29 | /// 当前方法声明构造器。 30 | public static T Parameter(this T builder, string name, TypeBuilder type) 31 | where T : BaseMethodDeclarationBuilder 32 | { 33 | builder.parameters.Add(name, type); 34 | return builder; 35 | } 36 | 37 | /// 38 | /// 添加方法体的语句。 39 | /// 40 | /// 方法声明的类型。 41 | /// 语句。 42 | /// 当前方法声明构造器。 43 | public static T Statement(this T builder, StatementBuilder? statement) 44 | where T : BaseMethodDeclarationBuilder 45 | { 46 | if (statement != null) 47 | { 48 | builder.block ??= new BlockBuilder(); 49 | builder.block.Add(statement); 50 | } 51 | return builder; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Declarations/MethodDeclarationBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 方法声明的构造器。 9 | /// 10 | internal sealed class MethodDeclarationBuilder : BaseMethodDeclarationBuilder 11 | { 12 | /// 13 | /// 方法的返回值。 14 | /// 15 | private readonly TypeBuilder returnType; 16 | /// 17 | /// 方法的名称。 18 | /// 19 | private readonly string methodName; 20 | 21 | /// 22 | /// 使用指定的方法返回类型和名称初始化 类的新实例。 23 | /// 24 | /// 方法的返回类型。 25 | /// 方法的名称。 26 | public MethodDeclarationBuilder(TypeBuilder returnType, string methodName) 27 | { 28 | this.returnType = returnType; 29 | this.methodName = methodName; 30 | } 31 | 32 | /// 33 | /// 构造方法声明语法节点。 34 | /// 35 | /// 语法的格式信息。 36 | /// 方法声明语法节点。 37 | public override MethodDeclarationSyntax GetSyntax(SyntaxFormat format) 38 | { 39 | SyntaxList attributeLists = attributes.GetSyntax(format); 40 | SyntaxTokenList modifiers = this.modifiers.GetSyntax(format); 41 | var returnType = this.returnType.GetSyntax(format) 42 | .WithLeadingTrivia(this.modifiers.Count > 0 ? SyntaxFactory.Space : format.Indentation); 43 | var identifier = SyntaxFactory.Identifier(methodName).WithLeadingTrivia(SyntaxFactory.Space); 44 | ParameterListSyntax parameterList = parameters.GetSyntax(format); 45 | BlockSyntax? body = null; 46 | if (block != null) 47 | { 48 | body = block.GetSyntax(format) 49 | .WithLeadingTrivia(format.EndOfLine, format.Indentation) 50 | .WithTrailingTrivia(format.EndOfLine); 51 | } 52 | 53 | MethodDeclarationSyntax syntax = SyntaxFactory.MethodDeclaration( 54 | attributeLists, modifiers, returnType, null, identifier, null, 55 | parameterList, SyntaxFactory.List(), 56 | body, null 57 | ); 58 | if (commentBuilder.HasComment) 59 | { 60 | syntax = syntax.InsertLeadingTrivia(0, SyntaxFactory.Trivia(commentBuilder.GetSyntax(format))); 61 | } 62 | return syntax; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/AssignmentExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 赋值表达式的构造器。 9 | /// 10 | internal class AssignmentExpressionBuilder : ExpressionBuilder 11 | { 12 | /// 13 | /// 赋值表达式的类型。 14 | /// 15 | private readonly SyntaxKind kind; 16 | /// 17 | /// 赋值表达式左侧的表达式。 18 | /// 19 | private readonly ExpressionBuilder left; 20 | /// 21 | /// 赋值表达式右侧的表达式。 22 | /// 23 | private readonly ExpressionBuilder right; 24 | 25 | /// 26 | /// 使用指定的表达式初始化 类的新实例。 27 | /// 28 | /// 表达式的类型。 29 | /// 左侧的表达式。 30 | /// 右侧的表达式。 31 | public AssignmentExpressionBuilder(SyntaxKind kind, ExpressionBuilder left, ExpressionBuilder right) 32 | { 33 | this.kind = kind; 34 | this.left = left; 35 | this.right = right; 36 | } 37 | 38 | /// 39 | /// 构造赋值表达式。 40 | /// 41 | /// 语法的格式信息。 42 | /// 赋值表达式。 43 | public override AssignmentExpressionSyntax GetSyntax(SyntaxFormat format) 44 | { 45 | return SyntaxFactory.AssignmentExpression(kind, 46 | left.GetSyntax(format).WithTrailingTrivia(SyntaxFactory.Space), 47 | right.GetSyntax(format).WithLeadingTrivia(SyntaxFactory.Space)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/BinaryExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | 6 | namespace Cyjb.Compilers.CodeAnalysis; 7 | 8 | /// 9 | /// 二元表达式的构造器。 10 | /// 11 | internal class BinaryExpressionBuilder : ExpressionBuilder 12 | { 13 | /// 14 | /// 二元表达式的类型。 15 | /// 16 | private readonly SyntaxKind kind; 17 | /// 18 | /// 二元表达式左侧的表达式。 19 | /// 20 | private readonly ExpressionBuilder left; 21 | /// 22 | /// 二元表达式右侧的表达式。 23 | /// 24 | private readonly ExpressionBuilder right; 25 | 26 | /// 27 | /// 使用指定的表达式初始化 类的新实例。 28 | /// 29 | /// 表达式的类型。 30 | /// 左侧的表达式。 31 | /// 右侧的表达式。 32 | public BinaryExpressionBuilder(SyntaxKind kind, ExpressionBuilder left, ExpressionBuilder right) 33 | { 34 | this.kind = kind; 35 | this.left = left; 36 | this.right = right; 37 | } 38 | 39 | /// 40 | /// 构造二元表达式。 41 | /// 42 | /// 语法的格式信息。 43 | /// 二元表达式。 44 | public override BinaryExpressionSyntax GetSyntax(SyntaxFormat format) 45 | { 46 | return SyntaxFactory.BinaryExpression(kind, 47 | left.GetSyntax(format).WithTrailingTrivia(SyntaxFactory.Space), 48 | right.GetSyntax(format).WithLeadingTrivia(SyntaxFactory.Space)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/CastExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 强制类型转换表达式构造器。 8 | /// 9 | internal class CastExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 要转换到的类型。 13 | /// 14 | private readonly TypeBuilder type; 15 | /// 16 | /// 要转换的表达式。 17 | /// 18 | private readonly ExpressionBuilder expression; 19 | 20 | /// 21 | /// 使用指定的类型和表达式初始化 类的新实例。 22 | /// 23 | /// 要转换到的类型。 24 | /// 要转换的表达式。 25 | public CastExpressionBuilder(TypeBuilder type, ExpressionBuilder expression) 26 | { 27 | this.type = type; 28 | this.expression = expression; 29 | } 30 | 31 | /// 32 | /// 构造强制类型转换表达式。 33 | /// 34 | /// 语法的格式信息。 35 | /// 强制类型转换表达式。 36 | public override CastExpressionSyntax GetSyntax(SyntaxFormat format) 37 | { 38 | return SyntaxFactory.CastExpression(type.GetSyntax(format), expression.GetSyntax(format)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/ElementAccessExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 元素访问表达式的构造器。 8 | /// 9 | internal class ElementAccessExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 要访问元素的表达式。 13 | /// 14 | private readonly ExpressionBuilder expression; 15 | /// 16 | /// 要访问的元素。 17 | /// 18 | private readonly List arguments = new(); 19 | 20 | /// 21 | /// 使用指定的表达式初始化 类的新实例。 22 | /// 23 | /// 要访问元素的表达式。 24 | public ElementAccessExpressionBuilder(ExpressionBuilder expression) 25 | { 26 | this.expression = expression; 27 | } 28 | 29 | /// 30 | /// 添加指定的元素参数。 31 | /// 32 | /// 参数表达式。 33 | /// 当前元素访问表达式的构造器。 34 | public ElementAccessExpressionBuilder Argument(ExpressionBuilder expression) 35 | { 36 | arguments.Add(new ArgumentBuilder(expression)); 37 | return this; 38 | } 39 | 40 | /// 41 | /// 构造元素访问表达式。 42 | /// 43 | /// 语法的格式信息。 44 | /// 元素访问表达式语法节点。 45 | public override ElementAccessExpressionSyntax GetSyntax(SyntaxFormat format) 46 | { 47 | return SyntaxFactory.ElementAccessExpression(expression.GetSyntax(format), 48 | SyntaxFactory.BracketedArgumentList(SyntaxBuilder.SeparatedList( 49 | arguments.Select(arg => arg.GetSyntax(format)), format.IncDepth(), 0))); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/InvocationExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 方法调用表达式的构造器。 8 | /// 9 | internal sealed class InvocationExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 要调用的方法。 13 | /// 14 | private readonly ExpressionBuilder expression; 15 | /// 16 | /// 方法的调用参数。 17 | /// 18 | private readonly ArgumentListBuilder arguments = new(); 19 | 20 | /// 21 | /// 使用要调用的方法表达式初始化 类的新实例。 22 | /// 23 | /// 要调用的方法。 24 | public InvocationExpressionBuilder(ExpressionBuilder expression) 25 | { 26 | this.expression = expression; 27 | } 28 | 29 | /// 30 | /// 添加方法的参数。 31 | /// 32 | /// 方法的参数。 33 | /// 当前方法调用表达式构造器。 34 | public InvocationExpressionBuilder Argument(ExpressionBuilder argument) 35 | { 36 | arguments.Add(argument); 37 | return this; 38 | } 39 | 40 | /// 41 | /// 添加方法的参数。 42 | /// 43 | /// 方法的参数。 44 | /// 参数的名称。 45 | /// 当前方法调用表达式构造器。 46 | public InvocationExpressionBuilder Argument(ExpressionBuilder argument, string name) 47 | { 48 | arguments.Add(argument, name); 49 | return this; 50 | } 51 | 52 | /// 53 | /// 构造方法调用表达式。 54 | /// 55 | /// 语法的格式信息。 56 | /// 方法调用表达式。 57 | public override InvocationExpressionSyntax GetSyntax(SyntaxFormat format) 58 | { 59 | if (arguments.Count == 0) 60 | { 61 | return SyntaxFactory.InvocationExpression(expression.GetSyntax(format)); 62 | } 63 | else 64 | { 65 | return SyntaxFactory.InvocationExpression(expression.GetSyntax(format), arguments.GetSyntax(format)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/MemberAccessExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 成员访问表达式的构造器。 8 | /// 9 | internal sealed class MemberAccessExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 对象表达式。 13 | /// 14 | private readonly ExpressionBuilder expression; 15 | /// 16 | /// 成员名称。 17 | /// 18 | private readonly string name; 19 | 20 | /// 21 | /// 使用指定的对象表达式和成员名称初始化 类的新实例。 22 | /// 23 | /// 对象表达式。 24 | /// 成员名称。 25 | public MemberAccessExpressionBuilder(ExpressionBuilder expression, string name) 26 | { 27 | this.expression = expression; 28 | this.name = name; 29 | } 30 | 31 | /// 32 | /// 构造成员访问表达式。 33 | /// 34 | /// 语法格式。 35 | /// 成员访问表达式。 36 | public override MemberAccessExpressionSyntax GetSyntax(SyntaxFormat format) 37 | { 38 | return SyntaxFactory.MemberAccessExpression( 39 | SyntaxKind.SimpleMemberAccessExpression, 40 | expression.GetSyntax(format), 41 | (SimpleNameSyntax)SyntaxFactory.ParseTypeName(name)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/ParenthesizedExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 括号表达式的构造器。 8 | /// 9 | internal class ParenthesizedExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 括号内的表达式。 13 | /// 14 | private readonly ExpressionBuilder expression; 15 | 16 | /// 17 | /// 使用括号内的表达式初始化 类的新实例。 18 | /// 19 | /// 括号内的表达式。 20 | public ParenthesizedExpressionBuilder(ExpressionBuilder expression) 21 | { 22 | this.expression = expression; 23 | } 24 | 25 | /// 26 | /// 构造括号表达式。 27 | /// 28 | /// 语法的格式信息。 29 | /// 括号表达式。 30 | public override ParenthesizedExpressionSyntax GetSyntax(SyntaxFormat format) 31 | { 32 | return SyntaxFactory.ParenthesizedExpression(expression.GetSyntax(format)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/PrefixUnaryExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// 前缀一元表达式的构造器。 9 | /// 10 | internal class PrefixUnaryExpressionBuilder : ExpressionBuilder 11 | { 12 | /// 13 | /// 前缀一元表达式的类型。 14 | /// 15 | private readonly SyntaxKind kind; 16 | /// 17 | /// 前缀一元表达式的操作数表达式。 18 | /// 19 | private readonly ExpressionBuilder operand; 20 | 21 | /// 22 | /// 使用指定的表达式初始化 类的新实例。 23 | /// 24 | /// 表达式的类型。 25 | /// 操作数表达式。 26 | public PrefixUnaryExpressionBuilder(SyntaxKind kind, ExpressionBuilder operand) 27 | { 28 | this.kind = kind; 29 | this.operand = operand; 30 | } 31 | 32 | /// 33 | /// 构造前缀一元表达式。 34 | /// 35 | /// 语法的格式信息。 36 | /// 前缀一元表达式。 37 | public override PrefixUnaryExpressionSyntax GetSyntax(SyntaxFormat format) 38 | { 39 | return SyntaxFactory.PrefixUnaryExpression(kind, operand.GetSyntax(format)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Expressions/TypeOfExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// typeof 表达式构造器。 8 | /// 9 | internal class TypeOfExpressionBuilder : ExpressionBuilder 10 | { 11 | /// 12 | /// 类型。 13 | /// 14 | private readonly TypeBuilder type; 15 | 16 | /// 17 | /// 使用指定的类型初始化 类的新实例。 18 | /// 19 | /// 类型。 20 | public TypeOfExpressionBuilder(TypeBuilder type) 21 | { 22 | this.type = type; 23 | } 24 | 25 | /// 26 | /// 构造 typeof 表达式。 27 | /// 28 | /// 语法的格式信息。 29 | /// typeof 表达式。 30 | public override TypeOfExpressionSyntax GetSyntax(SyntaxFormat format) 31 | { 32 | return SyntaxFactory.TypeOfExpression(type.GetSyntax(format)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/ModifierBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 修饰符的构造器。 8 | /// 9 | internal sealed class ModifierBuilder 10 | { 11 | /// 12 | /// 修饰符列表。 13 | /// 14 | private readonly List modifiers = new(); 15 | 16 | /// 17 | /// 获取修饰符的个数。 18 | /// 19 | public int Count => modifiers.Count; 20 | 21 | /// 22 | /// 添加修饰符。 23 | /// 24 | /// 要添加的修饰符。 25 | /// 当前修饰符构造器。 26 | public ModifierBuilder Add(params SyntaxKind[] modifiers) 27 | { 28 | this.modifiers.AddRange(modifiers); 29 | return this; 30 | } 31 | 32 | /// 33 | /// 构造修饰符的节点列表。 34 | /// 35 | /// 语法格式。 36 | /// 修饰符的节点列表。 37 | public SyntaxTokenList GetSyntax(SyntaxFormat format) 38 | { 39 | if (modifiers.Count == 0) 40 | { 41 | return SyntaxFactory.TokenList(); 42 | } 43 | List tokens = new(); 44 | bool isFirst = true; 45 | foreach (SyntaxKind modifier in modifiers) 46 | { 47 | SyntaxToken token = SyntaxFactory.Token(modifier); 48 | if (isFirst) 49 | { 50 | token = token.WithLeadingTrivia(format.Indentation); 51 | isFirst = false; 52 | } 53 | else 54 | { 55 | token = token.WithLeadingTrivia(SyntaxFactory.Space); 56 | } 57 | tokens.Add(token); 58 | } 59 | return SyntaxFactory.TokenList(tokens); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/ParameterBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 形参的构造器。 9 | /// 10 | internal sealed class ParameterBuilder 11 | { 12 | /// 13 | /// 参数的名称。 14 | /// 15 | private readonly string name; 16 | /// 17 | /// 参数类型。 18 | /// 19 | private TypeBuilder? type; 20 | 21 | /// 22 | /// 使用指定的参数名称初始化 类的新实例。 23 | /// 24 | /// 参数的名称。 25 | public ParameterBuilder(string name) 26 | { 27 | this.name = name; 28 | } 29 | 30 | /// 31 | /// 是否是简单参数(只包含参数名称)。 32 | /// 33 | public bool IsSimple => type == null; 34 | 35 | /// 36 | /// 设置参数的类型。 37 | /// 38 | /// 参数的类型。 39 | /// 当前方法形参的构造器。 40 | public ParameterBuilder Type(TypeBuilder type) 41 | { 42 | this.type = type; 43 | return this; 44 | } 45 | 46 | /// 47 | /// 构造形参语法节点。 48 | /// 49 | /// 语法的格式信息。 50 | /// 形参语法节点。 51 | public ParameterSyntax GetSyntax(SyntaxFormat format) 52 | { 53 | ParameterSyntax syntax = SyntaxFactory.Parameter(SyntaxFactory.Identifier(name)); 54 | if (type != null) 55 | { 56 | syntax = syntax.WithType(type.GetSyntax(format).WithTrailingTrivia(SyntaxFactory.Space)); 57 | } 58 | return syntax; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/ParameterListBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 形参列表的构造器。 8 | /// 9 | internal sealed class ParameterListBuilder 10 | { 11 | /// 12 | /// 形参列表。 13 | /// 14 | private readonly List parameters = new(); 15 | 16 | /// 17 | /// 参数的个数。 18 | /// 19 | public int Count => parameters.Count; 20 | /// 21 | /// 是否是简单参数列表(只包含单个参数)。 22 | /// 23 | public bool IsSimple => parameters.Count == 1 && parameters[0].IsSimple; 24 | 25 | /// 26 | /// 添加指定的形参。 27 | /// 28 | /// 参数的名称。 29 | /// 当前形参列表构造器。 30 | public ParameterListBuilder Add(string name) 31 | { 32 | parameters.Add(new ParameterBuilder(name)); 33 | return this; 34 | } 35 | 36 | /// 37 | /// 添加指定的形参。 38 | /// 39 | /// 参数的名称。 40 | /// 参数的类型。 41 | /// 当前形参列表构造器。 42 | public ParameterListBuilder Add(string name, TypeBuilder type) 43 | { 44 | parameters.Add(new ParameterBuilder(name).Type(type)); 45 | return this; 46 | } 47 | 48 | /// 49 | /// 构造简单形参。 50 | /// 51 | /// 语法的格式信息。 52 | /// 简单形参。 53 | public ParameterSyntax GetSimpleSyntax(SyntaxFormat format) 54 | { 55 | return parameters[0].GetSyntax(format); 56 | } 57 | 58 | /// 59 | /// 构造形参的列表。 60 | /// 61 | /// 语法的格式信息。 62 | /// 形参列表。 63 | public ParameterListSyntax GetSyntax(SyntaxFormat format) 64 | { 65 | if (parameters.Count == 0) 66 | { 67 | return SyntaxFactory.ParameterList(); 68 | } 69 | return SyntaxFactory.ParameterList(SyntaxBuilder.SeparatedList( 70 | parameters.Select(arg => arg.GetSyntax(format)), format)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Pattern/BinaryPatternBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | 6 | namespace Cyjb.Compilers.CodeAnalysis; 7 | 8 | /// 9 | /// 二元模式的构造器。 10 | /// 11 | internal class BinaryPatternBuilder : PatternBuilder 12 | { 13 | /// 14 | /// 二元模式的类型。 15 | /// 16 | private readonly SyntaxKind kind; 17 | /// 18 | /// 二元模式左侧的模式。 19 | /// 20 | private readonly PatternBuilder left; 21 | /// 22 | /// 二元模式右侧的模式。 23 | /// 24 | private readonly PatternBuilder right; 25 | 26 | /// 27 | /// 使用指定的模式初始化 类的新实例。 28 | /// 29 | /// 模式的类型。 30 | /// 左侧的模式。 31 | /// 右侧的模式。 32 | public BinaryPatternBuilder(SyntaxKind kind, PatternBuilder left, PatternBuilder right) 33 | { 34 | this.kind = kind; 35 | this.left = left; 36 | this.right = right; 37 | } 38 | 39 | /// 40 | /// 构造二元模式。 41 | /// 42 | /// 语法的格式信息。 43 | /// 二元模式。 44 | public override BinaryPatternSyntax GetSyntax(SyntaxFormat format) 45 | { 46 | return SyntaxFactory.BinaryPattern(kind, 47 | left.GetSyntax(format).WithTrailingTrivia(SyntaxFactory.Space), 48 | right.GetSyntax(format).WithLeadingTrivia(SyntaxFactory.Space)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Pattern/PatternBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// 模式的构造器。 9 | /// 10 | internal abstract class PatternBuilder 11 | { 12 | /// 13 | /// 返回小于等于模式。 14 | /// 15 | /// 小于等于模式。 16 | public static RelationalPatternBuilder LessThanEquals(ExpressionBuilder expression) => 17 | new(SyntaxKind.LessThanEqualsToken, expression); 18 | 19 | /// 20 | /// 返回大于等于模式。 21 | /// 22 | /// 大于等于模式。 23 | public static RelationalPatternBuilder GreaterThanEquals(ExpressionBuilder expression) => 24 | new(SyntaxKind.GreaterThanEqualsToken, expression); 25 | 26 | /// 27 | /// 构造模式语法节点。 28 | /// 29 | /// 语法的格式信息。 30 | /// 表达式语法节点。 31 | public abstract PatternSyntax GetSyntax(SyntaxFormat format); 32 | 33 | /// 34 | /// 返回当前模式与指定模式的构造器。 35 | /// 36 | /// 二元模式构造器。 37 | public BinaryPatternBuilder And(PatternBuilder pattern) 38 | { 39 | return new BinaryPatternBuilder(SyntaxKind.AndPattern, this, pattern); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Pattern/RelationalPatternBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// 关系模式的构造器。 9 | /// 10 | internal class RelationalPatternBuilder : PatternBuilder 11 | { 12 | /// 13 | /// 关系模式的类型。 14 | /// 15 | private readonly SyntaxKind kind; 16 | /// 17 | /// 关系模式的表达式。 18 | /// 19 | private readonly ExpressionBuilder expression; 20 | 21 | /// 22 | /// 使用指定的表达式初始化 类的新实例。 23 | /// 24 | /// 表达式的类型。 25 | /// 关系模式的表达式。 26 | public RelationalPatternBuilder(SyntaxKind kind, ExpressionBuilder expression) 27 | { 28 | this.kind = kind; 29 | this.expression = expression; 30 | } 31 | 32 | /// 33 | /// 构造关系模式。 34 | /// 35 | /// 语法的格式信息。 36 | /// 关系模式。 37 | public override RelationalPatternSyntax GetSyntax(SyntaxFormat format) 38 | { 39 | return SyntaxFactory.RelationalPattern( 40 | SyntaxFactory.Token(kind).WithTrailingTrivia(SyntaxFactory.Space), 41 | expression.GetSyntax(format)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/SeparatedListUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 相关的工具方法。 8 | /// 9 | internal static class SeparatedListUtils 10 | { 11 | /// 12 | /// 逗号单元。 13 | /// 14 | private static readonly SyntaxToken CommaToken = SyntaxFactory.Token(SyntaxKind.CommaToken) 15 | .WithTrailingTrivia(SyntaxFactory.Space); 16 | 17 | /// 18 | /// 返回指定项个数的分隔符列表。 19 | /// 20 | /// 项的个数。 21 | /// 分隔符列表。 22 | public static IEnumerable GetSeparators(int itemCount) 23 | { 24 | if (itemCount <= 1) 25 | { 26 | return Array.Empty(); 27 | } 28 | return Enumerable.Repeat(CommaToken, itemCount - 1); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/BlockBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 语句块的构造器。 9 | /// 10 | internal sealed class BlockBuilder : StatementBuilder 11 | { 12 | /// 13 | /// 包含的语句列表。 14 | /// 15 | private readonly List statements = new(); 16 | 17 | /// 18 | /// 添加指定的语句。 19 | /// 20 | /// 语句。 21 | /// 当前语句块构造器。 22 | public BlockBuilder Add(StatementBuilder statement) 23 | { 24 | statements.Add(statement); 25 | return this; 26 | } 27 | 28 | /// 29 | /// 构造语句块语法节点。 30 | /// 31 | /// 语法的格式信息。 32 | /// 语句块语法节点。 33 | public override BlockSyntax GetSyntax(SyntaxFormat format) 34 | { 35 | SyntaxFormat stmFormat = format.IncDepth(); 36 | return SyntaxFactory.Block( 37 | SyntaxFactory.Token(SyntaxKind.OpenBraceToken) 38 | .WithLeadingTrivia(GetLeadingTrivia(format)) 39 | .WithTrailingTrivia(format.EndOfLine), 40 | SyntaxFactory.List(statements.Select(stm => stm.GetSyntax(stmFormat))), 41 | SyntaxFactory.Token(SyntaxKind.CloseBraceToken) 42 | .WithLeadingTrivia(format.Indentation) 43 | .WithTrailingTrivia(format.EndOfLine) 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/BreakStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Cyjb.Compilers.CodeAnalysis; 7 | 8 | /// 9 | /// break 语句的构造器。 10 | /// 11 | internal class BreakStatementBuilder : StatementBuilder 12 | { 13 | /// 14 | /// 构造 break 语句。 15 | /// 16 | /// 语法的格式信息。 17 | /// break 语句。 18 | public override BreakStatementSyntax GetSyntax(SyntaxFormat format) 19 | { 20 | return SyntaxFactory.BreakStatement() 21 | .WithLeadingTrivia(format.Indentation) 22 | .WithTrailingTrivia(format.EndOfLine); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/ExpressionStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 表达式语句的构造器。 9 | /// 10 | internal sealed class ExpressionStatementBuilder : StatementBuilder 11 | { 12 | /// 13 | /// 语句包含的表达式。 14 | /// 15 | private readonly ExpressionBuilder expression; 16 | 17 | /// 18 | /// 使用指定的表达式初始化 类的新实例。 19 | /// 20 | /// 语句包含的表达式。 21 | public ExpressionStatementBuilder(ExpressionBuilder expression) 22 | { 23 | this.expression = expression; 24 | } 25 | 26 | /// 27 | /// 构造返回语句。 28 | /// 29 | /// 语法的格式信息。 30 | /// 返回语句。 31 | public override ExpressionStatementSyntax GetSyntax(SyntaxFormat format) 32 | { 33 | return SyntaxFactory.ExpressionStatement(expression.GetSyntax(format)) 34 | .WithLeadingTrivia(GetLeadingTrivia(format)).WithTrailingTrivia(format.EndOfLine); 35 | } 36 | 37 | /// 38 | /// 允许从表达式构造器隐式转换为 对象。 39 | /// 40 | /// 要转换的表达式构造器。 41 | /// 转换到的 对象。 42 | public static implicit operator ExpressionStatementBuilder(ExpressionBuilder expression) 43 | { 44 | return new ExpressionStatementBuilder(expression); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/GotoStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// 表示 goto 语句的构造器。 9 | /// 10 | internal class GotoStatementBuilder : StatementBuilder 11 | { 12 | /// 13 | /// goto 语句的类型。 14 | /// 15 | private readonly SyntaxKind kind; 16 | /// 17 | /// goto 语句的表达式。 18 | /// 19 | private readonly ExpressionBuilder expression; 20 | 21 | /// 22 | /// 使用指定的标签名初始化 类的新实例。 23 | /// 24 | /// 要跳转的标签名。 25 | public GotoStatementBuilder(string identifier) 26 | { 27 | kind = SyntaxKind.GotoStatement; 28 | expression = SyntaxFactory.IdentifierName(identifier); 29 | } 30 | 31 | /// 32 | /// 构造 goto 语句。 33 | /// 34 | /// 语法的格式信息。 35 | /// goto 语句。 36 | public override GotoStatementSyntax GetSyntax(SyntaxFormat format) 37 | { 38 | return SyntaxFactory.GotoStatement(kind, 39 | SyntaxFactory.Token(SyntaxKind.GotoKeyword) 40 | .WithLeadingTrivia(format.Indentation) 41 | .WithTrailingTrivia(SyntaxFactory.Space), 42 | SyntaxFactory.Token(SyntaxKind.None), 43 | expression.GetSyntax(format), 44 | SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(format.EndOfLine) 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/LabeledStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// 标签语句的构造器。 9 | /// 10 | internal class LabeledStatementBuilder : StatementBuilder 11 | { 12 | /// 13 | /// 标签的标识符。 14 | /// 15 | private readonly string identifier; 16 | /// 17 | /// 标签的语句。 18 | /// 19 | private readonly StatementBuilder statement; 20 | 21 | /// 22 | /// 使用标签标识符和语句初始化 类的新实例。 23 | /// 24 | /// 标签的标识符。 25 | /// 标签的语句。 26 | public LabeledStatementBuilder(string identifier, StatementBuilder statement) 27 | { 28 | this.identifier = identifier; 29 | this.statement = statement; 30 | } 31 | 32 | /// 33 | /// 构造标签语句。 34 | /// 35 | /// 语法的格式信息。 36 | /// 标签语句。 37 | public override LabeledStatementSyntax GetSyntax(SyntaxFormat format) 38 | { 39 | return SyntaxFactory.LabeledStatement( 40 | SyntaxFactory.Identifier(identifier).WithLeadingTrivia(format.DecDepth().Indentation), 41 | SyntaxFactory.Token(SyntaxKind.ColonToken).WithTrailingTrivia(format.EndOfLine), 42 | statement.GetSyntax(format) 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/ReturnStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 返回语句的构造器。 8 | /// 9 | internal sealed class ReturnStatementBuilder : StatementBuilder 10 | { 11 | /// 12 | /// 要返回的表达式。 13 | /// 14 | private readonly ExpressionBuilder expression; 15 | 16 | /// 17 | /// 使用指定的表达式初始化 类的新实例。 18 | /// 19 | /// 要返回的表达式。 20 | public ReturnStatementBuilder(ExpressionBuilder expression) 21 | { 22 | this.expression = expression; 23 | } 24 | 25 | /// 26 | /// 构造返回语句。 27 | /// 28 | /// 语法的格式信息。 29 | /// 返回语句。 30 | public override ReturnStatementSyntax GetSyntax(SyntaxFormat format) 31 | { 32 | return SyntaxFactory.ReturnStatement( 33 | SyntaxFactory.Token(SyntaxKind.ReturnKeyword) 34 | .WithLeadingTrivia(GetLeadingTrivia(format)) 35 | .WithTrailingTrivia(SyntaxFactory.Space), 36 | expression.GetSyntax(format), 37 | SyntaxFactory.Token(SyntaxKind.SemicolonToken) 38 | .WithTrailingTrivia(format.EndOfLine) 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/Builders/Statements/WhileStatementBuilder.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | 5 | namespace Cyjb.Compilers.CodeAnalysis; 6 | 7 | /// 8 | /// while 语句的构造器。 9 | /// 10 | internal class WhileStatementBuilder : StatementBuilder 11 | { 12 | /// 13 | /// while 语句的条件表达式。 14 | /// 15 | private readonly ExpressionBuilder condition; 16 | /// 17 | /// 包含的语句块。 18 | /// 19 | private readonly BlockBuilder block = new(); 20 | 21 | /// 22 | /// 初始化 类的新实例。 23 | /// 24 | /// while 语句的条件表达式。 25 | public WhileStatementBuilder(ExpressionBuilder condition) 26 | { 27 | this.condition = condition; 28 | } 29 | 30 | /// 31 | /// 添加指定的语句。 32 | /// 33 | /// 语句。 34 | /// 当前语句块构造器。 35 | public WhileStatementBuilder Statement(StatementBuilder statement) 36 | { 37 | block.Add(statement); 38 | return this; 39 | } 40 | 41 | /// 42 | /// 构造 while 语句。 43 | /// 44 | /// 语法的格式信息。 45 | /// while 语句。 46 | public override WhileStatementSyntax GetSyntax(SyntaxFormat format) 47 | { 48 | return SyntaxFactory.WhileStatement( 49 | SyntaxFactory.Token(SyntaxKind.WhileKeyword) 50 | .WithLeadingTrivia(GetLeadingTrivia(format)) 51 | .WithTrailingTrivia(SyntaxFactory.Space), 52 | SyntaxFactory.Token(SyntaxKind.OpenParenToken), 53 | condition.GetSyntax(format), 54 | SyntaxFactory.Token(SyntaxKind.CloseParenToken) 55 | .WithTrailingTrivia(format.EndOfLine), 56 | block.GetSyntax(format) 57 | ); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/IndentDetector.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 语法节点的缩进检测器。 9 | /// 10 | internal class IndentDetector : CSharpSyntaxWalker 11 | { 12 | /// 13 | /// 检测到的缩进。 14 | /// 15 | private string indent = string.Empty; 16 | 17 | /// 18 | /// 获取当前缩进。 19 | /// 20 | public string Indent => indent; 21 | 22 | /// 23 | /// 访问指定的语法节点。 24 | /// 25 | /// 要访问的语法节点。 26 | public override void Visit(SyntaxNode? node) 27 | { 28 | if (node == null) 29 | { 30 | return; 31 | } 32 | if (indent.Length > 0) 33 | { 34 | // 已经检测到缩进,返回。 35 | return; 36 | } 37 | // 找到成员声明或语句的首个空白琐事作为缩进。 38 | if (node is MemberDeclarationSyntax || node is StatementSyntax) 39 | { 40 | indent = GetIndent(node); 41 | } 42 | base.Visit(node); 43 | } 44 | 45 | /// 46 | /// 返回指定语法节点的缩进。 47 | /// 48 | /// 要检查的语法节点。 49 | /// 语法节点的缩进。 50 | public static string GetIndent(SyntaxNode? node) 51 | { 52 | if (node == null) 53 | { 54 | return string.Empty; 55 | } 56 | foreach (SyntaxTrivia trivia in node.GetLeadingTrivia()) 57 | { 58 | if (trivia.IsKind(SyntaxKind.EndOfLineTrivia)) 59 | { 60 | continue; 61 | } 62 | else if (trivia.IsKind(SyntaxKind.WhitespaceTrivia)) 63 | { 64 | return trivia.ToString(); 65 | } 66 | break; 67 | } 68 | return string.Empty; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/NameSyntaxUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Syntax; 2 | 3 | namespace Cyjb.CodeAnalysis.CSharp; 4 | 5 | /// 6 | /// 提供对 的扩展方法。 7 | /// 8 | internal static class NameSyntaxUtil 9 | { 10 | /// 11 | /// 返回当前 。 12 | /// 13 | /// 要检查的 对象。 14 | /// 本身,或者限定符最右侧的 15 | public static SimpleNameSyntax GetSimpleName(this NameSyntax name) 16 | { 17 | if (name is SimpleNameSyntax simpleName) 18 | { 19 | return simpleName; 20 | } 21 | else if (name is AliasQualifiedNameSyntax aliasQualifiedName) 22 | { 23 | return aliasQualifiedName.Name; 24 | } 25 | else if (name is QualifiedNameSyntax qualifiedName) 26 | { 27 | return qualifiedName.Right; 28 | } 29 | else 30 | { 31 | throw CommonExceptions.Unreachable(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/ParameterSyntaxUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Cyjb.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// 提供对 的扩展方法。 9 | /// 10 | internal static class ParameterSyntaxUtil 11 | { 12 | /// 13 | /// 返回当前 是否是 params 参数。 14 | /// 15 | /// 要检查的 对象。 16 | /// 如果 是 params 参数,则为 true;否则为 false 17 | public static bool IsParamsArray(this ParameterSyntax param) 18 | { 19 | foreach (var modifier in param.Modifiers) 20 | { 21 | if (modifier.IsKind(SyntaxKind.ParamsKeyword)) 22 | { 23 | return true; 24 | } 25 | } 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/SyntaxTokenUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Cyjb.CodeAnalysis.CSharp; 4 | 5 | /// 6 | /// 提供 的扩展方法。 7 | /// 8 | internal static class SyntaxTokenUtils 9 | { 10 | /// 11 | /// 插入指定的前置琐事。 12 | /// 13 | /// 语法单元。 14 | /// 要插入的索引。 15 | /// 要插入的琐事。 16 | /// 更新后的语法单元。 17 | public static SyntaxToken InsertLeadingTrivia(this SyntaxToken token, int index, IEnumerable? trivia) 18 | { 19 | if (trivia == null) 20 | { 21 | return token; 22 | } 23 | return token.WithLeadingTrivia(token.LeadingTrivia.InsertRange(index, trivia)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Generator/CodeAnalysis.CSharp/XmlNodeSyntaxOrString.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.CodeAnalysis.CSharp; 5 | 6 | /// 7 | /// 表示 或者字符串。 8 | /// 9 | internal sealed class XmlNodeSyntaxOrString 10 | { 11 | /// 12 | /// 使用指定的 初始化 类的新实例。 13 | /// 14 | /// 要封装的 。 15 | public XmlNodeSyntaxOrString(XmlNodeSyntax node) 16 | { 17 | Node = node; 18 | } 19 | 20 | /// 21 | /// 使用指定的字符串初始化 类的新实例。 22 | /// 23 | /// 要封装的字符串。 24 | public XmlNodeSyntaxOrString(string text) 25 | { 26 | Node = SyntaxFactory.XmlText(text); 27 | } 28 | 29 | /// 30 | /// 包含的 。 31 | /// 32 | public XmlNodeSyntax Node { get; } 33 | 34 | /// 35 | /// 允许从 实例隐式转换为 实例。 36 | /// 37 | /// 要封装的 。 38 | public static implicit operator XmlNodeSyntaxOrString(XmlNodeSyntax node) 39 | { 40 | return new XmlNodeSyntaxOrString(node); 41 | } 42 | 43 | /// 44 | /// 允许从字符串实例隐式转换为 实例。 45 | /// 46 | /// 要封装的字符串。 47 | public static implicit operator XmlNodeSyntaxOrString(string text) 48 | { 49 | return new XmlNodeSyntaxOrString(text); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Generator/Controller.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Cyjb.Compilers; 5 | 6 | /// 7 | /// 词法/语法分析控制器。 8 | /// 9 | internal abstract class Controller 10 | { 11 | /// 12 | /// 使用指定的控制器名称和标识符类型初始化 类的新实例。 13 | /// 14 | /// 模板上下文。 15 | /// 控制器语法节点。 16 | /// 标识符类型。 17 | protected Controller(TransformationContext context, ClassDeclarationSyntax syntax, string kindType) 18 | { 19 | Context = context; 20 | Name = syntax.Identifier.ToString(); 21 | ControllerType = Name.AsName(); 22 | if (syntax.TypeParameterList != null) 23 | { 24 | foreach (TypeParameterSyntax typeParam in syntax.TypeParameterList.Parameters) 25 | { 26 | ControllerType.TypeArgument(typeParam.ToString()); 27 | } 28 | } 29 | KindType = kindType; 30 | Format = new SyntaxFormat(syntax).IncDepth(); 31 | } 32 | 33 | /// 34 | /// 获取控制器的名称。 35 | /// 36 | public string Name { get; } 37 | /// 38 | /// 获取控制器的类型。 39 | /// 40 | public NameBuilder ControllerType { get; } 41 | 42 | /// 43 | /// 获取模板上下文。 44 | /// 45 | protected TransformationContext Context { get; } 46 | /// 47 | /// 获取标识符的类型。 48 | /// 49 | protected string KindType { get; } 50 | /// 51 | /// 获取语法的格式化信息。 52 | /// 53 | protected SyntaxFormat Format { get; } 54 | 55 | /// 56 | /// 解析控制器语法节点。 57 | /// 58 | /// 控制器语法节点。 59 | public abstract void Parse(ClassDeclarationSyntax controllerSyntax); 60 | 61 | /// 62 | /// 生成控制器的成员。 63 | /// 64 | /// 控制器的成员。 65 | public abstract IEnumerable Generate(); 66 | } 67 | -------------------------------------------------------------------------------- /Generator/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, CYJB 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Generator/Lexers/LexerController.CharClass.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Cyjb.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | 5 | namespace Cyjb.Compilers.Lexers; 6 | 7 | internal sealed partial class LexerController 8 | { 9 | /// 10 | /// 声明词法分析器数据的字符类 Unicode 类别。 11 | /// 12 | /// 词法分析器数据 13 | /// 字符类 Unicode 类别声明与。 14 | public static LocalDeclarationStatementBuilder? DeclareCharClassCategories(LexerData data) 15 | { 16 | if (data.CharClasses.Categories == null) 17 | { 18 | return null; 19 | } 20 | var categories = ExpressionBuilder.CreateObject().InitializerWrap(1); 21 | foreach (var pair in data.CharClasses.Categories!) 22 | { 23 | categories.Initializer(ExpressionBuilder.InitializerExpression(SyntaxKind.ComplexElementInitializerExpression) 24 | .Add(SyntaxBuilder.Type().AccessMember(pair.Key.ToString())) 25 | .Add(pair.Value)); 26 | } 27 | return SyntaxBuilder.DeclareLocal>("categories") 28 | .Comment("字符类 Unicode 类别") 29 | .Value(categories); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Generator/Lexers/LexerController.Contexts.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | 3 | namespace Cyjb.Compilers.Lexers; 4 | 5 | internal sealed partial class LexerController 6 | { 7 | /// 8 | /// 返回表示上下文的键的表达式。 9 | /// 10 | /// 上下文的键。 11 | /// 表示上下文的键的表达式。 12 | private static ExpressionBuilder GetContextKey(string key) 13 | { 14 | if (key == ContextData.Initial) 15 | { 16 | return SyntaxBuilder.Type().AccessMember("Initial"); 17 | } 18 | else 19 | { 20 | return key; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Generator/Parsers/ParserController.Goto.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | internal sealed partial class ParserController 6 | { 7 | /// 8 | /// 返回指定语法分析器数据的转移检查。 9 | /// 10 | /// 转移的检查数据 11 | /// 转移检查数据。 12 | private static ExpressionBuilder GotoCheck(SymbolKind[] gotoCheck) 13 | { 14 | var builder = ExpressionBuilder.CreateArray().InitializerWrap(1); 15 | foreach (SymbolKind value in gotoCheck) 16 | { 17 | builder.Initializer(value.Syntax); 18 | } 19 | return builder; 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Generator/Parsers/ParserController.KindMap.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | 4 | namespace Cyjb.Compilers.Parsers; 5 | 6 | internal sealed partial class ParserController 7 | { 8 | /// 9 | /// 返回指定符号类别字典的数据。 10 | /// 11 | /// 符号类别字典。 12 | /// 符号类别字典的数据。 13 | private static ExpressionBuilder KindMap(Dictionary map) 14 | { 15 | var builder = ExpressionBuilder.CreateObject().InitializerWrap(1); 16 | // 按照索引顺序生成起始状态 17 | foreach (var (key, value) in map.OrderBy(pair => pair.Key.Index)) 18 | { 19 | builder.Initializer(ExpressionBuilder.InitializerExpression(SyntaxKind.ComplexElementInitializerExpression) 20 | .Add(key.Syntax) 21 | .Add(value)); 22 | } 23 | return builder; 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Generator/Parsers/ParserController.Productions.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.CodeAnalysis.CSharp; 2 | 3 | namespace Cyjb.Compilers.Parsers; 4 | 5 | internal sealed partial class ParserController 6 | { 7 | /// 8 | /// 声明指定语法分析器数据的产生式数据。 9 | /// 10 | /// 语法分析器数据 11 | /// 产生式数据声明语句。 12 | private LocalDeclarationStatementBuilder DeclareProductions(ParserData data) 13 | { 14 | TypeBuilder productionType = typeof(ProductionData<>).AsName().TypeArgument(KindType); 15 | var builder = ExpressionBuilder.CreateArray().InitializerWrap(1); 16 | foreach (ProductionData production in data.Productions) 17 | { 18 | var productionBuilder = ExpressionBuilder.CreateObject(productionType).ArgumentWrap(1); 19 | builder.Initializer(productionBuilder); 20 | productionBuilder.Argument(production.HeadIndex); 21 | productionBuilder.Argument(production.Head.Syntax); 22 | if (production.Action == null) 23 | { 24 | productionBuilder.Argument(ExpressionBuilder.Null()); 25 | } 26 | else if (production.Action == ProductionAction.Optional) 27 | { 28 | productionBuilder.Argument(SyntaxBuilder.Type().AccessMember("Optional")); 29 | } 30 | else if (production.Action == ProductionAction.More) 31 | { 32 | productionBuilder.Argument(SyntaxBuilder.Type().AccessMember("More")); 33 | } 34 | else 35 | { 36 | var action = ExpressionBuilder.Lambda() 37 | .Parameter("c", Name) 38 | .Body("c".AsName().AccessMember(actionMap[production.Action]).Invoke()); 39 | productionBuilder.Argument(action); 40 | } 41 | foreach (SymbolKind kind in production.Body) 42 | { 43 | productionBuilder.Argument(kind.Syntax); 44 | } 45 | } 46 | return SyntaxBuilder.DeclareLocal(productionType.Clone().Array(), "productions") 47 | .Comment("产生式数据").Value(builder); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Generator/Properties/PublishProfiles/Publish.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | Release 8 | Any CPU 9 | bin\publish\ 10 | FileSystem 11 | net6.0 12 | false 13 | 14 | -------------------------------------------------------------------------------- /Generator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "CompilerGenerator": { 4 | "commandName": "Project", 5 | "commandLineArgs": "D:\\Workspace_DotNet\\Cyjb.Compilers\\TestCompilers\\Lexers\\TestCalcLexer.cs" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Generator/README.md: -------------------------------------------------------------------------------- 1 | Cyjb.Compilers.Generator 2 | ==== 3 | 4 | 允许根据设计时词法分析和语法分析定义,生成相应的实现,基于 .NET 6。 5 | 6 | 使用方式: 7 | 8 | ```bash 9 | Generator 10 | ``` 11 | 12 | 参数: 13 | 14 | - `` 已声明的词法/语法分析控制器文件。 15 | 16 | 输出: 17 | 18 | 生成的词法/语法分析器代码。 19 | 20 | 欢迎访问我的[博客](http://www.cnblogs.com/cyjb/)获取更多信息。 21 | 22 | C# 词法分析器系列博文 23 | 24 | - [C# 词法分析器(一)词法分析介绍](http://www.cnblogs.com/cyjb/archive/p/LexerIntroduce.html) 25 | - [C# 词法分析器(二)输入缓冲和代码定位](http://www.cnblogs.com/cyjb/archive/p/LexerInputBuffer.html) 26 | - [C# 词法分析器(三)正则表达式](http://www.cnblogs.com/cyjb/archive/p/LexerRegex.html) 27 | - [C# 词法分析器(四)构造 NFA](http://www.cnblogs.com/cyjb/archive/p/LexerNfa.html) 28 | - [C# 词法分析器(五)转换 DFA](http://www.cnblogs.com/cyjb/archive/p/LexerDfa.html) 29 | - [C# 词法分析器(六)构造词法分析器](http://www.cnblogs.com/cyjb/archive/p/LexerLexer.html) 30 | - [C# 词法分析器(七)总结](http://www.cnblogs.com/cyjb/p/LexerSummary.html) 31 | -------------------------------------------------------------------------------- /Generator/Resources.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Generator/Resources.cs -------------------------------------------------------------------------------- /Generator/Resources.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="$(PkgCyjb)\content\ResourcesTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /Generator/TransformationContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Cyjb.Compilers; 4 | 5 | /// 6 | /// 表示转换的上下文。 7 | /// 8 | internal class TransformationContext 9 | { 10 | /// 11 | /// 获取是否包含错误。 12 | /// 13 | public bool HasError { get; private set; } 14 | 15 | /// 16 | /// 添加指定的错误信息。 17 | /// 18 | /// 错误信息。 19 | /// 语法节点。 20 | public void AddError(string message, SyntaxNode node) 21 | { 22 | AddError(message, node.GetLocation()); 23 | } 24 | 25 | /// 26 | /// 添加指定的错误信息。 27 | /// 28 | /// 错误信息。 29 | /// 错误信息的位置。 30 | public void AddError(string message, Location? location) 31 | { 32 | HasError = true; 33 | string position = string.Empty; 34 | if (location != null) 35 | { 36 | FileLinePositionSpan span = location.GetLineSpan(); 37 | string file = Path.GetFileName(span.Path); 38 | int line = span.StartLinePosition.Line + 1; 39 | int column = span.StartLinePosition.Character + 1; 40 | position = $"{file}({line},{column}): "; 41 | } 42 | Console.WriteLine(position + message); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Generator/tests/TestCalcLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers 4 | { 5 | 6 | public enum Calc { Id, Add, Sub, Mul, Div, Pow, LBrace, RBrace } 7 | 8 | /// 9 | /// 用于单元测试的计算器控制器。 10 | /// 11 | [LexerSymbol("\\+", Kind = Calc.Add)] 12 | [LexerSymbol("\\-", Kind = Calc.Sub)] 13 | [LexerSymbol("\\*", Kind = Calc.Mul)] 14 | [LexerSymbol("\\/", Kind = Calc.Div)] 15 | [LexerSymbol("\\^", Kind = Calc.Pow)] 16 | [LexerSymbol("\\(", Kind = Calc.LBrace)] 17 | [LexerSymbol("\\)", Kind = Calc.RBrace)] 18 | [LexerSymbol("\\)", Kind = Calc.RBrace)] 19 | [LexerSymbol("\\)", Kind = Calc.RBrace)] 20 | [LexerSymbol("\\s")] 21 | public partial class TestCalcLexer : LexerController 22 | where T : struct 23 | { 24 | /// 25 | /// 数字的终结符定义。 26 | /// 27 | [LexerSymbol("[0-9]+", Kind = Calc.Id)] 28 | public void DigitAction() 29 | { 30 | Value = int.Parse(Text); 31 | Accept(); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Generator/tests/TestEscapeStrController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | using Cyjb.Compilers.Lexers; 5 | 6 | namespace TestCompilers.Lexers; 7 | 8 | /// 9 | /// 用于单元测试的转义字符串控制器。 10 | /// 11 | [LexerContext("str")] 12 | [LexerContext("vstr")] 13 | 14 | public partial class TestEscapeStrController : LexerController 15 | { 16 | private const string CtxStr = "str"; 17 | private const string CtxVstr = "vstr"; 18 | 19 | /// 20 | /// 当前起始索引。 21 | /// 22 | private int curStart; 23 | /// 24 | /// 处理后的文本。 25 | /// 26 | private readonly StringBuilder decodedText = new(); 27 | 28 | [LexerSymbol(@"\""")] 29 | public void BeginStrAction() 30 | { 31 | PushContext(CtxStr); 32 | curStart = Start; 33 | decodedText.Clear(); 34 | } 35 | 36 | [LexerSymbol(@"@\""")] 37 | public void BeginVstrAction() 38 | { 39 | PushContext(CtxVstr); 40 | curStart = Start; 41 | decodedText.Clear(); 42 | } 43 | 44 | [LexerSymbol(@"\""", Kind = Str.Str)] 45 | public void EndAction() 46 | { 47 | PopContext(); 48 | Start = curStart; 49 | Text = decodedText.ToString(); 50 | } 51 | 52 | [LexerSymbol(@"\\u[0-9]{4}")] 53 | [LexerSymbol(@"\\x[0-9]{2}")] 54 | public void HexEscapeAction() 55 | { 56 | decodedText.Append((char)int.Parse(Text.AsSpan()[2..], NumberStyles.HexNumber)); 57 | } 58 | 59 | [LexerSymbol(@"\\n")] 60 | public void EscapeLFAction() 61 | { 62 | decodedText.Append('\n'); 63 | } 64 | 65 | [LexerSymbol(@"\\\""")] 66 | public void EscapeQuoteAction() 67 | { 68 | decodedText.Append('\"'); 69 | } 70 | 71 | [LexerSymbol(@"\\r")] 72 | public void EscapeCRAction() 73 | { 74 | decodedText.Append('\r'); 75 | } 76 | 77 | [LexerSymbol(@"<*>.")] 78 | public void CopyAction() 79 | { 80 | decodedText.Append(Text); 81 | } 82 | 83 | [LexerSymbol(@"\""\""")] 84 | public void VstrQuoteAction() 85 | { 86 | decodedText.Append('"'); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Generator/tests/TestEscapeStrController.second.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | using Cyjb.Compilers.Lexers; 5 | 6 | namespace TestCompilers.Lexers; 7 | 8 | [LexerContext("vstr2")] 9 | 10 | public partial class TestEscapeStrController 11 | { 12 | [LexerSymbol(@"\""\""")] 13 | public void VstrQuoteAction2() 14 | { 15 | decodedText.Append('"'); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Generator/tests/TestProductionController.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 产生式的类型。 7 | /// 8 | public enum ProductionKind 9 | { 10 | /// 11 | /// 词法单元类型。 12 | /// 13 | Id, 14 | /// 15 | /// 左小括号。 16 | /// 17 | LBrace, 18 | /// 19 | /// 右小括号。 20 | /// 21 | RBrace, 22 | /// 23 | /// 星号 24 | /// 25 | Star, 26 | /// 27 | /// 加号 28 | /// 29 | Plus, 30 | /// 31 | /// 问号 32 | /// 33 | Question, 34 | } 35 | 36 | /// 37 | /// 用于单元测试的计算器控制器。 38 | /// 39 | [LexerSymbol(@"\d+|\w[\w\d]*", Kind = ProductionKind.Id)] 40 | [LexerSymbol(@"\(", Kind = ProductionKind.LBrace)] 41 | [LexerSymbol(@"\)", Kind = ProductionKind.RBrace)] 42 | [LexerSymbol(@"\+", Kind = ProductionKind.Plus)] 43 | [LexerSymbol(@"\*", Kind = ProductionKind.Star)] 44 | [LexerSymbol(@"\?", Kind = ProductionKind.Question)] 45 | [LexerSymbol(@"\s")] 46 | public partial class TestProductionController : LexerController 47 | { } 48 | 49 | -------------------------------------------------------------------------------- /Generator/tests/TestProductionParser.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Parsers; 2 | 3 | namespace Cyjb.Compilers.Parsers.Production; 4 | 5 | /// 6 | /// 产生式语法分析器。 7 | /// 8 | internal partial class TestProductionParser : ParserController 9 | { 10 | [ParserProduction(ProductionKind.Expression, ProductionKind.Expression, ProductionKind.Repeat)] 11 | private object? ExpressionAction() 12 | { 13 | ProductionNode node = (this[0].Value as ProductionNode)!; 14 | if (node.RepeatKind != ProductionKind.Id) 15 | { 16 | node = new ProductionNode(node); 17 | } 18 | node.Symbols.Add(this[1].Value!); 19 | return node; 20 | } 21 | 22 | [ParserProduction(ProductionKind.Expression, ProductionKind.Repeat)] 23 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item)] 24 | [ParserProduction(ProductionKind.Item, ProductionKind.Id)] 25 | private object? CopyAction() 26 | { 27 | object? value = this[0].Value; 28 | if (value is ProductionNode) 29 | { 30 | return value; 31 | } 32 | else 33 | { 34 | return new ProductionNode(value!); 35 | } 36 | } 37 | 38 | [ParserProduction(ProductionKind.Item, ProductionKind.LBrace, ProductionKind.Expression, ProductionKind.RBrace)] 39 | private object? BraceAction() 40 | { 41 | return new ProductionNode(this[1].Value!); 42 | } 43 | 44 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Plus)] 45 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Star)] 46 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Question)] 47 | private object? RepeatAction() 48 | { 49 | ProductionNode node = (this[0].Value as ProductionNode)!; 50 | node.RepeatKind = this[1].Kind; 51 | return node; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Generator/tests/TestSymbolValueLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 测试词法单元的值。 8 | /// 9 | public partial class TestSymbolValueLexer : LexerController 10 | { 11 | [LexerSymbol("[0-9]+", Kind = Calc.Id, Value = 101)] 12 | public void DigitAction() 13 | { 14 | Assert.AreEqual(101, Value); 15 | Accept((int)Value! + 10); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Generator/tests/UnitTestTemplateDiagnostics.template.cs: -------------------------------------------------------------------------------- 1 | [LexerSymbol(10, 20, 30, 40, 50)] 2 | [LexerSymbol(Regex: "f")] 3 | [LexerSymbol("F", regex : "f")] 4 | [LexerSymbol(options: RegexOptions.None, "f")] 5 | [LexerSymbol("f", Test = 10)] 6 | [LexerSymbol("F", Kind = "f", Kind = "F2")] 7 | [LexerSymbol] 8 | [LexerSymbol(10)] 9 | [LexerSymbol("T", RegexOptions.Test)] 10 | [LexerSymbol("T", 10.3)] 11 | [LexerSymbol("T", "abc")] 12 | [LexerSymbol("T", Priority = "abc")] 13 | [LexerContext("")] 14 | [LexerSymbol(" 16 | { 17 | [LexerSymbol("T")] 18 | public void InvalidAction(int a) { } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cyjb.Compilers 2 | ==== 3 | 4 | [![](https://img.shields.io/nuget/v/Cyjb.Compilers.svg)](https://www.nuget.org/packages/Cyjb.Compilers) 5 | [![codecov](https://codecov.io/gh/CYJB/Cyjb.Compilers/branch/master/graph/badge.svg?token=Fp9xTPxwm9)](https://codecov.io/gh/CYJB/Cyjb.Compilers) 6 | 7 | 提供编译相关功能,基于 .NET 6。 8 | 9 | 本项目包括一些与编译相关的功能,目前包含词法分析器和 LALR 语法分析器,以及相应的运行时。 10 | 11 | - Compilers 包含了构造词法和语法分析器的功能。 12 | - Design 提供了通过设计时 T4 模板生成词法分析器和语法分析器的能力。 13 | - Generator 是用于 T4 模板的代码生成器,其产物会由 Design 工具嵌入使用。 14 | - Runtime 提供了词法和语法分析的运行时。 15 | 16 | 详细的类库文档,请参见 [Wiki](https://github.com/CYJB/Cyjb.Compilers/wiki)。 17 | 18 | 欢迎访问我的[博客](http://www.cnblogs.com/cyjb/)获取更多信息。 19 | 20 | C# 词法分析器系列博文 21 | 22 | - [C# 词法分析器(一)词法分析介绍](http://www.cnblogs.com/cyjb/archive/p/LexerIntroduce.html) 23 | - [C# 词法分析器(二)输入缓冲和代码定位](http://www.cnblogs.com/cyjb/archive/p/LexerInputBuffer.html) 24 | - [C# 词法分析器(三)正则表达式](http://www.cnblogs.com/cyjb/archive/p/LexerRegex.html) 25 | - [C# 词法分析器(四)构造 NFA](http://www.cnblogs.com/cyjb/archive/p/LexerNfa.html) 26 | - [C# 词法分析器(五)转换 DFA](http://www.cnblogs.com/cyjb/archive/p/LexerDfa.html) 27 | - [C# 词法分析器(六)构造词法分析器](http://www.cnblogs.com/cyjb/archive/p/LexerLexer.html) 28 | - [C# 词法分析器(七)总结](http://www.cnblogs.com/cyjb/p/LexerSummary.html) 29 | 30 | C# 语法法分析器系列博文 31 | 32 | - [C# 语法分析器(一)语法分析介绍](https://www.cnblogs.com/cyjb/p/ParserIntroduce.html) 33 | - [C# 语法分析器(二)LR(0) 语法分析](https://www.cnblogs.com/cyjb/p/ParserLR_0.html) 34 | - [C# 语法分析器(三)LALR 语法分析](https://www.cnblogs.com/cyjb/p/ParserLALR.html) 35 | - [C# 语法分析器(四)二义性文法](https://www.cnblogs.com/cyjb/p/ParserAmbiguous.html) 36 | - [C# 语法分析器(五)错误恢复](https://www.cnblogs.com/cyjb/p/ParserErrorRecovery.html) 37 | - [C# 语法分析器(六)构造语法分析器](https://www.cnblogs.com/cyjb/p/ParserCreate.html) 38 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // 有关程序集的常规信息通过以下 5 | // 特性集控制。更改这些特性值可修改 6 | // 与程序集关联的信息。 7 | [assembly: AssemblyTrademark("CYJB")] 8 | [assembly: AssemblyCulture("")] 9 | [assembly: CLSCompliant(true)] 10 | 11 | // 将 ComVisible 设置为 false 使此程序集中的类型 12 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 13 | // 则将该类型上的 ComVisible 特性设置为 true。 14 | [assembly: ComVisible(false)] 15 | 16 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 17 | [assembly: Guid("ca903a41-123d-48ff-a523-e6011a1859f8")] 18 | 19 | -------------------------------------------------------------------------------- /Runtime/Cyjb.Compilers.Runtime.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | True 7 | True 8 | https://github.com/CYJB/Cyjb.Compilers 9 | 提供编译相关运行时 10 | Copyright (c) 2022, CYJB 11 | README.md 12 | LICENSE.txt 13 | en 14 | $(VersionPrefix) 15 | 1.0.23 16 | Cyjb.Compilers 17 | CYJB 18 | Compiler 19 | true 20 | snupkg 21 | 22 | 23 | ../CYJB_Code_Key.snk 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | True 33 | 34 | 35 | 36 | 37 | True 38 | True 39 | Resources.tt 40 | 41 | 42 | 43 | 44 | TextTemplatingFileGenerator 45 | Resources.cs 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Runtime/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, CYJB 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Runtime/Lexers/ContextData.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析中上下文的数据。 5 | /// 6 | public class ContextData 7 | { 8 | /// 9 | /// 初始的词法分析器上下文名称。 10 | /// 11 | public const string Initial = "Initial"; 12 | 13 | /// 14 | /// 默认的词法分析上下文。 15 | /// 16 | public readonly static IReadOnlyDictionary Default = 17 | new Dictionary() 18 | { 19 | { Initial, new ContextData(0, Initial) } 20 | }; 21 | 22 | /// 23 | /// 使用指定的上下文数据初始化 类的新实例。 24 | /// 25 | /// 上下文的索引。 26 | /// 上下文的标签。 27 | /// EOF 动作。 28 | /// EOF 动作的值。 29 | public ContextData(int index, string label, Delegate? eofAction = null, object? eofValue = null) 30 | { 31 | Index = index; 32 | Label = label; 33 | EofAction = eofAction; 34 | EofValue = eofValue; 35 | } 36 | 37 | /// 38 | /// 上下文的索引。 39 | /// 40 | public int Index { get; } 41 | /// 42 | /// 上下文的标签。 43 | /// 44 | public string Label { get; } 45 | /// 46 | /// 上下文的 EOF 动作。 47 | /// 48 | public Delegate? EofAction { get; } 49 | /// 50 | /// 上下文的 EOF 动作的值。 51 | /// 52 | public object? EofValue { get; } 53 | } 54 | -------------------------------------------------------------------------------- /Runtime/Lexers/Core/BasicCore`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示基本的词法分析器核心。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal sealed class BasicCore : LexerCore 8 | where T : struct 9 | { 10 | /// 11 | /// 词法分析器的数据。 12 | /// 13 | private readonly LexerData lexerData; 14 | /// 15 | /// DFA 的状态列表。 16 | /// 17 | private readonly int[] states; 18 | 19 | /// 20 | /// 使用给定的词法分析器信息初始化 类的新实例。 21 | /// 22 | /// 要使用的词法分析器的数据。 23 | /// 词法分析控制器。 24 | public BasicCore(LexerData lexerData, LexerController controller) : 25 | base(lexerData.States, lexerData.Terminals, lexerData.ContainsBeginningOfLine, controller) 26 | { 27 | this.lexerData = lexerData; 28 | states = lexerData.States; 29 | } 30 | 31 | /// 32 | /// 读取输入流中的下一个词法单元并提升输入流的字符位置。 33 | /// 34 | /// DFA 的起始状态。 35 | /// 当前词法单元的起始位置。 36 | /// 词法单元读入是否成功。 37 | public override bool NextToken(int state, int start) 38 | { 39 | // 最后一次匹配的符号和文本索引。 40 | int lastAccept = -1, lastIndex = source.Index; 41 | int symbolStart = 0, symbolEnd = 0; 42 | while (true) 43 | { 44 | state = lexerData.NextState(state, source.Read()); 45 | if (state == -1) 46 | { 47 | // 没有合适的转移,退出。 48 | break; 49 | } 50 | if (lexerData.GetSymbols(state, ref symbolStart, ref symbolEnd)) 51 | { 52 | lastAccept = states[symbolStart]; 53 | lastIndex = source.Index; 54 | // 使用最短匹配时,可以直接返回。 55 | if (lexerData.UseShortest && terminalData[lastAccept].UseShortest) 56 | { 57 | break; 58 | } 59 | } 60 | } 61 | if (lastAccept >= 0) 62 | { 63 | // 将流调整到与接受状态匹配的状态。 64 | DoAction(start, lastIndex, terminalData[lastAccept]); 65 | return true; 66 | } 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Runtime/Lexers/Core/BasicDebugCore.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示带有调试信息的基本的词法分析器核心。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal sealed class BasicDebugCore : LexerCore 8 | where T : struct 9 | { 10 | /// 11 | /// 词法分析器的数据。 12 | /// 13 | private readonly LexerData lexerData; 14 | /// 15 | /// DFA 的状态列表。 16 | /// 17 | private readonly int[] states; 18 | 19 | /// 20 | /// 使用给定的词法分析器信息初始化 类的新实例。 21 | /// 22 | /// 要使用的词法分析器的数据。 23 | /// 词法分析控制器。 24 | public BasicDebugCore(LexerData lexerData, LexerController controller) : 25 | base(lexerData.States, lexerData.Terminals, lexerData.ContainsBeginningOfLine, controller) 26 | { 27 | this.lexerData = lexerData; 28 | states = lexerData.States; 29 | } 30 | 31 | /// 32 | /// 读取输入流中的下一个词法单元并提升输入流的字符位置。 33 | /// 34 | /// DFA 的起始状态。 35 | /// 当前词法单元的起始位置。 36 | /// 词法单元读入是否成功。 37 | public override bool NextToken(int state, int start) 38 | { 39 | // 最后一次匹配的符号和文本索引。 40 | int startIndex = source.Index; 41 | int lastAccept = -1, lastIndex = source.Index; 42 | int symbolStart = 0, symbolEnd = 0; 43 | while (true) 44 | { 45 | state = lexerData.NextState(state, source.Read()); 46 | if (state == -1) 47 | { 48 | // 没有合适的转移,退出。 49 | break; 50 | } 51 | if (lexerData.GetSymbols(state, ref symbolStart, ref symbolEnd)) 52 | { 53 | lastAccept = states[symbolStart]; 54 | lastIndex = source.Index; 55 | // 使用最短匹配时,可以直接返回。 56 | if (lexerData.UseShortest && terminalData[lastAccept].UseShortest) 57 | { 58 | break; 59 | } 60 | } 61 | } 62 | PrintReadedText(); 63 | if (lastAccept >= 0) 64 | { 65 | // 将流调整到与接受状态匹配的状态。 66 | DoAction(start, lastIndex, terminalData[lastAccept]); 67 | Console.WriteLine(" Match {0}..{1} [{2}] {3}", startIndex, lastIndex, lastAccept, 68 | terminalData[lastAccept].Kind); 69 | return true; 70 | } 71 | else 72 | { 73 | Console.WriteLine(" Match none"); 74 | } 75 | return false; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Runtime/Lexers/Core/LexerStateInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 词法分析器的状态信息。 5 | /// 6 | class LexerStateInfo 7 | { 8 | /// 9 | /// 当前源文件索引。 10 | /// 11 | public int SourceIndex; 12 | /// 13 | /// 匹配的终结符起始索引(包含)。 14 | /// 15 | public int TerminalStart; 16 | /// 17 | /// 匹配的终结符结束索引(不包含)。 18 | /// 19 | public int TerminalEnd; 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/Lexers/DfaStateData.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// DFA 的状态数据。 5 | /// 6 | public static class DfaStateData 7 | { 8 | /// 9 | /// 表示无效的状态。 10 | /// 11 | public const int InvalidState = -1; 12 | /// 13 | /// 默认状态的偏移。 14 | /// 15 | public const int DefaultStateOffset = 1; 16 | /// 17 | /// 符号列表长度的偏移。 18 | /// 19 | public const int SymbolsLengthOffset = 2; 20 | /// 21 | /// 符号索引的偏移。 22 | /// 23 | public const int SymbolIndexOffset = 3; 24 | } 25 | -------------------------------------------------------------------------------- /Runtime/Lexers/ILexerFactory`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | public interface ILexerFactory 8 | where T : struct 9 | { 10 | /// 11 | /// 创建词法分析器。 12 | /// 13 | /// 是否需要打印调试信息。 14 | /// 已创建的词法分析器。 15 | LexerTokenizer CreateTokenizer(bool debug = false); 16 | 17 | /// 18 | /// 创建词法分析运行器。 19 | /// 20 | /// 是否需要打印调试信息。 21 | /// 已创建的词法分析运行器。 22 | LexerRunner CreateRunner(bool debug = false); 23 | } 24 | -------------------------------------------------------------------------------- /Runtime/Lexers/LexerFactory`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 关于如何构造自己的词法分析器,可以参考我的博文 8 | /// 9 | /// 《C# 词法分析器(六)构造词法分析器》 10 | /// 11 | /// 《C# 词法分析器(六)构造词法分析器》 12 | public sealed class LexerFactory : ILexerFactory 13 | where T : struct 14 | { 15 | /// 16 | /// 动作处理器。 17 | /// 18 | private static readonly Action> actionHandler = 19 | (Delegate action, LexerController controller) => 20 | { 21 | ((Action>)action)(controller); 22 | }; 23 | 24 | /// 25 | /// 词法分析器的数据。 26 | /// 27 | private readonly LexerData lexerData; 28 | 29 | /// 30 | /// 使用指定的词法分析器数据初始化 类的新实例。 31 | /// 32 | /// 词法分析器的数据。 33 | public LexerFactory(LexerData lexerData) 34 | { 35 | this.lexerData = lexerData; 36 | } 37 | 38 | /// 39 | /// 创建词法分析器。 40 | /// 41 | /// 是否需要打印调试信息。 42 | /// 已创建的词法分析器。 43 | public LexerTokenizer CreateTokenizer(bool debug = false) 44 | { 45 | LexerController controller = new() 46 | { 47 | ActionHandler = actionHandler 48 | }; 49 | LexerCore core = LexerCore.Create(lexerData, controller, debug); 50 | controller.SetCore(core, lexerData.Contexts, lexerData.Rejectable); 51 | return new LexerTokenizer(core); 52 | } 53 | 54 | /// 55 | /// 创建词法分析运行器。 56 | /// 57 | /// 是否需要打印调试信息。 58 | /// 已创建的词法分析运行器。 59 | public LexerRunner CreateRunner(bool debug = false) 60 | { 61 | LexerController controller = new() 62 | { 63 | ActionHandler = actionHandler 64 | }; 65 | LexerCore core = LexerCore.Create(lexerData, controller, debug); 66 | controller.SetCore(core, lexerData.Contexts, lexerData.Rejectable); 67 | return new LexerRunner(core); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Runtime/Lexers/LexerFactory`2.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 词法分析控制器的类型。 8 | /// 关于如何构造自己的词法分析器,可以参考我的博文 9 | /// 10 | /// 《C# 词法分析器(六)构造词法分析器》 11 | /// 12 | /// 《C# 词法分析器(六)构造词法分析器》 13 | public sealed class LexerFactory : ILexerFactory 14 | where T : struct 15 | where TController : LexerController, new() 16 | { 17 | /// 18 | /// 动作处理器。 19 | /// 20 | private static readonly Action> actionHandler = 21 | (Delegate action, LexerController controller) => 22 | { 23 | ((Action)action)((TController)controller); 24 | }; 25 | 26 | /// 27 | /// 词法分析器的数据。 28 | /// 29 | private readonly LexerData lexerData; 30 | 31 | /// 32 | /// 使用指定的词法分析器数据初始化 类的新实例。 33 | /// 34 | /// 词法分析器的数据。 35 | public LexerFactory(LexerData lexerData) 36 | { 37 | this.lexerData = lexerData; 38 | } 39 | 40 | /// 41 | /// 创建词法分析器。 42 | /// 43 | /// 是否需要打印调试信息。 44 | /// 已创建的词法分析器。 45 | public LexerTokenizer CreateTokenizer(bool debug = false) 46 | { 47 | TController controller = new() 48 | { 49 | ActionHandler = actionHandler 50 | }; 51 | LexerCore core = LexerCore.Create(lexerData, controller, debug); 52 | controller.SetCore(core, lexerData.Contexts, lexerData.Rejectable); 53 | return new LexerTokenizer(core); 54 | } 55 | 56 | /// 57 | /// 创建词法分析运行器。 58 | /// 59 | /// 是否需要打印调试信息。 60 | /// 已创建的词法分析运行器。 61 | public LexerRunner CreateRunner(bool debug = false) 62 | { 63 | TController controller = new() 64 | { 65 | ActionHandler = actionHandler 66 | }; 67 | LexerCore core = LexerCore.Create(lexerData, controller, debug); 68 | controller.SetCore(core, lexerData.Contexts, lexerData.Rejectable); 69 | return new LexerRunner(core); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Runtime/Lexers/RejectOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 拒绝匹配的选项。 5 | /// 6 | public enum RejectOptions 7 | { 8 | /// 9 | /// 拒绝当前匹配。 10 | /// 11 | Default, 12 | /// 13 | /// 拒绝当前状态的匹配。 14 | /// 15 | /// 会忽略当前状态的后续匹配,避免使用更短的字符串重复匹配相同状态。 16 | State, 17 | } 18 | -------------------------------------------------------------------------------- /Runtime/Lexers/TerminalData`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 表示词法分析中终结符的数据。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | public sealed class TerminalData 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的终结符信息初始化 类的新实例。 12 | /// 13 | /// 终结符的类型。 14 | /// 终结符的值。 15 | /// 终结符的动作。 16 | /// 向前看信息。 17 | /// 是否使用最短匹配。 18 | public TerminalData(T? kind = null, object? value = null, Delegate? action = null, 19 | int? trailing = null, bool useShortest = false) 20 | { 21 | Kind = kind; 22 | Value = value; 23 | Action = action; 24 | Trailing = trailing; 25 | UseShortest = useShortest; 26 | } 27 | 28 | /// 29 | /// 获取终结符的类型。 30 | /// 31 | public T? Kind { get; } 32 | /// 33 | /// 获取终结符的值。 34 | /// 35 | public object? Value { get; } 36 | /// 37 | /// 获取终结符的动作。 38 | /// 39 | public Delegate? Action { get; } 40 | /// 41 | /// 获取终结符的向前看信息。 42 | /// 43 | /// null 表示不是向前看符号,正数表示前面长度固定, 44 | /// 负数表示后面长度固定,0 表示长度不固定。 45 | public int? Trailing { get; } 46 | /// 47 | /// 获取设置是否使用终结符的最短匹配。 48 | /// 49 | /// 默认都会使用正则表达式的最长匹配,允许指定为使用最短匹配, 50 | /// 会在遇到第一个匹配时立即返回结果。 51 | public bool UseShortest { get; } 52 | 53 | /// 54 | /// 返回当前对象的字符串表示形式。 55 | /// 56 | /// 当前对象的字符串表示形式。 57 | public override string ToString() 58 | { 59 | string mark = UseShortest ? " (s)" : ""; 60 | if (Trailing == null) 61 | { 62 | return $"[{Kind}]{mark}"; 63 | } 64 | else 65 | { 66 | return $"[{Kind}] {Trailing}{mark}"; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Runtime/Lexers/TrailingType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Lexers; 2 | 3 | /// 4 | /// 向前看符号的类型。 5 | /// 6 | public enum TrailingType 7 | { 8 | /// 9 | /// 没有使用向前看符号。 10 | /// 11 | None, 12 | /// 13 | /// 定长的向前看符号。 14 | /// 15 | Fixed, 16 | /// 17 | /// 变长的向前看符号。 18 | /// 19 | Variable 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/Parsers/EmptyTokenizer`1.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Cyjb.Text; 4 | 5 | /// 6 | /// 提供空的词法分析器。 7 | /// 8 | /// 词法单元标识符的类型,一般是一个枚举类型。 9 | internal sealed class EmptyTokenizer : ITokenizer 10 | where T : struct 11 | { 12 | /// 13 | /// 空的词法分析器实例。 14 | /// 15 | public static readonly ITokenizer Empty = new EmptyTokenizer(); 16 | 17 | /// 18 | /// 词法分析错误的事件。 19 | /// 20 | public event TokenizeErrorHandler? TokenizeError; 21 | 22 | /// 23 | /// 获取词法分析器的解析状态。 24 | /// 25 | public ParseStatus Status => ParseStatus.Finished; 26 | /// 27 | /// 获取或设置共享的上下文对象。 28 | /// 29 | /// 可以与外部(例如语法分析器)共享信息。 30 | public object? SharedContext { get; set; } 31 | 32 | /// 33 | /// 读取输入流中的下一个词法单元并提升输入流的字符位置。 34 | /// 35 | /// 输入流中的下一个词法单元。 36 | public Token Read() 37 | { 38 | return Token.GetEndOfFile(0); 39 | } 40 | 41 | /// 42 | /// 取消后续词法分析。 43 | /// 44 | public void Cancel() { } 45 | 46 | /// 47 | /// 重置词法分析的状态,允许在结束/取消后继续进行分析。 48 | /// 49 | public void Reset() { } 50 | 51 | #region IDisposable 成员 52 | 53 | /// 54 | /// 执行与释放或重置非托管资源相关的应用程序定义的任务。 55 | /// 56 | public void Dispose() { } 57 | 58 | #endregion // IDisposable 成员 59 | 60 | #region IEnumerable> 成员 61 | 62 | /// 63 | /// 返回一个循环访问集合的枚举器。 64 | /// 65 | /// 可用于循环访问集合的 66 | /// 在枚举的时候, 会不断的读出词法单元, 67 | /// 应当总是只使用一个枚举器。在使用多个枚举器时,他们之间会相互干扰,导致枚举值与期望的不同。 68 | /// 如果需要多次枚举,必须将词法单元缓存到数组中,再进行枚举。 69 | public IEnumerator> GetEnumerator() 70 | { 71 | yield break; 72 | } 73 | 74 | /// 75 | /// 返回一个循环访问集合的枚举器。 76 | /// 77 | /// 可用于循环访问集合的 78 | IEnumerator IEnumerable.GetEnumerator() 79 | { 80 | return GetEnumerator(); 81 | } 82 | 83 | #endregion // IEnumerable> 成员 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Runtime/Parsers/IParserFactory`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示语法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | public interface IParserFactory 8 | where T : struct 9 | { 10 | /// 11 | /// 创建语法分析器。 12 | /// 13 | /// 已创建的语法分析器。 14 | LRParser CreateParser(); 15 | } 16 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParseOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 语法分析选项。 5 | /// 6 | public enum ParseOptions 7 | { 8 | /// 9 | /// 扫描到文件结尾。 10 | /// 11 | ScanToEOF, 12 | /// 13 | /// 扫描到匹配的语法单元。 14 | /// 15 | /// 对于 A* 这样的规则,会匹配到最后一个可能的 A 16 | ScanToMatch, 17 | /// 18 | /// 扫描到首次匹配的语法单元。 19 | /// 20 | /// 对于 A* 这样的规则,只会匹配第一个 A 21 | ScanToFirstMatch, 22 | } 23 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParserAction.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// LR 语法分析器的动作。 5 | /// 6 | public readonly record struct ParserAction 7 | { 8 | /// 9 | /// 获取表示接受的分析器动作。 10 | /// 11 | public static readonly ParserAction Accept = new(ParserActionType.Accept, 0); 12 | /// 13 | /// 获取表示错误的分析器动作。 14 | /// 15 | public static readonly ParserAction Error = new(ParserActionType.Error, 0); 16 | 17 | /// 18 | /// 返回表示归约的分析器动作。 19 | /// 20 | /// 归约使用的产生式编号。 21 | /// 表示归约的分析器动作。 22 | public static ParserAction Reduce(int index) => new(ParserActionType.Reduce, index); 23 | /// 24 | /// 返回表示移入的分析器动作。 25 | /// 26 | /// 移入后要压栈的状态编号。 27 | /// 表示移入的分析器动作。 28 | public static ParserAction Shift(int index) => new(ParserActionType.Shift, index); 29 | 30 | /// 31 | /// 使用指定的动作类型和索引初始化。 32 | /// 33 | /// 动作类型。 34 | /// 动作关联到的索引。 35 | private ParserAction(ParserActionType type, int index) 36 | { 37 | Type = type; 38 | Index = index; 39 | } 40 | 41 | /// 42 | /// 获取动作类型。 43 | /// 44 | public ParserActionType Type { get; init; } 45 | /// 46 | /// 获取动作关联到的索引。 47 | /// 48 | public int Index { get; init; } 49 | 50 | /// 51 | /// 返回当前对象的字符串表示。 52 | /// 53 | /// 当前对象的字符串表示。 54 | public override string ToString() 55 | { 56 | return Type switch 57 | { 58 | ParserActionType.Accept => "acc", 59 | ParserActionType.Shift => $"s{Index}", 60 | ParserActionType.Reduce => $"r{Index}", 61 | _ => "err", 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParserActionType.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// LR 语法分析器的动作类型。 5 | /// 6 | public enum ParserActionType 7 | { 8 | /// 9 | /// 错误动作。 10 | /// 11 | Error = 0, 12 | /// 13 | /// 移入动作。 14 | /// 15 | Shift = 1, 16 | /// 17 | /// 归约动作。 18 | /// 19 | Reduce = 2, 20 | /// 21 | /// 接受动作。 22 | /// 23 | Accept = 3, 24 | } 25 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParserData.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示 LR 语法分析器的数据。 5 | /// 6 | public sealed class ParserData 7 | { 8 | /// 9 | /// 表示无效的状态。 10 | /// 11 | public const int InvalidState = -1; 12 | } 13 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParserFactory`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示语法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | public sealed class ParserFactory : IParserFactory 8 | where T : struct 9 | { 10 | /// 11 | /// 动作处理器。 12 | /// 13 | private static readonly Func, object?> actionHandler = 14 | (Delegate action, ParserController controller) => 15 | { 16 | return ((Func, object?>)action)(controller); 17 | }; 18 | 19 | /// 20 | /// 语法分析器的数据。 21 | /// 22 | private readonly ParserData parserData; 23 | 24 | /// 25 | /// 使用指定的语法分析器数据初始化 类的新实例。 26 | /// 27 | /// 语法分析器的数据。 28 | public ParserFactory(ParserData parserData) 29 | { 30 | this.parserData = parserData; 31 | } 32 | 33 | /// 34 | /// 创建语法分析器。 35 | /// 36 | /// 已创建的语法分析器。 37 | public LRParser CreateParser() 38 | { 39 | ParserController controller = new(); 40 | LRParser parser = new(parserData, controller); 41 | controller.Init(parserData, parser, actionHandler); 42 | return parser; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Runtime/Parsers/ParserFactory`2.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 表示语法分析器的工厂。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 语法分析控制器的类型。 8 | public sealed class ParserFactory : IParserFactory 9 | where T : struct 10 | where TController : ParserController, new() 11 | { 12 | /// 13 | /// 动作处理器。 14 | /// 15 | private static readonly Func, object?> actionHandler = 16 | (Delegate action, ParserController controller) => 17 | { 18 | return ((Func)action)((TController)controller); 19 | }; 20 | 21 | /// 22 | /// 语法分析器的数据。 23 | /// 24 | private readonly ParserData parserData; 25 | 26 | /// 27 | /// 使用指定的语法分析器数据初始化 类的新实例。 28 | /// 29 | /// 语法分析器的数据。 30 | public ParserFactory(ParserData parserData) 31 | { 32 | this.parserData = parserData; 33 | } 34 | 35 | /// 36 | /// 创建语法分析器。 37 | /// 38 | /// 已创建的语法分析器。 39 | public LRParser CreateParser() 40 | { 41 | TController controller = new(); 42 | LRParser parser = new(parserData, controller); 43 | controller.Init(parserData, parser, actionHandler); 44 | return parser; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Runtime/Parsers/ProductionAction.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 提供产生式数据的预置动作。 5 | /// 6 | public sealed class ProductionAction 7 | { 8 | /// 9 | /// 供 使用的动作。 10 | /// 11 | public static readonly Delegate Optional = () => "OptionalAction"; 12 | /// 13 | /// 供 使用的动作。 14 | /// 15 | public static readonly Delegate More = () => "MoreAction"; 16 | } 17 | -------------------------------------------------------------------------------- /Runtime/Parsers/ProductionData`1.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text; 3 | 4 | namespace Cyjb.Compilers.Parsers; 5 | 6 | /// 7 | /// 表示产生式的数据。 8 | /// 9 | /// 词法单元标识符的类型,一般是一个枚举类型。 10 | public sealed class ProductionData 11 | where T : struct 12 | { 13 | /// 14 | /// 产生式体的大小。 15 | /// 16 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 17 | private readonly int bodySize; 18 | 19 | /// 20 | /// 使用产生式的数据初始化 类的新实例。 21 | /// 22 | /// 产生式头的索引。 23 | /// 产生式头。 24 | /// 产生式对应的动作。 25 | /// 产生式体。 26 | public ProductionData(int headIndex, T head, Delegate? action, params T[] body) 27 | { 28 | HeadIndex = headIndex; 29 | Head = head; 30 | Action = action; 31 | Body = body; 32 | bodySize = body.Length; 33 | } 34 | 35 | /// 36 | /// 获取产生式头的索引。 37 | /// 38 | /// 产生式头的索引。 39 | public int HeadIndex { get; } 40 | /// 41 | /// 获取产生式头。 42 | /// 43 | /// 产生式头。 44 | public T Head { get; } 45 | /// 46 | /// 获取产生式对应的动作。 47 | /// 48 | /// 产生式对应的动作。 49 | public Delegate? Action { get; } 50 | /// 51 | /// 获取产生式体的大小。 52 | /// 53 | public int BodySize => bodySize; 54 | /// 55 | /// 获取产生式体。 56 | /// 57 | /// 产生式体。 58 | public T[] Body { get; } 59 | 60 | /// 61 | /// 返回当前对象的字符串表示形式。 62 | /// 63 | /// 当前对象的字符串表示形式。 64 | public override string ToString() 65 | { 66 | StringBuilder text = new(); 67 | text.Append(Head); 68 | text.Append(" ->"); 69 | if (bodySize == 0) 70 | { 71 | text.Append(" ε"); 72 | } 73 | else 74 | { 75 | foreach (T item in Body) 76 | { 77 | text.Append(' '); 78 | text.Append(item); 79 | } 80 | } 81 | return text.ToString(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Runtime/Parsers/SymbolOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Compilers.Parsers; 2 | 3 | /// 4 | /// 符号选项。 5 | /// 6 | public enum SymbolOptions 7 | { 8 | /// 9 | /// 无选项。 10 | /// 11 | None, 12 | /// 13 | /// 可选的符号,可能出现 0 或 1 次。 14 | /// 15 | Optional, 16 | /// 17 | /// 允许出现 1 或更多次。 18 | /// 19 | OneOrMore, 20 | /// 21 | /// 允许出现 0 或更多次。 22 | /// 23 | ZeroOrMore, 24 | } 25 | -------------------------------------------------------------------------------- /Runtime/README.md: -------------------------------------------------------------------------------- 1 | Cyjb.Compilers.Runtime 2 | ==== 3 | 4 | [![](https://img.shields.io/nuget/v/Cyjb.Compilers.Runtime.svg)](https://www.nuget.org/packages/Cyjb.Compilers.Runtime) 5 | [![codecov](https://codecov.io/gh/CYJB/Cyjb.Compilers/branch/master/graph/badge.svg?token=Fp9xTPxwm9)](https://codecov.io/gh/CYJB/Cyjb.Compilers) 6 | 7 | 提供编译相关运行时,基于 .NET 6。 8 | 9 | 本项目包括一些与编译相关的基础接口,用于运行词法分析器和语法分析器。 10 | 11 | 详细的类库文档,请参见 [Wiki](https://github.com/CYJB/Cyjb.Compilers/wiki)。 12 | 13 | 欢迎访问我的[博客](http://www.cnblogs.com/cyjb/)获取更多信息。 14 | 15 | -------------------------------------------------------------------------------- /Runtime/Resources.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYJB/Cyjb.Compilers/59a4272bfac4c9b9bf0447a869fb7e3f68480e6f/Runtime/Resources.cs -------------------------------------------------------------------------------- /Runtime/Resources.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="$(PkgCyjb)\content\ResourcesTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /Runtime/Text/ITokenParser`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 表示语法分析错误的事件处理器。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 出现错误的语法分析器。 8 | /// 错误信息 9 | public delegate void TokenParseErrorHandler(ITokenParser parser, TokenParseError error) 10 | where T : struct; 11 | 12 | 13 | /// 14 | /// 表示语法分析器。 15 | /// 16 | /// 词法单元标识符的类型,一般是一个枚举类型。 17 | public interface ITokenParser : IDisposable 18 | where T : struct 19 | { 20 | /// 21 | /// 语法分析错误的事件。 22 | /// 23 | event TokenParseErrorHandler? ParseError; 24 | /// 25 | /// 获取语法分析器的解析状态。 26 | /// 27 | ParseStatus Status { get; } 28 | /// 29 | /// 获取或设置共享的上下文对象。 30 | /// 31 | /// 可以与外部(例如词法分析器)共享信息,只能够在首次调用 前设置。 32 | object? SharedContext { get; set; } 33 | 34 | /// 35 | /// 使用默认的目标类型分析当前词法单元序列。 36 | /// 37 | /// 语法分析的结果。 38 | public abstract ParserNode Parse(); 39 | 40 | /// 41 | /// 使用指定的目标类型分析当前词法单元序列。 42 | /// 43 | /// 目标类型。 44 | /// 语法分析的结果。 45 | public abstract ParserNode Parse(T target); 46 | 47 | /// 48 | /// 取消后续语法分析。 49 | /// 50 | void Cancel(); 51 | 52 | /// 53 | /// 重置语法分析的状态,允许在结束/取消后继续进行分析。 54 | /// 55 | void Reset(); 56 | } 57 | -------------------------------------------------------------------------------- /Runtime/Text/ITokenizer`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 表示词法分析错误的事件处理器。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | /// 出现错误的词法分析器。 8 | /// 错误信息 9 | public delegate void TokenizeErrorHandler(ITokenizer tokenizer, TokenizeError error) 10 | where T : struct; 11 | 12 | /// 13 | /// 表示一个词法分析器。 14 | /// 15 | /// 16 | /// 词法单元标识符的类型,一般是一个枚举类型。 17 | public interface ITokenizer : IDisposable, IEnumerable> 18 | where T : struct 19 | { 20 | /// 21 | /// 词法分析错误的事件。 22 | /// 23 | event TokenizeErrorHandler? TokenizeError; 24 | /// 25 | /// 获取词法分析器的解析状态。 26 | /// 27 | ParseStatus Status { get; } 28 | /// 29 | /// 获取或设置共享的上下文对象。 30 | /// 31 | /// 可以与外部(例如语法分析器)共享信息,只能够在首次调用 前设置。 32 | object? SharedContext { get; set; } 33 | 34 | /// 35 | /// 读取输入流中的下一个词法单元并提升输入流的字符位置。 36 | /// 37 | /// 输入流中的下一个词法单元。 38 | Token Read(); 39 | 40 | /// 41 | /// 取消后续词法分析。 42 | /// 43 | void Cancel(); 44 | 45 | /// 46 | /// 重置词法分析的状态,允许在结束/取消后继续进行分析。 47 | /// 48 | void Reset(); 49 | } 50 | -------------------------------------------------------------------------------- /Runtime/Text/MissingTokenError.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Cyjb.Compilers; 3 | 4 | namespace Cyjb.Text; 5 | 6 | /// 7 | /// 表示缺失词法单元的错误。 8 | /// 9 | /// 语法节点标识符的类型,必须是一个枚举类型。 10 | public class MissingTokenError : TokenParseError 11 | where T : struct 12 | { 13 | /// 14 | /// 缺失的词法单元。 15 | /// 16 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 17 | private readonly Token token; 18 | 19 | /// 20 | /// 使用缺失的词法单元初始化 类的新实例。 21 | /// 22 | /// 缺失的词法单元。 23 | public MissingTokenError(Token token) 24 | { 25 | this.token = token; 26 | } 27 | 28 | /// 29 | /// 获取缺失的词法单元类型。 30 | /// 31 | public T Missing => token.Kind; 32 | 33 | /// 34 | /// 获取语法分析错误的范围。 35 | /// 36 | /// 语法分析错误的范围。 37 | public override TextSpan Span => token.Span; 38 | 39 | /// 40 | /// 返回当前对象是否等于同一类型的另一对象。 41 | /// 42 | /// 要比较的对象。 43 | /// 如果当前对象等于 ,则为 true;否则为 false 44 | public override bool Equals(TokenParseError? other) 45 | { 46 | if (other is MissingTokenError error) 47 | { 48 | return token == error.token; 49 | } 50 | else 51 | { 52 | return false; 53 | } 54 | } 55 | 56 | /// 57 | /// 返回当前对象的哈希值。 58 | /// 59 | /// 当前对象的哈希值。 60 | public override int GetHashCode() 61 | { 62 | return token.GetHashCode(); 63 | } 64 | 65 | /// 66 | /// 返回当前对象的字符串表示形式。 67 | /// 68 | /// 当前对象的字符串表示形式。 69 | public override string ToString() 70 | { 71 | return Resources.MissingToken(Token.GetDisplayName(token.Kind), Span); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Runtime/Text/ParseStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 词法或语法分析器的解析状态。 5 | /// 6 | public enum ParseStatus 7 | { 8 | /// 9 | /// 已准备好解析。 10 | /// 11 | Ready, 12 | /// 13 | /// 已解析完毕。 14 | /// 15 | Finished, 16 | /// 17 | /// 已取消解析。 18 | /// 19 | Cancelled 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/Text/ParserNode`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 表示语法节点。 5 | /// 6 | /// 语法节点标识符的类型,一般是一个枚举类型。 7 | public sealed class ParserNode 8 | where T : struct 9 | { 10 | /// 11 | /// 使用指定的词法单元初始化 类的新实例。 12 | /// 13 | /// 初始化使用的词法单元。 14 | public ParserNode(Token token) 15 | { 16 | Kind = token.Kind; 17 | Text = token.Text; 18 | Span = token.Span; 19 | Value = token.Value; 20 | } 21 | 22 | /// 23 | /// 使用指定的语法节点信息初始化 类的新实例。 24 | /// 25 | /// 语法节点的类型。 26 | /// 语法节点的范围。 27 | public ParserNode(T kind, TextSpan span) 28 | { 29 | Kind = kind; 30 | Text = StringView.Empty; 31 | Span = span; 32 | } 33 | 34 | /// 35 | /// 获取语法节点的类型。 36 | /// 37 | public T Kind { get; init; } 38 | /// 39 | /// 获取语法节点的文本(仅终结符包含文本,非终结符为空字符串)。 40 | /// 41 | public StringView Text { get; init; } 42 | /// 43 | /// 获取语法节点的范围。 44 | /// 45 | public TextSpan Span { get; init; } 46 | /// 47 | /// 获取语法节点的值。 48 | /// 49 | public object? Value { get; internal set; } 50 | /// 51 | /// 获取当前语法节点是否是由错误恢复逻辑生成的。 52 | /// 53 | public bool IsMissing { get; init; } = false; 54 | /// 55 | /// 获取当前语法节点前被跳过的词法单元列表。 56 | /// 57 | public Token[] SkippedTokens { get; init; } = Array.Empty>(); 58 | /// 59 | /// 获取当前语法节点是否包含错误。 60 | /// 61 | public bool ContainsError => IsMissing || SkippedTokens.Length > 0; 62 | 63 | /// 64 | /// 返回当前对象的字符串表示形式。 65 | /// 66 | /// 当前对象的字符串表示形式。 67 | public override string ToString() 68 | { 69 | return $"{Kind} at {Span}"; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Runtime/Text/TokenDisplayNameAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 表示词法单元类型的显示名称。 5 | /// 6 | [AttributeUsage(AttributeTargets.Field)] 7 | public sealed class TokenDisplayNameAttribute : Attribute 8 | { 9 | /// 10 | /// 使用指定的显示名称初始化 类的新实例。 11 | /// 12 | /// 词法单元类型的显示名称。 13 | public TokenDisplayNameAttribute(string displayName) 14 | { 15 | DisplayName = displayName; 16 | } 17 | 18 | /// 19 | /// 获取词法单元类型的显示名称。 20 | /// 21 | public string DisplayName { get; } 22 | } 23 | -------------------------------------------------------------------------------- /Runtime/Text/TokenSpanComparer`1.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 表示一个词法单元的位置比较器。 5 | /// 6 | /// 词法单元标识符的类型,一般是一个枚举类型。 7 | internal class TokenSpanComparer : IComparer> 8 | where T : struct 9 | { 10 | /// 11 | /// 词法单元的位置比较器实例。 12 | /// 13 | public static readonly TokenSpanComparer Instance = new(); 14 | 15 | /// 16 | /// 比较两个对象并返回一个值,指示一个对象小于、等于还是大于另一个对象。 17 | /// 18 | /// 要比较的第一个对象。 19 | /// 要比较的第二个对象。 20 | /// 一个有符号整数,指示 的相对值。 21 | public int Compare(Token? x, Token? y) 22 | { 23 | if (ReferenceEquals(x, y)) 24 | { 25 | return 0; 26 | } 27 | else if (x is null) 28 | { 29 | return -1; 30 | } 31 | else if (y is null) 32 | { 33 | return 1; 34 | } 35 | return x.Span.CompareTo(y.Span); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Runtime/Text/Token`1.DisplayName.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Cyjb.Text; 4 | 5 | public partial class Token 6 | { 7 | /// 8 | /// 词法单元类型的显示名称映射表。 9 | /// 10 | private static readonly Lazy> displayNames = new(() => 11 | { 12 | Dictionary result = new(); 13 | try 14 | { 15 | foreach (FieldInfo field in typeof(T).GetFields()) 16 | { 17 | TokenDisplayNameAttribute? attr = field.GetCustomAttribute(); 18 | if (attr != null && Enum.TryParse(field.Name, out T value)) 19 | { 20 | result[value] = attr.DisplayName; 21 | } 22 | } 23 | } 24 | catch (ArgumentException) 25 | { 26 | // T 不是枚举。 27 | } 28 | return result; 29 | }); 30 | 31 | /// 32 | /// 返回指定词法单元类型的显示名称。 33 | /// 34 | /// 要检查的词法单元类型。 35 | /// 指定词法单元类型的显示名称。 36 | public static string GetDisplayName(T kind) 37 | { 38 | if (EqualityComparer.Default.Equals(kind, EndOfFile)) 39 | { 40 | return "<>"; 41 | } 42 | if (displayNames.Value.TryGetValue(kind, out string? name)) 43 | { 44 | return name; 45 | } 46 | else 47 | { 48 | return kind.ToString()!; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Runtime/Text/Utils.cs: -------------------------------------------------------------------------------- 1 | namespace Cyjb.Text; 2 | 3 | /// 4 | /// 提供工具方法。 5 | /// 6 | internal static class Utils 7 | { 8 | /// 9 | /// 换行的字符。 10 | /// 11 | public static char[] NewLineChars = { '\r', '\n' }; 12 | 13 | /// 14 | /// 返回指定的字符是否是换行符。 15 | /// 16 | public static bool IsLineBreak(char ch) 17 | { 18 | return ch is '\n' or '\r' or '\u0085' or '\u2028' or '\u2029'; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TestCompilers/Calc.cs: -------------------------------------------------------------------------------- 1 | namespace TestCompilers; 2 | 3 | public enum Calc { Id, Add, Sub, Mul, Div, Pow, LBrace, RBrace, E } 4 | -------------------------------------------------------------------------------- /TestCompilers/CompilerTemplate.t4: -------------------------------------------------------------------------------- 1 | <# 2 | /** 3 | * 生成词法分析器或语法分析器的实现。 4 | */ 5 | #> 6 | <#@ template language="C#" hostspecific="true" #> 7 | <#@output extension=".designed.cs" encoding="UTF-8"#> 8 | <#@ assembly name="System.Core" #> 9 | <#@ assembly name="System.Runtime" #> 10 | <#@ assembly name="EnvDTE" #> 11 | <#@ assembly name="VSLangProj" #> 12 | <#@ import namespace="EnvDTE" #> 13 | <#@ import namespace="System" #> 14 | <#@ import namespace="System.CodeDom.Compiler" #> 15 | <#@ import namespace="System.Diagnostics" #> 16 | <#@ import namespace="System.IO" #> 17 | <#@ import namespace="System.Text.RegularExpressions" #> 18 | <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> 19 | <# 20 | string toolPath = Host.ResolveAssemblyReference(@"Generator\bin\Release\net6.0\Generator.dll"); 21 | string filePath = Host.TemplateFile.Replace(".tt", ".cs"); 22 | try 23 | { 24 | using System.Diagnostics.Process myProcess = new(); 25 | myProcess.StartInfo.UseShellExecute = false; 26 | myProcess.StartInfo.RedirectStandardOutput = true; 27 | myProcess.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 28 | myProcess.StartInfo.FileName = "dotnet"; 29 | myProcess.StartInfo.Arguments = $"\"{Path.GetFullPath(toolPath)}\" \"{Path.GetFullPath(filePath)}\""; 30 | myProcess.StartInfo.CreateNoWindow = true; 31 | myProcess.Start(); 32 | string content = myProcess.StandardOutput.ReadToEnd(); 33 | myProcess.WaitForExit(); 34 | 35 | if (content.StartsWith(Path.GetFileName(filePath))) 36 | { 37 | // 包含异常 38 | string[] lines = content.Trim().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); 39 | Regex errorRegex = new Regex(@"^.+\((\d+),(\d+)\): (.+)$"); 40 | foreach(string line in lines) 41 | { 42 | Match match = errorRegex.Match(line); 43 | if (match.Success) { 44 | string lineNum = match.Groups[1].Captures[0].Value; 45 | string colNum = match.Groups[2].Captures[0].Value; 46 | string message = match.Groups[3].Captures[0].Value; 47 | Errors.Add(new CompilerError(filePath, int.Parse(lineNum), int.Parse(colNum), "", message)); 48 | } 49 | } 50 | Write("// 生成异常"); 51 | } else { 52 | Write(content); 53 | } 54 | } 55 | catch (Exception e) 56 | { 57 | Error($"执行 {toolPath} 失败:{e.Message}"); 58 | } 59 | #> 60 | 61 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/LexerRunnerContext.cs: -------------------------------------------------------------------------------- 1 | namespace TestCompilers.Lexers; 2 | 3 | internal class LexerRunnerContext 4 | { 5 | public object? Result { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer1.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于测试简单最短匹配的词法分析器。 7 | /// 8 | [LexerSymbol("ab+", Kind = TestKind.A, UseShortest = true)] 9 | internal partial class TestShortestLexer1 : LexerController 10 | { 11 | } 12 | 13 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer1.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer2.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于测试向前看最短匹配的词法分析器。 7 | /// 8 | [LexerSymbol("a.+/b", Kind = TestKind.A, UseShortest = true)] 9 | internal partial class TestShortestLexer2 : LexerController 10 | { 11 | } 12 | 13 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer2.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer3.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于测试可拒绝最短匹配的词法分析器。 7 | /// 8 | [LexerRejectable] 9 | internal partial class TestShortestLexer3 : LexerController 10 | { 11 | [LexerSymbol("ab+", Kind = TestKind.A, UseShortest = true)] 12 | public void TestAction() 13 | { 14 | if (Text.Length < 4) 15 | { 16 | Reject(); 17 | } 18 | else 19 | { 20 | Accept(); 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer3.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer4.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于测试向前看+可拒绝最短匹配的词法分析器。 7 | /// 8 | [LexerRejectable] 9 | internal partial class TestShortestLexer4 : LexerController 10 | { 11 | [LexerSymbol("a.+/b", Kind = TestKind.A, UseShortest = true)] 12 | public void TestAction() 13 | { 14 | if (Text.Length < 4) 15 | { 16 | Reject(); 17 | } 18 | else 19 | { 20 | Accept(); 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Shortest/TestShortestLexer4.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestCalcLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于单元测试的计算器控制器。 7 | /// 8 | [LexerSymbol("\\+", Kind = Calc.Add)] 9 | [LexerSymbol("\\-", Kind = Calc.Sub)] 10 | [LexerSymbol("\\*", Kind = Calc.Mul)] 11 | [LexerSymbol("\\/", Kind = Calc.Div)] 12 | [LexerSymbol("\\^", Kind = Calc.Pow)] 13 | [LexerSymbol("\\(", Kind = Calc.LBrace)] 14 | [LexerSymbol("\\)", Kind = Calc.RBrace)] 15 | [LexerSymbol("\\s")] 16 | internal partial class TestCalcLexer : LexerController 17 | { 18 | /// 19 | /// 数字的终结符定义。 20 | /// 21 | [LexerSymbol("[0-9]+", Kind = Calc.Id)] 22 | public void DigitAction() 23 | { 24 | Accept(double.Parse(Text.AsSpan())); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestCalcLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestCalcRunnerLexer.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Cyjb.Compilers.Lexers; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 用于单元测试的计算器控制器。 8 | /// 9 | [LexerSymbol("\\s")] 10 | internal partial class TestCalcRunnerLexer : LexerController 11 | { 12 | private readonly StringBuilder text = new(); 13 | 14 | 15 | /// 16 | /// 数字的终结符定义。 17 | /// 18 | [LexerSymbol("[0-9]+", Kind = Calc.Id)] 19 | public void DigitAction() 20 | { 21 | text.Append(Text.AsSpan()); 22 | } 23 | 24 | /// 25 | /// 操作符的动作。 26 | /// 27 | [LexerSymbol("\\+", Kind = Calc.Add)] 28 | [LexerSymbol("\\-", Kind = Calc.Sub)] 29 | [LexerSymbol("\\*", Kind = Calc.Mul)] 30 | [LexerSymbol("\\/", Kind = Calc.Div)] 31 | [LexerSymbol("\\^", Kind = Calc.Pow)] 32 | [LexerSymbol("\\(", Kind = Calc.LBrace)] 33 | [LexerSymbol("\\)", Kind = Calc.RBrace)] 34 | public void OperatorAction() 35 | { 36 | text.Append(Text.AsSpan()); 37 | } 38 | 39 | /// 40 | /// 文件结束的动作。 41 | /// 42 | [LexerSymbol("<>")] 43 | public void EofAction() 44 | { 45 | ((LexerRunnerContext)SharedContext!).Result = text.ToString(); 46 | } 47 | 48 | /// 49 | /// 已加载源读取器。 50 | /// 51 | protected override void SourceLoaded() 52 | { 53 | base.SourceLoaded(); 54 | text.Clear(); 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestCalcRunnerLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestEscapeStrLexer.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | using Cyjb.Compilers.Lexers; 4 | using Cyjb.Text; 5 | 6 | namespace TestCompilers.Lexers; 7 | 8 | /// 9 | /// 用于单元测试的转义字符串控制器。 10 | /// 11 | [LexerContext("str")] 12 | [LexerContext("vstr")] 13 | public partial class TestEscapeStrLexer : LexerController 14 | { 15 | private const string CtxStr = "str"; 16 | private const string CtxVstr = "vstr"; 17 | 18 | /// 19 | /// 当前起始索引。 20 | /// 21 | private int curStart; 22 | /// 23 | /// 处理后的文本。 24 | /// 25 | private readonly StringBuilder decodedText = new(); 26 | 27 | [LexerSymbol(@"\""")] 28 | public void BeginStrAction() 29 | { 30 | PushContext(CtxStr); 31 | curStart = Start; 32 | decodedText.Clear(); 33 | } 34 | 35 | [LexerSymbol(@"@\""")] 36 | public void BeginVstrAction() 37 | { 38 | PushContext(CtxVstr); 39 | curStart = Start; 40 | decodedText.Clear(); 41 | } 42 | 43 | [LexerSymbol(@"\""", Kind = Str.Str)] 44 | public void EndAction() 45 | { 46 | PopContext(); 47 | Start = curStart; 48 | Text = decodedText.ToString(); 49 | } 50 | 51 | [LexerSymbol(@"\\u[0-9]{4}")] 52 | [LexerSymbol(@"\\x[0-9]{2}")] 53 | public void HexEscapeAction() 54 | { 55 | decodedText.Append((char)int.Parse(Text.AsSpan(2), NumberStyles.HexNumber)); 56 | } 57 | 58 | [LexerSymbol(@"\\n")] 59 | public void EscapeLFAction() 60 | { 61 | decodedText.Append('\n'); 62 | } 63 | 64 | [LexerSymbol(@"\\\""")] 65 | public void EscapeQuoteAction() 66 | { 67 | decodedText.Append('\"'); 68 | } 69 | 70 | [LexerSymbol(@"\\r")] 71 | public void EscapeCRAction() 72 | { 73 | decodedText.Append('\r'); 74 | } 75 | 76 | [LexerSymbol(@"<*>.")] 77 | public void CopyAction() 78 | { 79 | StringBuilderUtil.Append(decodedText, Text); 80 | } 81 | 82 | [LexerSymbol(@"\""\""")] 83 | public void VstrQuoteAction() 84 | { 85 | decodedText.Append('"'); 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestEscapeStrLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestProductionLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | /// 6 | /// 用于单元测试的计算器控制器。 7 | /// 8 | [LexerSymbol(@"\d+|\w[\w\d]*", Kind = ProductionKind.Id)] 9 | [LexerSymbol(@"\(", Kind = ProductionKind.LBrace)] 10 | [LexerSymbol(@"\)", Kind = ProductionKind.RBrace)] 11 | [LexerSymbol(@"\+", Kind = ProductionKind.Plus)] 12 | [LexerSymbol(@"\*", Kind = ProductionKind.Star)] 13 | [LexerSymbol(@"\?", Kind = ProductionKind.Question)] 14 | [LexerSymbol(@"\s")] 15 | public partial class TestProductionLexer : LexerController 16 | { } 17 | 18 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestProductionLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestStrLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | 3 | namespace TestCompilers.Lexers; 4 | 5 | public enum Str { Str } 6 | 7 | /// 8 | /// 用于单元测试的字符串控制器。 9 | /// 10 | [LexerRegex("regular_char", @"[^""\\\n\r\u0085\u2028\u2029]|(\\.)")] 11 | [LexerRegex("regular_literal", @"\""{regular_char}*\""")] 12 | [LexerRegex("verbatim_char", @"[^""]|\""\""")] 13 | [LexerRegex("verbatim_literal", @"@\""{verbatim_char}*\""")] 14 | [LexerSymbol("{regular_literal}|{verbatim_literal}", Kind = Str.Str)] 15 | public partial class TestStrLexer : LexerController 16 | { 17 | } 18 | 19 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestStrLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestSymbolValueLexer.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 测试词法单元的值。 8 | /// 9 | public partial class TestSymbolValueLexer : LexerController 10 | { 11 | [LexerSymbol("[0-9]+", Kind = Calc.Id, Value = 101)] 12 | public void DigitAction() 13 | { 14 | Assert.AreEqual(101, Value); 15 | Accept((int)Value! + 10); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/TestSymbolValueLexer.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer1.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Cyjb.Compilers.Lexers; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 用于测试前面固定长度向前看的词法分析器。 8 | /// 9 | [LexerSymbol("ab/c", Kind = TestKind.A)] 10 | [LexerSymbol(".", RegexOptions.Singleline, Kind = TestKind.B)] 11 | internal partial class TestTrailingLexer1 : LexerController 12 | { 13 | } 14 | 15 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer1.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer2.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Cyjb.Compilers.Lexers; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 用于测试后面固定长度向前看的词法分析器。 8 | /// 9 | [LexerSymbol("a+/b", Kind = TestKind.A)] 10 | [LexerSymbol(".", RegexOptions.Singleline, Kind = TestKind.B)] 11 | internal partial class TestTrailingLexer2 : LexerController 12 | { 13 | } 14 | 15 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer2.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer3.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Cyjb.Compilers.Lexers; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 用于测试非固定长度向前看的词法分析器。 8 | /// 9 | [LexerSymbol("a+/b*c", Kind = TestKind.A)] 10 | [LexerSymbol(".", RegexOptions.Singleline, Kind = TestKind.B)] 11 | internal partial class TestTrailingLexer3 : LexerController 12 | { 13 | } 14 | 15 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexer3.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexerEOL.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Cyjb.Compilers.Lexers; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 用于测试向前看行尾的词法分析器。 8 | /// 9 | [LexerSymbol("a$", Kind = TestKind.A)] 10 | [LexerSymbol(".", RegexOptions.Singleline, Kind = TestKind.B)] 11 | internal partial class TestTrailingLexerEOL : LexerController 12 | { 13 | } 14 | 15 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/Trailing/TestTrailingLexerEOL.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="..\..\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/UnitTestLexer.cancel.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | using Cyjb.Text; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.Lexers; 6 | 7 | public partial class UnitTestLexer 8 | { 9 | /// 10 | /// 对取消词法分析进行测试。 11 | /// 12 | [TestMethod] 13 | public void TestCancel() 14 | { 15 | LexerTokenizer tokenizer = TestCalcLexer.Factory.CreateTokenizer(); 16 | tokenizer.Load("1 + 20 * 3 / 4*(5+6)"); 17 | 18 | Assert.AreEqual(ParseStatus.Ready, tokenizer.Status); 19 | Assert.AreEqual(new Token(Calc.Id, "1", new TextSpan(0, 1), 1), tokenizer.Read()); 20 | Assert.AreEqual(new Token(Calc.Add, "+", new TextSpan(2, 3)), tokenizer.Read()); 21 | Assert.AreEqual(new Token(Calc.Id, "20", new TextSpan(4, 6), 20), tokenizer.Read()); 22 | tokenizer.Cancel(); 23 | Assert.AreEqual(ParseStatus.Cancelled, tokenizer.Status); 24 | Assert.AreEqual(Token.GetEndOfFile(6), tokenizer.Read()); 25 | Assert.AreEqual(Token.GetEndOfFile(6), tokenizer.Read()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/UnitTestLexer.merge.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cyjb.Compilers.Lexers; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.Lexers; 6 | 7 | public partial class UnitTestLexer 8 | { 9 | /// 10 | /// 测试合并终结符。 11 | /// 12 | [TestMethod] 13 | public void TestMerge() 14 | { 15 | // 简单匹配。 16 | Lexer lexer = new(); 17 | lexer.DefineSymbol(@"a").Kind(TestKind.A); 18 | lexer.DefineSymbol(@"b").Kind(TestKind.A); 19 | lexer.DefineSymbol(@"c").Kind(TestKind.A); 20 | LexerData data = lexer.GetData(); 21 | 22 | // 终结符会被合并成一个。 23 | Assert.AreEqual(1, data.Terminals.Length); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/UnitTestLexer.token.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | using Cyjb.Text; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.Lexers; 6 | 7 | public partial class UnitTestLexer 8 | { 9 | /// 10 | /// 测试文本是可变的。 11 | /// 12 | [TestMethod] 13 | public void TestMutableText() 14 | { 15 | Lexer lexer = new(); 16 | lexer.DefineSymbol(@"a").Kind(TestKind.A).Action(c => 17 | { 18 | // 主动修改的 Text,不会跟着 Source.Index 发生变更。 19 | c.Text = "XXX"; 20 | c.Source.Index++; 21 | Assert.AreEqual("XXX", c.Text); 22 | c.Accept(); 23 | }); 24 | lexer.DefineSymbol(@"b").Kind(TestKind.B).Action(c => 25 | { 26 | // 修改 Source.Index 时,Text 会跟着发生变更。 27 | Assert.AreEqual("b", c.Text); 28 | c.Source.Index++; 29 | Assert.AreEqual("bc", c.Text); 30 | c.Accept(); 31 | }); 32 | var factory = lexer.GetFactory(); 33 | 34 | var tokenizer = factory.CreateTokenizer(); 35 | tokenizer.Load("axbcd"); 36 | Assert.AreEqual(new Token(TestKind.A, "XXX", new TextSpan(0, 2)), tokenizer.Read()); 37 | Assert.AreEqual(new Token(TestKind.B, "bc", new TextSpan(2, 4)), tokenizer.Read()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/UnitTestLexerRunner.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Lexers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TestCompilers.Lexers; 5 | 6 | /// 7 | /// 类的单元测试。 8 | /// 9 | [TestClass] 10 | public partial class UnitTestLexerRunner 11 | { 12 | /// 13 | /// 对计算器词法分析进行测试。 14 | /// 15 | [TestMethod] 16 | public void TestDefineCalc() 17 | { 18 | LexerRunner runner = TestCalcRunnerLexer.Factory.CreateRunner(); 19 | LexerRunnerContext context = new(); 20 | runner.SharedContext = context; 21 | 22 | runner.Parse("1 + 20 * 3 / 4*(5+6)"); 23 | Assert.AreEqual("1+20*3/4*(5+6)", context.Result); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TestCompilers/Lexers/UnitTestTemplateDiagnostics.template.cs: -------------------------------------------------------------------------------- 1 | [LexerSymbol(10, 20, 30, 40, 50)] 2 | [LexerSymbol(Regex: "f")] 3 | [LexerSymbol("F", regex : "f")] 4 | [LexerSymbol(options: RegexOptions.None, "f")] 5 | [LexerSymbol("f", Test = 10)] 6 | [LexerSymbol("F", Kind = "f", Kind = "F2")] 7 | [LexerSymbol] 8 | [LexerSymbol(10)] 9 | [LexerSymbol("T", RegexOptions.Test)] 10 | [LexerSymbol("T", 10.3)] 11 | [LexerSymbol("T", "abc")] 12 | [LexerContext("")] 13 | [LexerSymbol(" 15 | { 16 | [LexerSymbol("T")] 17 | public void InvalidAction(int a) { } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/TestCalcParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cyjb; 3 | using Cyjb.Compilers.Parsers; 4 | 5 | namespace TestCompilers.Parsers; 6 | 7 | /// 8 | /// 用于单元测试的计算器控制器。 9 | /// 10 | [ParserLeftAssociate(Calc.Add, Calc.Sub)] 11 | [ParserLeftAssociate(Calc.Mul, Calc.Div)] 12 | [ParserRightAssociate(Calc.Pow)] 13 | [ParserNonAssociate(Calc.Id)] 14 | public partial class TestCalcParser : ParserController 15 | { 16 | [ParserProduction(Calc.E, Calc.Id)] 17 | private object? IdAction() 18 | { 19 | return this[0].Value; 20 | } 21 | 22 | [ParserProduction(Calc.E, Calc.E, Calc.Add, Calc.E)] 23 | [ParserProduction(Calc.E, Calc.E, Calc.Sub, Calc.E)] 24 | [ParserProduction(Calc.E, Calc.E, Calc.Mul, Calc.E)] 25 | [ParserProduction(Calc.E, Calc.E, Calc.Div, Calc.E)] 26 | [ParserProduction(Calc.E, Calc.E, Calc.Pow, Calc.E)] 27 | private object? BinaryAction() 28 | { 29 | double left = (double)this[0].Value!; 30 | double right = (double)this[2].Value!; 31 | return this[1].Kind switch 32 | { 33 | Calc.Add => left + right, 34 | Calc.Sub => left - right, 35 | Calc.Mul => left * right, 36 | Calc.Div => left / right, 37 | Calc.Pow => Math.Pow(left, right), 38 | _ => throw CommonExceptions.Unreachable(), 39 | }; 40 | } 41 | 42 | [ParserProduction(Calc.E, Calc.LBrace, Calc.E, Calc.RBrace)] 43 | private object? BraceAction() 44 | { 45 | return this[1].Value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/TestCalcParser.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="$(PkgCyjb_Compilers_Design)\content\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/TestProductionParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq; 3 | using Cyjb.Compilers.Parsers; 4 | 5 | namespace TestCompilers.Parsers; 6 | 7 | /// 8 | /// 用于单元测试的产生式语法分析。 9 | /// 10 | internal partial class TestProductionParser : ParserController 11 | { 12 | [ParserProduction(ProductionKind.AltExp, ProductionKind.AltExp, ProductionKind.Or, ProductionKind.Exp)] 13 | private object? OrAction() 14 | { 15 | return $"({this[0].Value})|({this[2].Value})"; 16 | } 17 | 18 | [ParserProduction(ProductionKind.Exp, ProductionKind.Repeat, SymbolOptions.OneOrMore)] 19 | private object? ExpressionAction() 20 | { 21 | return string.Join(" ", ((IList)this[0].Value!).Cast()); 22 | } 23 | 24 | [ParserProduction(ProductionKind.Item, ProductionKind.Id)] 25 | private object? CopyTextAction() 26 | { 27 | return this[0].Text; 28 | } 29 | 30 | [ParserProduction(ProductionKind.AltExp, ProductionKind.Exp)] 31 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item)] 32 | private object? CopyValueAction() 33 | { 34 | return this[0].Value; 35 | } 36 | 37 | [ParserProduction(ProductionKind.Item, ProductionKind.LBrace, ProductionKind.AltExp, ProductionKind.RBrace)] 38 | private object? BraceAction() 39 | { 40 | return $"({this[1].Value})"; 41 | } 42 | 43 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Plus)] 44 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Star)] 45 | [ParserProduction(ProductionKind.Repeat, ProductionKind.Item, ProductionKind.Question)] 46 | private object? RepeatAction() 47 | { 48 | return $"{this[0].Value}{this[1].Text}"; 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/TestProductionParser.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="$(PkgCyjb_Compilers_Design)\content\CompilerTemplate.t4" #> 2 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/UnitTestParser.cancel.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Parsers; 2 | using Cyjb.Text; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.Parsers; 6 | 7 | public partial class UnitTestParser 8 | { 9 | /// 10 | /// 对取消语法分析进行测试。 11 | /// 12 | [TestMethod] 13 | public void TestCancel() 14 | { 15 | ITokenizer tokens = new EnumerableTokenizer(new Token[] 16 | { 17 | new(TestKind.Te, "e"), 18 | new(TestKind.Te, "e"), 19 | new(TestKind.Tc, "c"), 20 | new(TestKind.Td, "d"), 21 | new(TestKind.Ta, "a"), 22 | new(TestKind.Tb, "b"), 23 | }); 24 | 25 | Parser parser = new(); 26 | parser.DefineProduction(TestKind.A, TestKind.B, TestKind.Ta, TestKind.Tb) 27 | .Action(c => $"A[{c[0].Value}{c[1].Text}{c[2].Text}]"); 28 | parser.DefineProduction(TestKind.B, TestKind.C, TestKind.Tc, TestKind.Td) 29 | .Action(c => $"B[{c[0].Value}{c[1].Text}{c[2].Text}]"); 30 | parser.DefineProduction(TestKind.C, TestKind.Te) 31 | .Action(c => $"C[{c[0].Text}]"); 32 | IParserFactory factory = parser.GetFactory(); 33 | 34 | LRParser tokenParser = factory.CreateParser(); 35 | tokenParser.Load(tokens); 36 | tokenParser.ParseError += (parser, error) => 37 | { 38 | parser.Cancel(); 39 | }; 40 | Assert.AreEqual(ParseStatus.Ready, tokenParser.Status); 41 | Assert.AreEqual("A[B[C[e]]]", tokenParser.Parse().Value); 42 | Assert.AreEqual(ParseStatus.Cancelled, tokenParser.Status); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /TestCompilers/Parsers/UnitTestParser.start.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Compilers.Parsers; 2 | using Cyjb.Text; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.Parsers; 6 | 7 | public partial class UnitTestParser 8 | { 9 | /// 10 | /// 对语法分析多起始符号进行测试。 11 | /// 12 | [TestMethod] 13 | public void TestStart() 14 | { 15 | ITokenizer tokens = new EnumerableTokenizer(new Token[] 16 | { 17 | new(TestKind.Te, "e"), 18 | new(TestKind.Tc, "c"), 19 | new(TestKind.Td, "d"), 20 | new(TestKind.Ta, "a"), 21 | new(TestKind.Tb, "b"), 22 | new(TestKind.Te, "e"), 23 | new(TestKind.Te, "e"), 24 | new(TestKind.Tc, "c"), 25 | new(TestKind.Td, "d"), 26 | }); 27 | 28 | Parser parser = new(); 29 | parser.DefineProduction(TestKind.A, TestKind.B, TestKind.Ta, TestKind.Tb) 30 | .Action(c => $"A[{c[0].Value}{c[1].Text}{c[2].Text}]"); 31 | parser.DefineProduction(TestKind.B, TestKind.C, TestKind.Tc, TestKind.Td) 32 | .Action(c => $"B[{c[0].Value}{c[1].Text}{c[2].Text}]"); 33 | parser.DefineProduction(TestKind.C, TestKind.Te) 34 | .Action(c => $"C[{c[0].Text}]"); 35 | parser.AddStart(TestKind.A, ParseOptions.ScanToMatch); 36 | parser.AddStart(TestKind.B, ParseOptions.ScanToMatch); 37 | parser.AddStart(TestKind.C, ParseOptions.ScanToMatch); 38 | IParserFactory factory = parser.GetFactory(); 39 | 40 | var tokenParser = factory.CreateParser(); 41 | tokenParser.Load(tokens); 42 | tokenParser.ParseError += (parser, error) => 43 | { 44 | Assert.Fail("不应产生分析错误"); 45 | }; 46 | Assert.AreEqual(ParseStatus.Ready, tokenParser.Status); 47 | Assert.AreEqual("A[B[C[e]cd]ab]", tokenParser.Parse().Value); 48 | Assert.AreEqual(ParseStatus.Ready, tokenParser.Status); 49 | Assert.AreEqual("C[e]", tokenParser.Parse(TestKind.C).Value); 50 | Assert.AreEqual(ParseStatus.Ready, tokenParser.Status); 51 | Assert.AreEqual("B[C[e]cd]", tokenParser.Parse(TestKind.B).Value); 52 | Assert.AreEqual(ParseStatus.Finished, tokenParser.Status); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TestCompilers/ProductionKind.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Text; 2 | 3 | namespace TestCompilers; 4 | 5 | /// 6 | /// 产生式的类型。 7 | /// 8 | public enum ProductionKind 9 | { 10 | /// 11 | /// 词法单元类型。 12 | /// 13 | Id, 14 | /// 15 | /// 左小括号。 16 | /// 17 | [TokenDisplayName("(")] 18 | LBrace, 19 | /// 20 | /// 右小括号。 21 | /// 22 | [TokenDisplayName(")")] 23 | RBrace, 24 | /// 25 | /// 星号。 26 | /// 27 | [TokenDisplayName("*")] 28 | Star, 29 | /// 30 | /// 加号。 31 | /// 32 | [TokenDisplayName("+")] 33 | Plus, 34 | /// 35 | /// 问号。 36 | /// 37 | [TokenDisplayName("?")] 38 | Question, 39 | /// 40 | /// 或符号。 41 | /// 42 | [TokenDisplayName("|")] 43 | Or, 44 | /// 45 | /// 或产生式。 46 | /// 47 | AltExp, 48 | /// 49 | /// 产生式。 50 | /// 51 | Exp, 52 | /// 53 | /// 重复产生式项。 54 | /// 55 | Repeat, 56 | /// 57 | /// 产生式项。 58 | /// 59 | Item, 60 | } 61 | -------------------------------------------------------------------------------- /TestCompilers/RegularExpressions/UnitTestLexRegex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Cyjb.Compilers.RegularExpressions; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace TestCompilers.RegularExpressions; 6 | 7 | /// 8 | /// 类的单元测试。 9 | /// 10 | [TestClass] 11 | public class UnitTestLexRegex 12 | { 13 | /// 14 | /// 对 方法进行测试。 15 | /// 16 | [DataTestMethod] 17 | [DataRow("abc", RegexOptions.None, "\"abc\"")] 18 | [DataRow("a|b|c", RegexOptions.None, "\"a\"|\"b\"|\"c\"")] 19 | [DataRow("(a|b)cd", RegexOptions.None, "(\"a\"|\"b\")\"cd\"")] 20 | [DataRow("^abc", RegexOptions.None, "^\"abc\"")] 21 | [DataRow("abc+", RegexOptions.None, "\"ab\"\"c\"+")] 22 | [DataRow("abc*d{3}e{4,}f{5,6}a", RegexOptions.None, "\"ab\"\"c\"*\"d\"{3}\"e\"{4,}\"f\"{5,6}\"a\"")] 23 | [DataRow("(a|b)+[cde]*f/ghi", RegexOptions.None, "(\"a\"|\"b\")+[c-e]*\"f\"/\"ghi\"")] 24 | [DataRow("abc((d|e)f(g|h(ij)))k", RegexOptions.None, "\"abc\"(\"d\"|\"e\")\"f\"(\"g\"|\"h\"\"ij\")\"k\"")] 25 | [DataRow("abc", RegexOptions.IgnoreCase, "(?i:abc)")] 26 | [DataRow("abc(?i:def)ghi", RegexOptions.None, "\"abc\"(?i:def)\"ghi\"")] 27 | [DataRow("abc(?i:de(?-i:f))ghi", RegexOptions.None, "\"abc\"(?i:de)\"f\"\"ghi\"")] 28 | [DataRow("a.+b", RegexOptions.None, "\"a\"[^\\n\\r]+\"b\"")] 29 | [DataRow("a.+b", RegexOptions.Singleline, "\"a\"[\\0-\\uFFFF]+\"b\"")] 30 | [DataRow("ab(?# comment)c", RegexOptions.None, "\"ab\"\"c\"")] 31 | [DataRow("a b (?# comment) c", RegexOptions.IgnorePatternWhitespace, "\"a\"\"b\"\"c\"")] 32 | [DataRow(@"(\*\s*){0,3}", RegexOptions.None, "(\"*\"[\\t-\\r\\u0085\\p{Zs}\\p{Zl}\\p{Zp}]*){0,3}")] 33 | public void TestParse(string pattern, RegexOptions option, string expected) 34 | { 35 | Assert.AreEqual(expected, LexRegex.Parse(pattern, option).ToString()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TestCompilers/TestKind.cs: -------------------------------------------------------------------------------- 1 | namespace TestCompilers; 2 | 3 | internal enum TestKind 4 | { 5 | A, B, C, D, E, F 6 | } 7 | -------------------------------------------------------------------------------- /TestCompilers/Text/UnitTestToken`1.cs: -------------------------------------------------------------------------------- 1 | using Cyjb.Text; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TestCompilers.Text 5 | { 6 | /// 7 | /// 类的单元测试。 8 | /// 9 | [TestClass] 10 | public class UnitTestToken 11 | { 12 | /// 13 | /// 对 的构造函数进行测试。 14 | /// 15 | [TestMethod] 16 | public void TestToken() 17 | { 18 | Token token = new(10, "test", new TextSpan(10, 30)); 19 | Assert.AreEqual(10, token.Kind); 20 | Assert.AreEqual("test", token.Text); 21 | Assert.AreEqual(new TextSpan(10, 30), token.Span); 22 | Assert.IsNull(token.Value); 23 | 24 | token = new(11, "test2", new TextSpan(20, 40), "test2"); 25 | Assert.AreEqual(11, token.Kind); 26 | Assert.AreEqual("test2", token.Text); 27 | Assert.AreEqual(new TextSpan(20, 40), token.Span); 28 | Assert.AreEqual("test2", token.Value); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /generate-code-coverage-report.ps1: -------------------------------------------------------------------------------- 1 | dotnet test --no-build 2 | dotnet reportgenerator -reports:.\TestCompilers\coverage.cobertura.xml -targetdir:.\CodeCoverage 3 | --------------------------------------------------------------------------------