refact: syntax-node (#23)
重构语法树的部分,使用单独的类来抽象不同的非终结符节点。 **同时**,将`Pascal`语法的定义从测试项目中移动到核心项目中,在项目中只维护一份对于`Pascal`语法的定义。 Reviewed-on: PostGuard/Canon#23
This commit is contained in:
parent
c0a8e25d45
commit
5e3ea6303e
|
@ -1,5 +1,7 @@
|
|||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.LexicalParser;
|
||||
using Canon.Core.SyntaxNodes;
|
||||
|
||||
namespace Canon.Core.Abstractions;
|
||||
|
||||
|
@ -12,10 +14,10 @@ public abstract class GrammarParserBase
|
|||
|
||||
public abstract NonTerminator Begin { get; }
|
||||
|
||||
public SyntaxNode Analyse(IEnumerable<SemanticToken> tokens)
|
||||
public SyntaxNodeBase Analyse(IEnumerable<SemanticToken> tokens)
|
||||
{
|
||||
Stack<AnalyseState> stack = [];
|
||||
stack.Push(new AnalyseState(BeginTransformer, new SyntaxNode(SemanticToken.End)));
|
||||
stack.Push(new AnalyseState(BeginTransformer, SyntaxNodeBase.Create(SemanticToken.End)));
|
||||
|
||||
using IEnumerator<SemanticToken> enumerator = tokens.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
|
@ -37,21 +39,24 @@ public abstract class GrammarParserBase
|
|||
return top.Node;
|
||||
}
|
||||
|
||||
SyntaxNode newNode = new(information.Left.Type);
|
||||
List<SyntaxNodeBase> children = [];
|
||||
NonTerminatorType leftType = information.Left.Type;
|
||||
for (int i = 0; i < information.Length; i++)
|
||||
{
|
||||
newNode.Children.Add(stack.Pop().Node);
|
||||
children.Add(stack.Pop().Node);
|
||||
}
|
||||
|
||||
// 为了符合生成式的顺序而倒序
|
||||
children.Reverse();
|
||||
stack.Push(new AnalyseState(stack.Peek().State.ShiftTable[information.Left],
|
||||
newNode));
|
||||
SyntaxNodeBase.Create(leftType, children)));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果没有成功归约就进行移进
|
||||
if (top.State.ShiftTable.TryGetValue(enumerator.Current, out ITransformer? next))
|
||||
{
|
||||
stack.Push(new AnalyseState(next, new SyntaxNode(enumerator.Current)));
|
||||
stack.Push(new AnalyseState(next, SyntaxNodeBase.Create(enumerator.Current)));
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
continue;
|
||||
|
@ -66,5 +71,5 @@ public abstract class GrammarParserBase
|
|||
}
|
||||
}
|
||||
|
||||
private record AnalyseState(ITransformer State, SyntaxNode Node);
|
||||
private record AnalyseState(ITransformer State, SyntaxNodeBase Node);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Canon.Core.Abstractions;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.GrammarParser;
|
||||
|
||||
|
@ -86,6 +85,4 @@ public class Grammar
|
|||
public IDictionary<Terminator, ReduceInformation> ReduceTable { get; }
|
||||
= new Dictionary<Terminator, ReduceInformation>();
|
||||
}
|
||||
|
||||
private record AnalyseState(LrState State, SyntaxNode Node);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.GrammarParser;
|
||||
|
||||
namespace Canon.Tests.GrammarParserTests;
|
||||
namespace Canon.Core.GrammarParser;
|
||||
|
||||
public partial class PascalGrammarTests
|
||||
public static class PascalGrammar
|
||||
{
|
||||
private static readonly Dictionary<NonTerminator, List<List<TerminatorBase>>> s_pascalGrammar = new()
|
||||
public static readonly Dictionary<NonTerminator, List<List<TerminatorBase>>> Grammar = new()
|
||||
{
|
||||
{
|
||||
// ProgramStart -> ProgramStruct
|
|
@ -1,130 +0,0 @@
|
|||
using System.Collections;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.GrammarParser;
|
||||
|
||||
/// <summary>
|
||||
/// 抽象语法树上的节点
|
||||
/// </summary>
|
||||
public class SyntaxNode : IEquatable<SyntaxNode>, IEnumerable<SyntaxNode>
|
||||
{
|
||||
private readonly SemanticToken? _semanticToken;
|
||||
private readonly NonTerminatorType _nonTerminatorType;
|
||||
|
||||
public bool IsTerminated { get; }
|
||||
|
||||
public List<SyntaxNode> Children { get; } = [];
|
||||
|
||||
public SyntaxNode(SemanticToken token)
|
||||
{
|
||||
IsTerminated = true;
|
||||
_semanticToken = token;
|
||||
}
|
||||
|
||||
public SyntaxNode(NonTerminatorType nonTerminatorType)
|
||||
{
|
||||
IsTerminated = false;
|
||||
_nonTerminatorType = nonTerminatorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得终结节点包含的记号对象
|
||||
/// </summary>
|
||||
/// <returns>词法分析得到的记号对象</returns>
|
||||
/// <exception cref="InvalidOperationException">在非终结节点上调用该方法</exception>
|
||||
public SemanticToken GetSemanticToken()
|
||||
{
|
||||
if (!IsTerminated)
|
||||
{
|
||||
throw new InvalidOperationException("Can not get semantic token from a not terminated node");
|
||||
}
|
||||
|
||||
return _semanticToken!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得非终结节点的类型
|
||||
/// </summary>
|
||||
/// <returns>非终结节点类型</returns>
|
||||
/// <exception cref="InvalidOperationException">在终结节点上调用该方法</exception>
|
||||
public NonTerminatorType GetNonTerminatorType()
|
||||
{
|
||||
if (IsTerminated)
|
||||
{
|
||||
throw new InvalidOperationException("Can not get non terminated type from a terminated node");
|
||||
}
|
||||
|
||||
return _nonTerminatorType;
|
||||
}
|
||||
|
||||
public IEnumerator<SyntaxNode> GetEnumerator()
|
||||
{
|
||||
yield return this;
|
||||
|
||||
foreach (SyntaxNode child in Children)
|
||||
{
|
||||
foreach (SyntaxNode node in child)
|
||||
{
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(SyntaxNode? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsTerminated != other.IsTerminated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsTerminated)
|
||||
{
|
||||
return GetSemanticToken() == other.GetSemanticToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: 在判等时是否需要判断子节点也相等
|
||||
return GetNonTerminatorType() == other.GetNonTerminatorType();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not SyntaxNode other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (IsTerminated)
|
||||
{
|
||||
return GetSemanticToken().GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetNonTerminatorType().GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public static bool operator ==(SyntaxNode a, SyntaxNode b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(SyntaxNode a, SyntaxNode b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/AddOperator.cs
Normal file
13
Canon.Core/SyntaxNodes/AddOperator.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class AddOperator : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.AddOperator;
|
||||
|
||||
public static AddOperator Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new AddOperator { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/BasicType.cs
Normal file
13
Canon.Core/SyntaxNodes/BasicType.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class BasicType : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.BasicType;
|
||||
|
||||
public static BasicType Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new BasicType { Children = children };
|
||||
}
|
||||
}
|
15
Canon.Core/SyntaxNodes/CompoundStatement.cs
Normal file
15
Canon.Core/SyntaxNodes/CompoundStatement.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class CompoundStatement : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.CompoundStatement;
|
||||
|
||||
public IEnumerable<Statement> Statements => Children[1].Convert<StatementList>().Statements;
|
||||
|
||||
public static CompoundStatement Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new CompoundStatement { Children = children };
|
||||
}
|
||||
}
|
55
Canon.Core/SyntaxNodes/ConstDeclaration.cs
Normal file
55
Canon.Core/SyntaxNodes/ConstDeclaration.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ConstDeclaration : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ConstDeclaration;
|
||||
|
||||
/// <summary>
|
||||
/// 是否递归的声明下一个ConstDeclaration
|
||||
/// </summary>
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得声明的常量
|
||||
/// </summary>
|
||||
public (IdentifierSemanticToken, ConstValue) ConstValue => GetConstValue();
|
||||
|
||||
public static ConstDeclaration Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
if (children.Count == 3)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 5)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new ConstDeclaration { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
|
||||
private static IdentifierSemanticToken ConvertToIdentifierSemanticToken(SyntaxNodeBase node)
|
||||
{
|
||||
return (IdentifierSemanticToken)node.Convert<TerminatedSyntaxNode>().Token;
|
||||
}
|
||||
|
||||
private (IdentifierSemanticToken, ConstValue) GetConstValue()
|
||||
{
|
||||
if (IsRecursive)
|
||||
{
|
||||
return (ConvertToIdentifierSemanticToken(Children[2]), Children[4].Convert<ConstValue>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ConvertToIdentifierSemanticToken(Children[0]), Children[2].Convert<ConstValue>());
|
||||
}
|
||||
}
|
||||
}
|
43
Canon.Core/SyntaxNodes/ConstDeclarations.cs
Normal file
43
Canon.Core/SyntaxNodes/ConstDeclarations.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ConstDeclarations : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ConstDeclarations;
|
||||
|
||||
/// <summary>
|
||||
/// 声明的常量列表
|
||||
/// </summary>
|
||||
public IEnumerable<(IdentifierSemanticToken, ConstValue)> ConstValues => GetConstValues();
|
||||
|
||||
public static ConstDeclarations Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ConstDeclarations { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<(IdentifierSemanticToken, ConstValue)> GetConstValues()
|
||||
{
|
||||
if (Children.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
ConstDeclaration declaration = Children[1].Convert<ConstDeclaration>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return declaration.ConstValue;
|
||||
|
||||
if (declaration.IsRecursive)
|
||||
{
|
||||
declaration = declaration.Children[0].Convert<ConstDeclaration>();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/ConstValue.cs
Normal file
13
Canon.Core/SyntaxNodes/ConstValue.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ConstValue : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ConstValue;
|
||||
|
||||
public static ConstValue Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ConstValue { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/ElsePart.cs
Normal file
13
Canon.Core/SyntaxNodes/ElsePart.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ElsePart : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ElsePart;
|
||||
|
||||
public static ElsePart Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ElsePart { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/Expression.cs
Normal file
13
Canon.Core/SyntaxNodes/Expression.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Expression : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Expression;
|
||||
|
||||
public static Expression Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Expression { Children = children };
|
||||
}
|
||||
}
|
54
Canon.Core/SyntaxNodes/ExpressionList.cs
Normal file
54
Canon.Core/SyntaxNodes/ExpressionList.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ExpressionList : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ExpressionList;
|
||||
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 声明的表达式列表
|
||||
/// </summary>
|
||||
public IEnumerable<Expression> Expressions => GetExpressions();
|
||||
|
||||
public static ExpressionList Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 1)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 3)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new ExpressionList { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
|
||||
private IEnumerable<Expression> GetExpressions()
|
||||
{
|
||||
ExpressionList list = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (list.IsRecursive)
|
||||
{
|
||||
yield return list.Children[2].Convert<Expression>();
|
||||
list = list.Children[0].Convert<ExpressionList>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return list.Children[0].Convert<Expression>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/Factor.cs
Normal file
13
Canon.Core/SyntaxNodes/Factor.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Factor : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Factor;
|
||||
|
||||
public static Factor Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Factor { Children = children };
|
||||
}
|
||||
}
|
31
Canon.Core/SyntaxNodes/FormalParameter.cs
Normal file
31
Canon.Core/SyntaxNodes/FormalParameter.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class FormalParameter : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.FormalParameter;
|
||||
|
||||
/// <summary>
|
||||
/// 声明的参数列表
|
||||
/// </summary>
|
||||
public IEnumerable<Parameter> Parameters => GetParameters();
|
||||
|
||||
public static FormalParameter Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new FormalParameter { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<Parameter> GetParameters()
|
||||
{
|
||||
if (Children.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (Parameter parameter in Children[1].Convert<ParameterList>().Parameters)
|
||||
{
|
||||
yield return parameter;
|
||||
}
|
||||
}
|
||||
}
|
58
Canon.Core/SyntaxNodes/IdentifierList.cs
Normal file
58
Canon.Core/SyntaxNodes/IdentifierList.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class IdentifierList : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.IdentifierList;
|
||||
|
||||
/// <summary>
|
||||
/// 是否含有递归定义
|
||||
/// </summary>
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 声明的标识符列表
|
||||
/// </summary>
|
||||
public IEnumerable<IdentifierSemanticToken> Identifiers => GetIdentifiers();
|
||||
|
||||
public static IdentifierList Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 1)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 3)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new IdentifierList { IsRecursive = isRecursive, Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<IdentifierSemanticToken> GetIdentifiers()
|
||||
{
|
||||
IdentifierList identifier = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (identifier.IsRecursive)
|
||||
{
|
||||
yield return (IdentifierSemanticToken)identifier.Children[2].Convert<TerminatedSyntaxNode>().Token;
|
||||
identifier = identifier.Children[0].Convert<IdentifierList>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return (IdentifierSemanticToken)identifier.Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
Canon.Core/SyntaxNodes/IdentifierVarPart.cs
Normal file
51
Canon.Core/SyntaxNodes/IdentifierVarPart.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class IdentifierVarPart : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.IdVarPart;
|
||||
|
||||
/// <summary>
|
||||
/// 是否声明了索引部分
|
||||
/// </summary>
|
||||
public bool Exist { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 索引中的位置声明
|
||||
/// </summary>
|
||||
public IEnumerable<Expression> Positions => GetPositions();
|
||||
|
||||
private IEnumerable<Expression> GetPositions()
|
||||
{
|
||||
if (!Exist)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (Expression expression in Children[1].Convert<ExpressionList>().Expressions)
|
||||
{
|
||||
yield return expression;
|
||||
}
|
||||
}
|
||||
|
||||
public static IdentifierVarPart Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool exist;
|
||||
|
||||
if (children.Count == 0)
|
||||
{
|
||||
exist = false;
|
||||
}
|
||||
else if (children.Count == 3)
|
||||
{
|
||||
exist = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new IdentifierVarPart { Children = children, Exist = exist };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/MultiplyOperator.cs
Normal file
13
Canon.Core/SyntaxNodes/MultiplyOperator.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class MultiplyOperator : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator;
|
||||
|
||||
public static MultiplyOperator Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new MultiplyOperator { Children = children };
|
||||
}
|
||||
}
|
35
Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs
Normal file
35
Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Collections;
|
||||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<SyntaxNodeBase>
|
||||
{
|
||||
public override bool IsTerminated => false;
|
||||
|
||||
public abstract NonTerminatorType Type { get; }
|
||||
|
||||
public required List<SyntaxNodeBase> Children { get; init; }
|
||||
|
||||
public IEnumerator<SyntaxNodeBase> GetEnumerator()
|
||||
{
|
||||
yield return this;
|
||||
|
||||
foreach (SyntaxNodeBase child in Children)
|
||||
{
|
||||
if (child.IsTerminated)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
|
||||
NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>();
|
||||
|
||||
foreach (SyntaxNodeBase node in nonTerminatedNode)
|
||||
{
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
40
Canon.Core/SyntaxNodes/Parameter.cs
Normal file
40
Canon.Core/SyntaxNodes/Parameter.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Parameter : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Parameter;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为引用变量
|
||||
/// </summary>
|
||||
public bool IsVar { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 声明的变量名称
|
||||
/// </summary>
|
||||
public ValueParameter ValueParameter =>
|
||||
IsVar ? Children[0].Convert<VarParameter>().ValueParameter : Children[0].Convert<ValueParameter>();
|
||||
|
||||
public static Parameter Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
NonTerminatedSyntaxNode node = children[0].Convert<NonTerminatedSyntaxNode>();
|
||||
|
||||
bool isVar;
|
||||
if (node.Type == NonTerminatorType.VarParameter)
|
||||
{
|
||||
isVar = true;
|
||||
}
|
||||
else if (node.Type == NonTerminatorType.ValueParameter)
|
||||
{
|
||||
isVar = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new Parameter { Children = children, IsVar = isVar };
|
||||
}
|
||||
}
|
54
Canon.Core/SyntaxNodes/ParameterList.cs
Normal file
54
Canon.Core/SyntaxNodes/ParameterList.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ParameterList : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ParameterList;
|
||||
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 声明的参数列表
|
||||
/// </summary>
|
||||
public IEnumerable<Parameter> Parameters => GetParameters();
|
||||
|
||||
public static ParameterList Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 1)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 3)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new ParameterList { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
|
||||
private IEnumerable<Parameter> GetParameters()
|
||||
{
|
||||
ParameterList list = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (list.IsRecursive)
|
||||
{
|
||||
yield return list.Children[2].Convert<Parameter>();
|
||||
list = list.Children[0].Convert<ParameterList>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return list.Children[0].Convert<Parameter>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
Canon.Core/SyntaxNodes/Period.cs
Normal file
69
Canon.Core/SyntaxNodes/Period.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Period : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Period;
|
||||
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 数组上下界列表
|
||||
/// </summary>
|
||||
public IEnumerable<(NumberSemanticToken, NumberSemanticToken)> Ranges => GetRanges();
|
||||
|
||||
public static Period Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 3)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 5)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new Period { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
|
||||
private (NumberSemanticToken, NumberSemanticToken) GetRange()
|
||||
{
|
||||
if (IsRecursive)
|
||||
{
|
||||
return ((NumberSemanticToken)Children[2].Convert<TerminatedSyntaxNode>().Token,
|
||||
(NumberSemanticToken)Children[4].Convert<TerminatedSyntaxNode>().Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((NumberSemanticToken)Children[0].Convert<TerminatedSyntaxNode>().Token,
|
||||
(NumberSemanticToken)Children[2].Convert<TerminatedSyntaxNode>().Token);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<(NumberSemanticToken, NumberSemanticToken)> GetRanges()
|
||||
{
|
||||
Period period = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (period.IsRecursive)
|
||||
{
|
||||
yield return period.GetRange();
|
||||
period = period.Children[0].Convert<Period>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return period.GetRange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
Canon.Core/SyntaxNodes/ProcedureCall.cs
Normal file
32
Canon.Core/SyntaxNodes/ProcedureCall.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ProcedureCall : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ProcedureCall;
|
||||
|
||||
public IdentifierSemanticToken ProcedureId
|
||||
=> (IdentifierSemanticToken)Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||
|
||||
public IEnumerable<Expression> Arguments => GetArguments();
|
||||
|
||||
public static ProcedureCall Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ProcedureCall { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<Expression> GetArguments()
|
||||
{
|
||||
if (Children.Count == 1)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (Expression expression in Children[2].Convert<ExpressionList>().Expressions)
|
||||
{
|
||||
yield return expression;
|
||||
}
|
||||
}
|
||||
}
|
33
Canon.Core/SyntaxNodes/ProgramBody.cs
Normal file
33
Canon.Core/SyntaxNodes/ProgramBody.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ProgramBody : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ProgramBody;
|
||||
|
||||
/// <summary>
|
||||
/// 常量声明
|
||||
/// </summary>
|
||||
public ConstDeclarations ConstDeclarations => Children[0].Convert<ConstDeclarations>();
|
||||
|
||||
/// <summary>
|
||||
/// 变量声明
|
||||
/// </summary>
|
||||
public VarDeclarations VarDeclarations => Children[1].Convert<VarDeclarations>();
|
||||
|
||||
/// <summary>
|
||||
/// 子程序声明
|
||||
/// </summary>
|
||||
public SubprogramDeclarations SubprogramDeclarations => Children[2].Convert<SubprogramDeclarations>();
|
||||
|
||||
/// <summary>
|
||||
/// 语句声明
|
||||
/// </summary>
|
||||
public CompoundStatement CompoundStatement => Children[3].Convert<CompoundStatement>();
|
||||
|
||||
public static ProgramBody Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ProgramBody { Children = children };
|
||||
}
|
||||
}
|
40
Canon.Core/SyntaxNodes/ProgramHead.cs
Normal file
40
Canon.Core/SyntaxNodes/ProgramHead.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ProgramHead : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ProgramHead;
|
||||
|
||||
/// <summary>
|
||||
/// 程序名称
|
||||
/// </summary>
|
||||
public IdentifierSemanticToken ProgramName
|
||||
=> (IdentifierSemanticToken)Children[1].Convert<TerminatedSyntaxNode>().Token;
|
||||
|
||||
/// <summary>
|
||||
/// 暂时意义不明的标识符列表
|
||||
/// https://wiki.freepascal.org/Program_Structure/zh_CN
|
||||
/// TODO: 查阅资料
|
||||
/// </summary>
|
||||
public IEnumerable<IdentifierSemanticToken> FileList => GetFileList();
|
||||
|
||||
public static ProgramHead Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ProgramHead { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<IdentifierSemanticToken> GetFileList()
|
||||
{
|
||||
if (Children.Count == 2)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (IdentifierSemanticToken token in Children[3].Convert<IdentifierList>().Identifiers)
|
||||
{
|
||||
yield return token;
|
||||
}
|
||||
}
|
||||
}
|
23
Canon.Core/SyntaxNodes/ProgramStruct.cs
Normal file
23
Canon.Core/SyntaxNodes/ProgramStruct.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ProgramStruct : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ProgramStruct;
|
||||
|
||||
/// <summary>
|
||||
/// 程序头
|
||||
/// </summary>
|
||||
public ProgramHead Head => Children[0].Convert<ProgramHead>();
|
||||
|
||||
/// <summary>
|
||||
/// 程序体
|
||||
/// </summary>
|
||||
public ProgramBody Body => Children[2].Convert<ProgramBody>();
|
||||
|
||||
public static ProgramStruct Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ProgramStruct { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/RelationOperator.cs
Normal file
13
Canon.Core/SyntaxNodes/RelationOperator.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class RelationOperator : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.RelationOperator;
|
||||
|
||||
public static RelationOperator Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new RelationOperator { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/SimpleExpression.cs
Normal file
13
Canon.Core/SyntaxNodes/SimpleExpression.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class SimpleExpression : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.SimpleExpression;
|
||||
|
||||
public static SimpleExpression Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new SimpleExpression { Children = children };
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/Statement.cs
Normal file
13
Canon.Core/SyntaxNodes/Statement.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Statement : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Statement;
|
||||
|
||||
public static Statement Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Statement { Children = children };
|
||||
}
|
||||
}
|
51
Canon.Core/SyntaxNodes/StatementList.cs
Normal file
51
Canon.Core/SyntaxNodes/StatementList.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class StatementList : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.StatementList;
|
||||
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
public IEnumerable<Statement> Statements => GetStatements();
|
||||
|
||||
public static StatementList Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 1)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 3)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new StatementList { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
|
||||
private IEnumerable<Statement> GetStatements()
|
||||
{
|
||||
StatementList list = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (list.IsRecursive)
|
||||
{
|
||||
yield return list.Children[2].Convert<Statement>();
|
||||
list = list.Children[0].Convert<StatementList>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return list.Children[0].Convert<Statement>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
Canon.Core/SyntaxNodes/Subprogram.cs
Normal file
23
Canon.Core/SyntaxNodes/Subprogram.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Subprogram : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Subprogram;
|
||||
|
||||
/// <summary>
|
||||
/// 子程序头部
|
||||
/// </summary>
|
||||
public SubprogramHead Head => Children[0].Convert<SubprogramHead>();
|
||||
|
||||
/// <summary>
|
||||
/// 子程序体
|
||||
/// </summary>
|
||||
public SubprogramBody Body => Children[2].Convert<SubprogramBody>();
|
||||
|
||||
public static Subprogram Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Subprogram { Children = children };
|
||||
}
|
||||
}
|
28
Canon.Core/SyntaxNodes/SubprogramBody.cs
Normal file
28
Canon.Core/SyntaxNodes/SubprogramBody.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class SubprogramBody : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.SubprogramBody;
|
||||
|
||||
/// <summary>
|
||||
/// 常量声明部分
|
||||
/// </summary>
|
||||
public ConstDeclarations ConstDeclarations => Children[0].Convert<ConstDeclarations>();
|
||||
|
||||
/// <summary>
|
||||
/// 变量声明部分
|
||||
/// </summary>
|
||||
public VarDeclarations VarDeclarations => Children[1].Convert<VarDeclarations>();
|
||||
|
||||
/// <summary>
|
||||
/// 语句声明部分
|
||||
/// </summary>
|
||||
public CompoundStatement CompoundStatement => Children[2].Convert<CompoundStatement>();
|
||||
|
||||
public static SubprogramBody Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new SubprogramBody() { Children = children };
|
||||
}
|
||||
}
|
34
Canon.Core/SyntaxNodes/SubprogramDeclarations.cs
Normal file
34
Canon.Core/SyntaxNodes/SubprogramDeclarations.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class SubprogramDeclarations : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.SubprogramDeclarations;
|
||||
|
||||
/// <summary>
|
||||
/// 声明的子程序列表
|
||||
/// </summary>
|
||||
public IEnumerable<Subprogram> Subprograms => GetSubprograms();
|
||||
|
||||
public static SubprogramDeclarations Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new SubprogramDeclarations { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<Subprogram> GetSubprograms()
|
||||
{
|
||||
SubprogramDeclarations declarations = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (declarations.Children.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return declarations.Children[1].Convert<Subprogram>();
|
||||
declarations = declarations.Children[0].Convert<SubprogramDeclarations>();
|
||||
}
|
||||
}
|
||||
}
|
45
Canon.Core/SyntaxNodes/SubprogramHead.cs
Normal file
45
Canon.Core/SyntaxNodes/SubprogramHead.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class SubprogramHead : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.SubprogramHead;
|
||||
|
||||
public bool IsProcedure { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 子程序的名称
|
||||
/// </summary>
|
||||
public IdentifierSemanticToken SubprogramName =>
|
||||
(IdentifierSemanticToken)Children[1].Convert<TerminatedSyntaxNode>().Token;
|
||||
|
||||
/// <summary>
|
||||
/// 子程序的参数
|
||||
/// </summary>
|
||||
public IEnumerable<Parameter> Parameters => Children[2].Convert<FormalParameter>().Parameters;
|
||||
|
||||
public static SubprogramHead Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isProcedure;
|
||||
|
||||
TerminatedSyntaxNode node = children[0].Convert<TerminatedSyntaxNode>();
|
||||
KeywordSemanticToken token = (KeywordSemanticToken)node.Token;
|
||||
|
||||
if (token.KeywordType == KeywordType.Procedure)
|
||||
{
|
||||
isProcedure = true;
|
||||
}
|
||||
else if (token.KeywordType == KeywordType.Function)
|
||||
{
|
||||
isProcedure = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new SubprogramHead { Children = children, IsProcedure = isProcedure };
|
||||
}
|
||||
}
|
107
Canon.Core/SyntaxNodes/SyntaxNodeBase.cs
Normal file
107
Canon.Core/SyntaxNodes/SyntaxNodeBase.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public abstract class SyntaxNodeBase
|
||||
{
|
||||
public abstract bool IsTerminated { get; }
|
||||
|
||||
public T Convert<T>() where T : SyntaxNodeBase
|
||||
{
|
||||
T? result = this as T;
|
||||
|
||||
if (result is null)
|
||||
{
|
||||
throw new InvalidOperationException("Can't cast into target SyntaxNode");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SyntaxNodeBase Create(SemanticToken token)
|
||||
{
|
||||
return new TerminatedSyntaxNode { Token = token };
|
||||
}
|
||||
|
||||
public static SyntaxNodeBase Create(NonTerminatorType type, List<SyntaxNodeBase> children)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case NonTerminatorType.ProgramStruct:
|
||||
return ProgramStruct.Create(children);
|
||||
case NonTerminatorType.ProgramHead:
|
||||
return ProgramHead.Create(children);
|
||||
case NonTerminatorType.ProgramBody:
|
||||
return ProgramBody.Create(children);
|
||||
case NonTerminatorType.IdentifierList:
|
||||
return IdentifierList.Create(children);
|
||||
case NonTerminatorType.VarDeclarations:
|
||||
return VarDeclarations.Create(children);
|
||||
case NonTerminatorType.SubprogramDeclarations:
|
||||
return SubprogramDeclarations.Create(children);
|
||||
case NonTerminatorType.CompoundStatement:
|
||||
return CompoundStatement.Create(children);
|
||||
case NonTerminatorType.ConstValue:
|
||||
return ConstValue.Create(children);
|
||||
case NonTerminatorType.ConstDeclaration:
|
||||
return ConstDeclaration.Create(children);
|
||||
case NonTerminatorType.ConstDeclarations:
|
||||
return ConstDeclarations.Create(children);
|
||||
case NonTerminatorType.VarDeclaration:
|
||||
return VarDeclaration.Create(children);
|
||||
case NonTerminatorType.Type:
|
||||
return TypeSyntaxNode.Create(children);
|
||||
case NonTerminatorType.BasicType:
|
||||
return BasicType.Create(children);
|
||||
case NonTerminatorType.Period:
|
||||
return Period.Create(children);
|
||||
case NonTerminatorType.Subprogram:
|
||||
return Subprogram.Create(children);
|
||||
case NonTerminatorType.SubprogramHead:
|
||||
return SubprogramHead.Create(children);
|
||||
case NonTerminatorType.SubprogramBody:
|
||||
return SubprogramBody.Create(children);
|
||||
case NonTerminatorType.FormalParameter:
|
||||
return FormalParameter.Create(children);
|
||||
case NonTerminatorType.ParameterList:
|
||||
return ParameterList.Create(children);
|
||||
case NonTerminatorType.Parameter:
|
||||
return Parameter.Create(children);
|
||||
case NonTerminatorType.VarParameter:
|
||||
return VarParameter.Create(children);
|
||||
case NonTerminatorType.ValueParameter:
|
||||
return ValueParameter.Create(children);
|
||||
case NonTerminatorType.StatementList:
|
||||
return StatementList.Create(children);
|
||||
case NonTerminatorType.Statement:
|
||||
return Statement.Create(children);
|
||||
case NonTerminatorType.Variable:
|
||||
return Variable.Create(children);
|
||||
case NonTerminatorType.Expression:
|
||||
return Expression.Create(children);
|
||||
case NonTerminatorType.ProcedureCall:
|
||||
return ProcedureCall.Create(children);
|
||||
case NonTerminatorType.ElsePart:
|
||||
return ElsePart.Create(children);
|
||||
case NonTerminatorType.ExpressionList:
|
||||
return ExpressionList.Create(children);
|
||||
case NonTerminatorType.SimpleExpression:
|
||||
return SimpleExpression.Create(children);
|
||||
case NonTerminatorType.Term:
|
||||
return Term.Create(children);
|
||||
case NonTerminatorType.Factor:
|
||||
return Factor.Create(children);
|
||||
case NonTerminatorType.AddOperator:
|
||||
return AddOperator.Create(children);
|
||||
case NonTerminatorType.MultiplyOperator:
|
||||
return MultiplyOperator.Create(children);
|
||||
case NonTerminatorType.RelationOperator:
|
||||
return RelationOperator.Create(children);
|
||||
case NonTerminatorType.IdVarPart:
|
||||
return IdentifierVarPart.Create(children);
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
13
Canon.Core/SyntaxNodes/Term.cs
Normal file
13
Canon.Core/SyntaxNodes/Term.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Term : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Term;
|
||||
|
||||
public static Term Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Term { Children = children };
|
||||
}
|
||||
}
|
10
Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs
Normal file
10
Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class TerminatedSyntaxNode : SyntaxNodeBase
|
||||
{
|
||||
public override bool IsTerminated => true;
|
||||
|
||||
public required SemanticToken Token { get; init; }
|
||||
}
|
13
Canon.Core/SyntaxNodes/TypeSyntaxNode.cs
Normal file
13
Canon.Core/SyntaxNodes/TypeSyntaxNode.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class TypeSyntaxNode : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Type;
|
||||
|
||||
public static TypeSyntaxNode Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new TypeSyntaxNode { Children = children };
|
||||
}
|
||||
}
|
23
Canon.Core/SyntaxNodes/ValueParameter.cs
Normal file
23
Canon.Core/SyntaxNodes/ValueParameter.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class ValueParameter : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.ValueParameter;
|
||||
|
||||
/// <summary>
|
||||
/// 声明的变量列表
|
||||
/// </summary>
|
||||
public IdentifierList IdentifierList => Children[0].Convert<IdentifierList>();
|
||||
|
||||
/// <summary>
|
||||
/// 声明的变量类型
|
||||
/// </summary>
|
||||
public BasicType BasicType => Children[2].Convert<BasicType>();
|
||||
|
||||
public static ValueParameter Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new ValueParameter { Children = children };
|
||||
}
|
||||
}
|
47
Canon.Core/SyntaxNodes/VarDeclaration.cs
Normal file
47
Canon.Core/SyntaxNodes/VarDeclaration.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class VarDeclaration : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.VarDeclaration;
|
||||
|
||||
public bool IsRecursive { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// 声明的变量
|
||||
/// </summary>
|
||||
public (IdentifierList, TypeSyntaxNode) Variable => GetVariable();
|
||||
|
||||
private (IdentifierList, TypeSyntaxNode) GetVariable()
|
||||
{
|
||||
if (IsRecursive)
|
||||
{
|
||||
return (Children[2].Convert<IdentifierList>(), Children[4].Convert<TypeSyntaxNode>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Children[0].Convert<IdentifierList>(), Children[2].Convert<TypeSyntaxNode>());
|
||||
}
|
||||
}
|
||||
|
||||
public static VarDeclaration Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
bool isRecursive;
|
||||
|
||||
if (children.Count == 3)
|
||||
{
|
||||
isRecursive = false;
|
||||
}
|
||||
else if (children.Count == 5)
|
||||
{
|
||||
isRecursive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return new VarDeclaration { Children = children, IsRecursive = isRecursive };
|
||||
}
|
||||
}
|
42
Canon.Core/SyntaxNodes/VarDeclarations.cs
Normal file
42
Canon.Core/SyntaxNodes/VarDeclarations.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class VarDeclarations : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.VarDeclarations;
|
||||
|
||||
/// <summary>
|
||||
/// 声明的变量列表
|
||||
/// </summary>
|
||||
public IEnumerable<(IdentifierList, TypeSyntaxNode)> Variables => EnumerateVariables();
|
||||
|
||||
public static VarDeclarations Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new VarDeclarations { Children = children };
|
||||
}
|
||||
|
||||
private IEnumerable<(IdentifierList, TypeSyntaxNode)> EnumerateVariables()
|
||||
{
|
||||
if (Children.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
VarDeclaration declaration = Children[1].Convert<VarDeclaration>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return declaration.Variable;
|
||||
|
||||
if (declaration.IsRecursive)
|
||||
{
|
||||
declaration = declaration.Children[0].Convert<VarDeclaration>();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
Canon.Core/SyntaxNodes/VarParameter.cs
Normal file
15
Canon.Core/SyntaxNodes/VarParameter.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class VarParameter : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.VarParameter;
|
||||
|
||||
public ValueParameter ValueParameter => Children[1].Convert<ValueParameter>();
|
||||
|
||||
public static VarParameter Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new VarParameter { Children = children };
|
||||
}
|
||||
}
|
20
Canon.Core/SyntaxNodes/Variable.cs
Normal file
20
Canon.Core/SyntaxNodes/Variable.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
||||
public class Variable : NonTerminatedSyntaxNode
|
||||
{
|
||||
public override NonTerminatorType Type => NonTerminatorType.Variable;
|
||||
|
||||
/// <summary>
|
||||
/// 变量的名称
|
||||
/// </summary>
|
||||
public IdentifierSemanticToken Identifier =>
|
||||
(IdentifierSemanticToken)Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||
|
||||
public static Variable Create(List<SyntaxNodeBase> children)
|
||||
{
|
||||
return new Variable { Children = children };
|
||||
}
|
||||
}
|
|
@ -1,413 +0,0 @@
|
|||
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<NonTerminator, List<List<TerminatorBase>>> 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<SemanticToken> 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<SemanticToken> 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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
using Canon.Core.Abstractions;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.LexicalParser;
|
||||
using Canon.Core.SyntaxNodes;
|
||||
|
||||
namespace Canon.Tests.GrammarParserTests;
|
||||
|
||||
public partial class PascalGrammarTests
|
||||
public class PascalGrammarTests
|
||||
{
|
||||
private readonly GrammarBuilder _builder = new()
|
||||
{
|
||||
Generators = s_pascalGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||
Generators = PascalGrammar.Grammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||
};
|
||||
|
||||
private readonly GrammarParserBase _parser;
|
||||
|
@ -24,4 +26,21 @@ public partial class PascalGrammarTests
|
|||
{
|
||||
Assert.NotNull(_parser);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoNothingTest()
|
||||
{
|
||||
const string program = """
|
||||
program DoNothing;
|
||||
begin
|
||||
end.
|
||||
""";
|
||||
|
||||
Lexer lexer = new(program);
|
||||
List<SemanticToken> tokens = lexer.Tokenize();
|
||||
tokens.Add(SemanticToken.End);
|
||||
|
||||
ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>();
|
||||
Assert.Equal("DoNothing", root.Head.ProgramName.LiteralValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using Canon.Core.Abstractions;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Tests.GrammarParserTests;
|
||||
|
||||
|
@ -109,99 +107,4 @@ public class SimpleGrammarTests
|
|||
Assert.Contains(new Terminator(DelimiterType.LeftParenthesis), grammar.BeginState.Transformer.Keys);
|
||||
Assert.Contains(Terminator.IdentifierTerminator, grammar.BeginState.Transformer.Keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyseSingleSentenceTest()
|
||||
{
|
||||
GrammarBuilder builder = new()
|
||||
{
|
||||
Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||
};
|
||||
|
||||
Grammar grammar = builder.Build();
|
||||
GrammarParserBase parser = grammar.ToGrammarParser();
|
||||
// n + n
|
||||
List<SemanticToken> tokens =
|
||||
[
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "n" },
|
||||
new OperatorSemanticToken
|
||||
{
|
||||
LinePos = 0, CharacterPos = 0, LiteralValue = "+", OperatorType = OperatorType.Plus
|
||||
},
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "n" },
|
||||
SemanticToken.End
|
||||
];
|
||||
|
||||
// 分析树为
|
||||
// E
|
||||
// |
|
||||
// /\
|
||||
// / | \
|
||||
// E + T
|
||||
// | |
|
||||
// T F
|
||||
// | |
|
||||
// F n
|
||||
// |
|
||||
// n
|
||||
SyntaxNode root = parser.Analyse(tokens);
|
||||
Assert.Equal(NonTerminatorType.ProgramStruct, root.GetNonTerminatorType());
|
||||
Assert.Equal(3, 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;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
Assert.Equal(9, root.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyseComplexSentenceTest()
|
||||
{
|
||||
GrammarBuilder builder = new()
|
||||
{
|
||||
Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||
};
|
||||
|
||||
Grammar grammar = builder.Build();
|
||||
GrammarParserBase parser = grammar.ToGrammarParser();
|
||||
|
||||
// (n + n) * n
|
||||
List<SemanticToken> tokens =
|
||||
[
|
||||
new DelimiterSemanticToken
|
||||
{
|
||||
LinePos = 0, CharacterPos = 0, LiteralValue = "(", DelimiterType = DelimiterType.LeftParenthesis
|
||||
},
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "n" },
|
||||
new OperatorSemanticToken
|
||||
{
|
||||
LinePos = 0, CharacterPos = 0, LiteralValue = "+", OperatorType = OperatorType.Plus
|
||||
},
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "n" },
|
||||
new DelimiterSemanticToken
|
||||
{
|
||||
LinePos = 0, CharacterPos = 0, LiteralValue = ")", DelimiterType = DelimiterType.RightParenthesis
|
||||
},
|
||||
new OperatorSemanticToken
|
||||
{
|
||||
LinePos = 0, CharacterPos = 0, LiteralValue = "*", OperatorType = OperatorType.Multiply
|
||||
},
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "n" },
|
||||
SemanticToken.End
|
||||
];
|
||||
|
||||
|
||||
SyntaxNode root = parser.Analyse(tokens);
|
||||
Assert.Equal(18, root.Count());
|
||||
Assert.False(root.IsTerminated);
|
||||
Assert.Equal(NonTerminatorType.ProgramStruct, root.GetNonTerminatorType());
|
||||
Assert.Single(root.Children);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using Canon.Core.Abstractions;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.LexicalParser;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Canon.Tests.GrammarParserTests;
|
||||
|
@ -167,29 +166,4 @@ public class SimpleGrammarWithEmptyTests(ITestOutputHelper testOutputHelper)
|
|||
Assert.Single(transformer1.ReduceTable);
|
||||
Assert.Contains(new NonTerminator(NonTerminatorType.ProgramStruct),transformer1.ShiftTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyseSingleSentenceTest()
|
||||
{
|
||||
GrammarBuilder builder = new()
|
||||
{
|
||||
Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||
};
|
||||
|
||||
Grammar grammar = builder.Build();
|
||||
GrammarParserBase parser = grammar.ToGrammarParser();
|
||||
|
||||
List<SemanticToken> tokens =
|
||||
[
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "a" },
|
||||
new IdentifierSemanticToken { LinePos = 0, CharacterPos = 0, LiteralValue = "a" },
|
||||
SemanticToken.End
|
||||
];
|
||||
|
||||
SyntaxNode root = parser.Analyse(tokens);
|
||||
|
||||
Assert.False(root.IsTerminated);
|
||||
Assert.Equal(NonTerminatorType.ProgramStruct, root.GetNonTerminatorType());
|
||||
Assert.Equal(7, root.Count());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using Canon.Core.GrammarParser;
|
||||
|
||||
namespace Canon.Tests;
|
||||
namespace Canon.Tests;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
|
@ -15,25 +13,4 @@ public static class Utils
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证两棵语法树一致
|
||||
/// </summary>
|
||||
/// <param name="a">一棵语法树</param>
|
||||
/// <param name="b">另一棵语法树</param>
|
||||
public static void CheckSyntaxRoot(SyntaxNode a, SyntaxNode b)
|
||||
{
|
||||
int length = a.Count();
|
||||
Assert.Equal(length, b.Count());
|
||||
|
||||
using IEnumerator<SyntaxNode> aIter = a.GetEnumerator(), bIter = b.GetEnumerator();
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
Assert.True(aIter.MoveNext());
|
||||
Assert.True(bIter.MoveNext());
|
||||
|
||||
Assert.Equal(aIter.Current, bIter.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user