从任意纯文本中提取特定标签
Extracing specific tags from arbitrary plain text
我想解析纯文本评论并在其中查找某些标签。我正在寻找的标签类型如下:
<name#1234>
其中 "name" 是 [a-z] 字符串(来自固定列表),“1234”表示 [0-9]+ 数字。这些标签可以在字符串中出现零次或多次,并被任意其他文本包围。例如,以下字符串都是有效的:
"Hello <foo#56> world!"
"<bar#1>!"
"1 < 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+>
但我想使用 antlr 有几个原因:
- 我希望任何与该格式不匹配的内容都会导致解析错误,因此如果文本包含“<”或“>”但与模式不匹配,则会失败。这些字符必须转义为“<”和“>”分别如果它不是标签。
- 我将来可能会扩展它以支持其他类型的模式(例如:“{foo+666}”或“[[@1234]]”,并希望避免正则表达式语句的激增。有一个我可以扩展的单个语法文件会很棒。
- 我喜欢这样的事实,即 antlr4 实现了访问者模式,并且当遇到特定类型的标签时会调用我的代码,而不必将不同的正则表达式组合在一起。
如何使用antlr4实现这样的语法?我见过的大多数例子都是针对整个文本都遵循精确规则的语言,而我只希望语法适用于任意文本中的匹配模式。
我想出了这个,我认为这是正确的:
grammar Tags;
parse
: ( tag | text )*
;
tag
: '<' fixedlist '#' ID '>'
;
fixedlist
: 'foo'
| 'bar'
| 'baz'
| 'squid';
text
: ~('<' | '>')+
;
ID
: [0-9]+
;
这是正确的吗?
一般而言,所确定的问题通常被描述为孤岛语法问题 - 在这种情况下,单个文档的各个部分由两个或多个不同的、通常相互模棱两可的规范描述。
ANTLR 4 通过使用 mode
s 直接支持孤岛语法。请注意,模式仅在拆分 lexer/parser 语法中可用。
解析器
parser grammar TagsParser ;
options {
tokenVocab = TagsLexer ;
}
parse : ( tag | text )* EOF ;
tag : LANGLE fixedlist GRIDLET ID RANGLE ;
text : . ;
fixedlist
: FOO
| BAR
| BAZ
| SQUID
;
词法分析器
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
标记以及恰好与标记模式规则匹配但不是标记的有效部分的任何文本。
我想解析纯文本评论并在其中查找某些标签。我正在寻找的标签类型如下:
<name#1234>
其中 "name" 是 [a-z] 字符串(来自固定列表),“1234”表示 [0-9]+ 数字。这些标签可以在字符串中出现零次或多次,并被任意其他文本包围。例如,以下字符串都是有效的:
"Hello <foo#56> world!"
"<bar#1>!"
"1 < 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+>
但我想使用 antlr 有几个原因:
- 我希望任何与该格式不匹配的内容都会导致解析错误,因此如果文本包含“<”或“>”但与模式不匹配,则会失败。这些字符必须转义为“<”和“>”分别如果它不是标签。
- 我将来可能会扩展它以支持其他类型的模式(例如:“{foo+666}”或“[[@1234]]”,并希望避免正则表达式语句的激增。有一个我可以扩展的单个语法文件会很棒。
- 我喜欢这样的事实,即 antlr4 实现了访问者模式,并且当遇到特定类型的标签时会调用我的代码,而不必将不同的正则表达式组合在一起。
如何使用antlr4实现这样的语法?我见过的大多数例子都是针对整个文本都遵循精确规则的语言,而我只希望语法适用于任意文本中的匹配模式。
我想出了这个,我认为这是正确的:
grammar Tags;
parse
: ( tag | text )*
;
tag
: '<' fixedlist '#' ID '>'
;
fixedlist
: 'foo'
| 'bar'
| 'baz'
| 'squid';
text
: ~('<' | '>')+
;
ID
: [0-9]+
;
这是正确的吗?
一般而言,所确定的问题通常被描述为孤岛语法问题 - 在这种情况下,单个文档的各个部分由两个或多个不同的、通常相互模棱两可的规范描述。
ANTLR 4 通过使用 mode
s 直接支持孤岛语法。请注意,模式仅在拆分 lexer/parser 语法中可用。
解析器
parser grammar TagsParser ;
options {
tokenVocab = TagsLexer ;
}
parse : ( tag | text )* EOF ;
tag : LANGLE fixedlist GRIDLET ID RANGLE ;
text : . ;
fixedlist
: FOO
| BAR
| BAZ
| SQUID
;
词法分析器
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
标记以及恰好与标记模式规则匹配但不是标记的有效部分的任何文本。