From 99fdd6438b1d27e7e83d2ec25ba7c681ee263740 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 13 Mar 2024 23:58:06 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E8=BF=99=E5=B0=B1=E6=98=AF=E5=AE=8C?= =?UTF-8?q?=E5=85=A8=E7=9A=84Pascal=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5!8!4!个状态堂堂登场! Co-authored-by: Ichirinko <1621543655@qq.com> Reviewed-on: https://git.rrricardo.top/PostGuard/Canon/pulls/11 --- Canon.Core/Enums/GrammarEnums.cs | 8 +- Canon.Core/Enums/SemanticEnums.cs | 22 +- .../PascalGrammarConstPart.cs | 413 ++++++++++++ .../PascalGrammarTests.Grammar.cs | 595 ++++++++++++++++++ .../GrammarParserTests/PascalGrammarTests.cs | 27 + .../SimpleGrammarWithEmptyTests.cs | 1 - .../LexicalParserTests/OperatorTypeTests.cs | 67 +- 7 files changed, 1088 insertions(+), 45 deletions(-) create mode 100644 Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs create mode 100644 Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs create mode 100644 Canon.Tests/GrammarParserTests/PascalGrammarTests.cs diff --git a/Canon.Core/Enums/GrammarEnums.cs b/Canon.Core/Enums/GrammarEnums.cs index 78c4ab2..685a5a0 100644 --- a/Canon.Core/Enums/GrammarEnums.cs +++ b/Canon.Core/Enums/GrammarEnums.cs @@ -19,7 +19,7 @@ public enum NonTerminatorType VarDeclaration, Type, BasicType, - Range, + Period, Subprogram, SubprogramHead, SubprogramBody, @@ -37,5 +37,9 @@ public enum NonTerminatorType ExpressionList, SimpleExpression, Term, - Factor + Factor, + AddOperator, + MultiplyOperator, + RelationOperator, + IdVarPart } diff --git a/Canon.Core/Enums/SemanticEnums.cs b/Canon.Core/Enums/SemanticEnums.cs index 2eeda0d..a334e37 100644 --- a/Canon.Core/Enums/SemanticEnums.cs +++ b/Canon.Core/Enums/SemanticEnums.cs @@ -29,7 +29,15 @@ public enum DelimiterType LeftSquareBracket, RightSquareBracket, SingleQuotation, - DoubleQuotation + DoubleQuotation, + /// + /// 访问记录字段用的点 x.a + /// + Dot, + /// + /// 数组声明上下界之间的分隔符 1..50 + /// + DoubleDots } public enum KeywordType @@ -49,6 +57,15 @@ public enum KeywordType For, To, Do, + Integer, + Real, + Boolean, + Character, + Divide, + Not, + Mod, + And, + Or } public enum OperatorType @@ -63,9 +80,6 @@ public enum OperatorType Minus, Multiply, Divide, - Mod, - And, - Or, Assign } diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs b/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs new file mode 100644 index 0000000..8e1a920 --- /dev/null +++ b/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs @@ -0,0 +1,413 @@ +using Canon.Core.Abstractions; +using Canon.Core.Enums; +using Canon.Core.GrammarParser; +using Canon.Core.LexicalParser; + +namespace Canon.Tests.GrammarParserTests; + +public class PascalGrammarConstPart +{ + private static readonly Dictionary>> s_pascalGrammar = new() + { + { + // ProgramStart -> ProgramBody + new NonTerminator(NonTerminatorType.StartNonTerminator), [ + [new NonTerminator(NonTerminatorType.ProgramBody)] + ] + }, + { + // ProgramBody -> ConstDeclarations + new NonTerminator(NonTerminatorType.ProgramBody), [ + [ + new NonTerminator(NonTerminatorType.ConstDeclarations), + // new NonTerminator(NonTerminatorType.VarDeclarations), + // new NonTerminator(NonTerminatorType.SubprogramDeclarations), + // new NonTerminator(NonTerminatorType.CompoundStatement) + ] + ] + }, + { + // ConstDeclarations -> ε | const ConstDeclaration ; + new NonTerminator(NonTerminatorType.ConstDeclarations), [ + [ + Terminator.EmptyTerminator, + ], + [ + new Terminator(KeywordType.Const), + new NonTerminator(NonTerminatorType.ConstDeclaration), + new Terminator(DelimiterType.Semicolon) + ] + ] + }, + { + // ConstDeclaration -> id = ConstValue | ConstDeclaration ; id = ConstValue + new NonTerminator(NonTerminatorType.ConstDeclaration), [ + [ + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Equal), + new NonTerminator(NonTerminatorType.ConstValue) + ], + [ + new NonTerminator(NonTerminatorType.ConstDeclaration), + new Terminator(DelimiterType.Semicolon), + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Equal), + new NonTerminator(NonTerminatorType.ConstValue) + ] + ] + }, + { + // ConstValue -> +num | -num | num | 'letter' + new NonTerminator(NonTerminatorType.ConstValue), [ + [ + new Terminator(OperatorType.Plus), Terminator.NumberTerminator + ], + [ + new Terminator(OperatorType.Minus), Terminator.NumberTerminator, + ], + [ + Terminator.NumberTerminator, + ], + [ + new Terminator(DelimiterType.SingleQuotation), + Terminator.CharacterTerminator, + new Terminator(DelimiterType.SingleQuotation), + ] + ] + } + }; + + [Fact] + public void AstTestFirst() + { + GrammarBuilder builder = new() + { + Generators = s_pascalGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator) + }; + + GrammarParserBase grammar = builder.Build().ToGrammarParser(); + + // const a = +5; + List tokens = + [ + new KeywordSemanticToken + { + CharacterPos = 0, KeywordType = KeywordType.Const, LinePos = 0, LiteralValue = "const" + }, + new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "a" }, + new OperatorSemanticToken + { + LinePos = 0, CharacterPos = 0, LiteralValue = "=", OperatorType = OperatorType.Equal + }, + new OperatorSemanticToken + { + LinePos = 0, CharacterPos = 0, LiteralValue = "+", OperatorType = OperatorType.Plus + }, + new NumberSemanticToken + { + CharacterPos = 0, LinePos = 0, LiteralValue = "5", NumberType = NumberType.Integer + }, + new DelimiterSemanticToken + { + CharacterPos = 0, DelimiterType = DelimiterType.Semicolon, LinePos = 0, LiteralValue = ";" + }, + SemanticToken.End + ]; + + + // ProgramBody + SyntaxNode root = grammar.Analyse(tokens); + Assert.Equal(NonTerminatorType.ProgramBody, root.GetNonTerminatorType()); + Assert.Single(root.Children); + Assert.Equal(10, root.Count()); + + // ConstDeclarations + root = root.Children[0]; + Assert.Equal(NonTerminatorType.ConstDeclarations, root.GetNonTerminatorType()); + Assert.Equal(3, root.Children.Count); + + Assert.Contains(root.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Keyword) + { + KeywordSemanticToken token = (KeywordSemanticToken)node.GetSemanticToken(); + + return token.KeywordType == KeywordType.Const; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Delimiter) + { + DelimiterSemanticToken token = (DelimiterSemanticToken)node.GetSemanticToken(); + + return token.DelimiterType == DelimiterType.Semicolon; + } + + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration) + { + return true; + } + + return false; + }); + + // ConstDeclaration + root = root.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration + ).ElementAt(0); + + Assert.Equal(NonTerminatorType.ConstDeclaration, root.GetNonTerminatorType()); + Assert.Equal(3, root.Children.Count); + Assert.Contains(root.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Identifier) + { + return true; + } + + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstValue) + { + return true; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Operator) + { + OperatorSemanticToken token = (OperatorSemanticToken)node.GetSemanticToken(); + + return token.OperatorType == OperatorType.Equal; + } + + return false; + }); + + // ConstValue + root = root.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstValue + ).ElementAt(0); + Assert.Equal(NonTerminatorType.ConstValue, root.GetNonTerminatorType()); + Assert.Equal(2, root.Children.Count); + Assert.Contains(root.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Operator) + { + OperatorSemanticToken token = (OperatorSemanticToken)node.GetSemanticToken(); + + return token.OperatorType == OperatorType.Plus; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Number) + { + return true; + } + + return false; + }); + } + + [Fact] + public void AstTestSecond() + { + GrammarBuilder builder = new() + { + Generators = s_pascalGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator) + }; + + GrammarParserBase grammar = builder.Build().ToGrammarParser(); + + // const a = 5; McCafe = 'Under Pressure (Queen & D. Bowie)' ; + List tokens = + [ + new KeywordSemanticToken + { + CharacterPos = 0, KeywordType = KeywordType.Const, LinePos = 0, LiteralValue = "const" + }, + new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "a" }, + new OperatorSemanticToken + { + LinePos = 0, CharacterPos = 0, LiteralValue = "=", OperatorType = OperatorType.Equal + }, + new NumberSemanticToken + { + CharacterPos = 0, LinePos = 0, LiteralValue = "5", NumberType = NumberType.Integer + }, + new DelimiterSemanticToken + { + CharacterPos = 0, DelimiterType = DelimiterType.Semicolon, LinePos = 0, LiteralValue = ";" + }, + new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "McCafe" }, + new OperatorSemanticToken + { + LinePos = 0, CharacterPos = 0, LiteralValue = "=", OperatorType = OperatorType.Equal + }, + new DelimiterSemanticToken + { + CharacterPos = 0, DelimiterType = DelimiterType.SingleQuotation, LinePos = 0, LiteralValue = "'" + }, + new CharacterSemanticToken + { + CharacterPos = 0, LinePos = 0, LiteralValue = "Under Pressure (Queen & D. Bowie)" + }, + new DelimiterSemanticToken + { + CharacterPos = 0, DelimiterType = DelimiterType.SingleQuotation, LinePos = 0, LiteralValue = "'" + }, + new DelimiterSemanticToken + { + CharacterPos = 0, DelimiterType = DelimiterType.Semicolon, LinePos = 0, LiteralValue = ";" + }, + SemanticToken.End + ]; + + // 分析树见文档 + + // ProgramBody + SyntaxNode root = grammar.Analyse(tokens); + Assert.Equal(NonTerminatorType.ProgramBody, root.GetNonTerminatorType()); + Assert.Single(root.Children); + Assert.Equal(17, root.Count()); + + // ConstDeclarations + root = root.Children[0]; + Assert.Equal(NonTerminatorType.ConstDeclarations, root.GetNonTerminatorType()); + Assert.Equal(3, root.Children.Count); + + Assert.Contains(root.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Keyword) + { + KeywordSemanticToken token = (KeywordSemanticToken)node.GetSemanticToken(); + + return token.KeywordType == KeywordType.Const; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Delimiter) + { + DelimiterSemanticToken token = (DelimiterSemanticToken)node.GetSemanticToken(); + + return token.DelimiterType == DelimiterType.Semicolon; + } + + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration) + { + return true; + } + + return false; + }); + + // ConstDeclaration layer3 + root = root.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration + ).ElementAt(0); + + Assert.Equal(NonTerminatorType.ConstDeclaration, root.GetNonTerminatorType()); + Assert.Equal(5, root.Children.Count); + Assert.Contains(root.Children, node => + { + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration) + { + return true; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Delimiter) + { + DelimiterSemanticToken token = (DelimiterSemanticToken)node.GetSemanticToken(); + + return token.DelimiterType == DelimiterType.Semicolon; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Identifier) + { + return true; + } + + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstValue) + { + return true; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Operator) + { + OperatorSemanticToken token = (OperatorSemanticToken)node.GetSemanticToken(); + + return token.OperatorType == OperatorType.Equal; + } + + return false; + }); + + // ConstDeclaration layer4 + SyntaxNode constDeclarationLayer4 = root.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstDeclaration + ).ElementAt(0); + Assert.Equal(NonTerminatorType.ConstDeclaration, constDeclarationLayer4.GetNonTerminatorType()); + Assert.Equal(3, constDeclarationLayer4.Children.Count); + Assert.Contains(constDeclarationLayer4.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Identifier) + { + return true; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Operator) + { + OperatorSemanticToken token = (OperatorSemanticToken)node.GetSemanticToken(); + + return token.OperatorType == OperatorType.Equal; + } + + if (!node.IsTerminated && node.GetNonTerminatorType() == NonTerminatorType.ConstValue) + { + return true; + } + + return false; + }); + + + // ConstValue layer4 + SyntaxNode constValueLayer4 = root.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstValue + ).ElementAt(0); + Assert.Equal(NonTerminatorType.ConstValue, constValueLayer4.GetNonTerminatorType()); + Assert.Equal(3, constValueLayer4.Children.Count); + Assert.Contains(constValueLayer4.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Delimiter) + { + DelimiterSemanticToken token = (DelimiterSemanticToken)node.GetSemanticToken(); + + return token.DelimiterType == DelimiterType.SingleQuotation; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Character) + { + return true; + } + + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Delimiter) + { + DelimiterSemanticToken token = (DelimiterSemanticToken)node.GetSemanticToken(); + + return token.DelimiterType == DelimiterType.SingleQuotation; + } + + return false; + }); + + // ConstValue layer5 + SyntaxNode constValueLayer5 = constDeclarationLayer4.Children.Where(child => + !child.IsTerminated && child.GetNonTerminatorType() == NonTerminatorType.ConstValue + ).ElementAt(0); + Assert.Equal(NonTerminatorType.ConstValue, constValueLayer5.GetNonTerminatorType()); + Assert.Single(constValueLayer5.Children); + Assert.Contains(constValueLayer5.Children, node => + { + if (node.IsTerminated && node.GetSemanticToken().TokenType == SemanticTokenType.Number) + { + return true; + } + + return false; + }); + } +} diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs b/Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs new file mode 100644 index 0000000..054d6e5 --- /dev/null +++ b/Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs @@ -0,0 +1,595 @@ +using Canon.Core.Enums; +using Canon.Core.GrammarParser; + +namespace Canon.Tests.GrammarParserTests; + +public partial class PascalGrammarTests +{ + private static readonly Dictionary>> s_pascalGrammar = new() + { + { + // ProgramStart -> ProgramStruct + new NonTerminator(NonTerminatorType.StartNonTerminator), [ + [new NonTerminator(NonTerminatorType.ProgramStruct)] + ] + }, + { + // ProgramStruct -> ProgramHead ; ProgramBody . + new NonTerminator(NonTerminatorType.ProgramStruct), [ + [ + new NonTerminator(NonTerminatorType.ProgramHead), + new Terminator(DelimiterType.Semicolon), + new NonTerminator(NonTerminatorType.ProgramBody), + new Terminator(DelimiterType.Period) + ] + ] + }, + { + // ProgramHead -> program id (IdList) | program id + new NonTerminator(NonTerminatorType.ProgramHead), [ + [ + new Terminator(KeywordType.Program), + Terminator.IdentifierTerminator, + new Terminator(DelimiterType.LeftParenthesis), + new NonTerminator(NonTerminatorType.IdentifierList), + new Terminator(DelimiterType.RightParenthesis), + ], + [ + new Terminator(KeywordType.Program), + Terminator.IdentifierTerminator, + ] + ] + }, + { + // ProgramBody -> ConstDeclarations + // VarDeclarations + // SubprogramDeclarations + // CompoundStatement + new NonTerminator(NonTerminatorType.ProgramBody), [ + [ + new NonTerminator(NonTerminatorType.ConstDeclarations), + new NonTerminator(NonTerminatorType.VarDeclarations), + new NonTerminator(NonTerminatorType.SubprogramDeclarations), + new NonTerminator(NonTerminatorType.CompoundStatement) + ] + ] + }, + { + // IdList -> id | IdList , id + new NonTerminator(NonTerminatorType.IdentifierList), [ + [ + Terminator.IdentifierTerminator, + ], + [ + new NonTerminator(NonTerminatorType.IdentifierList), + new Terminator(DelimiterType.Comma), + Terminator.IdentifierTerminator + ] + ] + }, + { + // ConstDeclarations -> ε | const ConstDeclaration ; + new NonTerminator(NonTerminatorType.ConstDeclarations), [ + [ + Terminator.EmptyTerminator, + ], + [ + new Terminator(KeywordType.Const), + new NonTerminator(NonTerminatorType.ConstDeclaration), + new Terminator(DelimiterType.Semicolon) + ] + ] + }, + { + // ConstDeclaration -> id = ConstValue | ConstDeclaration ; id = ConstValue + new NonTerminator(NonTerminatorType.ConstDeclaration), [ + [ + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Equal), + new NonTerminator(NonTerminatorType.ConstValue) + ], + [ + new NonTerminator(NonTerminatorType.ConstDeclaration), + new Terminator(DelimiterType.Semicolon), + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Equal), + new NonTerminator(NonTerminatorType.ConstValue) + ] + ] + }, + { + // ConstValue -> +num | -num | num | 'letter' + new NonTerminator(NonTerminatorType.ConstValue), [ + [ + new Terminator(OperatorType.Plus), Terminator.NumberTerminator + ], + [ + new Terminator(OperatorType.Minus), Terminator.NumberTerminator, + ], + [ + Terminator.NumberTerminator, + ], + [ + new Terminator(DelimiterType.SingleQuotation), + Terminator.CharacterTerminator, + new Terminator(DelimiterType.SingleQuotation), + ] + ] + }, + { + // VarDeclarations -> ε | var VarDeclaration ; + new NonTerminator(NonTerminatorType.VarDeclarations), [ + [ + Terminator.EmptyTerminator + ], + [ + new Terminator(KeywordType.Var), + new NonTerminator(NonTerminatorType.VarDeclaration), + new Terminator(DelimiterType.Semicolon) + ] + ] + }, + { + // VarDeclaration -> IdList : Type | VarDeclaration ; IdList : Type + new NonTerminator(NonTerminatorType.VarDeclaration), [ + [ + new NonTerminator(NonTerminatorType.IdentifierList), + new Terminator(DelimiterType.Colon), + new NonTerminator(NonTerminatorType.Type) + ], + [ + new NonTerminator(NonTerminatorType.VarDeclaration), + new Terminator(DelimiterType.Semicolon), + new NonTerminator(NonTerminatorType.IdentifierList), + new Terminator(DelimiterType.Colon), + new NonTerminator(NonTerminatorType.Type) + ] + ] + }, + { + // Type -> BasicType | Array [ Period ] of BasicType + new NonTerminator(NonTerminatorType.Type), [ + [ + new NonTerminator(NonTerminatorType.BasicType) + ], + [ + new Terminator(KeywordType.Array), + new Terminator(DelimiterType.LeftSquareBracket), + new NonTerminator(NonTerminatorType.Period), + new Terminator(DelimiterType.RightSquareBracket), + new Terminator(KeywordType.Of), + new NonTerminator(NonTerminatorType.BasicType) + ] + ] + }, + { + // BasicType -> Integer | Real | Boolean | char + new NonTerminator(NonTerminatorType.BasicType), [ + [ + new Terminator(KeywordType.Integer) + ], + [ + new Terminator(KeywordType.Real) + ], + [ + new Terminator(KeywordType.Boolean) + ], + [ + new Terminator(KeywordType.Character) + ] + ] + }, + { + // Period -> digits .. digits | Period , digits .. digits + new NonTerminator(NonTerminatorType.Period), [ + [ + Terminator.NumberTerminator, + new Terminator(DelimiterType.DoubleDots), + Terminator.NumberTerminator, + ], + [ + new NonTerminator(NonTerminatorType.Period), + new Terminator(DelimiterType.Comma), + Terminator.NumberTerminator, + new Terminator(DelimiterType.DoubleDots), + Terminator.NumberTerminator, + ] + ] + }, + { + // SubprogramDeclarations -> ε | SubprogramDeclarations Subprogram ; + new NonTerminator(NonTerminatorType.SubprogramDeclarations), [ + [ + Terminator.EmptyTerminator + ], + [ + new NonTerminator(NonTerminatorType.SubprogramDeclarations), + new NonTerminator(NonTerminatorType.Subprogram), + new Terminator(DelimiterType.Semicolon) + ] + ] + }, + { + // Subprogram -> SubprogramHead ; SubprogramBody + new NonTerminator(NonTerminatorType.Subprogram), [ + [ + new NonTerminator(NonTerminatorType.SubprogramHead), + new Terminator(DelimiterType.Semicolon), + new NonTerminator(NonTerminatorType.SubprogramBody) + ] + ] + }, + { + // SubprogramHead -> procedure id FormalParameter + // | function id FormalParameter : BasicType + new NonTerminator(NonTerminatorType.SubprogramHead), [ + [ + new Terminator(KeywordType.Procedure), + Terminator.IdentifierTerminator, + new NonTerminator(NonTerminatorType.FormalParameter) + ], + [ + new Terminator(KeywordType.Function), + Terminator.IdentifierTerminator, + new NonTerminator(NonTerminatorType.FormalParameter), + new Terminator(DelimiterType.Colon), + new NonTerminator(NonTerminatorType.BasicType) + ] + ] + }, + { + // FormalParameter -> ε | ( ParameterList ) + new NonTerminator(NonTerminatorType.FormalParameter), [ + [ + Terminator.EmptyTerminator, + ], + [ + new Terminator(DelimiterType.LeftParenthesis), + new NonTerminator(NonTerminatorType.ParameterList), + new Terminator(DelimiterType.RightParenthesis) + ] + ] + }, + { + // ParameterList -> Parameter | ParameterList ; Parameter + new NonTerminator(NonTerminatorType.ParameterList), [ + [ + new NonTerminator(NonTerminatorType.Parameter) + ], + [ + new NonTerminator(NonTerminatorType.ParameterList), + new Terminator(DelimiterType.Semicolon), + new NonTerminator(NonTerminatorType.Parameter) + ] + ] + }, + { + // Parameter -> VarParameter | ValueParameter + new NonTerminator(NonTerminatorType.Parameter), [ + [ + new NonTerminator(NonTerminatorType.VarParameter) + ], + [ + new NonTerminator(NonTerminatorType.ValueParameter) + ] + ] + }, + { + // VarParameter -> var ValueParameter + new NonTerminator(NonTerminatorType.VarParameter), [ + [ + new Terminator(KeywordType.Var), + new NonTerminator(NonTerminatorType.ValueParameter) + ] + ] + }, + { + // ValueParameter -> IdList : BasicType + new NonTerminator(NonTerminatorType.ValueParameter), [ + [ + new NonTerminator(NonTerminatorType.IdentifierList), + new Terminator(DelimiterType.Colon), + new NonTerminator(NonTerminatorType.BasicType) + ] + ] + }, + { + // SubprogramBody -> ConstDeclarations + // VarDeclarations + // CompoundStatement + new NonTerminator(NonTerminatorType.SubprogramBody), [ + [ + new NonTerminator(NonTerminatorType.ConstDeclarations), + new NonTerminator(NonTerminatorType.VarDeclarations), + new NonTerminator(NonTerminatorType.CompoundStatement) + ] + ] + }, + { + // CompoundStatement -> begin StatementList end + new NonTerminator(NonTerminatorType.CompoundStatement), [ + [ + new Terminator(KeywordType.Begin), + new NonTerminator(NonTerminatorType.StatementList), + new Terminator(KeywordType.End) + ] + ] + }, + { + // StatementList -> Statement | StatementList ; Statement + new NonTerminator(NonTerminatorType.StatementList), [ + [ + new NonTerminator(NonTerminatorType.Statement) + ], + [ + new NonTerminator(NonTerminatorType.StatementList), + new Terminator(DelimiterType.Semicolon), + new NonTerminator(NonTerminatorType.Statement) + ] + ] + }, + { + // Statement -> ε + // | Variable AssignOp Expression + // | FuncId AssignOp Expression + // | ProcedureCall + // | CompoundStatement + // | if Expression then Statement ElsePart + // | for id AssignOp Expression to Expression do Statement + // | read ( VariableList ) + // | write( ExpressionList ) + // 注意这里 read 和 write 作为普通的函数调用处理了 + // 因此下面并没有单独声明 + new NonTerminator(NonTerminatorType.Statement), [ + [ + // ε + Terminator.EmptyTerminator, + ], + [ + // Variable AssignOp Expression + new NonTerminator(NonTerminatorType.Variable), + new Terminator(OperatorType.Assign), + new NonTerminator(NonTerminatorType.Expression) + ], + [ + // FuncId AssignOp Expression + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Assign), + new NonTerminator(NonTerminatorType.Expression) + ], + [ + // ProcedureCall + new NonTerminator(NonTerminatorType.ProcedureCall) + ], + [ + // CompoundStatement + new NonTerminator(NonTerminatorType.CompoundStatement) + ], + [ + // if Expression then Statement ElsePart + new Terminator(KeywordType.If), + new NonTerminator(NonTerminatorType.Expression), + new Terminator(KeywordType.Then), + new NonTerminator(NonTerminatorType.Statement), + new NonTerminator(NonTerminatorType.ElsePart) + ], + [ + // for id AssignOp Expression to Expression do Statement + new Terminator(KeywordType.For), + Terminator.IdentifierTerminator, + new Terminator(OperatorType.Assign), + new NonTerminator(NonTerminatorType.Expression), + new Terminator(KeywordType.To), + new NonTerminator(NonTerminatorType.Expression), + new Terminator(KeywordType.Do), + new NonTerminator(NonTerminatorType.Statement) + ] + ] + }, + // { + // // VariableList -> Variable | VariableList , Variable + // // 这里用expressionList代替VariableList + // new NonTerminator(NonTerminatorType.ExpressionList), [ + // [ + // new NonTerminator(NonTerminatorType.Variable) + // ], + // [ + // new NonTerminator(NonTerminatorType.ExpressionList), + // new Terminator(DelimiterType.Comma), + // new NonTerminator(NonTerminatorType.Variable) + // ] + // ] + // }, + { + // Variable -> id IdVarPart + new NonTerminator(NonTerminatorType.Variable), [ + [ + Terminator.IdentifierTerminator, + new NonTerminator(NonTerminatorType.IdVarPart) + ] + ] + }, + { + // IdVarPart -> ε | [ ExpressionList ] + new NonTerminator(NonTerminatorType.IdVarPart), [ + [ + Terminator.EmptyTerminator, + ], + [ + new Terminator(DelimiterType.LeftSquareBracket), + new NonTerminator(NonTerminatorType.ExpressionList), + new Terminator(DelimiterType.RightSquareBracket) + ] + ] + }, + { + // ProcedureCall -> id | id ( ExpressionList ) + new NonTerminator(NonTerminatorType.ProcedureCall), [ + [ + Terminator.IdentifierTerminator, + ], + [ + Terminator.IdentifierTerminator, + new Terminator(DelimiterType.LeftParenthesis), + new NonTerminator(NonTerminatorType.ExpressionList), + new Terminator(DelimiterType.RightParenthesis) + ] + ] + }, + { + // ElsePart -> ε | else statement + new NonTerminator(NonTerminatorType.ElsePart), [ + [ + Terminator.EmptyTerminator, + ], + [ + new Terminator(KeywordType.Else), + new NonTerminator(NonTerminatorType.Statement) + ] + ] + }, + { + // ExpressionList -> Expression | ExpressionList , Expression + new NonTerminator(NonTerminatorType.ExpressionList), [ + [ + new NonTerminator(NonTerminatorType.Expression) + ], + [ + new NonTerminator(NonTerminatorType.ExpressionList), + new Terminator(DelimiterType.Comma), + new NonTerminator(NonTerminatorType.Expression) + ] + ] + }, + { + // Expression -> SimpleExpression | SimpleExpression RelationOperator SimpleExpression + new NonTerminator(NonTerminatorType.Expression), [ + [ + new NonTerminator(NonTerminatorType.SimpleExpression) + ], + [ + new NonTerminator(NonTerminatorType.SimpleExpression), + new NonTerminator(NonTerminatorType.RelationOperator), + new NonTerminator(NonTerminatorType.SimpleExpression) + ] + ] + }, + { + // SimpleExpression -> Term | SimpleExpression AddOperator Term + new NonTerminator(NonTerminatorType.SimpleExpression), [ + [ + new NonTerminator(NonTerminatorType.Term) + ], + [ + new NonTerminator(NonTerminatorType.SimpleExpression), + new NonTerminator(NonTerminatorType.AddOperator), + new NonTerminator(NonTerminatorType.Term) + ] + ] + }, + { + // Term -> Factor | Term MultiplyOperator Factor + new NonTerminator(NonTerminatorType.Term), [ + [ + new NonTerminator(NonTerminatorType.Factor) + ], + [ + new NonTerminator(NonTerminatorType.Term), + new NonTerminator(NonTerminatorType.MultiplyOperator), + new NonTerminator(NonTerminatorType.Factor) + ] + ] + }, + { + // Factor -> num | Variable + // | ( Expression ) + // | id ( ExpressionList ) + // | not Factor + // | minus Factor + new NonTerminator(NonTerminatorType.Factor), [ + [ + Terminator.NumberTerminator, + ], + [ + new NonTerminator(NonTerminatorType.Variable) + ], + [ + new Terminator(DelimiterType.LeftParenthesis), + new NonTerminator(NonTerminatorType.Expression), + new Terminator(DelimiterType.RightParenthesis) + ], + [ + Terminator.IdentifierTerminator, + new Terminator(DelimiterType.LeftParenthesis), + new NonTerminator(NonTerminatorType.ExpressionList), + new Terminator(DelimiterType.RightParenthesis) + ], + [ + new Terminator(KeywordType.Not), + new NonTerminator(NonTerminatorType.Factor) + ], + [ + new Terminator(OperatorType.Minus), + new NonTerminator(NonTerminatorType.Factor) + ] + ] + }, + { + // AddOperator -> + | - | or + new NonTerminator(NonTerminatorType.AddOperator), [ + [ + new Terminator(OperatorType.Plus) + ], + [ + new Terminator(OperatorType.Minus) + ], + [ + new Terminator(KeywordType.Or) + ] + ] + }, + { + // MultiplyOperator -> * | / | div | mod | and + new NonTerminator(NonTerminatorType.MultiplyOperator), [ + [ + new Terminator(OperatorType.Multiply), + ], + [ + new Terminator(OperatorType.Divide), + ], + [ + new Terminator(KeywordType.Divide) + ], + [ + new Terminator(KeywordType.Mod) + ], + [ + new Terminator(KeywordType.And) + ] + ] + }, + { + // RelationOperator -> = | <> | < | <= | > | >= + new NonTerminator(NonTerminatorType.RelationOperator), [ + [ + new Terminator(OperatorType.Equal) + ], + [ + new Terminator(OperatorType.NotEqual) + ], + [ + new Terminator(OperatorType.Less) + ], + [ + new Terminator(OperatorType.LessEqual) + ], + [ + new Terminator(OperatorType.Greater) + ], + [ + new Terminator(OperatorType.GreaterEqual) + ] + ] + } + }; +} diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs new file mode 100644 index 0000000..93196ec --- /dev/null +++ b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs @@ -0,0 +1,27 @@ +using Canon.Core.Abstractions; +using Canon.Core.Enums; +using Canon.Core.GrammarParser; + +namespace Canon.Tests.GrammarParserTests; + +public partial class PascalGrammarTests +{ + private readonly GrammarBuilder _builder = new() + { + Generators = s_pascalGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator) + }; + + private readonly GrammarParserBase _parser; + + public PascalGrammarTests() + { + Grammar grammar = _builder.Build(); + _parser = grammar.ToGrammarParser(); + } + + [Fact] + public void GrammarTest() + { + Assert.NotNull(_parser); + } +} diff --git a/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs index c3fc9e3..cb72830 100644 --- a/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs +++ b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs @@ -18,7 +18,6 @@ public class SimpleGrammarWithEmptyTests(ITestOutputHelper testOutputHelper) /// B ProgramBody /// a Identifier /// - // private readonly ITestOutputHelper _testOutputHelper; private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; diff --git a/Canon.Tests/LexicalParserTests/OperatorTypeTests.cs b/Canon.Tests/LexicalParserTests/OperatorTypeTests.cs index b14961f..7c37117 100644 --- a/Canon.Tests/LexicalParserTests/OperatorTypeTests.cs +++ b/Canon.Tests/LexicalParserTests/OperatorTypeTests.cs @@ -6,45 +6,36 @@ namespace Canon.Tests.LexicalParserTests; public class OperatorTypeTests { [Theory] - [InlineData("+ 123", OperatorType.Plus, 0u, 0u, true)] - [InlineData("1 + 123", OperatorType.Plus, 0u, 2u, true)] - [InlineData("+123", OperatorType.Plus, 0u, 0u, true)] - [InlineData("m +123", OperatorType.Plus, 0u, 2u, true)] - [InlineData("-123", OperatorType.Minus, 0u, 0u, true)] - [InlineData("*123", OperatorType.Multiply, 0u, 0u, true)] - [InlineData("/123", OperatorType.Divide, 0u, 0u, true)] - [InlineData("=123", OperatorType.Equal, 0u, 0u, true)] - [InlineData("<123", OperatorType.Less, 0u, 0u, true)] - [InlineData(">123", OperatorType.Greater, 0u, 0u, true)] - [InlineData("<=123", OperatorType.LessEqual, 0u, 0u, true)] - [InlineData(">=123", OperatorType.GreaterEqual, 0u, 0u, true)] - [InlineData("<>123", OperatorType.NotEqual, 0u, 0u, true)] - [InlineData(":=123", OperatorType.Assign, 0u, 0u, true)] - [InlineData("and 123", OperatorType.And, 0u, 0u, true)] - [InlineData("or123", OperatorType.Or, 0u, 0u, true)] - [InlineData("mod123", OperatorType.Mod, 0u, 0u, true)] - - [InlineData("and123", OperatorType.And, 0u, 0u, false)] - [InlineData("andasd", OperatorType.And, 0u, 0u, false)] - [InlineData("andand", OperatorType.And, 0u, 0u, false)] - [InlineData("<><123", OperatorType.NotEqual, 0u, 0u, false)] - [InlineData("<><123", OperatorType.Less, 0u, 0u, false)] - [InlineData("<=<123", OperatorType.LessEqual, 0u, 0u, false)] - public void SmokeTest(string input, OperatorType type, uint expectedLinePos, uint expectedCharacterPos, bool expectedResult) + [InlineData("+ 123", OperatorType.Plus)] + [InlineData("1 + 123", OperatorType.Plus)] + [InlineData("+123", OperatorType.Plus)] + [InlineData("m +123", OperatorType.Plus)] + [InlineData("-123", OperatorType.Minus)] + [InlineData("*123", OperatorType.Multiply)] + [InlineData("/123", OperatorType.Divide)] + [InlineData("=123", OperatorType.Equal)] + [InlineData("<123", OperatorType.Less)] + [InlineData(">123", OperatorType.Greater)] + [InlineData("<=123", OperatorType.LessEqual)] + [InlineData(">=123", OperatorType.GreaterEqual)] + [InlineData("<>123", OperatorType.NotEqual)] + [InlineData(":=123", OperatorType.Assign)] + public void ParseTest(string input, OperatorType result) { LinkedList content = Utils.GetLinkedList(input); - Assert.Equal(expectedResult, OperatorSemanticToken.TryParse(expectedLinePos, expectedCharacterPos, content.First!, - out OperatorSemanticToken? token)); - if (expectedResult) - { - Assert.NotNull(token); - Assert.Equal(type, token.OperatorType); - Assert.Equal(expectedLinePos, token.LinePos); - Assert.Equal(expectedCharacterPos, token.CharacterPos); - } - else - { - Assert.Null(token); - } + Assert.True(OperatorSemanticToken.TryParse(0, 0, + content.First!, out OperatorSemanticToken? token)); + Assert.Equal(result, token?.OperatorType); + } + + [Theory] + [InlineData("<><123")] + [InlineData("<=<123")] + public void ParseFailedTest(string input) + { + LinkedList content = Utils.GetLinkedList(input); + Assert.False(OperatorSemanticToken.TryParse(0, 0, + content.First!, out OperatorSemanticToken? token)); + Assert.Null(token); } }