diff --git a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs index 1036d1990..deb1d9cce 100644 --- a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs +++ b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs @@ -52,6 +52,9 @@ public IExpressionNode AnyMergeToken( return EmptyExpressionNode.EmptyFollowsMemberAccessToken; } + if (UtilityApi.IsBinaryOperatorSyntaxKind(token.SyntaxKind)) + return HandleBinaryOperator(expressionPrimary, token, compilationUnit, ref parserModel); + switch (expressionPrimary.SyntaxKind) { case SyntaxKind.EmptyExpressionNode: @@ -106,6 +109,8 @@ public IExpressionNode AnyMergeExpression( switch (expressionPrimary.SyntaxKind) { + case SyntaxKind.BinaryExpressionNode: + return BinaryMergeExpression((BinaryExpressionNode)expressionPrimary, expressionSecondary, compilationUnit, ref parserModel); case SyntaxKind.ParenthesizedExpressionNode: return ParenthesizedMergeExpression((ParenthesizedExpressionNode)expressionPrimary, expressionSecondary, compilationUnit, ref parserModel); case SyntaxKind.CommaSeparatedExpressionNode: @@ -136,6 +141,84 @@ public IExpressionNode AnyMergeExpression( return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), expressionPrimary, expressionSecondary); }; } + + public IExpressionNode HandleBinaryOperator( + IExpressionNode expressionPrimary, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + var parentExpressionNode = GetParentNode(expressionPrimary, compilationUnit, ref parserModel); + + if (parentExpressionNode.SyntaxKind == SyntaxKind.BinaryExpressionNode) + { + var parentBinaryExpressionNode = (BinaryExpressionNode)parentExpressionNode; + var precedenceParent = UtilityApi.GetOperatorPrecedence(parentBinaryExpressionNode.BinaryOperatorNode.OperatorToken.SyntaxKind); + + var precedenceChild = UtilityApi.GetOperatorPrecedence(token.SyntaxKind); + + if (parentBinaryExpressionNode.RightExpressionNode.SyntaxKind == SyntaxKind.EmptyExpressionNode) + { + if (precedenceParent >= precedenceChild) + { + // Child's left expression becomes the parent. + + parentBinaryExpressionNode.SetRightExpressionNode(expressionPrimary); + + var typeClauseNode = expressionPrimary.ResultTypeClauseNode; + var binaryOperatorNode = new BinaryOperatorNode(typeClauseNode, token, typeClauseNode, typeClauseNode); + var binaryExpressionNode = new BinaryExpressionNode(parentBinaryExpressionNode, binaryOperatorNode); + + ClearFromExpressionList(parentBinaryExpressionNode, compilationUnit, ref parserModel); + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, binaryExpressionNode)); + + return EmptyExpressionNode.Empty; + } + else + { + // Parent's right expression becomes the child. + + var typeClauseNode = expressionPrimary.ResultTypeClauseNode; + var binaryOperatorNode = new BinaryOperatorNode(typeClauseNode, token, typeClauseNode, typeClauseNode); + var binaryExpressionNode = new BinaryExpressionNode(expressionPrimary, binaryOperatorNode); + + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, binaryExpressionNode)); + + //parentBinaryExpressionNode.SetRightExpressionNode(binaryExpressionNode); + + return EmptyExpressionNode.Empty; + } + } + else + { + // Weird situation? + // This sounds like it just wouldn't compile. + // + // Something like: + // 1 + 2 3 + 4 + // + // NOTE: There is no operator between the '2' and the '3'. + // It is just two binary expressions side by side. + // + // I think you'd want to pretend that the parent binary expression didn't exist + // for the sake of parser recovery. + + var typeClauseNode = expressionPrimary.ResultTypeClauseNode; + var binaryOperatorNode = new BinaryOperatorNode(typeClauseNode, token, typeClauseNode, typeClauseNode); + var binaryExpressionNode = new BinaryExpressionNode(expressionPrimary, binaryOperatorNode); + + ClearFromExpressionList(parentBinaryExpressionNode, compilationUnit, ref parserModel); + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, binaryExpressionNode)); + return EmptyExpressionNode.Empty; + } + } + + // Scope to avoid variable name collision. + { + var typeClauseNode = expressionPrimary.ResultTypeClauseNode; + var binaryOperatorNode = new BinaryOperatorNode(typeClauseNode, token, typeClauseNode, typeClauseNode); + var binaryExpressionNode = new BinaryExpressionNode(expressionPrimary, binaryOperatorNode); + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, binaryExpressionNode)); + return EmptyExpressionNode.Empty; + } + } public IExpressionNode AmbiguousIdentifierMergeToken( AmbiguousIdentifierExpressionNode ambiguousIdentifierExpressionNode, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) @@ -159,10 +242,8 @@ public IExpressionNode AmbiguousIdentifierMergeToken( BindFunctionInvocationNode( functionInvocationNode, compilationUnit); - - parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, functionInvocationNode)); - parserModel.ExpressionList.Add((SyntaxKind.CommaToken, functionInvocationNode.FunctionParametersListingNode)); - return EmptyExpressionNode.Empty; + + return ParseFunctionParametersListingNode(functionInvocationNode, functionInvocationNode.FunctionParametersListingNode, compilationUnit, ref parserModel); } else if (token.SyntaxKind == SyntaxKind.OpenAngleBracketToken) { @@ -472,6 +553,18 @@ public IExpressionNode BinaryMergeToken( } } + public IExpressionNode BinaryMergeExpression( + BinaryExpressionNode binaryExpressionNode, IExpressionNode expressionSecondary, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + if (binaryExpressionNode.RightExpressionNode.SyntaxKind == SyntaxKind.EmptyExpressionNode) + { + binaryExpressionNode.SetRightExpressionNode(expressionSecondary); + return binaryExpressionNode; + } + + return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), binaryExpressionNode, expressionSecondary); + } + public IExpressionNode CommaSeparatedMergeToken( CommaSeparatedExpressionNode commaSeparatedExpressionNode, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { @@ -533,9 +626,8 @@ public IExpressionNode ConstructorInvocationMergeToken( constructorInvocationExpressionNode.SetFunctionParametersListingNode(functionParametersListingNode); constructorInvocationExpressionNode.ConstructorInvocationStageKind = ConstructorInvocationStageKind.FunctionParameters; - parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, constructorInvocationExpressionNode)); - parserModel.ExpressionList.Add((SyntaxKind.CommaToken, constructorInvocationExpressionNode.FunctionParametersListingNode)); - return EmptyExpressionNode.Empty; + + return ParseFunctionParametersListingNode(constructorInvocationExpressionNode, constructorInvocationExpressionNode.FunctionParametersListingNode, compilationUnit, ref parserModel); case SyntaxKind.CloseParenthesisToken: if (constructorInvocationExpressionNode.FunctionParametersListingNode is not null) { @@ -961,6 +1053,10 @@ public IExpressionNode FunctionParametersListingMergeToken( { case SyntaxKind.CommaToken: parserModel.ExpressionList.Add((SyntaxKind.CommaToken, functionParametersListingNode)); + parserModel.ExpressionList.Add((SyntaxKind.ColonToken, functionParametersListingNode)); + return ParseNamedParameterSyntaxAndReturnEmptyExpressionNode(compilationUnit, ref parserModel); + case SyntaxKind.ColonToken: + parserModel.ExpressionList.Add((SyntaxKind.ColonToken, functionParametersListingNode)); return EmptyExpressionNode.Empty; default: return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), functionParametersListingNode, token); @@ -970,6 +1066,13 @@ public IExpressionNode FunctionParametersListingMergeToken( public IExpressionNode FunctionParametersListingMergeExpression( FunctionParametersListingNode functionParametersListingNode, IExpressionNode expressionSecondary, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { + Console.WriteLine("aaa FunctionParametersListingMergeExpression"); + if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.ColonToken) + { + Console.WriteLine($"aaa CURRENT optional? bbb {expressionSecondary.SyntaxKind}"); + return functionParametersListingNode; + } + if (expressionSecondary.SyntaxKind == SyntaxKind.EmptyExpressionNode) return functionParametersListingNode; @@ -1179,19 +1282,7 @@ public IExpressionNode LambdaMergeExpression( public IExpressionNode LiteralMergeToken( LiteralExpressionNode literalExpressionNode, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { - switch (token.SyntaxKind) - { - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - case SyntaxKind.StarToken: - case SyntaxKind.DivisionToken: - case SyntaxKind.EqualsEqualsToken: - var typeClauseNode = literalExpressionNode.ResultTypeClauseNode; - var binaryOperatorNode = new BinaryOperatorNode(typeClauseNode, token, typeClauseNode, typeClauseNode); - return new BinaryExpressionNode(literalExpressionNode, binaryOperatorNode); - default: - return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), literalExpressionNode, token); - } + return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), literalExpressionNode, token); } public IExpressionNode ParenthesizedMergeToken( @@ -1323,9 +1414,7 @@ public IExpressionNode TypeClauseMergeToken( functionInvocationNode, compilationUnit); - parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, functionInvocationNode)); - parserModel.ExpressionList.Add((SyntaxKind.CommaToken, functionInvocationNode.FunctionParametersListingNode)); - return EmptyExpressionNode.Empty; + return ParseFunctionParametersListingNode(functionInvocationNode, functionInvocationNode.FunctionParametersListingNode, compilationUnit, ref parserModel); } goto default; @@ -1617,4 +1706,70 @@ public void ClearFromExpressionList(IExpressionNode expressionNode, CSharpCompil parserModel.ExpressionList.RemoveAt(i); } } + + /// + /// 'bool foundChild' usage: + /// If the child is NOT in the ExpressionList then this is true, + /// + /// But, if the child is in the ExpressionList, and is not the final entry in the ExpressionList, + /// then this needs to be set to 'false', otherwise a descendent node of 'childExpressionNode' + /// will be thought to be the parent node due to the list being traversed end to front order. + /// + public IExpressionNode GetParentNode( + IExpressionNode childExpressionNode, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel, bool foundChild = true) + { + for (int i = parserModel.ExpressionList.Count - 1; i > -1; i--) + { + var delimiterExpressionTuple = parserModel.ExpressionList[i]; + + if (foundChild) + { + if (delimiterExpressionTuple.ExpressionNode is null) + break; + + if (!Object.ReferenceEquals(childExpressionNode, delimiterExpressionTuple.ExpressionNode)) + return delimiterExpressionTuple.ExpressionNode; + } + else + { + if (Object.ReferenceEquals(childExpressionNode, delimiterExpressionTuple.ExpressionNode)) + foundChild = true; + } + } + + return EmptyExpressionNode.Empty; + } + + /// + /// The argument 'IExpressionNode functionInvocationNode' is not of type 'FunctionInvocationNode' + /// in order to permit this method to be invoked for 'ConstructorInvocationExpressionNode' as well. + /// + public IExpressionNode ParseFunctionParametersListingNode(IExpressionNode functionInvocationNode, FunctionParametersListingNode functionParametersListingNode, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, functionInvocationNode)); + parserModel.ExpressionList.Add((SyntaxKind.CommaToken, functionParametersListingNode)); + parserModel.ExpressionList.Add((SyntaxKind.ColonToken, functionParametersListingNode)); + return ParseNamedParameterSyntaxAndReturnEmptyExpressionNode(compilationUnit, ref parserModel); + } + + public IExpressionNode ParseNamedParameterSyntaxAndReturnEmptyExpressionNode(CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + if (UtilityApi.IsConvertibleToIdentifierToken(parserModel.TokenWalker.Peek(1).SyntaxKind) && + parserModel.TokenWalker.Peek(2).SyntaxKind == SyntaxKind.ColonToken) + { + // Consume the open parenthesis + _ = parserModel.TokenWalker.Consume(); + + // Consume the identifierToken + compilationUnit.Binder.CreateVariableSymbol( + UtilityApi.ConvertToIdentifierToken(parserModel.TokenWalker.Consume(), compilationUnit, ref parserModel), + VariableKind.Local, + compilationUnit); + + // Consume the ColonToken + _ = parserModel.TokenWalker.Consume(); + } + + return EmptyExpressionNode.Empty; + } } diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/CSharpParserModel.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/CSharpParserModel.cs index 86e4381a1..6b13ce8d9 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/CSharpParserModel.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/CSharpParserModel.cs @@ -43,7 +43,18 @@ public CSharpParserModel( public TokenWalker TokenWalker { get; } public Stack SyntaxStack { get; set; } public CSharpStatementBuilder StatementBuilder { get; set; } = new(); + + /// + /// The C# IParserModel implementation will only "short circuit" if the 'SyntaxKind DelimiterSyntaxKind' + /// is registered as a delimiter. + /// + /// This is done in order to speed up the while loop, as the list of short circuits doesn't have to be + /// iterated unless the current token is a possible delimiter. + /// + /// Luthetus.CompilerServices.CSharp.ParserCase.Internals.ParseOthers.SyntaxIsEndDelimiter(SyntaxKind syntaxKind) {...} + /// public List<(SyntaxKind DelimiterSyntaxKind, IExpressionNode ExpressionNode)> ExpressionList { get; set; } + public IExpressionNode? NoLongerRelevantExpressionNode { get; set; } public List TryParseExpressionSyntaxKindList { get; } = new(); public IExpressionNode ForceParseExpressionInitialPrimaryExpression { get; set; } diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs index 2d88c37c6..f1e520ff4 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs @@ -168,7 +168,6 @@ public static FunctionArgumentsListingNode HandleFunctionArguments(CSharpCompila if (successTypeClauseNode) { - // 'TypeClauseNode' or 'VariableDeclarationNode' var successNameableToken = false; if (UtilityApi.IsConvertibleToIdentifierToken(parserModel.TokenWalker.Current.SyntaxKind)) @@ -178,7 +177,11 @@ public static FunctionArgumentsListingNode HandleFunctionArguments(CSharpCompila if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsToken) { - // Optional + _ = parserModel.TokenWalker.Consume(); + + parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, null)); + parserModel.ExpressionList.Add((SyntaxKind.CommaToken, null)); + var expressionNode = ParseOthers.ParseExpression(compilationUnit, ref parserModel); } var variableDeclarationNode = new VariableDeclarationNode( diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseOthers.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseOthers.cs index 0484f640e..ddb1fdf7c 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseOthers.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseOthers.cs @@ -180,7 +180,7 @@ public static IExpressionNode ParseExpression(CSharpCompilationUnit compilationU var indexTokenRoot = parserModel.TokenWalker.Index; var expressionPrimaryPreviousRoot = expressionPrimary; - while (!parserModel.TokenWalker.IsEof) + while (true) { #if DEBUG WriteExpressionList(parserModel.ExpressionList); @@ -208,7 +208,19 @@ public static IExpressionNode ParseExpression(CSharpCompilationUnit compilationU } } - if (forceExit) // delimiterExpressionTuple.ExpressionNode is null + // The while loop used to be 'while (!parserModel.TokenWalker.IsEof)' + // This caused an issue where 'BubbleUpParseExpression(...)' would not run + // if the end of file was reached. + // + // Given how this parser is written, adding 'SyntaxKind.EndOfFile' to 'parserModel.ExpressionList' + // would follow the pattern of how 'SyntaxKind.StatementDelimiterToken' is written. + // + // But, the 'while (true)' loop makes me extremely uncomfortable. + // + // So I added '|| parserModel.TokenWalker.IsEof' here. + // + // If upon further inspection on way or the other is deemed safe then this redundancy can be removed. + if (forceExit || parserModel.TokenWalker.IsEof) // delimiterExpressionTuple.ExpressionNode is null { expressionPrimary = BubbleUpParseExpression(0, expressionPrimary, compilationUnit, ref parserModel); break; @@ -292,6 +304,7 @@ public static IExpressionNode ParseExpression(CSharpCompilationUnit compilationU // It is vital that this 'clear' and 'add' are done in a way that permits an invoker of the 'ParseExpression' method to 'add' a similar 'forceExit' delimiter // Example: 'parserModel.ExpressionList.Add((SyntaxKind.CloseParenthesisToken, null));' parserModel.ExpressionList.Clear(); + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, null)); parserModel.ExpressionList.Add((SyntaxKind.StatementDelimiterToken, null)); if (expressionPrimary.SyntaxKind == SyntaxKind.AmbiguousIdentifierExpressionNode) diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs index a06d47b62..53ea93f30 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs @@ -81,6 +81,11 @@ public static bool IsBinaryOperatorSyntaxKind(SyntaxKind syntaxKind) case SyntaxKind.MinusToken: case SyntaxKind.StarToken: case SyntaxKind.DivisionToken: + case SyntaxKind.EqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.PipePipeToken: + case SyntaxKind.QuestionMarkQuestionMarkToken: return true; default: return false; diff --git a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj index 84fbf8cf7..fe20ed7fa 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj +++ b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 0.9.7.10 + 0.9.7.11 diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor index fcc354850..1c0a0718b 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor @@ -172,6 +172,31 @@ Recent Changes:
+
v 0.9.7.11 (2024-12-20)
+
    +
  • + Text Editor NuGet Package v3.5.0 (by the end of the day I will publish this) + (nuget.org) +
  • +
  • + Fix: optional argument entry in function definition signature +
  • +
  • + Fix: named parameters in function invocation +
  • +
  • + Started logic for operator precedence. +
  • +
  • + Support more operators such as '||'. +
  • +
  • + Expressions are now parsed properly when terminated by the end of file + (rather than for example: a StatementDelimiterToken ';') +
  • +
+
+
v 0.9.7.10 (2024-12-18)
  • diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs index 8a0894f59..6328afb32 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs @@ -77,13 +77,58 @@ public void Numeric_Add_BinaryExpressionNode() Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); } + /// + /// When encountering a binary operator, + /// change control from the expressionPrimary to the binary operator making the decisions. + /// + /// "Push" the binary expression node onto the ExpressionList with an EndOfFileToken delimiter. + /// + /// Then return an EmptyExpressionNode. + /// + /// Whatever the EmptyExpressionNode becomes will then eventually bubble back up to + /// be used as the right operand. + /// + /// This is being said in reference to how things currently are erroneously done. + /// In which a BinaryExpressionNode explicitly is looking for certain SyntaxKind. + /// + /// Checking the SyntaxKind is fine for binding whether there is a syntax error due + /// to an operator not being defined for various operands. + /// + /// But in terms of the parsing of an expression, it needs to just start a fresh EmptyExpressionNode + /// and grab the right operand that way. + /// + [Fact] + public void Numeric_Add_BinaryExpressionNode_Number_FunctionInvocation() + { + var test = new Test(@"1 + SomeMethod()"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + + var binaryExpressionNode = (BinaryExpressionNode)topCodeBlock.GetChildList().Single(); + var textTypeClause = "int"; + + var leftLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + //public ISyntaxToken binaryOperatorNode.OperatorToken { get; } + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + var rightFunctionInvocationNode = (FunctionInvocationNode)binaryExpressionNode.RightExpressionNode; + // Assert.Equal(textTypeClause, rightFunctionInvocationNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + [Fact] public void Numeric_Add_BinaryExpressionNode_More() { var test = new Test(@"1 + 1 + 1"); var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; - + var binaryExpressionNode = (BinaryExpressionNode)topCodeBlock.GetChildList().Single(); + WriteChildrenIndentedRecursive(topCodeBlock); var textTypeClause = "int"; // Left Expression @@ -184,6 +229,195 @@ public void Numeric_Star_BinaryExpressionNode() Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); } + [Fact] + public void Numeric_Star_BinaryExpressionNode_Precedence_Parent_LessThan_Child() + { + var test = new Test(@"1 + 2 * 3"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + + var binaryExpressionNode = (BinaryExpressionNode)topCodeBlock.GetChildList().Single(); + WriteChildrenIndentedRecursive(topCodeBlock); + var textTypeClause = "int"; + + // Left Expression + { + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("1", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + } + + // Operator + { + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("+", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Right Expression + { + var rightBinaryExpressionNode = (BinaryExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightBinaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + // Temporarily swap variables for sanity #change + var rememberBinaryExpressionNode = binaryExpressionNode; + binaryExpressionNode = rightBinaryExpressionNode; + // Inner Binary Expression + { + var leftLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("2", leftLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("*", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("3", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Temporarily swap variables for sanity #restore + binaryExpressionNode = rememberBinaryExpressionNode; + } + + // Result + { + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + } + + [Fact] + public void Numeric_Star_BinaryExpressionNode_Precedence_Parent_GreaterThan_Child() + { + var test = new Test(@"1 * 2 + 3"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + + var binaryExpressionNode = (BinaryExpressionNode)topCodeBlock.GetChildList().Single(); + WriteChildrenIndentedRecursive(topCodeBlock); + var textTypeClause = "int"; + + // Left Expression + { + var leftBinaryExpressionNode = (BinaryExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftBinaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + // Temporarily swap variables for sanity #change + var rememberBinaryExpressionNode = binaryExpressionNode; + binaryExpressionNode = leftBinaryExpressionNode; + // Inner Binary Expression + { + var leftLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("1", leftLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("*", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("2", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Temporarily swap variables for sanity #restore + binaryExpressionNode = rememberBinaryExpressionNode; + } + + // Operator + { + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("+", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Right Expression + { + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("3", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + } + + // Result + { + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + } + + [Fact] + public void Numeric_Star_BinaryExpressionNode_Precedence_Parent_EqualTo_Child() + { + var test = new Test(@"1 * 2 * 3"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + + var binaryExpressionNode = (BinaryExpressionNode)topCodeBlock.GetChildList().Single(); + WriteChildrenIndentedRecursive(topCodeBlock); + var textTypeClause = "int"; + + // Left Expression + { + var leftBinaryExpressionNode = (BinaryExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftBinaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + // Temporarily swap variables for sanity #change + var rememberBinaryExpressionNode = binaryExpressionNode; + binaryExpressionNode = leftBinaryExpressionNode; + // Inner Binary Expression + { + var leftLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.LeftExpressionNode; + Assert.Equal(textTypeClause, leftLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("1", leftLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("*", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("2", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Temporarily swap variables for sanity #restore + binaryExpressionNode = rememberBinaryExpressionNode; + } + + // Operator + { + var binaryOperatorNode = binaryExpressionNode.BinaryOperatorNode; + Assert.Equal(textTypeClause, binaryOperatorNode.LeftOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("*", binaryOperatorNode.OperatorToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.RightOperandTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal(textTypeClause, binaryOperatorNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + + // Right Expression + { + var rightLiteralExpressionNode = (LiteralExpressionNode)binaryExpressionNode.RightExpressionNode; + Assert.Equal(textTypeClause, rightLiteralExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + Assert.Equal("3", rightLiteralExpressionNode.LiteralSyntaxToken.TextSpan.GetText()); + } + + // Result + { + Assert.Equal(textTypeClause, binaryExpressionNode.ResultTypeClauseNode.TypeIdentifierToken.TextSpan.GetText()); + } + } + [Fact] public void Numeric_Division_BinaryExpressionNode() {