parent
6130adfa7c
commit
8da24523c9
|
@ -21,7 +21,7 @@ public class Compiler(
|
||||||
IEnumerable<SemanticToken> tokens = lexer.Tokenize(await CreateSourceReader());
|
IEnumerable<SemanticToken> tokens = lexer.Tokenize(await CreateSourceReader());
|
||||||
ProgramStruct root = grammarParser.Analyse(tokens);
|
ProgramStruct root = grammarParser.Analyse(tokens);
|
||||||
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
CodeGeneratorVisitor visitor = new();
|
||||||
traveller.Travel(root, visitor);
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
await WriteToOutputFile(visitor.Builder.Build());
|
await WriteToOutputFile(visitor.Builder.Build());
|
||||||
|
|
|
@ -10,16 +10,65 @@ public class CCodeBuilder
|
||||||
{
|
{
|
||||||
private readonly StringBuilder _builder = new();
|
private readonly StringBuilder _builder = new();
|
||||||
|
|
||||||
/// <summary>
|
private int _scopeCount = 0;
|
||||||
/// 符号表
|
private string _scopeEmpty = string.Empty;
|
||||||
/// </summary>
|
|
||||||
public SymbolTable SymbolTable { get; } = new();
|
|
||||||
|
|
||||||
public void AddString(string code)
|
public void AddString(string code)
|
||||||
{
|
{
|
||||||
_builder.Append(code);
|
_builder.Append(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddLine(string code)
|
||||||
|
{
|
||||||
|
foreach (string line in code.Split('\n'))
|
||||||
|
{
|
||||||
|
_builder.Append(_scopeEmpty);
|
||||||
|
_builder.Append(line);
|
||||||
|
_builder.Append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始一段代码块
|
||||||
|
/// </summary>
|
||||||
|
public void BeginScope()
|
||||||
|
{
|
||||||
|
_builder.Append(_scopeEmpty).Append("{\n");
|
||||||
|
|
||||||
|
_scopeCount += 1;
|
||||||
|
string scopeEmpty = string.Empty;
|
||||||
|
|
||||||
|
for (int i = 0; i < _scopeCount; i++)
|
||||||
|
{
|
||||||
|
scopeEmpty += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
_scopeEmpty = scopeEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束一段代码块
|
||||||
|
/// </summary>
|
||||||
|
public void EndScope()
|
||||||
|
{
|
||||||
|
if (_scopeCount <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The scope has been closed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
_scopeCount -= 1;
|
||||||
|
|
||||||
|
string scopeEmpty = string.Empty;
|
||||||
|
|
||||||
|
for (int i = 0; i < _scopeCount; i++)
|
||||||
|
{
|
||||||
|
scopeEmpty += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
_scopeEmpty = scopeEmpty;
|
||||||
|
|
||||||
|
_builder.Append(_scopeEmpty).Append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
public string Build()
|
public string Build()
|
||||||
{
|
{
|
||||||
|
|
884
Canon.Core/SemanticParser/CodeGeneratorVisitor.cs
Normal file
884
Canon.Core/SemanticParser/CodeGeneratorVisitor.cs
Normal file
|
@ -0,0 +1,884 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using Canon.Core.CodeGenerators;
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
using Canon.Core.SyntaxNodes;
|
||||||
|
|
||||||
|
namespace Canon.Core.SemanticParser;
|
||||||
|
|
||||||
|
public class CodeGeneratorVisitor : TypeCheckVisitor
|
||||||
|
{
|
||||||
|
public CCodeBuilder Builder { get; } = new();
|
||||||
|
|
||||||
|
public override void PreVisit(ProgramHead programHead)
|
||||||
|
{
|
||||||
|
base.PreVisit(programHead);
|
||||||
|
|
||||||
|
Builder.AddLine("#include <stdio.h>");
|
||||||
|
Builder.AddLine("#include <stdbool.h>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(ProgramBody programBody)
|
||||||
|
{
|
||||||
|
base.PreVisit(programBody);
|
||||||
|
|
||||||
|
programBody.CompoundStatement.IsMain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(ConstDeclaration constDeclaration)
|
||||||
|
{
|
||||||
|
base.PreVisit(constDeclaration);
|
||||||
|
|
||||||
|
(IdentifierSemanticToken token, ConstValue constValue) = constDeclaration.ConstValue;
|
||||||
|
|
||||||
|
if (SymbolTable.TryGetSymbol(token.IdentifierName, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
Builder.AddLine(
|
||||||
|
$"const {GenerateBasicTypeString(symbol.SymbolType)} {token.IdentifierName} = {constValue.ValueString};");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(ConstValue constValue)
|
||||||
|
{
|
||||||
|
base.PostVisit(constValue);
|
||||||
|
|
||||||
|
constValue.OnNumberGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string numberValue = e.IsNegative ? "-" : "+";
|
||||||
|
|
||||||
|
if (constValue.ConstType == PascalBasicType.Integer)
|
||||||
|
{
|
||||||
|
numberValue += e.Token.ParseAsInteger().ToString();
|
||||||
|
}
|
||||||
|
else if (constValue.ConstType == PascalBasicType.Real)
|
||||||
|
{
|
||||||
|
numberValue += e.Token.ParseAsReal().ToString(CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
constValue.ValueString = numberValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
constValue.OnCharacterGenerator += (_, e) => { constValue.ValueString = $"'{e.Token.LiteralValue}'"; };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(VarDeclaration varDeclaration)
|
||||||
|
{
|
||||||
|
base.PreVisit(varDeclaration);
|
||||||
|
|
||||||
|
varDeclaration.IdentifierList.IsVariableDefinition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(VarDeclaration varDeclaration)
|
||||||
|
{
|
||||||
|
base.PostVisit(varDeclaration);
|
||||||
|
|
||||||
|
if (varDeclaration.IdentifierList.DefinitionType is PascalBasicType)
|
||||||
|
{
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(varDeclaration.IdentifierList.DefinitionType)} " +
|
||||||
|
$"{varDeclaration.Token.IdentifierName};");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varDeclaration.IdentifierList.DefinitionType is PascalArrayType)
|
||||||
|
{
|
||||||
|
(string basicValue, string periodValue) = GenerateArrayTypeString(
|
||||||
|
varDeclaration.IdentifierList.DefinitionType);
|
||||||
|
|
||||||
|
Builder.AddLine($"{basicValue} {varDeclaration.Token.IdentifierName}{periodValue};");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(IdentifierList identifierList)
|
||||||
|
{
|
||||||
|
base.PreVisit(identifierList);
|
||||||
|
|
||||||
|
identifierList.OnIdentifierGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
if (identifierList.IsVariableDefinition)
|
||||||
|
{
|
||||||
|
e.IdentifierList.IsVariableDefinition = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(IdentifierList identifierList)
|
||||||
|
{
|
||||||
|
base.PostVisit(identifierList);
|
||||||
|
|
||||||
|
identifierList.OnIdentifierGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
if (!identifierList.IsVariableDefinition)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifierList.DefinitionType is PascalArrayType)
|
||||||
|
{
|
||||||
|
(string basicTypeString, string periodString) = GenerateArrayTypeString(identifierList.DefinitionType);
|
||||||
|
|
||||||
|
Builder.AddLine($"{basicTypeString} {e.IdentifierToken.IdentifierName}{periodString};");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifierList.DefinitionType is PascalBasicType)
|
||||||
|
{
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(identifierList.DefinitionType)} " +
|
||||||
|
$"{e.IdentifierToken.IdentifierName};");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(CompoundStatement compoundStatement)
|
||||||
|
{
|
||||||
|
if (compoundStatement.IsMain)
|
||||||
|
{
|
||||||
|
Builder.AddLine("int main()");
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder.BeginScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(CompoundStatement compoundStatement)
|
||||||
|
{
|
||||||
|
if (compoundStatement.IsMain)
|
||||||
|
{
|
||||||
|
Builder.AddLine("return 0;");
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder.EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(Factor factor)
|
||||||
|
{
|
||||||
|
base.PostVisit(factor);
|
||||||
|
|
||||||
|
factor.OnNumberGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
switch (e.Token.NumberType)
|
||||||
|
{
|
||||||
|
case NumberType.Integer:
|
||||||
|
Builder.AddLine($"int {temporaryName} = {e.Token.ParseAsInteger()};");
|
||||||
|
break;
|
||||||
|
case NumberType.Real:
|
||||||
|
Builder.AddLine($"double {temporaryName} = {e.Token.ParseAsReal()};");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
|
||||||
|
factor.OnBooleanGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
string value = e.Value ? "true" : "false";
|
||||||
|
|
||||||
|
Builder.AddLine($"bool {temporaryName} = {value};");
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
|
||||||
|
factor.OnVariableGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
if (SymbolTable.TryGetSymbol(e.Variable.VariableName, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
// 处理不带括号调用无参函数的问题
|
||||||
|
if (symbol.SymbolType is PascalFunctionType { Parameters.Count: 0 } functionType)
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(functionType.ReturnType)} {temporaryName} = " +
|
||||||
|
$"{symbol.SymbolName}_pascal_procedure();");
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factor.VariableName = e.Variable.VariableName;
|
||||||
|
};
|
||||||
|
|
||||||
|
factor.OnParethnesisGenerator += (_, e) => { factor.VariableName = e.Expression.VariableName; };
|
||||||
|
|
||||||
|
factor.OnNotGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine(
|
||||||
|
$"{GenerateBasicTypeString(factor.VariableType)} {temporaryName} = ~{e.Factor.VariableName};");
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
|
||||||
|
factor.OnPlusGenerator += (_, e) => { factor.VariableName = e.Factor.VariableName; };
|
||||||
|
|
||||||
|
factor.OnUminusGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine(
|
||||||
|
$"{GenerateBasicTypeString(factor.VariableType)} {temporaryName} = -{e.Factor.VariableName};");
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
|
||||||
|
factor.OnProcedureCallGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(factor.VariableType)} {temporaryName}= " +
|
||||||
|
$"{GenerateProcedureCall(e.ProcedureName.IdentifierName, e.Parameters)};");
|
||||||
|
factor.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(Variable variable)
|
||||||
|
{
|
||||||
|
base.PostVisit(variable);
|
||||||
|
|
||||||
|
if (!SymbolTable.TryGetSymbol(variable.Identifier.IdentifierName, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PascalType type = symbol.SymbolType;
|
||||||
|
|
||||||
|
if (type is PascalArrayType)
|
||||||
|
{
|
||||||
|
string indexValue = string.Empty;
|
||||||
|
foreach (Expression expression in variable.VarPart.Expressions)
|
||||||
|
{
|
||||||
|
PascalArrayType arrayType = type.Convert<PascalArrayType>();
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine($"int {temporaryName} = {expression.VariableName} - {arrayType.Begin};");
|
||||||
|
|
||||||
|
indexValue += $"[{temporaryName}]";
|
||||||
|
type = arrayType.ElementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
variable.VariableName = variable.Identifier.IdentifierName + indexValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
variable.VariableName = variable.Identifier.IdentifierName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(Term term)
|
||||||
|
{
|
||||||
|
base.PostVisit(term);
|
||||||
|
|
||||||
|
term.OnFactorGenerator += (_, e) => { term.VariableName = e.Factor.VariableName; };
|
||||||
|
|
||||||
|
term.OnMultiplyGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine(
|
||||||
|
$"{GenerateBasicTypeString(term.VariableType)} {temporaryName} = " +
|
||||||
|
$"{e.Left.VariableName} {GenerateMultipleOperator(e.Operator)} {e.Right.VariableName};");
|
||||||
|
term.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(SimpleExpression simpleExpression)
|
||||||
|
{
|
||||||
|
base.PostVisit(simpleExpression);
|
||||||
|
|
||||||
|
simpleExpression.OnTermGenerator += (_, e) => { simpleExpression.VariableName = e.Term.VariableName; };
|
||||||
|
|
||||||
|
simpleExpression.OnAddGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine(
|
||||||
|
$"{GenerateBasicTypeString(simpleExpression.VariableType)} {temporaryName} = " +
|
||||||
|
$"{e.Left.VariableName} {GenerateAddOperator(e.Operator)} {e.Right.VariableName};");
|
||||||
|
|
||||||
|
simpleExpression.VariableName = temporaryName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(Expression expression)
|
||||||
|
{
|
||||||
|
base.PostVisit(expression);
|
||||||
|
|
||||||
|
expression.OnSimpleExpressionGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
expression.VariableName = e.SimpleExpression.VariableName;
|
||||||
|
|
||||||
|
HandleExpression(expression);
|
||||||
|
};
|
||||||
|
|
||||||
|
expression.OnRelationGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
string temporaryName = GenerateTemporaryVariable();
|
||||||
|
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(expression.VariableType)} {temporaryName} = " +
|
||||||
|
$"{e.Left.VariableName} {GenerateRelationOperator(e.Operator)} {e.Right.VariableName};");
|
||||||
|
expression.VariableName = temporaryName;
|
||||||
|
|
||||||
|
HandleExpression(expression);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleExpression(Expression expression)
|
||||||
|
{
|
||||||
|
if (expression.IsIfCondition)
|
||||||
|
{
|
||||||
|
_ifConditionNames.Push(expression.VariableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.IsForConditionBegin)
|
||||||
|
{
|
||||||
|
_forBeginConditions.Push(expression.VariableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.IsForConditionEnd)
|
||||||
|
{
|
||||||
|
_forEndConditions.Push(expression.VariableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 存储IF语句中条件变量的名称
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _ifConditionNames = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IF语句中成功分支的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _ifTrueLabels = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IF语句中失败分支的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _ifFalseLabels = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IF语句中结束的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _ifEndLabels = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句中的循环变量名称
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forVariables = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句中的循环变量的初始值
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forBeginConditions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句中循环变量的判断值
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forEndConditions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句开始的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forLabels = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句条件判断部分的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forConditionLabels = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FOR语句结束的标签
|
||||||
|
/// </summary>
|
||||||
|
private readonly Stack<string> _forEndLabels = new();
|
||||||
|
|
||||||
|
public override void PreVisit(Statement statement)
|
||||||
|
{
|
||||||
|
base.PreVisit(statement);
|
||||||
|
|
||||||
|
statement.OnIfGenerator += (_, e) => { e.Condition.IsIfCondition = true; };
|
||||||
|
|
||||||
|
statement.OnForGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
e.Begin.IsForConditionBegin = true;
|
||||||
|
e.End.IsForConditionEnd = true;
|
||||||
|
_forVariables.Push(e.Iterator.IdentifierName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(Statement statement)
|
||||||
|
{
|
||||||
|
base.PostVisit(statement);
|
||||||
|
|
||||||
|
statement.OnAssignGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
if (!SymbolTable.TryGetSymbol(e.Variable.Identifier.IdentifierName, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol.Reference)
|
||||||
|
{
|
||||||
|
e.Variable.VariableName = "*" + e.Variable.VariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder.AddLine($"{e.Variable.VariableName} = {e.Expression.VariableName};");
|
||||||
|
};
|
||||||
|
|
||||||
|
statement.OnForGenerator += (_, _) =>
|
||||||
|
{
|
||||||
|
Builder.AddLine($"""
|
||||||
|
{_forVariables.Peek()} = {_forVariables.Peek()} + 1;
|
||||||
|
goto {_forConditionLabels.Peek()};
|
||||||
|
{_forEndLabels.Peek()}:;
|
||||||
|
""");
|
||||||
|
|
||||||
|
_forLabels.Pop();
|
||||||
|
_forConditionLabels.Pop();
|
||||||
|
_forEndLabels.Pop();
|
||||||
|
_forVariables.Pop();
|
||||||
|
_forBeginConditions.Pop();
|
||||||
|
_forEndConditions.Pop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(ElsePart elsePart)
|
||||||
|
{
|
||||||
|
base.PreVisit(elsePart);
|
||||||
|
|
||||||
|
// 这是成功分支跳过Else的语句
|
||||||
|
Builder.AddLine($"goto {_ifEndLabels.Peek()};");
|
||||||
|
Builder.AddLine($"{_ifFalseLabels.Peek()}:;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(ElsePart elsePart)
|
||||||
|
{
|
||||||
|
base.PostVisit(elsePart);
|
||||||
|
|
||||||
|
Builder.AddLine($"{_ifEndLabels.Peek()}:;");
|
||||||
|
_ifConditionNames.Pop();
|
||||||
|
_ifTrueLabels.Pop();
|
||||||
|
_ifFalseLabels.Pop();
|
||||||
|
_ifEndLabels.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(ProcedureCall procedureCall)
|
||||||
|
{
|
||||||
|
base.PostVisit(procedureCall);
|
||||||
|
|
||||||
|
Builder.AddLine(GenerateProcedureCall(procedureCall.ProcedureId.IdentifierName, procedureCall.Parameters) +
|
||||||
|
";");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(SubprogramHead subprogramHead)
|
||||||
|
{
|
||||||
|
base.PostVisit(subprogramHead);
|
||||||
|
|
||||||
|
subprogramHead.OnProcedureGenerator += (_, _) =>
|
||||||
|
{
|
||||||
|
GenerateProcedureHead(subprogramHead.SubprogramName.IdentifierName);
|
||||||
|
};
|
||||||
|
|
||||||
|
subprogramHead.OnFunctionGenerator += (_, _) =>
|
||||||
|
{
|
||||||
|
GenerateProcedureHead(subprogramHead.SubprogramName.IdentifierName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Symbol? _function;
|
||||||
|
|
||||||
|
private void GenerateProcedureHead(string procedureId)
|
||||||
|
{
|
||||||
|
if (!SymbolTable.TryGetParent(out SymbolTable? parent))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent.TryGetSymbol(procedureId, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol.SymbolType is not PascalFunctionType functionType)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_function = symbol;
|
||||||
|
string result =
|
||||||
|
$"{GenerateBasicTypeString(functionType.ReturnType)} {procedureId}_pascal_procedure(";
|
||||||
|
|
||||||
|
// 控制格式
|
||||||
|
bool isStart = true;
|
||||||
|
|
||||||
|
foreach (PascalParameterType parameter in functionType.Parameters)
|
||||||
|
{
|
||||||
|
string value = string.Empty;
|
||||||
|
|
||||||
|
if (parameter.ParameterType is PascalBasicType)
|
||||||
|
{
|
||||||
|
string refValue = parameter.IsVar ? "*" : string.Empty;
|
||||||
|
value = $"{GenerateBasicTypeString(parameter.ParameterType)} {refValue}{parameter.ParameterName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameter.ParameterType is PascalArrayType)
|
||||||
|
{
|
||||||
|
value = $"{GenerateArrayTypeString(parameter.ParameterType)} {parameter.ParameterName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStart)
|
||||||
|
{
|
||||||
|
result += value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += ", " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += ")";
|
||||||
|
|
||||||
|
Builder.AddLine(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreVisit(SubprogramBody subprogramBody)
|
||||||
|
{
|
||||||
|
base.PreVisit(subprogramBody);
|
||||||
|
|
||||||
|
Builder.BeginScope();
|
||||||
|
|
||||||
|
if (_function is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PascalFunctionType functionType = _function.SymbolType.Convert<PascalFunctionType>();
|
||||||
|
|
||||||
|
if (functionType.ReturnType != PascalBasicType.Void)
|
||||||
|
{
|
||||||
|
Builder.AddLine($"{GenerateBasicTypeString(functionType.ReturnType)} {_function.SymbolName};");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(SubprogramBody subprogramBody)
|
||||||
|
{
|
||||||
|
base.PostVisit(subprogramBody);
|
||||||
|
|
||||||
|
if (_function is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PascalFunctionType functionType = _function.SymbolType.Convert<PascalFunctionType>();
|
||||||
|
|
||||||
|
if (functionType.ReturnType != PascalBasicType.Void)
|
||||||
|
{
|
||||||
|
Builder.AddLine($"return {_function.SymbolName};");
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder.EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostVisit(TerminatedSyntaxNode terminatedSyntaxNode)
|
||||||
|
{
|
||||||
|
base.PostVisit(terminatedSyntaxNode);
|
||||||
|
|
||||||
|
if (terminatedSyntaxNode.Token.TokenType == SemanticTokenType.Keyword)
|
||||||
|
{
|
||||||
|
KeywordType keywordType = terminatedSyntaxNode.Token.Convert<KeywordSemanticToken>().KeywordType;
|
||||||
|
|
||||||
|
switch (keywordType)
|
||||||
|
{
|
||||||
|
case KeywordType.Then:
|
||||||
|
GenerateIfLabel();
|
||||||
|
|
||||||
|
Builder.AddLine($"""
|
||||||
|
if ({_ifConditionNames.Peek()})
|
||||||
|
goto {_ifTrueLabels.Peek()};
|
||||||
|
else
|
||||||
|
goto {_ifFalseLabels.Peek()};
|
||||||
|
{_ifTrueLabels.Peek()}:;
|
||||||
|
""");
|
||||||
|
break;
|
||||||
|
case KeywordType.To:
|
||||||
|
GenerateForLabel();
|
||||||
|
|
||||||
|
Builder.AddLine($"""
|
||||||
|
{_forVariables.Peek()} = {_forBeginConditions.Peek()};
|
||||||
|
{_forConditionLabels.Peek()}:;
|
||||||
|
""");
|
||||||
|
break;
|
||||||
|
case KeywordType.Do:
|
||||||
|
Builder.AddLine($"""
|
||||||
|
if ({_forVariables.Peek()} <= {_forEndConditions.Peek()})
|
||||||
|
goto {_forLabels.Peek()};
|
||||||
|
else
|
||||||
|
goto {_forEndLabels.Peek()};
|
||||||
|
{_forLabels.Peek()}:;
|
||||||
|
""");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateProcedureCall(string procedureId, List<Expression> parameters)
|
||||||
|
{
|
||||||
|
string isReturn = procedureId == "writeln" || procedureId == "readln" ? "\\n" : string.Empty;
|
||||||
|
|
||||||
|
if (procedureId == "write" || procedureId == "writeln")
|
||||||
|
{
|
||||||
|
string result = $"printf(\"{GenerateFormatString(parameters) + isReturn}\"";
|
||||||
|
|
||||||
|
foreach (Expression parameter in parameters)
|
||||||
|
{
|
||||||
|
result += $", {parameter.VariableName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (procedureId == "read" || procedureId == "readln")
|
||||||
|
{
|
||||||
|
string result = $"scanf(\"{GenerateFormatString(parameters) + isReturn}\"";
|
||||||
|
|
||||||
|
foreach (Expression parameter in parameters)
|
||||||
|
{
|
||||||
|
result += $", &{parameter.VariableName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SymbolTable.TryGetSymbol(procedureId, out Symbol? symbol))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string parameterValue = string.Empty;
|
||||||
|
|
||||||
|
PascalFunctionType functionType;
|
||||||
|
if (symbol.SymbolType is not PascalFunctionType innerType)
|
||||||
|
{
|
||||||
|
// 处理函数内部的递归调用
|
||||||
|
if (!SymbolTable.TryGetParent(out SymbolTable? parent))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent.TryGetSymbol(procedureId, out symbol))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol.SymbolType is PascalFunctionType outerType)
|
||||||
|
{
|
||||||
|
functionType = outerType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
functionType = innerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((Expression parameter, PascalParameterType parameterType) in
|
||||||
|
parameters.Zip(functionType.Parameters))
|
||||||
|
{
|
||||||
|
if (parameterType.IsVar)
|
||||||
|
{
|
||||||
|
parameterValue += $", &{parameter.VariableName}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parameterValue += $", {parameter.VariableName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterValue = parameterValue == string.Empty ? parameterValue : parameterValue[1..];
|
||||||
|
return $"{procedureId}_pascal_procedure({parameterValue})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateFormatString(List<Expression> expressions, bool output = false)
|
||||||
|
{
|
||||||
|
string value = string.Empty;
|
||||||
|
|
||||||
|
foreach (Expression expression in expressions)
|
||||||
|
{
|
||||||
|
if (expression.VariableType == PascalBasicType.Integer)
|
||||||
|
{
|
||||||
|
value += "%d ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.VariableType == PascalBasicType.Real)
|
||||||
|
{
|
||||||
|
// 这里需要按照输出调整
|
||||||
|
value += output ? "%.6lf " : "%lf ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.VariableType == PascalBasicType.Character)
|
||||||
|
{
|
||||||
|
value += "%c ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateBasicTypeString(PascalType pascalType)
|
||||||
|
{
|
||||||
|
if (pascalType == PascalBasicType.Character)
|
||||||
|
{
|
||||||
|
return "char";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pascalType == PascalBasicType.Boolean)
|
||||||
|
{
|
||||||
|
return "bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pascalType == PascalBasicType.Integer)
|
||||||
|
{
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pascalType == PascalBasicType.Real)
|
||||||
|
{
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pascalType == PascalBasicType.Void)
|
||||||
|
{
|
||||||
|
return "void";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string, string) GenerateArrayTypeString(PascalType pascalType)
|
||||||
|
{
|
||||||
|
string periodString = string.Empty;
|
||||||
|
|
||||||
|
while (pascalType is PascalArrayType arrayType)
|
||||||
|
{
|
||||||
|
periodString += $"[{arrayType.End - arrayType.Begin + 1}]";
|
||||||
|
|
||||||
|
pascalType = arrayType.ElementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (GenerateBasicTypeString(pascalType), periodString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateMultipleOperator(MultiplyOperator multiplyOperator)
|
||||||
|
{
|
||||||
|
if (multiplyOperator.OperatorToken.TokenType == SemanticTokenType.Operator)
|
||||||
|
{
|
||||||
|
OperatorType operatorType = multiplyOperator.OperatorToken.Convert<OperatorSemanticToken>().OperatorType;
|
||||||
|
if (operatorType == OperatorType.Multiply)
|
||||||
|
{
|
||||||
|
return "*";
|
||||||
|
}
|
||||||
|
else if (operatorType == OperatorType.Divide)
|
||||||
|
{
|
||||||
|
//实数除法,需要将操作数强转为double
|
||||||
|
return "/(double)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
KeywordType keywordType = multiplyOperator.OperatorToken.Convert<KeywordSemanticToken>().KeywordType;
|
||||||
|
switch (keywordType)
|
||||||
|
{
|
||||||
|
case KeywordType.And:
|
||||||
|
return "&&";
|
||||||
|
case KeywordType.Mod:
|
||||||
|
return "%";
|
||||||
|
case KeywordType.Divide:
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateAddOperator(AddOperator addOperator)
|
||||||
|
{
|
||||||
|
SemanticToken token = addOperator.OperatorToken;
|
||||||
|
if (token.TokenType == SemanticTokenType.Operator)
|
||||||
|
{
|
||||||
|
OperatorType operatorType = token.Convert<OperatorSemanticToken>().OperatorType;
|
||||||
|
if (operatorType == OperatorType.Plus)
|
||||||
|
{
|
||||||
|
return "+";
|
||||||
|
}
|
||||||
|
else if (operatorType == OperatorType.Minus)
|
||||||
|
{
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "||";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateRelationOperator(RelationOperator relationOperator)
|
||||||
|
{
|
||||||
|
var operatorType = relationOperator.OperatorToken.Convert<OperatorSemanticToken>().OperatorType;
|
||||||
|
switch (operatorType)
|
||||||
|
{
|
||||||
|
case OperatorType.Equal:
|
||||||
|
return "==";
|
||||||
|
case OperatorType.Greater:
|
||||||
|
return ">";
|
||||||
|
case OperatorType.Less:
|
||||||
|
return "<";
|
||||||
|
case OperatorType.GreaterEqual:
|
||||||
|
return ">=";
|
||||||
|
case OperatorType.LessEqual:
|
||||||
|
return "<=";
|
||||||
|
case OperatorType.NotEqual:
|
||||||
|
return "!=";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long _temporaryVariableCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 产生一个全局唯一的临时变量名
|
||||||
|
/// </summary>
|
||||||
|
private string GenerateTemporaryVariable()
|
||||||
|
{
|
||||||
|
string name = $"__temp_{_temporaryVariableCount}";
|
||||||
|
_temporaryVariableCount += 1;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long _labelCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 产生一对IF语句中的标签
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateIfLabel()
|
||||||
|
{
|
||||||
|
_ifTrueLabels.Push($"if_true_{_labelCount}");
|
||||||
|
_ifFalseLabels.Push($"if_false_{_labelCount}");
|
||||||
|
_ifEndLabels.Push($"_if_end_{_labelCount}");
|
||||||
|
|
||||||
|
_labelCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 产生FOR语句中的标签
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateForLabel()
|
||||||
|
{
|
||||||
|
_forLabels.Push($"for_{_labelCount}");
|
||||||
|
_forConditionLabels.Push($"for_condition_{_labelCount}");
|
||||||
|
_forEndLabels.Push($"for_end_{_labelCount}");
|
||||||
|
|
||||||
|
_labelCount += 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ public class PascalFunctionType(List<PascalParameterType> parameters, PascalType
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pascal核心库函数的类型
|
/// Pascal核心库函数的类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static PascalFunctionType CoreFuntionType => new PascalFunctionType([], PascalBasicType.Void);
|
public static PascalFunctionType CoreFuntionType => new([], PascalBasicType.Void);
|
||||||
|
|
||||||
public override string TypeName
|
public override string TypeName
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
namespace Canon.Core.SemanticParser;
|
namespace Canon.Core.SemanticParser;
|
||||||
|
|
||||||
public class PascalParameterType(PascalType parameterType, bool isVar) : PascalType
|
public class PascalParameterType(PascalType parameterType, bool isVar, string parameterName) : PascalType
|
||||||
{
|
{
|
||||||
public PascalType ParameterType { get; } = parameterType;
|
public PascalType ParameterType { get; } = parameterType;
|
||||||
|
|
||||||
public bool IsVar { get; } = isVar;
|
public bool IsVar { get; } = isVar;
|
||||||
|
|
||||||
|
public string ParameterName { get; } = parameterName;
|
||||||
|
|
||||||
public override string TypeName
|
public override string TypeName
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
|
@ -88,50 +88,46 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
{
|
{
|
||||||
case NumberType.Integer:
|
case NumberType.Integer:
|
||||||
factor.FactorType = PascalBasicType.Integer;
|
factor.FactorType = PascalBasicType.Integer;
|
||||||
|
factor.VariableType = PascalBasicType.Integer;
|
||||||
break;
|
break;
|
||||||
case NumberType.Real:
|
case NumberType.Real:
|
||||||
factor.FactorType = PascalBasicType.Real;
|
factor.FactorType = PascalBasicType.Real;
|
||||||
|
factor.VariableType = PascalBasicType.Real;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// factor -> true | false
|
// factor -> true | false
|
||||||
factor.OnBooleanGenerator += (_, _) => { factor.FactorType = PascalBasicType.Boolean; };
|
factor.OnBooleanGenerator += (_, _) =>
|
||||||
|
{
|
||||||
|
factor.FactorType = PascalBasicType.Boolean;
|
||||||
|
factor.VariableType = PascalBasicType.Boolean;
|
||||||
|
};
|
||||||
|
|
||||||
// factor -> variable
|
// factor -> variable
|
||||||
factor.OnVariableGenerator += (_, e) => { factor.FactorType = e.Variable.VariableType; };
|
factor.OnVariableGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
factor.FactorType = e.Variable.VariableType;
|
||||||
|
factor.VariableType = e.Variable.VariableType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// factor -> (expression)
|
// factor -> (expression)
|
||||||
factor.OnParethnesisGenerator += (_, e) => { factor.FactorType = e.Expression.ExpressionType; };
|
factor.OnParethnesisGenerator += (_, e) =>
|
||||||
|
|
||||||
// factor -> id ()
|
|
||||||
factor.OnNoParameterProcedureCallGenerator += (_, e) =>
|
|
||||||
{
|
{
|
||||||
if (ValidateProcedureCall(e.ProcedureName, [], out PascalFunctionType? functionType))
|
factor.FactorType = e.Expression.ExpressionType;
|
||||||
{
|
factor.VariableType = e.Expression.VariableType;
|
||||||
if (functionType.ReturnType != PascalBasicType.Void)
|
|
||||||
{
|
|
||||||
factor.FactorType = functionType.ReturnType;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IsError = true;
|
|
||||||
logger?.LogError("The procedure '{}' returns void.", e.ProcedureName.IdentifierName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
factor.FactorType = PascalBasicType.Void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// factor -> id ( ExpressionList)
|
// factor -> id ( ExpressionList)
|
||||||
factor.OnProcedureCallGenerator += (_, e) =>
|
factor.OnProcedureCallGenerator += (_, e) =>
|
||||||
{
|
{
|
||||||
if (ValidateProcedureCall(e.ProcedureName, e.Parameters.Expressions, out PascalFunctionType? functionType))
|
if (ValidateProcedureCall(e.ProcedureName, e.Parameters, out PascalFunctionType? functionType))
|
||||||
{
|
{
|
||||||
if (functionType.ReturnType != PascalBasicType.Void)
|
if (functionType.ReturnType != PascalBasicType.Void)
|
||||||
{
|
{
|
||||||
factor.FactorType = functionType.ReturnType;
|
factor.FactorType = functionType.ReturnType;
|
||||||
|
factor.VariableType = functionType.ReturnType;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -142,29 +138,47 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
}
|
}
|
||||||
|
|
||||||
factor.FactorType = PascalBasicType.Void;
|
factor.FactorType = PascalBasicType.Void;
|
||||||
|
factor.VariableType = PascalBasicType.Void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// factor -> not factor
|
// factor -> not factor
|
||||||
factor.OnNotGenerator += (_, e) => { factor.FactorType = e.Factor.FactorType; };
|
factor.OnNotGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
factor.FactorType = e.Factor.FactorType;
|
||||||
|
factor.VariableType = e.Factor.VariableType;
|
||||||
|
};
|
||||||
|
|
||||||
// factor -> uminus factor
|
// factor -> uminus factor
|
||||||
factor.OnUminusGenerator += (_, e) => { factor.FactorType = e.Factor.FactorType; };
|
factor.OnUminusGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
factor.FactorType = e.Factor.FactorType;
|
||||||
|
factor.VariableType = e.Factor.VariableType;
|
||||||
|
};
|
||||||
|
|
||||||
// factor -> plus factor
|
// factor -> plus factor
|
||||||
factor.OnPlusGenerator += (_, e) => { factor.FactorType = e.Factor.FactorType; };
|
factor.OnPlusGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
factor.FactorType = e.Factor.FactorType;
|
||||||
|
factor.VariableType = e.Factor.VariableType;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PostVisit(Term term)
|
public override void PostVisit(Term term)
|
||||||
{
|
{
|
||||||
base.PostVisit(term);
|
base.PostVisit(term);
|
||||||
|
|
||||||
term.OnFactorGenerator += (_, e) => { term.TermType = e.Factor.FactorType; };
|
term.OnFactorGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
term.TermType = e.Factor.FactorType;
|
||||||
|
term.VariableType = e.Factor.VariableType;
|
||||||
|
};
|
||||||
|
|
||||||
term.OnMultiplyGenerator += (_, e) =>
|
term.OnMultiplyGenerator += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PascalType.IsCalculatable(e.Left.TermType) && PascalType.IsCalculatable(e.Right.FactorType))
|
if (PascalType.IsCalculatable(e.Left.TermType) && PascalType.IsCalculatable(e.Right.FactorType))
|
||||||
{
|
{
|
||||||
term.TermType = e.Left.TermType + e.Right.FactorType;
|
term.TermType = e.Left.TermType + e.Right.FactorType;
|
||||||
|
term.VariableType = e.Left.TermType + e.Right.FactorType;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,13 +191,18 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
{
|
{
|
||||||
base.PostVisit(simpleExpression);
|
base.PostVisit(simpleExpression);
|
||||||
|
|
||||||
simpleExpression.OnTermGenerator += (_, e) => { simpleExpression.SimpleExpressionType = e.Term.TermType; };
|
simpleExpression.OnTermGenerator += (_, e) =>
|
||||||
|
{
|
||||||
|
simpleExpression.SimpleExpressionType = e.Term.TermType;
|
||||||
|
simpleExpression.VariableType = e.Term.VariableType;
|
||||||
|
};
|
||||||
|
|
||||||
simpleExpression.OnAddGenerator += (_, e) =>
|
simpleExpression.OnAddGenerator += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PascalType.IsCalculatable(e.Left.SimpleExpressionType) && PascalType.IsCalculatable(e.Right.TermType))
|
if (PascalType.IsCalculatable(e.Left.SimpleExpressionType) && PascalType.IsCalculatable(e.Right.TermType))
|
||||||
{
|
{
|
||||||
simpleExpression.SimpleExpressionType = e.Left.SimpleExpressionType + e.Right.TermType;
|
simpleExpression.SimpleExpressionType = e.Left.SimpleExpressionType + e.Right.TermType;
|
||||||
|
simpleExpression.VariableType = e.Left.VariableType + e.Right.VariableType;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,9 +218,14 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
expression.OnSimpleExpressionGenerator += (_, e) =>
|
expression.OnSimpleExpressionGenerator += (_, e) =>
|
||||||
{
|
{
|
||||||
expression.ExpressionType = e.SimpleExpression.SimpleExpressionType;
|
expression.ExpressionType = e.SimpleExpression.SimpleExpressionType;
|
||||||
|
expression.VariableType = e.SimpleExpression.VariableType;
|
||||||
};
|
};
|
||||||
|
|
||||||
expression.OnRelationGenerator += (_, _) => { expression.ExpressionType = PascalBasicType.Boolean; };
|
expression.OnRelationGenerator += (_, _) =>
|
||||||
|
{
|
||||||
|
expression.ExpressionType = PascalBasicType.Boolean;
|
||||||
|
expression.VariableType = PascalBasicType.Boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PostVisit(TypeSyntaxNode typeSyntaxNode)
|
public override void PostVisit(TypeSyntaxNode typeSyntaxNode)
|
||||||
|
@ -334,7 +358,7 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
{
|
{
|
||||||
foreach (Symbol symbol in children.AsEnumerable().Reverse())
|
foreach (Symbol symbol in children.AsEnumerable().Reverse())
|
||||||
{
|
{
|
||||||
parameters.Add(new PascalParameterType(symbol.SymbolType, symbol.Reference));
|
parameters.Add(new PascalParameterType(symbol.SymbolType, symbol.Reference, symbol.SymbolName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,13 +446,13 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
e.Variable.Identifier.IdentifierName);
|
e.Variable.Identifier.IdentifierName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.Variable.VariableType != e.Expression.ExpressionType)
|
if (e.Variable.VariableType != e.Expression.VariableType)
|
||||||
{
|
{
|
||||||
IsError = true;
|
IsError = true;
|
||||||
logger?.LogError("Variable '{}' type mismatch, expect '{}' but '{}'.",
|
logger?.LogError("Variable '{}' type mismatch, expect '{}' but '{}'.",
|
||||||
e.Variable.Identifier.IdentifierName,
|
e.Variable.Identifier.IdentifierName,
|
||||||
e.Variable.VariableType.ToString(),
|
e.Variable.VariableType.ToString(),
|
||||||
e.Expression.ExpressionType.ToString());
|
e.Expression.VariableType.ToString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class CompoundStatement : NonTerminatedSyntaxNode
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否为主函数部分
|
/// 是否为主函数部分
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsMain;
|
public bool IsMain { get; set; }
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,8 @@ public class ConstValue : NonTerminatedSyntaxNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ValueString { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用数值产生式的事件
|
/// 使用数值产生式的事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -50,10 +50,23 @@ public class Expression : NonTerminatedSyntaxNode
|
||||||
/// 当前表达式对应的数组下标维度的左边界
|
/// 当前表达式对应的数组下标维度的左边界
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LeftBound;
|
public int LeftBound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为FOR语句中的起始语句
|
||||||
|
/// </summary>
|
||||||
public bool IsForConditionBegin { get; set; }
|
public bool IsForConditionBegin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为FOR语句中的结束语句
|
||||||
|
/// </summary>
|
||||||
public bool IsForConditionEnd { get; set; }
|
public bool IsForConditionEnd { get; set; }
|
||||||
public bool IsAssign { get; set; }
|
public bool IsAssign { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为IF语句中的条件语句
|
||||||
|
/// </summary>
|
||||||
|
public bool IsIfCondition { get; set; }
|
||||||
|
|
||||||
private IdentifierSemanticToken? _iterator;
|
private IdentifierSemanticToken? _iterator;
|
||||||
|
|
||||||
public IdentifierSemanticToken Iterator
|
public IdentifierSemanticToken Iterator
|
||||||
|
|
|
@ -20,16 +20,11 @@ public class ParethnesisGeneratorEventArgs : EventArgs
|
||||||
public required Expression Expression { get; init; }
|
public required Expression Expression { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NoParameterProcedureCallGeneratorEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public required IdentifierSemanticToken ProcedureName { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProcedureCallGeneratorEventArgs : EventArgs
|
public class ProcedureCallGeneratorEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public required IdentifierSemanticToken ProcedureName { get; init; }
|
public required IdentifierSemanticToken ProcedureName { get; init; }
|
||||||
|
|
||||||
public required ExpressionList Parameters { get; init; }
|
public List<Expression> Parameters { get; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NotGeneratorEventArgs : EventArgs
|
public class NotGeneratorEventArgs : EventArgs
|
||||||
|
@ -103,11 +98,6 @@ public class Factor : NonTerminatedSyntaxNode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<BooleanGeneratorEventArgs>? OnBooleanGenerator;
|
public event EventHandler<BooleanGeneratorEventArgs>? OnBooleanGenerator;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 没有参数的过程调用产生式事件
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<NoParameterProcedureCallGeneratorEventArgs>? OnNoParameterProcedureCallGenerator;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 过程调用产生式的事件
|
/// 过程调用产生式的事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -185,7 +175,7 @@ public class Factor : NonTerminatedSyntaxNode
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// factor -> id ( )
|
// factor -> id ( )
|
||||||
OnNoParameterProcedureCallGenerator?.Invoke(this, new NoParameterProcedureCallGeneratorEventArgs
|
OnProcedureCallGenerator?.Invoke(this, new ProcedureCallGeneratorEventArgs
|
||||||
{
|
{
|
||||||
ProcedureName = terminatedSyntaxNode.Token.Convert<IdentifierSemanticToken>()
|
ProcedureName = terminatedSyntaxNode.Token.Convert<IdentifierSemanticToken>()
|
||||||
});
|
});
|
||||||
|
@ -194,11 +184,13 @@ public class Factor : NonTerminatedSyntaxNode
|
||||||
else if (Children.Count == 4)
|
else if (Children.Count == 4)
|
||||||
{
|
{
|
||||||
// factor -> id ( expressionList)
|
// factor -> id ( expressionList)
|
||||||
OnProcedureCallGenerator?.Invoke(this, new ProcedureCallGeneratorEventArgs
|
ProcedureCallGeneratorEventArgs eventArgs = new()
|
||||||
{
|
{
|
||||||
ProcedureName = Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
|
ProcedureName =
|
||||||
Parameters = Children[2].Convert<ExpressionList>()
|
Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>(),
|
||||||
});
|
};
|
||||||
|
eventArgs.Parameters.AddRange(Children[2].Convert<ExpressionList>().Expressions);
|
||||||
|
OnProcedureCallGenerator?.Invoke(this, eventArgs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,6 +66,11 @@ public class IdentifierList : NonTerminatedSyntaxNode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsProcedure { get; set; }
|
public bool IsProcedure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为变量定义中使用
|
||||||
|
/// </summary>
|
||||||
|
public bool IsVariableDefinition { get; set; }
|
||||||
|
|
||||||
public event EventHandler<IdentifierGeneratorEventArgs>? OnIdentifierGenerator;
|
public event EventHandler<IdentifierGeneratorEventArgs>? OnIdentifierGenerator;
|
||||||
|
|
||||||
public event EventHandler<TypeGeneratorEventArgs>? OnTypeGenerator;
|
public event EventHandler<TypeGeneratorEventArgs>? OnTypeGenerator;
|
||||||
|
|
|
@ -23,6 +23,11 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<int> LeftBounds = new();
|
public List<int> LeftBounds = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 索引中的表达式
|
||||||
|
/// </summary>
|
||||||
|
public List<Expression> Expressions { get; } = [];
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
@ -42,7 +47,16 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode
|
||||||
|
|
||||||
public static IdentifierVarPart Create(List<SyntaxNodeBase> children)
|
public static IdentifierVarPart Create(List<SyntaxNodeBase> children)
|
||||||
{
|
{
|
||||||
return new IdentifierVarPart { Children = children };
|
IdentifierVarPart result = new() { Children = children };
|
||||||
|
|
||||||
|
if (children.Count == 3)
|
||||||
|
{
|
||||||
|
ExpressionList expressionList = children[1].Convert<ExpressionList>();
|
||||||
|
|
||||||
|
result.Expressions.AddRange(expressionList.Expressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RaiseEvent()
|
private void RaiseEvent()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using Canon.Core.Abstractions;
|
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.SemanticParser;
|
||||||
|
|
||||||
namespace Canon.Core.SyntaxNodes;
|
namespace Canon.Core.SyntaxNodes;
|
||||||
|
|
||||||
|
@ -12,6 +12,44 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
|
||||||
|
|
||||||
public required List<SyntaxNodeBase> Children { get; init; }
|
public required List<SyntaxNodeBase> Children { get; init; }
|
||||||
|
|
||||||
|
private PascalType? _variableType;
|
||||||
|
|
||||||
|
private string? _variableName;
|
||||||
|
|
||||||
|
public PascalType VariableType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_variableType is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Did not set the type of the node");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _variableType;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_variableType = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string VariableName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_variableName is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Did not set the name of the node");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _variableName;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_variableName = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerator<SyntaxNodeBase> GetEnumerator()
|
public IEnumerator<SyntaxNodeBase> GetEnumerator()
|
||||||
{
|
{
|
||||||
yield return this;
|
yield return this;
|
||||||
|
|
|
@ -16,9 +16,17 @@ public class ProcedureCall : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.ProcedureCall;
|
public override NonTerminatorType Type => NonTerminatorType.ProcedureCall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用函数的名称
|
||||||
|
/// </summary>
|
||||||
public IdentifierSemanticToken ProcedureId
|
public IdentifierSemanticToken ProcedureId
|
||||||
=> Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>();
|
=> Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用函数的参数
|
||||||
|
/// </summary>
|
||||||
|
public List<Expression> Parameters { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调用函数时含有参数的事件
|
/// 调用函数时含有参数的事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -62,7 +70,14 @@ public class ProcedureCall : NonTerminatedSyntaxNode
|
||||||
|
|
||||||
public static ProcedureCall Create(List<SyntaxNodeBase> children)
|
public static ProcedureCall Create(List<SyntaxNodeBase> children)
|
||||||
{
|
{
|
||||||
return new ProcedureCall { Children = children };
|
ProcedureCall result = new() { Children = children };
|
||||||
|
|
||||||
|
if (children.Count == 4)
|
||||||
|
{
|
||||||
|
result.Parameters.AddRange(children[2].Convert<ExpressionList>().Expressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RaiseEvent()
|
private void RaiseEvent()
|
||||||
|
|
|
@ -9,28 +9,6 @@ public class Variable : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.Variable;
|
public override NonTerminatorType Type => NonTerminatorType.Variable;
|
||||||
|
|
||||||
private PascalType? _variableType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Variable实际的类型,用于数组赋值
|
|
||||||
/// </summary>
|
|
||||||
public PascalType VariableType
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_variableType is null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _variableType;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_variableType = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 变量的名称
|
/// 变量的名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -28,7 +28,6 @@ builder.Services.AddSingleton<IGrammarParser>(
|
||||||
_ => GeneratedGrammarParser.Instance);
|
_ => GeneratedGrammarParser.Instance);
|
||||||
builder.Services.AddSingleton<SyntaxTreePresentationService>();
|
builder.Services.AddSingleton<SyntaxTreePresentationService>();
|
||||||
builder.Services.AddSingleton<SyntaxTreeTraveller>();
|
builder.Services.AddSingleton<SyntaxTreeTraveller>();
|
||||||
builder.Services.AddTransient<CCodeGenerateVisitor>();
|
|
||||||
builder.Services.AddTransient<CompilerService>();
|
builder.Services.AddTransient<CompilerService>();
|
||||||
builder.Services.AddHostedService<DatabaseSetupService>();
|
builder.Services.AddHostedService<DatabaseSetupService>();
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ public class CompilerService(
|
||||||
ILexer lexer,
|
ILexer lexer,
|
||||||
IGrammarParser grammarParser,
|
IGrammarParser grammarParser,
|
||||||
SyntaxTreeTraveller traveller,
|
SyntaxTreeTraveller traveller,
|
||||||
CCodeGenerateVisitor visitor,
|
|
||||||
ICompilerLogger compilerLogger,
|
ICompilerLogger compilerLogger,
|
||||||
CompileDbContext dbContext,
|
CompileDbContext dbContext,
|
||||||
GridFsService gridFsService,
|
GridFsService gridFsService,
|
||||||
|
@ -41,6 +40,7 @@ public class CompilerService(
|
||||||
await using Stream imageStream = syntaxTreePresentationService.Present(root);
|
await using Stream imageStream = syntaxTreePresentationService.Present(root);
|
||||||
string filename = await gridFsService.UploadStream(imageStream, "image/png");
|
string filename = await gridFsService.UploadStream(imageStream, "image/png");
|
||||||
|
|
||||||
|
CodeGeneratorVisitor visitor = new();
|
||||||
traveller.Travel(root, visitor);
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
CompileResult result = new()
|
CompileResult result = new()
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class BasicTest
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public BasicTest(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ProgramStructTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint main()\n{\n;\n\nreturn 0;\n}\n",
|
|
||||||
visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class DeclarationTests
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public DeclarationTests(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ConstDeclarationTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
const a = 'a'; b = 200; c = 3.14; d = 'm';
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nconst char a = 'a';\nconst " +
|
|
||||||
"int b = 200;\nconst double c = 3.14;\nconst char d = 'm';\nint main()\n{\n;\n\nreturn 0;\n}\n",
|
|
||||||
visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void VarDeclarationTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b, c:array[3..6, 4..999, 0..7, 8..80] of real;
|
|
||||||
d, e, f:integer;
|
|
||||||
g, h:array [6..8] of boolean;
|
|
||||||
i, j:char;
|
|
||||||
m, n:integer;
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\ndouble c[4][996][8][73]," +
|
|
||||||
" b[4][996][8][73], a[4][996][8][73];\nint f, e, d;\nbool h[3], g[3];\nchar j, i;" +
|
|
||||||
"\nint n, m;\nint main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class ExpressionTests
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public ExpressionTests(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ExpressionTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b:integer; flag, tag:boolean;
|
|
||||||
begin
|
|
||||||
a := 1;
|
|
||||||
b := a + b * 1 / 1 - 1 div 1 - - 2;
|
|
||||||
tag := flag or tag;
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint b, a;\n" +
|
|
||||||
"bool tag, flag;\nint main()\n{\na = 1;\nb = a + b * 1 /(double)1 - 1 / 1 - (-2);" +
|
|
||||||
"\ntag = flag || tag;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ArrayTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a: array[9..12, 3..5, 6..20] of real; b: array[5..10] of integer;
|
|
||||||
begin
|
|
||||||
a[9, 4, 20] := 3.6 + b[6] - a[12, 5, 6];
|
|
||||||
b[5] := 250;
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\ndouble a[4][3][15];" +
|
|
||||||
"\nint b[6];\nint main()\n{\na[9-9][4-3][20-6] = 3.6 + b[6-5] - a[12-9][5-3][6-6];" +
|
|
||||||
"\nb[5-5] = 250;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class ReadTest
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public ReadTest(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SimpleReadTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b:integer;
|
|
||||||
begin
|
|
||||||
read(a);
|
|
||||||
write(b + 1);
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint b, a;\n" +
|
|
||||||
"bool tag, flag;\nint main()\n{\na = 1;\nb = a + b * 1 /(double)1 - 1 / 1 - (-2);" +
|
|
||||||
"\ntag = flag || tag;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class StatementTests
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public StatementTests(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void IfTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a:integer;
|
|
||||||
begin
|
|
||||||
a := 1;
|
|
||||||
if a = 1 then
|
|
||||||
a := 1
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
if a = 2 + a then
|
|
||||||
a := a
|
|
||||||
else a := 999;
|
|
||||||
end;
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint a;\nint main()\n" +
|
|
||||||
"{\na = 1;\nif(a == 1)\na = 1;\nelse\n{\nif(a == 2 + a)\n" +
|
|
||||||
"a = a;\nelse\na = 999;\n;\n;\n}\n;\n;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ForLoopTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b, c:integer;
|
|
||||||
begin
|
|
||||||
b := 5;
|
|
||||||
c := 6;
|
|
||||||
for a := 1 to 60 do
|
|
||||||
begin
|
|
||||||
for b := a + c to 5 * a do
|
|
||||||
begin
|
|
||||||
c := 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\n" +
|
|
||||||
"int c, b, a;\nint main()\n" +
|
|
||||||
"{\nb = 5;\nc = 6;\nfor(a = 1; a <= 60; a++){\n" +
|
|
||||||
"for(b = a + c; b <= 5 * a; b++)" +
|
|
||||||
"{\nc = 1;\n;\n}\n;\n;\n;\n}\n;\n;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ProcedureCallTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b:integer; c:real;
|
|
||||||
|
|
||||||
function test1(var a1:integer; b1:integer; c1:real):integer;
|
|
||||||
var i, j, k:integer;
|
|
||||||
begin
|
|
||||||
a1:= 10086;
|
|
||||||
b1 := 2;
|
|
||||||
c1 := 63;
|
|
||||||
test1 := test1(i, j, k);
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
|
||||||
test1(a, b, c);
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint b, a;\ndouble c;" +
|
|
||||||
"\nint test1(int* a1, int b1, double c1)\n{" +
|
|
||||||
"\nint test1;\nint k, j, i;\n" +
|
|
||||||
"{\n(*a1) = 10086;\nb1 = 2;\nc1 = 63;\n" +
|
|
||||||
"test1 = test1(&i, j, k);\n;\n}\nreturn test1;\n}\n" +
|
|
||||||
"int main()\n{\ntest1(&a, b, c);\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
using Canon.Core.SemanticParser;
|
|
||||||
using Canon.Core.SyntaxNodes;
|
|
||||||
using Canon.Tests.Utils;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Canon.Tests.CodeGeneratorTests;
|
|
||||||
|
|
||||||
public class SubprogramTests
|
|
||||||
{
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
public SubprogramTests(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ProcedureDeclarationTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
const PI = 3.1415;
|
|
||||||
procedure test1;
|
|
||||||
var ch:char;
|
|
||||||
begin
|
|
||||||
end;
|
|
||||||
procedure test2;
|
|
||||||
var i, j:integer;
|
|
||||||
begin
|
|
||||||
end;
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nconst double pi = 3.1415;\n" +
|
|
||||||
"void test1()\n{\nchar ch;\n{\n;\n}\n\n}\n" +
|
|
||||||
"void test2()\n{\nint j, i;\n{\n;\n}\n\n}\n" +
|
|
||||||
"int main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FunctionDeclarationTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
var a, b: boolean;
|
|
||||||
function func1(var a:integer; b:integer; c:real):integer;
|
|
||||||
begin
|
|
||||||
a := b + c;
|
|
||||||
func1 := a * 3;
|
|
||||||
end;
|
|
||||||
function func2(var a, b:boolean; c: array[0..6,3..8] of char):char;
|
|
||||||
begin
|
|
||||||
a := b and not b;
|
|
||||||
func2 := c[5,8];
|
|
||||||
end;
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
SyntaxTreeTraveller traveller = new();
|
|
||||||
CCodeGenerateVisitor visitor = new();
|
|
||||||
traveller.Travel(root, visitor);
|
|
||||||
|
|
||||||
string result = visitor.Builder.Build();
|
|
||||||
_output.WriteLine(result);
|
|
||||||
Assert.Equal("""
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
bool b, a;
|
|
||||||
int func1(int* a, int b, double c)
|
|
||||||
{
|
|
||||||
int func1;
|
|
||||||
{
|
|
||||||
(*a) = b + c;
|
|
||||||
func1 = (*a) * 3;
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return func1;
|
|
||||||
}
|
|
||||||
char func2(bool* a, bool* b, char c[][6])
|
|
||||||
{
|
|
||||||
char func2;
|
|
||||||
{
|
|
||||||
(*a) = (*b) && (~(*b));
|
|
||||||
func2 = c[5-0][8-3];
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return func2;
|
|
||||||
}
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
""", visitor.Builder.Build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,14 +36,14 @@ public class PascalTypeTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PascalFunctionTypeTests()
|
public void PascalFunctionTypeTests()
|
||||||
{
|
{
|
||||||
PascalType function1 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)],
|
PascalType function1 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false, "a")],
|
||||||
PascalBasicType.Void);
|
PascalBasicType.Void);
|
||||||
PascalType function2 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)],
|
PascalType function2 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false, "a")],
|
||||||
PascalBasicType.Void);
|
PascalBasicType.Void);
|
||||||
|
|
||||||
Assert.Equal(function1, function2);
|
Assert.Equal(function1, function2);
|
||||||
|
|
||||||
PascalType function3 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Real, true)],
|
PascalType function3 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Real, true, "a")],
|
||||||
PascalBasicType.Integer);
|
PascalBasicType.Integer);
|
||||||
Assert.NotEqual(function1, function3);
|
Assert.NotEqual(function1, function3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,9 +126,9 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Integer, false)
|
new PascalParameterType(PascalBasicType.Integer, false, "a")
|
||||||
], PascalBasicType.Void));
|
], PascalBasicType.Void));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Real, true),
|
new PascalParameterType(PascalBasicType.Real, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Real, true),
|
new PascalParameterType(PascalBasicType.Real, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Real, true)
|
new PascalParameterType(PascalBasicType.Real, true, "a")
|
||||||
], PascalBasicType.Void));
|
], PascalBasicType.Void));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,10 +170,10 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Character, true),
|
new PascalParameterType(PascalBasicType.Character, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Character, true)
|
new PascalParameterType(PascalBasicType.Character, true, "a")
|
||||||
], PascalBasicType.Void));
|
], PascalBasicType.Void));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,10 +193,10 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Character, true),
|
new PascalParameterType(PascalBasicType.Character, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Character, true)
|
new PascalParameterType(PascalBasicType.Character, true, "a")
|
||||||
], PascalBasicType.Real));
|
], PascalBasicType.Real));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,16 +219,16 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test1", out Symbol? symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test1", out Symbol? symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Integer, false),
|
new PascalParameterType(PascalBasicType.Integer, false, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Real, true),
|
new PascalParameterType(PascalBasicType.Real, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Real, true),
|
new PascalParameterType(PascalBasicType.Real, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Boolean, false)
|
new PascalParameterType(PascalBasicType.Boolean, false, "a")
|
||||||
], PascalBasicType.Void));
|
], PascalBasicType.Void));
|
||||||
|
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test2", out symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test2", out symbol));
|
||||||
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
Assert.Equal(symbol.SymbolType, new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Boolean, true),
|
new PascalParameterType(PascalBasicType.Boolean, true, "a"),
|
||||||
new PascalParameterType(PascalBasicType.Boolean, true)
|
new PascalParameterType(PascalBasicType.Boolean, true, "a")
|
||||||
], PascalBasicType.Boolean));
|
], PascalBasicType.Boolean));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out symbol));
|
Assert.True(visitor.SymbolTable.TryGetSymbol("test", out symbol));
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
new PascalFunctionType([
|
new PascalFunctionType([
|
||||||
new PascalParameterType(PascalBasicType.Boolean, false)
|
new PascalParameterType(PascalBasicType.Boolean, false, "a")
|
||||||
], PascalBasicType.Void), symbol.SymbolType);
|
], PascalBasicType.Void), symbol.SymbolType);
|
||||||
|
|
||||||
Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol));
|
Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user