From 8da24523c98792972cb01782f5e85eda0bfc8766 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 4 May 2024 11:56:06 +0800 Subject: [PATCH] =?UTF-8?q?refact:=20=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.rrricardo.top/PostGuard/Canon/pulls/72 --- Canon.Console/Services/Compiler.cs | 2 +- Canon.Core/CodeGenerators/CCodeBuilder.cs | 57 +- .../SemanticParser/CodeGeneratorVisitor.cs | 884 ++++++++++++++++++ .../SemanticParser/PascalFunctionType.cs | 2 +- .../SemanticParser/PascalParameterType.cs | 4 +- Canon.Core/SemanticParser/TypeCheckVisitor.cs | 86 +- Canon.Core/SyntaxNodes/CompoundStatement.cs | 2 +- Canon.Core/SyntaxNodes/ConstValue.cs | 2 + Canon.Core/SyntaxNodes/Expression.cs | 13 + Canon.Core/SyntaxNodes/Factor.cs | 24 +- Canon.Core/SyntaxNodes/IdentifierList.cs | 5 + Canon.Core/SyntaxNodes/IdentifierVarPart.cs | 16 +- .../SyntaxNodes/NonTerminatedSyntaxNode.cs | 40 +- Canon.Core/SyntaxNodes/ProcedureCall.cs | 17 +- Canon.Core/SyntaxNodes/Variable.cs | 22 - Canon.Server/Program.cs | 1 - Canon.Server/Services/CompilerService.cs | 2 +- Canon.Tests/CodeGeneratorTests/BasicTest.cs | 36 - .../CodeGeneratorTests/DeclarationTests.cs | 64 -- .../CodeGeneratorTests/ExpressionTests.cs | 66 -- Canon.Tests/CodeGeneratorTests/ReadTest.cs | 40 - .../CodeGeneratorTests/StatementTests.cs | 118 --- .../CodeGeneratorTests/SubprogramTests.cs | 108 --- Canon.Tests/SemanticTests/PascalTypeTests.cs | 6 +- .../SemanticTests/TypeCheckVisitorTests.cs | 42 +- 25 files changed, 1121 insertions(+), 538 deletions(-) create mode 100644 Canon.Core/SemanticParser/CodeGeneratorVisitor.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/BasicTest.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/DeclarationTests.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/ExpressionTests.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/ReadTest.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/StatementTests.cs delete mode 100644 Canon.Tests/CodeGeneratorTests/SubprogramTests.cs diff --git a/Canon.Console/Services/Compiler.cs b/Canon.Console/Services/Compiler.cs index 2c3138e..2681824 100644 --- a/Canon.Console/Services/Compiler.cs +++ b/Canon.Console/Services/Compiler.cs @@ -21,7 +21,7 @@ public class Compiler( IEnumerable tokens = lexer.Tokenize(await CreateSourceReader()); ProgramStruct root = grammarParser.Analyse(tokens); - CCodeGenerateVisitor visitor = new(); + CodeGeneratorVisitor visitor = new(); traveller.Travel(root, visitor); await WriteToOutputFile(visitor.Builder.Build()); diff --git a/Canon.Core/CodeGenerators/CCodeBuilder.cs b/Canon.Core/CodeGenerators/CCodeBuilder.cs index bc6e652..347bf6d 100644 --- a/Canon.Core/CodeGenerators/CCodeBuilder.cs +++ b/Canon.Core/CodeGenerators/CCodeBuilder.cs @@ -10,16 +10,65 @@ public class CCodeBuilder { private readonly StringBuilder _builder = new(); - /// - /// 符号表 - /// - public SymbolTable SymbolTable { get; } = new(); + private int _scopeCount = 0; + private string _scopeEmpty = string.Empty; public void AddString(string 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'); + } + } + + /// + /// 开始一段代码块 + /// + public void BeginScope() + { + _builder.Append(_scopeEmpty).Append("{\n"); + + _scopeCount += 1; + string scopeEmpty = string.Empty; + + for (int i = 0; i < _scopeCount; i++) + { + scopeEmpty += " "; + } + + _scopeEmpty = scopeEmpty; + } + + /// + /// 结束一段代码块 + /// + 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() { diff --git a/Canon.Core/SemanticParser/CodeGeneratorVisitor.cs b/Canon.Core/SemanticParser/CodeGeneratorVisitor.cs new file mode 100644 index 0000000..75961b9 --- /dev/null +++ b/Canon.Core/SemanticParser/CodeGeneratorVisitor.cs @@ -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 "); + Builder.AddLine("#include "); + } + + 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(); + 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); + } + } + + + /// + /// 存储IF语句中条件变量的名称 + /// + private readonly Stack _ifConditionNames = new(); + + /// + /// IF语句中成功分支的标签 + /// + private readonly Stack _ifTrueLabels = new(); + + /// + /// IF语句中失败分支的标签 + /// + private readonly Stack _ifFalseLabels = new(); + + /// + /// IF语句中结束的标签 + /// + private readonly Stack _ifEndLabels = new(); + + /// + /// FOR语句中的循环变量名称 + /// + private readonly Stack _forVariables = new(); + + /// + /// FOR语句中的循环变量的初始值 + /// + private readonly Stack _forBeginConditions = new(); + + /// + /// FOR语句中循环变量的判断值 + /// + private readonly Stack _forEndConditions = new(); + + /// + /// FOR语句开始的标签 + /// + private readonly Stack _forLabels = new(); + + /// + /// FOR语句条件判断部分的标签 + /// + private readonly Stack _forConditionLabels = new(); + + /// + /// FOR语句结束的标签 + /// + private readonly Stack _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(); + + 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(); + + 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().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 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 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().OperatorType; + if (operatorType == OperatorType.Multiply) + { + return "*"; + } + else if (operatorType == OperatorType.Divide) + { + //实数除法,需要将操作数强转为double + return "/(double)"; + } + } + else + { + KeywordType keywordType = multiplyOperator.OperatorToken.Convert().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().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().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; + + /// + /// 产生一个全局唯一的临时变量名 + /// + private string GenerateTemporaryVariable() + { + string name = $"__temp_{_temporaryVariableCount}"; + _temporaryVariableCount += 1; + return name; + } + + private long _labelCount; + + /// + /// 产生一对IF语句中的标签 + /// + private void GenerateIfLabel() + { + _ifTrueLabels.Push($"if_true_{_labelCount}"); + _ifFalseLabels.Push($"if_false_{_labelCount}"); + _ifEndLabels.Push($"_if_end_{_labelCount}"); + + _labelCount += 1; + } + + /// + /// 产生FOR语句中的标签 + /// + private void GenerateForLabel() + { + _forLabels.Push($"for_{_labelCount}"); + _forConditionLabels.Push($"for_condition_{_labelCount}"); + _forEndLabels.Push($"for_end_{_labelCount}"); + + _labelCount += 1; + } +} diff --git a/Canon.Core/SemanticParser/PascalFunctionType.cs b/Canon.Core/SemanticParser/PascalFunctionType.cs index 761ed6f..5d8719e 100644 --- a/Canon.Core/SemanticParser/PascalFunctionType.cs +++ b/Canon.Core/SemanticParser/PascalFunctionType.cs @@ -11,7 +11,7 @@ public class PascalFunctionType(List parameters, PascalType /// /// Pascal核心库函数的类型 /// - public static PascalFunctionType CoreFuntionType => new PascalFunctionType([], PascalBasicType.Void); + public static PascalFunctionType CoreFuntionType => new([], PascalBasicType.Void); public override string TypeName { diff --git a/Canon.Core/SemanticParser/PascalParameterType.cs b/Canon.Core/SemanticParser/PascalParameterType.cs index d8fb7ce..4826945 100644 --- a/Canon.Core/SemanticParser/PascalParameterType.cs +++ b/Canon.Core/SemanticParser/PascalParameterType.cs @@ -1,11 +1,13 @@ 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 bool IsVar { get; } = isVar; + public string ParameterName { get; } = parameterName; + public override string TypeName { get diff --git a/Canon.Core/SemanticParser/TypeCheckVisitor.cs b/Canon.Core/SemanticParser/TypeCheckVisitor.cs index 9762ffd..e5693c8 100644 --- a/Canon.Core/SemanticParser/TypeCheckVisitor.cs +++ b/Canon.Core/SemanticParser/TypeCheckVisitor.cs @@ -88,50 +88,46 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito { case NumberType.Integer: factor.FactorType = PascalBasicType.Integer; + factor.VariableType = PascalBasicType.Integer; break; case NumberType.Real: factor.FactorType = PascalBasicType.Real; + factor.VariableType = PascalBasicType.Real; break; } }; // factor -> true | false - factor.OnBooleanGenerator += (_, _) => { factor.FactorType = PascalBasicType.Boolean; }; + factor.OnBooleanGenerator += (_, _) => + { + factor.FactorType = PascalBasicType.Boolean; + factor.VariableType = PascalBasicType.Boolean; + }; // 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.OnParethnesisGenerator += (_, e) => { factor.FactorType = e.Expression.ExpressionType; }; - - // factor -> id () - factor.OnNoParameterProcedureCallGenerator += (_, e) => + factor.OnParethnesisGenerator += (_, e) => { - if (ValidateProcedureCall(e.ProcedureName, [], out PascalFunctionType? functionType)) - { - 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.FactorType = e.Expression.ExpressionType; + factor.VariableType = e.Expression.VariableType; }; // factor -> id ( ExpressionList) 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) { factor.FactorType = functionType.ReturnType; + factor.VariableType = functionType.ReturnType; return; } else @@ -142,29 +138,47 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito } factor.FactorType = PascalBasicType.Void; + factor.VariableType = PascalBasicType.Void; }; // 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.OnUminusGenerator += (_, e) => { factor.FactorType = e.Factor.FactorType; }; + factor.OnUminusGenerator += (_, e) => + { + factor.FactorType = e.Factor.FactorType; + factor.VariableType = e.Factor.VariableType; + }; // 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) { 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) => { if (PascalType.IsCalculatable(e.Left.TermType) && PascalType.IsCalculatable(e.Right.FactorType)) { term.TermType = e.Left.TermType + e.Right.FactorType; + term.VariableType = e.Left.TermType + e.Right.FactorType; return; } @@ -177,13 +191,18 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito { 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) => { if (PascalType.IsCalculatable(e.Left.SimpleExpressionType) && PascalType.IsCalculatable(e.Right.TermType)) { simpleExpression.SimpleExpressionType = e.Left.SimpleExpressionType + e.Right.TermType; + simpleExpression.VariableType = e.Left.VariableType + e.Right.VariableType; return; } @@ -199,9 +218,14 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito expression.OnSimpleExpressionGenerator += (_, e) => { 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) @@ -334,7 +358,7 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito { 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); } - if (e.Variable.VariableType != e.Expression.ExpressionType) + if (e.Variable.VariableType != e.Expression.VariableType) { IsError = true; logger?.LogError("Variable '{}' type mismatch, expect '{}' but '{}'.", e.Variable.Identifier.IdentifierName, e.Variable.VariableType.ToString(), - e.Expression.ExpressionType.ToString()); + e.Expression.VariableType.ToString()); } }; diff --git a/Canon.Core/SyntaxNodes/CompoundStatement.cs b/Canon.Core/SyntaxNodes/CompoundStatement.cs index 8808f2a..bc2b074 100644 --- a/Canon.Core/SyntaxNodes/CompoundStatement.cs +++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs @@ -10,7 +10,7 @@ public class CompoundStatement : NonTerminatedSyntaxNode /// /// 是否为主函数部分 /// - public bool IsMain; + public bool IsMain { get; set; } public override void PreVisit(SyntaxNodeVisitor visitor) { diff --git a/Canon.Core/SyntaxNodes/ConstValue.cs b/Canon.Core/SyntaxNodes/ConstValue.cs index 26c7013..5d58fad 100644 --- a/Canon.Core/SyntaxNodes/ConstValue.cs +++ b/Canon.Core/SyntaxNodes/ConstValue.cs @@ -72,6 +72,8 @@ public class ConstValue : NonTerminatedSyntaxNode } } + public string ValueString { get; set; } = string.Empty; + /// /// 使用数值产生式的事件 /// diff --git a/Canon.Core/SyntaxNodes/Expression.cs b/Canon.Core/SyntaxNodes/Expression.cs index 037ff7e..95c727d 100644 --- a/Canon.Core/SyntaxNodes/Expression.cs +++ b/Canon.Core/SyntaxNodes/Expression.cs @@ -50,10 +50,23 @@ public class Expression : NonTerminatedSyntaxNode /// 当前表达式对应的数组下标维度的左边界 /// public int LeftBound; + + /// + /// 是否为FOR语句中的起始语句 + /// public bool IsForConditionBegin { get; set; } + + /// + /// 是否为FOR语句中的结束语句 + /// public bool IsForConditionEnd { get; set; } public bool IsAssign { get; set; } + /// + /// 是否为IF语句中的条件语句 + /// + public bool IsIfCondition { get; set; } + private IdentifierSemanticToken? _iterator; public IdentifierSemanticToken Iterator diff --git a/Canon.Core/SyntaxNodes/Factor.cs b/Canon.Core/SyntaxNodes/Factor.cs index 263ec46..39ccb0e 100644 --- a/Canon.Core/SyntaxNodes/Factor.cs +++ b/Canon.Core/SyntaxNodes/Factor.cs @@ -20,16 +20,11 @@ public class ParethnesisGeneratorEventArgs : EventArgs public required Expression Expression { get; init; } } -public class NoParameterProcedureCallGeneratorEventArgs : EventArgs -{ - public required IdentifierSemanticToken ProcedureName { get; init; } -} - public class ProcedureCallGeneratorEventArgs : EventArgs { public required IdentifierSemanticToken ProcedureName { get; init; } - public required ExpressionList Parameters { get; init; } + public List Parameters { get; } = []; } public class NotGeneratorEventArgs : EventArgs @@ -103,11 +98,6 @@ public class Factor : NonTerminatedSyntaxNode /// public event EventHandler? OnBooleanGenerator; - /// - /// 没有参数的过程调用产生式事件 - /// - public event EventHandler? OnNoParameterProcedureCallGenerator; - /// /// 过程调用产生式的事件 /// @@ -185,7 +175,7 @@ public class Factor : NonTerminatedSyntaxNode else { // factor -> id ( ) - OnNoParameterProcedureCallGenerator?.Invoke(this, new NoParameterProcedureCallGeneratorEventArgs + OnProcedureCallGenerator?.Invoke(this, new ProcedureCallGeneratorEventArgs { ProcedureName = terminatedSyntaxNode.Token.Convert() }); @@ -194,11 +184,13 @@ public class Factor : NonTerminatedSyntaxNode else if (Children.Count == 4) { // factor -> id ( expressionList) - OnProcedureCallGenerator?.Invoke(this, new ProcedureCallGeneratorEventArgs + ProcedureCallGeneratorEventArgs eventArgs = new() { - ProcedureName = Children[0].Convert().Token.Convert(), - Parameters = Children[2].Convert() - }); + ProcedureName = + Children[0].Convert().Token.Convert(), + }; + eventArgs.Parameters.AddRange(Children[2].Convert().Expressions); + OnProcedureCallGenerator?.Invoke(this, eventArgs); } else { diff --git a/Canon.Core/SyntaxNodes/IdentifierList.cs b/Canon.Core/SyntaxNodes/IdentifierList.cs index 6fc3fd9..d9bf382 100644 --- a/Canon.Core/SyntaxNodes/IdentifierList.cs +++ b/Canon.Core/SyntaxNodes/IdentifierList.cs @@ -66,6 +66,11 @@ public class IdentifierList : NonTerminatedSyntaxNode /// public bool IsProcedure { get; set; } + /// + /// 是否为变量定义中使用 + /// + public bool IsVariableDefinition { get; set; } + public event EventHandler? OnIdentifierGenerator; public event EventHandler? OnTypeGenerator; diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs index 22f579f..e48aa79 100644 --- a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs +++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs @@ -23,6 +23,11 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode /// public List LeftBounds = new(); + /// + /// 索引中的表达式 + /// + public List Expressions { get; } = []; + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -42,7 +47,16 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode public static IdentifierVarPart Create(List children) { - return new IdentifierVarPart { Children = children }; + IdentifierVarPart result = new() { Children = children }; + + if (children.Count == 3) + { + ExpressionList expressionList = children[1].Convert(); + + result.Expressions.AddRange(expressionList.Expressions); + } + + return result; } private void RaiseEvent() diff --git a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs index cc8f5b3..a3fc7aa 100644 --- a/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/NonTerminatedSyntaxNode.cs @@ -1,6 +1,6 @@ using System.Collections; -using Canon.Core.Abstractions; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -12,6 +12,44 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable 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 GetEnumerator() { yield return this; diff --git a/Canon.Core/SyntaxNodes/ProcedureCall.cs b/Canon.Core/SyntaxNodes/ProcedureCall.cs index 6106560..89b1652 100644 --- a/Canon.Core/SyntaxNodes/ProcedureCall.cs +++ b/Canon.Core/SyntaxNodes/ProcedureCall.cs @@ -16,9 +16,17 @@ public class ProcedureCall : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.ProcedureCall; + /// + /// 调用函数的名称 + /// public IdentifierSemanticToken ProcedureId => Children[0].Convert().Token.Convert(); + /// + /// 调用函数的参数 + /// + public List Parameters { get; } = []; + /// /// 调用函数时含有参数的事件 /// @@ -62,7 +70,14 @@ public class ProcedureCall : NonTerminatedSyntaxNode public static ProcedureCall Create(List children) { - return new ProcedureCall { Children = children }; + ProcedureCall result = new() { Children = children }; + + if (children.Count == 4) + { + result.Parameters.AddRange(children[2].Convert().Expressions); + } + + return result; } private void RaiseEvent() diff --git a/Canon.Core/SyntaxNodes/Variable.cs b/Canon.Core/SyntaxNodes/Variable.cs index 6aea1aa..7e58364 100644 --- a/Canon.Core/SyntaxNodes/Variable.cs +++ b/Canon.Core/SyntaxNodes/Variable.cs @@ -9,28 +9,6 @@ public class Variable : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.Variable; - private PascalType? _variableType; - - /// - /// Variable实际的类型,用于数组赋值 - /// - public PascalType VariableType - { - get - { - if (_variableType is null) - { - throw new InvalidOperationException(); - } - - return _variableType; - } - set - { - _variableType = value; - } - } - /// /// 变量的名称 /// diff --git a/Canon.Server/Program.cs b/Canon.Server/Program.cs index 92d9832..c90bb18 100644 --- a/Canon.Server/Program.cs +++ b/Canon.Server/Program.cs @@ -28,7 +28,6 @@ builder.Services.AddSingleton( _ => GeneratedGrammarParser.Instance); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddHostedService(); diff --git a/Canon.Server/Services/CompilerService.cs b/Canon.Server/Services/CompilerService.cs index 0cf48c5..dbb5de9 100644 --- a/Canon.Server/Services/CompilerService.cs +++ b/Canon.Server/Services/CompilerService.cs @@ -13,7 +13,6 @@ public class CompilerService( ILexer lexer, IGrammarParser grammarParser, SyntaxTreeTraveller traveller, - CCodeGenerateVisitor visitor, ICompilerLogger compilerLogger, CompileDbContext dbContext, GridFsService gridFsService, @@ -41,6 +40,7 @@ public class CompilerService( await using Stream imageStream = syntaxTreePresentationService.Present(root); string filename = await gridFsService.UploadStream(imageStream, "image/png"); + CodeGeneratorVisitor visitor = new(); traveller.Travel(root, visitor); CompileResult result = new() diff --git a/Canon.Tests/CodeGeneratorTests/BasicTest.cs b/Canon.Tests/CodeGeneratorTests/BasicTest.cs deleted file mode 100644 index 9fe7cb3..0000000 --- a/Canon.Tests/CodeGeneratorTests/BasicTest.cs +++ /dev/null @@ -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 \n#include \nint main()\n{\n;\n\nreturn 0;\n}\n", - visitor.Builder.Build()); - } -} diff --git a/Canon.Tests/CodeGeneratorTests/DeclarationTests.cs b/Canon.Tests/CodeGeneratorTests/DeclarationTests.cs deleted file mode 100644 index b735bdb..0000000 --- a/Canon.Tests/CodeGeneratorTests/DeclarationTests.cs +++ /dev/null @@ -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 \n#include \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 \n#include \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()); - } -} diff --git a/Canon.Tests/CodeGeneratorTests/ExpressionTests.cs b/Canon.Tests/CodeGeneratorTests/ExpressionTests.cs deleted file mode 100644 index 084a023..0000000 --- a/Canon.Tests/CodeGeneratorTests/ExpressionTests.cs +++ /dev/null @@ -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 \n#include \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 \n#include \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()); - } - -} diff --git a/Canon.Tests/CodeGeneratorTests/ReadTest.cs b/Canon.Tests/CodeGeneratorTests/ReadTest.cs deleted file mode 100644 index 87078ee..0000000 --- a/Canon.Tests/CodeGeneratorTests/ReadTest.cs +++ /dev/null @@ -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 \n#include \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()); - } -} diff --git a/Canon.Tests/CodeGeneratorTests/StatementTests.cs b/Canon.Tests/CodeGeneratorTests/StatementTests.cs deleted file mode 100644 index 88ce9d4..0000000 --- a/Canon.Tests/CodeGeneratorTests/StatementTests.cs +++ /dev/null @@ -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 \n#include \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 \n#include \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 \n#include \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()); - } -} diff --git a/Canon.Tests/CodeGeneratorTests/SubprogramTests.cs b/Canon.Tests/CodeGeneratorTests/SubprogramTests.cs deleted file mode 100644 index 26634a9..0000000 --- a/Canon.Tests/CodeGeneratorTests/SubprogramTests.cs +++ /dev/null @@ -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 \n#include \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 - #include - 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()); - } -} diff --git a/Canon.Tests/SemanticTests/PascalTypeTests.cs b/Canon.Tests/SemanticTests/PascalTypeTests.cs index ff07e64..c38371a 100644 --- a/Canon.Tests/SemanticTests/PascalTypeTests.cs +++ b/Canon.Tests/SemanticTests/PascalTypeTests.cs @@ -36,14 +36,14 @@ public class PascalTypeTests [Fact] public void PascalFunctionTypeTests() { - PascalType function1 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)], + PascalType function1 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false, "a")], PascalBasicType.Void); - PascalType function2 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false)], + PascalType function2 = new PascalFunctionType([new PascalParameterType(PascalBasicType.Integer, false, "a")], PascalBasicType.Void); 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); Assert.NotEqual(function1, function3); } diff --git a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs index da797d4..9afda2b 100644 --- a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs +++ b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs @@ -126,9 +126,9 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Integer, false) + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Integer, false, "a") ], PascalBasicType.Void)); } @@ -148,9 +148,9 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Real, true), - new PascalParameterType(PascalBasicType.Real, true), - new PascalParameterType(PascalBasicType.Real, true) + new PascalParameterType(PascalBasicType.Real, true, "a"), + new PascalParameterType(PascalBasicType.Real, true, "a"), + new PascalParameterType(PascalBasicType.Real, true, "a") ], PascalBasicType.Void)); } @@ -170,10 +170,10 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Character, true), - new PascalParameterType(PascalBasicType.Character, true) + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Character, true, "a"), + new PascalParameterType(PascalBasicType.Character, true, "a") ], PascalBasicType.Void)); } @@ -193,10 +193,10 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test", out Symbol? symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Character, true), - new PascalParameterType(PascalBasicType.Character, true) + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Character, true, "a"), + new PascalParameterType(PascalBasicType.Character, true, "a") ], PascalBasicType.Real)); } @@ -219,16 +219,16 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test1", out Symbol? symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Integer, false), - new PascalParameterType(PascalBasicType.Real, true), - new PascalParameterType(PascalBasicType.Real, true), - new PascalParameterType(PascalBasicType.Boolean, false) + new PascalParameterType(PascalBasicType.Integer, false, "a"), + new PascalParameterType(PascalBasicType.Real, true, "a"), + new PascalParameterType(PascalBasicType.Real, true, "a"), + new PascalParameterType(PascalBasicType.Boolean, false, "a") ], PascalBasicType.Void)); Assert.True(visitor.SymbolTable.TryGetSymbol("test2", out symbol)); Assert.Equal(symbol.SymbolType, new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Boolean, true), - new PascalParameterType(PascalBasicType.Boolean, true) + new PascalParameterType(PascalBasicType.Boolean, true, "a"), + new PascalParameterType(PascalBasicType.Boolean, true, "a") ], PascalBasicType.Boolean)); } @@ -290,7 +290,7 @@ public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) Assert.True(visitor.SymbolTable.TryGetSymbol("test", out symbol)); Assert.Equal( new PascalFunctionType([ - new PascalParameterType(PascalBasicType.Boolean, false) + new PascalParameterType(PascalBasicType.Boolean, false, "a") ], PascalBasicType.Void), symbol.SymbolType); Assert.False(visitor.SymbolTable.TryGetSymbol("d", out symbol));