从任意纯文本中提取特定标记

本文关键字:提取 任意纯 文本 | 更新日期: 2023-09-27 18:04:44

我想解析纯文本注释并在其中查找某些标记。我正在寻找的标签类型如下:

<name#1234>

其中"name"是一个[a-z]字符串(来自固定列表),"1234"表示一个[0-9]+数字。这些标记可以在字符串中出现0次或多次,并被任意其他文本包围。例如,以下字符串都是有效的:

"Hello <foo#56> world!"
"<bar#1>!"
"1 &lt; 2"
"+<baz#99>+<squid#0> and also<baz#99>.'n'nBy the way, maybe <foo#9876>"

下列字符串都是无效的:

"1 < 2"
"<foo>"
"<bar#>"
"Hello <notinfixedlist#1234>"

最后一个无效,因为"notinfixedlist"不支持命名标识符。

我可以使用简单的正则表达式很容易地解析它,例如(为了简单起见,我省略了命名组):

<[a-z]+#'d+>

或直接指定一个固定列表:

<(foo|bar|baz|squid)#'d+>

但我想用鹿角有几个原因:

  • 我希望任何与该格式不匹配的内容都会导致解析错误,因此如果文本包含"<"或">"但与模式不匹配,则会失败。如果不是标签,这些字符必须分别转义为"<"answers">"。
  • 我可能会在将来扩展它以支持其他类型的模式(例如:"{foo+666}"或"[[@1234]]"),并希望避免regex语句的爆炸。有一个单一的语法文件,我可以扩展将是伟大的。
  • 我喜欢这样一个事实,即antlr4实现了访问者模式,当遇到特定类型的标记时,我的代码会被调用,而不是将不同的正则表达式拼凑在一起。

如何使用antlr4实现这样的语法?我看到的大多数示例都是针对遵循整个文本的精确规则的语言,而我只希望语法适用于任意文本中的匹配模式。

我想到了这个,我相信这是正确的:

grammar Tags;
parse 
    : ( tag | text )*
    ;
tag 
    : '<' fixedlist '#' ID '>'
    ;
fixedlist 
    : 'foo' 
    | 'bar' 
    | 'baz' 
    | 'squid';
text 
    : ~('<' | '>')+
    ;
ID
    : [0-9]+
    ;

正确吗?

从任意纯文本中提取特定标记

一般来说,所识别的问题通常被描述为孤岛语法问题——一个单独文档的各个部分由两个或多个不同的、通常相互模糊的规范描述。

ANTLR 4通过使用mode s直接支持岛语法。注意,模式只在拆分词法分析器/解析器语法中可用。

解析器

parser grammar TagsParser ;
options {
    tokenVocab = TagsLexer ;
}
parse   : ( tag | text )* EOF ;
tag     : LANGLE fixedlist GRIDLET ID RANGLE ;
text    : . ;
fixedlist
    : FOO
    | BAR
    | BAZ
    | SQUID
    ;

lexer

lexer grammar TagsLexer ;
LANGLE  : '<' -> pushMode(tag) ;
TEXT    : . ;
mode tag ;
    RANGLE  : '>' -> popMode ;
    FOO     : 'foo' ;
    BAR     : 'bar' ;
    BAZ     : 'baz' ;
    SQUID   : 'squid' ;
    GRIDLET : '#' ;
    ID      : [0-9]+ ;
    NONTAG  : . -> popMode ;

解析器中的text规则将匹配它上面的解析器规则先前未使用的所有令牌。这将包括所有的TEXT标记以及任何恰好与标签模式规则匹配但不是标签有效组成部分的文本。