feat: 针对C的代码生成 (#50)
Co-authored-by: Lan_G <2911328695@qq.com> Reviewed-on: PostGuard/Canon#50
This commit is contained in:
parent
4353fb0c01
commit
3a584751dc
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(" ||");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(" }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(" /");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Canon.Core.Enums;
|
||||
using Canon.Core.CodeGenerators;
|
||||
using Canon.Core.Enums;
|
||||
using Canon.Core.LexicalParser;
|
||||
|
||||
namespace Canon.Core.SyntaxNodes;
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("; }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
90
Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs
Normal file
90
Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs
Normal 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);
|
||||
}
|
||||
}
|
47
Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs
Normal file
47
Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs
Normal 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);
|
||||
}
|
||||
}
|
111
Canon.Tests/CCodeGeneratorTests/StatementTests.cs
Normal file
111
Canon.Tests/CCodeGeneratorTests/StatementTests.cs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user