gpt4 book ai didi

parsing - 如何区分数组和过程调用?

转载 作者:行者123 更新时间:2023-12-03 00:32:00 27 4
gpt4 key购买 nike

上下文

我正在解析代码,其中...

  • 此代码输出索引 i 处数组 a 第一维的内容:

    Debug.Print a(i, 1)
  • 此代码输出给定参数 i1 的函数 a 的结果:

    Debug.Print a(i, 1)
  • 此代码调用过程 DoSomething,同时将 foo 评估为值并将其按值传递给过程(无论签名是否将其作为“通过引用”参数):

    DoSomething (foo)
  • 此代码调用过程 DoSomething,而不将 foo 计算为值,并且如果签名“通过引用”采用参数,则通过引用传递它:

    Call DoSomething(foo)

所以我的 lExpression 解析器规则是有问题的,因为第一个替代方案 (#indexExpr) 匹配数组和过程调用:

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
;
<小时/>

问题

我试图在这里解决的具体问题,最好由我从用此代码引发的解析异常中得到的堆栈跟踪来描述:

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

failing test stack trace

我可以看到 callStmt() 规则按其应有的方式启动,但是随后用于匹配 DoSomething表达式 却匹配了#lExpr 捕获应该是“参数列表”的内容,但却被作为数组索引获取。

我尝试过的一切,从将 #parenthesizedExpr 移动到比 #lExpr 更高的优先级,到制定 memberExpression 规则并在 callStmt 规则中使用它来代替 expression 已失败(项目构建,但我最终得到了 1500 个失败的测试,因为不再解析任何内容)。

#lExpr 匹配 DoSomething (foo) 的原因是,那里有一个 indexExpr 是完全合法的 - 它就像如果我需要某种方法来忽略解析中的规则,但前提是我知道谱系中存在 callStmt

是否有可能消除 a(i, 1) (数组调用)与 a(i, 1) (函数调用)的歧义?

如果是这样...怎么办?

<小时/>

其他上下文

下面是调用 lExpression 规则的 expression 规则:

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)
;

还有 callStmt 规则,这意味着仅接收过程调用(前面可能有也可能没有 Call 关键字):

callStmt :
CALL whiteSpace expression
| expression (whiteSpace argumentList)?
;

最佳答案

(我已经构建了 VB6/VBA 解析器)。

不,您无法在解析时进行区分,正是因为函数调用和数组访问的语法是相同的,使用纯粹的上下文无关解析引擎。

简单的做法是将构造简单地解析为 array_access_or_function_call ,并在通过后处理树进行解析后消除歧义,发现其范围包含引用的实体的声明(例如构建符号表)(咨询符号表),并用它来决定。

这个问题并不是 VB 独有的; C and C++ famously have a similar problem 。大多数 C/C++ 解析器使用的解决方案是让解析器在解析时收集声明信息作为副作用,然后在遇到实例语法时引用该信息来做出决定。
这种方法将解析器更改为上下文相关的解析器。缺点是它使(至少部分)符号表构建与解析纠缠在一起,并且您的解析引擎可能会或可能不会合作,这或多或少难以实现。

(我认为 ANTLR 会让您在解析过程中的各个点调用任意代码,这些代码可用于保存声明信息,并且 ANTLR 会让您调用解析时谓词来帮助指导解析器;这些应该足够了] .

我更喜欢解析然后解析方法,因为它更干净且更易于维护。

关于parsing - 如何区分数组和过程调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40583141/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com