add: basic grammar parser including expression and program.
This commit is contained in:
		
							
								
								
									
										201
									
								
								CanonSharp.Pascal/Parser/GrammarParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								CanonSharp.Pascal/Parser/GrammarParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | |||||||
|  | using CanonSharp.Combinator.Abstractions; | ||||||
|  | using CanonSharp.Combinator.Extensions; | ||||||
|  | using static CanonSharp.Combinator.ParserBuilder; | ||||||
|  | using CanonSharp.Pascal.Scanner; | ||||||
|  | using CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Pascal.Parser; | ||||||
|  |  | ||||||
|  | public sealed class GrammarParser : GrammarParserBuilder | ||||||
|  | { | ||||||
|  |     public static IParser<LexicalToken, SyntaxNodeBase> FactorParser() | ||||||
|  |     { | ||||||
|  |         // factor -> true | false | num | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> trueParser = from _ in Keyword("true") | ||||||
|  |             select new BooleanValueNode(true); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> falseParser = from _ in Keyword("false") | ||||||
|  |             select new BooleanValueNode(false); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> integerParser = | ||||||
|  |             from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstInteger) | ||||||
|  |             select new IntegerValueNode(int.Parse(token.LiteralValue)); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> floatParser = | ||||||
|  |             from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstFloat) | ||||||
|  |             select new FloatValueNode(double.Parse(token.LiteralValue)); | ||||||
|  |  | ||||||
|  |         // factor -> - factor | + factor | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> minusParser = | ||||||
|  |             from _ in Operator("-") | ||||||
|  |             from node in FactorParser() | ||||||
|  |             select new UnaryOperatorNode(UnaryOperatorType.Minus, node); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> plusParser = | ||||||
|  |             from _ in Operator("+") | ||||||
|  |             from node in FactorParser() | ||||||
|  |             select new UnaryOperatorNode(UnaryOperatorType.Plus, node); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> notParser = | ||||||
|  |             from _ in Keyword("not") | ||||||
|  |             from node in FactorParser() | ||||||
|  |             select new UnaryOperatorNode(UnaryOperatorType.Not, node); | ||||||
|  |  | ||||||
|  |         IParser<LexicalToken, SyntaxNodeBase> parenthesisParser = | ||||||
|  |             from _1 in Delimiter("(") | ||||||
|  |             from node in ExpressionParser() | ||||||
|  |             from _2 in Delimiter(")") | ||||||
|  |             select node; | ||||||
|  |  | ||||||
|  |         return Choice( | ||||||
|  |             trueParser, | ||||||
|  |             falseParser, | ||||||
|  |             integerParser, | ||||||
|  |             floatParser, | ||||||
|  |             minusParser, | ||||||
|  |             plusParser, | ||||||
|  |             notParser, | ||||||
|  |             VariableParser(), | ||||||
|  |             parenthesisParser | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static IParser<LexicalToken, SyntaxNodeBase> TermRecursively(SyntaxNodeBase left) | ||||||
|  |     { | ||||||
|  |         // MultiplyOperator -> * | / | div | mod | and | ||||||
|  |         IParser<LexicalToken, BinaryOperatorType> multiplyOperator = Choice( | ||||||
|  |             from _ in Operator("*") | ||||||
|  |             select BinaryOperatorType.Multiply, | ||||||
|  |             from _ in Operator("/") | ||||||
|  |             select BinaryOperatorType.Divide, | ||||||
|  |             from _ in Keyword("div") | ||||||
|  |             select BinaryOperatorType.IntegerDivide, | ||||||
|  |             from _ in Keyword("mod") | ||||||
|  |             select BinaryOperatorType.Mod, | ||||||
|  |             from _ in Keyword("and") | ||||||
|  |             select BinaryOperatorType.And); | ||||||
|  |  | ||||||
|  |         return multiplyOperator.Next(op => | ||||||
|  |         { | ||||||
|  |             return FactorParser().Map(right => new BinaryOperatorNode(op, left, right)) | ||||||
|  |                 .Bind(TermRecursively); | ||||||
|  |         }, left); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, SyntaxNodeBase> TermParser() | ||||||
|  |     { | ||||||
|  |         // Term -> Factor | Term MultiplyOperator Factor | ||||||
|  |         // 消除左递归为 | ||||||
|  |         // Term -> Factor Term' | ||||||
|  |         // Term' -> MultiplyOperator Factor Term' | ε | ||||||
|  |         return FactorParser().Bind(TermRecursively); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static IParser<LexicalToken, SyntaxNodeBase> SimpleExpressionRecursively(SyntaxNodeBase left) | ||||||
|  |     { | ||||||
|  |         // AddOperator -> + | - | or | ||||||
|  |         IParser<LexicalToken, BinaryOperatorType> addOperator = Choice( | ||||||
|  |             from _ in Operator("+") | ||||||
|  |             select BinaryOperatorType.Add, | ||||||
|  |             from _ in Operator("-") | ||||||
|  |             select BinaryOperatorType.Subtract, | ||||||
|  |             from _ in Keyword("or") | ||||||
|  |             select BinaryOperatorType.Or); | ||||||
|  |  | ||||||
|  |         return addOperator.Next(op => | ||||||
|  |         { | ||||||
|  |             return TermParser().Map(right => new BinaryOperatorNode(op, left, right)) | ||||||
|  |                 .Bind(SimpleExpressionRecursively); | ||||||
|  |         }, left); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, SyntaxNodeBase> SimpleExpressionParser() | ||||||
|  |     { | ||||||
|  |         // SimpleExpression -> Term | SimpleExpression AddOperator Term | ||||||
|  |         // 消除左递归为 | ||||||
|  |         // SimpleExpression -> Term SimpleExpression' | ||||||
|  |         // SimpleExpression' -> AddOperator Term SimpleExpression' | ε | ||||||
|  |         return TermParser().Bind(SimpleExpressionRecursively); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, SyntaxNodeBase> ExpressionParser() | ||||||
|  |     { | ||||||
|  |         // RelationOperator -> = | <> | < | <= | > | >= | ||||||
|  |         IParser<LexicalToken, BinaryOperatorType> relationOperator = Choice( | ||||||
|  |             from _ in Operator("=") | ||||||
|  |             select BinaryOperatorType.Equal, | ||||||
|  |             from _ in Operator("<>") | ||||||
|  |             select BinaryOperatorType.NotEqual, | ||||||
|  |             from _ in Operator("<") | ||||||
|  |             select BinaryOperatorType.Less, | ||||||
|  |             from _ in Operator("<=") | ||||||
|  |             select BinaryOperatorType.LessEqual, | ||||||
|  |             from _ in Operator(">") | ||||||
|  |             select BinaryOperatorType.Greater, | ||||||
|  |             from _ in Operator(">=") | ||||||
|  |             select BinaryOperatorType.GreaterEqual); | ||||||
|  |  | ||||||
|  |         // Expression -> SimpleExpression | SimpleExpression RelationOperator SimpleExpression | ||||||
|  |         return Choice( | ||||||
|  |             from left in SimpleExpressionParser() | ||||||
|  |             from op in relationOperator | ||||||
|  |             from right in SimpleExpressionParser() | ||||||
|  |             select new BinaryOperatorNode(op, left, right), | ||||||
|  |             SimpleExpressionParser() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// ExpressionList Parser | ||||||
|  |     /// ExpressionList -> Expression | ExpressionList , Expression | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public static IParser<LexicalToken, IEnumerable<SyntaxNodeBase>> ExpressionsParser() | ||||||
|  |         => ExpressionParser().SeparatedBy1(Delimiter(",")); | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, VariableNode> VariableParser() | ||||||
|  |     { | ||||||
|  |         return from token in Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Identifier) | ||||||
|  |             select new VariableNode(token.LiteralValue); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, SyntaxNodeBase> StatementParser() | ||||||
|  |     { | ||||||
|  |         return Choice( | ||||||
|  |             from variable in VariableParser() | ||||||
|  |             from _ in Operator(":=") | ||||||
|  |             from expression in ExpressionParser() | ||||||
|  |             select new AssignNode(variable, expression) | ||||||
|  |             ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, BlockNode> CompoundStatementParser() | ||||||
|  |     { | ||||||
|  |         return from _1 in Keyword("begin") | ||||||
|  |             from statements in StatementParser().SeparatedOrEndBy(Delimiter(";")) | ||||||
|  |             from _2 in Keyword("end") | ||||||
|  |             select new BlockNode(statements); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, ProgramBody> ProgramBodyParser() | ||||||
|  |     { | ||||||
|  |         return from block in CompoundStatementParser() | ||||||
|  |             select new ProgramBody(block); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, ProgramHead> ProgramHeadParser() | ||||||
|  |     { | ||||||
|  |         return from _ in Keyword("program") | ||||||
|  |             from token in Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Identifier) | ||||||
|  |             select new ProgramHead(token); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IParser<LexicalToken, Program> ProgramParser() | ||||||
|  |     { | ||||||
|  |         return from head in ProgramHeadParser() | ||||||
|  |             from _1 in Delimiter(";") | ||||||
|  |             from body in ProgramBodyParser() | ||||||
|  |             from _2 in Delimiter(".") | ||||||
|  |             select new Program(head, body); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								CanonSharp.Pascal/Parser/GrammarParserBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								CanonSharp.Pascal/Parser/GrammarParserBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | using CanonSharp.Combinator.Abstractions; | ||||||
|  | using CanonSharp.Pascal.Scanner; | ||||||
|  | using static CanonSharp.Combinator.ParserBuilder; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Pascal.Parser; | ||||||
|  |  | ||||||
|  | public abstract class GrammarParserBuilder | ||||||
|  | { | ||||||
|  |     protected static IParser<LexicalToken, LexicalToken> Keyword(string value) | ||||||
|  |         => Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Keyword && token.LiteralValue == value); | ||||||
|  |  | ||||||
|  |     protected static IParser<LexicalToken, LexicalToken> Operator(string value) | ||||||
|  |         => Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Operator && token.LiteralValue == value); | ||||||
|  |  | ||||||
|  |     protected static IParser<LexicalToken, LexicalToken> Delimiter(string value) | ||||||
|  |         => Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Delimiter && token.LiteralValue == value); | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								CanonSharp.Pascal/Parser/LexicalTokenReadState.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								CanonSharp.Pascal/Parser/LexicalTokenReadState.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | using CanonSharp.Combinator.Abstractions; | ||||||
|  | using CanonSharp.Pascal.Scanner; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Pascal.Parser; | ||||||
|  |  | ||||||
|  | public sealed class LexicalTokenReadState : IReadState<LexicalToken, LexicalTokenReadState> | ||||||
|  | { | ||||||
|  |     private readonly List<LexicalToken> _tokens; | ||||||
|  |     private readonly int _pos; | ||||||
|  |  | ||||||
|  |     public LexicalToken Current => _tokens[_pos]; | ||||||
|  |  | ||||||
|  |     public bool HasValue => _pos < _tokens.Count; | ||||||
|  |  | ||||||
|  |     public LexicalTokenReadState Next => new(_tokens, _pos + 1); | ||||||
|  |  | ||||||
|  |     private LexicalTokenReadState(List<LexicalToken> tokens, int pos) | ||||||
|  |     { | ||||||
|  |         _tokens = tokens; | ||||||
|  |         _pos = pos; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public LexicalTokenReadState(IEnumerable<LexicalToken> tokens) | ||||||
|  |     { | ||||||
|  |         _tokens = tokens.ToList(); | ||||||
|  |         _pos = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public bool Equals(LexicalTokenReadState? other) | ||||||
|  |     { | ||||||
|  |         if (other is null) | ||||||
|  |         { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (_tokens.Count != other._tokens.Count) | ||||||
|  |         { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ((LexicalToken first, LexicalToken second) in _tokens.Zip(other._tokens)) | ||||||
|  |         { | ||||||
|  |             if (!first.Equals(second)) | ||||||
|  |             { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return _pos == other._pos; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override string ToString() => HasValue ? Current.ToString() : "End of input stream."; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/AssignNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/AssignNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class AssignNode(VariableNode variable, SyntaxNodeBase expression) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.Assign; | ||||||
|  |  | ||||||
|  |     public VariableNode Variable => variable; | ||||||
|  |  | ||||||
|  |     public SyntaxNodeBase Expression => expression; | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public enum BinaryOperatorType | ||||||
|  | { | ||||||
|  |     Add, | ||||||
|  |     Subtract, | ||||||
|  |     Multiply, | ||||||
|  |  | ||||||
|  |     // Pascal特有的整数除法关键词div | ||||||
|  |     IntegerDivide, | ||||||
|  |     Divide, | ||||||
|  |     Mod, | ||||||
|  |     And, | ||||||
|  |     Or, | ||||||
|  |     Equal, | ||||||
|  |     NotEqual, | ||||||
|  |     Greater, | ||||||
|  |     GreaterEqual, | ||||||
|  |     Less, | ||||||
|  |     LessEqual | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public sealed class BinaryOperatorNode(BinaryOperatorType operatorType, SyntaxNodeBase left, SyntaxNodeBase right) | ||||||
|  |     : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.BinaryOperator; | ||||||
|  |  | ||||||
|  |     public BinaryOperatorType OperatorType => operatorType; | ||||||
|  |  | ||||||
|  |     public SyntaxNodeBase Left => left; | ||||||
|  |  | ||||||
|  |     public SyntaxNodeBase Right => right; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/BlockNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/BlockNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class BlockNode(IEnumerable<SyntaxNodeBase> statements) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.Block; | ||||||
|  |  | ||||||
|  |     public IList<SyntaxNodeBase> Statements => statements.ToList(); | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class BooleanValueNode(bool value) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.BooleanValue; | ||||||
|  |  | ||||||
|  |     public bool Value => value; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class FloatValueNode(double value) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.FloatValue; | ||||||
|  |  | ||||||
|  |     public double Value => value; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class IntegerValueNode(int value) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.IntegerValue; | ||||||
|  |  | ||||||
|  |     public int Value => value; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class Program(ProgramHead head, ProgramBody body) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.Program; | ||||||
|  |  | ||||||
|  |     public ProgramHead Head => head; | ||||||
|  |  | ||||||
|  |     public ProgramBody Body => body; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/ProgramBody.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/ProgramBody.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class ProgramBody(BlockNode block) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody; | ||||||
|  |  | ||||||
|  |     public BlockNode MainBlock => block; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/ProgramHead.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								CanonSharp.Pascal/SyntaxTree/ProgramHead.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | using CanonSharp.Pascal.Scanner; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class ProgramHead(LexicalToken programName) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramHead; | ||||||
|  |  | ||||||
|  |     public LexicalToken ProgramName => programName; | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public enum SyntaxNodeType | ||||||
|  | { | ||||||
|  |     BinaryOperator, | ||||||
|  |     UnaryOperator, | ||||||
|  |     IntegerValue, | ||||||
|  |     FloatValue, | ||||||
|  |     BooleanValue, | ||||||
|  |     Variable, | ||||||
|  |     Assign, | ||||||
|  |     Block, | ||||||
|  |     ProgramBody, | ||||||
|  |     ProgramHead, | ||||||
|  |     Program | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public abstract class SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public abstract SyntaxNodeType NodeType { get; } | ||||||
|  |  | ||||||
|  |     public T Convert<T>() where T : SyntaxNodeBase | ||||||
|  |     { | ||||||
|  |         if (this is not T result) | ||||||
|  |         { | ||||||
|  |             throw new InvalidCastException($"Can't convert {NodeType} to target node."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public enum UnaryOperatorType | ||||||
|  | { | ||||||
|  |     Plus, | ||||||
|  |     Minus, | ||||||
|  |     Not | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public sealed class UnaryOperatorNode(UnaryOperatorType operatorType, SyntaxNodeBase node) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.UnaryOperator; | ||||||
|  |  | ||||||
|  |     public UnaryOperatorType OperatorType => operatorType; | ||||||
|  |  | ||||||
|  |     public SyntaxNodeBase Node => node; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/VariableNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								CanonSharp.Pascal/SyntaxTree/VariableNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | public sealed class VariableNode(string name) : SyntaxNodeBase | ||||||
|  | { | ||||||
|  |     public override SyntaxNodeType NodeType => SyntaxNodeType.Variable; | ||||||
|  |  | ||||||
|  |     public string IdentifierName => name; | ||||||
|  | } | ||||||
							
								
								
									
										245
									
								
								CanonSharp.Tests/ParserTests/ExpressionParserTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								CanonSharp.Tests/ParserTests/ExpressionParserTests.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,245 @@ | |||||||
|  | using CanonSharp.Pascal.Parser; | ||||||
|  | using CanonSharp.Pascal.SyntaxTree; | ||||||
|  | using CanonSharp.Tests.Utils; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Tests.ParserTests; | ||||||
|  |  | ||||||
|  | public sealed class ExpressionParserTests : GrammarParserTestBase | ||||||
|  | { | ||||||
|  |     [Fact] | ||||||
|  |     public void BoolTest() | ||||||
|  |     { | ||||||
|  |         BooleanValueNode node = RunParser<BooleanValueNode>(GrammarParser.FactorParser(), "false"); | ||||||
|  |         Assert.False(node.Value); | ||||||
|  |  | ||||||
|  |         node = RunParser<BooleanValueNode>(GrammarParser.FactorParser(), "true"); | ||||||
|  |         Assert.True(node.Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void NumberTest() | ||||||
|  |     { | ||||||
|  |         IntegerValueNode integerValueNode = RunParser<IntegerValueNode>(GrammarParser.FactorParser(), "123456"); | ||||||
|  |         Assert.Equal(123456, integerValueNode.Value); | ||||||
|  |  | ||||||
|  |         FloatValueNode floatValueNode = RunParser<FloatValueNode>(GrammarParser.FactorParser(), "123.456"); | ||||||
|  |         Assert.Equal(123.456, floatValueNode.Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void UnaryOperatorTest() | ||||||
|  |     { | ||||||
|  |         UnaryOperatorNode node = RunParser<UnaryOperatorNode>(GrammarParser.FactorParser(), "- 123"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Minus, node.OperatorType); | ||||||
|  |         Assert.Equal(123, node.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         node = RunParser<UnaryOperatorNode>(GrammarParser.FactorParser(), "+ 100.5"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Plus, node.OperatorType); | ||||||
|  |         Assert.Equal(100.5, node.Node.Convert<FloatValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void IdentifierTest() | ||||||
|  |     { | ||||||
|  |         VariableNode node = RunParser<VariableNode>(GrammarParser.FactorParser(), "temp"); | ||||||
|  |         Assert.Equal("temp", node.IdentifierName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SingleTermTest() | ||||||
|  |     { | ||||||
|  |         VariableNode node = RunParser<VariableNode>(GrammarParser.TermParser(), "temp"); | ||||||
|  |         Assert.Equal("temp", node.IdentifierName); | ||||||
|  |  | ||||||
|  |         UnaryOperatorNode unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.TermParser(), "- 123"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(123, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.TermParser(), "+ 100.5"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(100.5, unaryOperatorNode.Node.Convert<FloatValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void MultiplyTermTest1() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "10 / 2"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Divide, node.OperatorType); | ||||||
|  |         Assert.Equal(10, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void MultiplyTermTest2() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "10 div 2"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType); | ||||||
|  |         Assert.Equal(10, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void MultiplyTermTest3() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "temp * 2 div 3"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType); | ||||||
|  |         Assert.Equal(3, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |         BinaryOperatorNode leftNode = node.Left.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Multiply, leftNode.OperatorType); | ||||||
|  |         Assert.Equal("temp", leftNode.Left.Convert<VariableNode>().IdentifierName); | ||||||
|  |         Assert.Equal(2, leftNode.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SimpleExpressionTest1() | ||||||
|  |     { | ||||||
|  |         VariableNode node = RunParser<VariableNode>(GrammarParser.SimpleExpressionParser(), "temp"); | ||||||
|  |         Assert.Equal("temp", node.IdentifierName); | ||||||
|  |  | ||||||
|  |         UnaryOperatorNode unaryOperatorNode = | ||||||
|  |             RunParser<UnaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "- 123"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(123, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "+ 100.5"); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(100.5, unaryOperatorNode.Node.Convert<FloatValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SimpleExpressionTest2() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + 1"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, node.OperatorType); | ||||||
|  |         Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + 1 - 2"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); | ||||||
|  |         Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         BinaryOperatorNode leftNode = node.Left.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, leftNode.OperatorType); | ||||||
|  |         Assert.Equal(1, leftNode.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(1, leftNode.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SimpleExpressionTest3() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 - 2 * 5"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); | ||||||
|  |         Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         BinaryOperatorNode rightNode = node.Right.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Multiply, rightNode.OperatorType); | ||||||
|  |         Assert.Equal(2, rightNode.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(5, rightNode.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SimpleExpressionTest4() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + +1"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, node.OperatorType); | ||||||
|  |         Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         UnaryOperatorNode unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>(); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 - -1"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); | ||||||
|  |         Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>(); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "+ 1 - - 1"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType); | ||||||
|  |         unaryOperatorNode = node.Left.Convert<UnaryOperatorNode>(); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |         unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>(); | ||||||
|  |         Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void SimpleExpressionTest5() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), | ||||||
|  |             "true and temp or temp and false"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Or, node.OperatorType); | ||||||
|  |  | ||||||
|  |         BinaryOperatorNode left = node.Left.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.And, left.OperatorType); | ||||||
|  |         BinaryOperatorNode right = node.Right.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.And, right.OperatorType); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ExpressionTest1() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(), | ||||||
|  |             "true and temp or temp and false"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Or, node.OperatorType); | ||||||
|  |  | ||||||
|  |         BinaryOperatorNode left = node.Left.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.And, left.OperatorType); | ||||||
|  |         BinaryOperatorNode right = node.Right.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.And, right.OperatorType); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ExpressionTest2() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(), "2 >= 1"); | ||||||
|  |         Assert.Equal(BinaryOperatorType.GreaterEqual, node.OperatorType); | ||||||
|  |         Assert.Equal(2, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ExpressionTest3() | ||||||
|  |     { | ||||||
|  |         BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(), "(1 + 1) * 2"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Multiply, node.OperatorType); | ||||||
|  |         Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |  | ||||||
|  |         node = node.Left.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, node.OperatorType); | ||||||
|  |         Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ExpressionsTest1() | ||||||
|  |     { | ||||||
|  |         List<BinaryOperatorNode> nodes = | ||||||
|  |             RunParser<BinaryOperatorNode>(GrammarParser.ExpressionsParser(), "1 + 1, 2 * 3"); | ||||||
|  |  | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, nodes[0].OperatorType); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Multiply, nodes[1].OperatorType); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void VariableTest1() | ||||||
|  |     { | ||||||
|  |         VariableNode node = RunParser<VariableNode>(GrammarParser.VariableParser(), "temp"); | ||||||
|  |         Assert.Equal("temp", node.IdentifierName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								CanonSharp.Tests/ParserTests/ProgramParserTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								CanonSharp.Tests/ParserTests/ProgramParserTests.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | using CanonSharp.Pascal.SyntaxTree; | ||||||
|  | using CanonSharp.Tests.Utils; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Tests.ParserTests; | ||||||
|  |  | ||||||
|  | public class ProgramParserTests : GrammarParserTestBase | ||||||
|  | { | ||||||
|  |     [Fact] | ||||||
|  |     public void ProgramParseTest1() | ||||||
|  |     { | ||||||
|  |         Program program = ProgramParse(""" | ||||||
|  |                                        program main; | ||||||
|  |                                        begin | ||||||
|  |                                        end. | ||||||
|  |                                        """); | ||||||
|  |  | ||||||
|  |         Assert.Equal("main", program.Head.ProgramName.LiteralValue); | ||||||
|  |         Assert.Empty(program.Body.MainBlock.Statements); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ProgramParseTest2() | ||||||
|  |     { | ||||||
|  |         Program program = ProgramParse(""" | ||||||
|  |                                        program main; | ||||||
|  |                                        begin | ||||||
|  |                                        temp := 1 + 1; | ||||||
|  |                                        end. | ||||||
|  |                                        """); | ||||||
|  |  | ||||||
|  |         Assert.Equal("main", program.Head.ProgramName.LiteralValue); | ||||||
|  |  | ||||||
|  |         AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert<AssignNode>(); | ||||||
|  |         Assert.Equal("temp", assignNode.Variable.IdentifierName); | ||||||
|  |  | ||||||
|  |         BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert<BinaryOperatorNode>(); | ||||||
|  |         Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType); | ||||||
|  |         Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value); | ||||||
|  |         Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								CanonSharp.Tests/ParserTests/StatementParserTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								CanonSharp.Tests/ParserTests/StatementParserTests.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | using CanonSharp.Pascal.Parser; | ||||||
|  | using CanonSharp.Pascal.SyntaxTree; | ||||||
|  | using CanonSharp.Tests.Utils; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Tests.ParserTests; | ||||||
|  |  | ||||||
|  | public class StatementParserTests : GrammarParserTestBase | ||||||
|  | { | ||||||
|  |     [Fact] | ||||||
|  |     public void StatementTest1() | ||||||
|  |     { | ||||||
|  |         AssignNode node = RunParser<AssignNode>(GrammarParser.StatementParser(), "temp := 1"); | ||||||
|  |  | ||||||
|  |         Assert.Equal("temp", node.Variable.IdentifierName); | ||||||
|  |         Assert.Equal(1, node.Expression.Convert<IntegerValueNode>().Value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Theory] | ||||||
|  |     [InlineData(""" | ||||||
|  |                 begin | ||||||
|  |                 temp := 1 + 1; | ||||||
|  |                 flag := true and false; | ||||||
|  |                 end | ||||||
|  |                 """)] | ||||||
|  |     [InlineData(""" | ||||||
|  |                 begin | ||||||
|  |                 temp := 1 + 1; | ||||||
|  |                 flag := true and false | ||||||
|  |                 end | ||||||
|  |                 """)] | ||||||
|  |     public void CompoundStatementTest1(string input) | ||||||
|  |     { | ||||||
|  |         BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), input); | ||||||
|  |  | ||||||
|  |         Assert.Equal(2, node.Statements.Count); | ||||||
|  |         AssignNode assignNode = node.Statements[0].Convert<AssignNode>(); | ||||||
|  |         Assert.Equal("temp", assignNode.Variable.IdentifierName); | ||||||
|  |  | ||||||
|  |         assignNode = node.Statements[1].Convert<AssignNode>(); | ||||||
|  |         Assert.Equal("flag", assignNode.Variable.IdentifierName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void CompoundStatementTest2() | ||||||
|  |     { | ||||||
|  |         BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), "begin end"); | ||||||
|  |         Assert.Empty(node.Statements); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public void ProgramHeadTest1() | ||||||
|  |     { | ||||||
|  |         ProgramHead head = RunParser<ProgramHead>(GrammarParser.ProgramHeadParser(), "program main"); | ||||||
|  |  | ||||||
|  |         Assert.Equal("main", head.ProgramName.LiteralValue); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Theory] | ||||||
|  |     [InlineData(""" | ||||||
|  |                 begin | ||||||
|  |                 temp := 1 + 1; | ||||||
|  |                 flag := true and false; | ||||||
|  |                 end | ||||||
|  |                 """)] | ||||||
|  |     [InlineData(""" | ||||||
|  |                 begin | ||||||
|  |                 temp := 1 + 1; | ||||||
|  |                 flag := true and false | ||||||
|  |                 end | ||||||
|  |                 """)] | ||||||
|  |     public void ProgramBodyTest1(string input) | ||||||
|  |     { | ||||||
|  |         BlockNode node = RunParser<ProgramBody>(GrammarParser.ProgramBodyParser(), input).MainBlock; | ||||||
|  |  | ||||||
|  |         Assert.Equal(2, node.Statements.Count); | ||||||
|  |         AssignNode assignNode = node.Statements[0].Convert<AssignNode>(); | ||||||
|  |         Assert.Equal("temp", assignNode.Variable.IdentifierName); | ||||||
|  |  | ||||||
|  |         assignNode = node.Statements[1].Convert<AssignNode>(); | ||||||
|  |         Assert.Equal("flag", assignNode.Variable.IdentifierName); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								CanonSharp.Tests/Utils/GrammarParserTestBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								CanonSharp.Tests/Utils/GrammarParserTestBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | using CanonSharp.Combinator.Abstractions; | ||||||
|  | using CanonSharp.Pascal.Parser; | ||||||
|  | using CanonSharp.Pascal.Scanner; | ||||||
|  | using CanonSharp.Pascal.SyntaxTree; | ||||||
|  |  | ||||||
|  | namespace CanonSharp.Tests.Utils; | ||||||
|  |  | ||||||
|  | public abstract class GrammarParserTestBase | ||||||
|  | { | ||||||
|  |     protected static T RunParser<T>(IParser<LexicalToken, SyntaxNodeBase> parser, string input) where T : SyntaxNodeBase | ||||||
|  |     { | ||||||
|  |         LexicalScanner scanner = new(); | ||||||
|  |         LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input))); | ||||||
|  |         IParseResult<LexicalToken, SyntaxNodeBase> parseResult = parser.Parse(state); | ||||||
|  |  | ||||||
|  |         return parseResult.Value.Convert<T>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected static List<T> RunParser<T>(IParser<LexicalToken, IEnumerable<SyntaxNodeBase>> parser, | ||||||
|  |         string input) where T : SyntaxNodeBase | ||||||
|  |     { | ||||||
|  |         LexicalScanner scanner = new(); | ||||||
|  |         LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input))); | ||||||
|  |         IParseResult<LexicalToken, IEnumerable<SyntaxNodeBase>> parseResult = parser.Parse(state); | ||||||
|  |  | ||||||
|  |         return parseResult.Value.Select(node => node.Convert<T>()).ToList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected static Program ProgramParse(string input) | ||||||
|  |         => RunParser<Program>(GrammarParser.ProgramParser(), input); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user