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); + } +}