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()
{