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

时间:2016-08-30 23:13:26

标签: c# regex parsing antlr4 text-parsing

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

<name#1234>

其中&#34;姓名&#34;是[a-z]字符串(来自固定列表)和&#34; 1234&#34;代表[0-9] +数字。这些标记可以在字符串中出现零次或多次,并被任意其他文本包围。例如,以下字符串都是有效的:

"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>"

最后一个是无效的,因为&#34; notinfixedlist&#34;不是受支持的命名标识符。

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

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

或直接指定固定列表:

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

但是我想使用antlr有几个原因:

  • 我想要任何与该格式不匹配的结果导致解析错误,因此如果文本包含&#34;&lt;&#34;或&#34;&gt;&#34;但是与模式不匹配,它失败了。这些字符必须转义为&#34;&amp; lt;&#34;和&#34;&amp; gt;&#34;分别如果它不是标签。
  • 我可能会在将来扩展它以支持其他类型的模式(例如:&#34; {foo + 666}&#34;或&#34; [[@ 1234]]&#34;并希望为了避免爆发正则表达式语句。拥有一个我可以扩展的语法文件会很棒。
  • 我喜欢这样的事实:antlr4实现了访问者模式,当遇到特定类型的标记时我的代码被调用,而不是必须破解不同的正则表达式。

如何使用antlr4实现这样的语法?我见过的大多数例子都是针对整个文本的完全规则的语言,而我只希望语法适用于任意文本中的匹配模式。

我想出了这个,我认为是正确的:

grammar Tags;

parse 
    : ( tag | text )*
    ;

tag 
    : '<' fixedlist '#' ID '>'
    ;

fixedlist 
    : 'foo' 
    | 'bar' 
    | 'baz' 
    | 'squid';

text 
    : ~('<' | '>')+
    ;

ID
    : [0-9]+
    ;

这是对的吗?

1 个答案:

答案 0 :(得分:2)

一般而言,所识别的问题通常被描述为岛屿语法问题 - 其中单数文档的部分由两个或更多不同的,通常是相互模糊的规范描述。

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

解析器

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令牌以及恰好与标记模式规则匹配但不是标记的有效部分的任何文本。