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

Reviewed-on: PostGuard/Canon#56
This commit is contained in:
jackfiled 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,302 @@
using Canon.Core.SyntaxNodes;
namespace Canon.Core.Abstractions;
public abstract class SyntaxNodeVisitor
{
public virtual void PreVisit(AddOperator addOperator)
{
}
public virtual void PostVisit(AddOperator addOperator)
{
}
public virtual void PreVisit(BasicType basicType)
{
}
public virtual void PostVisit(BasicType basicType)
{
}
public virtual void PreVisit(CompoundStatement compoundStatement)
{
}
public virtual void PostVisit(CompoundStatement compoundStatement)
{
}
public virtual void PreVisit(ConstDeclaration constDeclaration)
{
}
public virtual void PostVisit(ConstDeclaration constDeclaration)
{
}
public virtual void PreVisit(ConstDeclarations constDeclarations)
{
}
public virtual void PostVisit(ConstDeclarations constDeclarations)
{
}
public virtual void PreVisit(ConstValue constValue)
{
}
public virtual void PostVisit(ConstValue constValue)
{
}
public virtual void PreVisit(ElsePart elsePart)
{
}
public virtual void PostVisit(ElsePart elsePart)
{
}
public virtual void PreVisit(Expression expression)
{
}
public virtual void PostVisit(Expression expression)
{
}
public virtual void PreVisit(ExpressionList expressionList)
{
}
public virtual void PostVisit(ExpressionList expressionList)
{
}
public virtual void PreVisit(Factor factor)
{
}
public virtual void PostVisit(Factor factor)
{
}
public virtual void PreVisit(FormalParameter formalParameter)
{
}
public virtual void PostVisit(FormalParameter formalParameter)
{
}
public virtual void PreVisit(IdentifierList identifierList)
{
}
public virtual void PostVisit(IdentifierList identifierList)
{
}
public virtual void PreVisit(IdentifierVarPart identifierVarPart)
{
}
public virtual void PostVisit(IdentifierVarPart identifierVarPart)
{
}
public virtual void PreVisit(MultiplyOperator multiplyOperator)
{
}
public virtual void PostVisit(MultiplyOperator multiplyOperator)
{
}
public virtual void PreVisit(Parameter parameter)
{
}
public virtual void PostVisit(Parameter parameter)
{
}
public virtual void PreVisit(ParameterList parameterList)
{
}
public virtual void PostVisit(ParameterList parameterList)
{
}
public virtual void PreVisit(Period period)
{
}
public virtual void PostVisit(Period period)
{
}
public virtual void PreVisit(ProcedureCall procedureCall)
{
}
public virtual void PostVisit(ProcedureCall procedureCall)
{
}
public virtual void PreVisit(ProgramBody programBody)
{
}
public virtual void PostVisit(ProgramBody programBody)
{
}
public virtual void PreVisit(ProgramHead programHead)
{
}
public virtual void PostVisit(ProgramHead programHead)
{
}
public virtual void PreVisit(ProgramStruct programStruct)
{
}
public virtual void PostVisit(ProgramStruct programStruct)
{
}
public virtual void PreVisit(RelationOperator relationOperator)
{
}
public virtual void PostVisit(RelationOperator relationOperator)
{
}
public virtual void PreVisit(SimpleExpression simpleExpression)
{
}
public virtual void PostVisit(SimpleExpression simpleExpression)
{
}
public virtual void PreVisit(Statement statement)
{
}
public virtual void PostVisit(Statement statement)
{
}
public virtual void PreVisit(StatementList statementList)
{
}
public virtual void PostVisit(StatementList statementList)
{
}
public virtual void PreVisit(Subprogram subprogram)
{
}
public virtual void PostVisit(Subprogram subprogram)
{
}
public virtual void PreVisit(SubprogramBody subprogramBody)
{
}
public virtual void PostVisit(SubprogramBody subprogramBody)
{
}
public virtual void PreVisit(SubprogramDeclarations subprogramDeclarations)
{
}
public virtual void PostVisit(SubprogramDeclarations subprogramDeclarations)
{
}
public virtual void PreVisit(SubprogramHead subprogramHead)
{
}
public virtual void PostVisit(SubprogramHead subprogramHead)
{
}
public virtual void PreVisit(Term term)
{
}
public virtual void PostVisit(Term term)
{
}
public virtual void PreVisit(TypeSyntaxNode typeSyntaxNode)
{
}
public virtual void PostVisit(TypeSyntaxNode typeSyntaxNode)
{
}
public virtual void PreVisit(ValueParameter valueParameter)
{
}
public virtual void PostVisit(ValueParameter valueParameter)
{
}
public virtual void PreVisit(VarDeclaration varDeclaration)
{
}
public virtual void PostVisit(VarDeclaration varDeclaration)
{
}
public virtual void PreVisit(VarDeclarations varDeclarations)
{
}
public virtual void PostVisit(VarDeclarations varDeclarations)
{
}
public virtual void PreVisit(Variable variable)
{
}
public virtual void PostVisit(Variable variable)
{
}
public virtual void PreVisit(VarParameter varParameter)
{
}
public virtual void PostVisit(VarParameter varParameter)
{
}
public virtual void PreVisit(TerminatedSyntaxNode terminatedSyntaxNode)
{
}
public virtual void PostVisit(TerminatedSyntaxNode terminatedSyntaxNode)
{
}
}

View File

@ -10,4 +10,8 @@
<None Include="../.gitignore" />
<None Include="../.editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@ -160,6 +160,36 @@ public class NumberSemanticToken : SemanticToken
public required NumberType NumberType { get; init; }
/// <summary>
/// 将数值类型记号识别为整数
/// </summary>
/// <returns>该记号表示的整数</returns>
/// <exception cref="InvalidOperationException">目标记号不是整数类型</exception>
public int ParseAsInteger()
{
if (NumberType != NumberType.Integer)
{
throw new InvalidOperationException("Target semantic token isn't integer");
}
return int.Parse(LiteralValue);
}
/// <summary>
/// 将数值类型记号识别为浮点数
/// </summary>
/// <returns>该记号标识的浮点数</returns>
/// <exception cref="InvalidOperationException">目标记号不是浮点数类型</exception>
public double ParseAsReal()
{
if (NumberType != NumberType.Real)
{
throw new InvalidOperationException("Target semantic token isn't real");
}
return double.Parse(LiteralValue);
}
public override int GetHashCode()
{
return base.GetHashCode() ^ NumberType.GetHashCode();

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

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -33,4 +34,14 @@ public class AddOperator : NonTerminatedSyntaxNode
builder.AddString(" ||");
}
}
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
@ -9,9 +10,31 @@ public class BasicType : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.BasicType;
public static BasicType Create(List<SyntaxNodeBase> children)
/// <summary>
/// BasicType代表的Pascal类型
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public PascalType PascalType
{
return new BasicType { Children = children };
get
{
KeywordType keywordType = Children[0].Convert<TerminatedSyntaxNode>().Token
.Convert<KeywordSemanticToken>().KeywordType;
switch (keywordType)
{
case KeywordType.Integer:
return PascalBasicType.Integer;
case KeywordType.Real:
return PascalBasicType.Real;
case KeywordType.Character:
return PascalBasicType.Character;
case KeywordType.Boolean:
return PascalBasicType.Boolean;
}
throw new InvalidOperationException();
}
}
public override void GenerateCCode(CCodeBuilder builder)
@ -36,27 +59,18 @@ public class BasicType : NonTerminatedSyntaxNode
}
}
/// <summary>
///尝试获取Pascal的基本类型
/// </summary>
/// <returns></returns>
public PascalType TryGetPascalType()
public override void PreVisit(SyntaxNodeVisitor visitor)
{
var keywordType = Children[0].Convert<TerminatedSyntaxNode>().Token
.Convert<KeywordSemanticToken>().KeywordType;
switch (keywordType)
{
case KeywordType.Integer:
return PascalBasicType.Integer;
case KeywordType.Real:
return PascalBasicType.Real;
case KeywordType.Boolean:
return PascalBasicType.Boolean;
case KeywordType.Character:
return PascalBasicType.Character;
visitor.PreVisit(this);
}
return PascalBasicType.Void;
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static BasicType Create(List<SyntaxNodeBase> children)
{
return new BasicType { Children = children };
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -9,6 +10,16 @@ public class CompoundStatement : NonTerminatedSyntaxNode
public IEnumerable<Statement> Statements => Children[1].Convert<StatementList>().Statements;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static CompoundStatement Create(List<SyntaxNodeBase> children)
{
return new CompoundStatement { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -17,6 +18,16 @@ public class ConstDeclaration : NonTerminatedSyntaxNode
/// </summary>
public (IdentifierSemanticToken, ConstValue) ConstValue => GetConstValue();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ConstDeclaration Create(List<SyntaxNodeBase> children)
{
bool isRecursive;

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -13,6 +14,16 @@ public class ConstDeclarations : NonTerminatedSyntaxNode
/// </summary>
public IEnumerable<(IdentifierSemanticToken, ConstValue)> ConstValues => GetConstValues();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ConstDeclarations Create(List<SyntaxNodeBase> children)
{
return new ConstDeclarations { Children = children };

View File

@ -1,12 +1,127 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
/// <summary>
/// 使用数值产生式事件的事件参数
/// </summary>
public class NumberConstValueEventArgs : EventArgs
{
/// <summary>
/// 是否含有负号
/// </summary>
public bool IsNegative { get; init; }
/// <summary>
/// 数值记号
/// </summary>
public required NumberSemanticToken Token { get; init; }
}
/// <summary>
/// 使用字符产生式事件的事件参数
/// </summary>
public class CharacterConstValueEventArgs : EventArgs
{
/// <summary>
/// 字符记号
/// </summary>
public required CharacterSemanticToken Token { get; init; }
}
public class ConstValue : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.ConstValue;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseGeneratorEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseGeneratorEvent();
}
private PascalType? _constType;
/// <summary>
/// 该ConstValue代表的类型
/// </summary>
/// <exception cref="InvalidOperationException">尚未分析该类型</exception>
public PascalType ConstType
{
get
{
if (_constType is null)
{
throw new InvalidOperationException("ConstType has not been set");
}
return _constType;
}
set
{
_constType = value;
}
}
/// <summary>
/// 使用数值产生式的事件
/// </summary>
public event EventHandler<NumberConstValueEventArgs>? OnNumberGenerator;
/// <summary>
/// 使用字符产生式的事件
/// </summary>
public event EventHandler<CharacterConstValueEventArgs>? OnCharacterGenerator;
private void RaiseGeneratorEvent()
{
if (Children.Count == 2)
{
OperatorSemanticToken operatorSemanticToken = Children[0].Convert<TerminatedSyntaxNode>().Token
.Convert<OperatorSemanticToken>();
NumberSemanticToken numberSemanticToken = Children[1].Convert<TerminatedSyntaxNode>().Token
.Convert<NumberSemanticToken>();
OnNumberGenerator?.Invoke(this, new NumberConstValueEventArgs
{
Token = numberSemanticToken,
IsNegative = operatorSemanticToken.OperatorType == OperatorType.Minus
});
return;
}
SemanticToken token = Children[0].Convert<TerminatedSyntaxNode>().Token;
if (token.TokenType == SemanticTokenType.Number)
{
OnNumberGenerator?.Invoke(this,
new NumberConstValueEventArgs
{
Token = token.Convert<NumberSemanticToken>()
});
}
else
{
OnCharacterGenerator?.Invoke(this, new CharacterConstValueEventArgs
{
Token = token.Convert<CharacterSemanticToken>()
});
}
OnNumberGenerator = null;
OnCharacterGenerator = null;
}
public static ConstValue Create(List<SyntaxNodeBase> children)
{
return new ConstValue { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -7,6 +8,16 @@ public class ElsePart : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.ElsePart;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ElsePart Create(List<SyntaxNodeBase> children)
{
return new ElsePart { Children = children };

View File

@ -1,17 +1,97 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnSimpleExpressionGeneratorEventArgs : EventArgs
{
public required SimpleExpression SimpleExpression { get; init; }
}
public class OnRelationGeneratorEventArgs : EventArgs
{
public required SimpleExpression Left { get; init; }
public required RelationOperator Operator { get; init; }
public required SimpleExpression Right { get; init; }
}
public class Expression : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Expression;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
/// <summary>
/// 直接赋值产生式的事件
/// </summary>
public event EventHandler<OnSimpleExpressionGeneratorEventArgs>? OnSimpleExpressionGenerator;
/// <summary>
/// 关系产生式的事件
/// </summary>
public event EventHandler<OnRelationGeneratorEventArgs>? OnRelationGenerator;
private PascalType? _expressionType;
public PascalType ExprssionType
{
get
{
if (_expressionType is null)
{
throw new InvalidOperationException();
}
return _expressionType;
}
set
{
_expressionType = value;
}
}
public static Expression Create(List<SyntaxNodeBase> children)
{
return new Expression { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 1)
{
OnSimpleExpressionGenerator?.Invoke(this, new OnSimpleExpressionGeneratorEventArgs
{
SimpleExpression = Children[0].Convert<SimpleExpression>()
});
}
else
{
OnRelationGenerator?.Invoke(this, new OnRelationGeneratorEventArgs
{
Left = Children[0].Convert<SimpleExpression>(),
Operator = Children[1].Convert<RelationOperator>(),
Right = Children[2].Convert<SimpleExpression>()
});
}
OnSimpleExpressionGenerator = null;
OnRelationGenerator = null;
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)

View File

@ -1,4 +1,4 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -7,67 +7,39 @@ public class ExpressionList : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.ExpressionList;
public bool IsRecursive { get; private init; }
/// <summary>
/// 声明的表达式列表
/// 子表达式列表
/// </summary>
public IEnumerable<Expression> Expressions => GetExpressions();
public List<Expression> Expressions { get; } = [];
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ExpressionList Create(List<SyntaxNodeBase> children)
{
bool isRecursive;
ExpressionList result = new() { Children = children };
if (children.Count == 1)
{
isRecursive = false;
result.Expressions.Add(children[0].Convert<Expression>());
}
else if (children.Count == 3)
{
isRecursive = true;
}
else
foreach (Expression expression in children[0].Convert<ExpressionList>().Expressions)
{
throw new InvalidOperationException();
result.Expressions.Add(expression);
}
return new ExpressionList { Children = children, IsRecursive = isRecursive };
}
private IEnumerable<Expression> GetExpressions()
{
ExpressionList list = this;
while (true)
{
if (list.IsRecursive)
{
yield return list.Children[2].Convert<Expression>();
list = list.Children[0].Convert<ExpressionList>();
}
else
{
yield return list.Children[0].Convert<Expression>();
break;
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
//用逗号分隔输出的expression
using var enumerator = Expressions.GetEnumerator();
if (enumerator.MoveNext())
{
enumerator.Current.GenerateCCode(builder);
}
while (enumerator.MoveNext())
{
builder.AddString(", ");
enumerator.Current.GenerateCCode(builder);
result.Expressions.Add(children[2].Convert<Expression>());
}
return result;
}
}

View File

@ -1,18 +1,173 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnNumberGeneratorEventArgs : EventArgs
{
public required NumberSemanticToken Token { get; init; }
}
public class OnVariableGeneratorEventArgs : EventArgs
{
public required Variable Variable { get; init; }
}
public class OnParethnesisGeneratorEventArgs : EventArgs
{
public required Expression Expression { get; init; }
}
public class OnProcedureCallGeneratorEventArgs : EventArgs
{
public required IdentifierSemanticToken ProcedureName { get; init; }
public required ExpressionList Parameters { get; init; }
}
public class OnNotGeneratorEventArgs : EventArgs
{
public required Factor Factor { get; init; }
}
public class OnUminusGeneratorEventArgs : EventArgs
{
public required Factor Factor { get; init; }
}
public class Factor : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Factor;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
/// <summary>
/// 使用数值产生式的事件
/// </summary>
public event EventHandler<OnNumberGeneratorEventArgs>? OnNumberGenerator;
/// <summary>
/// 使用括号产生式的事件
/// </summary>
public event EventHandler<OnParethnesisGeneratorEventArgs>? OnParethnesisGenerator;
/// <summary>
/// 使用变量产生式的事件
/// </summary>
public event EventHandler<OnVariableGeneratorEventArgs>? OnVariableGenerator;
/// <summary>
/// 使用过程调用产生式的事件
/// </summary>
public event EventHandler<OnProcedureCallGeneratorEventArgs>? OnProcedureCallGenerator;
/// <summary>
/// 使用否定产生式的事件
/// </summary>
public event EventHandler<OnNotGeneratorEventArgs>? OnNotGenerator;
/// <summary>
/// 使用负号产生式的事件
/// </summary>
public event EventHandler<OnUminusGeneratorEventArgs>? OnUminusGenerator;
private PascalType? _factorType;
public PascalType FactorType
{
get
{
if (_factorType is null)
{
throw new InvalidOperationException();
}
return _factorType;
}
set
{
_factorType = value;
}
}
public static Factor Create(List<SyntaxNodeBase> children)
{
return new Factor { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 1)
{
//factor -> num
if (Children[0].IsTerminated)
{
SemanticToken token = Children[0].Convert<TerminatedSyntaxNode>().Token;
OnNumberGenerator?.Invoke(this,
new OnNumberGeneratorEventArgs { Token = token.Convert<NumberSemanticToken>() });
}
// factor -> variable
else
{
OnVariableGenerator?.Invoke(this,
new OnVariableGeneratorEventArgs { Variable = Children[0].Convert<Variable>() });
}
}
//factor -> ( expression )
else if (Children.Count == 3)
{
OnParethnesisGenerator?.Invoke(this,
new OnParethnesisGeneratorEventArgs { Expression = Children[1].Convert<Expression>() });
}
//factor -> id ( expression )
else if (Children.Count == 4)
{
OnProcedureCallGenerator?.Invoke(this,
new OnProcedureCallGeneratorEventArgs
{
ProcedureName =
Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
Parameters = Children[2].Convert<ExpressionList>()
});
}
else
{
SemanticToken token = Children[0].Convert<TerminatedSyntaxNode>().Token;
Factor factor = Children[1].Convert<Factor>();
if (token.TokenType == SemanticTokenType.Keyword)
{
// factor -> not factor
OnNotGenerator?.Invoke(this, new OnNotGeneratorEventArgs { Factor = factor });
}
else
{
// factor -> uminus factor
OnUminusGenerator?.Invoke(this, new OnUminusGeneratorEventArgs { Factor = factor });
}
}
OnNumberGenerator = null;
OnVariableGenerator = null;
OnParethnesisGenerator = null;
OnProcedureCallGenerator = null;
OnNotGenerator = null;
OnUminusGenerator = null;
}
public override void GenerateCCode(CCodeBuilder builder)
{
if (Children.Count == 1)
@ -42,14 +197,15 @@ public class Factor : NonTerminatedSyntaxNode
//factor -> id ( expression )
else if (Children.Count == 4)
{
builder.AddString(" " + Children[0].Convert<TerminatedSyntaxNode>().Token.
Convert<IdentifierSemanticToken>().IdentifierName);
builder.AddString(" " + Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>()
.IdentifierName);
builder.AddString("(");
Children[2].GenerateCCode(builder);
builder.AddString(")");
}
else
{ //factor -> not factor
{
//factor -> not factor
builder.AddString(" (");
if (Children[0].Convert<TerminatedSyntaxNode>().Token.TokenType == SemanticTokenType.Keyword)
{

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -6,26 +7,18 @@ public class FormalParameter : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.FormalParameter;
/// <summary>
/// 声明的参数列表
/// </summary>
public IEnumerable<Parameter> Parameters => GetParameters();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static FormalParameter Create(List<SyntaxNodeBase> children)
{
return new FormalParameter { Children = children };
}
private IEnumerable<Parameter> GetParameters()
{
if (Children.Count == 0)
{
yield break;
}
foreach (Parameter parameter in Children[1].Convert<ParameterList>().Parameters)
{
yield return parameter;
}
}
}

View File

@ -1,75 +1,97 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnIdentifierGeneratorEventArgs : EventArgs
{
public required IdentifierSemanticToken IdentifierToken { get; init; }
public required IdentifierList IdentifierList { get; init; }
}
public class OnTypeGeneratorEventArgs : EventArgs
{
public required TypeSyntaxNode TypeSyntaxNode { get; init; }
}
public class IdentifierList : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.IdentifierList;
/// <summary>
/// 是否含有递归定义
/// </summary>
public bool IsRecursive { get; private init; }
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
private PascalType? _definitionType;
/// <summary>
/// 声明的标识符列表
/// IdentifierList中定义的类型
/// </summary>
public IEnumerable<IdentifierSemanticToken> Identifiers => GetIdentifiers();
public static IdentifierList Create(List<SyntaxNodeBase> children)
/// <exception cref="InvalidOperationException">尚未确定定义的类型</exception>
public PascalType DefinitionType
{
bool isRecursive;
if (children.Count == 2)
get
{
isRecursive = false;
}
else if (children.Count == 3)
{
isRecursive = true;
}
else
if (_definitionType is null)
{
throw new InvalidOperationException();
}
return new IdentifierList { IsRecursive = isRecursive, Children = children };
return _definitionType;
}
set
{
_definitionType = value;
}
}
private IEnumerable<IdentifierSemanticToken> GetIdentifiers()
{
IdentifierList identifier = this;
/// <summary>
/// 是否为参数中的引用参数
/// </summary>
public bool IsReference { get; set; }
while (true)
/// <summary>
/// 是否在过程定义中使用
/// </summary>
public bool IsProcedure { get; set; }
public event EventHandler<OnIdentifierGeneratorEventArgs>? OnIdentifierGenerator;
public event EventHandler<OnTypeGeneratorEventArgs>? OnTypeGenerator;
public static IdentifierList Create(List<SyntaxNodeBase> children)
{
if (identifier.IsRecursive)
return new IdentifierList { Children = children };
}
private void RaiseEvent()
{
yield return (IdentifierSemanticToken)identifier.Children[2].Convert<TerminatedSyntaxNode>().Token;
identifier = identifier.Children[0].Convert<IdentifierList>();
if (Children.Count == 2)
{
OnTypeGenerator?.Invoke(this,
new OnTypeGeneratorEventArgs { TypeSyntaxNode = Children[1].Convert<TypeSyntaxNode>() });
}
else
{
yield return (IdentifierSemanticToken)identifier.Children[0].Convert<TerminatedSyntaxNode>().Token;
break;
}
}
OnIdentifierGenerator?.Invoke(this, new OnIdentifierGeneratorEventArgs
{
IdentifierToken = Children[1].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
IdentifierList = Children[2].Convert<IdentifierList>()
});
}
public override void GenerateCCode(CCodeBuilder builder)
{
//用逗号分隔输出的expression
using var enumerator = Identifiers.Reverse().GetEnumerator();
if (enumerator.MoveNext())
{
builder.AddString(" " + enumerator.Current.IdentifierName);
}
while (enumerator.MoveNext())
{
builder.AddString(", " + enumerator.Current.IdentifierName);
}
OnTypeGenerator = null;
OnIdentifierGenerator = null;
}
}

View File

@ -1,54 +1,46 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnParameterGeneratorEventArgs : EventArgs
{
public required ExpressionList Parameters { get; init; }
}
public class IdentifierVarPart : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.IdVarPart;
/// <summary>
/// 是否声明了索引部分
/// </summary>
public bool Exist { get; private init; }
/// <summary>
/// 索引中的位置声明
/// </summary>
public IEnumerable<Expression> Positions => GetPositions();
private IEnumerable<Expression> GetPositions()
public override void PreVisit(SyntaxNodeVisitor visitor)
{
if (!Exist)
{
yield break;
visitor.PreVisit(this);
RaiseEvent();
}
foreach (Expression expression in Children[1].Convert<ExpressionList>().Expressions)
public override void PostVisit(SyntaxNodeVisitor visitor)
{
yield return expression;
}
visitor.PostVisit(this);
RaiseEvent();
}
public event EventHandler<OnParameterGeneratorEventArgs>? OnParameterGenerator;
public static IdentifierVarPart Create(List<SyntaxNodeBase> children)
{
bool exist;
if (children.Count == 0)
{
exist = false;
}
else if (children.Count == 3)
{
exist = true;
}
else
{
throw new InvalidOperationException();
return new IdentifierVarPart { Children = children };
}
return new IdentifierVarPart { Children = children, Exist = exist };
private void RaiseEvent()
{
if (Children.Count == 3)
{
OnParameterGenerator?.Invoke(this, new OnParameterGeneratorEventArgs
{
Parameters = Children[1].Convert<ExpressionList>()
});
}
OnParameterGenerator = null;
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -8,6 +9,16 @@ public class MultiplyOperator : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static MultiplyOperator Create(List<SyntaxNodeBase> children)
{
return new MultiplyOperator { Children = children };
@ -45,6 +56,5 @@ public class MultiplyOperator : NonTerminatedSyntaxNode
builder.AddString(" /");
}
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -9,6 +10,8 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
public abstract NonTerminatorType Type { get; }
public required List<SyntaxNodeBase> Children { get; init; }
public IEnumerator<SyntaxNodeBase> GetEnumerator()

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -17,6 +18,16 @@ public class Parameter : NonTerminatedSyntaxNode
public ValueParameter ValueParameter =>
IsVar ? Children[0].Convert<VarParameter>().ValueParameter : Children[0].Convert<ValueParameter>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static Parameter Create(List<SyntaxNodeBase> children)
{
NonTerminatedSyntaxNode node = children[0].Convert<NonTerminatedSyntaxNode>();

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -13,6 +14,16 @@ public class ParameterList : NonTerminatedSyntaxNode
/// </summary>
public IEnumerable<Parameter> Parameters => GetParameters();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ParameterList Create(List<SyntaxNodeBase> children)
{
bool isRecursive;

View File

@ -1,4 +1,4 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -8,63 +8,61 @@ public class Period : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Period;
public bool IsRecursive { get; private init; }
/// <summary>
/// 所有定义的Period
/// </summary>
public List<Period> Periods { get; } = [];
/// <summary>
/// 数组上下界列表
/// 数组的开始下标和结束下标
/// </summary>
public IEnumerable<(NumberSemanticToken, NumberSemanticToken)> Ranges => GetRanges();
public (NumberSemanticToken, NumberSemanticToken) Range
{
get
{
if (Children.Count == 3)
{
return (Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<NumberSemanticToken>(),
Children[2].Convert<TerminatedSyntaxNode>().Token.Convert<NumberSemanticToken>());
}
else
{
return (Children[2].Convert<TerminatedSyntaxNode>().Token.Convert<NumberSemanticToken>(),
Children[4].Convert<TerminatedSyntaxNode>().Token.Convert<NumberSemanticToken>());
}
}
}
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static Period Create(List<SyntaxNodeBase> children)
{
bool isRecursive;
Period result = new() { Children = children };
if (children.Count == 3)
{
isRecursive = false;
}
else if (children.Count == 5)
{
isRecursive = true;
result.Periods.Add(result);
}
else
{
throw new InvalidOperationException();
Period child = children[0].Convert<Period>();
foreach (Period period in child.Periods)
{
result.Periods.Add(period);
}
return new Period { Children = children, IsRecursive = isRecursive };
result.Periods.Add(result);
}
private (NumberSemanticToken, NumberSemanticToken) GetRange()
{
if (IsRecursive)
{
return ((NumberSemanticToken)Children[2].Convert<TerminatedSyntaxNode>().Token,
(NumberSemanticToken)Children[4].Convert<TerminatedSyntaxNode>().Token);
}
else
{
return ((NumberSemanticToken)Children[0].Convert<TerminatedSyntaxNode>().Token,
(NumberSemanticToken)Children[2].Convert<TerminatedSyntaxNode>().Token);
}
}
private IEnumerable<(NumberSemanticToken, NumberSemanticToken)> GetRanges()
{
Period period = this;
while (true)
{
if (period.IsRecursive)
{
yield return period.GetRange();
period = period.Children[0].Convert<Period>();
}
else
{
yield return period.GetRange();
break;
}
}
return result;
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -13,6 +14,16 @@ public class ProcedureCall : NonTerminatedSyntaxNode
public IEnumerable<Expression> Arguments => GetArguments();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ProcedureCall Create(List<SyntaxNodeBase> children)
{
return new ProcedureCall { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -27,6 +28,16 @@ public class ProgramBody : NonTerminatedSyntaxNode
/// </summary>
public CompoundStatement CompoundStatement => Children[3].Convert<CompoundStatement>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ProgramBody Create(List<SyntaxNodeBase> children)
{
return new ProgramBody { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -13,28 +14,18 @@ public class ProgramHead : NonTerminatedSyntaxNode
public IdentifierSemanticToken ProgramName
=> (IdentifierSemanticToken)Children[1].Convert<TerminatedSyntaxNode>().Token;
/// <summary>
/// 暂时意义不明的标识符列表
/// https://wiki.freepascal.org/Program_Structure/zh_CN
/// TODO: 查阅资料
/// </summary>
public IEnumerable<IdentifierSemanticToken> FileList => GetFileList();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ProgramHead Create(List<SyntaxNodeBase> children)
{
return new ProgramHead { Children = children };
}
private IEnumerable<IdentifierSemanticToken> GetFileList()
{
if (Children.Count == 2)
{
yield break;
}
foreach (IdentifierSemanticToken token in Children[3].Convert<IdentifierList>().Identifiers)
{
yield return token;
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -17,6 +18,16 @@ public class ProgramStruct : NonTerminatedSyntaxNode
/// </summary>
public ProgramBody Body => Children[2].Convert<ProgramBody>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ProgramStruct Create(List<SyntaxNodeBase> children)
{
return new ProgramStruct { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
@ -8,6 +9,16 @@ public class RelationOperator : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.RelationOperator;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static RelationOperator Create(List<SyntaxNodeBase> children)
{
return new RelationOperator { Children = children };
@ -15,8 +26,8 @@ public class RelationOperator : NonTerminatedSyntaxNode
public override void GenerateCCode(CCodeBuilder builder)
{
var operatorType = Children[0].Convert<TerminatedSyntaxNode>().Token.
Convert<OperatorSemanticToken>().OperatorType;
var operatorType = Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<OperatorSemanticToken>()
.OperatorType;
switch (operatorType)
{
case OperatorType.Equal:

View File

@ -1,17 +1,97 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnTermGeneratorEventArgs : EventArgs
{
public required Term Term { get; init; }
}
public class OnAddGeneratorEventArgs : EventArgs
{
public required SimpleExpression Left { get; init; }
public required AddOperator Operator { get; init; }
public required Term Right { get; init; }
}
public class SimpleExpression : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.SimpleExpression;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
/// <summary>
/// 直接赋值产生式的事件
/// </summary>
public event EventHandler<OnTermGeneratorEventArgs>? OnTermGenerator;
/// <summary>
/// 加法产生式的事件
/// </summary>
public event EventHandler<OnAddGeneratorEventArgs>? OnAddGenerator;
private PascalType? _simpleExpressionType;
public PascalType SimpleExpressionType
{
get
{
if (_simpleExpressionType is null)
{
throw new InvalidOperationException();
}
return _simpleExpressionType;
}
set
{
_simpleExpressionType = value;
}
}
public static SimpleExpression Create(List<SyntaxNodeBase> children)
{
return new SimpleExpression { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 1)
{
OnTermGenerator?.Invoke(this, new OnTermGeneratorEventArgs
{
Term = Children[0].Convert<Term>()
});
}
else
{
OnAddGenerator?.Invoke(this, new OnAddGeneratorEventArgs
{
Left = Children[0].Convert<SimpleExpression>(),
Operator = Children[1].Convert<AddOperator>(),
Right = Children[2].Convert<Term>()
});
}
OnTermGenerator = null;
OnAddGenerator = null;
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)

View File

@ -1,24 +1,134 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
public class OnAssignGeneratorEventArgs : EventArgs
{
public required Variable Variable { get; init; }
public required Expression Expression { get; init; }
}
public class OnReturnGeneratorEventArgs : EventArgs
{
public required IdentifierSemanticToken FunctionName { get; set; }
public required Expression Expression { get; init; }
}
public class OnIfGeneratorEventArgs : EventArgs
{
public required Expression Condition { get; init; }
public required Statement Sentence { get; init; }
public required ElsePart ElseSentence { get; init; }
}
public class OnForGeneratorEventArgs : EventArgs
{
public required IdentifierSemanticToken Iterator { get; init; }
public required Expression Begin { get; init; }
public required Expression End { get; init; }
public required Statement Sentence { get; init; }
}
public class Statement : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Statement;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
/// <summary>
/// 使用赋值产生式的事件
/// </summary>
public event EventHandler<OnAssignGeneratorEventArgs>? OnAssignGenerator;
/// <summary>
/// 使用返回产生式的事件
/// </summary>
public event EventHandler<OnReturnGeneratorEventArgs>? OnReturnGenerator;
/// <summary>
/// 使用If产生式的事件
/// </summary>
public event EventHandler<OnIfGeneratorEventArgs>? OnIfGenerator;
/// <summary>
/// 使用For产生式的事件
/// </summary>
public event EventHandler<OnForGeneratorEventArgs>? OnForGenerator;
public static Statement Create(List<SyntaxNodeBase> children)
{
return new Statement { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 2)
{
if (Children[0].IsTerminated)
{
OnReturnGenerator?.Invoke(this, new OnReturnGeneratorEventArgs
{
FunctionName = Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
Expression = Children[2].Convert<Expression>()
});
}
else
{
OnAssignGenerator?.Invoke(this, new OnAssignGeneratorEventArgs
{
Variable = Children[0].Convert<Variable>(),
Expression = Children[2].Convert<Expression>()
});
}
}
else if (Children.Count == 5)
{
OnIfGenerator?.Invoke(this, new OnIfGeneratorEventArgs
{
Condition = Children[1].Convert<Expression>(),
Sentence = Children[3].Convert<Statement>(),
ElseSentence = Children[4].Convert<ElsePart>()
});
}
else if (Children.Count == 8)
{
OnForGenerator?.Invoke(this, new OnForGeneratorEventArgs
{
Iterator = Children[1].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
Begin = Children[3].Convert<Expression>(),
End = Children[5].Convert<Expression>(),
Sentence = Children[7].Convert<Statement>()
});
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
if (Children.Count == 0)
{
return;
}
// statement -> procedureCall | compoundStatement
if (Children.Count == 1)
{

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -11,6 +12,16 @@ public class StatementList : NonTerminatedSyntaxNode
public IEnumerable<Statement> Statements => GetStatements();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static StatementList Create(List<SyntaxNodeBase> children)
{
bool isRecursive;

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -17,6 +18,16 @@ public class Subprogram : NonTerminatedSyntaxNode
/// </summary>
public SubprogramBody Body => Children[2].Convert<SubprogramBody>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static Subprogram Create(List<SyntaxNodeBase> children)
{
return new Subprogram { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -22,6 +23,16 @@ public class SubprogramBody : NonTerminatedSyntaxNode
/// </summary>
public CompoundStatement CompoundStatement => Children[2].Convert<CompoundStatement>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static SubprogramBody Create(List<SyntaxNodeBase> children)
{
return new SubprogramBody() { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -12,6 +13,16 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode
/// </summary>
public IEnumerable<Subprogram> Subprograms => GetSubprograms();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static SubprogramDeclarations Create(List<SyntaxNodeBase> children)
{
return new SubprogramDeclarations { Children = children };

View File

@ -1,25 +1,48 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
public class OnProcedureGeneratorEventArgs : EventArgs;
public class OnFunctionGeneratorEventArgs : EventArgs
{
public required BasicType ReturnType { get; init; }
}
public class SubprogramHead : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.SubprogramHead;
/// <summary>
/// 过程定义还是函数定义
/// </summary>
public bool IsProcedure { get; private init; }
/// <summary>
/// 子程序的名称
/// </summary>
public IdentifierSemanticToken SubprogramName =>
(IdentifierSemanticToken)Children[1].Convert<TerminatedSyntaxNode>().Token;
Children[1].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>();
/// <summary>
/// 子程序的参数
/// </summary>
public IEnumerable<Parameter> Parameters => Children[2].Convert<FormalParameter>().Parameters;
public FormalParameter Parameters => Children[2].Convert<FormalParameter>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
public event EventHandler<OnProcedureGeneratorEventArgs>? OnProcedureGenerator;
public event EventHandler<OnFunctionGeneratorEventArgs>? OnFunctionGenerator;
public static SubprogramHead Create(List<SyntaxNodeBase> children)
{
@ -44,27 +67,19 @@ public class SubprogramHead : NonTerminatedSyntaxNode
return new SubprogramHead { Children = children, IsProcedure = isProcedure };
}
public override void GenerateCCode(CCodeBuilder builder)
private void RaiseEvent()
{
//可能要用到符号表
if (IsProcedure)
{
builder.AddString("void ");
OnProcedureGenerator?.Invoke(this, new OnProcedureGeneratorEventArgs());
}
else
{
//返回类型暂时未知
builder.AddString("int ");
OnFunctionGenerator?.Invoke(this,
new OnFunctionGeneratorEventArgs { ReturnType = Children[4].Convert<BasicType>() });
}
builder.AddString(SubprogramName.LiteralValue);
builder.AddString("(");
foreach (var param in Parameters)
{
}
builder.AddString(")");
OnProcedureGenerator = null;
OnFunctionGenerator = null;
}
}

View File

@ -9,6 +9,10 @@ public abstract class SyntaxNodeBase : ICCodeGenerator
{
public abstract bool IsTerminated { get; }
public abstract void PreVisit(SyntaxNodeVisitor visitor);
public abstract void PostVisit(SyntaxNodeVisitor visitor);
public T Convert<T>() where T : SyntaxNodeBase
{
T? result = this as T;
@ -26,7 +30,6 @@ public abstract class SyntaxNodeBase : ICCodeGenerator
/// </summary>
public virtual void GenerateCCode(CCodeBuilder builder)
{
}
public override string ToString()

View File

@ -1,17 +1,97 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnFactorGeneratorEventArgs : EventArgs
{
public required Factor Factor { get; init; }
}
public class OnMultiplyGeneratorEventArgs : EventArgs
{
public required Term Left { get; init; }
public required MultiplyOperator Operator { get; init; }
public required Factor Right { get; init; }
}
public class Term : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Term;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
/// <summary>
/// 直接赋值产生式的事件
/// </summary>
public event EventHandler<OnFactorGeneratorEventArgs>? OnFactorGenerator;
/// <summary>
/// 乘法产生式的事件
/// </summary>
public event EventHandler<OnMultiplyGeneratorEventArgs>? OnMultiplyGenerator;
private PascalType? _termType;
public PascalType TermType
{
get
{
if (_termType is null)
{
throw new InvalidOperationException();
}
return _termType;
}
set
{
_termType = value;
}
}
public static Term Create(List<SyntaxNodeBase> children)
{
return new Term { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 1)
{
OnFactorGenerator?.Invoke(this, new OnFactorGeneratorEventArgs
{
Factor = Children[0].Convert<Factor>()
});
}
else
{
OnMultiplyGenerator?.Invoke(this, new OnMultiplyGeneratorEventArgs
{
Left = Children[0].Convert<Term>(),
Operator = Children[1].Convert<MultiplyOperator>(),
Right = Children[2].Convert<Factor>()
});
}
OnFactorGenerator = null;
OnMultiplyGenerator = null;
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)

View File

@ -1,4 +1,5 @@
using Canon.Core.LexicalParser;
using Canon.Core.Abstractions;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -6,6 +7,16 @@ public class TerminatedSyntaxNode : SyntaxNodeBase
{
public override bool IsTerminated => true;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public required SemanticToken Token { get; init; }
public override string ToString()

View File

@ -1,17 +1,92 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
public class OnBasicTypeGeneratorEventArgs : EventArgs
{
public required BasicType BasicType { get; init; }
}
public class OnArrayTypeGeneratorEventArgs : EventArgs
{
public required Period Period { get; init; }
public required BasicType BasicType { get; init; }
}
public class TypeSyntaxNode : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.Type;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
RaiseEvent();
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
RaiseEvent();
}
public event EventHandler<OnBasicTypeGeneratorEventArgs>? OnBasicTypeGenerator;
public event EventHandler<OnArrayTypeGeneratorEventArgs>? OnArrayTypeGenerator;
private PascalType? _pascalType;
/// <summary>
/// Type节点制定的类型
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public PascalType PascalType
{
get
{
if (_pascalType is null)
{
throw new InvalidOperationException();
}
return _pascalType;
}
set
{
_pascalType = value;
}
}
public static TypeSyntaxNode Create(List<SyntaxNodeBase> children)
{
return new TypeSyntaxNode { Children = children };
}
private void RaiseEvent()
{
if (Children.Count == 1)
{
OnBasicTypeGenerator?.Invoke(this, new OnBasicTypeGeneratorEventArgs
{
BasicType = Children[0].Convert<BasicType>()
});
}
else
{
OnArrayTypeGenerator?.Invoke(this, new OnArrayTypeGeneratorEventArgs
{
Period = Children[2].Convert<Period>(),
BasicType = Children[5].Convert<BasicType>()
});
}
OnBasicTypeGenerator = null;
OnArrayTypeGenerator = null;
}
public override void GenerateCCode(CCodeBuilder builder)
{
//type -> basic_type
@ -22,7 +97,6 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode
//type -> array [ period ]of basic_type
else
{
}
}
}

View File

@ -1,5 +1,7 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -8,23 +10,27 @@ public class ValueParameter : NonTerminatedSyntaxNode
public override NonTerminatorType Type => NonTerminatorType.ValueParameter;
/// <summary>
/// 声明的变量列表
/// 是否为参数中的引用参数
/// </summary>
// public IdentifierList IdentifierList => Children[1].Convert<IdentifierList>();
public bool IsReference { get; set; }
/// <summary>
/// 声明的变量类型
/// </summary>
// public BasicType BasicType => Children[2].Convert<BasicType>();
public IdentifierSemanticToken Token =>
Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>();
public IdentifierList IdentifierList => Children[1].Convert<IdentifierList>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static ValueParameter Create(List<SyntaxNodeBase> children)
{
return new ValueParameter { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//可能涉及符号表访问
builder.AddString("valueParam ");
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -6,42 +8,39 @@ public class VarDeclaration : NonTerminatedSyntaxNode
{
public override NonTerminatorType Type => NonTerminatorType.VarDeclaration;
// public bool IsRecursive { get; private init; }
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
// /// <summary>
// /// 声明的变量
// /// </summary>
// public (IdentifierList, TypeSyntaxNode) Variable => GetVariable();
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
// private (IdentifierList, TypeSyntaxNode) GetVariable()
// {
// if (IsRecursive)
// {
// return (Children[2].Convert<IdentifierList>(), Children[4].Convert<TypeSyntaxNode>());
// }
// else
// {
// return (Children[0].Convert<IdentifierList>(), Children[2].Convert<TypeSyntaxNode>());
// }
// }
public required IdentifierSemanticToken Token { get; init; }
public required IdentifierList IdentifierList { get; init; }
public static VarDeclaration Create(List<SyntaxNodeBase> children)
{
/*bool isRecursive;
if (children.Count == 2)
{
isRecursive = false;
}
else if (children.Count == 4)
return new VarDeclaration
{
isRecursive = true;
Children = children,
Token = children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
IdentifierList = children[1].Convert<IdentifierList>()
};
}
else
{
throw new InvalidOperationException();
}*/
return new VarDeclaration {Children = children};
return new VarDeclaration
{
Children = children,
Token = children[2].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
IdentifierList = children[3].Convert<IdentifierList>()
};
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
@ -13,6 +14,16 @@ public class VarDeclarations : NonTerminatedSyntaxNode
/// </summary>
// public IEnumerable<(IdentifierList, TypeSyntaxNode)> Variables => EnumerateVariables();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static VarDeclarations Create(List<SyntaxNodeBase> children)
{
return new VarDeclarations { Children = children };

View File

@ -1,4 +1,5 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -9,6 +10,16 @@ public class VarParameter : NonTerminatedSyntaxNode
public ValueParameter ValueParameter => Children[1].Convert<ValueParameter>();
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static VarParameter Create(List<SyntaxNodeBase> children)
{
return new VarParameter { Children = children };

View File

@ -1,7 +1,6 @@
using Canon.Core.CodeGenerators;
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
@ -15,48 +14,18 @@ public class Variable : NonTerminatedSyntaxNode
public IdentifierSemanticToken Identifier =>
(IdentifierSemanticToken)Children[0].Convert<TerminatedSyntaxNode>().Token;
public override void PreVisit(SyntaxNodeVisitor visitor)
{
visitor.PreVisit(this);
}
public override void PostVisit(SyntaxNodeVisitor visitor)
{
visitor.PostVisit(this);
}
public static Variable Create(List<SyntaxNodeBase> children)
{
return new Variable { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//判断是否为引用变量
builder.SymbolTable.TryGetSymbol(Identifier.IdentifierName, out var symbol);
if (symbol is not null && symbol.Reference)
{
builder.AddString(" (*" + Identifier.IdentifierName + ")");
}
else
{
builder.AddString(" " + Identifier.IdentifierName);
}
//处理idVarPart数组下标部分
var idVarPart = Children[1].Convert<IdentifierVarPart>();
if (idVarPart.Exist)
{
PascalArrayType pascalArrayType = (PascalArrayType)symbol.SymbolType;
var positions = idVarPart.Positions;
foreach (var pos in positions.Reverse())
{
builder.AddString("[");
pos.GenerateCCode(builder);
//pascal下标减去左边界从而映射到C语言的下标
builder.AddString(" - " + System.Convert.ToString(pascalArrayType.Begin) + "]");
try
{
pascalArrayType = (PascalArrayType)pascalArrayType.ElementType;
}
catch (InvalidCastException e)
{
//do nothing
//因为最后一层嵌套类型必然不是PascalArrayType, 而是BasicType
}
}
}
}
}

View File

@ -5,9 +5,9 @@ namespace Canon.Generator.Extensions;
public static class RootCommandExtension
{
public static void AddGenerateCommand(this RootCommand rootCommand)
public static void AddGrammarCommand(this RootCommand rootCommand)
{
Command generateCommand = new("generate", "Generate source files.");
Command generateCommand = new("grammar", "Generate grammar parser source files.");
Argument<string> filenameArgument = new(name: "filename",
description: "determines the generated file name.",
@ -37,4 +37,16 @@ public static class RootCommandExtension
rootCommand.AddCommand(generateCommand);
}
public static void AddSyntaxVisitorCommand(this RootCommand rootCommand)
{
Command syntaxCommand = new("syntax", "Generate syntax visitor source code.");
syntaxCommand.SetHandler(async () =>
{
await SyntaxVisitorGenerator.SyntaxVisitorGenerator.Generate();
});
rootCommand.AddCommand(syntaxCommand);
}
}

View File

@ -3,6 +3,7 @@ using Canon.Generator.Extensions;
RootCommand rootCommand = new("Canon Compiler Source Generator");
rootCommand.AddGenerateCommand();
rootCommand.AddGrammarCommand();
rootCommand.AddSyntaxVisitorCommand();
await rootCommand.InvokeAsync(args);

View File

@ -0,0 +1,78 @@
using System.Text;
namespace Canon.Generator.SyntaxVisitorGenerator;
public static class SyntaxVisitorGenerator
{
private static readonly List<string> s_nodes =
[
"AddOperator",
"BasicType",
"CompoundStatement",
"ConstDeclaration",
"ConstDeclarations",
"ConstValue",
"ElsePart",
"Expression",
"ExpressionList",
"Factor",
"FormalParameter",
"IdentifierList",
"IdentifierVarPart",
"MultiplyOperator",
"Parameter",
"ParameterList",
"Period",
"ProcedureCall",
"ProgramBody",
"ProgramHead",
"ProgramStruct",
"RelationOperator",
"SimpleExpression",
"Statement",
"StatementList",
"Subprogram",
"SubprogramBody",
"SubprogramDeclarations",
"SubprogramHead",
"Term",
"TypeSyntaxNode",
"ValueParameter",
"VarDeclaration",
"VarDeclarations",
"Variable",
"VarParameter"
];
public static async Task Generate()
{
FileInfo output = new(Path.Combine(Environment.CurrentDirectory, "SyntaxNodeVisitor.cs"));
StringBuilder builder = new();
builder.Append("using Canon.Core.SyntaxNodes;\n").Append('\n');
builder.Append("namespace Canon.Core.Abstractions;\n").Append('\n');
builder.Append("public abstract class SyntaxNodeVisitor\n").Append("{\n");
foreach (string node in s_nodes)
{
string nodeName = node.Substring(0, 1).ToLower() + node.Substring(1);
builder.Append($" public virtual void PreVisit({node} {nodeName})\n")
.Append(" {\n")
.Append(" }\n")
.Append('\n');
builder.Append($" public virtual void PostVisit({node} {nodeName})\n")
.Append(" {\n")
.Append(" }\n")
.Append('\n');
}
builder.Append('}');
await using StreamWriter writer = output.CreateText();
await writer.WriteAsync(builder.ToString());
}
}

View File

@ -1,6 +1,7 @@
using Canon.Core.Abstractions;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
using Canon.Server.Extensions;
using Canon.Server.Services;
using Microsoft.EntityFrameworkCore;
@ -24,6 +25,7 @@ builder.Services.AddTransient<ILexer, Lexer>();
builder.Services.AddSingleton<IGrammarParser>(
_ => GeneratedGrammarParser.Instance);
builder.Services.AddSingleton<SyntaxTreePresentationService>();
builder.Services.AddSingleton<SyntaxTreeTraveller>();
builder.Services.AddTransient<CompilerService>();
builder.Services.AddHostedService<DatabaseSetupService>();

View File

@ -1,6 +1,6 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
using Canon.Core.SyntaxNodes;
using Canon.Server.DataTransferObjects;
using Canon.Server.Entities;
@ -12,6 +12,7 @@ namespace Canon.Server.Services;
public class CompilerService(
ILexer lexer,
IGrammarParser grammarParser,
SyntaxTreeTraveller traveller,
CompileDbContext dbContext,
GridFsService gridFsService,
SyntaxTreePresentationService syntaxTreePresentationService,
@ -38,14 +39,14 @@ public class CompilerService(
await using Stream imageStream = syntaxTreePresentationService.Present(root);
string filename = await gridFsService.UploadStream(imageStream, "image/png");
CCodeBuilder builder = new();
root.GenerateCCode(builder);
CCodeGenerateVisitor visitor = new();
traveller.Travel(root, visitor);
CompileResult result = new()
{
SourceCode = sourceCode.Code,
CompileId = Guid.NewGuid().ToString(),
CompiledCode = builder.Build(),
CompiledCode = visitor.Builder.Build(),
SytaxTreeImageFilename = filename,
CompileTime = DateTime.Now
};

View File

@ -1,42 +0,0 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class BasicTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public BasicTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void ProgramStructTest()
{
CCodeBuilder builder = new();
const string program = """
program DoNothing;
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0;}", result);
}
}

View File

@ -1,90 +0,0 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class DeclarationsTest
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public DeclarationsTest(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void VarDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
var a, b, c, d: integer; m, n: real; k: boolean;
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> char a; int main(){statement; return 0; }", result);
}
[Fact]
public void ConstDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
const a = 1; b = 2; c = 3; d = 2.5;
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0; }", result);
}
[Fact]
public void ArrayDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program arrayTest;
var a, b, c: array[1..6,5..8] of integer;
d: integer;
begin
a[2,3] := 10086;
d:=6;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0; }", result);
}
}

View File

@ -1,47 +0,0 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class ExpressionTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public ExpressionTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void ExpressionTest1()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
var a, b, c, d: integer; m, n: real; k: boolean;
begin
a := 1;
b := a + 6 * 9 + (a + 9) * 1 - (4 + a) * 5 / 1;
m := b / 3;
d := 9 mod 1;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> char a; int main(){statement; return 0; }", result);
}
}

View File

@ -1,111 +0,0 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class StatementTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public StatementTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void VariableAssignTest()
{
CCodeBuilder builder = new();
const string program = """
program varAssignTest;
var a, b: integer;
begin
a := 1;
b := a;
a := b;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> " +
"int a, b; int main(){ a = 1; b = a; a = b; return 0;}", result);
}
[Fact]
public void IfTest()
{
CCodeBuilder builder = new();
const string program = """
program main;
var
a,b:integer;
begin
if a = 5 then
begin
if b = 3 then
b := b + 1
else
b := b + 2
end
else
a := 2
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> int a, b; " +
"int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result);
}
[Fact]
public void ForLoopTest()
{
CCodeBuilder builder = new();
const string program = """
program ForLoopTest;
var a, b, c: integer;
begin
b := 1;
for a := b * 5 + 1 to 99 do
begin
c := a + 1;
b := a mod (a + c);
b := a + 1 - 1 * 1;
end;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> int a, b; " +
"int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result);
}
}

View File

@ -1,18 +1,10 @@
using Canon.Core.Abstractions;
using Canon.Core.Enums;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.GeneratedParserTests;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
namespace Canon.Tests.GrammarParserTests;
public class PascalGrammarTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
[Fact]
public void DoNothingTest()
{
@ -22,9 +14,7 @@ public class PascalGrammarTests
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
ProgramStruct root = CompilerHelpers.Analyse(program);
Assert.Equal("DoNothing", root.Head.ProgramName.LiteralValue);
Assert.Equal(15, root.Count());
}
@ -39,9 +29,8 @@ public class PascalGrammarTests
a := 1 + 1
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
ProgramStruct root = CompilerHelpers.Analyse(program);
Assert.Equal("Add", root.Head.ProgramName.LiteralValue);
}
@ -56,9 +45,8 @@ public class PascalGrammarTests
writeln( str, ret );
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
ProgramStruct root = CompilerHelpers.Analyse(program);
Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
}
@ -73,9 +61,8 @@ public class PascalGrammarTests
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
ProgramStruct root = CompilerHelpers.Analyse(program);
Assert.Equal("main", root.Head.ProgramName.LiteralValue);
}
@ -90,9 +77,21 @@ public class PascalGrammarTests
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
ProgramStruct root = CompilerHelpers.Analyse(program);
Assert.Equal("vartest", root.Head.ProgramName.IdentifierName);
}
[Fact]
public void ArrayTest()
{
const string program = """
program arrayTest;
var a : array [0..10] of integer;
begin
a[0] := 1;
end.
""";
CompilerHelpers.Analyse(program);
}
}

View File

@ -6,14 +6,40 @@ using Canon.Tests.Utils;
using Canon.Core.Abstractions;
namespace Canon.Tests.LexicalParserTests
{
public class NumberTests
public class NumberTests(ITestOutputHelper testOutputHelper)
{
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _testOutputHelper;
public NumberTests(ITestOutputHelper testOutputHelper)
[Theory]
[InlineData("123", 123)]
[InlineData("0", 0)]
public void IntegerTokenTest(string input, int result)
{
_testOutputHelper = testOutputHelper;
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(input));
NumberSemanticToken token = tokens.First().Convert<NumberSemanticToken>();
Assert.Equal(NumberType.Integer, token.NumberType);
Assert.Equal(result, token.ParseAsInteger());
}
[Theory]
[InlineData("0.0", 0)]
[InlineData("1.23", 1.23)]
[InlineData("1e7", 1e7)]
[InlineData("1E7", 1e7)]
[InlineData("1.23e2", 123)]
[InlineData("1.23E2", 123)]
[InlineData("123e-2", 1.23)]
[InlineData("123E-3", 0.123)]
[InlineData("12e-7", 12e-7)]
[InlineData(".123", 0.123)]
public void RealTokenTest(string input, double result)
{
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(input));
NumberSemanticToken token = tokens.First().Convert<NumberSemanticToken>();
Assert.Equal(NumberType.Real, token.NumberType);
Assert.Equal(result, token.ParseAsReal());
}
[Theory]
@ -50,7 +76,7 @@ namespace Canon.Tests.LexicalParserTests
public void TestParseNumberError(string input, uint expectedLine, uint expectedCharPosition, LexemeErrorType expectedErrorType)
{
var ex = Assert.Throws<LexemeException>(() => _lexer.Tokenize(new StringSourceReader(input)).ToList());
_testOutputHelper.WriteLine(ex.ToString());
testOutputHelper.WriteLine(ex.ToString());
Assert.Equal(expectedErrorType, ex.ErrorType);
Assert.Equal(expectedLine, ex.Line);
Assert.Equal(expectedCharPosition, ex.CharPosition);

View File

@ -0,0 +1,53 @@
using Canon.Core.Abstractions;
using Canon.Core.SemanticParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
namespace Canon.Tests.SemanticTests;
public class ConstValueTests
{
private class ConstValueVisitor : SyntaxNodeVisitor
{
public bool Pre { get; private set; }
public bool Post { get; private set; }
public override void PreVisit(ConstValue constValue)
{
constValue.OnNumberGenerator += (_, _) =>
{
Assert.False(Pre);
Pre = true;
};
}
public override void PostVisit(ConstValue constValue)
{
constValue.OnNumberGenerator += (_, _) =>
{
Assert.False(Post);
Post = true;
};
}
}
[Fact]
public void RaiseEventTest()
{
const string program = """
program main;
const a = 1;
begin
end.
""";
ProgramStruct root = CompilerHelpers.Analyse(program);
SyntaxTreeTraveller traveller = new();
ConstValueVisitor visitor = new();
traveller.Travel(root, visitor);
Assert.True(visitor.Pre);
Assert.True(visitor.Post);
}
}

View File

@ -0,0 +1,56 @@
using Canon.Core.Abstractions;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
namespace Canon.Tests.SemanticTests;
public class SyntaxTreeTravellerTests
{
private readonly ILexer _lexer = new Lexer();
private readonly IGrammarParser _grammarParser = GeneratedGrammarParser.Instance;
private readonly SyntaxTreeTraveller _traveller = new();
[Fact]
public void TravelTest()
{
const string program = """
program main;
begin
end.
""";
SampleSyntaxTreeVisitor visitor = new();
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _grammarParser.Analyse(tokens);
_traveller.Travel(root, visitor);
List<string> result =
[
"ProgramStruct",
"ProgramHead",
"ProgramHead",
"ProgramBody",
"SubprogramDeclarations",
"SubprogramDeclarations",
"CompoundStatement",
"StatementList",
"Statement",
"Statement",
"StatementList",
"CompoundStatement",
"ProgramBody",
"ProgramStruct"
];
string[] actual = visitor.ToString().Split('\n');
foreach ((string line, uint index) in result.WithIndex())
{
Assert.Equal(line, actual[(int)index]);
}
}
}

View File

@ -0,0 +1,306 @@
using Canon.Core.SemanticParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.Utils;
namespace Canon.Tests.SemanticTests;
public class TypeCheckVisitorTests
{
[Fact]
public void ConstTypeTest()
{
const string program = """
program main;
const a = 1; b = 1.23; c = 'a';
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol));
Assert.Equal(PascalBasicType.Integer, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol));
Assert.Equal(PascalBasicType.Real, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol));
Assert.Equal(PascalBasicType.Character, symbol.SymbolType);
}
[Fact]
public void SingleTypeTest()
{
const string program = """
program main;
var a : integer; b : char; c : boolean; d : real;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol));
Assert.Equal(PascalBasicType.Integer, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol));
Assert.Equal(PascalBasicType.Character, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol));
Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("d", out symbol));
Assert.Equal(PascalBasicType.Real, symbol.SymbolType);
}
[Fact]
public void MulitpleTypeTest()
{
const string program = """
program main;
var a, b, c, d : integer;
e, f, g : boolean;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
IEnumerable<string> names = ["a", "b", "c", "d"];
foreach (string name in names)
{
Assert.True(visitor.SymbolTable.TryGetSymbol(name, out Symbol? symbol));
Assert.Equal(PascalBasicType.Integer, symbol.SymbolType);
}
names = ["e", "f", "g"];
foreach (string name in names)
{
Assert.True(visitor.SymbolTable.TryGetSymbol(name, out Symbol? symbol));
Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType);
}
}
[Fact]
public void ArrayTest()
{
const string program = """
program main;
var a : array [0..10] of integer;
b : array [0..10, 0..20] of integer;
c : array [100..200, 1..5] of real;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalArrayType(PascalBasicType.Integer, 0, 10));
Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol));
Assert.Equal(symbol.SymbolType,
new PascalArrayType(new PascalArrayType(PascalBasicType.Integer, 0, 20), 0, 10));
Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol));
Assert.Equal(symbol.SymbolType, new PascalArrayType(
new PascalArrayType(PascalBasicType.Real, 1, 5), 100, 200));
}
[Fact]
public void ProcedureParameterTest()
{
const string program = """
program main;
procedure test(a, b, c : integer);
begin
end;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Integer, false)
], PascalBasicType.Void));
}
[Fact]
public void ProcedureVarParameterTest()
{
const string program = """
program main;
procedure test(var a, b, c : real);
begin
end;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Real, true),
new PascalParameterType(PascalBasicType.Real, true),
new PascalParameterType(PascalBasicType.Real, true)
], PascalBasicType.Void));
}
[Fact]
public void ProcedureBothParameterTest()
{
const string program = """
program main;
procedure test(a, b : integer; var c, d: char);
begin
end;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Character, true),
new PascalParameterType(PascalBasicType.Character, true)
], PascalBasicType.Void));
}
[Fact]
public void FunctionBothParameterTest()
{
const string program = """
program main;
function test(a, b : integer; var c, d: char) : real;
begin
end;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Character, true),
new PascalParameterType(PascalBasicType.Character, true)
], PascalBasicType.Real));
}
[Fact]
public void ProcedureAndFunctionTest()
{
const string program = """
program main;
procedure test1(a : integer; var b, c : real; d: boolean);
begin
end;
function test2(var a, b : boolean) : boolean;
begin
end;
begin
end.
""";
TypeCheckVisitor visitor = CheckType(program);
Assert.True(visitor.SymbolTable.TryGetSymbol("test1", out Symbol? symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Integer, false),
new PascalParameterType(PascalBasicType.Real, true),
new PascalParameterType(PascalBasicType.Real, true),
new PascalParameterType(PascalBasicType.Boolean, false)
], PascalBasicType.Void));
Assert.True(visitor.SymbolTable.TryGetSymbol("test2", out symbol));
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
new PascalParameterType(PascalBasicType.Boolean, true),
new PascalParameterType(PascalBasicType.Boolean, true)
], PascalBasicType.Boolean));
}
/// <summary>
/// 验证函数中的符号表是否正确
/// </summary>
private class SubprogramSymbolTableTestVisitor : TypeCheckVisitor
{
public override void PostVisit(SubprogramBody subprogramBody)
{
base.PostVisit(subprogramBody);
Assert.True(SymbolTable.TryGetSymbol("a", out Symbol? symbol));
Assert.Equal(PascalBasicType.Boolean, symbol.SymbolType);
// 递归查父符号表
Assert.True(SymbolTable.TryGetSymbol("b", out symbol));
Assert.Equal(PascalBasicType.Real, symbol.SymbolType);
Assert.True(SymbolTable.TryGetSymbol("c", out symbol));
Assert.Equal(PascalBasicType.Character, symbol.SymbolType);
Assert.True(SymbolTable.TryGetSymbol("d", out symbol));
Assert.Equal(PascalBasicType.Character, symbol.SymbolType);
}
}
[Fact]
public void SubprogramSymbolTableTest()
{
const string program = """
program main;
const a = 3;
var b, c : real;
procedure test(a : boolean);
var c, d : char;
begin
end;
begin
end.
""";
ProgramStruct root = CompilerHelpers.Analyse(program);
SubprogramSymbolTableTestVisitor visitor = new();
SyntaxTreeTraveller traveller = new();
traveller.Travel(root, visitor);
Assert.True(visitor.SymbolTable.TryGetSymbol("a", out Symbol? symbol));
Assert.Equal(PascalBasicType.Integer, symbol.SymbolType);
Assert.True(symbol.Const);
Assert.True(visitor.SymbolTable.TryGetSymbol("b", out symbol));
Assert.Equal(PascalBasicType.Real, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("c", out symbol));
Assert.Equal(PascalBasicType.Real, symbol.SymbolType);
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out symbol));
Assert.Equal(
new PascalFunctionType([
new PascalParameterType(PascalBasicType.Boolean, false)
], PascalBasicType.Void), symbol.SymbolType);
Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol));
}
private static TypeCheckVisitor CheckType(string program)
{
ProgramStruct root = CompilerHelpers.Analyse(program);
TypeCheckVisitor visitor = new();
SyntaxTreeTraveller traveller = new();
traveller.Travel(root, visitor);
return visitor;
}
}

View File

@ -0,0 +1,18 @@
using Canon.Core.Abstractions;
using Canon.Core.GrammarParser;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
namespace Canon.Tests.Utils;
public static class CompilerHelpers
{
public static ProgramStruct Analyse(string program)
{
ILexer lexer = new Lexer();
IGrammarParser grammarParser = GeneratedGrammarParser.Instance;
IEnumerable<SemanticToken> tokens = lexer.Tokenize(new StringSourceReader(program));
return grammarParser.Analyse(tokens);
}
}

View File

@ -0,0 +1,85 @@
using System.Text;
using Canon.Core.Abstractions;
using Canon.Core.SyntaxNodes;
namespace Canon.Tests.Utils;
public class SampleSyntaxTreeVisitor : SyntaxNodeVisitor
{
private readonly StringBuilder _builder = new();
public override string ToString()
{
return _builder.ToString();
}
public override void PreVisit(ProgramStruct programStruct)
{
_builder.Append(programStruct).Append('\n');
}
public override void PostVisit(ProgramStruct programStruct)
{
_builder.Append(programStruct).Append('\n');
}
public override void PreVisit(ProgramHead programHead)
{
_builder.Append(programHead).Append('\n');
}
public override void PostVisit(ProgramHead programHead)
{
_builder.Append(programHead).Append('\n');
}
public override void PreVisit(ProgramBody programBody)
{
_builder.Append(programBody).Append('\n');
}
public override void PostVisit(ProgramBody programBody)
{
_builder.Append(programBody).Append('\n');
}
public override void PreVisit(SubprogramDeclarations subprogramDeclarations)
{
_builder.Append(subprogramDeclarations).Append('\n');
}
public override void PostVisit(SubprogramDeclarations subprogramDeclarations)
{
_builder.Append(subprogramDeclarations).Append('\n');
}
public override void PreVisit(CompoundStatement compoundStatement)
{
_builder.Append(compoundStatement).Append('\n');
}
public override void PostVisit(CompoundStatement compoundStatement)
{
_builder.Append(compoundStatement).Append('\n');
}
public override void PreVisit(StatementList statementList)
{
_builder.Append(statementList).Append('\n');
}
public override void PostVisit(StatementList statementList)
{
_builder.Append(statementList).Append('\n');
}
public override void PreVisit(Statement statement)
{
_builder.Append(statement).Append('\n');
}
public override void PostVisit(Statement statement)
{
_builder.Append(statement).Append('\n');
}
}