在antlr而不是令牌中获取纯文本

时间:2018-03-19 14:14:35

标签: antlr antlr4

我正在尝试使用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' ;

除了各种标签之外,我还需要实现一个规则来获取不在任何标签内的文本。对于当前的语法来说,事情似乎工作得很好,但是因为&#39;文本&#39;规则属于Lexer方面,输入的任何文本都是标记化的,我得到一个令牌列表,而不是单个字符串令牌。 intellij中的antlr分析器还显示每个令牌的模糊调用。

  

例如,&#39;你好,你好吗?&#39;需要是一个令牌,而不是由这个语法生成的多个令牌。

我想我可能会看错了角度,并且想知道是否还有其他办法来处理这些文字&#39;规则。

1 个答案:

答案 0 :(得分:1)

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

  

例如,&#39;你好,你好吗?&#39;需要是一个令牌,而不是由这个语法生成的多个令牌。

您无法在当前设置中执行此操作。你可以做的是利用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;] |&#39;&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
  

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

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