fix:修改对递归函数的procedureCall类型检查,在父符号表中寻找id (#66)
Reviewed-on: PostGuard/Canon#66 Co-authored-by: ichirinko <1621543655@qq.com> Co-committed-by: ichirinko <1621543655@qq.com>
This commit is contained in:
parent
03852ed2bf
commit
4d325569fa
|
@ -52,6 +52,11 @@ public abstract class PascalType : IEquatable<PascalType>
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (a == PascalBasicType.Boolean && b == PascalBasicType.Boolean)
|
||||||
|
{
|
||||||
|
return PascalBasicType.Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
if (a == PascalBasicType.Integer && b == PascalBasicType.Integer)
|
if (a == PascalBasicType.Integer && b == PascalBasicType.Integer)
|
||||||
{
|
{
|
||||||
return PascalBasicType.Integer;
|
return PascalBasicType.Integer;
|
||||||
|
@ -69,6 +74,7 @@ public abstract class PascalType : IEquatable<PascalType>
|
||||||
/// <returns>是否为可计算的类型</returns>
|
/// <returns>是否为可计算的类型</returns>
|
||||||
public static bool IsCalculatable(PascalType pascalType)
|
public static bool IsCalculatable(PascalType pascalType)
|
||||||
{
|
{
|
||||||
return pascalType == PascalBasicType.Integer || pascalType == PascalBasicType.Real;
|
return pascalType == PascalBasicType.Integer || pascalType == PascalBasicType.Real
|
||||||
|
|| pascalType == PascalBasicType.Boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -496,22 +496,48 @@ public class TypeCheckVisitor(ILogger<TypeCheckVisitor>? logger = null) : Syntax
|
||||||
public override void PostVisit(ProcedureCall procedureCall)
|
public override void PostVisit(ProcedureCall procedureCall)
|
||||||
{
|
{
|
||||||
base.PostVisit(procedureCall);
|
base.PostVisit(procedureCall);
|
||||||
// 查看procedureId是否注册
|
// 查看当前符号表中procedureId是否注册
|
||||||
|
|
||||||
if (!SymbolTable.TryGetSymbol(procedureCall.ProcedureId.IdentifierName, out Symbol? procedure))
|
if (!SymbolTable.TryGetSymbol(procedureCall.ProcedureId.IdentifierName, out Symbol? procedure))
|
||||||
{
|
{
|
||||||
|
// id没有定义
|
||||||
IsError = true;
|
IsError = true;
|
||||||
logger?.LogError("procedure '{}' is not defined.", procedureCall.ProcedureId.IdentifierName);
|
logger?.LogError("procedure '{}' is not defined.", procedureCall.ProcedureId.LiteralValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看该符号是否为procedure
|
|
||||||
if (procedure.SymbolType is not PascalFunctionType functionType)
|
if (procedure.SymbolType is not PascalFunctionType functionType)
|
||||||
{
|
{
|
||||||
|
// id不是函数类型,要找父符号表
|
||||||
|
if (!SymbolTable.TryGetParent(out SymbolTable? parent))
|
||||||
|
{
|
||||||
|
// 没找到父符号表,说明这个id是非函数变量
|
||||||
IsError = true;
|
IsError = true;
|
||||||
logger?.LogError("Identifier '{}' is not a call-able.", procedure.SymbolName);
|
logger?.LogError("Identifier '{}' is not a call-able.", procedureCall.ProcedureId.LiteralValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!parent.TryGetSymbol(procedureCall.ProcedureId.IdentifierName, out Symbol? procedureParent))
|
||||||
|
{
|
||||||
|
// 找到父符号表但没有找到该id,说明这个id没定义
|
||||||
|
IsError = true;
|
||||||
|
logger?.LogError("procedure '{}' is not defined.", procedureCall.ProcedureId.LiteralValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 父符号表中找到该id
|
||||||
|
if (procedureParent.SymbolType is not PascalFunctionType functionTypeParent)
|
||||||
|
{
|
||||||
|
// 该符号不是函数类型
|
||||||
|
IsError = true;
|
||||||
|
logger?.LogError("Identifier '{}' is not a call-able.", procedureParent.SymbolName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 该符号是函数类型,赋给procedure
|
||||||
|
procedure = procedureParent;
|
||||||
|
functionType = functionTypeParent;
|
||||||
|
}
|
||||||
|
|
||||||
procedureCall.OnParameterGenerator += (_, e) =>
|
procedureCall.OnParameterGenerator += (_, e) =>
|
||||||
{
|
{
|
||||||
// 检查procedure输入参数个数是否相符
|
// 检查procedure输入参数个数是否相符
|
||||||
|
|
|
@ -426,7 +426,7 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
findMin(a, b, c,error);
|
findmin(a, b, c,error);
|
||||||
(* Procedure call *)
|
(* Procedure call *)
|
||||||
end.
|
end.
|
||||||
""";
|
""";
|
||||||
|
@ -436,6 +436,32 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RecursionProcedureCallTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a, b:integer; c:real;
|
||||||
|
function Test0(var a1:integer; b1:integer; c1:real):integer;
|
||||||
|
begin
|
||||||
|
test0(a1,b1,c1+0.5);
|
||||||
|
end;
|
||||||
|
function Test1(var a1:integer; b1:integer; c1:real):integer;
|
||||||
|
begin
|
||||||
|
test0(1,1,1.0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
teSt1(a,b,1.02);
|
||||||
|
test(a, b, c);
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
TypeCheckVisitor visitor = CheckType(program);
|
||||||
|
Assert.True(visitor.IsError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ArrayAssignIndexTest()
|
public void ArrayAssignIndexTest()
|
||||||
{
|
{
|
||||||
|
@ -506,6 +532,23 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
CheckType(program);
|
CheckType(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BooleanOperatorTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var flag, tag : boolean;
|
||||||
|
error:integer;
|
||||||
|
begin
|
||||||
|
tag := flag or tag;
|
||||||
|
flag := flag and error;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
TypeCheckVisitor visitor = CheckType(program);
|
||||||
|
Assert.True(visitor.IsError);
|
||||||
|
}
|
||||||
|
|
||||||
private TypeCheckVisitor CheckType(string program)
|
private TypeCheckVisitor CheckType(string program)
|
||||||
{
|
{
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user