parent
1a0d3c37db
commit
c0a8e25d45
25
Canon.Core/Enums/BasicType.cs
Normal file
25
Canon.Core/Enums/BasicType.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace Canon.Core.Enums;
|
||||
|
||||
public enum BasicType
|
||||
{
|
||||
/// <summary>
|
||||
/// 整数类型
|
||||
/// </summary>
|
||||
Integer,
|
||||
/// <summary>
|
||||
/// 浮点数类型
|
||||
/// </summary>
|
||||
Real,
|
||||
/// <summary>
|
||||
/// 布尔类型
|
||||
/// </summary>
|
||||
Boolean,
|
||||
/// <summary>
|
||||
/// 字符类型
|
||||
/// </summary>
|
||||
Character,
|
||||
/// <summary>
|
||||
/// 空类型
|
||||
/// </summary>
|
||||
Void
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
/// 标识符类型基类
|
||||
/// </summary>
|
||||
public abstract class IdentifierType;
|
||||
|
||||
public class BasicType : IdentifierType
|
||||
{
|
||||
public BasicIdType IdType;
|
||||
|
||||
public BasicType(BasicIdType basicIdType)
|
||||
{
|
||||
IdType = basicIdType;
|
||||
}
|
||||
|
||||
public static bool operator ==(BasicType a, BasicType b)
|
||||
{
|
||||
return a.IdType == b.IdType;
|
||||
}
|
||||
|
||||
public static bool operator !=(BasicType a, BasicType b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
|
||||
public class ArrayType : IdentifierType
|
||||
{
|
||||
public int Dimension;
|
||||
|
||||
public List<Limits> LimitsList;
|
||||
|
||||
public IdentifierType ElementType;
|
||||
|
||||
public ArrayType(int dimension, List<Limits> limitsList, IdentifierType elementType)
|
||||
{
|
||||
Dimension = dimension;
|
||||
LimitsList = limitsList;
|
||||
ElementType = elementType;
|
||||
}
|
||||
|
||||
public static bool operator ==(ArrayType a, ArrayType b)
|
||||
{
|
||||
if (a.Dimension != b.Dimension || a.ElementType != b.ElementType || a.LimitsList.Count != b.LimitsList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = a.LimitsList.Count;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (a.LimitsList[i] != b.LimitsList[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool operator !=(ArrayType a, ArrayType b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
|
||||
public class FuncType : IdentifierType
|
||||
{
|
||||
public List<Param> ParamTypeList;
|
||||
|
||||
public IdentifierType ReturnType;
|
||||
|
||||
public FuncType(List<Param> paramTypeList, IdentifierType returnType)
|
||||
{
|
||||
ParamTypeList = paramTypeList;
|
||||
ReturnType = returnType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcType : IdentifierType
|
||||
{
|
||||
public List<Param> ParamTypeList;
|
||||
|
||||
public ProcType()
|
||||
{
|
||||
ParamTypeList = new List<Param>();
|
||||
}
|
||||
|
||||
public ProcType(List<Param> paramTypeList)
|
||||
{
|
||||
ParamTypeList = paramTypeList;
|
||||
}
|
||||
}
|
||||
|
||||
public class RecordType : IdentifierType
|
||||
{
|
||||
public Dictionary<string, IdentifierType> MemberDic;
|
||||
|
||||
public RecordType()
|
||||
{
|
||||
MemberDic = new Dictionary<string, IdentifierType>();
|
||||
}
|
||||
|
||||
public static bool operator ==(RecordType a, RecordType b)
|
||||
{
|
||||
if (a.MemberDic.Count != b.MemberDic.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var k in a.MemberDic.Keys)
|
||||
{
|
||||
if (!b.MemberDic.ContainsKey(k) || a.MemberDic[k] != b.MemberDic[k])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool operator !=(RecordType a, RecordType b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 空类型,用于充当procedure的返回值
|
||||
/// </summary>
|
||||
public class NonType : IdentifierType
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class Limits
|
||||
{
|
||||
public uint LowerBound;
|
||||
|
||||
public uint UpperBound;
|
||||
|
||||
public Limits(uint lowerBound, uint upperBound)
|
||||
{
|
||||
LowerBound = lowerBound;
|
||||
UpperBound = upperBound;
|
||||
}
|
||||
|
||||
public static bool operator ==(Limits a, Limits b)
|
||||
{
|
||||
return a.LowerBound == b.LowerBound && a.UpperBound == b.UpperBound;
|
||||
}
|
||||
|
||||
public static bool operator !=(Limits a, Limits b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
|
||||
public class Param
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public IdentifierType Type;
|
||||
|
||||
public bool IsVar;
|
||||
|
||||
public Param(string name, IdentifierType type, bool isVar)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
IsVar = isVar;
|
||||
}
|
||||
}
|
39
Canon.Core/SemanticParser/PascalArrayType.cs
Normal file
39
Canon.Core/SemanticParser/PascalArrayType.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
public class PascalArrayType(PascalType elementType, int begin, int end) : PascalType
|
||||
{
|
||||
public PascalType ElementType { get; } = elementType;
|
||||
|
||||
public int Begin { get; } = begin;
|
||||
|
||||
public int End { get; } = end;
|
||||
|
||||
public override string TypeName => $"{ElementType.TypeName}_{Begin}_{End}";
|
||||
|
||||
public override bool Equals(PascalType? other)
|
||||
{
|
||||
if (other is not PascalArrayType pascalArrayType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ElementType != pascalArrayType.ElementType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Begin != pascalArrayType.Begin || End != pascalArrayType.End)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ElementType.GetHashCode()
|
||||
^ Begin.GetHashCode()
|
||||
^ End.GetHashCode();
|
||||
}
|
||||
}
|
62
Canon.Core/SemanticParser/PascalBasicType.cs
Normal file
62
Canon.Core/SemanticParser/PascalBasicType.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using Canon.Core.Enums;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
/// 基础Pascal类型
|
||||
/// </summary>
|
||||
public class PascalBasicType : PascalType
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础类型
|
||||
/// </summary>
|
||||
public BasicType Type { get; }
|
||||
|
||||
public override string TypeName { get; }
|
||||
|
||||
private PascalBasicType(BasicType basicType, string typeName)
|
||||
{
|
||||
Type = basicType;
|
||||
TypeName = typeName;
|
||||
}
|
||||
|
||||
public override bool Equals(PascalType? other)
|
||||
{
|
||||
if (other is not PascalBasicType pascalBasicType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Type == pascalBasicType.Type;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Type.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 整数类型的单例对象
|
||||
/// </summary>
|
||||
public static PascalType Integer => new PascalBasicType(BasicType.Integer, "integer");
|
||||
|
||||
/// <summary>
|
||||
/// 布尔类型的单例对象
|
||||
/// </summary>
|
||||
public static PascalType Boolean => new PascalBasicType(BasicType.Boolean, "boolean");
|
||||
|
||||
/// <summary>
|
||||
/// 字符类型的单例对象
|
||||
/// </summary>
|
||||
public static PascalType Character => new PascalBasicType(BasicType.Character, "char");
|
||||
|
||||
/// <summary>
|
||||
/// 浮点数类型的单例对象
|
||||
/// </summary>
|
||||
public static PascalType Real => new PascalBasicType(BasicType.Real, "real");
|
||||
|
||||
/// <summary>
|
||||
/// 空类型的单例对象
|
||||
/// </summary>
|
||||
public static PascalType Void => new PascalBasicType(BasicType.Void, "void");
|
||||
}
|
61
Canon.Core/SemanticParser/PascalFunctionType.cs
Normal file
61
Canon.Core/SemanticParser/PascalFunctionType.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
public class PascalFunctionType(List<PascalParameterType> parameters, PascalType returnType) : PascalType
|
||||
{
|
||||
public List<PascalParameterType> Parameters { get; } = parameters;
|
||||
|
||||
public PascalType ReturnType { get; } = returnType;
|
||||
|
||||
public override string TypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder builder = new();
|
||||
|
||||
foreach (PascalParameterType parameter in Parameters)
|
||||
{
|
||||
builder.Append(parameter.TypeName).Append('_');
|
||||
}
|
||||
|
||||
builder.Append(ReturnType.TypeName);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(PascalType? other)
|
||||
{
|
||||
if (other is not PascalFunctionType functionType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Parameters.Count != functionType.Parameters.Count || ReturnType != functionType.ReturnType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Parameters.Count; i++)
|
||||
{
|
||||
if (Parameters[i] != functionType.Parameters[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int code = ReturnType.GetHashCode();
|
||||
|
||||
foreach (PascalParameterType parameter in Parameters)
|
||||
{
|
||||
code ^= parameter.GetHashCode();
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
38
Canon.Core/SemanticParser/PascalParameterType.cs
Normal file
38
Canon.Core/SemanticParser/PascalParameterType.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
public class PascalParameterType(PascalType parameterType, bool isVar) : PascalType
|
||||
{
|
||||
public PascalType ParameterType { get; } = parameterType;
|
||||
|
||||
public bool IsVar { get; } = isVar;
|
||||
|
||||
public override string TypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsVar)
|
||||
{
|
||||
return $"var_{ParameterType.TypeName}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ParameterType.TypeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(PascalType? other)
|
||||
{
|
||||
if (other is not PascalParameterType parameterType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ParameterType == parameterType.ParameterType && IsVar == parameterType.IsVar;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ParameterType.GetHashCode() ^ IsVar.GetHashCode();
|
||||
}
|
||||
}
|
47
Canon.Core/SemanticParser/PascalType.cs
Normal file
47
Canon.Core/SemanticParser/PascalType.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
/// Pascal类型基类
|
||||
/// </summary>
|
||||
public abstract class PascalType : IEquatable<PascalType>
|
||||
{
|
||||
/// <summary>
|
||||
/// 类型的名称
|
||||
/// </summary>
|
||||
public abstract string TypeName { get; }
|
||||
|
||||
public virtual bool Equals(PascalType? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TypeName == other.TypeName;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is PascalType other)
|
||||
{
|
||||
return Equals(other);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return TypeName.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(PascalType a, PascalType b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(PascalType a, PascalType b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
|
@ -1,731 +0,0 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.Exceptions;
|
||||
using Canon.Core.GrammarParser;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
public class SemanticBuilder
|
||||
{
|
||||
private SymbolTable _curSymbolTable; //当前符号表
|
||||
|
||||
private Stack<SymbolTable> _stack;
|
||||
|
||||
public SemanticBuilder()
|
||||
{
|
||||
_curSymbolTable = new SymbolTable();
|
||||
_stack = new Stack<SymbolTable>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行语义分析
|
||||
/// </summary>
|
||||
/// <param name="root">语法树根节点</param>
|
||||
public void Build(SyntaxNode root)
|
||||
{
|
||||
//新建一个符号表,压入栈
|
||||
_stack.Push(_curSymbolTable);
|
||||
//开始递归调用,完成构建符号表和类型检查
|
||||
SolveProgramStruct(root);
|
||||
}
|
||||
|
||||
|
||||
private void SolveProgramStruct(SyntaxNode root)
|
||||
{
|
||||
SolveProgramHead(root.Children[0]);
|
||||
SolveProgramBody(root.Children[2]);
|
||||
}
|
||||
|
||||
private void SolveProgramHead(SyntaxNode root)
|
||||
{
|
||||
//不做任何处理
|
||||
}
|
||||
|
||||
private void SolveProgramBody(SyntaxNode root)
|
||||
{
|
||||
SolveConstDeclarations(root.Children[0]);
|
||||
SolveVarDeclarations(root.Children[1]);
|
||||
SolveSubprogramDeclarations(root.Children[2]);
|
||||
SolveCompoundStatement(root.Children[3]);
|
||||
}
|
||||
|
||||
private void SolveConstDeclarations(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveConstDeclaration(root.Children[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SolveConstDeclaration(SyntaxNode root)
|
||||
{
|
||||
int offset = 0;
|
||||
if (!root.Children[0].IsTerminated)
|
||||
{
|
||||
SolveConstDeclaration(root.Children[0]);
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
var idName = root.Children[offset].GetSemanticToken().LiteralValue;
|
||||
_curSymbolTable.AddEntry(idName, SolveConstValue(root.Children[offset+2]), true, false);
|
||||
}
|
||||
|
||||
private IdentifierType SolveConstValue(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count == 3)
|
||||
{
|
||||
return new BasicType(BasicIdType.Char);
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = ((NumberSemanticToken)root.GetSemanticToken()).NumberType;
|
||||
if (t == NumberType.Real)
|
||||
{
|
||||
return new BasicType(BasicIdType.Real);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BasicType(BasicIdType.Int);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SolveVarDeclarations(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveVarDeclaration(root.Children[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SolveVarDeclaration(SyntaxNode root)
|
||||
{
|
||||
int offset = 0;
|
||||
if (root.Children.Count > 3)
|
||||
{
|
||||
SolveVarDeclaration(root.Children[0]);
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
List<string> idList = new List<string>();
|
||||
SolveIdList(root.Children[offset], idList); //获取待定义的标识符列表
|
||||
|
||||
var type = SolveType(root.Children[offset + 2]); //获取类型
|
||||
|
||||
//将符号批量插入符号表
|
||||
foreach(var id in idList)
|
||||
{
|
||||
_curSymbolTable.AddEntry(id, type, false, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SolveIdList(SyntaxNode root, List<string> idList)
|
||||
{
|
||||
if (root.IsTerminated)
|
||||
{
|
||||
var word = root.GetSemanticToken().LiteralValue;
|
||||
if (word != ",")
|
||||
{
|
||||
idList.Add(word);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in root.Children)
|
||||
{
|
||||
SolveIdList(child, idList);
|
||||
}
|
||||
}
|
||||
|
||||
private IdentifierType SolveType(SyntaxNode root)
|
||||
{
|
||||
string typeName = root.Children[0].Children[0].GetSemanticToken().LiteralValue;
|
||||
//基本类型或记录类型
|
||||
if (root.Children.Count == 1)
|
||||
{
|
||||
//逐层向外检查类型是否在类型表中
|
||||
var cur = _curSymbolTable;
|
||||
while (cur != null && !cur.TypesTable.Check(typeName))
|
||||
{
|
||||
cur = cur.PreTable;
|
||||
}
|
||||
|
||||
if (cur != null)
|
||||
{
|
||||
return cur.TypesTable.GetTypeByName(typeName);
|
||||
}
|
||||
|
||||
throw new SemanticException(typeName + " is an undefined type!");
|
||||
}
|
||||
|
||||
|
||||
//数组类型
|
||||
var limitList = new List<Limits>();
|
||||
SolvePeriod(root.Children[2], limitList);
|
||||
return new ArrayType(limitList.Count, limitList, SolveType(root.Children[5]));
|
||||
}
|
||||
|
||||
private void SolvePeriod(SyntaxNode root, List<Limits> limitsList)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
if (!root.Children[0].IsTerminated)
|
||||
{
|
||||
SolvePeriod(root.Children[0], limitsList); //递归获取子节点的界限列表
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
//token1和2对应数组当前维度的上下界
|
||||
var token1 = (NumberSemanticToken)root.Children[offset].GetSemanticToken();
|
||||
var t1 = token1.NumberType;
|
||||
var token2 = (NumberSemanticToken)root.Children[offset + 3].GetSemanticToken();
|
||||
var t2 = token2.NumberType;
|
||||
|
||||
//检查数组上下界定义是否合法
|
||||
if (t1 == NumberType.Integer && t2 == NumberType.Integer)
|
||||
{
|
||||
int lower = int.Parse(token1.LiteralValue);
|
||||
int upper = int.Parse(token2.LiteralValue);
|
||||
if (upper >= lower)
|
||||
{
|
||||
if (lower >= 0)
|
||||
{
|
||||
limitsList.Add(new Limits((uint)lower, (uint)upper));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new SemanticException("Array's bounds must be nonnegative!");
|
||||
}
|
||||
|
||||
throw new SemanticException("Array's upper bound must greater than low bound!");
|
||||
}
|
||||
|
||||
throw new SemanticException("Array's bounds must be integer!");
|
||||
}
|
||||
|
||||
private void SolveSubprogramDeclarations(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveSubprogramDeclarations(root.Children[0]);
|
||||
SolveSubprogram(root.Children[1]);
|
||||
//处理完一个子过程/函数,将对应符号表出栈,当前符号表更新为新栈顶符号表
|
||||
_stack.Pop();
|
||||
_curSymbolTable = _stack.Peek();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SolveSubprogram(SyntaxNode root)
|
||||
{
|
||||
SolveSubprogramHead(root.Children[0]);
|
||||
SolveSubprogramBody(root.Children[2]);
|
||||
}
|
||||
|
||||
private void SolveSubprogramHead(SyntaxNode root)
|
||||
{
|
||||
//获取参数列表信息
|
||||
List<Param> paramList = new List<Param>();
|
||||
SolveFormalParameter(root.Children[2], paramList);
|
||||
|
||||
//区分procedure和function
|
||||
IdentifierType identifierType;
|
||||
if (root.Children.Count == 3)
|
||||
{
|
||||
identifierType = new ProcType(paramList);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = SolveType(root.Children[4]); //获取函数返回值
|
||||
identifierType = new FuncType(paramList, type);
|
||||
}
|
||||
|
||||
//创建子符号表
|
||||
SymbolTable subTable = new SymbolTable(_curSymbolTable);
|
||||
|
||||
//将参数列表参数拷贝一份到子符号表
|
||||
foreach (var param in paramList)
|
||||
{
|
||||
subTable.AddEntry(param.Name, param.Type, false, param.IsVar);
|
||||
}
|
||||
|
||||
//将proc/func头定义写入当前符号表
|
||||
_curSymbolTable.AddEntry(root.Children[1].GetSemanticToken().LiteralValue, identifierType, subTable);
|
||||
|
||||
//子符号表入栈
|
||||
_stack.Push(subTable);
|
||||
//更新当前符号表为子符号表
|
||||
_curSymbolTable = subTable;
|
||||
}
|
||||
|
||||
private void SolveFormalParameter(SyntaxNode root, List<Param> paramList)
|
||||
{
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveParameterList(root.Children[1], paramList);
|
||||
}
|
||||
}
|
||||
|
||||
private void SolveParameterList(SyntaxNode root, List<Param> paramList)
|
||||
{
|
||||
int offset = 0;
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveParameterList(root, paramList);
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
SolveParameter(root.Children[offset], paramList);
|
||||
}
|
||||
|
||||
private void SolveParameter(SyntaxNode root, List<Param> paramList)
|
||||
{
|
||||
bool isVarParam = false;
|
||||
SyntaxNode node = root.Children[0];
|
||||
if (node.GetNonTerminatorType() == NonTerminatorType.VarParameter)
|
||||
{
|
||||
isVarParam = true;
|
||||
node = node.Children[1];
|
||||
}
|
||||
|
||||
List<string> list = new List<string>();
|
||||
SolveIdList(node.Children[0], list);
|
||||
var type = SolveType(node.Children[2]);
|
||||
|
||||
foreach (var idName in list)
|
||||
{
|
||||
paramList.Add(new Param(idName, type, isVarParam));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SolveSubprogramBody(SyntaxNode root)
|
||||
{
|
||||
SolveConstDeclarations(root.Children[0]);
|
||||
SolveVarDeclarations(root.Children[1]);
|
||||
SolveCompoundStatement(root.Children[2]);
|
||||
}
|
||||
|
||||
private void SolveCompoundStatement(SyntaxNode root)
|
||||
{
|
||||
SolveStatementList(root.Children[1]);
|
||||
}
|
||||
|
||||
private void SolveStatementList(SyntaxNode root)
|
||||
{
|
||||
int offset = 0;
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveStatementList(root.Children[0]);
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
SolveStatement(root.Children[offset]);
|
||||
}
|
||||
|
||||
private void SolveStatement(SyntaxNode root)
|
||||
{
|
||||
var node = root.Children[0];
|
||||
if (node.IsTerminated)
|
||||
{
|
||||
//通过子节点个数判断用的statement的哪个产生式
|
||||
int childCount = root.Children.Count;
|
||||
switch (childCount)
|
||||
{
|
||||
case 3:
|
||||
CheckFuncAssign(root);
|
||||
break;
|
||||
case 5:
|
||||
SolveIf(root);
|
||||
break;
|
||||
case 8:
|
||||
CheckForLoop(root);
|
||||
break;
|
||||
default:
|
||||
throw new SemanticException("Semantic analysis failed and an illegal node was detected");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var nonTerminatorType = node.GetNonTerminatorType();
|
||||
switch (nonTerminatorType)
|
||||
{
|
||||
case NonTerminatorType.Variable:
|
||||
SolveVariableAssign(root);
|
||||
break;
|
||||
case NonTerminatorType.ProcedureCall:
|
||||
SolveCall(node, false);
|
||||
break;
|
||||
case NonTerminatorType.CompoundStatement:
|
||||
SolveCompoundStatement(node);
|
||||
break;
|
||||
default:
|
||||
throw new SemanticException("Semantic analysis failed and an illegal node was detected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理表达式
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/// <returns>表达式类型</returns>
|
||||
private IdentifierType SolveExpression(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count == 1)
|
||||
{
|
||||
return SolveSimpleExpression(root.Children[0]);
|
||||
}
|
||||
|
||||
var type1 = SolveSimpleExpression(root.Children[0]);
|
||||
var type2 = SolveSimpleExpression(root.Children[2]);
|
||||
|
||||
return CheckRelationOperation(type1, type2);
|
||||
}
|
||||
|
||||
private IdentifierType SolveSimpleExpression(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count == 1)
|
||||
{
|
||||
return SolveTerm(root.Children[0]);
|
||||
}
|
||||
|
||||
var type1 = SolveSimpleExpression(root.Children[0]);
|
||||
var type2 = SolveTerm(root.Children[2]);
|
||||
|
||||
return CheckAddOperation(type1, type2, root.Children[1].Children[0].GetSemanticToken());
|
||||
}
|
||||
|
||||
private IdentifierType SolveTerm(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count == 1)
|
||||
{
|
||||
return SolveFactor(root.Children[0]);
|
||||
}
|
||||
|
||||
var type1 = SolveTerm(root.Children[0]);
|
||||
var type2 = SolveFactor(root.Children[2]);
|
||||
|
||||
return CheckMultiplyOperation(type1, type2, root.Children[1].Children[0].GetSemanticToken());
|
||||
}
|
||||
|
||||
private IdentifierType SolveFactor(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count == 1)
|
||||
{
|
||||
if (root.Children[0].IsTerminated)
|
||||
{
|
||||
var numberSemanticToken = (NumberSemanticToken)root.Children[0].GetSemanticToken();
|
||||
if (numberSemanticToken.NumberType == NumberType.Real)
|
||||
{
|
||||
return _curSymbolTable.TypesTable.GetTypeByName("real");
|
||||
}
|
||||
|
||||
return _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
}
|
||||
|
||||
return SolveVariable(root.Children[0]);
|
||||
}
|
||||
|
||||
//处理 Factor -> (expression)
|
||||
if (root.Children.Count == 3)
|
||||
{
|
||||
return SolveExpression(root.Children[1]);
|
||||
}
|
||||
|
||||
//函数掉用
|
||||
if (root.Children.Count == 4)
|
||||
{
|
||||
return SolveCall(root, true);
|
||||
}
|
||||
|
||||
//处理 Factor -> not Factor
|
||||
if (root.Children[0].GetSemanticToken() == new Terminator(KeywordType.Not))
|
||||
{
|
||||
var type = SolveFactor(root.Children[1]);
|
||||
if (type == _curSymbolTable.TypesTable.GetTypeByName("boolean"))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
throw new SemanticException("NOT can only be used for Boolean expressions");
|
||||
}
|
||||
|
||||
//处理数字取负
|
||||
var type1 = SolveFactor(root.Children[1]);
|
||||
if (type1 == _curSymbolTable.TypesTable.GetTypeByName("integer") ||
|
||||
type1 == _curSymbolTable.TypesTable.GetTypeByName("real"))
|
||||
{
|
||||
return type1;
|
||||
}
|
||||
|
||||
throw new SemanticException("minus can only be used on integer or real");
|
||||
}
|
||||
|
||||
private void SolveIf(SyntaxNode root)
|
||||
{
|
||||
var boolType = _curSymbolTable.TypesTable.GetTypeByName("boolean");
|
||||
if (SolveExpression(root.Children[0]) != boolType)
|
||||
{
|
||||
throw new SemanticException("The conditional expression of the if statement must be of type Boolean");
|
||||
}
|
||||
|
||||
SolveStatement(root.Children[3]);
|
||||
SolveElsePart(root.Children[4]);
|
||||
}
|
||||
|
||||
private void SolveElsePart(SyntaxNode root)
|
||||
{
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveStatement(root.Children[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForLoop(SyntaxNode root)
|
||||
{
|
||||
var intType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
string idName = root.Children[1].GetSemanticToken().LiteralValue;
|
||||
if (_curSymbolTable.Find(idName) && _curSymbolTable.GetIdTypeByName(idName) == intType)
|
||||
{
|
||||
if (SolveExpression(root.Children[3]) == intType && SolveExpression(root.Children[5]) == intType)
|
||||
{
|
||||
SolveStatement(root.Children[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SemanticException("The upper and lower bounds of the loop variable in the for loop" +
|
||||
" must be set to integer");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SemanticException("The loop variable in the for loop must be integer");
|
||||
}
|
||||
}
|
||||
|
||||
private void SolveVariableAssign(SyntaxNode root)
|
||||
{
|
||||
var varType = SolveVariable(root.Children[0]);
|
||||
var expType = SolveExpression(root.Children[2]);
|
||||
CheckAssign(varType, expType);
|
||||
|
||||
//常量不允许被重复赋值
|
||||
var idName = root.Children[0].GetSemanticToken().LiteralValue;
|
||||
if (_curSymbolTable.IsConst(idName))
|
||||
{
|
||||
throw new SemanticException("Constants defined as const are not allowed to be assigned repeatedly");
|
||||
}
|
||||
}
|
||||
|
||||
private IdentifierType SolveVariable(SyntaxNode root)
|
||||
{
|
||||
var idName = root.Children[0].GetSemanticToken().LiteralValue;
|
||||
var idType = _curSymbolTable.GetIdTypeByName(idName);
|
||||
if (idType is BasicType)
|
||||
{
|
||||
if (root.Children[1].Children.Count == 1)
|
||||
{
|
||||
return idType;
|
||||
}
|
||||
|
||||
throw new SemanticException("The reference to variable "+ idName+ " is illegal");
|
||||
}
|
||||
|
||||
//暂时只考虑数组类型
|
||||
List<IdentifierType> typeList = new List<IdentifierType>();
|
||||
SolveExpressionList(root.Children[1].Children[1], typeList);
|
||||
|
||||
int dimension = ((ArrayType)idType).Dimension;
|
||||
//数组引用维数一致
|
||||
if (typeList.Count == dimension)
|
||||
{
|
||||
//每个维度的表达式类型都是int
|
||||
var IntType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
foreach (var t in typeList)
|
||||
{
|
||||
if (t != IntType)
|
||||
{
|
||||
throw new SemanticException("Array's index must be integer!");
|
||||
}
|
||||
}
|
||||
|
||||
return ((ArrayType)idType).ElementType;
|
||||
}
|
||||
|
||||
throw new SemanticException("The reference to array " + idName + " requires " + dimension + " subscripts");
|
||||
}
|
||||
|
||||
private void SolveExpressionList(SyntaxNode root, List<IdentifierType> typeList)
|
||||
{
|
||||
int offset = 0;
|
||||
if (root.Children.Count > 1)
|
||||
{
|
||||
SolveExpressionList(root.Children[0], typeList);
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
typeList.Add(SolveExpression(root.Children[offset]));
|
||||
}
|
||||
|
||||
private IdentifierType SolveCall(SyntaxNode root, bool isFunc)
|
||||
{
|
||||
var idName = root.Children[0].GetSemanticToken().LiteralValue;
|
||||
IdentifierType idType = _curSymbolTable.GetIdTypeByName(idName);
|
||||
//获取调用表达式的类型列表
|
||||
List<IdentifierType> typeList = new List<IdentifierType>();
|
||||
SolveExpressionList(root.Children[2], typeList);
|
||||
//将调用类型列表和定义参数列表的类型一一比对
|
||||
var paramList = isFunc ? ((FuncType)idType).ParamTypeList : ((ProcType)idType).ParamTypeList;
|
||||
if (paramList.Count == typeList.Count)
|
||||
{
|
||||
int n = paramList.Count;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (paramList[i].Type != typeList[i])
|
||||
{
|
||||
throw new SemanticException("The parameter types are inconsistent");
|
||||
}
|
||||
}
|
||||
|
||||
return isFunc ? ((FuncType)idType).ReturnType : new NonType();
|
||||
}
|
||||
|
||||
throw new SemanticException("The number of parameters of procedure is inconsistent");
|
||||
}
|
||||
|
||||
private void CheckFuncAssign(SyntaxNode root)
|
||||
{
|
||||
if (_curSymbolTable.PreTable is null)
|
||||
{
|
||||
throw new SemanticException("Not allowed to assign a value to a function name in the main program");
|
||||
}
|
||||
//获取函数返回值类型
|
||||
var idType =
|
||||
((FuncType)_curSymbolTable.PreTable.GetIdTypeByName(root.Children[0].GetSemanticToken().LiteralValue)).ReturnType;
|
||||
//获取右侧表达式类型
|
||||
var expressionType = SolveExpression(root.Children[2]);
|
||||
//对赋值进行类型检查
|
||||
CheckAssign(idType, expressionType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查赋值语句左右部分类型是否相容
|
||||
/// </summary>
|
||||
/// <param name="leftType">赋值号左边类型(若为函数,则取返回值)</param>
|
||||
/// <param name="rightType">赋值号右边类型</param>
|
||||
private void CheckAssign(IdentifierType leftType, IdentifierType rightType)
|
||||
{
|
||||
if (leftType == rightType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var intType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
var realType = _curSymbolTable.TypesTable.GetTypeByName("real");
|
||||
|
||||
if (leftType == realType && rightType == intType) //int可以赋值给real
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in assign operation");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查关系操作(比大小)的操作数类型
|
||||
/// </summary>
|
||||
/// <param name="leftType">左边符号类型</param>
|
||||
/// <param name="rightType">右边符号类型</param>
|
||||
/// <returns>成功则返回boolean类型</returns>
|
||||
private IdentifierType CheckRelationOperation(IdentifierType leftType, IdentifierType rightType)
|
||||
{
|
||||
var intType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
var realType = _curSymbolTable.TypesTable.GetTypeByName("real");
|
||||
var boolType = _curSymbolTable.TypesTable.GetTypeByName("boolean");
|
||||
|
||||
//左右为相等的基本类型或 一个int和一个real
|
||||
if (leftType == rightType && leftType is BasicType ||
|
||||
leftType == intType && rightType == realType ||
|
||||
leftType == realType && rightType == intType)
|
||||
{
|
||||
return boolType;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in relation operations");
|
||||
}
|
||||
|
||||
|
||||
private IdentifierType CheckAddOperation(IdentifierType leftType, IdentifierType rightType, SemanticToken semanticToken)
|
||||
{
|
||||
// or操作两边必为boolean
|
||||
var boolType = _curSymbolTable.TypesTable.GetTypeByName("boolean");
|
||||
if (semanticToken == new Terminator(KeywordType.Or))
|
||||
{
|
||||
if (leftType == boolType && rightType == boolType)
|
||||
{
|
||||
return boolType;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in add operation \"or\"");
|
||||
}
|
||||
|
||||
var intType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
var realType = _curSymbolTable.TypesTable.GetTypeByName("real");
|
||||
|
||||
//左右为相等的基本类型但不为boolean
|
||||
if (leftType == rightType && leftType is BasicType && leftType != boolType)
|
||||
{
|
||||
return leftType;
|
||||
}
|
||||
//int和real可兼容
|
||||
if (leftType == intType && rightType == realType || leftType == realType && rightType == intType)
|
||||
{
|
||||
return realType;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in add operations");
|
||||
}
|
||||
|
||||
private IdentifierType CheckMultiplyOperation(IdentifierType leftType, IdentifierType rightType, SemanticToken semanticToken)
|
||||
{
|
||||
// and操作两边必为boolean
|
||||
var boolType = _curSymbolTable.TypesTable.GetTypeByName("boolean");
|
||||
if (semanticToken == new Terminator(KeywordType.And))
|
||||
{
|
||||
if (leftType == boolType && rightType == boolType)
|
||||
{
|
||||
return boolType;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in multiply operation \"and\"");
|
||||
}
|
||||
|
||||
// div和mod操作数必为int
|
||||
var intType = _curSymbolTable.TypesTable.GetTypeByName("integer");
|
||||
if (semanticToken == new Terminator(KeywordType.Mod) || semanticToken == new Terminator(KeywordType.Divide))
|
||||
{
|
||||
if (leftType == intType && rightType == intType)
|
||||
{
|
||||
return intType;
|
||||
}
|
||||
throw new SemanticException("Incompatible types in multiply operation \"mod/div\"");
|
||||
}
|
||||
|
||||
//都是int或都是real
|
||||
var realType = _curSymbolTable.TypesTable.GetTypeByName("real");
|
||||
if (leftType == intType && rightType == intType || leftType == realType && rightType == realType)
|
||||
{
|
||||
return leftType;
|
||||
}
|
||||
//一个是int,另一个real
|
||||
if (leftType == intType && rightType == realType || leftType == realType && rightType == intType)
|
||||
{
|
||||
return realType;
|
||||
}
|
||||
|
||||
throw new SemanticException("Incompatible types in multiply operations");
|
||||
}
|
||||
}
|
58
Canon.Core/SemanticParser/Symbol.cs
Normal file
58
Canon.Core/SemanticParser/Symbol.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
/// 符号表表项类
|
||||
/// </summary>
|
||||
public class Symbol : IEquatable<Symbol>
|
||||
{
|
||||
/// <summary>
|
||||
/// 符号的名称
|
||||
/// </summary>
|
||||
public required string SymbolName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 符号的类型
|
||||
/// </summary>
|
||||
public required PascalType SymbolType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为常量
|
||||
/// </summary>
|
||||
public bool Const { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为引用变量
|
||||
/// </summary>
|
||||
public bool Reference { get; init; }
|
||||
|
||||
public bool Equals(Symbol? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SymbolName == other.SymbolName
|
||||
&& SymbolType == other.SymbolType
|
||||
&& Const == other.Const
|
||||
&& Reference == other.Reference;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return SymbolName.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Symbol other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,107 +1,109 @@
|
|||
using Canon.Core.Exceptions;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
///符号表类
|
||||
/// </summary>
|
||||
public class SymbolTable
|
||||
{
|
||||
public Dictionary<string, SymbolTableEntry> Entries;
|
||||
/// <summary>
|
||||
/// 符号表
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, Symbol> _symbols = [];
|
||||
|
||||
public TypeTable TypesTable; //当前符号表对应的类型表
|
||||
/// <summary>
|
||||
/// 类型表
|
||||
/// </summary>
|
||||
private readonly TypeTable _typeTable = new();
|
||||
|
||||
public SymbolTable? PreTable; //直接外围符号表
|
||||
/// <summary>
|
||||
/// 父符号表
|
||||
/// </summary>
|
||||
private readonly SymbolTable? _parent;
|
||||
|
||||
public SymbolTable()
|
||||
/// <summary>
|
||||
/// 获得当前符号表的所有父符号表
|
||||
/// </summary>
|
||||
public IEnumerable<SymbolTable> ParentTables => GetParents();
|
||||
|
||||
public SymbolTable() {}
|
||||
|
||||
private SymbolTable(SymbolTable parent)
|
||||
{
|
||||
Entries = new Dictionary<string, SymbolTableEntry>();
|
||||
TypesTable = new TypeTable();
|
||||
PreTable = null;
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
public SymbolTable(SymbolTable preTable)
|
||||
public SymbolTable CreateChildTable()
|
||||
{
|
||||
Entries = new Dictionary<string, SymbolTableEntry>();
|
||||
TypesTable = new TypeTable();
|
||||
PreTable = preTable;
|
||||
return new SymbolTable(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向符号表里插入一个表项
|
||||
/// 尝试向符号表中添加符号
|
||||
/// </summary>
|
||||
public void AddEntry(string idName, IdentifierType type, bool isConst, bool isVarParam)
|
||||
{
|
||||
if (Check(idName))
|
||||
{
|
||||
throw new SemanticException("failed to insert to SymbolTable! " + idName + " is defined repeatedly");
|
||||
}
|
||||
|
||||
Entries.Add(idName, new SymbolTableEntry(idName, type, isConst, isVarParam));
|
||||
}
|
||||
|
||||
public void AddEntry(string idName, IdentifierType type, SymbolTable subTable)
|
||||
{
|
||||
if (Check(idName))
|
||||
{
|
||||
throw new SemanticException("failed to insert to SymbolTable! " + idName + " is defined repeatedly");
|
||||
}
|
||||
|
||||
Entries.Add(idName, new SymbolTableEntry(idName, type, subTable));
|
||||
}
|
||||
|
||||
/// <param name="symbol">欲添加的符号</param>
|
||||
/// <returns>是否添加成功</returns>
|
||||
public bool TryAddSymbol(Symbol symbol) => _symbols.TryAdd(symbol.SymbolName, symbol);
|
||||
|
||||
/// <summary>
|
||||
///检查符号表,看是否有变量重复声明
|
||||
/// 尝试从符号表极其父符号表查找符号
|
||||
/// </summary>
|
||||
/// <param name="idName">查询的id名称</param>
|
||||
/// <returns>如果变量重复声明,返回true</returns>
|
||||
public bool Check(string idName)
|
||||
/// <param name="name">需要查找的符号名称</param>
|
||||
/// <param name="symbol">查找到的符号</param>
|
||||
/// <returns>是否查找到符号</returns>
|
||||
public bool TryGetSymbol(string name, [NotNullWhen(true)] out Symbol? symbol)
|
||||
{
|
||||
return Entries.ContainsKey(idName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在符号表里查找,看当前引用变量是否声明
|
||||
/// </summary>
|
||||
/// <param name="idName">查询的id名称</param>
|
||||
/// <returns>如果有定义,返回true</returns>
|
||||
public bool Find(string idName)
|
||||
{
|
||||
if (Entries.ContainsKey(idName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (PreTable is not null && PreTable.Entries.ContainsKey(idName))
|
||||
if (_symbols.TryGetValue(name, out symbol))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new SemanticException("identifier "+ idName + " is not defined!");
|
||||
foreach (SymbolTable table in ParentTables)
|
||||
{
|
||||
if (table._symbols.TryGetValue(name, out symbol))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
symbol = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过id名获取id的类型
|
||||
/// 从符号表极其父表的类型表中查找类型
|
||||
/// </summary>
|
||||
/// <param name="idName">id名字</param>
|
||||
/// <returns>id在符号表里的类型</returns>
|
||||
public IdentifierType GetIdTypeByName(string idName)
|
||||
/// <param name="typeName">欲查找的类型名称</param>
|
||||
/// <param name="type">查找到的类型</param>
|
||||
/// <returns>是否查找到类型</returns>
|
||||
public bool TryGetType(string typeName, [NotNullWhen(true)] out PascalType? type)
|
||||
{
|
||||
if (Entries.ContainsKey(idName))
|
||||
if (_typeTable.TryGetType(typeName, out type))
|
||||
{
|
||||
return Entries[idName].Type;
|
||||
}
|
||||
if (PreTable is not null && PreTable.Entries.ContainsKey(idName))
|
||||
{
|
||||
return PreTable.Entries[idName].Type;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new SemanticException("identifier "+ idName + " is not defined!");
|
||||
foreach (SymbolTable parent in ParentTables)
|
||||
{
|
||||
if (parent._typeTable.TryGetType(typeName, out type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
type = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool IsConst(string idName)
|
||||
private IEnumerable<SymbolTable> GetParents()
|
||||
{
|
||||
return Find(idName) && Entries[idName].IsConst;
|
||||
SymbolTable? now = _parent;
|
||||
|
||||
while (now is not null)
|
||||
{
|
||||
yield return now;
|
||||
now = now._parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
namespace Canon.Core.SemanticParser;
|
||||
/// <summary>
|
||||
/// 符号表表项类
|
||||
/// </summary>
|
||||
public class SymbolTableEntry
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public IdentifierType Type;
|
||||
|
||||
public bool IsConst; //是否为常量
|
||||
|
||||
public bool IsVarParam; //是否为引用变量
|
||||
|
||||
public SymbolTable? SubTable; //当前表项的子表
|
||||
|
||||
public SymbolTableEntry(string name, IdentifierType type, bool isConst, bool isVarParam)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
IsConst = isConst;
|
||||
IsVarParam = isVarParam;
|
||||
}
|
||||
|
||||
public SymbolTableEntry(string name, IdentifierType type, SymbolTable subTable)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
IsConst = false;
|
||||
IsVarParam = false;
|
||||
SubTable = subTable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,66 +1,37 @@
|
|||
using System.Security;
|
||||
using Canon.Core.Enums;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security;
|
||||
using Canon.Core.Exceptions;
|
||||
|
||||
namespace Canon.Core.SemanticParser;
|
||||
|
||||
/// <summary>
|
||||
/// 类型表
|
||||
/// </summary>
|
||||
public class TypeTable
|
||||
{
|
||||
private Dictionary<string, IdentifierType> EntryDict { get; init; }
|
||||
|
||||
public TypeTable()
|
||||
private readonly Dictionary<string, PascalType> _types = new()
|
||||
{
|
||||
EntryDict = new Dictionary<string, IdentifierType>();
|
||||
//加入4种基本类型
|
||||
EntryDict.Add("integer", new BasicType(BasicIdType.Int));
|
||||
EntryDict.Add("real", new BasicType(BasicIdType.Real));
|
||||
EntryDict.Add("char", new BasicType(BasicIdType.Char));
|
||||
EntryDict.Add("boolean", new BasicType(BasicIdType.Bool));
|
||||
}
|
||||
|
||||
{ PascalBasicType.Integer.TypeName, PascalBasicType.Integer },
|
||||
{ PascalBasicType.Boolean.TypeName, PascalBasicType.Boolean },
|
||||
{ PascalBasicType.Character.TypeName, PascalBasicType.Character },
|
||||
{ PascalBasicType.Real.TypeName, PascalBasicType.Real }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 判断类型表里是否已经有该类型
|
||||
/// 根据类型名称查找类型表
|
||||
/// </summary>
|
||||
/// <param name="typeName">类型名称</param>
|
||||
/// <returns>如果有,返回true</returns>
|
||||
public bool Check(string typeName)
|
||||
/// <param name="typeName">想要查找的类型名称</param>
|
||||
/// <param name="type">查找到的类型</param>
|
||||
/// <returns>是否查找到类型</returns>
|
||||
public bool TryGetType(string typeName, [NotNullWhen(true)] out PascalType? type)
|
||||
{
|
||||
return EntryDict.ContainsKey(typeName);
|
||||
}
|
||||
if (!_types.ContainsKey(typeName))
|
||||
{
|
||||
type = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 往类型表里添加类型
|
||||
/// </summary>
|
||||
/// <param name="typeName">类型名称</param>
|
||||
/// <param name="identifierType">类型的类别(一般是记录)</param>
|
||||
public void AddEntry(string typeName, IdentifierType identifierType)
|
||||
{
|
||||
if (!Check(typeName))
|
||||
{
|
||||
EntryDict.Add(typeName, identifierType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SemanticException("Failed to add to TypeTable! Types were repeatedly defined");
|
||||
}
|
||||
type = _types[typeName];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由类型名获取类型
|
||||
/// </summary>
|
||||
public IdentifierType GetTypeByName(string typeName)
|
||||
{
|
||||
if (Check(typeName))
|
||||
{
|
||||
return EntryDict[typeName];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SecurityException("Failed to get type from typeTable! Type is not existed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
50
Canon.Tests/SemanticTests/PascalTypeTests.cs
Normal file
50
Canon.Tests/SemanticTests/PascalTypeTests.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using Canon.Core.SemanticParser;
|
||||
|
||||
namespace Canon.Tests.SemanticTests;
|
||||
|
||||
public class PascalTypeTests
|
||||
{
|
||||
[Fact]
|
||||
public void PascalBasicTypeTests()
|
||||
{
|
||||
PascalType integer = PascalBasicType.Integer;
|
||||
PascalType boolean = PascalBasicType.Boolean;
|
||||
PascalType character = PascalBasicType.Character;
|
||||
PascalType real = PascalBasicType.Real;
|
||||
PascalType voidType = PascalBasicType.Void;
|
||||
|
||||
Assert.Equal(integer, PascalBasicType.Integer);
|
||||
Assert.Equal(boolean, PascalBasicType.Boolean);
|
||||
|
||||
Assert.NotEqual(integer, character);
|
||||
Assert.NotEqual(boolean, real);
|
||||
Assert.NotEqual(character, voidType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PascalArrayTypeTests()
|
||||
{
|
||||
PascalType array1 = new PascalArrayType(PascalBasicType.Integer, 0, 10);
|
||||
PascalType array2 = new PascalArrayType(PascalBasicType.Integer, 0, 10);
|
||||
|
||||
Assert.Equal(array1, array2);
|
||||
|
||||
PascalType array3 = new PascalArrayType(PascalBasicType.Integer, -9, -3);
|
||||
Assert.NotEqual(array1, array3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PascalFunctionTypeTests()
|
||||
{
|
||||
PascalType function1 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)],
|
||||
PascalBasicType.Void);
|
||||
PascalType function2 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)],
|
||||
PascalBasicType.Void);
|
||||
|
||||
Assert.Equal(function1, function2);
|
||||
|
||||
PascalType function3 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Real, true)],
|
||||
PascalBasicType.Integer);
|
||||
Assert.NotEqual(function1, function3);
|
||||
}
|
||||
}
|
66
Canon.Tests/SemanticTests/SymbolTableTests.cs
Normal file
66
Canon.Tests/SemanticTests/SymbolTableTests.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using Canon.Core.SemanticParser;
|
||||
|
||||
namespace Canon.Tests.SemanticTests;
|
||||
|
||||
public class SymbolTableTests
|
||||
{
|
||||
[Fact]
|
||||
public void BasicTypeTest()
|
||||
{
|
||||
SymbolTable table = new();
|
||||
|
||||
Assert.True(table.TryGetType("integer", out PascalType? integer));
|
||||
Assert.Equal(PascalBasicType.Integer, integer);
|
||||
Assert.True(table.TryGetType("real", out PascalType? real));
|
||||
Assert.Equal(PascalBasicType.Real, real);
|
||||
Assert.True(table.TryGetType("boolean", out PascalType? boolean));
|
||||
Assert.Equal(PascalBasicType.Boolean, boolean);
|
||||
Assert.True(table.TryGetType("char", out PascalType? character));
|
||||
Assert.Equal(PascalBasicType.Character, character);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleTableInsertAndFindTest()
|
||||
{
|
||||
SymbolTable table = new();
|
||||
|
||||
Assert.True(table.TryAddSymbol(new Symbol { SymbolName = "a", SymbolType = PascalBasicType.Integer }));
|
||||
Assert.True(table.TryAddSymbol(new Symbol
|
||||
{
|
||||
SymbolName = "temperature", SymbolType = PascalBasicType.Real, Const = true
|
||||
}));
|
||||
|
||||
Assert.True(table.TryGetSymbol("a", out Symbol? a));
|
||||
Assert.Equal(PascalBasicType.Integer, a.SymbolType);
|
||||
|
||||
Assert.False(table.TryGetSymbol("notExist", out a));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedTableInsertAndFindTest()
|
||||
{
|
||||
SymbolTable table = new();
|
||||
|
||||
Assert.True(table.TryAddSymbol(new Symbol { SymbolName = "a", SymbolType = PascalBasicType.Integer }));
|
||||
Assert.True(table.TryAddSymbol(new Symbol
|
||||
{
|
||||
SymbolName = "temperature", SymbolType = PascalBasicType.Real, Const = true
|
||||
}));
|
||||
|
||||
SymbolTable child = table.CreateChildTable();
|
||||
|
||||
Assert.True(child.TryAddSymbol(new Symbol{SymbolName = "a", SymbolType = PascalBasicType.Real}));
|
||||
Assert.True(child.TryAddSymbol(new Symbol
|
||||
{
|
||||
SymbolName = "level2", SymbolType = PascalBasicType.Boolean, Reference = true
|
||||
}));
|
||||
|
||||
Assert.True(child.TryGetSymbol("a", out Symbol? a));
|
||||
Assert.Equal(PascalBasicType.Real, a.SymbolType);
|
||||
Assert.True(table.TryGetSymbol("a", out a));
|
||||
Assert.Equal(PascalBasicType.Integer, a.SymbolType);
|
||||
|
||||
Assert.True(table.TryGetSymbol("temperature", out Symbol? temp));
|
||||
Assert.Equal(PascalBasicType.Real, temp.SymbolType);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user