feat-generater (#69)
Reviewed-on: PostGuard/Canon#69 Co-authored-by: Lan_G <2911328695@qq.com> Co-committed-by: Lan_G <2911328695@qq.com>
This commit is contained in:
parent
a349f0d9c0
commit
5ca947125b
|
@ -1,10 +1,13 @@
|
||||||
using Canon.Core.Abstractions;
|
using System.Globalization;
|
||||||
using Canon.Core.CodeGenerators;
|
using Canon.Core.CodeGenerators;
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
using Canon.Core.SyntaxNodes;
|
using Canon.Core.SyntaxNodes;
|
||||||
|
using BasicType = Canon.Core.SyntaxNodes.BasicType;
|
||||||
|
|
||||||
namespace Canon.Core.SemanticParser;
|
namespace Canon.Core.SemanticParser;
|
||||||
|
|
||||||
public class CCodeGenerateVisitor(ICompilerLogger? logger = null) : TypeCheckVisitor(logger)
|
public class CCodeGenerateVisitor : TypeCheckVisitor
|
||||||
{
|
{
|
||||||
public CCodeBuilder Builder { get; } = new();
|
public CCodeBuilder Builder { get; } = new();
|
||||||
|
|
||||||
|
@ -15,4 +18,624 @@ public class CCodeGenerateVisitor(ICompilerLogger? logger = null) : TypeCheckVis
|
||||||
Builder.AddString("#include <stdbool.h>\n");
|
Builder.AddString("#include <stdbool.h>\n");
|
||||||
Builder.AddString("#include <stdio.h>\n");
|
Builder.AddString("#include <stdio.h>\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<string> parametersInfo = new();
|
||||||
|
|
||||||
|
foreach (List<Symbol> 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<PascalType>()) + " " + _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<PascalFunctionType>().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<int> leftBounds = new();
|
||||||
|
PascalType curType = symbol.SymbolType;
|
||||||
|
while (curType is not PascalBasicType)
|
||||||
|
{
|
||||||
|
leftBounds.Add(curType.Convert<PascalArrayType>().Begin);
|
||||||
|
curType = curType.Convert<PascalArrayType>().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<PascalFunctionType>().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<OperatorSemanticToken>().OperatorType;
|
||||||
|
if (operatorType == OperatorType.Multiply)
|
||||||
|
{
|
||||||
|
Builder.AddString(" * ");
|
||||||
|
}
|
||||||
|
else if (operatorType == OperatorType.Divide)
|
||||||
|
{
|
||||||
|
//实数除法,需要将操作数强转为double
|
||||||
|
Builder.AddString(" /(double)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var keywordType = multiplyOperator.OperatorToken.Convert<KeywordSemanticToken>().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<OperatorSemanticToken>().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<OperatorSemanticToken>().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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试将pascalBasicType解析成C语言的基本类型
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>C语言形式的基本类型名</returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试解析Pascal数组类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pascalType"></param>
|
||||||
|
/// <param name="basicTypeName">数组实际存储的元素类型</param>
|
||||||
|
/// <param name="periods">数组下标定义</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,16 @@ public abstract class PascalType : IEquatable<PascalType>
|
||||||
return TypeName == other.TypeName;
|
return TypeName == other.TypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T Convert<T>() where T : PascalType
|
||||||
|
{
|
||||||
|
if (this is T result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Can not convert target PascalType");
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
if (obj is PascalType other)
|
if (obj is PascalType other)
|
||||||
|
|
|
@ -7,7 +7,7 @@ using Expression = Canon.Core.SyntaxNodes.Expression;
|
||||||
|
|
||||||
namespace Canon.Core.SemanticParser;
|
namespace Canon.Core.SemanticParser;
|
||||||
|
|
||||||
public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisitor
|
public class TypeCheckVisitor(ILogger<TypeCheckVisitor>? logger = null) : SyntaxNodeVisitor
|
||||||
{
|
{
|
||||||
public SymbolTable SymbolTable { get; private set; } = new();
|
public SymbolTable SymbolTable { get; private set; } = new();
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ public class TypeCheckVisitor(ICompilerLogger? logger = null) : SyntaxNodeVisito
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 多个ValueParameter下定义的参数列表
|
/// 多个ValueParameter下定义的参数列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<List<Symbol>> _valueParameters = [];
|
protected readonly List<List<Symbol>> _valueParameters = [];
|
||||||
|
|
||||||
public override void PreVisit(Subprogram subprogram)
|
public override void PreVisit(Subprogram subprogram)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,32 +9,13 @@ public class AddOperator : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.AddOperator;
|
public override NonTerminatorType Type => NonTerminatorType.AddOperator;
|
||||||
|
|
||||||
|
public SemanticToken OperatorToken => Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||||
|
|
||||||
public static AddOperator Create(List<SyntaxNodeBase> children)
|
public static AddOperator Create(List<SyntaxNodeBase> children)
|
||||||
{
|
{
|
||||||
return new AddOperator { Children = children };
|
return new AddOperator { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
var token = Children[0].Convert<TerminatedSyntaxNode>().Token;
|
|
||||||
if (token.TokenType == SemanticTokenType.Operator)
|
|
||||||
{
|
|
||||||
var operatorType = token.Convert<OperatorSemanticToken>().OperatorType;
|
|
||||||
if (operatorType == OperatorType.Plus)
|
|
||||||
{
|
|
||||||
builder.AddString(" +");
|
|
||||||
}
|
|
||||||
else if (operatorType == OperatorType.Minus)
|
|
||||||
{
|
|
||||||
builder.AddString(" -");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.AddString(" ||");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
|
|
@ -10,6 +10,7 @@ public class BasicType : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.BasicType;
|
public override NonTerminatorType Type => NonTerminatorType.BasicType;
|
||||||
|
|
||||||
|
public bool IsProcedure;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// BasicType代表的Pascal类型
|
/// BasicType代表的Pascal类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -7,6 +7,11 @@ public class CompoundStatement : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.CompoundStatement;
|
public override NonTerminatorType Type => NonTerminatorType.CompoundStatement;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为主函数部分
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMain;
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
|
|
@ -126,24 +126,4 @@ public class ConstValue : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new ConstValue { Children = children };
|
return new ConstValue { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
//获取常量值
|
|
||||||
var token = Children[0].Convert<TerminatedSyntaxNode>().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<TerminatedSyntaxNode>().Token.LiteralValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,4 @@ public class ElsePart : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new ElsePart { Children = children };
|
return new ElsePart { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
if (Children.Count > 0)
|
|
||||||
{
|
|
||||||
builder.AddString(" else{");
|
|
||||||
Children[1].GenerateCCode(builder);
|
|
||||||
builder.AddString(" }");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Canon.Core.Abstractions;
|
using Canon.Core.Abstractions;
|
||||||
using Canon.Core.CodeGenerators;
|
using Canon.Core.CodeGenerators;
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
using Canon.Core.SemanticParser;
|
using Canon.Core.SemanticParser;
|
||||||
|
|
||||||
namespace Canon.Core.SyntaxNodes;
|
namespace Canon.Core.SyntaxNodes;
|
||||||
|
@ -35,6 +36,43 @@ public class Expression : NonTerminatedSyntaxNode
|
||||||
RaiseEvent();
|
RaiseEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsParam; //是否为传参
|
||||||
|
|
||||||
|
public bool ReferenceParam; //是否为引用传参
|
||||||
|
|
||||||
|
public bool LastParam; //是否为传参列表里最后一个参数
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为数组下标
|
||||||
|
/// </summary>
|
||||||
|
public bool IsIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前表达式对应的数组下标维度的左边界
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 直接赋值产生式的事件
|
/// 直接赋值产生式的事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -91,12 +129,4 @@ public class Expression : NonTerminatedSyntaxNode
|
||||||
OnSimpleExpressionGenerator = null;
|
OnSimpleExpressionGenerator = null;
|
||||||
OnRelationGenerator = null;
|
OnRelationGenerator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
{
|
|
||||||
child.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Canon.Core.Abstractions;
|
using Canon.Core.Abstractions;
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.SemanticParser;
|
||||||
|
|
||||||
namespace Canon.Core.SyntaxNodes;
|
namespace Canon.Core.SyntaxNodes;
|
||||||
|
|
||||||
|
@ -17,6 +18,23 @@ public class ExpressionList : NonTerminatedSyntaxNode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Expression> Expressions { get; } = [];
|
public List<Expression> Expressions { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为传参列表
|
||||||
|
/// </summary>
|
||||||
|
public bool IsParamList;
|
||||||
|
|
||||||
|
public List<PascalParameterType> ParameterTypes { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为数组下标索引
|
||||||
|
/// </summary>
|
||||||
|
public bool IsIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数组左边界列表
|
||||||
|
/// </summary>
|
||||||
|
public List<int> LeftBounds = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前ExpressionList中的Expression定义
|
/// 当前ExpressionList中的Expression定义
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -167,57 +167,4 @@ public class Factor : NonTerminatedSyntaxNode
|
||||||
OnNotGenerator = null;
|
OnNotGenerator = null;
|
||||||
OnUminusGenerator = null;
|
OnUminusGenerator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
if (Children.Count == 1)
|
|
||||||
{
|
|
||||||
//factor -> num
|
|
||||||
if (Children[0].IsTerminated)
|
|
||||||
{
|
|
||||||
var token = Children[0].Convert<TerminatedSyntaxNode>().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<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>()
|
|
||||||
.IdentifierName);
|
|
||||||
builder.AddString("(");
|
|
||||||
Children[2].GenerateCCode(builder);
|
|
||||||
builder.AddString(")");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//factor -> not factor
|
|
||||||
builder.AddString(" (");
|
|
||||||
if (Children[0].Convert<TerminatedSyntaxNode>().Token.TokenType == SemanticTokenType.Keyword)
|
|
||||||
{
|
|
||||||
builder.AddString("!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.AddString("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
Children[1].GenerateCCode(builder);
|
|
||||||
builder.AddString(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Canon.Core.Abstractions;
|
using Canon.Core.Abstractions;
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.SemanticParser;
|
||||||
|
|
||||||
namespace Canon.Core.SyntaxNodes;
|
namespace Canon.Core.SyntaxNodes;
|
||||||
|
|
||||||
|
@ -17,6 +18,11 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int IndexCount { get; set; }
|
public int IndexCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数组左边界列表
|
||||||
|
/// </summary>
|
||||||
|
public List<int> LeftBounds = new();
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
|
|
@ -9,6 +9,8 @@ public class MultiplyOperator : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator;
|
public override NonTerminatorType Type => NonTerminatorType.MultiplyOperator;
|
||||||
|
|
||||||
|
public SemanticToken OperatorToken => Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
@ -23,38 +25,4 @@ public class MultiplyOperator : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new MultiplyOperator { Children = children };
|
return new MultiplyOperator { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
var token = Children[0].Convert<TerminatedSyntaxNode>().Token;
|
|
||||||
if (token.TokenType == SemanticTokenType.Operator)
|
|
||||||
{
|
|
||||||
var operatorType = token.Convert<OperatorSemanticToken>().OperatorType;
|
|
||||||
if (operatorType == OperatorType.Multiply)
|
|
||||||
{
|
|
||||||
builder.AddString(" *");
|
|
||||||
}
|
|
||||||
else if (operatorType == OperatorType.Divide)
|
|
||||||
{
|
|
||||||
//实数除法,需要将操作数强转为double
|
|
||||||
builder.AddString(" /(double)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var keywordType = token.Convert<KeywordSemanticToken>().KeywordType;
|
|
||||||
if (keywordType == KeywordType.And)
|
|
||||||
{
|
|
||||||
builder.AddString(" &&");
|
|
||||||
}
|
|
||||||
else if (keywordType == KeywordType.Mod)
|
|
||||||
{
|
|
||||||
builder.AddString(" %");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.AddString(" /");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,17 +42,4 @@ public class ProgramBody : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new ProgramBody { Children = children };
|
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;}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,4 @@ public class ProgramStruct : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new ProgramStruct { Children = children };
|
return new ProgramStruct { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
builder.AddString("#include <stdbool.h>");
|
|
||||||
Body.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ public class RelationOperator : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
public override NonTerminatorType Type => NonTerminatorType.RelationOperator;
|
public override NonTerminatorType Type => NonTerminatorType.RelationOperator;
|
||||||
|
|
||||||
|
public SemanticToken OperatorToken => Children[0].Convert<TerminatedSyntaxNode>().Token;
|
||||||
|
|
||||||
public override void PreVisit(SyntaxNodeVisitor visitor)
|
public override void PreVisit(SyntaxNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
visitor.PreVisit(this);
|
visitor.PreVisit(this);
|
||||||
|
@ -23,31 +25,4 @@ public class RelationOperator : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new RelationOperator { Children = children };
|
return new RelationOperator { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
var operatorType = Children[0].Convert<TerminatedSyntaxNode>().Token.Convert<OperatorSemanticToken>()
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,12 +91,4 @@ public class SimpleExpression : NonTerminatedSyntaxNode
|
||||||
OnTermGenerator = null;
|
OnTermGenerator = null;
|
||||||
OnAddGenerator = null;
|
OnAddGenerator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
{
|
|
||||||
child.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<TerminatedSyntaxNode>().Token.Convert<IdentifierSemanticToken>()
|
|
||||||
.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("; }");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,4 @@ public class Subprogram : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new Subprogram { Children = children };
|
return new Subprogram { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
//子函数头
|
|
||||||
Head.GenerateCCode(builder);
|
|
||||||
//子函数体
|
|
||||||
builder.AddString("{");
|
|
||||||
Body.GenerateCCode(builder);
|
|
||||||
builder.AddString("}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,4 @@ public class SubprogramBody : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new SubprogramBody() { Children = children };
|
return new SubprogramBody() { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
ConstDeclarations.GenerateCCode(builder);
|
|
||||||
VarDeclarations.GenerateCCode(builder);
|
|
||||||
CompoundStatement.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Canon.Core.Abstractions;
|
using Canon.Core.Abstractions;
|
||||||
using Canon.Core.CodeGenerators;
|
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
|
||||||
namespace Canon.Core.SyntaxNodes;
|
namespace Canon.Core.SyntaxNodes;
|
||||||
|
@ -22,4 +21,5 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new SubprogramDeclarations { Children = children };
|
return new SubprogramDeclarations { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,12 +91,4 @@ public class Term : NonTerminatedSyntaxNode
|
||||||
OnFactorGenerator = null;
|
OnFactorGenerator = null;
|
||||||
OnMultiplyGenerator = null;
|
OnMultiplyGenerator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
{
|
|
||||||
child.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,11 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode
|
||||||
|
|
||||||
public event EventHandler<OnArrayTypeGeneratorEventArgs>? OnArrayTypeGenerator;
|
public event EventHandler<OnArrayTypeGeneratorEventArgs>? OnArrayTypeGenerator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否在过程定义中使用
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcedure { get; set; }
|
||||||
|
|
||||||
private PascalType? _pascalType;
|
private PascalType? _pascalType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -86,17 +91,4 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode
|
||||||
OnBasicTypeGenerator = null;
|
OnBasicTypeGenerator = null;
|
||||||
OnArrayTypeGenerator = 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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,4 @@ public class VarParameter : NonTerminatedSyntaxNode
|
||||||
{
|
{
|
||||||
return new VarParameter { Children = children };
|
return new VarParameter { Children = children };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GenerateCCode(CCodeBuilder builder)
|
|
||||||
{
|
|
||||||
ValueParameter.GenerateCCode(builder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
35
Canon.Tests/CodeGeneratorTests/BasicTest.cs
Normal file
35
Canon.Tests/CodeGeneratorTests/BasicTest.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nint main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
}
|
64
Canon.Tests/CodeGeneratorTests/DeclarationTests.cs
Normal file
64
Canon.Tests/CodeGeneratorTests/DeclarationTests.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nconst char a = 'a';\nconst " +
|
||||||
|
"int b = 200;\nconst double c = 3.14;\nconst char d = 'm';\nint main()\n{\n;\n\nreturn 0;\n}\n",
|
||||||
|
visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarDeclarationTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a, b, c:array[3..6, 4..999, 0..7, 8..80] of real;
|
||||||
|
d, e, f:integer;
|
||||||
|
g, h:array [6..8] of boolean;
|
||||||
|
i, j:char;
|
||||||
|
m, n:integer;
|
||||||
|
begin
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
SyntaxTreeTraveller traveller = new();
|
||||||
|
CCodeGenerateVisitor visitor = new();
|
||||||
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
|
string result = visitor.Builder.Build();
|
||||||
|
_output.WriteLine(result);
|
||||||
|
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\ndouble c[4][996][8][73]," +
|
||||||
|
" b[4][996][8][73], a[4][996][8][73];\nint f, e, d;\nbool h[3], g[3];\nchar j, i;" +
|
||||||
|
"\nint n, m;\nint main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
}
|
66
Canon.Tests/CodeGeneratorTests/ExpressionTests.cs
Normal file
66
Canon.Tests/CodeGeneratorTests/ExpressionTests.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nint b, a;\n" +
|
||||||
|
"bool tag, flag;\nint main()\n{\na = 1;\nb = a + b * 1 /(double)1 - 1 / 1 - (-2);" +
|
||||||
|
"\ntag = flag || tag;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ArrayTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a: array[9..12, 3..5, 6..20] of real; b: array[5..10] of integer;
|
||||||
|
begin
|
||||||
|
a[9, 4, 20] := 3.6 + b[6] - a[12, 5, 6];
|
||||||
|
b[5] := 250;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
SyntaxTreeTraveller traveller = new();
|
||||||
|
CCodeGenerateVisitor visitor = new();
|
||||||
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
|
string result = visitor.Builder.Build();
|
||||||
|
_output.WriteLine(result);
|
||||||
|
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\ndouble a[4][3][15];" +
|
||||||
|
"\nint b[6];\nint main()\n{\na[9-9][4-3][20-6] = 3.6 + b[6-5] - a[12-9][5-3][6-6];" +
|
||||||
|
"\nb[5-5] = 250;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
Canon.Tests/CodeGeneratorTests/ReadTest.cs
Normal file
40
Canon.Tests/CodeGeneratorTests/ReadTest.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nint b, a;\n" +
|
||||||
|
"bool tag, flag;\nint main()\n{\na = 1;\nb = a + b * 1 /(double)1 - 1 / 1 - (-2);" +
|
||||||
|
"\ntag = flag || tag;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
}
|
118
Canon.Tests/CodeGeneratorTests/StatementTests.cs
Normal file
118
Canon.Tests/CodeGeneratorTests/StatementTests.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nint a;\nint main()\n" +
|
||||||
|
"{\na = 1;\nif(a == 1)\na = 1;\nelse\n{\nif(a == 2 + a)\n" +
|
||||||
|
"a = a;\nelse\na = 999;\n;\n;\n}\n;\n;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ForLoopTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a, b, c:integer;
|
||||||
|
begin
|
||||||
|
b := 5;
|
||||||
|
c := 6;
|
||||||
|
for a := 1 to 60 do
|
||||||
|
begin
|
||||||
|
for b := a + c to 5 * a do
|
||||||
|
begin
|
||||||
|
c := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
SyntaxTreeTraveller traveller = new();
|
||||||
|
CCodeGenerateVisitor visitor = new();
|
||||||
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
|
string result = visitor.Builder.Build();
|
||||||
|
_output.WriteLine(result);
|
||||||
|
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\n" +
|
||||||
|
"int c, b, a;\nint main()\n" +
|
||||||
|
"{\nb = 5;\nc = 6;\nfor(a = 1; a <= 60; a++){\n" +
|
||||||
|
"for(b = a + c; b <= 5 * a; b++)" +
|
||||||
|
"{\nc = 1;\n;\n}\n;\n;\n;\n}\n;\n;\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProcedureCallTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a, b:integer; c:real;
|
||||||
|
|
||||||
|
function test1(var a1:integer; b1:integer; c1:real):integer;
|
||||||
|
var i, j, k:integer;
|
||||||
|
begin
|
||||||
|
a1:= 10086;
|
||||||
|
b1 := 2;
|
||||||
|
c1 := 63;
|
||||||
|
test1 := test1(i, j, k);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
test1(a, b, c);
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
SyntaxTreeTraveller traveller = new();
|
||||||
|
CCodeGenerateVisitor visitor = new();
|
||||||
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
|
string result = visitor.Builder.Build();
|
||||||
|
_output.WriteLine(result);
|
||||||
|
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\nint b, a;\ndouble c;" +
|
||||||
|
"\nint test1(int* a1, int b1, double c1)\n{" +
|
||||||
|
"\nint test1;\nint k, j, i;\n" +
|
||||||
|
"{\n(*a1) = 10086;\nb1 = 2;\nc1 = 63;\n" +
|
||||||
|
"test1 = test1(&i, j, k);\n;\n}\nreturn test1;\n}\n" +
|
||||||
|
"int main()\n{\ntest1(&a, b, c);\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
}
|
84
Canon.Tests/CodeGeneratorTests/SubprogramTests.cs
Normal file
84
Canon.Tests/CodeGeneratorTests/SubprogramTests.cs
Normal file
|
@ -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 <stdbool.h>\n#include <stdio.h>\nconst double pi = 3.1415;\n" +
|
||||||
|
"void test1()\n{\nchar ch;\n{\n;\n}\n\n}\n" +
|
||||||
|
"void test2()\n{\nint j, i;\n{\n;\n}\n\n}\n" +
|
||||||
|
"int main()\n{\n;\n\nreturn 0;\n}\n", visitor.Builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FunctionDeclarationTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var a, b: boolean;
|
||||||
|
function func1(var a:integer; b:integer; c:real):integer;
|
||||||
|
begin
|
||||||
|
a := b + c;
|
||||||
|
func1 := a * 3;
|
||||||
|
end;
|
||||||
|
function func2(var a, b:boolean; c: array[0..6,3..8] of char):char;
|
||||||
|
begin
|
||||||
|
a := b and not b;
|
||||||
|
func2 := c[5,8];
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
ProgramStruct root = CompilerHelpers.Analyse(program);
|
||||||
|
SyntaxTreeTraveller traveller = new();
|
||||||
|
CCodeGenerateVisitor visitor = new();
|
||||||
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
|
string result = visitor.Builder.Build();
|
||||||
|
_output.WriteLine(result);
|
||||||
|
Assert.Equal("#include <stdbool.h>\n#include <stdio.h>\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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ namespace Canon.Tests.SemanticTests;
|
||||||
|
|
||||||
public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
public class TypeCheckVisitorTests(ITestOutputHelper testOutputHelper)
|
||||||
{
|
{
|
||||||
private readonly TestLogger _logger = new(testOutputHelper);
|
private readonly TestLogger<TypeCheckVisitor> _logger = new(testOutputHelper);
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ConstTypeTest()
|
public void ConstTypeTest()
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
using Canon.Core.Abstractions;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Canon.Tests.Utils;
|
namespace Canon.Tests.Utils;
|
||||||
|
|
||||||
public class TestLogger(ITestOutputHelper testOutputHelper) : ICompilerLogger
|
public class TestLogger<T>(ITestOutputHelper testOutputHelper) : ILogger<T>, IDisposable
|
||||||
{
|
{
|
||||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
|
||||||
Func<TState, Exception?, string> formatter)
|
Func<TState, Exception?, string> 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>(TState state) where TState : notnull
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user