使用Antlr4解析任意分隔符

时间:2016-07-06 07:45:57

标签: regex antlr4

我尝试在Antlr4中创建一个语法,该语法接受由任意字符分隔的正则表达式(类似于Perl)。我怎样才能做到这一点?

要明确:我的问题不是正则表达式本身(我实际上不在Antlr中处理,而是在访问者中),而是分隔符字符。我可以轻松地为词法分析器定义以下规则:

$scope.$broadcast('event',$scope.item, $scope.item);

这将使用正斜杠作为分隔符(就像它在Perl中常用)。但是,我还希望能够将正则表达式编写为REGEXP: '/' (ESC_SEQ | ~('\\' | '/'))+ '/' ; fragment ESC_SEQ: '\\' . ; (这在Perl中也是可能的)。

如果我必须使用正则表达式本身来解决这个问题,我会使用这样的反向引用:

m~regexp~

(" m",后跟任意字符,后跟表达式,后跟相同的任意字符)。但Antlr4似乎没有反向引用。

当我可以使用括号对时,即m(.)(.+?)\1 m(regexp),情况会更好。但由于可能的括号类型数量非常少,因此可以通过简单列举所有不同的变体来解决这个问题。

这可以用Antlr4解决吗?

1 个答案:

答案 0 :(得分:3)

你可以这样做:

lexer grammar TLexer;

REGEX
 : REGEX_DELIMITER ( {getText().charAt(0) != _input.LA(1)}? REGEX_ATOM )+ {getText().charAt(0) == _input.LA(1)}? .
 | '{' REGEX_ATOM+ '}'
 | '(' REGEX_ATOM+ ')'
 ;

ANY
 : .
 ;

fragment REGEX_DELIMITER
 : [/~@#]
 ;

fragment REGEX_ATOM
 : '\\' .
 | ~[\\]
 ;

如果您运行以下类:

public class Main {

  public static void main(String[] args) throws Exception {

    TLexer lexer = new TLexer(new ANTLRInputStream("/foo/ /bar\\ ~\\~~ {mu} (bla("));

    for (Token t : lexer.getAllTokens()) {
      System.out.printf("%-20s %s\n", TLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText().replace("\n", "\\n"));
    }
  }
}

您将看到以下输出:

REGEX                /foo/
ANY                   
ANY                  /
ANY                  b
ANY                  a
ANY                  r
ANY                  \
ANY                   
REGEX                ~\~~
ANY                   
REGEX                {mu}
ANY                   
ANY                  (
ANY                  b
ANY                  l
ANY                  a
ANY                  (

{...}?被称为谓词:

( {getText().charAt(0) != _input.LA(1)}? REGEX_ATOM )+部分告诉词法分析器继续匹配字符,只要REGEX_DELIMITER匹配的字符不在字符流中。并且{getText().charAt(0) == _input.LA(1)}? .确保实际上有第一个字符匹配的结束分隔符(当然是REGEX_DELIMITER)。

使用ANTLR 4.5.3进行测试

修改

要获得一个前缀为m +一些可选空格的分隔符,你可以试试这样的东西(未经测试!):

lexer grammar TLexer;

  @lexer::members {
    boolean delimiterAhead(String start) {
      return start.replaceAll("^m[ \t]*", "").charAt(0) == _input.LA(1);
    }
  }

  REGEX
   : '/' ( '\\' . | ~[/\\] )+ '/'
   | 'm' SPACES? REGEX_DELIMITER ( {!delimiterAhead(getText())}? ( '\\' . | ~[\\] ) )+ {delimiterAhead(getText())}? .
   | 'm' SPACES? '{' ( '\\' . | ~'}' )+ '}'
   | 'm' SPACES? '(' ( '\\' . | ~')' )+ ')'
   ;

  ANY
   : .
   ;

  fragment REGEX_DELIMITER
   : [~@#]
   ;

  fragment SPACES
   : [ \t]+
   ;