【问题标题】:Getting plain text in antlr instead of tokens在 antlr 中获取纯文本而不是令牌
【发布时间】:2023-03-08 22:31:02
【问题描述】:

我正在尝试使用 antlr 创建解析器。我的语法如下。

code : codeBlock* EOF;

codeBlock
: text
| tag1Ops
| tag2Ops
;

tag1Ops: START_1_TAG ID END_2_TAG ;

tag2Ops: START_2_TAG ID END_2_TAG ;

text: ~(START_1_TAG|START_2_TAG)+;

START_1_TAG : '<%' ;
END_1_TAG : '%>' ;
START_2_TAG : '<<';
END_2_TAG : '>>' ;

ID : [A-Za-z_][A-Za-z0-9_]*;
INT_NUMBER: [0-9]+;

WS :  ( ' ' | '\n' | '\r' | '\t')+ -> channel(HIDDEN);

SPACES: SPACE+;

ANY_CHAR : .;

fragment SPACE : ' ' | '\r' | '\n' | '\t' ;

除了各种标签,我还需要实现一个规则来获取不在任何标签内的文本。当前语法似乎一切正常,但由于“文本”规则属于 Lexer 方面,因此输入的任何文本都被标记化,我得到一个标记列表,而不是单个字符串标记。 intellij 中的 antlr 分析器还显示了对每个令牌的模棱两可的调用。

例如,“嗨,你好,你好吗??”需要是单个token,而不是多个token,由这个语法生成。

我想我可能看错了角度,想知道是否有其他方法来处理“文本”规则。

【问题讨论】:

    标签: antlr antlr4


    【解决方案1】:

    首先:你有一个 WS 规则,将空格字符放在隐藏通道上,但在语法的后面,你有一个 SPACES 规则。鉴于此 SPACES 规则位于 WS 之后并且完全匹配,SPACES 规则将永远不会匹配。

    例如,“嗨,你好,你好吗??”需要是单个token,而不是多个token,由这个语法生成。

    您无法在当前设置中执行此操作。你可以做的是利用lexical modes。快速演示:

    // Must be in a separate file called DemoLexer.g4
    lexer grammar DemoLexer;
    
    START_1_TAG : '<%' -> pushMode(IN_TAG);
    START_2_TAG : '<<' -> pushMode(IN_TAG);
    TEXT        : ( ~[<] | '<' ~[<%] )+;
    
    mode IN_TAG;
      ID         : [A-Za-z_][A-Za-z0-9_]*;
      INT_NUMBER : [0-9]+;
      END_1_TAG  : '%>' -> popMode;
      END_2_TAG  : '>>' -> popMode;
      SPACE      : [ \t\r\n] -> channel(HIDDEN);
    

    要测试这个词法分析器,运行这个类:

    import org.antlr.v4.runtime.*;
    
    public class Main {
    
      public static void main(String[] args) {
    
        String source = "<%FOO%>FOO BAR<<123>>456 mu!";
        DemoLexer lexer = new DemoLexer(CharStreams.fromString(source));
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        tokenStream.fill();
    
        for (Token t : tokenStream.getTokens()) {
          System.out.printf("%-20s %s\n", DemoLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
        }
      }
    }
    

    将打印:

    START_1_TAG          <%
    ID                   FOO
    END_1_TAG            %>
    TEXT                 FOO BAR
    START_2_TAG          <<
    INT_NUMBER           123
    END_2_TAG            >>
    TEXT                 456 mu!
    EOF                  <EOF>
    

    在单独的解析器语法中使用您的词法分析器语法,如下所示:

    // Must be in a separate file called DemoParser.g4
    parser grammar DemoParser;
    
    options {
      tokenVocab=DemoLexer;
    }
    
    code
     : codeBlock* EOF
     ;
    
    ...
    

    编辑

    [...] 但我对 TEXT 有点困惑: ( ~[ 规则。你能详细说明它的作用吗?

    ( ~[&lt;] | '&lt;' ~[&lt;%] )+的细分:

    (            # start group
      ~[<]       #   match any char other than '<'
      |          #   OR
      '<' ~[<%]  #   match a '<' followed by any char other than '<' and '%'
    )+           # end group, and repeat it once or more
    

    而且,词汇模式可以被视为语义谓词的替代品吗?

    有点。语义谓词更强大:您可以通过纯代码检查您喜欢的任何内容。但是,一个很大的缺点是您在语法中混合了目标特定的代码,而词法模式适用于所有目标。因此,经验法则是尽可能避免使用谓词。

    【讨论】:

    • 这种方法似乎有效......但我对 TEXT 有点困惑:( ~[规则。你能详细说明它的作用吗?而且,词汇模式可以被视为语义谓词的替代品吗?
    • @john 检查我的编辑
    • 感谢您的解释。
    • 没问题,约翰。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多