From 5e3ea6303e8cd3da432578652d5827d2bebbace7 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 7 Apr 2024 16:47:28 +0800 Subject: [PATCH] refact: syntax-node (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构语法树的部分,使用单独的类来抽象不同的非终结符节点。 **同时**,将`Pascal`语法的定义从测试项目中移动到核心项目中,在项目中只维护一份对于`Pascal`语法的定义。 Reviewed-on: https://git.rrricardo.top/PostGuard/Canon/pulls/23 --- Canon.Core/Abstractions/GrammarParseBase.cs | 21 +- Canon.Core/GrammarParser/Grammar.cs | 3 - .../GrammarParser/PascalGrammar.cs | 7 +- Canon.Core/GrammarParser/SyntaxNode.cs | 130 ------ Canon.Core/SyntaxNodes/AddOperator.cs | 13 + Canon.Core/SyntaxNodes/BasicType.cs | 13 + Canon.Core/SyntaxNodes/CompoundStatement.cs | 15 + Canon.Core/SyntaxNodes/ConstDeclaration.cs | 55 +++ Canon.Core/SyntaxNodes/ConstDeclarations.cs | 43 ++ Canon.Core/SyntaxNodes/ConstValue.cs | 13 + Canon.Core/SyntaxNodes/ElsePart.cs | 13 + Canon.Core/SyntaxNodes/Expression.cs | 13 + Canon.Core/SyntaxNodes/ExpressionList.cs | 54 +++ Canon.Core/SyntaxNodes/Factor.cs | 13 + Canon.Core/SyntaxNodes/FormalParameter.cs | 31 ++ Canon.Core/SyntaxNodes/IdentifierList.cs | 58 +++ Canon.Core/SyntaxNodes/IdentifierVarPart.cs | 51 +++ Canon.Core/SyntaxNodes/MultiplyOperator.cs | 13 + .../SyntaxNodes/NonTerminatedSyntaxNode.cs | 35 ++ Canon.Core/SyntaxNodes/Parameter.cs | 40 ++ Canon.Core/SyntaxNodes/ParameterList.cs | 54 +++ Canon.Core/SyntaxNodes/Period.cs | 69 +++ Canon.Core/SyntaxNodes/ProcedureCall.cs | 32 ++ Canon.Core/SyntaxNodes/ProgramBody.cs | 33 ++ Canon.Core/SyntaxNodes/ProgramHead.cs | 40 ++ Canon.Core/SyntaxNodes/ProgramStruct.cs | 23 + Canon.Core/SyntaxNodes/RelationOperator.cs | 13 + Canon.Core/SyntaxNodes/SimpleExpression.cs | 13 + Canon.Core/SyntaxNodes/Statement.cs | 13 + Canon.Core/SyntaxNodes/StatementList.cs | 51 +++ Canon.Core/SyntaxNodes/Subprogram.cs | 23 + Canon.Core/SyntaxNodes/SubprogramBody.cs | 28 ++ .../SyntaxNodes/SubprogramDeclarations.cs | 34 ++ Canon.Core/SyntaxNodes/SubprogramHead.cs | 45 ++ Canon.Core/SyntaxNodes/SyntaxNodeBase.cs | 107 +++++ Canon.Core/SyntaxNodes/Term.cs | 13 + .../SyntaxNodes/TerminatedSyntaxNode.cs | 10 + Canon.Core/SyntaxNodes/TypeSyntaxNode.cs | 13 + Canon.Core/SyntaxNodes/ValueParameter.cs | 23 + Canon.Core/SyntaxNodes/VarDeclaration.cs | 47 ++ Canon.Core/SyntaxNodes/VarDeclarations.cs | 42 ++ Canon.Core/SyntaxNodes/VarParameter.cs | 15 + Canon.Core/SyntaxNodes/Variable.cs | 20 + .../PascalGrammarConstPart.cs | 413 ------------------ .../GrammarParserTests/PascalGrammarTests.cs | 23 +- .../GrammarParserTests/SimpleGrammarTests.cs | 99 +---- .../SimpleGrammarWithEmptyTests.cs | 26 -- Canon.Tests/Utils.cs | 25 +- 48 files changed, 1273 insertions(+), 708 deletions(-) rename Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs => Canon.Core/GrammarParser/PascalGrammar.cs (99%) delete mode 100644 Canon.Core/GrammarParser/SyntaxNode.cs create mode 100644 Canon.Core/SyntaxNodes/AddOperator.cs create mode 100644 Canon.Core/SyntaxNodes/BasicType.cs create mode 100644 Canon.Core/SyntaxNodes/CompoundStatement.cs create mode 100644 Canon.Core/SyntaxNodes/ConstDeclaration.cs create mode 100644 Canon.Core/SyntaxNodes/ConstDeclarations.cs create mode 100644 Canon.Core/SyntaxNodes/ConstValue.cs create mode 100644 Canon.Core/SyntaxNodes/ElsePart.cs create mode 100644 Canon.Core/SyntaxNodes/Expression.cs create mode 100644 Canon.Core/SyntaxNodes/ExpressionList.cs create mode 100644 Canon.Core/SyntaxNodes/Factor.cs create mode 100644 Canon.Core/SyntaxNodes/FormalParameter.cs create mode 100644 Canon.Core/SyntaxNodes/IdentifierList.cs create mode 100644 Canon.Core/SyntaxNodes/IdentifierVarPart.cs create mode 100644 Canon.Core/SyntaxNodes/MultiplyOperator.cs create mode 100644 Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs create mode 100644 Canon.Core/SyntaxNodes/Parameter.cs create mode 100644 Canon.Core/SyntaxNodes/ParameterList.cs create mode 100644 Canon.Core/SyntaxNodes/Period.cs create mode 100644 Canon.Core/SyntaxNodes/ProcedureCall.cs create mode 100644 Canon.Core/SyntaxNodes/ProgramBody.cs create mode 100644 Canon.Core/SyntaxNodes/ProgramHead.cs create mode 100644 Canon.Core/SyntaxNodes/ProgramStruct.cs create mode 100644 Canon.Core/SyntaxNodes/RelationOperator.cs create mode 100644 Canon.Core/SyntaxNodes/SimpleExpression.cs create mode 100644 Canon.Core/SyntaxNodes/Statement.cs create mode 100644 Canon.Core/SyntaxNodes/StatementList.cs create mode 100644 Canon.Core/SyntaxNodes/Subprogram.cs create mode 100644 Canon.Core/SyntaxNodes/SubprogramBody.cs create mode 100644 Canon.Core/SyntaxNodes/SubprogramDeclarations.cs create mode 100644 Canon.Core/SyntaxNodes/SubprogramHead.cs create mode 100644 Canon.Core/SyntaxNodes/SyntaxNodeBase.cs create mode 100644 Canon.Core/SyntaxNodes/Term.cs create mode 100644 Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs create mode 100644 Canon.Core/SyntaxNodes/TypeSyntaxNode.cs create mode 100644 Canon.Core/SyntaxNodes/ValueParameter.cs create mode 100644 Canon.Core/SyntaxNodes/VarDeclaration.cs create mode 100644 Canon.Core/SyntaxNodes/VarDeclarations.cs create mode 100644 Canon.Core/SyntaxNodes/VarParameter.cs create mode 100644 Canon.Core/SyntaxNodes/Variable.cs delete mode 100644 Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs diff --git a/Canon.Core/Abstractions/GrammarParseBase.cs b/Canon.Core/Abstractions/GrammarParseBase.cs index aca801c..2cf8a55 100644 --- a/Canon.Core/Abstractions/GrammarParseBase.cs +++ b/Canon.Core/Abstractions/GrammarParseBase.cs @@ -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 tokens) + public SyntaxNodeBase Analyse(IEnumerable tokens) { Stack stack = []; - stack.Push(new AnalyseState(BeginTransformer, new SyntaxNode(SemanticToken.End))); + stack.Push(new AnalyseState(BeginTransformer, SyntaxNodeBase.Create(SemanticToken.End))); using IEnumerator enumerator = tokens.GetEnumerator(); if (!enumerator.MoveNext()) @@ -37,21 +39,24 @@ public abstract class GrammarParserBase return top.Node; } - SyntaxNode newNode = new(information.Left.Type); + List 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); } diff --git a/Canon.Core/GrammarParser/Grammar.cs b/Canon.Core/GrammarParser/Grammar.cs index b432889..166f513 100644 --- a/Canon.Core/GrammarParser/Grammar.cs +++ b/Canon.Core/GrammarParser/Grammar.cs @@ -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 ReduceTable { get; } = new Dictionary(); } - - private record AnalyseState(LrState State, SyntaxNode Node); } diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs b/Canon.Core/GrammarParser/PascalGrammar.cs similarity index 99% rename from Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs rename to Canon.Core/GrammarParser/PascalGrammar.cs index 054d6e5..d1b9612 100644 --- a/Canon.Tests/GrammarParserTests/PascalGrammarTests.Grammar.cs +++ b/Canon.Core/GrammarParser/PascalGrammar.cs @@ -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>> s_pascalGrammar = new() + public static readonly Dictionary>> Grammar = new() { { // ProgramStart -> ProgramStruct diff --git a/Canon.Core/GrammarParser/SyntaxNode.cs b/Canon.Core/GrammarParser/SyntaxNode.cs deleted file mode 100644 index 899840a..0000000 --- a/Canon.Core/GrammarParser/SyntaxNode.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Collections; -using Canon.Core.Enums; -using Canon.Core.LexicalParser; - -namespace Canon.Core.GrammarParser; - -/// -/// 抽象语法树上的节点 -/// -public class SyntaxNode : IEquatable, IEnumerable -{ - private readonly SemanticToken? _semanticToken; - private readonly NonTerminatorType _nonTerminatorType; - - public bool IsTerminated { get; } - - public List Children { get; } = []; - - public SyntaxNode(SemanticToken token) - { - IsTerminated = true; - _semanticToken = token; - } - - public SyntaxNode(NonTerminatorType nonTerminatorType) - { - IsTerminated = false; - _nonTerminatorType = nonTerminatorType; - } - - /// - /// 获得终结节点包含的记号对象 - /// - /// 词法分析得到的记号对象 - /// 在非终结节点上调用该方法 - public SemanticToken GetSemanticToken() - { - if (!IsTerminated) - { - throw new InvalidOperationException("Can not get semantic token from a not terminated node"); - } - - return _semanticToken!; - } - - /// - /// 获得非终结节点的类型 - /// - /// 非终结节点类型 - /// 在终结节点上调用该方法 - public NonTerminatorType GetNonTerminatorType() - { - if (IsTerminated) - { - throw new InvalidOperationException("Can not get non terminated type from a terminated node"); - } - - return _nonTerminatorType; - } - - public IEnumerator 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); - } -} diff --git a/Canon.Core/SyntaxNodes/AddOperator.cs b/Canon.Core/SyntaxNodes/AddOperator.cs new file mode 100644 index 0000000..3253be4 --- /dev/null +++ b/Canon.Core/SyntaxNodes/AddOperator.cs @@ -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 children) + { + return new AddOperator { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/BasicType.cs b/Canon.Core/SyntaxNodes/BasicType.cs new file mode 100644 index 0000000..aff154c --- /dev/null +++ b/Canon.Core/SyntaxNodes/BasicType.cs @@ -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 children) + { + return new BasicType { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/CompoundStatement.cs b/Canon.Core/SyntaxNodes/CompoundStatement.cs new file mode 100644 index 0000000..9dc777e --- /dev/null +++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs @@ -0,0 +1,15 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class CompoundStatement : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.CompoundStatement; + + public IEnumerable Statements => Children[1].Convert().Statements; + + public static CompoundStatement Create(List children) + { + return new CompoundStatement { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/ConstDeclaration.cs b/Canon.Core/SyntaxNodes/ConstDeclaration.cs new file mode 100644 index 0000000..b378348 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ConstDeclaration.cs @@ -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; + + /// + /// 是否递归的声明下一个ConstDeclaration + /// + public bool IsRecursive { get; private init; } + + /// + /// 获得声明的常量 + /// + public (IdentifierSemanticToken, ConstValue) ConstValue => GetConstValue(); + + public static ConstDeclaration Create(List 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().Token; + } + + private (IdentifierSemanticToken, ConstValue) GetConstValue() + { + if (IsRecursive) + { + return (ConvertToIdentifierSemanticToken(Children[2]), Children[4].Convert()); + } + else + { + return (ConvertToIdentifierSemanticToken(Children[0]), Children[2].Convert()); + } + } +} diff --git a/Canon.Core/SyntaxNodes/ConstDeclarations.cs b/Canon.Core/SyntaxNodes/ConstDeclarations.cs new file mode 100644 index 0000000..8b14e57 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ConstDeclarations.cs @@ -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; + + /// + /// 声明的常量列表 + /// + public IEnumerable<(IdentifierSemanticToken, ConstValue)> ConstValues => GetConstValues(); + + public static ConstDeclarations Create(List children) + { + return new ConstDeclarations { Children = children }; + } + + private IEnumerable<(IdentifierSemanticToken, ConstValue)> GetConstValues() + { + if (Children.Count == 0) + { + yield break; + } + + ConstDeclaration declaration = Children[1].Convert(); + + while (true) + { + yield return declaration.ConstValue; + + if (declaration.IsRecursive) + { + declaration = declaration.Children[0].Convert(); + } + else + { + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/ConstValue.cs b/Canon.Core/SyntaxNodes/ConstValue.cs new file mode 100644 index 0000000..d06ba3c --- /dev/null +++ b/Canon.Core/SyntaxNodes/ConstValue.cs @@ -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 children) + { + return new ConstValue { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/ElsePart.cs b/Canon.Core/SyntaxNodes/ElsePart.cs new file mode 100644 index 0000000..67937fa --- /dev/null +++ b/Canon.Core/SyntaxNodes/ElsePart.cs @@ -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 children) + { + return new ElsePart { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/Expression.cs b/Canon.Core/SyntaxNodes/Expression.cs new file mode 100644 index 0000000..b8aa742 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Expression.cs @@ -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 children) + { + return new Expression { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/ExpressionList.cs b/Canon.Core/SyntaxNodes/ExpressionList.cs new file mode 100644 index 0000000..4c76cfb --- /dev/null +++ b/Canon.Core/SyntaxNodes/ExpressionList.cs @@ -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; } + + /// + /// 声明的表达式列表 + /// + public IEnumerable Expressions => GetExpressions(); + + public static ExpressionList Create(List 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 GetExpressions() + { + ExpressionList list = this; + + while (true) + { + if (list.IsRecursive) + { + yield return list.Children[2].Convert(); + list = list.Children[0].Convert(); + } + else + { + yield return list.Children[0].Convert(); + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/Factor.cs b/Canon.Core/SyntaxNodes/Factor.cs new file mode 100644 index 0000000..ebf2597 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Factor.cs @@ -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 children) + { + return new Factor { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/FormalParameter.cs b/Canon.Core/SyntaxNodes/FormalParameter.cs new file mode 100644 index 0000000..59b04b6 --- /dev/null +++ b/Canon.Core/SyntaxNodes/FormalParameter.cs @@ -0,0 +1,31 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class FormalParameter : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.FormalParameter; + + /// + /// 声明的参数列表 + /// + public IEnumerable Parameters => GetParameters(); + + public static FormalParameter Create(List children) + { + return new FormalParameter { Children = children }; + } + + private IEnumerable GetParameters() + { + if (Children.Count == 0) + { + yield break; + } + + foreach (Parameter parameter in Children[1].Convert().Parameters) + { + yield return parameter; + } + } +} diff --git a/Canon.Core/SyntaxNodes/IdentifierList.cs b/Canon.Core/SyntaxNodes/IdentifierList.cs new file mode 100644 index 0000000..2bfb48a --- /dev/null +++ b/Canon.Core/SyntaxNodes/IdentifierList.cs @@ -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; + + /// + /// 是否含有递归定义 + /// + public bool IsRecursive { get; private init; } + + /// + /// 声明的标识符列表 + /// + public IEnumerable Identifiers => GetIdentifiers(); + + public static IdentifierList Create(List 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 GetIdentifiers() + { + IdentifierList identifier = this; + + while (true) + { + if (identifier.IsRecursive) + { + yield return (IdentifierSemanticToken)identifier.Children[2].Convert().Token; + identifier = identifier.Children[0].Convert(); + } + else + { + yield return (IdentifierSemanticToken)identifier.Children[0].Convert().Token; + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs new file mode 100644 index 0000000..8af4e74 --- /dev/null +++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs @@ -0,0 +1,51 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class IdentifierVarPart : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.IdVarPart; + + /// + /// 是否声明了索引部分 + /// + public bool Exist { get; private init; } + + /// + /// 索引中的位置声明 + /// + public IEnumerable Positions => GetPositions(); + + private IEnumerable GetPositions() + { + if (!Exist) + { + yield break; + } + + foreach (Expression expression in Children[1].Convert().Expressions) + { + yield return expression; + } + } + + public static IdentifierVarPart Create(List 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 }; + } +} diff --git a/Canon.Core/SyntaxNodes/MultiplyOperator.cs b/Canon.Core/SyntaxNodes/MultiplyOperator.cs new file mode 100644 index 0000000..c789130 --- /dev/null +++ b/Canon.Core/SyntaxNodes/MultiplyOperator.cs @@ -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 children) + { + return new MultiplyOperator { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs new file mode 100644 index 0000000..5339f47 --- /dev/null +++ b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs @@ -0,0 +1,35 @@ +using System.Collections; +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable +{ + public override bool IsTerminated => false; + + public abstract NonTerminatorType Type { get; } + + public required List Children { get; init; } + + public IEnumerator GetEnumerator() + { + yield return this; + + foreach (SyntaxNodeBase child in Children) + { + if (child.IsTerminated) + { + yield return child; + } + + NonTerminatedSyntaxNode nonTerminatedNode = child.Convert(); + + foreach (SyntaxNodeBase node in nonTerminatedNode) + { + yield return node; + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/Canon.Core/SyntaxNodes/Parameter.cs b/Canon.Core/SyntaxNodes/Parameter.cs new file mode 100644 index 0000000..1515ada --- /dev/null +++ b/Canon.Core/SyntaxNodes/Parameter.cs @@ -0,0 +1,40 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class Parameter : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.Parameter; + + /// + /// 是否为引用变量 + /// + public bool IsVar { get; private init; } + + /// + /// 声明的变量名称 + /// + public ValueParameter ValueParameter => + IsVar ? Children[0].Convert().ValueParameter : Children[0].Convert(); + + public static Parameter Create(List children) + { + NonTerminatedSyntaxNode node = children[0].Convert(); + + 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 }; + } +} diff --git a/Canon.Core/SyntaxNodes/ParameterList.cs b/Canon.Core/SyntaxNodes/ParameterList.cs new file mode 100644 index 0000000..ae7447f --- /dev/null +++ b/Canon.Core/SyntaxNodes/ParameterList.cs @@ -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; } + + /// + /// 声明的参数列表 + /// + public IEnumerable Parameters => GetParameters(); + + public static ParameterList Create(List 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 GetParameters() + { + ParameterList list = this; + + while (true) + { + if (list.IsRecursive) + { + yield return list.Children[2].Convert(); + list = list.Children[0].Convert(); + } + else + { + yield return list.Children[0].Convert(); + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/Period.cs b/Canon.Core/SyntaxNodes/Period.cs new file mode 100644 index 0000000..21d0cd6 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Period.cs @@ -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; } + + /// + /// 数组上下界列表 + /// + public IEnumerable<(NumberSemanticToken, NumberSemanticToken)> Ranges => GetRanges(); + + public static Period Create(List 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().Token, + (NumberSemanticToken)Children[4].Convert().Token); + } + else + { + return ((NumberSemanticToken)Children[0].Convert().Token, + (NumberSemanticToken)Children[2].Convert().Token); + } + } + + private IEnumerable<(NumberSemanticToken, NumberSemanticToken)> GetRanges() + { + Period period = this; + + while (true) + { + if (period.IsRecursive) + { + yield return period.GetRange(); + period = period.Children[0].Convert(); + } + else + { + yield return period.GetRange(); + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/ProcedureCall.cs b/Canon.Core/SyntaxNodes/ProcedureCall.cs new file mode 100644 index 0000000..aa5081c --- /dev/null +++ b/Canon.Core/SyntaxNodes/ProcedureCall.cs @@ -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().Token; + + public IEnumerable Arguments => GetArguments(); + + public static ProcedureCall Create(List children) + { + return new ProcedureCall { Children = children }; + } + + private IEnumerable GetArguments() + { + if (Children.Count == 1) + { + yield break; + } + + foreach (Expression expression in Children[2].Convert().Expressions) + { + yield return expression; + } + } +} diff --git a/Canon.Core/SyntaxNodes/ProgramBody.cs b/Canon.Core/SyntaxNodes/ProgramBody.cs new file mode 100644 index 0000000..1684303 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ProgramBody.cs @@ -0,0 +1,33 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class ProgramBody : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.ProgramBody; + + /// + /// 常量声明 + /// + public ConstDeclarations ConstDeclarations => Children[0].Convert(); + + /// + /// 变量声明 + /// + public VarDeclarations VarDeclarations => Children[1].Convert(); + + /// + /// 子程序声明 + /// + public SubprogramDeclarations SubprogramDeclarations => Children[2].Convert(); + + /// + /// 语句声明 + /// + public CompoundStatement CompoundStatement => Children[3].Convert(); + + public static ProgramBody Create(List children) + { + return new ProgramBody { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/ProgramHead.cs b/Canon.Core/SyntaxNodes/ProgramHead.cs new file mode 100644 index 0000000..ffd02e4 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ProgramHead.cs @@ -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; + + /// + /// 程序名称 + /// + public IdentifierSemanticToken ProgramName + => (IdentifierSemanticToken)Children[1].Convert().Token; + + /// + /// 暂时意义不明的标识符列表 + /// https://wiki.freepascal.org/Program_Structure/zh_CN + /// TODO: 查阅资料 + /// + public IEnumerable FileList => GetFileList(); + + public static ProgramHead Create(List children) + { + return new ProgramHead { Children = children }; + } + + private IEnumerable GetFileList() + { + if (Children.Count == 2) + { + yield break; + } + + foreach (IdentifierSemanticToken token in Children[3].Convert().Identifiers) + { + yield return token; + } + } +} diff --git a/Canon.Core/SyntaxNodes/ProgramStruct.cs b/Canon.Core/SyntaxNodes/ProgramStruct.cs new file mode 100644 index 0000000..2a3a5b7 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ProgramStruct.cs @@ -0,0 +1,23 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class ProgramStruct : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.ProgramStruct; + + /// + /// 程序头 + /// + public ProgramHead Head => Children[0].Convert(); + + /// + /// 程序体 + /// + public ProgramBody Body => Children[2].Convert(); + + public static ProgramStruct Create(List children) + { + return new ProgramStruct { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/RelationOperator.cs b/Canon.Core/SyntaxNodes/RelationOperator.cs new file mode 100644 index 0000000..4b6bc7b --- /dev/null +++ b/Canon.Core/SyntaxNodes/RelationOperator.cs @@ -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 children) + { + return new RelationOperator { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/SimpleExpression.cs b/Canon.Core/SyntaxNodes/SimpleExpression.cs new file mode 100644 index 0000000..8c9fd81 --- /dev/null +++ b/Canon.Core/SyntaxNodes/SimpleExpression.cs @@ -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 children) + { + return new SimpleExpression { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/Statement.cs b/Canon.Core/SyntaxNodes/Statement.cs new file mode 100644 index 0000000..e347140 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Statement.cs @@ -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 children) + { + return new Statement { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/StatementList.cs b/Canon.Core/SyntaxNodes/StatementList.cs new file mode 100644 index 0000000..740cc22 --- /dev/null +++ b/Canon.Core/SyntaxNodes/StatementList.cs @@ -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 Statements => GetStatements(); + + public static StatementList Create(List 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 GetStatements() + { + StatementList list = this; + + while (true) + { + if (list.IsRecursive) + { + yield return list.Children[2].Convert(); + list = list.Children[0].Convert(); + } + else + { + yield return list.Children[0].Convert(); + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/Subprogram.cs b/Canon.Core/SyntaxNodes/Subprogram.cs new file mode 100644 index 0000000..a8f29bf --- /dev/null +++ b/Canon.Core/SyntaxNodes/Subprogram.cs @@ -0,0 +1,23 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class Subprogram : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.Subprogram; + + /// + /// 子程序头部 + /// + public SubprogramHead Head => Children[0].Convert(); + + /// + /// 子程序体 + /// + public SubprogramBody Body => Children[2].Convert(); + + public static Subprogram Create(List children) + { + return new Subprogram { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/SubprogramBody.cs b/Canon.Core/SyntaxNodes/SubprogramBody.cs new file mode 100644 index 0000000..16cad3a --- /dev/null +++ b/Canon.Core/SyntaxNodes/SubprogramBody.cs @@ -0,0 +1,28 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class SubprogramBody : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.SubprogramBody; + + /// + /// 常量声明部分 + /// + public ConstDeclarations ConstDeclarations => Children[0].Convert(); + + /// + /// 变量声明部分 + /// + public VarDeclarations VarDeclarations => Children[1].Convert(); + + /// + /// 语句声明部分 + /// + public CompoundStatement CompoundStatement => Children[2].Convert(); + + public static SubprogramBody Create(List children) + { + return new SubprogramBody() { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs new file mode 100644 index 0000000..b6932bb --- /dev/null +++ b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs @@ -0,0 +1,34 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class SubprogramDeclarations : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.SubprogramDeclarations; + + /// + /// 声明的子程序列表 + /// + public IEnumerable Subprograms => GetSubprograms(); + + public static SubprogramDeclarations Create(List children) + { + return new SubprogramDeclarations { Children = children }; + } + + private IEnumerable GetSubprograms() + { + SubprogramDeclarations declarations = this; + + while (true) + { + if (declarations.Children.Count == 0) + { + yield break; + } + + yield return declarations.Children[1].Convert(); + declarations = declarations.Children[0].Convert(); + } + } +} diff --git a/Canon.Core/SyntaxNodes/SubprogramHead.cs b/Canon.Core/SyntaxNodes/SubprogramHead.cs new file mode 100644 index 0000000..28a78ed --- /dev/null +++ b/Canon.Core/SyntaxNodes/SubprogramHead.cs @@ -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; } + + /// + /// 子程序的名称 + /// + public IdentifierSemanticToken SubprogramName => + (IdentifierSemanticToken)Children[1].Convert().Token; + + /// + /// 子程序的参数 + /// + public IEnumerable Parameters => Children[2].Convert().Parameters; + + public static SubprogramHead Create(List children) + { + bool isProcedure; + + TerminatedSyntaxNode node = children[0].Convert(); + 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 }; + } +} diff --git a/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs b/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs new file mode 100644 index 0000000..c9dfd1f --- /dev/null +++ b/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs @@ -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() 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 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(); + } + } +} diff --git a/Canon.Core/SyntaxNodes/Term.cs b/Canon.Core/SyntaxNodes/Term.cs new file mode 100644 index 0000000..a18f894 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Term.cs @@ -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 children) + { + return new Term { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs new file mode 100644 index 0000000..c2fdd0c --- /dev/null +++ b/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs @@ -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; } +} diff --git a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs new file mode 100644 index 0000000..9aa978f --- /dev/null +++ b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs @@ -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 children) + { + return new TypeSyntaxNode { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/ValueParameter.cs b/Canon.Core/SyntaxNodes/ValueParameter.cs new file mode 100644 index 0000000..5ccaa36 --- /dev/null +++ b/Canon.Core/SyntaxNodes/ValueParameter.cs @@ -0,0 +1,23 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class ValueParameter : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.ValueParameter; + + /// + /// 声明的变量列表 + /// + public IdentifierList IdentifierList => Children[0].Convert(); + + /// + /// 声明的变量类型 + /// + public BasicType BasicType => Children[2].Convert(); + + public static ValueParameter Create(List children) + { + return new ValueParameter { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/VarDeclaration.cs b/Canon.Core/SyntaxNodes/VarDeclaration.cs new file mode 100644 index 0000000..498dd2f --- /dev/null +++ b/Canon.Core/SyntaxNodes/VarDeclaration.cs @@ -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; } + + /// + /// 声明的变量 + /// + public (IdentifierList, TypeSyntaxNode) Variable => GetVariable(); + + private (IdentifierList, TypeSyntaxNode) GetVariable() + { + if (IsRecursive) + { + return (Children[2].Convert(), Children[4].Convert()); + } + else + { + return (Children[0].Convert(), Children[2].Convert()); + } + } + + public static VarDeclaration Create(List 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 }; + } +} diff --git a/Canon.Core/SyntaxNodes/VarDeclarations.cs b/Canon.Core/SyntaxNodes/VarDeclarations.cs new file mode 100644 index 0000000..0c70405 --- /dev/null +++ b/Canon.Core/SyntaxNodes/VarDeclarations.cs @@ -0,0 +1,42 @@ +using Canon.Core.Enums; + +namespace Canon.Core.SyntaxNodes; + +public class VarDeclarations : NonTerminatedSyntaxNode +{ + public override NonTerminatorType Type => NonTerminatorType.VarDeclarations; + + /// + /// 声明的变量列表 + /// + public IEnumerable<(IdentifierList, TypeSyntaxNode)> Variables => EnumerateVariables(); + + public static VarDeclarations Create(List children) + { + return new VarDeclarations { Children = children }; + } + + private IEnumerable<(IdentifierList, TypeSyntaxNode)> EnumerateVariables() + { + if (Children.Count == 0) + { + yield break; + } + + VarDeclaration declaration = Children[1].Convert(); + + while (true) + { + yield return declaration.Variable; + + if (declaration.IsRecursive) + { + declaration = declaration.Children[0].Convert(); + } + else + { + break; + } + } + } +} diff --git a/Canon.Core/SyntaxNodes/VarParameter.cs b/Canon.Core/SyntaxNodes/VarParameter.cs new file mode 100644 index 0000000..75bada5 --- /dev/null +++ b/Canon.Core/SyntaxNodes/VarParameter.cs @@ -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(); + + public static VarParameter Create(List children) + { + return new VarParameter { Children = children }; + } +} diff --git a/Canon.Core/SyntaxNodes/Variable.cs b/Canon.Core/SyntaxNodes/Variable.cs new file mode 100644 index 0000000..24d1617 --- /dev/null +++ b/Canon.Core/SyntaxNodes/Variable.cs @@ -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; + + /// + /// 变量的名称 + /// + public IdentifierSemanticToken Identifier => + (IdentifierSemanticToken)Children[0].Convert().Token; + + public static Variable Create(List children) + { + return new Variable { Children = children }; + } +} diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs b/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs deleted file mode 100644 index 8e1a920..0000000 --- a/Canon.Tests/GrammarParserTests/PascalGrammarConstPart.cs +++ /dev/null @@ -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>> 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.cs b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs index 93196ec..5afbabe 100644 --- a/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs +++ b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs @@ -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 tokens = lexer.Tokenize(); + tokens.Add(SemanticToken.End); + + ProgramStruct root = _parser.Analyse(tokens).Convert(); + Assert.Equal("DoNothing", root.Head.ProgramName.LiteralValue); + } } diff --git a/Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs b/Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs index 05c6f9d..69ee139 100644 --- a/Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs +++ b/Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs @@ -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 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 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); - } } diff --git a/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs index cb72830..0715cab 100644 --- a/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs +++ b/Canon.Tests/GrammarParserTests/SimpleGrammarWithEmptyTests.cs @@ -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 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()); - } } diff --git a/Canon.Tests/Utils.cs b/Canon.Tests/Utils.cs index 3b5f4bf..9babb68 100644 --- a/Canon.Tests/Utils.cs +++ b/Canon.Tests/Utils.cs @@ -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; } - - /// - /// 验证两棵语法树一致 - /// - /// 一棵语法树 - /// 另一棵语法树 - public static void CheckSyntaxRoot(SyntaxNode a, SyntaxNode b) - { - int length = a.Count(); - Assert.Equal(length, b.Count()); - - using IEnumerator 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); - } - } }