diff --git a/Canon.Core/CodeGenerators/CCodeBuilder.cs b/Canon.Core/CodeGenerators/CCodeBuilder.cs
index ddff4c2..bc6e652 100644
--- a/Canon.Core/CodeGenerators/CCodeBuilder.cs
+++ b/Canon.Core/CodeGenerators/CCodeBuilder.cs
@@ -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();
+ ///
+ /// 符号表
+ ///
+ public SymbolTable SymbolTable { get; } = new();
+
public void AddString(string code)
{
_builder.Append(code);
diff --git a/Canon.Core/LexicalParser/SemanticToken.cs b/Canon.Core/LexicalParser/SemanticToken.cs
index f1d6b82..958e8df 100644
--- a/Canon.Core/LexicalParser/SemanticToken.cs
+++ b/Canon.Core/LexicalParser/SemanticToken.cs
@@ -49,6 +49,16 @@ public abstract class SemanticToken : IEquatable
}
}
+ public T Convert() where T : SemanticToken
+ {
+ if (this is T result)
+ {
+ return result;
+ }
+
+ throw new InvalidOperationException("Can not convert target type0");
+ }
+
///
/// 栈底符号单例对象
///
diff --git a/Canon.Core/SyntaxNodes/AddOperator.cs b/Canon.Core/SyntaxNodes/AddOperator.cs
index 3253be4..88a3320 100644
--- a/Canon.Core/SyntaxNodes/AddOperator.cs
+++ b/Canon.Core/SyntaxNodes/AddOperator.cs
@@ -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().Token.
+ Convert().OperatorType;
+ if (operatorType == OperatorType.Plus)
+ {
+ builder.AddString(" +");
+ }
+ else if (operatorType == OperatorType.Minus)
+ {
+ builder.AddString(" -");
+ }
+ else
+ {
+ builder.AddString(" ||");
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/BasicType.cs b/Canon.Core/SyntaxNodes/BasicType.cs
index aff154c..77899ea 100644
--- a/Canon.Core/SyntaxNodes/BasicType.cs
+++ b/Canon.Core/SyntaxNodes/BasicType.cs
@@ -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().Token
+ .Convert().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;
+ }
+ }
+
+ ///
+ ///尝试获取Pascal的基本类型
+ ///
+ ///
+ public PascalType TryGetPascalType()
+ {
+ var keywordType = Children[0].Convert().Token
+ .Convert().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;
+ }
}
diff --git a/Canon.Core/SyntaxNodes/CompoundStatement.cs b/Canon.Core/SyntaxNodes/CompoundStatement.cs
index 9dc777e..1b77be8 100644
--- a/Canon.Core/SyntaxNodes/CompoundStatement.cs
+++ b/Canon.Core/SyntaxNodes/CompoundStatement.cs
@@ -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(";");
+ }
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ConstDeclarations.cs b/Canon.Core/SyntaxNodes/ConstDeclarations.cs
index 8b14e57..ec73030 100644
--- a/Canon.Core/SyntaxNodes/ConstDeclarations.cs
+++ b/Canon.Core/SyntaxNodes/ConstDeclarations.cs
@@ -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().Token;
+ var tokenType = token.TokenType;
+ if (tokenType == SemanticTokenType.Number)
+ {
+ if (token.Convert().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(";");
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ConstValue.cs b/Canon.Core/SyntaxNodes/ConstValue.cs
index d06ba3c..6a45034 100644
--- a/Canon.Core/SyntaxNodes/ConstValue.cs
+++ b/Canon.Core/SyntaxNodes/ConstValue.cs
@@ -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().Token;
+ //constValue -> 'letter'
+ if (token.TokenType == SemanticTokenType.Character)
+ {
+ builder.AddString(" '" + token.LiteralValue + "'");
+ }
+ else
+ {
+ builder.AddString(" ");
+ // constValue -> +num | -num | num
+ foreach (var c in Children)
+ {
+ builder.AddString(c.Convert().Token.LiteralValue);
+ }
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ElsePart.cs b/Canon.Core/SyntaxNodes/ElsePart.cs
index 67937fa..ffde0c4 100644
--- a/Canon.Core/SyntaxNodes/ElsePart.cs
+++ b/Canon.Core/SyntaxNodes/ElsePart.cs
@@ -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(" }");
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Expression.cs b/Canon.Core/SyntaxNodes/Expression.cs
index b8aa742..cd5fb1d 100644
--- a/Canon.Core/SyntaxNodes/Expression.cs
+++ b/Canon.Core/SyntaxNodes/Expression.cs
@@ -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);
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ExpressionList.cs b/Canon.Core/SyntaxNodes/ExpressionList.cs
index 4c76cfb..eed197b 100644
--- a/Canon.Core/SyntaxNodes/ExpressionList.cs
+++ b/Canon.Core/SyntaxNodes/ExpressionList.cs
@@ -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);
+ }
+
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Factor.cs b/Canon.Core/SyntaxNodes/Factor.cs
index ebf2597..da7e977 100644
--- a/Canon.Core/SyntaxNodes/Factor.cs
+++ b/Canon.Core/SyntaxNodes/Factor.cs
@@ -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().Token;
+ if (token.TokenType == SemanticTokenType.Number)
+ {
+ builder.AddString(" " + token.LiteralValue);
+ }
+ }
+ // factor -> variable
+ else
+ {
+ Children[0].GenerateCCode(builder);
+ }
+ }
+ //factor -> ( expression )
+ else if (Children.Count == 3)
+ {
+ builder.AddString(" (");
+ Children[1].GenerateCCode(builder);
+ builder.AddString(")");
+ }
+ //factor -> id ( expression )
+ else if (Children.Count == 4)
+ {
+ builder.AddString(" " + Children[0].Convert().Token.
+ Convert().IdentifierName);
+ builder.AddString("(");
+ Children[2].GenerateCCode(builder);
+ builder.AddString(")");
+ }
+ else
+ { //factor -> not factor
+ builder.AddString(" (");
+ if (Children[0].Convert().Token.TokenType == SemanticTokenType.Keyword)
+ {
+ builder.AddString("!");
+ }
+ else
+ {
+ builder.AddString("-");
+ }
+
+ Children[1].GenerateCCode(builder);
+ builder.AddString(")");
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/IdentifierList.cs b/Canon.Core/SyntaxNodes/IdentifierList.cs
index 2bfb48a..5e23da0 100644
--- a/Canon.Core/SyntaxNodes/IdentifierList.cs
+++ b/Canon.Core/SyntaxNodes/IdentifierList.cs
@@ -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);
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs
index 8af4e74..d2619b9 100644
--- a/Canon.Core/SyntaxNodes/IdentifierVarPart.cs
+++ b/Canon.Core/SyntaxNodes/IdentifierVarPart.cs
@@ -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 };
}
+
}
diff --git a/Canon.Core/SyntaxNodes/MultiplyOperator.cs b/Canon.Core/SyntaxNodes/MultiplyOperator.cs
index c789130..f0793ed 100644
--- a/Canon.Core/SyntaxNodes/MultiplyOperator.cs
+++ b/Canon.Core/SyntaxNodes/MultiplyOperator.cs
@@ -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().Token;
+ if (token.TokenType == SemanticTokenType.Operator)
+ {
+ var operatorType = token.Convert().OperatorType;
+ if (operatorType == OperatorType.Multiply)
+ {
+ builder.AddString(" *");
+ }
+ else if (operatorType == OperatorType.Divide)
+ {
+ //实数除法,需要将操作数强转为float
+ builder.AddString(" /(double)");
+ }
+ }
+ else
+ {
+ var keywordType = token.Convert().KeywordType;
+ if (keywordType == KeywordType.And)
+ {
+ builder.AddString(" &&");
+ }
+ else if (keywordType == KeywordType.Mod)
+ {
+ builder.AddString(" %");
+ }
+ else
+ {
+ builder.AddString(" /");
+ }
+ }
+
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Period.cs b/Canon.Core/SyntaxNodes/Period.cs
index 21d0cd6..6ddbdf2 100644
--- a/Canon.Core/SyntaxNodes/Period.cs
+++ b/Canon.Core/SyntaxNodes/Period.cs
@@ -1,4 +1,5 @@
-using Canon.Core.Enums;
+using Canon.Core.CodeGenerators;
+using Canon.Core.Enums;
using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes;
diff --git a/Canon.Core/SyntaxNodes/ProcedureCall.cs b/Canon.Core/SyntaxNodes/ProcedureCall.cs
index aa5081c..685b586 100644
--- a/Canon.Core/SyntaxNodes/ProcedureCall.cs
+++ b/Canon.Core/SyntaxNodes/ProcedureCall.cs
@@ -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(")");
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ProgramBody.cs b/Canon.Core/SyntaxNodes/ProgramBody.cs
index 1684303..ad576e0 100644
--- a/Canon.Core/SyntaxNodes/ProgramBody.cs
+++ b/Canon.Core/SyntaxNodes/ProgramBody.cs
@@ -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;}");
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ProgramStruct.cs b/Canon.Core/SyntaxNodes/ProgramStruct.cs
index 25abdc6..ed0922d 100644
--- a/Canon.Core/SyntaxNodes/ProgramStruct.cs
+++ b/Canon.Core/SyntaxNodes/ProgramStruct.cs
@@ -24,6 +24,7 @@ public class ProgramStruct : NonTerminatedSyntaxNode
public override void GenerateCCode(CCodeBuilder builder)
{
- builder.AddString("#include ");
+ builder.AddString("#include #include ");
+ Body.GenerateCCode(builder);
}
}
diff --git a/Canon.Core/SyntaxNodes/RelationOperator.cs b/Canon.Core/SyntaxNodes/RelationOperator.cs
index 4b6bc7b..382049f 100644
--- a/Canon.Core/SyntaxNodes/RelationOperator.cs
+++ b/Canon.Core/SyntaxNodes/RelationOperator.cs
@@ -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().Token.
+ Convert().OperatorType;
+ switch (operatorType)
+ {
+ case OperatorType.Equal:
+ builder.AddString(" ==");
+ break;
+ case OperatorType.Greater:
+ builder.AddString(" >");
+ break;
+ case OperatorType.Less:
+ builder.AddString(" <");
+ break;
+ case OperatorType.GreaterEqual:
+ builder.AddString(" >=");
+ break;
+ case OperatorType.LessEqual:
+ builder.AddString(" <=");
+ break;
+ case OperatorType.NotEqual:
+ builder.AddString(" !=");
+ break;
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/SimpleExpression.cs b/Canon.Core/SyntaxNodes/SimpleExpression.cs
index 8c9fd81..635c56c 100644
--- a/Canon.Core/SyntaxNodes/SimpleExpression.cs
+++ b/Canon.Core/SyntaxNodes/SimpleExpression.cs
@@ -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);
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Statement.cs b/Canon.Core/SyntaxNodes/Statement.cs
index e347140..4f085b6 100644
--- a/Canon.Core/SyntaxNodes/Statement.cs
+++ b/Canon.Core/SyntaxNodes/Statement.cs
@@ -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().Token.Convert()
+ .IdentifierName;
+ builder.AddString(" for(" + idName + " =");
+ Children[3].GenerateCCode(builder);
+ builder.AddString("; " + idName + " <=");
+ Children[5].GenerateCCode(builder);
+ builder.AddString("; " + idName + "++){");
+ Children[7].GenerateCCode(builder);
+ builder.AddString("; }");
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/StatementList.cs b/Canon.Core/SyntaxNodes/StatementList.cs
index 740cc22..ff020a6 100644
--- a/Canon.Core/SyntaxNodes/StatementList.cs
+++ b/Canon.Core/SyntaxNodes/StatementList.cs
@@ -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(";");
+ }
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Subprogram.cs b/Canon.Core/SyntaxNodes/Subprogram.cs
index a8f29bf..12728c6 100644
--- a/Canon.Core/SyntaxNodes/Subprogram.cs
+++ b/Canon.Core/SyntaxNodes/Subprogram.cs
@@ -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("}");
+ }
}
diff --git a/Canon.Core/SyntaxNodes/SubprogramBody.cs b/Canon.Core/SyntaxNodes/SubprogramBody.cs
index 16cad3a..19503ae 100644
--- a/Canon.Core/SyntaxNodes/SubprogramBody.cs
+++ b/Canon.Core/SyntaxNodes/SubprogramBody.cs
@@ -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);
+ }
}
diff --git a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs
index b6932bb..7fe6cdc 100644
--- a/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs
+++ b/Canon.Core/SyntaxNodes/SubprogramDeclarations.cs
@@ -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();
}
}
+
+ public override void GenerateCCode(CCodeBuilder builder)
+ {
+ foreach (var subprogram in Subprograms)
+ {
+ subprogram.GenerateCCode(builder);
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/SubprogramHead.cs b/Canon.Core/SyntaxNodes/SubprogramHead.cs
index 28a78ed..ad720a7 100644
--- a/Canon.Core/SyntaxNodes/SubprogramHead.cs
+++ b/Canon.Core/SyntaxNodes/SubprogramHead.cs
@@ -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(")");
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Term.cs b/Canon.Core/SyntaxNodes/Term.cs
index a18f894..914c6d2 100644
--- a/Canon.Core/SyntaxNodes/Term.cs
+++ b/Canon.Core/SyntaxNodes/Term.cs
@@ -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);
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs
index 9aa978f..b1fec76 100644
--- a/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs
+++ b/Canon.Core/SyntaxNodes/TypeSyntaxNode.cs
@@ -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
+ {
+
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/ValueParameter.cs b/Canon.Core/SyntaxNodes/ValueParameter.cs
index 5ccaa36..af2d8bf 100644
--- a/Canon.Core/SyntaxNodes/ValueParameter.cs
+++ b/Canon.Core/SyntaxNodes/ValueParameter.cs
@@ -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 ");
+ }
}
diff --git a/Canon.Core/SyntaxNodes/VarDeclarations.cs b/Canon.Core/SyntaxNodes/VarDeclarations.cs
index 0c70405..3574770 100644
--- a/Canon.Core/SyntaxNodes/VarDeclarations.cs
+++ b/Canon.Core/SyntaxNodes/VarDeclarations.cs
@@ -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().Ranges;
+ PascalType pascalType = pair.Item2.Children[5].Convert().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
+ });
+ }
+ }
+ }
+ }
}
diff --git a/Canon.Core/SyntaxNodes/VarParameter.cs b/Canon.Core/SyntaxNodes/VarParameter.cs
index 75bada5..08c700e 100644
--- a/Canon.Core/SyntaxNodes/VarParameter.cs
+++ b/Canon.Core/SyntaxNodes/VarParameter.cs
@@ -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);
+ }
}
diff --git a/Canon.Core/SyntaxNodes/Variable.cs b/Canon.Core/SyntaxNodes/Variable.cs
index 24d1617..608eb11 100644
--- a/Canon.Core/SyntaxNodes/Variable.cs
+++ b/Canon.Core/SyntaxNodes/Variable.cs
@@ -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();
+ 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
+ }
+ }
+ }
+ }
}
diff --git a/Canon.Tests/CCodeGeneratorTests/BasicTests.cs b/Canon.Tests/CCodeGeneratorTests/BasicTests.cs
index 0a291b0..2a59105 100644
--- a/Canon.Tests/CCodeGeneratorTests/BasicTests.cs
+++ b/Canon.Tests/CCodeGeneratorTests/BasicTests.cs
@@ -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 ", builder.Build());
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include int main(){statement; return 0;}", result);
}
}
diff --git a/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs b/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs
new file mode 100644
index 0000000..2295ab8
--- /dev/null
+++ b/Canon.Tests/CCodeGeneratorTests/DeclarationsTests.cs
@@ -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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include 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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include 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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include int main(){statement; return 0; }", result);
+ }
+}
diff --git a/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs b/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs
new file mode 100644
index 0000000..e43f122
--- /dev/null
+++ b/Canon.Tests/CCodeGeneratorTests/ExpressionTests.cs
@@ -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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include char a; int main(){statement; return 0; }", result);
+ }
+}
diff --git a/Canon.Tests/CCodeGeneratorTests/StatementTests.cs b/Canon.Tests/CCodeGeneratorTests/StatementTests.cs
new file mode 100644
index 0000000..d12f229
--- /dev/null
+++ b/Canon.Tests/CCodeGeneratorTests/StatementTests.cs
@@ -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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include #include " +
+ "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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include #include 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 tokens = _lexer.Tokenize(new StringSourceReader(program));
+
+ ProgramStruct root = _parser.Analyse(tokens);
+ root.GenerateCCode(builder);
+
+ string result = builder.Build();
+ _outputHelper.WriteLine(result);
+ Assert.Equal("#include #include int a, b; " +
+ "int main(){ a = 1; if( a == 1){ a = a + 1; } else{ b = a + 2 }; return 0;}", result);
+ }
+}