Merge pull request 'ParserTest' (#10) from ParserTest into master
Reviewed-on: PostGuard/Canon#10
This commit is contained in:
commit
184604940e
|
@ -68,3 +68,10 @@ public enum OperatorType
|
||||||
Or,
|
Or,
|
||||||
Assign
|
Assign
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum NumberType
|
||||||
|
{
|
||||||
|
Integer,
|
||||||
|
Real,
|
||||||
|
Hex
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Canon.Core.LexicalParser;
|
||||||
|
|
||||||
using Enums;
|
using Enums;
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 词法记号基类
|
/// 词法记号基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -198,13 +200,69 @@ public class OperatorSemanticToken : SemanticToken
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数值类型记号
|
/// 数值类型记号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// TODO:进制表示(只有$1的十六进制表示)
|
||||||
public class NumberSemanticToken : SemanticToken
|
public class NumberSemanticToken : SemanticToken
|
||||||
{
|
{
|
||||||
public override SemanticTokenType TokenType => SemanticTokenType.Number;
|
public override SemanticTokenType TokenType => SemanticTokenType.Number;
|
||||||
|
|
||||||
|
public required NumberType NumberType { get; init; }
|
||||||
|
public double Value { get; private init; }
|
||||||
|
|
||||||
public static bool TryParse(uint linePos, uint characterPos, LinkedListNode<char> now,
|
public static bool TryParse(uint linePos, uint characterPos, LinkedListNode<char> now,
|
||||||
out NumberSemanticToken? token)
|
out NumberSemanticToken? token)
|
||||||
{
|
{
|
||||||
|
StringBuilder buffer = new();
|
||||||
|
|
||||||
|
bool hasDecimalPoint = false;
|
||||||
|
bool hasExponent = false;
|
||||||
|
bool hasMinusSign = false;
|
||||||
|
|
||||||
|
while (now != null && (char.IsDigit(now.Value) || now.Value == '.' || now.Value == 'e' || now.Value == 'E' || now.Value == '-' || now.Value == '+'))
|
||||||
|
{
|
||||||
|
if (now.Value == '.')
|
||||||
|
{
|
||||||
|
if (hasDecimalPoint)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasDecimalPoint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now.Value == 'e' || now.Value == 'E')
|
||||||
|
{
|
||||||
|
if (hasExponent)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasExponent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now.Value == '-' || now.Value == '+')
|
||||||
|
{
|
||||||
|
if (hasMinusSign)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasMinusSign = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Append(now.Value);
|
||||||
|
now = now.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (double.TryParse(buffer.ToString(), out double value))
|
||||||
|
{
|
||||||
|
token = new NumberSemanticToken
|
||||||
|
{
|
||||||
|
LinePos = linePos,
|
||||||
|
CharacterPos = characterPos,
|
||||||
|
LiteralValue = buffer.ToString(),
|
||||||
|
Value = value,
|
||||||
|
NumberType = hasDecimalPoint || hasExponent ? NumberType.Real : NumberType.Integer
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
token = null;
|
token = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
6
Canon.Tests/LexicalParserTests/Array.cs
Normal file
6
Canon.Tests/LexicalParserTests/Array.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Canon.Tests.LexicalParserTests;
|
||||||
|
|
||||||
|
public class Array
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
37
Canon.Tests/LexicalParserTests/IndentifierTests.cs
Normal file
37
Canon.Tests/LexicalParserTests/IndentifierTests.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Canon.Tests.LexicalParserTests
|
||||||
|
{
|
||||||
|
public class IdentifierTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("identifier", true)]
|
||||||
|
[InlineData("_identifier", true)]
|
||||||
|
[InlineData("identifier123", true)]
|
||||||
|
[InlineData("123identifier", false)]
|
||||||
|
[InlineData("identifier_with_underscores", true)]
|
||||||
|
[InlineData("IdentifierWithCamelCase", true)]
|
||||||
|
[InlineData("identifier-with-hyphen", false)]
|
||||||
|
[InlineData("identifier with spaces", false)]
|
||||||
|
[InlineData("identifier_with_special_chars@#", false)]
|
||||||
|
[InlineData("", false)]
|
||||||
|
[InlineData(" ", false)]
|
||||||
|
[InlineData("andand",false)]
|
||||||
|
public void TestParseIdentifier(string input, bool expectedResult)
|
||||||
|
{
|
||||||
|
LinkedList<char> content = Utils.GetLinkedList(input);
|
||||||
|
Assert.Equal(expectedResult, IdentifierSemanticToken.TryParse(0, 0, content.First!,
|
||||||
|
out IdentifierSemanticToken? token));
|
||||||
|
if (expectedResult)
|
||||||
|
{
|
||||||
|
Assert.NotNull(token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Null(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Canon.Tests/LexicalParserTests/KeywordTypeTests.cs
Normal file
33
Canon.Tests/LexicalParserTests/KeywordTypeTests.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
|
||||||
|
namespace Canon.Tests.LexicalParserTests;
|
||||||
|
|
||||||
|
public class KeywordTypeTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("program", KeywordType.Program)]
|
||||||
|
[InlineData("const", KeywordType.Const)]
|
||||||
|
[InlineData("var", KeywordType.Var)]
|
||||||
|
[InlineData("procedure", KeywordType.Procedure)]
|
||||||
|
[InlineData("function", KeywordType.Function)]
|
||||||
|
[InlineData("begin", KeywordType.Begin)]
|
||||||
|
[InlineData("end", KeywordType.End)]
|
||||||
|
[InlineData("array", KeywordType.Array)]
|
||||||
|
[InlineData("of", KeywordType.Of)]
|
||||||
|
[InlineData("if", KeywordType.If)]
|
||||||
|
[InlineData("then", KeywordType.Then)]
|
||||||
|
[InlineData("else", KeywordType.Else)]
|
||||||
|
[InlineData("for", KeywordType.For)]
|
||||||
|
[InlineData("to", KeywordType.To)]
|
||||||
|
[InlineData("do", KeywordType.Do)]
|
||||||
|
public void SmokeTest(string input, KeywordType type)
|
||||||
|
{
|
||||||
|
LinkedList<char> content = Utils.GetLinkedList(input);
|
||||||
|
|
||||||
|
Assert.True(KeywordSemanticToken.TryParse(0, 0, content.First!,
|
||||||
|
out KeywordSemanticToken? token));
|
||||||
|
Assert.NotNull(token);
|
||||||
|
Assert.Equal(type, token.KeywordType);
|
||||||
|
}
|
||||||
|
}
|
45
Canon.Tests/LexicalParserTests/NumberTests.cs
Normal file
45
Canon.Tests/LexicalParserTests/NumberTests.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
|
||||||
|
namespace Canon.Tests.LexicalParserTests
|
||||||
|
{
|
||||||
|
public class NumberTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("123", 123, NumberType.Integer)]
|
||||||
|
[InlineData("0", 0, NumberType.Integer)]
|
||||||
|
[InlineData("-123", -123, NumberType.Integer)]
|
||||||
|
[InlineData("1.23", 1.23, NumberType.Real)]
|
||||||
|
[InlineData("-1.23", -1.23, NumberType.Real)]
|
||||||
|
[InlineData("0.0", 0.0, NumberType.Real)]
|
||||||
|
[InlineData("1e7", 1e7, NumberType.Real)]
|
||||||
|
[InlineData("1E7", 1E7, NumberType.Real)]
|
||||||
|
[InlineData("1.23e-7", 1.23e-7, NumberType.Real)]
|
||||||
|
[InlineData("1.23E-7", 1.23E-7, NumberType.Real)]
|
||||||
|
[InlineData("1234567890", 1234567890, NumberType.Integer)]
|
||||||
|
[InlineData("1234567890.1234567890", 1234567890.1234567890, NumberType.Real)]
|
||||||
|
[InlineData("-1234567890", -1234567890, NumberType.Integer)]
|
||||||
|
[InlineData("-1234567890.1234567890", -1234567890.1234567890, NumberType.Real)]
|
||||||
|
[InlineData("1e-7", 1e-7, NumberType.Real)]
|
||||||
|
[InlineData("1E-7", 1E-7, NumberType.Real)]
|
||||||
|
[InlineData("1E", 0, NumberType.Real, false)]
|
||||||
|
[InlineData("abc", 0, NumberType.Integer, false)]
|
||||||
|
[InlineData("123abc", 123, NumberType.Integer, true)]
|
||||||
|
public void TestParseNumber(string input, double expected, NumberType expectedNumberType, bool expectedResult = true)
|
||||||
|
{
|
||||||
|
LinkedList<char> content = Utils.GetLinkedList(input);
|
||||||
|
Assert.Equal(expectedResult, NumberSemanticToken.TryParse(0, 0, content.First!,
|
||||||
|
out NumberSemanticToken? token));
|
||||||
|
if (expectedResult)
|
||||||
|
{
|
||||||
|
Assert.NotNull(token);
|
||||||
|
Assert.Equal(expected, token.Value);
|
||||||
|
Assert.Equal(expectedNumberType, token.NumberType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Null(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
Canon.Tests/LexicalParserTests/OperatorTypeTests.cs
Normal file
50
Canon.Tests/LexicalParserTests/OperatorTypeTests.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
LinkedList<char> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user