diff --git a/Canon.Core/GrammarParser/Terminator.cs b/Canon.Core/GrammarParser/Terminator.cs index 7bd9216..faab1f8 100644 --- a/Canon.Core/GrammarParser/Terminator.cs +++ b/Canon.Core/GrammarParser/Terminator.cs @@ -1,4 +1,5 @@ using Canon.Core.Enums; +using Canon.Core.LexicalParser; namespace Canon.Core.GrammarParser; @@ -56,6 +57,12 @@ public class Terminator : TerminatorBase, IEquatable /// public static Terminator CharacterTerminator => new(SemanticTokenType.Character); + /// + /// 数值终结符单例 + /// 鉴于在语法中不关心具体数值,因此可以使用单例对象 + /// + public static Terminator NumberTerminator => new(SemanticTokenType.Number); + public override int GetHashCode() { int hash = _terminatorType.GetHashCode(); @@ -117,6 +124,46 @@ public class Terminator : TerminatorBase, IEquatable { return !a.Equals(b); } + + public static bool operator ==(Terminator a, SemanticToken b) + { + return a.EqualSemanticToken(b); + } + + public static bool operator !=(Terminator a, SemanticToken b) + { + return !a.EqualSemanticToken(b); + } + + public static bool operator ==(SemanticToken a, Terminator b) + { + return b.EqualSemanticToken(a); + } + + public static bool operator !=(SemanticToken a, Terminator b) + { + return !b.EqualSemanticToken(a); + } + + private bool EqualSemanticToken(SemanticToken token) + { + if (token.TokenType != _terminatorType) + { + return false; + } + + switch (_terminatorType) + { + case SemanticTokenType.Delimiter: + return (token as DelimiterSemanticToken)?.DelimiterType == _delimiterType; + case SemanticTokenType.Keyword: + return (token as KeywordSemanticToken)?.KeywordType == _keywordType; + case SemanticTokenType.Operator: + return (token as OperatorSemanticToken)?.OperatorType == _operatorType; + } + + return true; + } } /// diff --git a/Canon.Tests/GrammarParserTests/TerminatorTests.cs b/Canon.Tests/GrammarParserTests/TerminatorTests.cs new file mode 100644 index 0000000..320d3eb --- /dev/null +++ b/Canon.Tests/GrammarParserTests/TerminatorTests.cs @@ -0,0 +1,94 @@ +using Canon.Core.Enums; +using Canon.Core.GrammarParser; +using Canon.Core.LexicalParser; + +namespace Canon.Tests.GrammarParserTests; + +public class TerminatorTests +{ + [Fact] + public void TerminatorInnerTest() + { + Terminator keywordTerminator1 = new(KeywordType.Array); + Terminator keywordTerminator2 = new(KeywordType.Begin); + + Assert.False(keywordTerminator1 == keywordTerminator2); + Assert.False(keywordTerminator1 == Terminator.CharacterTerminator); + Assert.False(keywordTerminator2 == Terminator.IdentifierTerminator); + + Terminator keywordTerminator3 = new(KeywordType.Array); + Assert.Equal(keywordTerminator1, keywordTerminator3); + + Terminator delimiterTerminator1 = new(DelimiterType.Colon); + Assert.NotEqual(keywordTerminator1, delimiterTerminator1); + } + + [Fact] + public void TerminatorAndKeywordSemanticTokenTest() + { + Terminator keywordTerminator = new(KeywordType.Array); + LinkedList keywordContent = Utils.GetLinkedList("array [3..9] of integer"); + + Assert.True(KeywordSemanticToken.TryParse(0, 0, keywordContent.First!, + out KeywordSemanticToken? keywordSemanticToken)); + Assert.NotNull(keywordSemanticToken); + Assert.True(keywordTerminator == keywordSemanticToken); + } + + [Fact] + public void TerminatorAndDelimiterSemanticTokenTest() + { + Terminator terminator = new(DelimiterType.Period); + LinkedList content = Utils.GetLinkedList("."); + + Assert.True(DelimiterSemanticToken.TryParse(0, 0, content.First!, + out DelimiterSemanticToken? token)); + Assert.NotNull(token); + Assert.True(token == terminator); + } + + [Fact] + public void TerminatorAndOperatorSemanticTokenTest() + { + Terminator terminator = new(OperatorType.GreaterEqual); + LinkedList content = Utils.GetLinkedList(">="); + + Assert.True(OperatorSemanticToken.TryParse(0, 0, content.First!, + out OperatorSemanticToken? token)); + Assert.NotNull(token); + Assert.True(token == terminator); + } + + [Fact] + public void TerminatorAndNumberSemanticTokenTest() + { + LinkedList content = Utils.GetLinkedList("123"); + + Assert.True(NumberSemanticToken.TryParse(0, 0, content.First!, + out NumberSemanticToken? token)); + Assert.NotNull(token); + Assert.True(Terminator.NumberTerminator == token); + } + + [Fact] + public void TerminatorAndCharacterSemanticTokenTest() + { + LinkedList content = Utils.GetLinkedList("'a'"); + + Assert.True(CharacterSemanticToken.TryParse(0, 0, content.First!, + out CharacterSemanticToken? token)); + Assert.NotNull(token); + Assert.True(Terminator.CharacterTerminator == token); + } + + [Fact] + public void TerminatorAndIdentifierSemanticTokenTest() + { + LinkedList content = Utils.GetLinkedList("gcd"); + + Assert.True(IdentifierSemanticToken.TryParse(0, 0, content.First!, + out IdentifierSemanticToken? token)); + Assert.NotNull(token); + Assert.True(Terminator.IdentifierTerminator == token); + } +}