diff --git a/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs b/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs index eb06da2..092e050 100644 --- a/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs +++ b/Canon.Core/SemanticParser/CCodeGenerateVisitor.cs @@ -1,10 +1,13 @@ -using Canon.Core.Abstractions; +using System.Globalization; using Canon.Core.CodeGenerators; +using Canon.Core.Enums; +using Canon.Core.LexicalParser; using Canon.Core.SyntaxNodes; +using BasicType = Canon.Core.SyntaxNodes.BasicType; namespace Canon.Core.SemanticParser; -public class CCodeGenerateVisitor(ICompilerLogger? logger = null) : TypeCheckVisitor(logger) +public class CCodeGenerateVisitor : TypeCheckVisitor { public CCodeBuilder Builder { get; } = new(); @@ -15,4 +18,624 @@ public class CCodeGenerateVisitor(ICompilerLogger? logger = null) : TypeCheckVis Builder.AddString("#include \n"); Builder.AddString("#include \n"); } + + private string? _constValue; + public override void PreVisit(ConstDeclaration constDeclaration) + { + base.PreVisit(constDeclaration); + (_, ConstValue constValue) = constDeclaration.ConstValue; + constValue.OnCharacterGenerator += (_, e) => + { + _constValue = "'" + e.Token.LiteralValue + "'"; + }; + constValue.OnNumberGenerator += (_, e) => + { + if (e.IsNegative) + { + _constValue = "-"; + } + + if (e.Token.NumberType == NumberType.Integer) + { + _constValue += e.Token.ParseAsInteger(); + } + else + { + _constValue += e.Token.ParseAsReal(); + } + }; + } + + public override void PostVisit(ConstDeclaration constDeclaration) + { + base.PostVisit(constDeclaration); + (IdentifierSemanticToken token, ConstValue constValue) = constDeclaration.ConstValue; + + string cTypeName = TryParseBasicType(constValue.ConstType); + Builder.AddString("const " + cTypeName + " " + token.IdentifierName + " = " + _constValue + ";\n"); + _constValue = ""; + } + + public override void PostVisit(VarDeclaration varDeclaration) + { + base.PostVisit(varDeclaration); + + string idName = varDeclaration.Token.IdentifierName; + var type = varDeclaration.IdentifierList.DefinitionType; + + if (type is PascalBasicType) + { + Builder.AddString(idName + ";\n"); + } + else + { + TryParseArrayType(type, out string _, out string periods); + Builder.AddString(idName + periods + ";\n"); + } + + } + + public override void PreVisit(IdentifierList identifierList) + { + base.PreVisit(identifierList); + identifierList.OnTypeGenerator += (_, e) => + { + e.TypeSyntaxNode.IsProcedure = identifierList.IsProcedure; + }; + } + + public override void PostVisit(IdentifierList identifierList) + { + base.PostVisit(identifierList); + identifierList.OnIdentifierGenerator += (_, e) => + { + if(!identifierList.IsProcedure) + { + string periods = ""; //如果是数组定义,需要每个标识符后带上 + if (e.IdentifierList.DefinitionType is PascalArrayType pascalArrayType) + { + TryParseArrayType(pascalArrayType, out string _, out string periods0); + periods = periods0; + } + + Builder.AddString(e.IdentifierToken.IdentifierName + periods + ", "); + } + }; + } + + + public override void PreVisit(TypeSyntaxNode typeSyntaxNode) + { + base.PreVisit(typeSyntaxNode); + typeSyntaxNode.OnBasicTypeGenerator += (_, e) => + { + e.BasicType.IsProcedure = typeSyntaxNode.IsProcedure; + }; + typeSyntaxNode.OnArrayTypeGenerator += (_, e) => + { + e.BasicType.IsProcedure = typeSyntaxNode.IsProcedure; + }; + } + + public override void PostVisit(BasicType basicType) + { + base.PostVisit(basicType); + if (!basicType.IsProcedure) + { + Builder.AddString(TryParseBasicType(basicType.PascalType) + " "); + } + } + + + public override void PostVisit(SubprogramHead subprogramHead) + { + base.PostVisit(subprogramHead); + + string subprogramName = subprogramHead.SubprogramName.IdentifierName; + //只需特判过程,函数的返回值类型已在basicType节点上生成 + if (subprogramHead.IsProcedure) + { + Builder.AddString("void "); + } + Builder.AddString(subprogramName); + //生成参数列表 + Builder.AddString("("); + List parametersInfo = new(); + + foreach (List children in _valueParameters) + { + foreach (Symbol symbol in children.AsEnumerable().Reverse()) + { + if (symbol.SymbolType is PascalBasicType pascalType) + { + string typeName = TryParseBasicType(pascalType); + if (symbol.Reference) + { + parametersInfo.Add(typeName + "* " + symbol.SymbolName); + } + else + { + parametersInfo.Add(typeName + " " + symbol.SymbolName); + } + } + else + { + TryParseArrayType(symbol.SymbolType, out string basicTypeName, out string periods); + parametersInfo.Add(string.Concat(basicTypeName, " ", symbol.SymbolName, "[]", + periods.Substring(periods.IndexOf(']') + 1))); + } + } + } + Builder.AddString(string.Join(", ", parametersInfo)); + Builder.AddString(")\n"); + } + + private string _subprogramName = ""; + public override void PreVisit(Subprogram subprogram) + { + base.PreVisit(subprogram); + _subprogramName = subprogram.Head.SubprogramName.IdentifierName; + } + + public override void PostVisit(Subprogram subprogram) + { + base.PostVisit(subprogram); + if (subprogram.Head.IsProcedure) + { + Builder.AddString("\n}\n"); + } + else + { + //为函数生成返回语句 + Builder.AddString("return " + subprogram.Head.SubprogramName.IdentifierName + ";\n}\n"); + } + } + + public override void PreVisit(SubprogramBody subprogramBody) + { + base.PreVisit(subprogramBody); + Builder.AddString("{\n"); + //生成函数返回值变量 + SymbolTable.TryGetSymbol(_subprogramName, out var symbol); + if (symbol is null || symbol.SymbolType is not PascalBasicType) + { + return; + } + + Builder.AddString(TryParseBasicType(symbol.SymbolType.Convert()) + " " + _subprogramName + ";\n"); + } + + public override void PreVisit(ProcedureCall procedureCall) + { + base.PreVisit(procedureCall); + string procedureName = procedureCall.ProcedureId.IdentifierName; + + if (procedureName == "read") + { + Builder.AddString("scanf(\"%d\", &"); + return; + } + if (procedureName == "write") + { + Builder.AddString("printf(\"%d\", "); + return; + } + + Builder.AddString(procedureName + "("); + + procedureCall.OnParameterGenerator += (_, e) => + { + string procedureIdName = procedureCall.ProcedureId.IdentifierName; + + SymbolTable.TryGetParent(out var parentTable); + parentTable ??= SymbolTable; + + parentTable.TryGetSymbol(procedureIdName, out var symbol); + + if (symbol is null) + { + return; + } + e.Parameters.ParameterTypes.AddRange(symbol.SymbolType.Convert().Parameters); + e.Parameters.Expression.LastParam = true; + e.Parameters.IsParamList = true; + }; + } + + public override void PostVisit(ProcedureCall procedureCall) + { + base.PostVisit(procedureCall); + Builder.AddString(")"); + } + + public override void PreVisit(ProgramBody programBody) + { + base.PreVisit(programBody); + //当子函数全部定义完成时,生成main函数头 + programBody.CompoundStatement.IsMain = true; + } + + public override void PostVisit(CompoundStatement compoundStatement) + { + base.PostVisit(compoundStatement); + if (compoundStatement.IsMain) + { + Builder.AddString("\nreturn 0;\n"); + } + Builder.AddString("}\n"); + } + + public override void PreVisit(CompoundStatement compoundStatement) + { + base.PreVisit(compoundStatement); + if (compoundStatement.IsMain) + { + Builder.AddString("int main()\n"); + } + Builder.AddString("{\n"); + } + + public override void PreVisit(Statement statement) + { + base.PreVisit(statement); + statement.OnForGenerator += (_, e) => + { + e.Begin.Iterator = e.Iterator; + e.Begin.IsForConditionBegin = true; + e.End.Iterator = e.Iterator; + e.End.IsForConditionEnd = true; + }; + statement.OnAssignGenerator += (_, e) => + { + e.Expression.IsAssign = true; + }; + } + + public override void PostVisit(Statement statement) + { + base.PostVisit(statement); + Builder.AddString(";\n"); + } + + public override void PreVisit(Variable variable) + { + base.PreVisit(variable); + + SymbolTable.TryGetSymbol(variable.Identifier.IdentifierName, out var symbol); + if (symbol == null) + { + return; + } + + if (symbol.Reference) + { + Builder.AddString("(*" + variable.Identifier.IdentifierName + ")"); + } + else + { + Builder.AddString(variable.Identifier.IdentifierName); + } + + if (symbol.SymbolType is PascalArrayType) + { + //解析数组类型,获取左边界列表 + List leftBounds = new(); + PascalType curType = symbol.SymbolType; + while (curType is not PascalBasicType) + { + leftBounds.Add(curType.Convert().Begin); + curType = curType.Convert().ElementType; + } + + //将数组维度信息向下传递 + variable.VarPart.LeftBounds.AddRange(leftBounds); + } + } + + public override void PreVisit(IdentifierVarPart identifierVarPart) + { + base.PreVisit(identifierVarPart); + identifierVarPart.OnIndexGenerator += (_, e) => + { + e.IndexParameters.IsIndex = true; + e.IndexParameters.LeftBounds = identifierVarPart.LeftBounds; + }; + } + + public override void PreVisit(ExpressionList expressionList) + { + base.PreVisit(expressionList); + + if (expressionList.IsIndex) + { + expressionList.Expression.LeftBound = expressionList.LeftBounds.Last(); + expressionList.Expression.IsIndex = true; + } + + if (expressionList.IsParamList) + { + expressionList.Expression.ReferenceParam = expressionList.ParameterTypes.Last().IsVar; + expressionList.Expression.IsParam = true; + } + expressionList.OnExpressionList += (_, e) => + { + if (expressionList.IsIndex) + { + e.ExpressionList.IsIndex = true; + expressionList.LeftBounds.RemoveAt(expressionList.LeftBounds.Count - 1); + e.ExpressionList.LeftBounds = expressionList.LeftBounds; + } + + if (expressionList.IsParamList) + { + e.ExpressionList.IsParamList = true; + for (int i = 0; i < expressionList.ParameterTypes.Count - 1; i++) + { + e.ExpressionList.ParameterTypes.Add(expressionList.ParameterTypes[i]); + } + } + }; + } + + public override void PreVisit(Expression expression) + { + base.PreVisit(expression); + if (expression.IsIndex) + { + Builder.AddString("["); + } + + if (expression.IsForConditionBegin) + { + Builder.AddString("for(" + expression.Iterator.IdentifierName + " = "); + } + if (expression.IsForConditionEnd) + { + Builder.AddString(expression.Iterator.IdentifierName + " <= "); + } + if (expression.IsAssign) + { + Builder.AddString(" = "); + } + + if (expression.ReferenceParam) + { + Builder.AddString("&"); + } + } + + public override void PostVisit(Expression expression) + { + base.PostVisit(expression); + if (expression.IsIndex) + { + //数组下标减去当前维度的左边界 + Builder.AddString("-" + expression.LeftBound + "]"); + } + if (expression.IsForConditionEnd) + { + Builder.AddString("; " + expression.Iterator.IdentifierName + "++)"); + } + if (expression is { IsParam: true, LastParam: false }) + { + Builder.AddString(", "); + } + } + + public override void PostVisit(Factor factor) + { + base.PostVisit(factor); + factor.OnNumberGenerator += (_, e) => + { + var token = e.Token; + string num = token.NumberType == NumberType.Integer ? token.ParseAsInteger().ToString() : + token.ParseAsReal().ToString(CultureInfo.InvariantCulture); + Builder.AddString(num); + }; + factor.OnProcedureCallGenerator += (_, _) => + { + Builder.AddString(")"); + }; + factor.OnNotGenerator += (_, _) => + { + Builder.AddString(")"); + }; + factor.OnUminusGenerator += (_, _) => + { + Builder.AddString(")"); + }; + } + + public override void PreVisit(Factor factor) + { + base.PreVisit(factor); + factor.OnProcedureCallGenerator += (_, e) => + { + Builder.AddString(e.ProcedureName.IdentifierName + "("); + SymbolTable.TryGetParent(out var parentTable); + parentTable ??= SymbolTable; + + parentTable.TryGetSymbol(e.ProcedureName.IdentifierName, out var symbol); + if (symbol == null) + { + return; + } + + e.Parameters.ParameterTypes.AddRange(symbol.SymbolType.Convert().Parameters); + e.Parameters.IsParamList = true; + e.Parameters.Expression.LastParam = true; + }; + factor.OnNotGenerator += (_, _) => + { + Builder.AddString("(!"); + }; + factor.OnUminusGenerator += (_, _) => + { + Builder.AddString("(-"); + }; + } + + public override void PostVisit(MultiplyOperator multiplyOperator) + { + base.PostVisit(multiplyOperator); + if (multiplyOperator.OperatorToken.TokenType == SemanticTokenType.Operator) + { + var operatorType = multiplyOperator.OperatorToken.Convert().OperatorType; + if (operatorType == OperatorType.Multiply) + { + Builder.AddString(" * "); + } + else if (operatorType == OperatorType.Divide) + { + //实数除法,需要将操作数强转为double + Builder.AddString(" /(double)"); + } + } + else + { + var keywordType = multiplyOperator.OperatorToken.Convert().KeywordType; + switch (keywordType) + { + case KeywordType.And: + Builder.AddString(" && "); + break; + case KeywordType.Mod: + Builder.AddString(" % "); + break; + default: + Builder.AddString(" / "); + break; + } + } + } + + public override void PostVisit(AddOperator addOperator) + { + base.PostVisit(addOperator); + var token = addOperator.OperatorToken; + if (token.TokenType == SemanticTokenType.Operator) + { + var operatorType = token.Convert().OperatorType; + if (operatorType == OperatorType.Plus) + { + Builder.AddString(" + "); + } + else if (operatorType == OperatorType.Minus) + { + Builder.AddString(" - "); + } + } + else + { + Builder.AddString(" || "); + } + } + + public override void PostVisit(RelationOperator relationOperator) + { + base.PostVisit(relationOperator); + var operatorType = relationOperator.OperatorToken.Convert().OperatorType; + switch (operatorType) + { + case OperatorType.Equal: + Builder.AddString(" == "); + break; + case OperatorType.Greater: + Builder.AddString(" > "); + break; + case OperatorType.Less: + Builder.AddString(" < "); + break; + case OperatorType.GreaterEqual: + Builder.AddString(" >= "); + break; + case OperatorType.LessEqual: + Builder.AddString(" <= "); + break; + case OperatorType.NotEqual: + Builder.AddString(" != "); + break; + } + } + + public override void PostVisit(TerminatedSyntaxNode terminatedSyntaxNode) + { + base.PostVisit(terminatedSyntaxNode); + string literalValue = terminatedSyntaxNode.Token.LiteralValue; + switch (literalValue) + { + case "if": + Builder.AddString("if("); + break; + case "then": + Builder.AddString(")\n"); + break; + case "else": + Builder.AddString("else\n"); + break; + case "to": + Builder.AddString("; "); + break; + } + } + + /// + /// 尝试将pascalBasicType解析成C语言的基本类型 + /// + /// C语言形式的基本类型名 + /// + private string TryParseBasicType(PascalType pascalType) + { + if (pascalType is PascalBasicType basicType) + { + if (basicType == PascalBasicType.Integer) + { + return "int"; + } + if (basicType == PascalBasicType.Real) + { + return "double"; + } + + if (basicType == PascalBasicType.Character) + { + return "char"; + } + + if (basicType == PascalBasicType.Boolean) + { + return "bool"; + } + + if (basicType == PascalBasicType.Void) + { + return "void"; + } + } + + throw new InvalidOperationException("Not a basic type"); + } + + /// + /// 尝试解析Pascal数组类型 + /// + /// + /// 数组实际存储的元素类型 + /// 数组下标定义 + private void TryParseArrayType(PascalType pascalType, out string basicTypeName, out string periods) + { + periods = ""; + PascalType curType = pascalType; + //依次处理数组每一维 + while (curType is PascalArrayType pascalArrayType) + { + int begin = pascalArrayType.Begin; + int end = pascalArrayType.End; + //C语言数组下标从0开始,所以下标要减去begin + periods += "[" + (end - begin + 1) + "]"; + curType = pascalArrayType.ElementType; + } + + basicTypeName = TryParseBasicType(curType); + } } diff --git a/Canon.Core/SemanticParser/PascalType.cs b/Canon.Core/SemanticParser/PascalType.cs index 2e815e2..55cf514 100644 --- a/Canon.Core/SemanticParser/PascalType.cs +++ b/Canon.Core/SemanticParser/PascalType.cs @@ -20,6 +20,16 @@ public abstract class PascalType : IEquatable return TypeName == other.TypeName; } + public T Convert() where T : PascalType + { + if (this is T result) + { + return result; + } + + throw new InvalidOperationException("Can not convert target PascalType"); + } + public override bool Equals(object? obj) { if (obj is PascalType other) diff --git a/Canon.Core/SemanticParser/TypeCheckVisitor.cs b/Canon.Core/SemanticParser/TypeCheckVisitor.cs index f8b6e9a..d8c0bb7 100644 --- a/Canon.Core/SemanticParser/TypeCheckVisitor.cs +++ b/Canon.Core/SemanticParser/TypeCheckVisitor.cs @@ -7,7 +7,7 @@ using Expression = Canon.Core.SyntaxNodes.Expression; namespace Canon.Core.SemanticParser; -public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisitor +public class TypeCheckVisitor(ILogger? logger = null) : SyntaxNodeVisitor { public SymbolTable SymbolTable { get; private set; } = new(); @@ -308,7 +308,7 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito /// /// 多个ValueParameter下定义的参数列表 /// - private readonly List> _valueParameters = []; + protected readonly List> _valueParameters = []; public override void PreVisit(Subprogram subprogram) { diff --git a/Canon.Core/SyntaxNodes/AddOperator.cs b/Canon.Core/SyntaxNodes/AddOperator.cs index b80f4c2..e54dbd1 100644 --- a/Canon.Core/SyntaxNodes/AddOperator.cs +++ b/Canon.Core/SyntaxNodes/AddOperator.cs @@ -9,32 +9,13 @@ public class AddOperator : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.AddOperator; + public SemanticToken OperatorToken => Children[0].Convert().Token; + public static AddOperator Create(List children) { return new AddOperator { Children = children }; } - public override void GenerateCCode(CCodeBuilder builder) - { - var token = Children[0].Convert().Token; - if (token.TokenType == SemanticTokenType.Operator) - { - var operatorType = token.Convert().OperatorType; - if (operatorType == OperatorType.Plus) - { - builder.AddString(" +"); - } - else if (operatorType == OperatorType.Minus) - { - builder.AddString(" -"); - } - } - else - { - builder.AddString(" ||"); - } - } - public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); diff --git a/Canon.Core/SyntaxNodes/BasicType.cs b/Canon.Core/SyntaxNodes/BasicType.cs index ae36bac..a3bff65 100644 --- a/Canon.Core/SyntaxNodes/BasicType.cs +++ b/Canon.Core/SyntaxNodes/BasicType.cs @@ -10,6 +10,7 @@ public class BasicType : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.BasicType; + public bool IsProcedure; /// /// BasicType代表的Pascal类型 /// diff --git a/Canon.Core/SyntaxNodes/CompoundStatement.cs b/Canon.Core/SyntaxNodes/CompoundStatement.cs index eb3aa65..8808f2a 100644 --- a/Canon.Core/SyntaxNodes/CompoundStatement.cs +++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs @@ -7,6 +7,11 @@ public class CompoundStatement : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.CompoundStatement; + /// + /// 是否为主函数部分 + /// + public bool IsMain; + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); diff --git a/Canon.Core/SyntaxNodes/ConstValue.cs b/Canon.Core/SyntaxNodes/ConstValue.cs index a1ea2a9..26c7013 100644 --- a/Canon.Core/SyntaxNodes/ConstValue.cs +++ b/Canon.Core/SyntaxNodes/ConstValue.cs @@ -126,24 +126,4 @@ public class ConstValue : NonTerminatedSyntaxNode { return new ConstValue { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //获取常量值 - var token = Children[0].Convert().Token; - //constValue -> 'letter' - if (token.TokenType == SemanticTokenType.Character) - { - builder.AddString(" '" + token.LiteralValue + "'"); - } - else - { - builder.AddString(" "); - // constValue -> +num | -num | num - foreach (var c in Children) - { - builder.AddString(c.Convert().Token.LiteralValue); - } - } - } } diff --git a/Canon.Core/SyntaxNodes/ElsePart.cs b/Canon.Core/SyntaxNodes/ElsePart.cs index d0b748f..a20bfcf 100644 --- a/Canon.Core/SyntaxNodes/ElsePart.cs +++ b/Canon.Core/SyntaxNodes/ElsePart.cs @@ -22,14 +22,4 @@ public class ElsePart : NonTerminatedSyntaxNode { return new ElsePart { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - if (Children.Count > 0) - { - builder.AddString(" else{"); - Children[1].GenerateCCode(builder); - builder.AddString(" }"); - } - } } diff --git a/Canon.Core/SyntaxNodes/Expression.cs b/Canon.Core/SyntaxNodes/Expression.cs index 631e1e0..d6f7a38 100644 --- a/Canon.Core/SyntaxNodes/Expression.cs +++ b/Canon.Core/SyntaxNodes/Expression.cs @@ -1,6 +1,7 @@ using Canon.Core.Abstractions; using Canon.Core.CodeGenerators; using Canon.Core.Enums; +using Canon.Core.LexicalParser; using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -35,6 +36,43 @@ public class Expression : NonTerminatedSyntaxNode RaiseEvent(); } + public bool IsParam; //是否为传参 + + public bool ReferenceParam; //是否为引用传参 + + public bool LastParam; //是否为传参列表里最后一个参数 + /// + /// 是否为数组下标 + /// + public bool IsIndex { get; set; } + + /// + /// 当前表达式对应的数组下标维度的左边界 + /// + public int LeftBound; + public bool IsForConditionBegin { get; set; } + public bool IsForConditionEnd { get; set; } + public bool IsAssign { get; set; } + + private IdentifierSemanticToken? _iterator; + + public IdentifierSemanticToken Iterator + { + get + { + if (_iterator is null) + { + throw new InvalidOperationException(); + } + + return _iterator; + } + set + { + _iterator = value; + } + } + /// /// 直接赋值产生式的事件 /// @@ -91,12 +129,4 @@ public class Expression : NonTerminatedSyntaxNode OnSimpleExpressionGenerator = null; OnRelationGenerator = null; } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var child in Children) - { - child.GenerateCCode(builder); - } - } } diff --git a/Canon.Core/SyntaxNodes/ExpressionList.cs b/Canon.Core/SyntaxNodes/ExpressionList.cs index 0b45d12..9b3c1b0 100644 --- a/Canon.Core/SyntaxNodes/ExpressionList.cs +++ b/Canon.Core/SyntaxNodes/ExpressionList.cs @@ -1,5 +1,6 @@ using Canon.Core.Abstractions; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,23 @@ public class ExpressionList : NonTerminatedSyntaxNode /// public List Expressions { get; } = []; + /// + /// 是否为传参列表 + /// + public bool IsParamList; + + public List ParameterTypes { get; } = []; + + /// + /// 是否为数组下标索引 + /// + public bool IsIndex { get; set; } + + /// + /// 数组左边界列表 + /// + public List LeftBounds = new(); + /// /// 当前ExpressionList中的Expression定义 /// diff --git a/Canon.Core/SyntaxNodes/Factor.cs b/Canon.Core/SyntaxNodes/Factor.cs index b015f84..4a49671 100644 --- a/Canon.Core/SyntaxNodes/Factor.cs +++ b/Canon.Core/SyntaxNodes/Factor.cs @@ -167,57 +167,4 @@ public class Factor : NonTerminatedSyntaxNode OnNotGenerator = null; OnUminusGenerator = null; } - - public override void GenerateCCode(CCodeBuilder builder) - { - if (Children.Count == 1) - { - //factor -> num - if (Children[0].IsTerminated) - { - var token = Children[0].Convert().Token; - if (token.TokenType == SemanticTokenType.Number) - { - builder.AddString(" " + token.LiteralValue); - } - } - // factor -> variable - else - { - Children[0].GenerateCCode(builder); - } - } - //factor -> ( expression ) - else if (Children.Count == 3) - { - builder.AddString(" ("); - Children[1].GenerateCCode(builder); - builder.AddString(")"); - } - //factor -> id ( expression ) - else if (Children.Count == 4) - { - builder.AddString(" " + Children[0].Convert().Token.Convert() - .IdentifierName); - builder.AddString("("); - Children[2].GenerateCCode(builder); - builder.AddString(")"); - } - else - { - //factor -> not factor - builder.AddString(" ("); - if (Children[0].Convert().Token.TokenType == SemanticTokenType.Keyword) - { - builder.AddString("!"); - } - else - { - builder.AddString("-"); - } - - Children[1].GenerateCCode(builder); - builder.AddString(")"); - } - } } diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs index c783347..5977971 100644 --- a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs +++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs @@ -1,5 +1,6 @@ using Canon.Core.Abstractions; using Canon.Core.Enums; +using Canon.Core.SemanticParser; namespace Canon.Core.SyntaxNodes; @@ -17,6 +18,11 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode /// public int IndexCount { get; set; } + /// + /// 数组左边界列表 + /// + public List LeftBounds = new(); + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); diff --git a/Canon.Core/SyntaxNodes/MultiplyOperator.cs b/Canon.Core/SyntaxNodes/MultiplyOperator.cs index e5fc726..b949ab8 100644 --- a/Canon.Core/SyntaxNodes/MultiplyOperator.cs +++ b/Canon.Core/SyntaxNodes/MultiplyOperator.cs @@ -9,6 +9,8 @@ public class MultiplyOperator : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator; + public SemanticToken OperatorToken => Children[0].Convert().Token; + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -23,38 +25,4 @@ public class MultiplyOperator : NonTerminatedSyntaxNode { return new MultiplyOperator { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - var token = Children[0].Convert().Token; - if (token.TokenType == SemanticTokenType.Operator) - { - var operatorType = token.Convert().OperatorType; - if (operatorType == OperatorType.Multiply) - { - builder.AddString(" *"); - } - else if (operatorType == OperatorType.Divide) - { - //实数除法,需要将操作数强转为double - builder.AddString(" /(double)"); - } - } - else - { - var keywordType = token.Convert().KeywordType; - if (keywordType == KeywordType.And) - { - builder.AddString(" &&"); - } - else if (keywordType == KeywordType.Mod) - { - builder.AddString(" %"); - } - else - { - builder.AddString(" /"); - } - } - } } diff --git a/Canon.Core/SyntaxNodes/ProgramBody.cs b/Canon.Core/SyntaxNodes/ProgramBody.cs index 97d4ece..6cb2d67 100644 --- a/Canon.Core/SyntaxNodes/ProgramBody.cs +++ b/Canon.Core/SyntaxNodes/ProgramBody.cs @@ -42,17 +42,4 @@ public class ProgramBody : NonTerminatedSyntaxNode { return new ProgramBody { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //全局常量,变量 - ConstDeclarations.GenerateCCode(builder); - VarDeclarations.GenerateCCode(builder); - //子函数声明 - SubprogramDeclarations.GenerateCCode(builder); - //main函数 - builder.AddString(" int main(){"); - CompoundStatement.GenerateCCode(builder); - builder.AddString(" return 0;}"); - } } diff --git a/Canon.Core/SyntaxNodes/ProgramStruct.cs b/Canon.Core/SyntaxNodes/ProgramStruct.cs index da98944..6a5f760 100644 --- a/Canon.Core/SyntaxNodes/ProgramStruct.cs +++ b/Canon.Core/SyntaxNodes/ProgramStruct.cs @@ -32,10 +32,4 @@ public class ProgramStruct : NonTerminatedSyntaxNode { return new ProgramStruct { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - builder.AddString("#include "); - Body.GenerateCCode(builder); - } } diff --git a/Canon.Core/SyntaxNodes/RelationOperator.cs b/Canon.Core/SyntaxNodes/RelationOperator.cs index 067d5bc..2788fe0 100644 --- a/Canon.Core/SyntaxNodes/RelationOperator.cs +++ b/Canon.Core/SyntaxNodes/RelationOperator.cs @@ -9,6 +9,8 @@ public class RelationOperator : NonTerminatedSyntaxNode { public override NonTerminatorType Type => NonTerminatorType.RelationOperator; + public SemanticToken OperatorToken => Children[0].Convert().Token; + public override void PreVisit(SyntaxNodeVisitor visitor) { visitor.PreVisit(this); @@ -23,31 +25,4 @@ public class RelationOperator : NonTerminatedSyntaxNode { return new RelationOperator { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - var operatorType = Children[0].Convert().Token.Convert() - .OperatorType; - switch (operatorType) - { - case OperatorType.Equal: - builder.AddString(" =="); - break; - case OperatorType.Greater: - builder.AddString(" >"); - break; - case OperatorType.Less: - builder.AddString(" <"); - break; - case OperatorType.GreaterEqual: - builder.AddString(" >="); - break; - case OperatorType.LessEqual: - builder.AddString(" <="); - break; - case OperatorType.NotEqual: - builder.AddString(" !="); - break; - } - } } diff --git a/Canon.Core/SyntaxNodes/SimpleExpression.cs b/Canon.Core/SyntaxNodes/SimpleExpression.cs index 72690d6..4b383d4 100644 --- a/Canon.Core/SyntaxNodes/SimpleExpression.cs +++ b/Canon.Core/SyntaxNodes/SimpleExpression.cs @@ -91,12 +91,4 @@ public class SimpleExpression : NonTerminatedSyntaxNode OnTermGenerator = null; OnAddGenerator = null; } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var child in Children) - { - child.GenerateCCode(builder); - } - } } diff --git a/Canon.Core/SyntaxNodes/Statement.cs b/Canon.Core/SyntaxNodes/Statement.cs index 9acec1b..1ee1128 100644 --- a/Canon.Core/SyntaxNodes/Statement.cs +++ b/Canon.Core/SyntaxNodes/Statement.cs @@ -121,48 +121,4 @@ public class Statement : NonTerminatedSyntaxNode }); } } - - public override void GenerateCCode(CCodeBuilder builder) - { - if (Children.Count == 0) - { - return; - } - - // statement -> procedureCall | compoundStatement - if (Children.Count == 1) - { - Children[0].GenerateCCode(builder); - } - //statement -> variable assign expression - else if (Children.Count == 3) - { - Children[0].GenerateCCode(builder); - builder.AddString(" ="); - Children[2].GenerateCCode(builder); - } - //if expression then statement else_part - else if (Children.Count == 5) - { - builder.AddString(" if("); - Children[1].GenerateCCode(builder); - builder.AddString("){"); - Children[3].GenerateCCode(builder); - builder.AddString("; }"); - Children[4].GenerateCCode(builder); - } - //for id assign expression to expression do statement - else - { - string idName = Children[1].Convert().Token.Convert() - .IdentifierName; - builder.AddString(" for(" + idName + " ="); - Children[3].GenerateCCode(builder); - builder.AddString("; " + idName + " <="); - Children[5].GenerateCCode(builder); - builder.AddString("; " + idName + "++){"); - Children[7].GenerateCCode(builder); - builder.AddString("; }"); - } - } } diff --git a/Canon.Core/SyntaxNodes/Subprogram.cs b/Canon.Core/SyntaxNodes/Subprogram.cs index 5e2d2d1..454937f 100644 --- a/Canon.Core/SyntaxNodes/Subprogram.cs +++ b/Canon.Core/SyntaxNodes/Subprogram.cs @@ -32,14 +32,4 @@ public class Subprogram : NonTerminatedSyntaxNode { return new Subprogram { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //子函数头 - Head.GenerateCCode(builder); - //子函数体 - builder.AddString("{"); - Body.GenerateCCode(builder); - builder.AddString("}"); - } } diff --git a/Canon.Core/SyntaxNodes/SubprogramBody.cs b/Canon.Core/SyntaxNodes/SubprogramBody.cs index 60e5ee4..186f062 100644 --- a/Canon.Core/SyntaxNodes/SubprogramBody.cs +++ b/Canon.Core/SyntaxNodes/SubprogramBody.cs @@ -37,11 +37,4 @@ public class SubprogramBody : NonTerminatedSyntaxNode { return new SubprogramBody() { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - ConstDeclarations.GenerateCCode(builder); - VarDeclarations.GenerateCCode(builder); - CompoundStatement.GenerateCCode(builder); - } } diff --git a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs index 2df1028..67a2e4c 100644 --- a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs +++ b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs @@ -1,5 +1,4 @@ using Canon.Core.Abstractions; -using Canon.Core.CodeGenerators; using Canon.Core.Enums; namespace Canon.Core.SyntaxNodes; @@ -22,4 +21,5 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode { return new SubprogramDeclarations { Children = children }; } + } diff --git a/Canon.Core/SyntaxNodes/Term.cs b/Canon.Core/SyntaxNodes/Term.cs index a6274b6..f9d937c 100644 --- a/Canon.Core/SyntaxNodes/Term.cs +++ b/Canon.Core/SyntaxNodes/Term.cs @@ -91,12 +91,4 @@ public class Term : NonTerminatedSyntaxNode OnFactorGenerator = null; OnMultiplyGenerator = null; } - - public override void GenerateCCode(CCodeBuilder builder) - { - foreach (var child in Children) - { - child.GenerateCCode(builder); - } - } } diff --git a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs index e1e2c1c..a315ef1 100644 --- a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs +++ b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs @@ -37,6 +37,11 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode public event EventHandler? OnArrayTypeGenerator; + /// + /// 是否在过程定义中使用 + /// + public bool IsProcedure { get; set; } + private PascalType? _pascalType; /// @@ -86,17 +91,4 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode OnBasicTypeGenerator = null; OnArrayTypeGenerator = null; } - - public override void GenerateCCode(CCodeBuilder builder) - { - //type -> basic_type - if (Children.Count == 1) - { - Children[0].GenerateCCode(builder); - } - //type -> array [ period ]of basic_type - else - { - } - } } diff --git a/Canon.Core/SyntaxNodes/VarParameter.cs b/Canon.Core/SyntaxNodes/VarParameter.cs index d22872b..a2ade7c 100644 --- a/Canon.Core/SyntaxNodes/VarParameter.cs +++ b/Canon.Core/SyntaxNodes/VarParameter.cs @@ -24,9 +24,4 @@ public class VarParameter : NonTerminatedSyntaxNode { return new VarParameter { Children = children }; } - - public override void GenerateCCode(CCodeBuilder builder) - { - ValueParameter.GenerateCCode(builder); - } } diff --git a/Canon.Tests/CodeGeneratorTests/BasicTest.cs b/Canon.Tests/CodeGeneratorTests/BasicTest.cs new file mode 100644 index 0000000..1ed7d25 --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/BasicTest.cs @@ -0,0 +1,35 @@ +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 new file mode 100644 index 0000000..b735bdb --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/DeclarationTests.cs @@ -0,0 +1,64 @@ +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 new file mode 100644 index 0000000..084a023 --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/ExpressionTests.cs @@ -0,0 +1,66 @@ +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 new file mode 100644 index 0000000..87078ee --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/ReadTest.cs @@ -0,0 +1,40 @@ +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 new file mode 100644 index 0000000..88ce9d4 --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/StatementTests.cs @@ -0,0 +1,118 @@ +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 new file mode 100644 index 0000000..375ece0 --- /dev/null +++ b/Canon.Tests/CodeGeneratorTests/SubprogramTests.cs @@ -0,0 +1,84 @@ +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 \n#include \nbool b, a;" + + "\nint func1(int* a, int b, double c)\n{\nint func1;\n" + + "{\n(*a) = b + c;\nfunc1 = (*a) * 3;\n;\n}\nreturn func1;\n}\n" + + "char func2(bool* a, bool* b, char c[][6])\n{\nchar func2;\n" + + "{\n(*a) = (*b) && (!(*b));\nfunc2 = c[5-0][8-3];\n;\n}\nreturn func2;\n}" + + "\nint main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build()); + } + + +} diff --git a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs index 7c9dd95..0f853b2 100644 --- a/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs +++ b/Canon.Tests/SemanticTests/TypeCheckVisitorTests.cs @@ -7,7 +7,7 @@ namespace Canon.Tests.SemanticTests; public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper) { - private readonly TestLogger _logger = new(testOutputHelper); + private readonly TestLogger _logger = new(testOutputHelper); [Fact] public void ConstTypeTest() diff --git a/Canon.Tests/Utils/TestLogger.cs b/Canon.Tests/Utils/TestLogger.cs index faf1230..3e27bed 100644 --- a/Canon.Tests/Utils/TestLogger.cs +++ b/Canon.Tests/Utils/TestLogger.cs @@ -1,16 +1,24 @@ -using Canon.Core.Abstractions; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; namespace Canon.Tests.Utils; -public class TestLogger(ITestOutputHelper testOutputHelper) : ICompilerLogger +public class TestLogger(ITestOutputHelper testOutputHelper) : ILogger, IDisposable { public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - testOutputHelper.WriteLine($"{logLevel}: {formatter(state, exception)}"); + testOutputHelper.WriteLine("{0}: {1}", logLevel, formatter(state, exception)); } - public string Build() => string.Empty; + public bool IsEnabled(LogLevel logLevel) => false; + + public void Dispose() + { + } + + public IDisposable BeginScope(TState state) where TState : notnull + { + return this; + } }