使用 Antlr4 解析任意分隔符
Parse arbitrary delimiter character using Antlr4
我尝试在 Antlr4 中创建一个语法,它接受正则表达式 由任意字符 分隔(类似于 Perl)。我怎样才能做到这一点?
需要说明的是:我的问题不是正则表达式本身(我实际上不在 Antlr 中处理,而是在访问者中处理),而是分隔符。我可以轻松地为词法分析器定义以下规则:
REGEXP: '/' (ESC_SEQ | ~('\' | '/'))+ '/' ;
fragment ESC_SEQ: '\' . ;
这将使用正斜杠作为分隔符(就像在 Perl 中常用的那样)。但是,我也希望能够将正则表达式编写为 m~regexp~
(这在 Perl 中也是可能的)。
如果我必须使用正则表达式本身来解决这个问题,我会使用这样的反向引用:
m(.)(.+?)
(这是一个 "m",后跟一个任意字符,然后是表达式,然后是 相同的任意字符 )。但是反向引用在Antlr4中似乎不可用。
如果我可以使用成对的括号,即 m(regexp)
或 m{regexp}
,那就更好了。但是由于可能的括号类型的数量非常少,这可以通过简单地枚举所有不同的变体来解决。
这个可以用Antlr4解决吗?
你可以这样做:
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
: '\' .
| ~[\]
;
如果你运行以下class:
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 (
{...}?
称为谓词:
- Syntax of semantic predicates in Antlr4
- Semantic predicates in ANTLR4?
( {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]+
;
我尝试在 Antlr4 中创建一个语法,它接受正则表达式 由任意字符 分隔(类似于 Perl)。我怎样才能做到这一点?
需要说明的是:我的问题不是正则表达式本身(我实际上不在 Antlr 中处理,而是在访问者中处理),而是分隔符。我可以轻松地为词法分析器定义以下规则:
REGEXP: '/' (ESC_SEQ | ~('\' | '/'))+ '/' ;
fragment ESC_SEQ: '\' . ;
这将使用正斜杠作为分隔符(就像在 Perl 中常用的那样)。但是,我也希望能够将正则表达式编写为 m~regexp~
(这在 Perl 中也是可能的)。
如果我必须使用正则表达式本身来解决这个问题,我会使用这样的反向引用:
m(.)(.+?)
(这是一个 "m",后跟一个任意字符,然后是表达式,然后是 相同的任意字符 )。但是反向引用在Antlr4中似乎不可用。
如果我可以使用成对的括号,即 m(regexp)
或 m{regexp}
,那就更好了。但是由于可能的括号类型的数量非常少,这可以通过简单地枚举所有不同的变体来解决。
这个可以用Antlr4解决吗?
你可以这样做:
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
: '\' .
| ~[\]
;
如果你运行以下class:
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 (
{...}?
称为谓词:
- Syntax of semantic predicates in Antlr4
- Semantic predicates in ANTLR4?
( {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]+
;