diff --git a/Canon.Core/Abstractions/SyntaxNodeVisitor.cs b/Canon.Core/Abstractions/SyntaxNodeVisitor.cs new file mode 100644 index 0000000..c2c65d3 --- /dev/null +++ b/Canon.Core/Abstractions/SyntaxNodeVisitor.cs @@ -0,0 +1,302 @@ +using Canon.Core.SyntaxNodes; + +namespace Canon.Core.Abstractions; + +public abstract class SyntaxNodeVisitor +{ + public virtual void PreVisit(AddOperator addOperator) + { + } + + public virtual void PostVisit(AddOperator addOperator) + { + } + + public virtual void PreVisit(BasicType basicType) + { + } + + public virtual void PostVisit(BasicType basicType) + { + } + + public virtual void PreVisit(CompoundStatement compoundStatement) + { + } + + public virtual void PostVisit(CompoundStatement compoundStatement) + { + } + + public virtual void PreVisit(ConstDeclaration constDeclaration) + { + } + + public virtual void PostVisit(ConstDeclaration constDeclaration) + { + } + + public virtual void PreVisit(ConstDeclarations constDeclarations) + { + } + + public virtual void PostVisit(ConstDeclarations constDeclarations) + { + } + + public virtual void PreVisit(ConstValue constValue) + { + } + + public virtual void PostVisit(ConstValue constValue) + { + } + + public virtual void PreVisit(ElsePart elsePart) + { + } + + public virtual void PostVisit(ElsePart elsePart) + { + } + + public virtual void PreVisit(Expression expression) + { + } + + public virtual void PostVisit(Expression expression) + { + } + + public virtual void PreVisit(ExpressionList expressionList) + { + } + + public virtual void PostVisit(ExpressionList expressionList) + { + } + + public virtual void PreVisit(Factor factor) + { + } + + public virtual void PostVisit(Factor factor) + { + } + + public virtual void PreVisit(FormalParameter formalParameter) + { + } + + public virtual void PostVisit(FormalParameter formalParameter) + { + } + + public virtual void PreVisit(IdentifierList identifierList) + { + } + + public virtual void PostVisit(IdentifierList identifierList) + { + } + + public virtual void PreVisit(IdentifierVarPart identifierVarPart) + { + } + + public virtual void PostVisit(IdentifierVarPart identifierVarPart) + { + } + + public virtual void PreVisit(MultiplyOperator multiplyOperator) + { + } + + public virtual void PostVisit(MultiplyOperator multiplyOperator) + { + } + + public virtual void PreVisit(Parameter parameter) + { + } + + public virtual void PostVisit(Parameter parameter) + { + } + + public virtual void PreVisit(ParameterList parameterList) + { + } + + public virtual void PostVisit(ParameterList parameterList) + { + } + + public virtual void PreVisit(Period period) + { + } + + public virtual void PostVisit(Period period) + { + } + + public virtual void PreVisit(ProcedureCall procedureCall) + { + } + + public virtual void PostVisit(ProcedureCall procedureCall) + { + } + + public virtual void PreVisit(ProgramBody programBody) + { + } + + public virtual void PostVisit(ProgramBody programBody) + { + } + + public virtual void PreVisit(ProgramHead programHead) + { + } + + public virtual void PostVisit(ProgramHead programHead) + { + } + + public virtual void PreVisit(ProgramStruct programStruct) + { + } + + public virtual void PostVisit(ProgramStruct programStruct) + { + } + + public virtual void PreVisit(RelationOperator relationOperator) + { + } + + public virtual void PostVisit(RelationOperator relationOperator) + { + } + + public virtual void PreVisit(SimpleExpression simpleExpression) + { + } + + public virtual void PostVisit(SimpleExpression simpleExpression) + { + } + + public virtual void PreVisit(Statement statement) + { + } + + public virtual void PostVisit(Statement statement) + { + } + + public virtual void PreVisit(StatementList statementList) + { + } + + public virtual void PostVisit(StatementList statementList) + { + } + + public virtual void PreVisit(Subprogram subprogram) + { + } + + public virtual void PostVisit(Subprogram subprogram) + { + } + + public virtual void PreVisit(SubprogramBody subprogramBody) + { + } + + public virtual void PostVisit(SubprogramBody subprogramBody) + { + } + + public virtual void PreVisit(SubprogramDeclarations subprogramDeclarations) + { + } + + public virtual void PostVisit(SubprogramDeclarations subprogramDeclarations) + { + } + + public virtual void PreVisit(SubprogramHead subprogramHead) + { + } + + public virtual void PostVisit(SubprogramHead subprogramHead) + { + } + + public virtual void PreVisit(Term term) + { + } + + public virtual void PostVisit(Term term) + { + } + + public virtual void PreVisit(TypeSyntaxNode typeSyntaxNode) + { + } + + public virtual void PostVisit(TypeSyntaxNode typeSyntaxNode) + { + } + + public virtual void PreVisit(ValueParameter valueParameter) + { + } + + public virtual void PostVisit(ValueParameter valueParameter) + { + } + + public virtual void PreVisit(VarDeclaration varDeclaration) + { + } + + public virtual void PostVisit(VarDeclaration varDeclaration) + { + } + + public virtual void PreVisit(VarDeclarations varDeclarations) + { + } + + public virtual void PostVisit(VarDeclarations varDeclarations) + { + } + + public virtual void PreVisit(Variable variable) + { + } + + public virtual void PostVisit(Variable variable) + { + } + + public virtual void PreVisit(VarParameter varParameter) + { + } + + public virtual void PostVisit(VarParameter varParameter) + { + } + + public virtual void PreVisit(TerminatedSyntaxNode terminatedSyntaxNode) + { + } + + public virtual void PostVisit(TerminatedSyntaxNode terminatedSyntaxNode) + { + } +} diff --git a/Canon.Core/Canon.Core.csproj b/Canon.Core/Canon.Core.csproj index 77c284c..b7bd4a6 100644 --- a/Canon.Core/Canon.Core.csproj +++ b/Canon.Core/Canon.Core.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/Canon.Core/LexicalParser/SemanticToken.cs b/Canon.Core/LexicalParser/SemanticToken.cs index 958e8df..db3ba3d 100644 --- a/Canon.Core/LexicalParser/SemanticToken.cs +++ b/Canon.Core/LexicalParser/SemanticToken.cs @@ -160,6 +160,36 @@ public class NumberSemanticToken : SemanticToken public required NumberType NumberType { get; init; } + /// + /// 将数值类型记号识别为整数 + /// + /// 该记号表示的整数 + /// 目标记号不是整数类型 + public int ParseAsInteger() + { + if (NumberType != NumberType.Integer) + { + throw new InvalidOperationException("Target semantic token isn't integer"); + } + + return int.Parse(LiteralValue); + } + + /// + /// 将数值类型记号识别为浮点数 + /// + /// 该记号标识的浮点数 + /// 目标记号不是浮点数类型 + public double ParseAsReal() + { + if (NumberType != NumberType.Real) + { + throw new InvalidOperationException("Target semantic token isn't real"); + } + + return double.Parse(LiteralValue); + } + public override int GetHashCode() { return base.GetHashCode() ^ NumberType.GetHashCode(); diff --git a/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs b/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs new file mode 100644 index 0000000..0b68598 --- /dev/null +++ b/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs @@ -0,0 +1,17 @@ +using Canon.Core.CodeGenerators; +using Canon.Core.SyntaxNodes; + +namespace Canon.Core.SemanticParser; + +public class CCodeGenerateVisitor : TypeCheckVisitor +{ + public CCodeBuilder Builder { get; } = new(); + + public override void PreVisit(ProgramStruct programStruct) + { + base.PreVisit(programStruct); + + Builder.AddString("#include \n"); + Builder.AddString("#include \n"); + } +} diff --git a/Canon.Core/SemanticParser/PascalType.cs b/Canon.Core/SemanticParser/PascalType.cs index fe81049..9edc16b 100644 --- a/Canon.Core/SemanticParser/PascalType.cs +++ b/Canon.Core/SemanticParser/PascalType.cs @@ -44,4 +44,31 @@ public abstract class PascalType : IEquatable { return !a.Equals(b); } + + public static PascalType operator +(PascalType a, PascalType b) + { + if (!IsCalculatable(a) || !IsCalculatable(b)) + { + throw new InvalidOperationException(); + } + + if (a == PascalBasicType.Integer && b == PascalBasicType.Integer) + { + return PascalBasicType.Integer; + } + else + { + return PascalBasicType.Real; + } + } + + /// + /// 是否为可计算的类型 + /// + /// 需要判断的Pascal类型 + /// 是否为可计算的类型 + public static bool IsCalculatable(PascalType pascalType) + { + return pascalType == PascalBasicType.Integer || pascalType == PascalBasicType.Real; + } } diff --git a/Canon.Core/SemanticParser/SymbolTable.cs b/Canon.Core/SemanticParser/SymbolTable.cs index 04fbc53..cb38e95 100644 --- a/Canon.Core/SemanticParser/SymbolTable.cs +++ b/Canon.Core/SemanticParser/SymbolTable.cs @@ -96,6 +96,23 @@ public class SymbolTable return false; } + /// + /// 尝试获得父符号表 + /// + /// 获得的父符号表 + /// 是否存在父符号表 + public bool TryGetParent([NotNullWhen(true)] out SymbolTable? parent) + { + if (_parent is null) + { + parent = null; + return false; + } + + parent = _parent; + return true; + } + private IEnumerable GetParents() { SymbolTable? now = _parent; diff --git a/Canon.Core/SemanticParser/SyntaxTreeTraveller.cs b/Canon.Core/SemanticParser/SyntaxTreeTraveller.cs new file mode 100644 index 0000000..c55efd9 --- /dev/null +++ b/Canon.Core/SemanticParser/SyntaxTreeTraveller.cs @@ -0,0 +1,55 @@ +using Canon.Core.Abstractions; +using Canon.Core.SyntaxNodes; + +namespace Canon.Core.SemanticParser; + +public class SyntaxTreeTraveller +{ + private readonly Stack _stack = []; + private readonly HashSet _visited = []; + + public void Travel(ProgramStruct root, SyntaxNodeVisitor visitor) + { + _stack.Clear(); + _visited.Clear(); + _stack.Push(root); + + while (_stack.Count != 0) + { + SyntaxNodeBase node = _stack.Peek(); + if (!_visited.Contains(node)) + { + node.PreVisit(visitor); + } + + if (node.IsTerminated) + { + node.PostVisit(visitor); + _stack.Pop(); + continue; + } + + NonTerminatedSyntaxNode nonTerminatedNode = node.Convert(); + + if (nonTerminatedNode.Children.Count == 0) + { + node.PostVisit(visitor); + _stack.Pop(); + continue; + } + + if (_visited.Contains(nonTerminatedNode)) + { + node.PostVisit(visitor); + _stack.Pop(); + continue; + } + + _visited.Add(nonTerminatedNode); + foreach (SyntaxNodeBase child in nonTerminatedNode.Children.AsEnumerable().Reverse()) + { + _stack.Push(child); + } + } + } +} diff --git a/Canon.Core/SemanticParser/TypeCheckVisitor.cs b/Canon.Core/SemanticParser/TypeCheckVisitor.cs new file mode 100644 index 0000000..1d326b8 --- /dev/null +++ b/Canon.Core/SemanticParser/TypeCheckVisitor.cs @@ -0,0 +1,374 @@ +using Canon.Core.Abstractions; +using Canon.Core.Enums; +using Canon.Core.LexicalParser; +using Canon.Core.SyntaxNodes; +using Microsoft.Extensions.Logging; + +namespace Canon.Core.SemanticParser; + +public class TypeCheckVisitor(ILogger? logger = null) : SyntaxNodeVisitor +{ + public SymbolTable SymbolTable { get; private set; } = new(); + + public override void PostVisit(ConstValue constValue) + { + base.PostVisit(constValue); + constValue.OnNumberGenerator += (_, e) => + { + switch (e.Token.NumberType) + { + case NumberType.Integer: + constValue.ConstType = PascalBasicType.Integer; + break; + case NumberType.Real: + constValue.ConstType = PascalBasicType.Real; + break; + } + }; + + constValue.OnCharacterGenerator += (_, _) => { constValue.ConstType = PascalBasicType.Character; }; + } + + public override void PostVisit(ConstDeclaration constDeclaration) + { + base.PostVisit(constDeclaration); + (IdentifierSemanticToken token, ConstValue constValue) = constDeclaration.ConstValue; + + bool result = SymbolTable.TryAddSymbol(new Symbol + { + Const = true, SymbolName = token.IdentifierName, SymbolType = constValue.ConstType + }); + + if (!result) + { + logger?.LogError("Identifier '{}' has been declared twice!", token.IdentifierName); + } + } + + public override void PostVisit(Factor factor) + { + base.PostVisit(factor); + + // factor -> num + factor.OnNumberGenerator += (_, e) => + { + switch (e.Token.NumberType) + { + case NumberType.Integer: + factor.FactorType = PascalBasicType.Integer; + break; + case NumberType.Real: + factor.FactorType = PascalBasicType.Real; + break; + } + }; + + // factor -> variable + factor.OnVariableGenerator += (_, e) => + { + if (SymbolTable.TryGetSymbol(e.Variable.Identifier.IdentifierName, out Symbol? symbol)) + { + factor.FactorType = symbol.SymbolType; + } + }; + + // factor -> (expression) + factor.OnParethnesisGenerator += (_, e) => { factor.FactorType = e.Expression.ExprssionType; }; + + // factor -> id (expression_list) + factor.OnProcedureCallGenerator += (_, e) => + { + if (!SymbolTable.TryGetSymbol(e.ProcedureName.IdentifierName, out Symbol? procedure)) + { + logger?.LogError("Procedure '{}' does not define.", e.ProcedureName.IdentifierName); + return; + } + + if (procedure.SymbolType is not PascalFunctionType functionType) + { + logger?.LogError("Identifier '{}' is not a call-able.", procedure.SymbolName); + return; + } + + if (functionType.ReturnType == PascalBasicType.Void) + { + logger?.LogError("Procedure '{}' returns void.", procedure.SymbolName); + return; + } + + factor.FactorType = functionType.ReturnType; + + if (e.Parameters.Expressions.Count != functionType.Parameters.Count) + { + logger?.LogError("Procedure '{}' expects {} parameters but {} provided.", + procedure.SymbolName, + functionType.Parameters.Count, + e.Parameters.Expressions.Count); + return; + } + + foreach ((Expression expression, PascalParameterType parameterType) in e.Parameters.Expressions.Zip( + functionType.Parameters)) + { + if (expression.ExprssionType != parameterType) + { + logger?.LogError(""); + return; + } + } + }; + + // factor -> factor + factor.OnNotGenerator += (_, e) => + { + if (e.Factor.FactorType != PascalBasicType.Boolean) + { + logger?.LogError("The boolean type is expected."); + return; + } + + factor.FactorType = PascalBasicType.Boolean; + }; + + // factor -> uminus factor + factor.OnUminusGenerator += (_, e) => { factor.FactorType = e.Factor.FactorType; }; + } + + public override void PostVisit(Term term) + { + base.PostVisit(term); + + term.OnFactorGenerator += (_, e) => { term.TermType = e.Factor.FactorType; }; + + term.OnMultiplyGenerator += (_, e) => + { + if (PascalType.IsCalculatable(e.Left.TermType) && PascalType.IsCalculatable(e.Right.FactorType)) + { + term.TermType = e.Left.TermType + e.Right.FactorType; + return; + } + + logger?.LogError("Can't calculate"); + }; + } + + public override void PostVisit(SimpleExpression simpleExpression) + { + base.PostVisit(simpleExpression); + + simpleExpression.OnTermGenerator += (_, e) => { simpleExpression.SimpleExpressionType = e.Term.TermType; }; + + simpleExpression.OnAddGenerator += (_, e) => + { + if (PascalType.IsCalculatable(e.Left.SimpleExpressionType) && PascalType.IsCalculatable(e.Right.TermType)) + { + simpleExpression.SimpleExpressionType = e.Left.SimpleExpressionType + e.Right.TermType; + return; + } + + logger?.LogError("Can't calculate"); + }; + } + + public override void PostVisit(Expression expression) + { + base.PostVisit(expression); + + expression.OnSimpleExpressionGenerator += (_, e) => + { + expression.ExprssionType = e.SimpleExpression.SimpleExpressionType; + }; + + expression.OnRelationGenerator += (_, _) => { expression.ExprssionType = PascalBasicType.Boolean; }; + } + + public override void PostVisit(TypeSyntaxNode typeSyntaxNode) + { + base.PostVisit(typeSyntaxNode); + + typeSyntaxNode.OnBasicTypeGenerator += (_, e) => { typeSyntaxNode.PascalType = e.BasicType.PascalType; }; + + typeSyntaxNode.OnArrayTypeGenerator += (_, e) => + { + List periods = e.Period.Periods; + (NumberSemanticToken begin, NumberSemanticToken end) = periods.Last().Range; + + PascalType arrayType = + new PascalArrayType(e.BasicType.PascalType, begin.ParseAsInteger(), end.ParseAsInteger()); + + for (int i = periods.Count - 2; i >= 0; i--) + { + (begin, end) = periods[i].Range; + arrayType = new PascalArrayType(arrayType, begin.ParseAsInteger(), end.ParseAsInteger()); + } + + typeSyntaxNode.PascalType = arrayType; + }; + } + + public override void PreVisit(IdentifierList identifierList) + { + base.PreVisit(identifierList); + + identifierList.OnIdentifierGenerator += (_, e) => + { + e.IdentifierList.IsReference = identifierList.IsReference; + e.IdentifierList.IsProcedure = identifierList.IsProcedure; + }; + } + + public override void PostVisit(IdentifierList identifierList) + { + base.PostVisit(identifierList); + + identifierList.OnTypeGenerator += (_, e) => { identifierList.DefinitionType = e.TypeSyntaxNode.PascalType; }; + + identifierList.OnIdentifierGenerator += (_, e) => + { + identifierList.DefinitionType = e.IdentifierList.DefinitionType; + + Symbol symbol = new() + { + SymbolName = e.IdentifierToken.IdentifierName, + SymbolType = identifierList.DefinitionType, + Reference = identifierList.IsReference + + }; + SymbolTable.TryAddSymbol(symbol); + + if (identifierList.IsProcedure) + { + _parameters!.Add(symbol); + } + }; + } + + public override void PostVisit(VarDeclaration varDeclaration) + { + base.PostVisit(varDeclaration); + + SymbolTable.TryAddSymbol(new Symbol + { + SymbolName = varDeclaration.Token.IdentifierName, + SymbolType = varDeclaration.IdentifierList.DefinitionType + }); + } + + /// + /// 存储定义函数过程中的参数列表 + /// 考虑到不同的ValueParameter + /// ValueParameter的顺序是正确的 + /// 但ValueParameter中的顺序是相反的 + /// 因此设置二维数组存储 + /// + private List? _parameters; + + /// + /// 多个ValueParameter下定义的参数列表 + /// + private readonly List> _valueParameters = []; + + public override void PreVisit(Subprogram subprogram) + { + base.PreVisit(subprogram); + + SymbolTable = SymbolTable.CreateChildTable(); + } + + public override void PostVisit(Subprogram subprogram) + { + base.PostVisit(subprogram); + + if (!SymbolTable.TryGetParent(out SymbolTable? parent)) + { + return; + } + + SymbolTable = parent; + } + + public override void PreVisit(SubprogramHead subprogramHead) + { + base.PreVisit(subprogramHead); + + _parameters = null; + _valueParameters.Clear(); + } + + public override void PostVisit(SubprogramHead subprogramHead) + { + base.PostVisit(subprogramHead); + + // 将当前过程的符号添加到父符号表中 + if (!SymbolTable.TryGetParent(out SymbolTable? parent)) + { + return; + } + + List parameters = []; + + // 正序遍历_valueParameter + // 倒序遍历其中的列表 + foreach (List children in _valueParameters) + { + foreach (Symbol symbol in children.AsEnumerable().Reverse()) + { + parameters.Add(new PascalParameterType(symbol.SymbolType, symbol.Reference)); + } + } + + subprogramHead.OnProcedureGenerator += (_, _) => + { + parent.TryAddSymbol(new Symbol + { + SymbolName = subprogramHead.SubprogramName.IdentifierName, + SymbolType = new PascalFunctionType(parameters, PascalBasicType.Void) + }); + }; + + subprogramHead.OnFunctionGenerator += (_, e) => + { + parent.TryAddSymbol(new Symbol + { + SymbolName = subprogramHead.SubprogramName.IdentifierName, + SymbolType = new PascalFunctionType(parameters, e.ReturnType.PascalType) + }); + }; + } + + public override void PreVisit(VarParameter varParameter) + { + base.PreVisit(varParameter); + + varParameter.ValueParameter.IsReference = true; + } + + public override void PreVisit(ValueParameter valueParameter) + { + base.PreVisit(valueParameter); + + valueParameter.IdentifierList.IsProcedure = true; + _parameters = []; + _valueParameters.Add(_parameters); + if (valueParameter.IsReference) + { + valueParameter.IdentifierList.IsReference = true; + } + } + + public override void PostVisit(ValueParameter valueParameter) + { + base.PostVisit(valueParameter); + + Symbol symbol = new() + { + SymbolName = valueParameter.Token.IdentifierName, + SymbolType = valueParameter.IdentifierList.DefinitionType, + Reference = valueParameter.IsReference + }; + SymbolTable.TryAddSymbol(symbol); + + // 同时添加到参数列表 + _parameters!.Add(symbol); + } +} diff --git a/Canon.Core/SyntaxNodes/AddOperator.cs b/Canon.Core/SyntaxNodes/AddOperator.cs index 4b974d2..b80f4c2 100644 --- a/Canon.Core/SyntaxNodes/AddOperator.cs +++ b/Canon.Core/SyntaxNodes/AddOperator.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -33,4 +34,14 @@ public class AddOperator : NonTerminatedSyntaxNode builder.AddString(" ||"); } } + + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } } diff --git a/Canon.Core/SyntaxNodes/BasicType.cs b/Canon.Core/SyntaxNodes/BasicType.cs index 77899ea..ae36bac 100644 --- a/Canon.Core/SyntaxNodes/BasicType.cs +++ b/Canon.Core/SyntaxNodes/BasicType.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; using Canon.Core.SemanticParser; @@ -9,9 +10,31 @@ public class BasicType : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.BasicType; - public static BasicType Create(List children) + /// + /// BasicType代表的Pascal类型 + /// + /// + public PascalType PascalType { - return new BasicType { Children = children }; + get + { + KeywordType keywordType = Children[0].Convert().Token + .Convert().KeywordType; + + switch (keywordType) + { + case KeywordType.Integer: + return PascalBasicType.Integer; + case KeywordType.Real: + return PascalBasicType.Real; + case KeywordType.Character: + return PascalBasicType.Character; + case KeywordType.Boolean: + return PascalBasicType.Boolean; + } + + throw new InvalidOperationException(); + } } public override void GenerateCCode(CCodeBuilder builder) @@ -36,27 +59,18 @@ public class BasicType : NonTerminatedSyntaxNode } } - /// - ///尝试获取Pascal的基本类型 - /// - /// - public PascalType TryGetPascalType() + public override void PreVisit(SyntaxNodeVisitor visitor) { - var keywordType = Children[0].Convert().Token - .Convert().KeywordType; + visitor.PreVisit(this); + } - switch (keywordType) - { - case KeywordType.Integer: - return PascalBasicType.Integer; - case KeywordType.Real: - return PascalBasicType.Real; - case KeywordType.Boolean: - return PascalBasicType.Boolean; - case KeywordType.Character: - return PascalBasicType.Character; - } + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } - return PascalBasicType.Void; + 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 index 1b77be8..e0137f4 100644 --- a/Canon.Core/SyntaxNodes/CompoundStatement.cs +++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -9,6 +10,16 @@ public class CompoundStatement : NonTerminatedSyntaxNode public IEnumerable Statements => Children[1].Convert().Statements; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index b378348..ff72d2e 100644 --- a/Canon.Core/SyntaxNodes/ConstDeclaration.cs +++ b/Canon.Core/SyntaxNodes/ConstDeclaration.cs @@ -1,4 +1,5 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,16 @@ public class ConstDeclaration : NonTerminatedSyntaxNode /// public (IdentifierSemanticToken, ConstValue) ConstValue => GetConstValue(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static ConstDeclaration Create(List children) { bool isRecursive; diff --git a/Canon.Core/SyntaxNodes/ConstDeclarations.cs b/Canon.Core/SyntaxNodes/ConstDeclarations.cs index ec73030..0253813 100644 --- a/Canon.Core/SyntaxNodes/ConstDeclarations.cs +++ b/Canon.Core/SyntaxNodes/ConstDeclarations.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -13,6 +14,16 @@ public class ConstDeclarations : NonTerminatedSyntaxNode /// public IEnumerable<(IdentifierSemanticToken, ConstValue)> ConstValues => GetConstValues(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static ConstDeclarations Create(List children) { return new ConstDeclarations { Children = children }; diff --git a/Canon.Core/SyntaxNodes/ConstValue.cs b/Canon.Core/SyntaxNodes/ConstValue.cs index 6a45034..a1ea2a9 100644 --- a/Canon.Core/SyntaxNodes/ConstValue.cs +++ b/Canon.Core/SyntaxNodes/ConstValue.cs @@ -1,12 +1,127 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +/// +/// 使用数值产生式事件的事件参数 +/// +public class NumberConstValueEventArgs : EventArgs +{ + /// + /// 是否含有负号 + /// + public bool IsNegative { get; init; } + + /// + /// 数值记号 + /// + public required NumberSemanticToken Token { get; init; } +} + +/// +/// 使用字符产生式事件的事件参数 +/// +public class CharacterConstValueEventArgs : EventArgs +{ + /// + /// 字符记号 + /// + public required CharacterSemanticToken Token { get; init; } +} + public class ConstValue : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ConstValue; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseGeneratorEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseGeneratorEvent(); + } + + private PascalType? _constType; + + /// + /// 该ConstValue代表的类型 + /// + /// 尚未分析该类型 + public PascalType ConstType + { + get + { + if (_constType is null) + { + throw new InvalidOperationException("ConstType has not been set"); + } + + return _constType; + } + set + { + _constType = value; + } + } + + /// + /// 使用数值产生式的事件 + /// + public event EventHandler? OnNumberGenerator; + + /// + /// 使用字符产生式的事件 + /// + public event EventHandler? OnCharacterGenerator; + + private void RaiseGeneratorEvent() + { + if (Children.Count == 2) + { + OperatorSemanticToken operatorSemanticToken = Children[0].Convert().Token + .Convert(); + NumberSemanticToken numberSemanticToken = Children[1].Convert().Token + .Convert(); + + OnNumberGenerator?.Invoke(this, new NumberConstValueEventArgs + { + Token = numberSemanticToken, + IsNegative = operatorSemanticToken.OperatorType == OperatorType.Minus + }); + + return; + } + + SemanticToken token = Children[0].Convert().Token; + + if (token.TokenType == SemanticTokenType.Number) + { + OnNumberGenerator?.Invoke(this, + new NumberConstValueEventArgs + { + Token = token.Convert() + }); + } + else + { + OnCharacterGenerator?.Invoke(this, new CharacterConstValueEventArgs + { + Token = token.Convert() + }); + } + + OnNumberGenerator = null; + OnCharacterGenerator = null; + } + 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 index ffde0c4..d0b748f 100644 --- a/Canon.Core/SyntaxNodes/ElsePart.cs +++ b/Canon.Core/SyntaxNodes/ElsePart.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -7,6 +8,16 @@ public class ElsePart : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ElsePart; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index cd5fb1d..d334946 100644 --- a/Canon.Core/SyntaxNodes/Expression.cs +++ b/Canon.Core/SyntaxNodes/Expression.cs @@ -1,17 +1,97 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnSimpleExpressionGeneratorEventArgs : EventArgs +{ + public required SimpleExpression SimpleExpression { get; init; } +} + +public class OnRelationGeneratorEventArgs : EventArgs +{ + public required SimpleExpression Left { get; init; } + + public required RelationOperator Operator { get; init; } + + public required SimpleExpression Right { get; init; } +} + public class Expression : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Expression; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + /// + /// 直接赋值产生式的事件 + /// + public event EventHandler? OnSimpleExpressionGenerator; + + /// + /// 关系产生式的事件 + /// + public event EventHandler? OnRelationGenerator; + + private PascalType? _expressionType; + + public PascalType ExprssionType + { + get + { + if (_expressionType is null) + { + throw new InvalidOperationException(); + } + + return _expressionType; + } + set + { + _expressionType = value; + } + } + public static Expression Create(List children) { return new Expression { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 1) + { + OnSimpleExpressionGenerator?.Invoke(this, new OnSimpleExpressionGeneratorEventArgs + { + SimpleExpression = Children[0].Convert() + }); + } + else + { + OnRelationGenerator?.Invoke(this, new OnRelationGeneratorEventArgs + { + Left = Children[0].Convert(), + Operator = Children[1].Convert(), + Right = Children[2].Convert() + }); + } + + OnSimpleExpressionGenerator = null; + OnRelationGenerator = null; + } + public override void GenerateCCode(CCodeBuilder builder) { foreach (var child in Children) diff --git a/Canon.Core/SyntaxNodes/ExpressionList.cs b/Canon.Core/SyntaxNodes/ExpressionList.cs index eed197b..59ae5d6 100644 --- a/Canon.Core/SyntaxNodes/ExpressionList.cs +++ b/Canon.Core/SyntaxNodes/ExpressionList.cs @@ -1,4 +1,4 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -7,67 +7,39 @@ public class ExpressionList : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ExpressionList; - public bool IsRecursive { get; private init; } - /// - /// 声明的表达式列表 + /// 子表达式列表 /// - public IEnumerable Expressions => GetExpressions(); + public List Expressions { get; } = []; + + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } public static ExpressionList Create(List children) { - bool isRecursive; + ExpressionList result = new() { Children = children }; if (children.Count == 1) { - isRecursive = false; + result.Expressions.Add(children[0].Convert()); } 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) + foreach (Expression expression in children[0].Convert().Expressions) { - yield return list.Children[2].Convert(); - list = list.Children[0].Convert(); + result.Expressions.Add(expression); } - else - { - yield return list.Children[0].Convert(); - break; - } - } - } - public override void GenerateCCode(CCodeBuilder builder) - { - //用逗号分隔输出的expression - using var enumerator = Expressions.GetEnumerator(); - - if (enumerator.MoveNext()) - { - enumerator.Current.GenerateCCode(builder); - } - - while (enumerator.MoveNext()) - { - builder.AddString(", "); - enumerator.Current.GenerateCCode(builder); + result.Expressions.Add(children[2].Convert()); } + return result; } } diff --git a/Canon.Core/SyntaxNodes/Factor.cs b/Canon.Core/SyntaxNodes/Factor.cs index da7e977..b015f84 100644 --- a/Canon.Core/SyntaxNodes/Factor.cs +++ b/Canon.Core/SyntaxNodes/Factor.cs @@ -1,18 +1,173 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnNumberGeneratorEventArgs : EventArgs +{ + public required NumberSemanticToken Token { get; init; } +} + +public class OnVariableGeneratorEventArgs : EventArgs +{ + public required Variable Variable { get; init; } +} + +public class OnParethnesisGeneratorEventArgs : EventArgs +{ + public required Expression Expression { get; init; } +} + +public class OnProcedureCallGeneratorEventArgs : EventArgs +{ + public required IdentifierSemanticToken ProcedureName { get; init; } + + public required ExpressionList Parameters { get; init; } +} + +public class OnNotGeneratorEventArgs : EventArgs +{ + public required Factor Factor { get; init; } +} + +public class OnUminusGeneratorEventArgs : EventArgs +{ + public required Factor Factor { get; init; } +} + public class Factor : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Factor; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + /// + /// 使用数值产生式的事件 + /// + public event EventHandler? OnNumberGenerator; + + /// + /// 使用括号产生式的事件 + /// + public event EventHandler? OnParethnesisGenerator; + + /// + /// 使用变量产生式的事件 + /// + public event EventHandler? OnVariableGenerator; + + /// + /// 使用过程调用产生式的事件 + /// + public event EventHandler? OnProcedureCallGenerator; + + /// + /// 使用否定产生式的事件 + /// + public event EventHandler? OnNotGenerator; + + /// + /// 使用负号产生式的事件 + /// + public event EventHandler? OnUminusGenerator; + + private PascalType? _factorType; + + public PascalType FactorType + { + get + { + if (_factorType is null) + { + throw new InvalidOperationException(); + } + + return _factorType; + } + set + { + _factorType = value; + } + } + public static Factor Create(List children) { return new Factor { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 1) + { + //factor -> num + if (Children[0].IsTerminated) + { + SemanticToken token = Children[0].Convert().Token; + OnNumberGenerator?.Invoke(this, + new OnNumberGeneratorEventArgs { Token = token.Convert() }); + } + // factor -> variable + else + { + OnVariableGenerator?.Invoke(this, + new OnVariableGeneratorEventArgs { Variable = Children[0].Convert() }); + } + } + //factor -> ( expression ) + else if (Children.Count == 3) + { + OnParethnesisGenerator?.Invoke(this, + new OnParethnesisGeneratorEventArgs { Expression = Children[1].Convert() }); + } + //factor -> id ( expression ) + else if (Children.Count == 4) + { + OnProcedureCallGenerator?.Invoke(this, + new OnProcedureCallGeneratorEventArgs + { + ProcedureName = + Children[0].Convert().Token.Convert(), + Parameters = Children[2].Convert() + }); + } + else + { + SemanticToken token = Children[0].Convert().Token; + Factor factor = Children[1].Convert(); + + if (token.TokenType == SemanticTokenType.Keyword) + { + // factor -> not factor + OnNotGenerator?.Invoke(this, new OnNotGeneratorEventArgs { Factor = factor }); + } + else + { + // factor -> uminus factor + OnUminusGenerator?.Invoke(this, new OnUminusGeneratorEventArgs { Factor = factor }); + } + } + + OnNumberGenerator = null; + OnVariableGenerator = null; + OnParethnesisGenerator = null; + OnProcedureCallGenerator = null; + OnNotGenerator = null; + OnUminusGenerator = null; + } + public override void GenerateCCode(CCodeBuilder builder) { if (Children.Count == 1) @@ -42,14 +197,15 @@ public class Factor : NonTerminatedSyntaxNode //factor -> id ( expression ) else if (Children.Count == 4) { - builder.AddString(" " + Children[0].Convert().Token. - Convert().IdentifierName); + builder.AddString(" " + Children[0].Convert().Token.Convert() + .IdentifierName); builder.AddString("("); Children[2].GenerateCCode(builder); builder.AddString(")"); } else - { //factor -> not factor + { + //factor -> not factor builder.AddString(" ("); if (Children[0].Convert().Token.TokenType == SemanticTokenType.Keyword) { diff --git a/Canon.Core/SyntaxNodes/FormalParameter.cs b/Canon.Core/SyntaxNodes/FormalParameter.cs index 59b04b6..6d02c12 100644 --- a/Canon.Core/SyntaxNodes/FormalParameter.cs +++ b/Canon.Core/SyntaxNodes/FormalParameter.cs @@ -1,4 +1,5 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -6,26 +7,18 @@ public class FormalParameter : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.FormalParameter; - /// - /// 声明的参数列表 - /// - public IEnumerable Parameters => GetParameters(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } 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 index e67806a..86d46a2 100644 --- a/Canon.Core/SyntaxNodes/IdentifierList.cs +++ b/Canon.Core/SyntaxNodes/IdentifierList.cs @@ -1,75 +1,97 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnIdentifierGeneratorEventArgs : EventArgs +{ + public required IdentifierSemanticToken IdentifierToken { get; init; } + + public required IdentifierList IdentifierList { get; init; } +} + +public class OnTypeGeneratorEventArgs : EventArgs +{ + public required TypeSyntaxNode TypeSyntaxNode { get; init; } +} + public class IdentifierList : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.IdentifierList; - /// - /// 是否含有递归定义 - /// - public bool IsRecursive { get; private init; } + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + private PascalType? _definitionType; /// - /// 声明的标识符列表 + /// IdentifierList中定义的类型 /// - public IEnumerable Identifiers => GetIdentifiers(); + /// 尚未确定定义的类型 + public PascalType DefinitionType + { + get + { + if (_definitionType is null) + { + throw new InvalidOperationException(); + } + + return _definitionType; + } + set + { + _definitionType = value; + } + } + + /// + /// 是否为参数中的引用参数 + /// + public bool IsReference { get; set; } + + /// + /// 是否在过程定义中使用 + /// + public bool IsProcedure { get; set; } + + public event EventHandler? OnIdentifierGenerator; + + public event EventHandler? OnTypeGenerator; public static IdentifierList Create(List children) { - bool isRecursive; + return new IdentifierList { Children = children }; + } - if (children.Count == 2) + private void RaiseEvent() + { + if (Children.Count == 2) { - isRecursive = false; - } - else if (children.Count == 3) - { - isRecursive = true; + OnTypeGenerator?.Invoke(this, + new OnTypeGeneratorEventArgs { TypeSyntaxNode = Children[1].Convert() }); } else { - throw new InvalidOperationException(); - } - - return new IdentifierList { IsRecursive = isRecursive, Children = children }; - } - - private IEnumerable GetIdentifiers() - { - IdentifierList identifier = this; - - while (true) - { - if (identifier.IsRecursive) + OnIdentifierGenerator?.Invoke(this, new OnIdentifierGeneratorEventArgs { - yield return (IdentifierSemanticToken)identifier.Children[2].Convert().Token; - identifier = identifier.Children[0].Convert(); - } - else - { - yield return (IdentifierSemanticToken)identifier.Children[0].Convert().Token; - break; - } - } - } - - public override void GenerateCCode(CCodeBuilder builder) - { - //用逗号分隔输出的expression - using var enumerator = Identifiers.Reverse().GetEnumerator(); - - if (enumerator.MoveNext()) - { - builder.AddString(" " + enumerator.Current.IdentifierName); + IdentifierToken = Children[1].Convert().Token.Convert(), + IdentifierList = Children[2].Convert() + }); } - while (enumerator.MoveNext()) - { - builder.AddString(", " + enumerator.Current.IdentifierName); - } + OnTypeGenerator = null; + OnIdentifierGenerator = null; } } diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs index d2619b9..cf4cc3d 100644 --- a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs +++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs @@ -1,54 +1,46 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; -using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnParameterGeneratorEventArgs : EventArgs +{ + public required ExpressionList Parameters { get; init; } +} + public class IdentifierVarPart : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.IdVarPart; - /// - /// 是否声明了索引部分 - /// - public bool Exist { get; private init; } - - /// - /// 索引中的位置声明 - /// - public IEnumerable Positions => GetPositions(); - - private IEnumerable GetPositions() + public override void PreVisit(SyntaxNodeVisitor visitor) { - if (!Exist) - { - yield break; - } - - foreach (Expression expression in Children[1].Convert().Expressions) - { - yield return expression; - } + visitor.PreVisit(this); + RaiseEvent(); } + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + public event EventHandler? OnParameterGenerator; + 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 }; + return new IdentifierVarPart { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 3) + { + OnParameterGenerator?.Invoke(this, new OnParameterGeneratorEventArgs + { + Parameters = Children[1].Convert() + }); + } + + OnParameterGenerator = null; + } } diff --git a/Canon.Core/SyntaxNodes/MultiplyOperator.cs b/Canon.Core/SyntaxNodes/MultiplyOperator.cs index dcc4b1e..e5fc726 100644 --- a/Canon.Core/SyntaxNodes/MultiplyOperator.cs +++ b/Canon.Core/SyntaxNodes/MultiplyOperator.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -8,6 +9,16 @@ public class MultiplyOperator : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static MultiplyOperator Create(List children) { return new MultiplyOperator { Children = children }; @@ -45,6 +56,5 @@ public class MultiplyOperator : NonTerminatedSyntaxNode builder.AddString(" /"); } } - } } diff --git a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs index 56422fa..be2ce28 100644 --- a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs @@ -1,4 +1,5 @@ using System.Collections; +using Canon.Core.Abstractions; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -9,6 +10,8 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable Children { get; init; } public IEnumerator GetEnumerator() diff --git a/Canon.Core/SyntaxNodes/Parameter.cs b/Canon.Core/SyntaxNodes/Parameter.cs index 1515ada..f4c4acc 100644 --- a/Canon.Core/SyntaxNodes/Parameter.cs +++ b/Canon.Core/SyntaxNodes/Parameter.cs @@ -1,4 +1,5 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,16 @@ public class Parameter : NonTerminatedSyntaxNode public ValueParameter ValueParameter => IsVar ? Children[0].Convert().ValueParameter : Children[0].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static Parameter Create(List children) { NonTerminatedSyntaxNode node = children[0].Convert(); diff --git a/Canon.Core/SyntaxNodes/ParameterList.cs b/Canon.Core/SyntaxNodes/ParameterList.cs index ae7447f..1d08df3 100644 --- a/Canon.Core/SyntaxNodes/ParameterList.cs +++ b/Canon.Core/SyntaxNodes/ParameterList.cs @@ -1,4 +1,5 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -13,6 +14,16 @@ public class ParameterList : NonTerminatedSyntaxNode /// public IEnumerable Parameters => GetParameters(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static ParameterList Create(List children) { bool isRecursive; diff --git a/Canon.Core/SyntaxNodes/Period.cs b/Canon.Core/SyntaxNodes/Period.cs index 6ddbdf2..0c40231 100644 --- a/Canon.Core/SyntaxNodes/Period.cs +++ b/Canon.Core/SyntaxNodes/Period.cs @@ -1,4 +1,4 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -8,63 +8,61 @@ public class Period : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Period; - public bool IsRecursive { get; private init; } + /// + /// 所有定义的Period + /// + public List Periods { get; } = []; /// - /// 数组上下界列表 + /// 数组的开始下标和结束下标 /// - public IEnumerable<(NumberSemanticToken, NumberSemanticToken)> Ranges => GetRanges(); - - public static Period Create(List children) + public (NumberSemanticToken, NumberSemanticToken) Range { - bool isRecursive; - - if (children.Count == 3) + get { - 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) + if (Children.Count == 3) { - yield return period.GetRange(); - period = period.Children[0].Convert(); + return (Children[0].Convert().Token.Convert(), + Children[2].Convert().Token.Convert()); } else { - yield return period.GetRange(); - break; + return (Children[2].Convert().Token.Convert(), + Children[4].Convert().Token.Convert()); } } } + + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + + public static Period Create(List children) + { + Period result = new() { Children = children }; + + if (children.Count == 3) + { + result.Periods.Add(result); + } + else + { + Period child = children[0].Convert(); + + foreach (Period period in child.Periods) + { + result.Periods.Add(period); + } + + result.Periods.Add(result); + } + + return result; + } } diff --git a/Canon.Core/SyntaxNodes/ProcedureCall.cs b/Canon.Core/SyntaxNodes/ProcedureCall.cs index 685b586..4136f42 100644 --- a/Canon.Core/SyntaxNodes/ProcedureCall.cs +++ b/Canon.Core/SyntaxNodes/ProcedureCall.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -13,6 +14,16 @@ public class ProcedureCall : NonTerminatedSyntaxNode public IEnumerable Arguments => GetArguments(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static ProcedureCall Create(List children) { return new ProcedureCall { Children = children }; diff --git a/Canon.Core/SyntaxNodes/ProgramBody.cs b/Canon.Core/SyntaxNodes/ProgramBody.cs index ad576e0..97d4ece 100644 --- a/Canon.Core/SyntaxNodes/ProgramBody.cs +++ b/Canon.Core/SyntaxNodes/ProgramBody.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -27,6 +28,16 @@ public class ProgramBody : NonTerminatedSyntaxNode /// public CompoundStatement CompoundStatement => Children[3].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index ffd02e4..f663b1d 100644 --- a/Canon.Core/SyntaxNodes/ProgramHead.cs +++ b/Canon.Core/SyntaxNodes/ProgramHead.cs @@ -1,4 +1,5 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -13,28 +14,18 @@ public class ProgramHead : NonTerminatedSyntaxNode public IdentifierSemanticToken ProgramName => (IdentifierSemanticToken)Children[1].Convert().Token; - /// - /// 暂时意义不明的标识符列表 - /// https://wiki.freepascal.org/Program_Structure/zh_CN - /// TODO: 查阅资料 - /// - public IEnumerable FileList => GetFileList(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } 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 index 4793e27..da98944 100644 --- a/Canon.Core/SyntaxNodes/ProgramStruct.cs +++ b/Canon.Core/SyntaxNodes/ProgramStruct.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,16 @@ public class ProgramStruct : NonTerminatedSyntaxNode /// public ProgramBody Body => Children[2].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index 382049f..067d5bc 100644 --- a/Canon.Core/SyntaxNodes/RelationOperator.cs +++ b/Canon.Core/SyntaxNodes/RelationOperator.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; @@ -8,6 +9,16 @@ public class RelationOperator : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.RelationOperator; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static RelationOperator Create(List children) { return new RelationOperator { Children = children }; @@ -15,8 +26,8 @@ public class RelationOperator : NonTerminatedSyntaxNode public override void GenerateCCode(CCodeBuilder builder) { - var operatorType = Children[0].Convert().Token. - Convert().OperatorType; + var operatorType = Children[0].Convert().Token.Convert() + .OperatorType; switch (operatorType) { case OperatorType.Equal: diff --git a/Canon.Core/SyntaxNodes/SimpleExpression.cs b/Canon.Core/SyntaxNodes/SimpleExpression.cs index 635c56c..72690d6 100644 --- a/Canon.Core/SyntaxNodes/SimpleExpression.cs +++ b/Canon.Core/SyntaxNodes/SimpleExpression.cs @@ -1,17 +1,97 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnTermGeneratorEventArgs : EventArgs +{ + public required Term Term { get; init; } +} + +public class OnAddGeneratorEventArgs : EventArgs +{ + public required SimpleExpression Left { get; init; } + + public required AddOperator Operator { get; init; } + + public required Term Right { get; init; } +} + public class SimpleExpression : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.SimpleExpression; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + /// + /// 直接赋值产生式的事件 + /// + public event EventHandler? OnTermGenerator; + + /// + /// 加法产生式的事件 + /// + public event EventHandler? OnAddGenerator; + + private PascalType? _simpleExpressionType; + + public PascalType SimpleExpressionType + { + get + { + if (_simpleExpressionType is null) + { + throw new InvalidOperationException(); + } + + return _simpleExpressionType; + } + set + { + _simpleExpressionType = value; + } + } + public static SimpleExpression Create(List children) { return new SimpleExpression { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 1) + { + OnTermGenerator?.Invoke(this, new OnTermGeneratorEventArgs + { + Term = Children[0].Convert() + }); + } + else + { + OnAddGenerator?.Invoke(this, new OnAddGeneratorEventArgs + { + Left = Children[0].Convert(), + Operator = Children[1].Convert(), + Right = Children[2].Convert() + }); + } + + OnTermGenerator = null; + OnAddGenerator = null; + } + public override void GenerateCCode(CCodeBuilder builder) { foreach (var child in Children) diff --git a/Canon.Core/SyntaxNodes/Statement.cs b/Canon.Core/SyntaxNodes/Statement.cs index 4f085b6..023b4bc 100644 --- a/Canon.Core/SyntaxNodes/Statement.cs +++ b/Canon.Core/SyntaxNodes/Statement.cs @@ -1,24 +1,134 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; +public class OnAssignGeneratorEventArgs : EventArgs +{ + public required Variable Variable { get; init; } + + public required Expression Expression { get; init; } +} + +public class OnReturnGeneratorEventArgs : EventArgs +{ + public required IdentifierSemanticToken FunctionName { get; set; } + + public required Expression Expression { get; init; } +} + +public class OnIfGeneratorEventArgs : EventArgs +{ + public required Expression Condition { get; init; } + + public required Statement Sentence { get; init; } + + public required ElsePart ElseSentence { get; init; } +} + +public class OnForGeneratorEventArgs : EventArgs +{ + public required IdentifierSemanticToken Iterator { get; init; } + + public required Expression Begin { get; init; } + + public required Expression End { get; init; } + + public required Statement Sentence { get; init; } +} + public class Statement : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Statement; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + /// + /// 使用赋值产生式的事件 + /// + public event EventHandler? OnAssignGenerator; + + /// + /// 使用返回产生式的事件 + /// + public event EventHandler? OnReturnGenerator; + + /// + /// 使用If产生式的事件 + /// + public event EventHandler? OnIfGenerator; + + /// + /// 使用For产生式的事件 + /// + public event EventHandler? OnForGenerator; + public static Statement Create(List children) { return new Statement { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 2) + { + if (Children[0].IsTerminated) + { + OnReturnGenerator?.Invoke(this, new OnReturnGeneratorEventArgs + { + FunctionName = Children[0].Convert().Token.Convert(), + Expression = Children[2].Convert() + }); + } + else + { + OnAssignGenerator?.Invoke(this, new OnAssignGeneratorEventArgs + { + Variable = Children[0].Convert(), + Expression = Children[2].Convert() + }); + } + } + else if (Children.Count == 5) + { + OnIfGenerator?.Invoke(this, new OnIfGeneratorEventArgs + { + Condition = Children[1].Convert(), + Sentence = Children[3].Convert(), + ElseSentence = Children[4].Convert() + }); + } + else if (Children.Count == 8) + { + OnForGenerator?.Invoke(this, new OnForGeneratorEventArgs + { + Iterator = Children[1].Convert().Token.Convert(), + Begin = Children[3].Convert(), + End = Children[5].Convert(), + Sentence = Children[7].Convert() + }); + } + } + public override void GenerateCCode(CCodeBuilder builder) { if (Children.Count == 0) { return; } + // statement -> procedureCall | compoundStatement if (Children.Count == 1) { diff --git a/Canon.Core/SyntaxNodes/StatementList.cs b/Canon.Core/SyntaxNodes/StatementList.cs index ff020a6..a50323a 100644 --- a/Canon.Core/SyntaxNodes/StatementList.cs +++ b/Canon.Core/SyntaxNodes/StatementList.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -11,6 +12,16 @@ public class StatementList : NonTerminatedSyntaxNode public IEnumerable Statements => GetStatements(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static StatementList Create(List children) { bool isRecursive; diff --git a/Canon.Core/SyntaxNodes/Subprogram.cs b/Canon.Core/SyntaxNodes/Subprogram.cs index 12728c6..5e2d2d1 100644 --- a/Canon.Core/SyntaxNodes/Subprogram.cs +++ b/Canon.Core/SyntaxNodes/Subprogram.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,16 @@ public class Subprogram : NonTerminatedSyntaxNode /// public SubprogramBody Body => Children[2].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index 19503ae..60e5ee4 100644 --- a/Canon.Core/SyntaxNodes/SubprogramBody.cs +++ b/Canon.Core/SyntaxNodes/SubprogramBody.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -22,6 +23,16 @@ public class SubprogramBody : NonTerminatedSyntaxNode /// public CompoundStatement CompoundStatement => Children[2].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index 7fe6cdc..bcdd455 100644 --- a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs +++ b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -12,6 +13,16 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode /// public IEnumerable Subprograms => GetSubprograms(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static SubprogramDeclarations Create(List children) { return new SubprogramDeclarations { Children = children }; diff --git a/Canon.Core/SyntaxNodes/SubprogramHead.cs b/Canon.Core/SyntaxNodes/SubprogramHead.cs index ad720a7..793356e 100644 --- a/Canon.Core/SyntaxNodes/SubprogramHead.cs +++ b/Canon.Core/SyntaxNodes/SubprogramHead.cs @@ -1,25 +1,48 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; +public class OnProcedureGeneratorEventArgs : EventArgs; + +public class OnFunctionGeneratorEventArgs : EventArgs +{ + public required BasicType ReturnType { get; init; } +} + public class SubprogramHead : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.SubprogramHead; + /// + /// 过程定义还是函数定义 + /// public bool IsProcedure { get; private init; } /// /// 子程序的名称 /// public IdentifierSemanticToken SubprogramName => - (IdentifierSemanticToken)Children[1].Convert().Token; + Children[1].Convert().Token.Convert(); - /// - /// 子程序的参数 - /// - public IEnumerable Parameters => Children[2].Convert().Parameters; + public FormalParameter Parameters => Children[2].Convert(); + + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + public event EventHandler? OnProcedureGenerator; + + public event EventHandler? OnFunctionGenerator; public static SubprogramHead Create(List children) { @@ -44,27 +67,19 @@ public class SubprogramHead : NonTerminatedSyntaxNode return new SubprogramHead { Children = children, IsProcedure = isProcedure }; } - public override void GenerateCCode(CCodeBuilder builder) + private void RaiseEvent() { - //可能要用到符号表 if (IsProcedure) { - builder.AddString("void "); + OnProcedureGenerator?.Invoke(this, new OnProcedureGeneratorEventArgs()); } else { - //返回类型暂时未知 - builder.AddString("int "); + OnFunctionGenerator?.Invoke(this, + new OnFunctionGeneratorEventArgs { ReturnType = Children[4].Convert() }); } - builder.AddString(SubprogramName.LiteralValue); - - builder.AddString("("); - foreach (var param in Parameters) - { - - } - - builder.AddString(")"); + OnProcedureGenerator = null; + OnFunctionGenerator = null; } } diff --git a/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs b/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs index 0af0507..bf589e7 100644 --- a/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs +++ b/Canon.Core/SyntaxNodes/SyntaxNodeBase.cs @@ -9,6 +9,10 @@ public abstract class SyntaxNodeBase : ICCodeGenerator { public abstract bool IsTerminated { get; } + public abstract void PreVisit(SyntaxNodeVisitor visitor); + + public abstract void PostVisit(SyntaxNodeVisitor visitor); + public T Convert() where T : SyntaxNodeBase { T? result = this as T; @@ -26,7 +30,6 @@ public abstract class SyntaxNodeBase : ICCodeGenerator /// public virtual void GenerateCCode(CCodeBuilder builder) { - } public override string ToString() diff --git a/Canon.Core/SyntaxNodes/Term.cs b/Canon.Core/SyntaxNodes/Term.cs index 914c6d2..a6274b6 100644 --- a/Canon.Core/SyntaxNodes/Term.cs +++ b/Canon.Core/SyntaxNodes/Term.cs @@ -1,17 +1,97 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnFactorGeneratorEventArgs : EventArgs +{ + public required Factor Factor { get; init; } +} + +public class OnMultiplyGeneratorEventArgs : EventArgs +{ + public required Term Left { get; init; } + + public required MultiplyOperator Operator { get; init; } + + public required Factor Right { get; init; } +} + public class Term : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Term; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + /// + /// 直接赋值产生式的事件 + /// + public event EventHandler? OnFactorGenerator; + + /// + /// 乘法产生式的事件 + /// + public event EventHandler? OnMultiplyGenerator; + + private PascalType? _termType; + + public PascalType TermType + { + get + { + if (_termType is null) + { + throw new InvalidOperationException(); + } + + return _termType; + } + set + { + _termType = value; + } + } + public static Term Create(List children) { return new Term { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 1) + { + OnFactorGenerator?.Invoke(this, new OnFactorGeneratorEventArgs + { + Factor = Children[0].Convert() + }); + } + else + { + OnMultiplyGenerator?.Invoke(this, new OnMultiplyGeneratorEventArgs + { + Left = Children[0].Convert(), + Operator = Children[1].Convert(), + Right = Children[2].Convert() + }); + } + + OnFactorGenerator = null; + OnMultiplyGenerator = null; + } + public override void GenerateCCode(CCodeBuilder builder) { foreach (var child in Children) diff --git a/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs index f996a8a..e26bb57 100644 --- a/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/TerminatedSyntaxNode.cs @@ -1,4 +1,5 @@ -using Canon.Core.LexicalParser; +using Canon.Core.Abstractions; +using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -6,6 +7,16 @@ public class TerminatedSyntaxNode : SyntaxNodeBase { public override bool IsTerminated => true; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public required SemanticToken Token { get; init; } public override string ToString() diff --git a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs index b1fec76..e1e2c1c 100644 --- a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs @@ -1,17 +1,92 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; +public class OnBasicTypeGeneratorEventArgs : EventArgs +{ + public required BasicType BasicType { get; init; } +} + +public class OnArrayTypeGeneratorEventArgs : EventArgs +{ + public required Period Period { get; init; } + + public required BasicType BasicType { get; init; } +} + public class TypeSyntaxNode : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Type; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + RaiseEvent(); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + RaiseEvent(); + } + + public event EventHandler? OnBasicTypeGenerator; + + public event EventHandler? OnArrayTypeGenerator; + + private PascalType? _pascalType; + + /// + /// Type节点制定的类型 + /// + /// + public PascalType PascalType + { + get + { + if (_pascalType is null) + { + throw new InvalidOperationException(); + } + + return _pascalType; + } + set + { + _pascalType = value; + } + } + public static TypeSyntaxNode Create(List children) { return new TypeSyntaxNode { Children = children }; } + private void RaiseEvent() + { + if (Children.Count == 1) + { + OnBasicTypeGenerator?.Invoke(this, new OnBasicTypeGeneratorEventArgs + { + BasicType = Children[0].Convert() + }); + } + else + { + OnArrayTypeGenerator?.Invoke(this, new OnArrayTypeGeneratorEventArgs + { + Period = Children[2].Convert(), + BasicType = Children[5].Convert() + }); + } + + OnBasicTypeGenerator = null; + OnArrayTypeGenerator = null; + } + public override void GenerateCCode(CCodeBuilder builder) { //type -> basic_type @@ -22,7 +97,6 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode //type -> array [ period ]of basic_type else { - } } } diff --git a/Canon.Core/SyntaxNodes/ValueParameter.cs b/Canon.Core/SyntaxNodes/ValueParameter.cs index 312d196..23cc84d 100644 --- a/Canon.Core/SyntaxNodes/ValueParameter.cs +++ b/Canon.Core/SyntaxNodes/ValueParameter.cs @@ -1,5 +1,7 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -8,23 +10,27 @@ public class ValueParameter : NonTerminatedSyntaxNode public override NonTerminatorType Type => NonTerminatorType.ValueParameter; /// - /// 声明的变量列表 + /// 是否为参数中的引用参数 /// - // public IdentifierList IdentifierList => Children[1].Convert(); + public bool IsReference { get; set; } - /// - /// 声明的变量类型 - /// - // public BasicType BasicType => Children[2].Convert(); + public IdentifierSemanticToken Token => + Children[0].Convert().Token.Convert(); + + public IdentifierList IdentifierList => Children[1].Convert(); + + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } public static ValueParameter Create(List children) { return new ValueParameter { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //可能涉及符号表访问 - builder.AddString("valueParam "); - } } diff --git a/Canon.Core/SyntaxNodes/VarDeclaration.cs b/Canon.Core/SyntaxNodes/VarDeclaration.cs index 6eda29a..cebc754 100644 --- a/Canon.Core/SyntaxNodes/VarDeclaration.cs +++ b/Canon.Core/SyntaxNodes/VarDeclaration.cs @@ -1,4 +1,6 @@ -using Canon.Core.Enums; +using Canon.Core.Abstractions; +using Canon.Core.Enums; +using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -6,42 +8,39 @@ public class VarDeclaration : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.VarDeclaration; - // public bool IsRecursive { get; private init; } + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } - // /// - // /// 声明的变量 - // /// - // public (IdentifierList, TypeSyntaxNode) Variable => GetVariable(); + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } - // private (IdentifierList, TypeSyntaxNode) GetVariable() - // { - // if (IsRecursive) - // { - // return (Children[2].Convert(), Children[4].Convert()); - // } - // else - // { - // return (Children[0].Convert(), Children[2].Convert()); - // } - // } + public required IdentifierSemanticToken Token { get; init; } + + public required IdentifierList IdentifierList { get; init; } public static VarDeclaration Create(List children) { - /*bool isRecursive; - if (children.Count == 2) { - isRecursive = false; - } - else if (children.Count == 4) - { - isRecursive = true; + return new VarDeclaration + { + Children = children, + Token = children[0].Convert().Token.Convert(), + IdentifierList = children[1].Convert() + }; } else { - throw new InvalidOperationException(); - }*/ - - return new VarDeclaration {Children = children}; + return new VarDeclaration + { + Children = children, + Token = children[2].Convert().Token.Convert(), + IdentifierList = children[3].Convert() + }; + } } } diff --git a/Canon.Core/SyntaxNodes/VarDeclarations.cs b/Canon.Core/SyntaxNodes/VarDeclarations.cs index bf2951e..e9af74d 100644 --- a/Canon.Core/SyntaxNodes/VarDeclarations.cs +++ b/Canon.Core/SyntaxNodes/VarDeclarations.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.SemanticParser; @@ -13,6 +14,16 @@ public class VarDeclarations : NonTerminatedSyntaxNode /// // public IEnumerable<(IdentifierList, TypeSyntaxNode)> Variables => EnumerateVariables(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static VarDeclarations Create(List children) { return new VarDeclarations { Children = children }; diff --git a/Canon.Core/SyntaxNodes/VarParameter.cs b/Canon.Core/SyntaxNodes/VarParameter.cs index 08c700e..d22872b 100644 --- a/Canon.Core/SyntaxNodes/VarParameter.cs +++ b/Canon.Core/SyntaxNodes/VarParameter.cs @@ -1,4 +1,5 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; +using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -9,6 +10,16 @@ public class VarParameter : NonTerminatedSyntaxNode public ValueParameter ValueParameter => Children[1].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + 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 index 608eb11..df7d1c1 100644 --- a/Canon.Core/SyntaxNodes/Variable.cs +++ b/Canon.Core/SyntaxNodes/Variable.cs @@ -1,7 +1,6 @@ -using Canon.Core.CodeGenerators; +using Canon.Core.Abstractions; using Canon.Core.Enums; using Canon.Core.LexicalParser; -using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -15,48 +14,18 @@ public class Variable : NonTerminatedSyntaxNode public IdentifierSemanticToken Identifier => (IdentifierSemanticToken)Children[0].Convert().Token; + public override void PreVisit(SyntaxNodeVisitor visitor) + { + visitor.PreVisit(this); + } + + public override void PostVisit(SyntaxNodeVisitor visitor) + { + visitor.PostVisit(this); + } + public static Variable Create(List children) { return new Variable { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //判断是否为引用变量 - builder.SymbolTable.TryGetSymbol(Identifier.IdentifierName, out var symbol); - if (symbol is not null && symbol.Reference) - { - builder.AddString(" (*" + Identifier.IdentifierName + ")"); - } - else - { - builder.AddString(" " + Identifier.IdentifierName); - } - - //处理idVarPart(数组下标部分) - var idVarPart = Children[1].Convert(); - if (idVarPart.Exist) - { - PascalArrayType pascalArrayType = (PascalArrayType)symbol.SymbolType; - var positions = idVarPart.Positions; - - foreach (var pos in positions.Reverse()) - { - builder.AddString("["); - pos.GenerateCCode(builder); - //pascal下标减去左边界,从而映射到C语言的下标 - builder.AddString(" - " + System.Convert.ToString(pascalArrayType.Begin) + "]"); - - try - { - pascalArrayType = (PascalArrayType)pascalArrayType.ElementType; - } - catch (InvalidCastException e) - { - //do nothing - //因为最后一层嵌套类型,必然不是PascalArrayType, 而是BasicType - } - } - } - } } diff --git a/Canon.Generator/Extensions/RootCommandExtension.cs b/Canon.Generator/Extensions/RootCommandExtension.cs index 3538261..a00d02f 100644 --- a/Canon.Generator/Extensions/RootCommandExtension.cs +++ b/Canon.Generator/Extensions/RootCommandExtension.cs @@ -5,9 +5,9 @@ namespace Canon.Generator.Extensions; public static class RootCommandExtension { - public static void AddGenerateCommand(this RootCommand rootCommand) + public static void AddGrammarCommand(this RootCommand rootCommand) { - Command generateCommand = new("generate", "Generate source files."); + Command generateCommand = new("grammar", "Generate grammar parser source files."); Argument filenameArgument = new(name: "filename", description: "determines the generated file name.", @@ -37,4 +37,16 @@ public static class RootCommandExtension rootCommand.AddCommand(generateCommand); } + + public static void AddSyntaxVisitorCommand(this RootCommand rootCommand) + { + Command syntaxCommand = new("syntax", "Generate syntax visitor source code."); + + syntaxCommand.SetHandler(async () => + { + await SyntaxVisitorGenerator.SyntaxVisitorGenerator.Generate(); + }); + + rootCommand.AddCommand(syntaxCommand); + } } diff --git a/Canon.Generator/Program.cs b/Canon.Generator/Program.cs index 3e1c7b1..19d4e61 100644 --- a/Canon.Generator/Program.cs +++ b/Canon.Generator/Program.cs @@ -3,6 +3,7 @@ using Canon.Generator.Extensions; RootCommand rootCommand = new("Canon Compiler Source Generator"); -rootCommand.AddGenerateCommand(); +rootCommand.AddGrammarCommand(); +rootCommand.AddSyntaxVisitorCommand(); await rootCommand.InvokeAsync(args); diff --git a/Canon.Generator/SyntaxVisitorGenerator/SyntaxVisitorGenerator.cs b/Canon.Generator/SyntaxVisitorGenerator/SyntaxVisitorGenerator.cs new file mode 100644 index 0000000..2c9f1e1 --- /dev/null +++ b/Canon.Generator/SyntaxVisitorGenerator/SyntaxVisitorGenerator.cs @@ -0,0 +1,78 @@ +using System.Text; + +namespace Canon.Generator.SyntaxVisitorGenerator; + +public static class SyntaxVisitorGenerator +{ + private static readonly List s_nodes = + [ + "AddOperator", + "BasicType", + "CompoundStatement", + "ConstDeclaration", + "ConstDeclarations", + "ConstValue", + "ElsePart", + "Expression", + "ExpressionList", + "Factor", + "FormalParameter", + "IdentifierList", + "IdentifierVarPart", + "MultiplyOperator", + "Parameter", + "ParameterList", + "Period", + "ProcedureCall", + "ProgramBody", + "ProgramHead", + "ProgramStruct", + "RelationOperator", + "SimpleExpression", + "Statement", + "StatementList", + "Subprogram", + "SubprogramBody", + "SubprogramDeclarations", + "SubprogramHead", + "Term", + "TypeSyntaxNode", + "ValueParameter", + "VarDeclaration", + "VarDeclarations", + "Variable", + "VarParameter" + ]; + + public static async Task Generate() + { + FileInfo output = new(Path.Combine(Environment.CurrentDirectory, "SyntaxNodeVisitor.cs")); + + StringBuilder builder = new(); + + builder.Append("using Canon.Core.SyntaxNodes;\n").Append('\n'); + builder.Append("namespace Canon.Core.Abstractions;\n").Append('\n'); + + builder.Append("public abstract class SyntaxNodeVisitor\n").Append("{\n"); + + foreach (string node in s_nodes) + { + string nodeName = node.Substring(0, 1).ToLower() + node.Substring(1); + + builder.Append($" public virtual void PreVisit({node} {nodeName})\n") + .Append(" {\n") + .Append(" }\n") + .Append('\n'); + + builder.Append($" public virtual void PostVisit({node} {nodeName})\n") + .Append(" {\n") + .Append(" }\n") + .Append('\n'); + } + + builder.Append('}'); + + await using StreamWriter writer = output.CreateText(); + await writer.WriteAsync(builder.ToString()); + } +} diff --git a/Canon.Server/Program.cs b/Canon.Server/Program.cs index ac5cb0b..94ca0b6 100644 --- a/Canon.Server/Program.cs +++ b/Canon.Server/Program.cs @@ -1,6 +1,7 @@ using Canon.Core.Abstractions; using Canon.Core.GrammarParser; using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; using Canon.Server.Extensions; using Canon.Server.Services; using Microsoft.EntityFrameworkCore; @@ -24,6 +25,7 @@ builder.Services.AddTransient(); builder.Services.AddSingleton( _ => GeneratedGrammarParser.Instance); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddHostedService(); diff --git a/Canon.Server/Services/CompilerService.cs b/Canon.Server/Services/CompilerService.cs index 91fb0d8..f5464d8 100644 --- a/Canon.Server/Services/CompilerService.cs +++ b/Canon.Server/Services/CompilerService.cs @@ -1,6 +1,6 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; using Canon.Core.SyntaxNodes; using Canon.Server.DataTransferObjects; using Canon.Server.Entities; @@ -12,6 +12,7 @@ namespace Canon.Server.Services; public class CompilerService( ILexer lexer, IGrammarParser grammarParser, + SyntaxTreeTraveller traveller, CompileDbContext dbContext, GridFsService gridFsService, SyntaxTreePresentationService syntaxTreePresentationService, @@ -38,14 +39,14 @@ public class CompilerService( await using Stream imageStream = syntaxTreePresentationService.Present(root); string filename = await gridFsService.UploadStream(imageStream, "image/png"); - CCodeBuilder builder = new(); - root.GenerateCCode(builder); + CCodeGenerateVisitor visitor = new(); + traveller.Travel(root, visitor); CompileResult result = new() { SourceCode = sourceCode.Code, CompileId = Guid.NewGuid().ToString(), - CompiledCode = builder.Build(), + CompiledCode = visitor.Builder.Build(), SytaxTreeImageFilename = filename, CompileTime = DateTime.Now }; diff --git a/Canon.Tests/CCodeGeneratorTests/BasicTests.cs b/Canon.Tests/CCodeGeneratorTests/BasicTests.cs deleted file mode 100644 index bf46dd8..0000000 --- a/Canon.Tests/CCodeGeneratorTests/BasicTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; -using Canon.Core.GrammarParser; -using Canon.Core.LexicalParser; -using Canon.Core.SyntaxNodes; -using Canon.Tests.Utils; -using Xunit.Abstractions; - -namespace Canon.Tests.CCodeGeneratorTests; - -public class BasicTests -{ - private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance; - private readonly ILexer _lexer = new Lexer(); - private readonly ITestOutputHelper _outputHelper; - - public BasicTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - - [Fact] - public void ProgramStructTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program DoNothing; - begin - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include int main(){statement; return 0;}", result); - } -} diff --git a/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs b/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs deleted file mode 100644 index 68a9bb3..0000000 --- a/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; -using Canon.Core.GrammarParser; -using Canon.Core.LexicalParser; -using Canon.Core.SyntaxNodes; -using Canon.Tests.Utils; -using Xunit.Abstractions; - -namespace Canon.Tests.CCodeGeneratorTests; - -public class DeclarationsTest -{ - private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance; - private readonly ILexer _lexer = new Lexer(); - private readonly ITestOutputHelper _outputHelper; - - public DeclarationsTest(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - - [Fact] - public void VarDeclarationsTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program varTest; - var a, b, c, d: integer; m, n: real; k: boolean; - begin - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include char a; int main(){statement; return 0; }", result); - } - - [Fact] - public void ConstDeclarationsTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program varTest; - const a = 1; b = 2; c = 3; d = 2.5; - begin - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include int main(){statement; return 0; }", result); - } - - [Fact] - public void ArrayDeclarationsTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program arrayTest; - var a, b, c: array[1..6,5..8] of integer; - d: integer; - begin - a[2,3] := 10086; - d:=6; - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include int main(){statement; return 0; }", result); - } -} diff --git a/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs b/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs deleted file mode 100644 index 833d90a..0000000 --- a/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; -using Canon.Core.GrammarParser; -using Canon.Core.LexicalParser; -using Canon.Core.SyntaxNodes; -using Canon.Tests.Utils; -using Xunit.Abstractions; - -namespace Canon.Tests.CCodeGeneratorTests; - -public class ExpressionTests -{ - private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance; - private readonly ILexer _lexer = new Lexer(); - private readonly ITestOutputHelper _outputHelper; - - public ExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - - [Fact] - public void ExpressionTest1() - { - CCodeBuilder builder = new(); - - const string program = """ - program varTest; - var a, b, c, d: integer; m, n: real; k: boolean; - begin - a := 1; - b := a + 6 * 9 + (a + 9) * 1 - (4 + a) * 5 / 1; - m := b / 3; - d := 9 mod 1; - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include char a; int main(){statement; return 0; }", result); - } -} diff --git a/Canon.Tests/CCodeGeneratorTests/StatementTests.cs b/Canon.Tests/CCodeGeneratorTests/StatementTests.cs deleted file mode 100644 index 97806f7..0000000 --- a/Canon.Tests/CCodeGeneratorTests/StatementTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; -using Canon.Core.GrammarParser; -using Canon.Core.LexicalParser; -using Canon.Core.SyntaxNodes; -using Canon.Tests.Utils; -using Xunit.Abstractions; - -namespace Canon.Tests.CCodeGeneratorTests; - -public class StatementTests -{ - private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance; - private readonly ILexer _lexer = new Lexer(); - private readonly ITestOutputHelper _outputHelper; - - public StatementTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - - [Fact] - public void VariableAssignTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program varAssignTest; - var a, b: integer; - begin - a := 1; - b := a; - a := b; - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include #include " + - "int a, b; int main(){ a = 1; b = a; a = b; return 0;}", result); - } - - [Fact] - public void IfTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program main; - var - a,b:integer; - begin - if a = 5 then - begin - if b = 3 then - b := b + 1 - else - b := b + 2 - end - else - a := 2 - end. - - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include #include int a, b; " + - "int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result); - } - - [Fact] - public void ForLoopTest() - { - CCodeBuilder builder = new(); - - const string program = """ - program ForLoopTest; - var a, b, c: integer; - begin - b := 1; - for a := b * 5 + 1 to 99 do - begin - c := a + 1; - b := a mod (a + c); - b := a + 1 - 1 * 1; - end; - end. - """; - - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); - root.GenerateCCode(builder); - - string result = builder.Build(); - _outputHelper.WriteLine(result); - Assert.Equal("#include #include int a, b; " + - "int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result); - } -} diff --git a/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs index 0cccbee..757927c 100644 --- a/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs +++ b/Canon.Tests/GrammarParserTests/PascalGrammarTests.cs @@ -1,18 +1,10 @@ -using Canon.Core.Abstractions; -using Canon.Core.Enums; -using Canon.Core.GrammarParser; -using Canon.Core.LexicalParser; -using Canon.Core.SyntaxNodes; -using Canon.Tests.GeneratedParserTests; +using Canon.Core.SyntaxNodes; using Canon.Tests.Utils; namespace Canon.Tests.GrammarParserTests; public class PascalGrammarTests { - private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance; - private readonly ILexer _lexer = new Lexer(); - [Fact] public void DoNothingTest() { @@ -22,9 +14,7 @@ public class PascalGrammarTests end. """; - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); + ProgramStruct root = CompilerHelpers.Analyse(program); Assert.Equal("DoNothing", root.Head.ProgramName.LiteralValue); Assert.Equal(15, root.Count()); } @@ -39,9 +29,8 @@ public class PascalGrammarTests a := 1 + 1 end. """; - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - ProgramStruct root = _parser.Analyse(tokens); + ProgramStruct root = CompilerHelpers.Analyse(program); Assert.Equal("Add", root.Head.ProgramName.LiteralValue); } @@ -56,9 +45,8 @@ public class PascalGrammarTests writeln( str, ret ); end. """; - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - ProgramStruct root = _parser.Analyse(tokens); + ProgramStruct root = CompilerHelpers.Analyse(program); Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue); } @@ -73,9 +61,8 @@ public class PascalGrammarTests begin end. """; - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - ProgramStruct root = _parser.Analyse(tokens); + ProgramStruct root = CompilerHelpers.Analyse(program); Assert.Equal("main", root.Head.ProgramName.LiteralValue); } @@ -90,9 +77,21 @@ public class PascalGrammarTests end. """; - IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); - - ProgramStruct root = _parser.Analyse(tokens); + ProgramStruct root = CompilerHelpers.Analyse(program); Assert.Equal("vartest", root.Head.ProgramName.IdentifierName); } + + [Fact] + public void ArrayTest() + { + const string program = """ + program arrayTest; + var a : array [0..10] of integer; + begin + a[0] := 1; + end. + """; + + CompilerHelpers.Analyse(program); + } } diff --git a/Canon.Tests/LexicalParserTests/NumberTests.cs b/Canon.Tests/LexicalParserTests/NumberTests.cs index 7b815d4..4bcb53e 100644 --- a/Canon.Tests/LexicalParserTests/NumberTests.cs +++ b/Canon.Tests/LexicalParserTests/NumberTests.cs @@ -6,14 +6,40 @@ using Canon.Tests.Utils; using Canon.Core.Abstractions; namespace Canon.Tests.LexicalParserTests { - - public class NumberTests + public class NumberTests(ITestOutputHelper testOutputHelper) { private readonly ILexer _lexer = new Lexer(); - private readonly ITestOutputHelper _testOutputHelper; - public NumberTests(ITestOutputHelper testOutputHelper) + + [Theory] + [InlineData("123", 123)] + [InlineData("0", 0)] + public void IntegerTokenTest(string input, int result) { - _testOutputHelper = testOutputHelper; + IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(input)); + NumberSemanticToken token = tokens.First().Convert(); + + Assert.Equal(NumberType.Integer, token.NumberType); + Assert.Equal(result, token.ParseAsInteger()); + } + + [Theory] + [InlineData("0.0", 0)] + [InlineData("1.23", 1.23)] + [InlineData("1e7", 1e7)] + [InlineData("1E7", 1e7)] + [InlineData("1.23e2", 123)] + [InlineData("1.23E2", 123)] + [InlineData("123e-2", 1.23)] + [InlineData("123E-3", 0.123)] + [InlineData("12e-7", 12e-7)] + [InlineData(".123", 0.123)] + public void RealTokenTest(string input, double result) + { + IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(input)); + NumberSemanticToken token = tokens.First().Convert(); + + Assert.Equal(NumberType.Real, token.NumberType); + Assert.Equal(result, token.ParseAsReal()); } [Theory] @@ -50,7 +76,7 @@ namespace Canon.Tests.LexicalParserTests public void TestParseNumberError(string input, uint expectedLine, uint expectedCharPosition, LexemeErrorType expectedErrorType) { var ex = Assert.Throws(() => _lexer.Tokenize(new StringSourceReader(input)).ToList()); - _testOutputHelper.WriteLine(ex.ToString()); + testOutputHelper.WriteLine(ex.ToString()); Assert.Equal(expectedErrorType, ex.ErrorType); Assert.Equal(expectedLine, ex.Line); Assert.Equal(expectedCharPosition, ex.CharPosition); diff --git a/Canon.Tests/SemanticTests/ConstValueTests.cs b/Canon.Tests/SemanticTests/ConstValueTests.cs new file mode 100644 index 0000000..bc19175 --- /dev/null +++ b/Canon.Tests/SemanticTests/ConstValueTests.cs @@ -0,0 +1,53 @@ +using Canon.Core.Abstractions; +using Canon.Core.SemanticParser; +using Canon.Core.SyntaxNodes; +using Canon.Tests.Utils; + +namespace Canon.Tests.SemanticTests; + +public class ConstValueTests +{ + private class ConstValueVisitor : SyntaxNodeVisitor + { + public bool Pre { get; private set; } + + public bool Post { get; private set; } + + public override void PreVisit(ConstValue constValue) + { + constValue.OnNumberGenerator += (_, _) => + { + Assert.False(Pre); + Pre = true; + }; + } + + public override void PostVisit(ConstValue constValue) + { + constValue.OnNumberGenerator += (_, _) => + { + Assert.False(Post); + Post = true; + }; + } + } + + [Fact] + public void RaiseEventTest() + { + const string program = """ + program main; + const a = 1; + begin + end. + """; + + ProgramStruct root = CompilerHelpers.Analyse(program); + SyntaxTreeTraveller traveller = new(); + ConstValueVisitor visitor = new(); + traveller.Travel(root, visitor); + + Assert.True(visitor.Pre); + Assert.True(visitor.Post); + } +} diff --git a/Canon.Tests/SemanticTests/SyntaxTreeTravellerTests.cs b/Canon.Tests/SemanticTests/SyntaxTreeTravellerTests.cs new file mode 100644 index 0000000..9ccd13a --- /dev/null +++ b/Canon.Tests/SemanticTests/SyntaxTreeTravellerTests.cs @@ -0,0 +1,56 @@ +using Canon.Core.Abstractions; +using Canon.Core.GrammarParser; +using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; +using Canon.Core.SyntaxNodes; +using Canon.Tests.Utils; + +namespace Canon.Tests.SemanticTests; + +public class SyntaxTreeTravellerTests +{ + private readonly ILexer _lexer = new Lexer(); + private readonly IGrammarParser _grammarParser = GeneratedGrammarParser.Instance; + private readonly SyntaxTreeTraveller _traveller = new(); + + [Fact] + public void TravelTest() + { + const string program = """ + program main; + begin + end. + """; + + SampleSyntaxTreeVisitor visitor = new(); + IEnumerable tokens = _lexer.Tokenize(new StringSourceReader(program)); + ProgramStruct root = _grammarParser.Analyse(tokens); + + _traveller.Travel(root, visitor); + + List result = + [ + "ProgramStruct", + "ProgramHead", + "ProgramHead", + "ProgramBody", + "SubprogramDeclarations", + "SubprogramDeclarations", + "CompoundStatement", + "StatementList", + "Statement", + "Statement", + "StatementList", + "CompoundStatement", + "ProgramBody", + "ProgramStruct" + ]; + + string[] actual = visitor.ToString().Split('\n'); + + foreach ((string line, uint index) in result.WithIndex()) + { + Assert.Equal(line, actual[(int)index]); + } + } +} diff --git a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs new file mode 100644 index 0000000..6fcf4fc --- /dev/null +++ b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs @@ -0,0 +1,306 @@ +using Canon.Core.SemanticParser; +using Canon.Core.SyntaxNodes; +using Canon.Tests.Utils; + +namespace Canon.Tests.SemanticTests; + +public class TypeCheckVisitorTests +{ + [Fact] + public void ConstTypeTest() + { + const string program = """ + program main; + const a = 1; b = 1.23; c = 'a'; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol)); + Assert.Equal(PascalBasicType.Integer, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol)); + Assert.Equal(PascalBasicType.Real, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol)); + Assert.Equal(PascalBasicType.Character, symbol.SymbolType); + } + + [Fact] + public void SingleTypeTest() + { + const string program = """ + program main; + var a : integer; b : char; c : boolean; d : real; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol)); + Assert.Equal(PascalBasicType.Integer, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol)); + Assert.Equal(PascalBasicType.Character, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol)); + Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("d", out symbol)); + Assert.Equal(PascalBasicType.Real, symbol.SymbolType); + } + + [Fact] + public void MulitpleTypeTest() + { + const string program = """ + program main; + var a, b, c, d : integer; + e, f, g : boolean; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + IEnumerable names = ["a", "b", "c", "d"]; + foreach (string name in names) + { + Assert.True(visitor.SymbolTable.TryGetSymbol(name, out Symbol? symbol)); + Assert.Equal(PascalBasicType.Integer, symbol.SymbolType); + } + + names = ["e", "f", "g"]; + foreach (string name in names) + { + Assert.True(visitor.SymbolTable.TryGetSymbol(name, out Symbol? symbol)); + Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType); + } + } + + [Fact] + public void ArrayTest() + { + const string program = """ + program main; + var a : array [0..10] of integer; + b : array [0..10, 0..20] of integer; + c : array [100..200, 1..5] of real; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalArrayType(PascalBasicType.Integer, 0, 10)); + + Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol)); + Assert.Equal(symbol.SymbolType, + new PascalArrayType(new PascalArrayType(PascalBasicType.Integer, 0, 20), 0, 10)); + + Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol)); + Assert.Equal(symbol.SymbolType, new PascalArrayType( + new PascalArrayType(PascalBasicType.Real, 1, 5), 100, 200)); + } + + [Fact] + public void ProcedureParameterTest() + { + const string program = """ + program main; + procedure test(a, b, c : integer); + begin + end; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Integer, false) + ], PascalBasicType.Void)); + } + + [Fact] + public void ProcedureVarParameterTest() + { + const string program = """ + program main; + procedure test(var a, b, c : real); + begin + end; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Real, true), + new PascalParameterType(PascalBasicType.Real, true), + new PascalParameterType(PascalBasicType.Real, true) + ], PascalBasicType.Void)); + } + + [Fact] + public void ProcedureBothParameterTest() + { + const string program = """ + program main; + procedure test(a, b : integer; var c, d: char); + begin + end; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Character, true), + new PascalParameterType(PascalBasicType.Character, true) + ], PascalBasicType.Void)); + } + + [Fact] + public void FunctionBothParameterTest() + { + const string program = """ + program main; + function test(a, b : integer; var c, d: char) : real; + begin + end; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Character, true), + new PascalParameterType(PascalBasicType.Character, true) + ], PascalBasicType.Real)); + } + + [Fact] + public void ProcedureAndFunctionTest() + { + const string program = """ + program main; + procedure test1(a : integer; var b, c : real; d: boolean); + begin + end; + function test2(var a, b : boolean) : boolean; + begin + end; + begin + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test1", out Symbol? symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Integer, false), + new PascalParameterType(PascalBasicType.Real, true), + new PascalParameterType(PascalBasicType.Real, true), + new PascalParameterType(PascalBasicType.Boolean, false) + ], PascalBasicType.Void)); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test2", out symbol)); + Assert.Equal(symbol.SymbolType, new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Boolean, true), + new PascalParameterType(PascalBasicType.Boolean, true) + ], PascalBasicType.Boolean)); + } + + /// + /// 验证函数中的符号表是否正确 + /// + private class SubprogramSymbolTableTestVisitor : TypeCheckVisitor + { + public override void PostVisit(SubprogramBody subprogramBody) + { + base.PostVisit(subprogramBody); + + Assert.True(SymbolTable.TryGetSymbol("a", out Symbol? symbol)); + Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType); + + // 递归查父符号表 + Assert.True(SymbolTable.TryGetSymbol("b", out symbol)); + Assert.Equal(PascalBasicType.Real, symbol.SymbolType); + + Assert.True(SymbolTable.TryGetSymbol("c", out symbol)); + Assert.Equal(PascalBasicType.Character, symbol.SymbolType); + + Assert.True(SymbolTable.TryGetSymbol("d", out symbol)); + Assert.Equal(PascalBasicType.Character, symbol.SymbolType); + } + } + + [Fact] + public void SubprogramSymbolTableTest() + { + const string program = """ + program main; + const a = 3; + var b, c : real; + procedure test(a : boolean); + var c, d : char; + begin + end; + begin + end. + """; + + ProgramStruct root = CompilerHelpers.Analyse(program); + SubprogramSymbolTableTestVisitor visitor = new(); + SyntaxTreeTraveller traveller = new(); + + traveller.Travel(root, visitor); + + Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol)); + Assert.Equal(PascalBasicType.Integer, symbol.SymbolType); + Assert.True(symbol.Const); + + Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol)); + Assert.Equal(PascalBasicType.Real, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol)); + Assert.Equal(PascalBasicType.Real, symbol.SymbolType); + + Assert.True(visitor.SymbolTable.TryGetSymbol("test", out symbol)); + Assert.Equal( + new PascalFunctionType([ + new PascalParameterType(PascalBasicType.Boolean, false) + ], PascalBasicType.Void), symbol.SymbolType); + + Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol)); + } + + private static TypeCheckVisitor CheckType(string program) + { + ProgramStruct root = CompilerHelpers.Analyse(program); + TypeCheckVisitor visitor = new(); + SyntaxTreeTraveller traveller = new(); + + traveller.Travel(root, visitor); + + return visitor; + } +} diff --git a/Canon.Tests/Utils/CompilerHelpers.cs b/Canon.Tests/Utils/CompilerHelpers.cs new file mode 100644 index 0000000..8e1291e --- /dev/null +++ b/Canon.Tests/Utils/CompilerHelpers.cs @@ -0,0 +1,18 @@ +using Canon.Core.Abstractions; +using Canon.Core.GrammarParser; +using Canon.Core.LexicalParser; +using Canon.Core.SyntaxNodes; + +namespace Canon.Tests.Utils; + +public static class CompilerHelpers +{ + public static ProgramStruct Analyse(string program) + { + ILexer lexer = new Lexer(); + IGrammarParser grammarParser = GeneratedGrammarParser.Instance; + + IEnumerable tokens = lexer.Tokenize(new StringSourceReader(program)); + return grammarParser.Analyse(tokens); + } +} diff --git a/Canon.Tests/Utils/SampleSyntaxTreeVisitor.cs b/Canon.Tests/Utils/SampleSyntaxTreeVisitor.cs new file mode 100644 index 0000000..9fd0c01 --- /dev/null +++ b/Canon.Tests/Utils/SampleSyntaxTreeVisitor.cs @@ -0,0 +1,85 @@ +using System.Text; +using Canon.Core.Abstractions; +using Canon.Core.SyntaxNodes; + +namespace Canon.Tests.Utils; + +public class SampleSyntaxTreeVisitor : SyntaxNodeVisitor +{ + private readonly StringBuilder _builder = new(); + + public override string ToString() + { + return _builder.ToString(); + } + + public override void PreVisit(ProgramStruct programStruct) + { + _builder.Append(programStruct).Append('\n'); + } + + public override void PostVisit(ProgramStruct programStruct) + { + _builder.Append(programStruct).Append('\n'); + } + + public override void PreVisit(ProgramHead programHead) + { + _builder.Append(programHead).Append('\n'); + } + + public override void PostVisit(ProgramHead programHead) + { + _builder.Append(programHead).Append('\n'); + } + + public override void PreVisit(ProgramBody programBody) + { + _builder.Append(programBody).Append('\n'); + } + + public override void PostVisit(ProgramBody programBody) + { + _builder.Append(programBody).Append('\n'); + } + + public override void PreVisit(SubprogramDeclarations subprogramDeclarations) + { + _builder.Append(subprogramDeclarations).Append('\n'); + } + + public override void PostVisit(SubprogramDeclarations subprogramDeclarations) + { + _builder.Append(subprogramDeclarations).Append('\n'); + } + + public override void PreVisit(CompoundStatement compoundStatement) + { + _builder.Append(compoundStatement).Append('\n'); + } + + public override void PostVisit(CompoundStatement compoundStatement) + { + _builder.Append(compoundStatement).Append('\n'); + } + + public override void PreVisit(StatementList statementList) + { + _builder.Append(statementList).Append('\n'); + } + + public override void PostVisit(StatementList statementList) + { + _builder.Append(statementList).Append('\n'); + } + + public override void PreVisit(Statement statement) + { + _builder.Append(statement).Append('\n'); + } + + public override void PostVisit(Statement statement) + { + _builder.Append(statement).Append('\n'); + } +}