我如何修改这个 ANTLR 语法以便特定的匹配模式得到它自己的结果?

How do I modify this ANTLR grammar so a particular match pattern gets its own production?

我正在尝试使用 this ANTLR grammar 为 Java 代码实现语法突出显示。我的策略是将代码解析为具有该语法的树,然后使用访问者遍历树中的每个终端并为其相应的文本分配颜色。这种颜色通常只是与终端令牌关联的颜色,但可以 覆盖 取决于上下文。例如,考虑来自 VSCode:

的截图

默认情况下,标识符为白色。但是,如果已知它们指的是 classes/methods,则它们会显示为绿色。我想通过默认将标识符标记为白色来在我的访问者中做出类似的区分,但用绿色覆盖 classes/methods.

到目前为止,我已经成功地为 class/method 声明 实现了这一点。 classDeclaration 的产生式规则如下所示:

classDeclaration
    :   'class' Identifier typeParameters?
        ('extends' typeType)?
        ('implements' typeList)?
        classBody
    ;

这里,Identifier 是终结符,而所有其他非文字都是非终结符。我的策略是用绿色 (1) 的 可覆盖令牌 为每个 child 终端着色。到最后一个学期,我在我的代码库中发明了一些东西来处理这个问题。本质上,无论上下文如何,关键字都应始终具有相同的颜色,因此它们的标记不可覆盖。标识符的颜色取决于上下文,因此它们具有默认值(白色),但您可以将它们设为绿色。上述产生式中仅有的终端是 'class'Identifier'extends''implements'。第一个和最后两个是关键字且不可覆盖,因此以下过程 (1) 仅将 class 名称着色为绿色。

Here是我用来实现上述策略的C#代码

不幸的是,在尝试突出显示 方法调用 时,此策略似乎有问题,例如上面的 blah.blah()Hereexpression:

的产生式规则
expression
    :   primary
    |   expression '.' Identifier
    |   expression '.' 'this'
    |   expression '.' 'new' nonWildcardTypeArguments? innerCreator
    |   expression '.' 'super' superSuffix
    |   expression '.' explicitGenericInvocation
    |   expression '[' expression ']'
    |   expression '(' expressionList? ')'
    |   // Lots of other stuff
    ;

这意味着 foo.bar() 解析为 (('foo') '.' 'bar') '(' ')'。如果,对于所有 expression,我将所有 Identifier children 都涂成绿色,那么 foo.bar() 将按预期具有 foo 白色和 bar 绿色. (注意foo是一个primary,它的终端不是expression的直接child。)但是,foo.bar也有foo 白色和 bar 绿色,这与上面 VSCode 的行为不符。

我试图通过为看起来像 expression '.' Identifier '(' expressionList? ')' 的表达式创建一个新产生式并从 expression.

引用它来解决这个问题
expression
    :   // ...
    |   expression '[' expression ']'
    |   invocationExpression
    |   // ...
    ;

invocationExpression
    :   expression '.' Identifier '(' expressionList? ')'
    |   expression '(' expressionList? ')'
    ;

然后,我将能够 运行 程序 (1) 针对我的访问者中的 invocationExpressions,将所有 child Identifiers 着色为绿色,这将使foo.bar() white-green 和 foo.bar white-white 符合预期。但是,ANTLR 正在抱怨,因为 expressioninvocationExpression 相互 left-recursive。我该如何克服这个问题,或者是否有其他方法可以解决这个问题?

据我所知,您只是创建了额外的规则,以便它生成另一个 Token,以便您的代码知道正在进行一个方法调用。

为此,您不必创建新规则。您可以改用标签。基本上这意味着给规则中的每个备选方案一个不同的标签,这样每个备选方案都会创建它自己的 Token。此外,ANTLR 将为每个备选方案创建额外的进入和退出方法。
Here 您可以在 ANTLR GitHub 页面上找到这些标签的描述。

你应该把这两个方面分成单独的步骤,而不是试图一次解决它。您首先需要的是一个符号 table,它包含有关语法实体的信息(例如 class 名称、变量名称、常量等)。当某些内容发生变化时,您可以在解析输入时创建它。这是完全孤立的。

当您的编辑器想要标记输入时(使用词法分析器,仅此而已!)您可以在符号中查找 table 如果您找到的标识符是已知实体名称并相应地更改颜色。