MCVEs:
'This causes Parse Error, because 0 parameter)
Debug.Print Now _
()
'This causes Parse Error, because > 1 parameter)
Debug.Print Round _
(1, 2)
I'm unsure if passing 1 parameter is resolving the parentheses as Value cast of the parameter?
'This parses because exactly 1 parameter, but maybe isn't resolved correctly?
Debug.Print Round _
(1)
When there are certain valid lines after one of the invalid syntaxes, the Parser Error can report that the problematic line and column is several lines beyond where the invalid syntax actually occurs.
This apparently isn't only an issue with Debug.Print. This example also produces a compile error:
Sub Foo()
Dim x As Long
x = Bar _
()
End Sub
Public Function Bar()
Bar = 42
End Function
This was probably a regression caused by the grammar change that picked up ByVal parameters, because it doesn't have a problem with indexers:
'This parses fine.
Sub Foo()
Dim x() As String
x = Split("1,2,3", ",")
Debug.Print x _
(1)
End Sub
Same thing with arrays, although not with a Dim statement, but with a Redim
'This works
Dim a _
() As Long
'These don't
ReDim a _
(1, 2)
a _
(1, 2) = 5
Debug.Print a _
(1, 2)
Linking #2205, #2206
I had a look at the grammar and what parse trees it contructs from the examples.
The problem why the line continuation does not work for multiple or no characters is that a lExpression currently does not allow whitespace between the leading lExpression (the identifier) and the opening parenthesis.
That the version with one parameter parses is caused by a combination of two oddities in the grammar:
1) An endOfStatement can be empty.
2) A parenthesizedExpression can start with a whiteSpace, which can contain one or multiple line continuations.
In the example with x = Bar, the statement ends right there with an empty endOfStatement. The following line continuation then belongs to the parenthesizedExpression with the one identifier enclosed by parentheses.
I did a bit of experimenting and had to find out that this cannot be solved without a larger grammar rework.
First, to fix this, the indexExpr would need to get an optional whiteSpace between the leading lExpression and the opening parenthesis. However, this leads to problems with arguments passed byval in call statements.
Currently, a callStmt is defined to be either an eplicit call statement starting with the Call keyword or an expression optionally followed by whiteSpace and an argumentList. The problem is that in this case the expression must match less than in other cases , for the same input.
Sub CallStatement()
Something _
(bar)
End Sub
Sub Assignment()
Dim x As Variant
x = Something _
(bar)
End Sub
In the first example the expression must be an identifierExpression with content Something and the line continuation and (bar) a parenthesizedExpr. In the second example those together must be one indexExpr.
After adding the optional whiteSpace to indexExpr, the first example gets parsed as an indexExpr. (That is also the case without the line continuation.)
One idea to solve this would be to reorder the rules in the lExpression. However, that will not work immediately because the second example would probably be parsed as an identifier follewed by an empty endOfStatement followed by a parenthesizedExpr. So we would have to remove the possibility to be empty from an endOfStatement (adding EOF to the possibilities to not get problems at the end of the file.). Still, we would probably get a ton of other errors from the reordering and problems with the SSL parser.
An alternative approach would be to introduce a distinct calleeExpression. However, that will probably be quite an undertaking, both in the grammar and in the resolver.
One more thought about this problem. I think the problem is even more ambiguous than I thought.
If in the first example Something is an array, then this really has to be parsed as an indexExpr, since this is an index access to an array and not a byval argument passed to function or procedure used without returning a value. So, we basically have another flavor of the array/function ambiguity, here.
After the PR #2985, the one argument example no longer parses with an empty endOfStatement. However, it still parses, now as a callStmt consisting of the expression x = Bar followed by one argument consisting of the line continued parenthesizedExpr.)
To prevent such nonsensical parsing results, the rule for a callStmt would need to enforce that the expression is an lExpression whenever arguments are present. That is easy, but breaks the reference resolver. Accordingly, we could just as well introduce the calleeExpression. That is actually easy in the grammar, but the reference resolver will have to be adjusted. (That would include special treatment for the array access, which would become a calleeExpression followed by a ByVal argument instead of an indexExpr.
It tried some things and I have to completely reverse my last comment.
Adjusting the resolver to a changed grammar for callStmt is actually quite easy. What is really hard though is to not break the SLL parser when changing the grammar.
Most helpful comment
I did a bit of experimenting and had to find out that this cannot be solved without a larger grammar rework.
First, to fix this, the
indexExprwould need to get an optionalwhiteSpacebetween the leadinglExpressionand the opening parenthesis. However, this leads to problems with arguments passed byval in call statements.Currently, a
callStmtis defined to be either an eplicit call statement starting with theCallkeyword or anexpressionoptionally followed bywhiteSpaceand anargumentList. The problem is that in this case the expression must match less than in other cases , for the same input.In the first example the
expressionmust be anidentifierExpressionwith contentSomethingand the line continuation and(bar)aparenthesizedExpr. In the second example those together must be oneindexExpr.After adding the optional
whiteSpacetoindexExpr, the first example gets parsed as anindexExpr. (That is also the case without the line continuation.)One idea to solve this would be to reorder the rules in the
lExpression. However, that will not work immediately because the second example would probably be parsed as anidentifierfollewed by an emptyendOfStatementfollowed by aparenthesizedExpr. So we would have to remove the possibility to be empty from anendOfStatement(addingEOFto the possibilities to not get problems at the end of the file.). Still, we would probably get a ton of other errors from the reordering and problems with the SSL parser.An alternative approach would be to introduce a distinct
calleeExpression. However, that will probably be quite an undertaking, both in the grammar and in the resolver.