feat: 针对C的代码生成 (#50)

Co-authored-by: Lan_G <2911328695@qq.com>
Reviewed-on: PostGuard/Canon#50
This commit is contained in:
jackfiled 2024-04-21 22:24:35 +08:00
parent 4353fb0c01
commit 3a584751dc
36 changed files with 906 additions and 31 deletions

View File

@ -1,4 +1,5 @@
using System.Text;
using Canon.Core.SemanticParser;
namespace Canon.Core.CodeGenerators;
@ -9,6 +10,11 @@ public class CCodeBuilder
{
private readonly StringBuilder _builder = new();
/// <summary>
/// 符号表
/// </summary>
public SymbolTable SymbolTable { get; } = new();
public void AddString(string code)
{
_builder.Append(code);

View File

@ -49,6 +49,16 @@ public abstract class SemanticToken : IEquatable<SemanticToken>
}
}
public T Convert<T>() where T : SemanticToken
{
if (this is T result)
{
return result;
}
throw new InvalidOperationException("Can not convert target type0");
}
/// <summary>
/// 栈底符号单例对象
/// </summary>

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +12,22 @@ public class AddOperator : NonTerminatedSyntaxNode
{
return new AddOperator { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
var operatorType = Children[0].Convert<TerminatedSyntaxNode>().Token.
Convert<OperatorSemanticToken>().OperatorType;
if (operatorType == OperatorType.Plus)
{
builder.AddString(" +");
}
else if (operatorType == OperatorType.Minus)
{
builder.AddString(" -");
}
else
{
builder.AddString(" ||");
}
}
}

View File

@ -1,4 +1,7 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +13,50 @@ public class BasicType : NonTerminatedSyntaxNode
{
return new BasicType { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
var keywordType = Children[0].Convert<TerminatedSyntaxNode>().Token
.Convert<KeywordSemanticToken>().KeywordType;
switch (keywordType)
{
case KeywordType.Integer:
builder.AddString(" int");
break;
case KeywordType.Real:
builder.AddString(" double");
break;
case KeywordType.Boolean:
builder.AddString(" bool");
break;
case KeywordType.Character:
builder.AddString(" char");
break;
}
}
/// <summary>
///尝试获取Pascal的基本类型
/// </summary>
/// <returns></returns>
public PascalType TryGetPascalType()
{
var keywordType = Children[0].Convert<TerminatedSyntaxNode>().Token
.Convert<KeywordSemanticToken>().KeywordType;
switch (keywordType)
{
case KeywordType.Integer:
return PascalBasicType.Integer;
case KeywordType.Real:
return PascalBasicType.Real;
case KeywordType.Boolean:
return PascalBasicType.Boolean;
case KeywordType.Character:
return PascalBasicType.Character;
}
return PascalBasicType.Void;
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -12,4 +13,16 @@ public class CompoundStatement : NonTerminatedSyntaxNode
{
return new CompoundStatement { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var statement in Statements.Reverse())
{
if (statement.Children.Count > 0)
{
statement.GenerateCCode(builder);
builder.AddString(";");
}
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -40,4 +41,38 @@ public class ConstDeclarations : NonTerminatedSyntaxNode
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var pair in ConstValues.Reverse())
{
builder.AddString(" const");
//获取常量类型
var token = pair.Item2.Children[0].Convert<TerminatedSyntaxNode>().Token;
var tokenType = token.TokenType;
if (tokenType == SemanticTokenType.Number)
{
if (token.Convert<NumberSemanticToken>().NumberType == NumberType.Integer)
{
builder.AddString(" int ");
}
else
{
builder.AddString(" double ");
}
}
else if (tokenType == SemanticTokenType.Character)
{
builder.AddString(" char ");
}
else
{
builder.AddString(" bool ");
}
builder.AddString(pair.Item1.IdentifierName + " =");
pair.Item2.GenerateCCode(builder);
builder.AddString(";");
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,24 @@ public class ConstValue : NonTerminatedSyntaxNode
{
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);
}
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,14 @@ 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(" }");
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,12 @@ public class Expression : NonTerminatedSyntaxNode
{
return new Expression { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)
{
child.GenerateCCode(builder);
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -51,4 +52,22 @@ public class ExpressionList : NonTerminatedSyntaxNode
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
//用逗号分隔输出的expression
using var enumerator = Expressions.GetEnumerator();
if (enumerator.MoveNext())
{
enumerator.Current.GenerateCCode(builder);
}
while (enumerator.MoveNext())
{
builder.AddString(", ");
enumerator.Current.GenerateCCode(builder);
}
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +12,56 @@ public class Factor : NonTerminatedSyntaxNode
{
return new Factor { Children = children };
}
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(")");
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -55,4 +56,20 @@ public class IdentifierList : NonTerminatedSyntaxNode
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
//用逗号分隔输出的expression
using var enumerator = Identifiers.Reverse().GetEnumerator();
if (enumerator.MoveNext())
{
builder.AddString(" " + enumerator.Current.IdentifierName);
}
while (enumerator.MoveNext())
{
builder.AddString(", " + enumerator.Current.IdentifierName);
}
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
@ -48,4 +50,5 @@ public class IdentifierVarPart : NonTerminatedSyntaxNode
return new IdentifierVarPart { Children = children, Exist = exist };
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +12,39 @@ public class MultiplyOperator : NonTerminatedSyntaxNode
{
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)
{
//实数除法需要将操作数强转为float
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(" /");
}
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -29,4 +30,26 @@ public class ProcedureCall : NonTerminatedSyntaxNode
yield return expression;
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
builder.AddString(ProcedureId.IdentifierName + "(");
//用逗号分隔输出的expression
using (var enumerator = Arguments.GetEnumerator())
{
if (enumerator.MoveNext())
{
enumerator.Current.GenerateCCode(builder);
}
while (enumerator.MoveNext())
{
builder.AddString(", ");
enumerator.Current.GenerateCCode(builder);
}
}
builder.AddString(")");
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -30,4 +31,17 @@ 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;}");
}
}

View File

@ -24,6 +24,7 @@ public class ProgramStruct : NonTerminatedSyntaxNode
public override void GenerateCCode(CCodeBuilder builder)
{
builder.AddString("#include <PascalCoreLib.h>");
builder.AddString("#include <PascalCoreLib.h> #include <stdbool.h>");
Body.GenerateCCode(builder);
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +12,31 @@ public class RelationOperator : NonTerminatedSyntaxNode
{
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;
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,12 @@ public class SimpleExpression : NonTerminatedSyntaxNode
{
return new SimpleExpression { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)
{
child.GenerateCCode(builder);
}
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +12,47 @@ public class Statement : NonTerminatedSyntaxNode
{
return new Statement { Children = children };
}
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("; }");
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -48,4 +49,16 @@ public class StatementList : NonTerminatedSyntaxNode
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var statement in Statements.Reverse())
{
if (statement.Children.Count > 0)
{
statement.GenerateCCode(builder);
builder.AddString(";");
}
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -20,4 +21,14 @@ 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("}");
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -25,4 +26,11 @@ public class SubprogramBody : NonTerminatedSyntaxNode
{
return new SubprogramBody() { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
ConstDeclarations.GenerateCCode(builder);
VarDeclarations.GenerateCCode(builder);
CompoundStatement.GenerateCCode(builder);
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -31,4 +32,12 @@ public class SubprogramDeclarations : NonTerminatedSyntaxNode
declarations = declarations.Children[0].Convert<SubprogramDeclarations>();
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var subprogram in Subprograms)
{
subprogram.GenerateCCode(builder);
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
@ -42,4 +43,28 @@ public class SubprogramHead : NonTerminatedSyntaxNode
return new SubprogramHead { Children = children, IsProcedure = isProcedure };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//可能要用到符号表
if (IsProcedure)
{
builder.AddString("void ");
}
else
{
//返回类型暂时未知
builder.AddString("int ");
}
builder.AddString(SubprogramName.LiteralValue);
builder.AddString("(");
foreach (var param in Parameters)
{
}
builder.AddString(")");
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,12 @@ public class Term : NonTerminatedSyntaxNode
{
return new Term { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var child in Children)
{
child.GenerateCCode(builder);
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -10,4 +11,18 @@ public class TypeSyntaxNode : NonTerminatedSyntaxNode
{
return new TypeSyntaxNode { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//type -> basic_type
if (Children.Count == 1)
{
Children[0].GenerateCCode(builder);
}
//type -> array [ period ]of basic_type
else
{
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -20,4 +21,10 @@ public class ValueParameter : NonTerminatedSyntaxNode
{
return new ValueParameter { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//可能涉及符号表访问
builder.AddString("valueParam ");
}
}

View File

@ -1,4 +1,6 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
@ -39,4 +41,49 @@ public class VarDeclarations : NonTerminatedSyntaxNode
}
}
}
public override void GenerateCCode(CCodeBuilder builder)
{
foreach (var pair in Variables.Reverse())
{
//BasicType定义
if (pair.Item2.Children.Count == 1)
{
//输出类型
pair.Item2.GenerateCCode(builder);
//输出idList
pair.Item1.GenerateCCode(builder);
builder.AddString(";");
}
//array定义
else
{
//构造出C语言形式的数组下标定义
string arrayPeriod = "";
var ranges = pair.Item2.Children[2]
.Convert<Period>().Ranges;
PascalType pascalType = pair.Item2.Children[5].Convert<BasicType>().TryGetPascalType();
foreach (var range in ranges)
{
int low = int.Parse(range.Item1.LiteralValue);
int high = int.Parse(range.Item2.LiteralValue);
arrayPeriod = "[" + System.Convert.ToString(high-low+1) + "]" + arrayPeriod;
pascalType = new PascalArrayType(pascalType, low, high); //嵌套地构造出多维数组
}
//依次定义每一个符号
foreach (var id in pair.Item1.Identifiers.Reverse())
{
pair.Item2.Children[5].GenerateCCode(builder);
builder.AddString(" " + id.IdentifierName + arrayPeriod + ";");
//写入符号表
builder.SymbolTable.TryAddSymbol(new Symbol()
{
SymbolName = id.IdentifierName, SymbolType = pascalType, Reference = false
});
}
}
}
}
}

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes;
@ -12,4 +13,9 @@ public class VarParameter : NonTerminatedSyntaxNode
{
return new VarParameter { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
ValueParameter.GenerateCCode(builder);
}
}

View File

@ -1,5 +1,7 @@
using Canon.Core.Enums;
using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
using Canon.Core.LexicalParser;
using Canon.Core.SemanticParser;
namespace Canon.Core.SyntaxNodes;
@ -17,4 +19,44 @@ public class Variable : NonTerminatedSyntaxNode
{
return new Variable { Children = children };
}
public override void GenerateCCode(CCodeBuilder builder)
{
//判断是否为引用变量
builder.SymbolTable.TryGetSymbol(Identifier.IdentifierName, out var symbol);
if (symbol is not null && symbol.Reference)
{
builder.AddString(" (*" + Identifier.IdentifierName + ")");
}
else
{
builder.AddString(" " + Identifier.IdentifierName);
}
//处理idVarPart数组下标部分
var idVarPart = Children[1].Convert<IdentifierVarPart>();
if (idVarPart.Exist)
{
PascalArrayType pascalArrayType = (PascalArrayType)symbol.SymbolType;
var positions = idVarPart.Positions;
foreach (var pos in positions.Reverse())
{
builder.AddString("[");
pos.GenerateCCode(builder);
//pascal下标减去左边界从而映射到C语言的下标
builder.AddString(" - " + System.Convert.ToString(pascalArrayType.Begin) + "]");
try
{
pascalArrayType = (PascalArrayType)pascalArrayType.ElementType;
}
catch (InvalidCastException e)
{
//do nothing
//因为最后一层嵌套类型必然不是PascalArrayType, 而是BasicType
}
}
}
}
}

View File

@ -4,6 +4,7 @@ using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.GeneratedParserTests;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
@ -11,6 +12,12 @@ public class BasicTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public BasicTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void ProgramStructTest()
@ -28,6 +35,8 @@ public class BasicTests
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
Assert.Equal("#include <PascalCoreLib.h>", builder.Build());
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0;}", result);
}
}

View File

@ -0,0 +1,90 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.GeneratedParserTests;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class DeclarationsTest
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public DeclarationsTest(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void VarDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
var a, b, c, d: integer; m, n: real; k: boolean;
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> char a; int main(){statement; return 0; }", result);
}
[Fact]
public void ConstDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
const a = 1; b = 2; c = 3; d = 2.5;
begin
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0; }", result);
}
[Fact]
public void ArrayDeclarationsTest()
{
CCodeBuilder builder = new();
const string program = """
program arrayTest;
var a, b, c: array[1..6,5..8] of integer;
d: integer;
begin
a[2,3] := 10086;
d:=6;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> int main(){statement; return 0; }", result);
}
}

View File

@ -0,0 +1,47 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.GeneratedParserTests;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class ExpressionTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public ExpressionTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void ExpressionTest1()
{
CCodeBuilder builder = new();
const string program = """
program varTest;
var a, b, c, d: integer; m, n: real; k: boolean;
begin
a := 1;
b := a + 6 * 9 + (a + 9) * 1 - (4 + a) * 5 / 1;
m := b / 3;
d := 9 mod 1;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> char a; int main(){statement; return 0; }", result);
}
}

View File

@ -0,0 +1,111 @@
using Canon.Core.Abstractions;
using Canon.Core.CodeGenerators;
using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes;
using Canon.Tests.GeneratedParserTests;
using Canon.Tests.Utils;
using Xunit.Abstractions;
namespace Canon.Tests.CCodeGeneratorTests;
public class StatementTests
{
private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
private readonly ILexer _lexer = new Lexer();
private readonly ITestOutputHelper _outputHelper;
public StatementTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void VariableAssignTest()
{
CCodeBuilder builder = new();
const string program = """
program varAssignTest;
var a, b: integer;
begin
a := 1;
b := a;
a := b;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> " +
"int a, b; int main(){ a = 1; b = a; a = b; return 0;}", result);
}
[Fact]
public void IfTest()
{
CCodeBuilder builder = new();
const string program = """
program main;
var
a,b:integer;
begin
if a = 5 then
begin
if b = 3 then
b := b + 1
else
b := b + 2
end
else
a := 2
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> int a, b; " +
"int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result);
}
[Fact]
public void ForLoopTest()
{
CCodeBuilder builder = new();
const string program = """
program ForLoopTest;
var a, b, c: integer;
begin
b := 1;
for a := b * 5 + 1 to 99 do
begin
c := a + 1;
b := a mod (a + c);
b := a + 1 - 1 * 1;
end;
end.
""";
IEnumerable<SemanticToken> tokens = _lexer.Tokenize(new StringSourceReader(program));
ProgramStruct root = _parser.Analyse(tokens);
root.GenerateCCode(builder);
string result = builder.Build();
_outputHelper.WriteLine(result);
Assert.Equal("#include <PascalCoreLib.h> #include <stdbool.h> int a, b; " +
"int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result);
}
}