【问题标题】:Switching to island mode on multi-character token在多字符令牌上切换到孤岛模式
【发布时间】:2015-09-27 23:25:24
【问题描述】:

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

假设“岛”是大括号之间的一切,“海”是一切不是的。像这样:

{(岛屿内容)}

那么这个简单的语法就起作用了:

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

Fluff
:
    ~[\{\}]+
;

....

但是我很难想出一个类似的解决方案来解决我想要为我的“岛”块打开复杂(多字符)的情况,就像这样:

{#(岛屿内容)}

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

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

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

如何让它发挥作用?


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

我的词法分析器语法:

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 '{'
    ;

【问题讨论】:

    标签: antlr antlr4


    【解决方案1】:

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

    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 '{'
                ;
    

    当它是相对于 IslandStart 的较长匹配时,Fluff alt2 有效。 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 一开始就有问题,但无论如何越简单越好。

    【讨论】:

    • 谢谢,这条规则似乎很好用。但是,我为每个字符获得了一个 Fluff 标记的实例。有没有办法在词法分析器而不是解析器中将它们全部(或至少其中一些)集中在一个大标记中?
    • 另外,我不完全确定 alt2 的用途。你能再解释一下吗?它是否使“{##”不是岛块的有效开启者?
    • @Dyppl - q1:您的实际语法实现可能存在问题 - 该示例应该为您提供“胖”标记; q2:完全正确 - 演示如何处理重叠序列(如果需要)。
    • 我编辑了我的问题,以给出一个语法示例,该语法使用您的标记定义并给了我许多标记,而不是“胖”标记。这很简单,不应该有实现问题。
    • 很抱歉拖了这么久,如果你愿意,我可以单独提出一个问题并给你一个链接。
    猜你喜欢
    • 2022-11-17
    • 2021-03-07
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多