Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backwards merging #392

Merged
merged 19 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f320fa0
Merge pull request #389 from Luthetus/beta
Luthetus Dec 18, 2024
4298860
Merge pull request #55 from Luthetus/staging
huntercfreeman Dec 18, 2024
e05b42d
Update version
huntercfreeman Dec 18, 2024
07a89c9
Merge branch 'optimizations' of https://github.com/huntercfreeman/Lut…
huntercfreeman Dec 18, 2024
c6dbf0f
|| parserModel.TokenWalker.IsEof
huntercfreeman Dec 19, 2024
304cda3
Numeric_Add_BinaryExpressionNode_Number_FunctionInvocation
huntercfreeman Dec 19, 2024
942d47c
1 + 1 + 1
huntercfreeman Dec 20, 2024
e66b2f0
Numeric_Star_BinaryExpressionNode_Precedence_B()
huntercfreeman Dec 20, 2024
a33475a
Numeric_Star_BinaryExpressionNode_Precedence_A()
huntercfreeman Dec 20, 2024
59aed82
Numeric_Star_BinaryExpressionNode_Precedence_Parent_EqualTo_Child()
huntercfreeman Dec 20, 2024
3ccc09e
Remove: Console.WriteLine
huntercfreeman Dec 20, 2024
045a2d5
Fix: optional argument entry in listing
huntercfreeman Dec 20, 2024
0dbd167
Parse optional parameters
huntercfreeman Dec 20, 2024
0c46d4f
Pseudo amend previous commit: this parses named parameters
huntercfreeman Dec 20, 2024
ed30adb
Parse named parameters (see 2 commits previous with wrong name)
huntercfreeman Dec 20, 2024
5789695
Named parameters: 'CreateVariableSymbol'
huntercfreeman Dec 20, 2024
3f97629
Update IdeInfoDisplay.razor
huntercfreeman Dec 20, 2024
fb15565
Merge pull request #390 from huntercfreeman/optimizations
Luthetus Dec 20, 2024
0fe144c
Merge pull request #391 from Luthetus/staging
Luthetus Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1617,4 +1706,70 @@ public void ClearFromExpressionList(IExpressionNode expressionNode, CSharpCompil
parserModel.ExpressionList.RemoveAt(i);
}
}

/// <summary>
/// '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.
/// </summary>
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;
}

/// <summary>
/// The argument 'IExpressionNode functionInvocationNode' is not of type 'FunctionInvocationNode'
/// in order to permit this method to be invoked for 'ConstructorInvocationExpressionNode' as well.
/// </summary>
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;
}
}
11 changes: 11 additions & 0 deletions Source/Lib/CompilerServices/CSharp/ParserCase/CSharpParserModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,18 @@ public CSharpParserModel(
public TokenWalker TokenWalker { get; }
public Stack<ISyntax> SyntaxStack { get; set; }
public CSharpStatementBuilder StatementBuilder { get; set; } = new();

/// <summary>
/// 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) {...}
/// </summary>
public List<(SyntaxKind DelimiterSyntaxKind, IExpressionNode ExpressionNode)> ExpressionList { get; set; }

public IExpressionNode? NoLongerRelevantExpressionNode { get; set; }
public List<SyntaxKind> TryParseExpressionSyntaxKindList { get; } = new();
public IExpressionNode ForceParseExpressionInitialPrimaryExpression { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ public static FunctionArgumentsListingNode HandleFunctionArguments(CSharpCompila

if (successTypeClauseNode)
{
// 'TypeClauseNode' or 'VariableDeclarationNode'
var successNameableToken = false;

if (UtilityApi.IsConvertibleToIdentifierToken(parserModel.TokenWalker.Current.SyntaxKind))
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>0.9.7.10</Version>
<Version>0.9.7.11</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading
Loading