feat: 语法树的访问者和类型检测访问者 (#56)

Reviewed-on: PostGuard/Canon#56
This commit is contained in:
2024-04-26 10:18:49 +08:00
parent b20c3234c5
commit 17dbcccb59
63 changed files with 2757 additions and 704 deletions

View File

@@ -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 <stdbool.h>\n");
Builder.AddString("#include <stdio.h>\n");
}
}

View File

@@ -44,4 +44,31 @@ public abstract class PascalType : IEquatable<PascalType>
{
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;
}
}
/// <summary>
/// 是否为可计算的类型
/// </summary>
/// <param name="pascalType">需要判断的Pascal类型</param>
/// <returns>是否为可计算的类型</returns>
public static bool IsCalculatable(PascalType pascalType)
{
return pascalType == PascalBasicType.Integer || pascalType == PascalBasicType.Real;
}
}

View File

@@ -96,6 +96,23 @@ public class SymbolTable
return false;
}
/// <summary>
/// 尝试获得父符号表
/// </summary>
/// <param name="parent">获得的父符号表</param>
/// <returns>是否存在父符号表</returns>
public bool TryGetParent([NotNullWhen(true)] out SymbolTable? parent)
{
if (_parent is null)
{
parent = null;
return false;
}
parent = _parent;
return true;
}
private IEnumerable<SymbolTable> GetParents()
{
SymbolTable? now = _parent;

View File

@@ -0,0 +1,55 @@
using Canon.Core.Abstractions;
using Canon.Core.SyntaxNodes;
namespace Canon.Core.SemanticParser;
public class SyntaxTreeTraveller
{
private readonly Stack<SyntaxNodeBase> _stack = [];
private readonly HashSet<SyntaxNodeBase> _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<NonTerminatedSyntaxNode>();
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);
}
}
}
}

View File

@@ -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<TypeCheckVisitor>? 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<Period> 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
});
}
/// <summary>
/// 存储定义函数过程中的参数列表
/// 考虑到不同的ValueParameter
/// ValueParameter的顺序是正确的
/// 但ValueParameter中的顺序是相反的
/// 因此设置二维数组存储
/// </summary>
private List<Symbol>? _parameters;
/// <summary>
/// 多个ValueParameter下定义的参数列表
/// </summary>
private readonly List<List<Symbol>> _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<PascalParameterType> parameters = [];
// 正序遍历_valueParameter
// 倒序遍历其中的列表
foreach (List<Symbol> 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);
}
}