refact: 重构代码生成 (#72)

Reviewed-on: PostGuard/Canon#72
This commit is contained in:
jackfiled 2024-05-04 11:56:06 +08:00
parent 6130adfa7c
commit 8da24523c9
25 changed files with 1121 additions and 538 deletions

View File

@ -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());

View File

@ -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()
{ {

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

View File

@ -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
{ {

View File

@ -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

View File

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

View File

@ -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)
{ {

View File

@ -72,6 +72,8 @@ public class ConstValue : NonTerminatedSyntaxNode
} }
} }
public string ValueString { get; set; } = string.Empty;
/// <summary> /// <summary>
/// 使用数值产生式的事件 /// 使用数值产生式的事件
/// </summary> /// </summary>

View File

@ -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

View File

@ -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
{ {

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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()

View File

@ -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>

View File

@ -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>();

View File

@ -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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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