How to report an array from a procedure call?

Context

I am parsing vba code where ...

  • This code outputs the contents of the first dimension of array a to index i :

     Debug.Print a(i, 1) 
  • This code displays the result of the function a with the given parameters i and 1 :

     Debug.Print a(i, 1) 
  • This code calls the DoSomething procedure when evaluating foo as a value and passes it by value to the procedure (regardless of whether the parameter has a signature "by reference"):

     DoSomething (foo) 
  • This code calls the DoSomething procedure without evaluating foo as a value and passing it by reference if the signature accepts the "by reference" parameter:

     Call DoSomething(foo) 

So, I have this lExpression parser rule, which is problematic because the first alternative ( #indexExpr ) matches both the array and the procedure call:

 lExpression : lExpression whiteSpace? LPAREN whiteSpace? argumentList? whiteSpace? RPAREN # indexExpr | lExpression mandatoryLineContinuation? DOT mandatoryLineContinuation? unrestrictedIdentifier # memberAccessExpr | lExpression mandatoryLineContinuation? EXCLAMATIONPOINT mandatoryLineContinuation? unrestrictedIdentifier # dictionaryAccessExpr | ME # instanceExpr | identifier # simpleNameExpr | DOT mandatoryLineContinuation? unrestrictedIdentifier # withMemberAccessExpr | EXCLAMATIONPOINT mandatoryLineContinuation? unrestrictedIdentifier # withDictionaryAccessExpr ; 

Problem

The specific problem I'm trying to fix here is best described by the stack trace that I get from the parsing exception created with this code:

 Sub Test() DoSomething (foo), bar End Sub 

failed stack stack trace

I can see the callStmt() rule, as it should, but then the expression , which must match DoSomething , matches #lExpr , which fixes what should be a "list of arguments", but instead gets selected as the index of the array.

Everything I tried, from moving #parenthesizedExpr to a higher priority than #lExpr , to the memberExpression rule and using it instead of expression in the callStmt rule, failed (project, but I end up with 1500 failed tests because I don't analyze anything else) .

The reason #lExpr corresponds to DoSomething (foo) , in particular, because itโ€™s quite logical to have indexExpr there - as if I had to somehow ignore the rule in parsing, but only when I know that there callStmt in lines.

Is it also possible to fix the problem a(i, 1) (array call) from a(i, 1) (function call)?

If so ... how?


Additional context

Here's the expression rule from which the lExpression rule is called:

 expression : // Literal Expression has to come before lExpression, otherwise it'll be classified as simple name expression instead. literalExpression # literalExpr | lExpression # lExpr | builtInType # builtInTypeExpr | LPAREN whiteSpace? expression whiteSpace? RPAREN # parenthesizedExpr | TYPEOF whiteSpace expression # typeofexpr // To make the grammar SLL, the type-of-is-expression is actually the child of an IS relational op. | NEW whiteSpace expression # newExpr | expression whiteSpace? POW whiteSpace? expression # powOp | MINUS whiteSpace? expression # unaryMinusOp | expression whiteSpace? (MULT | DIV) whiteSpace? expression # multOp | expression whiteSpace? INTDIV whiteSpace? expression # intDivOp | expression whiteSpace? MOD whiteSpace? expression # modOp | expression whiteSpace? (PLUS | MINUS) whiteSpace? expression # addOp | expression whiteSpace? AMPERSAND whiteSpace? expression # concatOp | expression whiteSpace? (EQ | NEQ | LT | GT | LEQ | GEQ | LIKE | IS) whiteSpace? expression # relationalOp | NOT whiteSpace? expression # logicalNotOp | expression whiteSpace? AND whiteSpace? expression # logicalAndOp | expression whiteSpace? OR whiteSpace? expression # logicalOrOp | expression whiteSpace? XOR whiteSpace? expression # logicalXorOp | expression whiteSpace? EQV whiteSpace? expression # logicalEqvOp | expression whiteSpace? IMP whiteSpace? expression # logicalImpOp | HASH expression # markedFileNumberExpr // Added to support special forms such as Input(file1, #file1) ; 

And the callStmt rule, which means only receiving procedure calls (which may or may not be preceded by the Call keyword):

 callStmt : CALL whiteSpace expression | expression (whiteSpace argumentList)? ; 
+5
source share
2 answers

(I built the VB6 / VBA parser).

No, you cannot distinguish during parsing, precisely because the syntax of the function call and access to the array are identical, using a pure analysis mechanism without context.

The easiest way is to simply analyze the construct as array_access_or_function_call and eliminate it after it has been processed by post-processing the tree by opening the object declaration (for example, building a symbol table), the volume of which contains a link (consulting symbol table) and using this solution.

This issue is not unique to VB; C and C ++ have a similar problem . The solution used in most C / C ++ parsers should contain parser collection declaration information as a side effect of the analysis, and then consult this information when it encounters instance syntax. This approach changes the parser to context-sensitive. The disadvantage is that it links (at least partially) the construction of the symbol table with parsing, and your parsing mechanism may or may not interact, making it more or less inconvenient to implement.

(I think ANTLR will allow you to call arbitrary code at different points in the parsing process, which can be used to store declaration information, and ANTLR will allow you to call parsing predicates to help the parser, that should be enough].

I prefer the parsing approach because it is cleaner and easier to maintain.

+3
source

You cannot define an array from a procedure call. Even at the time of resolution, you still cannot know, since the subtype of the variable may change already at run time.

This example shows the effect of default members accepting optional arguments

  Dim var As Variant Set var = Range("A1:B2") Debug.Print var(1, 1) 'Access the _Default/Item property with indice arguments var = var 'Accesses the _Default/Item property without arguments Debug.Print var(1, 1) 'Array indices 

You cannot even reliably say whether the result of a procedure is a procedure call or an array index:

  Dim var1 As Variant Set var1 = New Dictionary Dim var2 As Variant Set var2 = New Dictionary var2.Add 0, "Foo" var1.Add 0, var2 Debug.Print var1(0)(0) 'Accesses the default/Item of the default/Item var1 = Array(Array(1)) Debug.Print var1(0)(0) 'Accesses the first index of the first index 

You need to process the blocks in brackets that follow the variable name, possibly belonging to a procedure or array. In fact, it may even be useful to think about accessing a member of the array as if it had a default Item element. Thus, an array is no different from an object with a default element, which requires arguments that are indexes (and, occasionally, have special constructor syntaxes).

+3
source

Source: https://habr.com/ru/post/1259706/


All Articles