在多字符令牌上切换到孤岛模式

Switching to island mode on multi-character token

我正在研究一种基本上是孤岛语法的语法。

假设 "island" 是大括号之间的所有内容,"sea" 是不是所有内容。像这样:

{(岛屿内容)}

那么这个简单的语法就可以工作了:

IslandStart
:
    '{' -> pushMode(Island)
;

Fluff
:
    ~[\{\}]+
;

....

但是我很难想出一个类似的解决方案来解决我想要为我的 "island" 块打开复杂(多字符)的情况,如下所示:

{#(岛屿内容)}

在这种情况下,我不知道如何为 "Fluff" 制定规则(除了我的开场序列之外的所有内容)。

IslandStart
    :
        '{#' -> pushMode(Island)
    ;

Fluff
    :
        ~[\{\}]+ /* Should now include opening braces as well 
                    if they are not immaediately followed by # sign */
    ;

如何让它发挥作用?


编辑:GRosenberg 提出了一个解决方案,但我得到了很多令牌(每个字符一个)。这是演示此行为的示例:

我的词法分析器语法:

lexer grammar Demolex;

IslandStart
    :
        '{$' -> pushMode(Island)
    ;


Fluff
    : 
          '{' ~'$' .* // any 2+ char seq that starts with '{', but not '{#'
        | '{' '$$' .* // starts with hypothetical not IslandStart marker
        | '{'         // just the 1 char 
        | .*? ~'{'    // minimum sequence that ends before an '{'
    ;

mode Island;

IslandEnd
    :
        '}' -> popMode
    ;

最简单的解析器语法:

grammar Demo;
options { tokenVocab = Demolex; }

template
    :
        Fluff+
    ;

当我在 Eclipse 的 antlr4 插件中调试它时,这会从输入 "somanytokens" 生成一个包含大量标记的树:

不太可能是插件问题。我可以很容易地想出一个令牌定义,它会在树中产生一个大胖令牌。

实际上,即使是最简单的语法形式也会给出这样的结果:

grammar Demo2;

template4
    :
        Fluff+
    ;

Fluff
    : 
         .*? ~'{'    // minimum sequence that ends before an '{'
    ;

只需要指定序列差的补码即可:

IslandStart : '{#' -> pushMode(Island) ;

Fluff       : '{' ~'#' .* // any 2+ char seq that starts with '{', but not '{#'
            | '{' '##' .* // starts with hypothetical not IslandStart marker
            | '{'         // just the 1 char 
            | .*? ~'{'    // minimum sequence that ends before an '{'
            ;

当 Fluff alt2 是相对于 IslandStart 的较长匹配时,它会起作用。 Fluff alt3 仅在 IslandStart 和 Fluff alt1 不匹配以“{”开头的字符序列时才起作用。 Fluff alt4 是包含“{”之前但不包括“{”的内容的包罗万象,允许词法分析器考虑在“{”上对齐的序列。

更新

让它成为一个更合理完整的示例语法

parser grammar TestParser;

options{
    tokenVocab=TestLexer;
}

template : ( Fluff | Stuff )+ EOF ;

lexer grammar TestLexer;

IslandStart : '{' '$' -> pushMode(Island),more ;

Fluff : '{' ~'$' ~'{'*? '}'     // any 2+ char seq that starts with '{', but not '{$'
      | '{' '$' '$' ~'{'*? '}'  // or starts with hypothetical not IslandStart marker
      | '{' '}'                 // just the empty pair
      | ~'{'+                   // minimum sequence that ends before an '{'
      ;

mode Island;

Stuff : '}' -> popMode ;
Char  : .   -> more    ;

输入 so{$Island}many{}tokens{$$notIsland}and{inner}end

令牌转储:

Fluff: [@0,0:1='so',<1>,1:0]
Stuff: [@1,2:10='{$Island}',<2>,1:2]
Fluff: [@2,11:14='many',<1>,1:11]
Fluff: [@3,15:16='{}',<1>,1:15]
Fluff: [@4,17:22='tokens',<1>,1:17]
Fluff: [@5,23:35='{$$notIsland}',<1>,1:23]
Fluff: [@6,36:38='and',<1>,1:36]
Fluff: [@7,39:45='{inner}',<1>,1:39]
Fluff: [@8,46:48='end',<1>,1:46]

解析树:

(template so {$Island} many {} tokens {$$notIsland} and {inner} end <EOF>)

词法分析器规则的操作保持不变。进行了更改以适应正确的双亲匹配终端。 Alt4,作为简化,按最初的预期工作。不完全确定为什么一开始对 Antlr 来说是个问题,但无论如何越简单越好。