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;
+ }
+}