如何使用 ANTLR4 突出显示 QScintilla?

How to highlight QScintilla using ANTLR4?

我正在努力学习 ANTLR4,但我的第一个实验已经遇到了一些问题。

这里的目标是学习如何使用 ANTLR 来语法高亮 QScintilla 组件。为了练习一下,我决定学习如何正确突出显示 *.ini 个文件。

首先,为了 运行 mcve,您需要:

来自 运行宁 antlr ini.g4 -Dlanguage=Python3 -o ini

和运行它,如果一切顺利你应该得到这个结果:

这是我的问题:

你可以在那个截图上看到变量赋值的突出显示是不同的(variable=deeppink and values=yellowish)但我不知道如何实现,我试过使用这个稍微修改过的语法:

grammar ini;

start : section (option)*;
section : '[' STRING ']';
option : VARIABLE '=' VALUE;

COMMENT : ';'  ~[\r\n]*;
VARIABLE  : [a-zA-Z0-9]+;
VALUE  : [a-zA-Z0-9]+;
WS      : [ \t\n\r]+;

然后将样式更改为:

style = {
    "T__0": lst[3],
    "T__1": lst[3],
    "T__2": lst[3],
    "COMMENT": lst[2],
    "VARIABLE": lst[0],
    "VALUE": lst[1],
    "WS": lst[3],
}

但是如果您查看词法分析输出,您会发现 VARIABLEVALUES 之间没有区别,因为在 ANTLR 语法中顺序优先。所以我的问题是,您将如何修改 grammar/snippet 以实现这种视觉效果?

Sctintilla 中的语法高亮是由专用高亮器 类 完成的,它们是词法分析器。解析器不太适合此类工作,因为语法高亮功能必须有效,即使输入包含错误。解析器是一种验证输入正确性的工具 - 2 个完全不同的任务。

所以我建议您停止考虑为此使用 ANTLR4,只需使用现有的 Lex 之一 类 并为您想要突出显示的语言创建一个新的。

问题在于词法分析器需要对上下文敏感:= 左侧的所有内容都需要是变量,而其右侧是值。您可以使用 ANTLR 的 lexical modes 来完成此操作。您首先将连续的 non-spaces 分类为变量,当遇到 = 时,您进入 value-mode。当在 value-mode 内时,只要遇到换行符就会退出此模式。

请注意,词法模式仅适用于词法分析器语法,不适用于您现在拥有的组合语法。此外,对于语法突出显示,您可能只需要词法分析器。

这是一个如何工作的快速演示(将其粘贴在名为 IniLexer.g4 的文件中):

lexer grammar IniLexer;

SECTION
 : '[' ~[\]]+ ']'
 ;

COMMENT
 : ';' ~[\r\n]*
 ;

ASSIGN
 : '=' -> pushMode(VALUE_MODE)
 ;

KEY
 : ~[ \t\r\n]+
 ;

SPACES
 : [ \t\r\n]+ -> skip
 ;

UNRECOGNIZED
 : .
 ;

mode VALUE_MODE;

  VALUE_MODE_SPACES
   : [ \t]+ -> skip
   ;

  VALUE
   : ~[ \t\r\n]+
   ;

  VALUE_MODE_COMMENT
   : ';' ~[\r\n]* -> type(COMMENT)
   ;

  VALUE_MODE_NL
   : [\r\n]+ -> skip, popMode
   ;

如果您现在 运行 以下脚本:

source = """
; Comment outside

[section s1]
; Comment inside
a = 1
b = 2

[section s2]
c = 3 ; Comment right side
d = e
"""

lexer = IniLexer(InputStream(source))
stream = CommonTokenStream(lexer)
stream.fill()

for token in stream.tokens[:-1]:
    print("{0:<25} '{1}'".format(IniLexer.symbolicNames[token.type], token.text))

您将看到以下输出:

COMMENT                   '; Comment outside'
SECTION                   '[section s1]'
COMMENT                   '; Comment inside'
KEY                       'a'
ASSIGN                    '='
VALUE                     '1'
KEY                       'b'
ASSIGN                    '='
VALUE                     '2'
SECTION                   '[section s2]'
KEY                       'c'
ASSIGN                    '='
VALUE                     '3'
COMMENT                   '; Comment right side'
KEY                       'd'
ASSIGN                    '='
VALUE                     'e'

附带的解析器语法可能如下所示:

parser grammar IniParser;

options {
  tokenVocab=IniLexer;
}

sections
 : section* EOF
 ;

section
 : COMMENT
 | SECTION section_atom*
 ;

section_atom
 : COMMENT
 | KEY ASSIGN VALUE
 ;

这将在以下解析树中解析您的示例输入:

我已经用 C++ 实现了类似的东西。

https://github.com/tora-tool/tora/blob/master/src/editor/tosqltext.cpp

Sub-classed QScintilla class 并根据 ANTLR 生成的源实施了自定义词法分析器。

你甚至可以使用 ANTLR 解析器(我没有使用它),QScitilla 允许你有多个分析器(具有不同的权重),因此你可以定期对文本进行一些语义检查。在 QScintilla 中不能轻易完成的是将 token 与一些额外的数据相关联。