From c44b720e09bd20284d9d4b365e11e60510ec1936 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 17 Aug 2024 20:29:57 +0800 Subject: [PATCH] add: for & if & while support. --- CanonSharp.Pascal/Parser/GrammarParser.cs | 47 ++++++++++++- CanonSharp.Pascal/SyntaxTree/ForNode.cs | 20 ++++++ CanonSharp.Pascal/SyntaxTree/IfNode.cs | 26 +++++++ .../SyntaxTree/SyntaxNodeBase.cs | 3 + CanonSharp.Pascal/SyntaxTree/WhileNode.cs | 10 +++ .../ParserTests/ProgramParserTests.cs | 15 ++++ .../ParserTests/StatementParserTests.cs | 68 +++++++++++++++++++ 7 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 CanonSharp.Pascal/SyntaxTree/ForNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/IfNode.cs create mode 100644 CanonSharp.Pascal/SyntaxTree/WhileNode.cs diff --git a/CanonSharp.Pascal/Parser/GrammarParser.cs b/CanonSharp.Pascal/Parser/GrammarParser.cs index 4354f32..2ce60b2 100644 --- a/CanonSharp.Pascal/Parser/GrammarParser.cs +++ b/CanonSharp.Pascal/Parser/GrammarParser.cs @@ -150,13 +150,56 @@ public sealed class GrammarParser : GrammarParserBuilder ); } + public static IParser IfParser() + { + IParser commonPart = from _ in Keyword("if") + from condition in ExpressionParser() + from _1 in Keyword("then") + from statement in StatementParser() + select new IfNode(condition, statement); + + return Choice( + from common in commonPart + from _ in Keyword("else") + from elseStatement in StatementParser() + select new IfNode(common.Condition, common.Statement, elseStatement), + commonPart + ); + } + + public static IParser ForParser() + { + return from _ in Keyword("for") + from identifier in IdentifierParser() + from _1 in Operator(":=") + from left in ExpressionParser() + from _2 in Keyword("to") + from right in ExpressionParser() + from _3 in Keyword("do") + from statement in StatementParser() + select new ForNode(identifier, left, right, statement); + } + + public static IParser WhileParser() + { + return from _ in Keyword("while") + from condition in ExpressionParser() + from _1 in Keyword("do") + from statement in StatementParser() + select new WhileNode(condition, statement); + } + public static IParser StatementParser() { - return Choice( + return Choice( from variable in VariableParser() from _ in Operator(":=") from expression in ExpressionParser() - select new AssignNode(variable, expression) + select new AssignNode(variable, expression), + IfParser(), + ForParser(), + WhileParser(), + CompoundStatementParser() ); } diff --git a/CanonSharp.Pascal/SyntaxTree/ForNode.cs b/CanonSharp.Pascal/SyntaxTree/ForNode.cs new file mode 100644 index 0000000..50be740 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/ForNode.cs @@ -0,0 +1,20 @@ +using CanonSharp.Pascal.Scanner; + +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class ForNode( + LexicalToken identifier, + SyntaxNodeBase leftCondition, + SyntaxNodeBase rightCondition, + SyntaxNodeBase statement) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.For; + + public LexicalToken Identifier => identifier; + + public SyntaxNodeBase LeftCondition => leftCondition; + + public SyntaxNodeBase RightCondition => rightCondition; + + public SyntaxNodeBase Statement => statement; +} diff --git a/CanonSharp.Pascal/SyntaxTree/IfNode.cs b/CanonSharp.Pascal/SyntaxTree/IfNode.cs new file mode 100644 index 0000000..4019e37 --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/IfNode.cs @@ -0,0 +1,26 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class IfNode : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.If; + + public SyntaxNodeBase Condition { get; } + + public SyntaxNodeBase Statement { get; } + + public SyntaxNodeBase? ElseStatement { get; } + + public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement) + { + Condition = condition; + Statement = statement; + ElseStatement = null; + } + + public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement, SyntaxNodeBase elseStatement) + { + Condition = condition; + Statement = statement; + ElseStatement = elseStatement; + } +} diff --git a/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs b/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs index 4c79926..31e412d 100644 --- a/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs +++ b/CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs @@ -14,6 +14,9 @@ public enum SyntaxNodeType Block, Constant, VariableDeclaration, + If, + While, + For, ProgramBody, ProgramHead, Program diff --git a/CanonSharp.Pascal/SyntaxTree/WhileNode.cs b/CanonSharp.Pascal/SyntaxTree/WhileNode.cs new file mode 100644 index 0000000..c3df1da --- /dev/null +++ b/CanonSharp.Pascal/SyntaxTree/WhileNode.cs @@ -0,0 +1,10 @@ +namespace CanonSharp.Pascal.SyntaxTree; + +public sealed class WhileNode(SyntaxNodeBase condition, SyntaxNodeBase statement) : SyntaxNodeBase +{ + public override SyntaxNodeType NodeType => SyntaxNodeType.While; + + public SyntaxNodeBase Condition => condition; + + public SyntaxNodeBase Statement => statement; +} diff --git a/CanonSharp.Tests/ParserTests/ProgramParserTests.cs b/CanonSharp.Tests/ParserTests/ProgramParserTests.cs index 8a9fa92..03d21c5 100644 --- a/CanonSharp.Tests/ParserTests/ProgramParserTests.cs +++ b/CanonSharp.Tests/ParserTests/ProgramParserTests.cs @@ -138,6 +138,21 @@ public class ProgramParserTests : GrammarParserTestBase a[10,0] := 1; end. """)] + [InlineData(""" + program main; + begin + begin + end + end. + """)] + [InlineData(""" + program main; + var i : integer; + begin + while i < 10 do + i := i + 1; + end. + """)] public void ProgramParseTest(string input) { ProgramParse(input); diff --git a/CanonSharp.Tests/ParserTests/StatementParserTests.cs b/CanonSharp.Tests/ParserTests/StatementParserTests.cs index 80f6128..b54ef27 100644 --- a/CanonSharp.Tests/ParserTests/StatementParserTests.cs +++ b/CanonSharp.Tests/ParserTests/StatementParserTests.cs @@ -15,6 +15,74 @@ public class StatementParserTests : GrammarParserTestBase Assert.Equal(1, node.Expression.Convert().Value); } + [Fact] + public void IfTest1() + { + IfNode node = RunParser(GrammarParser.IfParser(), """ + if i = 1 then + a := a + 1 + else + a := a + 2 + """); + Assert.NotNull(node.ElseStatement); + + BinaryOperatorNode condition = node.Condition.Convert(); + Assert.Equal(BinaryOperatorType.Equal, condition.OperatorType); + + AssignNode statement = node.Statement.Convert(); + Assert.Equal("a", statement.Variable.Identifier.LiteralValue); + + AssignNode elseStatement = node.Statement.Convert(); + Assert.Equal("a", elseStatement.Variable.Identifier.LiteralValue); + } + + [Fact] + public void IfTest2() + { + IfNode node = RunParser(GrammarParser.IfParser(), """ + if i = 1 then + if i = 2 then + a := a + 1 + else + a := a + 2 + """); + + Assert.Null(node.ElseStatement); + + IfNode subIfNode = node.Statement.Convert(); + Assert.NotNull(subIfNode.ElseStatement); + } + + [Fact] + public void ForTest1() + { + ForNode node = RunParser(GrammarParser.ForParser(), """ + for i := 1 to 10 do + a := a + i + """); + Assert.Equal("i", node.Identifier.LiteralValue); + Assert.Equal(1, node.LeftCondition.Convert().Value); + Assert.Equal(10, node.RightCondition.Convert().Value); + + AssignNode assignNode = node.Statement.Convert(); + Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue); + } + + [Fact] + public void WhileTest1() + { + WhileNode node = RunParser(GrammarParser.WhileParser(), """ + while c >= 1 do + a := a + 1; + """); + + BinaryOperatorNode binaryOperatorNode = node.Condition.Convert(); + Assert.Equal(BinaryOperatorType.GreaterEqual, binaryOperatorNode.OperatorType); + + AssignNode assignNode = node.Statement.Convert(); + Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue); + } + [Theory] [InlineData(""" begin