diff --git a/Canon.Core/SemanticParser/PascalBasicType.cs b/Canon.Core/SemanticParser/PascalBasicType.cs index bbe3817..fc2f57e 100644 --- a/Canon.Core/SemanticParser/PascalBasicType.cs +++ b/Canon.Core/SemanticParser/PascalBasicType.cs @@ -35,6 +35,8 @@ public class PascalBasicType : PascalType return Type.GetHashCode(); } + public override string ToString() => TypeName; + /// /// 整数类型的单例对象 /// diff --git a/Canon.Core/SemanticParser/TypeCheckVisitor.cs b/Canon.Core/SemanticParser/TypeCheckVisitor.cs index 1d326b8..fcbd119 100644 --- a/Canon.Core/SemanticParser/TypeCheckVisitor.cs +++ b/Canon.Core/SemanticParser/TypeCheckVisitor.cs @@ -3,6 +3,7 @@ using Canon.Core.Enums; using Canon.Core.LexicalParser; using Canon.Core.SyntaxNodes; using Microsoft.Extensions.Logging; +using Expression = Canon.Core.SyntaxNodes.Expression; namespace Canon.Core.SemanticParser; @@ -10,12 +11,48 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax { public SymbolTable SymbolTable { get; private set; } = new(); - public override void PostVisit(ConstValue constValue) + /// + /// 语法检查中是否发现错误 + /// + public bool IsError { get; private set; } + + public override void PreVisit(ConstDeclaration constDeclaration) { - base.PostVisit(constValue); - constValue.OnNumberGenerator += (_, e) => + base.PostVisit(constDeclaration); + + (IdentifierSemanticToken token, ConstValue constValue) = constDeclaration.ConstValue; + + // Lookahead 判断常量值的类型 + if (constValue.Children.Count == 1) { - switch (e.Token.NumberType) + SemanticToken valueToken = constValue.Children[0].Convert().Token; + + switch (valueToken.TokenType) + { + case SemanticTokenType.Number: + NumberSemanticToken numberSemanticToken = valueToken.Convert(); + switch (numberSemanticToken.NumberType) + { + case NumberType.Integer: + constValue.ConstType = PascalBasicType.Integer; + break; + case NumberType.Real: + constValue.ConstType = PascalBasicType.Real; + break; + } + + break; + case SemanticTokenType.Character: + constValue.ConstType = PascalBasicType.Character; + break; + } + } + else + { + NumberSemanticToken numberSemanticToken = constValue.Children[1].Convert() + .Token.Convert(); + + switch (numberSemanticToken.NumberType) { case NumberType.Integer: constValue.ConstType = PascalBasicType.Integer; @@ -24,23 +61,14 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax 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) + if (!SymbolTable.TryAddSymbol(new Symbol + { + Const = true, SymbolName = token.IdentifierName, SymbolType = constValue.ConstType + })) { + IsError = true; logger?.LogError("Identifier '{}' has been declared twice!", token.IdentifierName); } } @@ -64,35 +92,44 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax }; // factor -> variable - factor.OnVariableGenerator += (_, e) => - { - if (SymbolTable.TryGetSymbol(e.Variable.Identifier.IdentifierName, out Symbol? symbol)) - { - factor.FactorType = symbol.SymbolType; - } - }; + factor.OnVariableGenerator += (_, e) => { factor.FactorType = e.Variable.VariableType; }; // factor -> (expression) - factor.OnParethnesisGenerator += (_, e) => { factor.FactorType = e.Expression.ExprssionType; }; + factor.OnParethnesisGenerator += (_, e) => { factor.FactorType = e.Expression.ExpressionType; }; // factor -> id (expression_list) factor.OnProcedureCallGenerator += (_, e) => { if (!SymbolTable.TryGetSymbol(e.ProcedureName.IdentifierName, out Symbol? procedure)) { + IsError = true; logger?.LogError("Procedure '{}' does not define.", e.ProcedureName.IdentifierName); return; } - if (procedure.SymbolType is not PascalFunctionType functionType) + PascalFunctionType? functionType = procedure.SymbolType as PascalFunctionType; + if (functionType is null) { - logger?.LogError("Identifier '{}' is not a call-able.", procedure.SymbolName); + if (SymbolTable.TryGetParent(out SymbolTable? parent)) + { + if (parent.TryGetSymbol(e.ProcedureName.IdentifierName, out procedure)) + { + functionType = procedure.SymbolType as PascalFunctionType; + } + } + } + + if (functionType is null) + { + IsError = true; + logger?.LogError("'{}' is not call able.", e.ProcedureName.IdentifierName); return; } if (functionType.ReturnType == PascalBasicType.Void) { - logger?.LogError("Procedure '{}' returns void.", procedure.SymbolName); + IsError = true; + logger?.LogError("Procedure '{}' returns void.", e.ProcedureName.IdentifierName); return; } @@ -100,8 +137,9 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax if (e.Parameters.Expressions.Count != functionType.Parameters.Count) { + IsError = true; logger?.LogError("Procedure '{}' expects {} parameters but {} provided.", - procedure.SymbolName, + e.ProcedureName.IdentifierName, functionType.Parameters.Count, e.Parameters.Expressions.Count); return; @@ -110,9 +148,11 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax foreach ((Expression expression, PascalParameterType parameterType) in e.Parameters.Expressions.Zip( functionType.Parameters)) { - if (expression.ExprssionType != parameterType) + if (expression.ExpressionType != parameterType.ParameterType) { - logger?.LogError(""); + IsError = true; + logger?.LogError("Parameter expect '{}' but '{}'", + parameterType.ParameterType.TypeName, expression.ExpressionType); return; } } @@ -123,6 +163,7 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax { if (e.Factor.FactorType != PascalBasicType.Boolean) { + IsError = true; logger?.LogError("The boolean type is expected."); return; } @@ -148,6 +189,7 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax return; } + IsError = true; logger?.LogError("Can't calculate"); }; } @@ -166,6 +208,7 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax return; } + IsError = true; logger?.LogError("Can't calculate"); }; } @@ -176,10 +219,10 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax expression.OnSimpleExpressionGenerator += (_, e) => { - expression.ExprssionType = e.SimpleExpression.SimpleExpressionType; + expression.ExpressionType = e.SimpleExpression.SimpleExpressionType; }; - expression.OnRelationGenerator += (_, _) => { expression.ExprssionType = PascalBasicType.Boolean; }; + expression.OnRelationGenerator += (_, _) => { expression.ExpressionType = PascalBasicType.Boolean; }; } public override void PostVisit(TypeSyntaxNode typeSyntaxNode) @@ -232,7 +275,6 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax SymbolName = e.IdentifierToken.IdentifierName, SymbolType = identifierList.DefinitionType, Reference = identifierList.IsReference - }; SymbolTable.TryAddSymbol(symbol); @@ -333,6 +375,12 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax SymbolName = subprogramHead.SubprogramName.IdentifierName, SymbolType = new PascalFunctionType(parameters, e.ReturnType.PascalType) }); + + // 在Pascal中返回值是添加一个类型为返回类型 名称为函数名称的局部变量 + SymbolTable.TryAddSymbol(new Symbol + { + SymbolName = subprogramHead.SubprogramName.IdentifierName, SymbolType = e.ReturnType.PascalType + }); }; } @@ -371,4 +419,173 @@ public class TypeCheckVisitor(ILogger? logger = null) : Syntax // 同时添加到参数列表 _parameters!.Add(symbol); } + + public override void PostVisit(Statement statement) + { + base.PostVisit(statement); + // statement -> Variable AssignOp Expression + + statement.OnAssignGenerator += (_, e) => + { + // 检查是否有注册变量 + if (!SymbolTable.TryGetSymbol(e.Variable.Identifier.IdentifierName, out Symbol? variable)) + { + IsError = true; + logger?.LogError("Variable '{}' does not define.", e.Variable.Identifier.IdentifierName); + return; + } + + // 检查是否为常量 + if (variable.Const) + { + IsError = true; + logger?.LogError("Can't assign value to const '{}'.,", + e.Variable.Identifier.IdentifierName); + } + + if (e.Variable.VariableType != e.Expression.ExpressionType) + { + IsError = true; + logger?.LogError("Variable '{}' type mismatch, expect '{}' but '{}'.", + e.Variable.Identifier.IdentifierName, + e.Variable.VariableType.ToString(), + e.Expression.ExpressionType.ToString()); + } + }; + + // statement -> for id AssignOp Expression to Expression do Statement + statement.OnForGenerator += (_, e) => + { + // 检查id是否存在 + if (!SymbolTable.TryGetSymbol(e.Iterator.IdentifierName, out Symbol? _)) + { + IsError = true; + logger?.LogError("Variable '{}' does not define.", e.Iterator.IdentifierName); + return; + } + + // 检查ExpressionA是否为Integer + if (e.Begin.ExpressionType != PascalBasicType.Integer) + { + IsError = true; + logger?.LogError("The loop begin parameter is not integer."); + return; + } + + // 检查ExpressionB是否为Integer + if (e.End.ExpressionType != PascalBasicType.Integer) + { + IsError = true; + logger?.LogError("The loop end parameter is not integer."); + } + }; + + // statement -> if Expression then Statement ElsePart + statement.OnIfGenerator += (_, e) => + { + // 条件是否为Boolean + if (e.Condition.ExpressionType != PascalBasicType.Boolean) + { + IsError = true; + logger?.LogError("Expect '{}' but '{}'.", PascalBasicType.Boolean.TypeName, + e.Condition.ExpressionType.ToString()); + } + }; + } + + public override void PostVisit(ProcedureCall procedureCall) + { + base.PostVisit(procedureCall); + // 查看procedureId是否注册 + if (!SymbolTable.TryGetSymbol(procedureCall.ProcedureId.IdentifierName, out Symbol? procedure)) + { + IsError = true; + logger?.LogError("procedure '{}' is not defined.", procedureCall.ProcedureId.IdentifierName); + return; + } + + // 查看该符号是否为procedure + if (procedure.SymbolType is not PascalFunctionType functionType) + { + IsError = true; + logger?.LogError("Identifier '{}' is not a call-able.", procedure.SymbolName); + return; + } + + procedureCall.OnParameterGenerator += (_, e) => + { + // 检查procedure输入参数个数是否相符 + if (e.Parameters.Expressions.Count != functionType.Parameters.Count) + { + IsError = true; + logger?.LogError("Procedure '{}' expects {} parameters but {} provided.", + procedure.SymbolName, + functionType.Parameters.Count, + e.Parameters.Expressions.Count); + return; + } + + // 检查每个参数的类型与procedure参数定义类型是否相符 + foreach ((Expression expression, PascalParameterType parameterType) in e.Parameters.Expressions.Zip( + functionType.Parameters)) + { + if (expression.ExpressionType != parameterType.ParameterType) + { + IsError = true; + logger?.LogError("Parameter expect '{}' but '{}'", + parameterType.ParameterType.TypeName, expression.ExpressionType); + return; + } + } + }; + } + + public override void PostVisit(Variable variable) + { + base.PostVisit(variable); + if (SymbolTable.TryGetSymbol(variable.Identifier.IdentifierName, out Symbol? id)) + { + variable.VariableType = id.SymbolType; + + for (int i = 0; i < variable.VarPart.IndexCount; i++) + { + if (variable.VariableType is PascalArrayType arrayType) + { + variable.VariableType = arrayType.ElementType; + } + else + { + // 提前不为ArrayType,赋值变量维数写多 + IsError = true; + logger?.LogError("Array dimension mismatch, more than expect"); + return; + } + } + } + else + { + // 没有注册变量 + IsError = true; + logger?.LogError("Variable '{}' does not define.", variable.Identifier.IdentifierName); + } + } + + public override void PostVisit(IdentifierVarPart identifierVarPart) + { + base.PostVisit(identifierVarPart); + identifierVarPart.OnIndexGenerator += (_, e) => + { + foreach (Expression expression in e.IndexParameters.Expressions) + { + if (expression.ExpressionType != PascalBasicType.Integer) + { + IsError = true; + logger?.LogError("Index of array expect 'int' but '{}'", + expression.ExpressionType.ToString()); + } + } + + identifierVarPart.IndexCount = e.IndexParameters.Expressions.Count; + }; + } } diff --git a/Canon.Core/SyntaxNodes/CompoundStatement.cs b/Canon.Core/SyntaxNodes/CompoundStatement.cs index e0137f4..eb3aa65 100644 --- a/Canon.Core/SyntaxNodes/CompoundStatement.cs +++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs @@ -1,5 +1,4 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -8,8 +7,6 @@ public class CompoundStatement : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.CompoundStatement; - public IEnumerable Statements => Children[1].Convert().Statements; - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -24,16 +21,4 @@ public class CompoundStatement : NonTerminatedSyntaxNode { return new CompoundStatement { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var statement in Statements.Reverse()) - { - if (statement.Children.Count > 0) - { - statement.GenerateCCode(builder); - builder.AddString(";"); - } - } - } } diff --git a/Canon.Core/SyntaxNodes/ConstDeclarations.cs b/Canon.Core/SyntaxNodes/ConstDeclarations.cs index 0253813..d668d95 100644 --- a/Canon.Core/SyntaxNodes/ConstDeclarations.cs +++ b/Canon.Core/SyntaxNodes/ConstDeclarations.cs @@ -1,7 +1,5 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; -using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; @@ -9,11 +7,6 @@ public class ConstDeclarations : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ConstDeclarations; - /// - /// 声明的常量列表 - /// - public IEnumerable<(IdentifierSemanticToken, ConstValue)> ConstValues => GetConstValues(); - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -28,62 +21,4 @@ public class ConstDeclarations : NonTerminatedSyntaxNode { return new ConstDeclarations { Children = children }; } - - private IEnumerable<(IdentifierSemanticToken, ConstValue)> GetConstValues() - { - if (Children.Count == 0) - { - yield break; - } - - ConstDeclaration declaration = Children[1].Convert(); - - while (true) - { - yield return declaration.ConstValue; - - if (declaration.IsRecursive) - { - declaration = declaration.Children[0].Convert(); - } - else - { - break; - } - } - } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var pair in ConstValues.Reverse()) - { - builder.AddString(" const"); - //获取常量类型 - var token = pair.Item2.Children[0].Convert().Token; - var tokenType = token.TokenType; - if (tokenType == SemanticTokenType.Number) - { - if (token.Convert().NumberType == NumberType.Integer) - { - builder.AddString(" int "); - } - else - { - builder.AddString(" double "); - } - } - else if (tokenType == SemanticTokenType.Character) - { - builder.AddString(" char "); - } - else - { - builder.AddString(" bool "); - } - - builder.AddString(pair.Item1.IdentifierName + " ="); - pair.Item2.GenerateCCode(builder); - builder.AddString(";"); - } - } } diff --git a/Canon.Core/SyntaxNodes/Expression.cs b/Canon.Core/SyntaxNodes/Expression.cs index d334946..631e1e0 100644 --- a/Canon.Core/SyntaxNodes/Expression.cs +++ b/Canon.Core/SyntaxNodes/Expression.cs @@ -47,7 +47,7 @@ public class Expression : NonTerminatedSyntaxNode private PascalType? _expressionType; - public PascalType ExprssionType + public PascalType ExpressionType { get { diff --git a/Canon.Core/SyntaxNodes/ExpressionList.cs b/Canon.Core/SyntaxNodes/ExpressionList.cs index 59ae5d6..0b45d12 100644 --- a/Canon.Core/SyntaxNodes/ExpressionList.cs +++ b/Canon.Core/SyntaxNodes/ExpressionList.cs @@ -3,6 +3,11 @@ using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; +public class OnExpressionListEventArgs : EventArgs +{ + public required ExpressionList ExpressionList { get; init; } +} + public class ExpressionList : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ExpressionList; @@ -12,26 +17,40 @@ public class ExpressionList : NonTerminatedSyntaxNode /// public List Expressions { get; } = []; + /// + /// 当前ExpressionList中的Expression定义 + /// + public required Expression Expression { get; init; } + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); + RaiseEvent(); } public override void PostVisit(SyntaxNodeVisitor visitor) { visitor.PostVisit(this); + RaiseEvent(); } + /// + /// 使用ExpressionList产生式的时间 + /// + public event EventHandler? OnExpressionList; + public static ExpressionList Create(List children) { - ExpressionList result = new() { Children = children }; + ExpressionList result; if (children.Count == 1) { + result = new ExpressionList { Expression = children[0].Convert(), Children = children }; result.Expressions.Add(children[0].Convert()); } - else if (children.Count == 3) + else { + result = new ExpressionList { Expression = children[2].Convert(), Children = children }; foreach (Expression expression in children[0].Convert().Expressions) { result.Expressions.Add(expression); @@ -42,4 +61,15 @@ public class ExpressionList : NonTerminatedSyntaxNode return result; } + + private void RaiseEvent() + { + if (Children.Count == 3) + { + OnExpressionList?.Invoke(this, + new OnExpressionListEventArgs { ExpressionList = Children[0].Convert() }); + } + + OnExpressionList = null; + } } diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs index cf4cc3d..c783347 100644 --- a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs +++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs @@ -3,15 +3,20 @@ using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; -public class OnParameterGeneratorEventArgs : EventArgs +public class OnIndexGeneratorEventArgs : EventArgs { - public required ExpressionList Parameters { get; init; } + public required ExpressionList IndexParameters { get; init; } } public class IdentifierVarPart : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.IdVarPart; + /// + /// 数组索引的个数 + /// + public int IndexCount { get; set; } + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -24,7 +29,10 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode RaiseEvent(); } - public event EventHandler? OnParameterGenerator; + /// + /// 使用了索引产生式的事件 + /// + public event EventHandler? OnIndexGenerator; public static IdentifierVarPart Create(List children) { @@ -35,12 +43,12 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode { if (Children.Count == 3) { - OnParameterGenerator?.Invoke(this, new OnParameterGeneratorEventArgs + OnIndexGenerator?.Invoke(this, new OnIndexGeneratorEventArgs() { - Parameters = Children[1].Convert() + IndexParameters = Children[1].Convert() }); } - OnParameterGenerator = null; + OnIndexGenerator = null; } } diff --git a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs index be2ce28..cc8f5b3 100644 --- a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs @@ -10,8 +10,6 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable Children { get; init; } public IEnumerator GetEnumerator() diff --git a/Canon.Core/SyntaxNodes/ParameterList.cs b/Canon.Core/SyntaxNodes/ParameterList.cs index 1d08df3..5538812 100644 --- a/Canon.Core/SyntaxNodes/ParameterList.cs +++ b/Canon.Core/SyntaxNodes/ParameterList.cs @@ -7,13 +7,6 @@ public class ParameterList : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ParameterList; - public bool IsRecursive { get; private init; } - - /// - /// 声明的参数列表 - /// - public IEnumerable Parameters => GetParameters(); - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -26,40 +19,6 @@ public class ParameterList : NonTerminatedSyntaxNode public static ParameterList Create(List children) { - bool isRecursive; - - if (children.Count == 1) - { - isRecursive = false; - } - else if (children.Count == 3) - { - isRecursive = true; - } - else - { - throw new InvalidOperationException(); - } - - return new ParameterList { Children = children, IsRecursive = isRecursive }; - } - - private IEnumerable GetParameters() - { - ParameterList list = this; - - while (true) - { - if (list.IsRecursive) - { - yield return list.Children[2].Convert(); - list = list.Children[0].Convert(); - } - else - { - yield return list.Children[0].Convert(); - break; - } - } + return new ParameterList { Children = children }; } } diff --git a/Canon.Core/SyntaxNodes/ProcedureCall.cs b/Canon.Core/SyntaxNodes/ProcedureCall.cs index 4136f42..3920e6a 100644 --- a/Canon.Core/SyntaxNodes/ProcedureCall.cs +++ b/Canon.Core/SyntaxNodes/ProcedureCall.cs @@ -1,27 +1,36 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; namespace Canon.Core.SyntaxNodes; +public class OnParameterGeneratorEventArgs : EventArgs +{ + public required ExpressionList Parameters { get; init; } +} + public class ProcedureCall : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ProcedureCall; public IdentifierSemanticToken ProcedureId - => (IdentifierSemanticToken)Children[0].Convert().Token; + => Children[0].Convert().Token.Convert(); - public IEnumerable Arguments => GetArguments(); + /// + /// 调用函数时含有参数的事件 + /// + public event EventHandler? OnParameterGenerator; public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); + RaiseEvent(); } public override void PostVisit(SyntaxNodeVisitor visitor) { visitor.PostVisit(this); + RaiseEvent(); } public static ProcedureCall Create(List children) @@ -29,38 +38,16 @@ public class ProcedureCall : NonTerminatedSyntaxNode return new ProcedureCall { Children = children }; } - private IEnumerable GetArguments() + private void RaiseEvent() { - if (Children.Count == 1) + if (Children.Count == 4) { - yield break; - } - - foreach (Expression expression in Children[2].Convert().Expressions) - { - yield return expression; - } - } - - public override void GenerateCCode(CCodeBuilder builder) - { - builder.AddString(ProcedureId.IdentifierName + "("); - - //用逗号分隔输出的expression - using (var enumerator = Arguments.GetEnumerator()) - { - if (enumerator.MoveNext()) + OnParameterGenerator?.Invoke(this, new OnParameterGeneratorEventArgs { - enumerator.Current.GenerateCCode(builder); - } - - while (enumerator.MoveNext()) - { - builder.AddString(", "); - enumerator.Current.GenerateCCode(builder); - } + Parameters = Children[2].Convert() + }); } - builder.AddString(")"); + OnParameterGenerator = null; } } diff --git a/Canon.Core/SyntaxNodes/Statement.cs b/Canon.Core/SyntaxNodes/Statement.cs index 023b4bc..9acec1b 100644 --- a/Canon.Core/SyntaxNodes/Statement.cs +++ b/Canon.Core/SyntaxNodes/Statement.cs @@ -82,7 +82,7 @@ public class Statement : NonTerminatedSyntaxNode private void RaiseEvent() { - if (Children.Count == 2) + if (Children.Count == 3) { if (Children[0].IsTerminated) { diff --git a/Canon.Core/SyntaxNodes/StatementList.cs b/Canon.Core/SyntaxNodes/StatementList.cs index a50323a..210784d 100644 --- a/Canon.Core/SyntaxNodes/StatementList.cs +++ b/Canon.Core/SyntaxNodes/StatementList.cs @@ -8,10 +8,6 @@ public class StatementList : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.StatementList; - public bool IsRecursive { get; private init; } - - public IEnumerable Statements => GetStatements(); - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -24,52 +20,6 @@ public class StatementList : NonTerminatedSyntaxNode public static StatementList Create(List children) { - bool isRecursive; - - if (children.Count == 1) - { - isRecursive = false; - } - else if (children.Count == 3) - { - isRecursive = true; - } - else - { - throw new InvalidOperationException(); - } - - return new StatementList { Children = children, IsRecursive = isRecursive }; - } - - private IEnumerable GetStatements() - { - StatementList list = this; - - while (true) - { - if (list.IsRecursive) - { - yield return list.Children[2].Convert(); - list = list.Children[0].Convert(); - } - else - { - yield return list.Children[0].Convert(); - break; - } - } - } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var statement in Statements.Reverse()) - { - if (statement.Children.Count > 0) - { - statement.GenerateCCode(builder); - builder.AddString(";"); - } - } + return new StatementList { Children = children}; } } diff --git a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs index bcdd455..2df1028 100644 --- a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs +++ b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs @@ -8,11 +8,6 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.SubprogramDeclarations; - /// - /// 声明的子程序列表 - /// - public IEnumerable Subprograms => GetSubprograms(); - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -27,28 +22,4 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode { return new SubprogramDeclarations { Children = children }; } - - private IEnumerable GetSubprograms() - { - SubprogramDeclarations declarations = this; - - while (true) - { - if (declarations.Children.Count == 0) - { - yield break; - } - - yield return declarations.Children[1].Convert(); - declarations = declarations.Children[0].Convert(); - } - } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var subprogram in Subprograms) - { - subprogram.GenerateCCode(builder); - } - } } diff --git a/Canon.Core/SyntaxNodes/ValueParameter.cs b/Canon.Core/SyntaxNodes/ValueParameter.cs index 23cc84d..1750d65 100644 --- a/Canon.Core/SyntaxNodes/ValueParameter.cs +++ b/Canon.Core/SyntaxNodes/ValueParameter.cs @@ -1,5 +1,4 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; using Canon.Core.LexicalParser; diff --git a/Canon.Core/SyntaxNodes/VarDeclarations.cs b/Canon.Core/SyntaxNodes/VarDeclarations.cs index e9af74d..e657adb 100644 --- a/Canon.Core/SyntaxNodes/VarDeclarations.cs +++ b/Canon.Core/SyntaxNodes/VarDeclarations.cs @@ -1,7 +1,5 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; -using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -9,11 +7,6 @@ public class VarDeclarations : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.VarDeclarations; - /// - /// 声明的变量列表 - /// - // public IEnumerable<(IdentifierList, TypeSyntaxNode)> Variables => EnumerateVariables(); - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -28,73 +21,4 @@ public class VarDeclarations : NonTerminatedSyntaxNode { return new VarDeclarations { Children = children }; } - - // private IEnumerable<(IdentifierList, TypeSyntaxNode)> EnumerateVariables() - // { - // if (Children.Count == 0) - // { - // yield break; - // } - // - // VarDeclaration declaration = Children[1].Convert(); - // - // while (true) - // { - // yield return declaration.Variable; - // - // if (declaration.IsRecursive) - // { - // declaration = declaration.Children[0].Convert(); - // } - // else - // { - // break; - // } - // } - // } - - // public override void GenerateCCode(CCodeBuilder builder) - // { - // foreach (var pair in Variables.Reverse()) - // { - // //BasicType定义 - // if (pair.Item2.Children.Count == 1) - // { - // //输出类型 - // pair.Item2.GenerateCCode(builder); - // //输出idList - // pair.Item1.GenerateCCode(builder); - // builder.AddString(";"); - // } - // //array定义 - // else - // { - // //构造出C语言形式的数组下标定义 - // string arrayPeriod = ""; - // var ranges = pair.Item2.Children[2] - // .Convert().Ranges; - // PascalType pascalType = pair.Item2.Children[5].Convert().TryGetPascalType(); - // - // foreach (var range in ranges) - // { - // int low = int.Parse(range.Item1.LiteralValue); - // int high = int.Parse(range.Item2.LiteralValue); - // arrayPeriod = "[" + System.Convert.ToString(high-low+1) + "]" + arrayPeriod; - // pascalType = new PascalArrayType(pascalType, low, high); //嵌套地构造出多维数组 - // } - // - // //依次定义每一个符号 - // foreach (var id in pair.Item1.Identifiers.Reverse()) - // { - // pair.Item2.Children[5].GenerateCCode(builder); - // builder.AddString(" " + id.IdentifierName + arrayPeriod + ";"); - // //写入符号表 - // builder.SymbolTable.TryAddSymbol(new Symbol() - // { - // SymbolName = id.IdentifierName, SymbolType = pascalType, Reference = false - // }); - // } - // } - // } - // } } diff --git a/Canon.Core/SyntaxNodes/Variable.cs b/Canon.Core/SyntaxNodes/Variable.cs index df7d1c1..6aea1aa 100644 --- a/Canon.Core/SyntaxNodes/Variable.cs +++ b/Canon.Core/SyntaxNodes/Variable.cs @@ -1,6 +1,7 @@ using Canon.Core.Abstractions; using Canon.Core.Enums; using Canon.Core.LexicalParser; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -8,12 +9,39 @@ public class Variable : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Variable; + private PascalType? _variableType; + + /// + /// Variable实际的类型,用于数组赋值 + /// + public PascalType VariableType + { + get + { + if (_variableType is null) + { + throw new InvalidOperationException(); + } + + return _variableType; + } + set + { + _variableType = value; + } + } + /// /// 变量的名称 /// public IdentifierSemanticToken Identifier => (IdentifierSemanticToken)Children[0].Convert().Token; + /// + /// 声明数组访问的部分 + /// + public IdentifierVarPart VarPart => Children[1].Convert(); + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); diff --git a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs index 6fcf4fc..c41b035 100644 --- a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs +++ b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs @@ -1,11 +1,14 @@ using Canon.Core.SemanticParser; using Canon.Core.SyntaxNodes; using Canon.Tests.Utils; +using Xunit.Abstractions; namespace Canon.Tests.SemanticTests; -public class TypeCheckVisitorTests +public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) { + private readonly TestLogger _logger = new(testOutputHelper); + [Fact] public void ConstTypeTest() { @@ -293,10 +296,220 @@ public class TypeCheckVisitorTests Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol)); } - private static TypeCheckVisitor CheckType(string program) + [Fact] + public void VarAssignStatementTest() + { + const string program = """ + program main; + var + a : char; + b : integer; + begin + b := 3; + a := b; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void TryConstAssignStatementTest() + { + const string program = """ + program main; + const + a = 3; + var + b : integer; + begin + a := 4; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void FunctionAssignStatementTest() + { + const string program = """ + program exFunction; + var + a, b, ret : integer; + + + function max(num1, num2: integer): integer; + var + error:char; + result: integer; + + begin + if (num1 > num2) then + result := num1 + + else + result := num2; + max := error; + end; + + begin + a := 100; + b := 200; + (* calling a function to get max value *) + ret := max(a, b); + + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void IfStatementTest() + { + const string program = """ + program exFunction; + var + a, b, ret : integer; + begin + a := 100; + b := 200; + if 200 then + begin + b := 100; + end + else + b:=200; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void ForStatementTest() + { + const string program = """ + program exFunction; + var + a, b, ret : integer; + c : char; + begin + + for a := c to b do + begin + b:=b+10; + end; + + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void ProcedureCallTest() + { + const string program = """ + program main; + var + a, b, c, min: integer; + error:char; + procedure findMin(x, y, z: integer; var m: integer); + begin + end; + + begin + findMin(a, b, c,error); + (* Procedure call *) + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + + [Fact] + public void ArrayAssignIndexTest() + { + const string program = """ + program main; + var a : array [0..10, 0..10] of integer; + function test(a, b : integer) : integer; + begin + test := 1; + end; + begin + a[0, 1.5] := 1; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void ArrayAssignDimensionTest() + { + const string program = """ + program main; + var a : array [0..10, 0..10] of integer; + begin + a[0,1,3] := 1; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void ArrayAssignTypeTest() + { + const string program = """ + program main; + var + a : array [0..10, 0..10] of integer; + b : array [0..10, 0..20] of integer; + c : integer; + d : char; + begin + a[0,1] := c; + c := b[0,5]; + a[0,1] := b; + end. + """; + + TypeCheckVisitor visitor = CheckType(program); + Assert.True(visitor.IsError); + } + + [Fact] + public void ArrayCalculationTest() + { + const string program = """ + program main; + var a: array[9..12, 3..5, 6..20] of real; + b: array[0..10] of integer; + begin + a[9, 4, 20] := 3.6 + b[5]; + end. + """; + + CheckType(program); + } + + private TypeCheckVisitor CheckType(string program) { ProgramStruct root = CompilerHelpers.Analyse(program); - TypeCheckVisitor visitor = new(); + TypeCheckVisitor visitor = new(_logger); SyntaxTreeTraveller traveller = new(); traveller.Travel(root, visitor); diff --git a/Canon.Tests/Utils/TestLogger.cs b/Canon.Tests/Utils/TestLogger.cs new file mode 100644 index 0000000..3e27bed --- /dev/null +++ b/Canon.Tests/Utils/TestLogger.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Canon.Tests.Utils; + +public class TestLogger(ITestOutputHelper testOutputHelper) : ILogger, IDisposable +{ + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter) + { + testOutputHelper.WriteLine("{0}: {1}", logLevel, formatter(state, exception)); + } + + public bool IsEnabled(LogLevel logLevel) => false; + + public void Dispose() + { + } + + public IDisposable BeginScope(TState state) where TState : notnull + { + return this; + } +}