ANTLR4 RegEx 词法分析器模式
ANTLR4 RegEx lexer modes
我正在为 XSD 中的 RegEx 开发一个 Regx 解析器。
我之前的问题在这里描述:
从那时起我就拆分了 Lexer 和 Parser。
现在我在解析括号内的括号时遇到问题。它们应该被视为方括号内的字符,而在外方则被视为分组标记。
这是我的词法分析器语法:
lexer grammar RegExLexer;
Char : ALPHA ;
Int : DIGIT ;
LBrack : '[' ;//-> pushMode(modeRange) ;
RBrack : ']' ;//-> popMode ;
LBrace : '(' ;
RBrace : ')' ;
Semi : ';' ;
Comma : ',' ;
Asterisk: '*' ;
Plus : '+' ;
Dot : '.' ;
Dash : '-' ;
Question: '?' ;
LCBrace : '{' ;
RCBrace : '}' ;
Pipe : '|' ;
Esc : '\' ;
WS : [ \t\r\n]+ -> skip ;
fragment DIGIT : [0-9] ;
fragment ALPHA : [a-zA-Z] ;
这里是例子:
[0-9a-z()]+
我觉得我应该使用括号上的模式来改变 ALPHA 片段的行为。如果我复制该片段,我会收到一条错误消息,提示我不能声明两次。
我已经阅读了有关此的参考资料,但我仍然不明白我应该做什么。
如何实现这些模式?
您将不得不在解析器中处理这个问题,而不是在词法分析器中。当词法分析器看到 '(' 时,它会 return 标记 LBrace。对于词法分析器,没有关于在哪里看到标记的上下文。它只是将输入分割成标记。您将必须定义解析规则以及何时处理解析树,然后你可以确定是否是括号内的 LBrace。
这里有一个快速演示,说明如何使用 ANTLR4 的词法模式创建上下文敏感的词法分析器:
lexer grammar RegexLexer;
START_CHAR_CLASS
: '[' -> pushMode(CharClass)
;
START_GROUP
: '('
;
END_GROUP
: ')'
;
PLAIN_ATOM
: ~[()\[\]]
;
mode CharClass;
END_CHAR_CLASS
: ']' -> popMode
;
CHAR_CLASS_ATOM
: ~[\r\n\\]]
| '\' .
;
词法分析器生成后,可以使用下面的class进行测试:
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.Token;
public class Main {
public static void main(String[] args) {
RegexLexer lexer = new RegexLexer(new ANTLRInputStream("([()\]])"));
for (Token token : lexer.getAllTokens()) {
System.out.printf("%-20s %s\n", RegexLexer.VOCABULARY.getSymbolicName(token.getType()), token.getText());
}
}
}
如果您 运行 这个 Main class,以下内容将打印到您的控制台:
START_GROUP (
START_CHAR_CLASS [
CHAR_CLASS_ATOM (
CHAR_CLASS_ATOM )
CHAR_CLASS_ATOM \]
END_CHAR_CLASS ]
END_GROUP )
如您所见,(
和 )
在字符 class 外部的标记化与在字符内部的不同。
我正在为 XSD 中的 RegEx 开发一个 Regx 解析器。
我之前的问题在这里描述:
从那时起我就拆分了 Lexer 和 Parser。 现在我在解析括号内的括号时遇到问题。它们应该被视为方括号内的字符,而在外方则被视为分组标记。 这是我的词法分析器语法:
lexer grammar RegExLexer;
Char : ALPHA ;
Int : DIGIT ;
LBrack : '[' ;//-> pushMode(modeRange) ;
RBrack : ']' ;//-> popMode ;
LBrace : '(' ;
RBrace : ')' ;
Semi : ';' ;
Comma : ',' ;
Asterisk: '*' ;
Plus : '+' ;
Dot : '.' ;
Dash : '-' ;
Question: '?' ;
LCBrace : '{' ;
RCBrace : '}' ;
Pipe : '|' ;
Esc : '\' ;
WS : [ \t\r\n]+ -> skip ;
fragment DIGIT : [0-9] ;
fragment ALPHA : [a-zA-Z] ;
这里是例子:
[0-9a-z()]+
我觉得我应该使用括号上的模式来改变 ALPHA 片段的行为。如果我复制该片段,我会收到一条错误消息,提示我不能声明两次。 我已经阅读了有关此的参考资料,但我仍然不明白我应该做什么。
如何实现这些模式?
您将不得不在解析器中处理这个问题,而不是在词法分析器中。当词法分析器看到 '(' 时,它会 return 标记 LBrace。对于词法分析器,没有关于在哪里看到标记的上下文。它只是将输入分割成标记。您将必须定义解析规则以及何时处理解析树,然后你可以确定是否是括号内的 LBrace。
这里有一个快速演示,说明如何使用 ANTLR4 的词法模式创建上下文敏感的词法分析器:
lexer grammar RegexLexer;
START_CHAR_CLASS
: '[' -> pushMode(CharClass)
;
START_GROUP
: '('
;
END_GROUP
: ')'
;
PLAIN_ATOM
: ~[()\[\]]
;
mode CharClass;
END_CHAR_CLASS
: ']' -> popMode
;
CHAR_CLASS_ATOM
: ~[\r\n\\]]
| '\' .
;
词法分析器生成后,可以使用下面的class进行测试:
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.Token;
public class Main {
public static void main(String[] args) {
RegexLexer lexer = new RegexLexer(new ANTLRInputStream("([()\]])"));
for (Token token : lexer.getAllTokens()) {
System.out.printf("%-20s %s\n", RegexLexer.VOCABULARY.getSymbolicName(token.getType()), token.getText());
}
}
}
如果您 运行 这个 Main class,以下内容将打印到您的控制台:
START_GROUP (
START_CHAR_CLASS [
CHAR_CLASS_ATOM (
CHAR_CLASS_ATOM )
CHAR_CLASS_ATOM \]
END_CHAR_CLASS ]
END_GROUP )
如您所见,(
和 )
在字符 class 外部的标记化与在字符内部的不同。