我正在编写一种语言,我将以这种方式合并XML文字:
X ?= <element attr='blah'>text<another attr='blah /> more text</element>;
我正在与Terrence Parr提供的XML grammars合作。我的问题出现了,因为PCDATA的定义类似于(~'<')+
,其中抓取我的模块中除XML字面之外的所有其他源。
我想做的是在TEXT令牌上设置一个警卫,以便它只在我们期待XML时激活。
PCDATA : {isInXmlFragment}?=> (~'<')+;
问题是该范围变量需要从解析器设置,但在词法分析器中使用。我意识到我可以通过静态变量在一些常见的类中安排一些东西,但如果我想并行解析多个模块,那将会很棘手。 Parr的解决方案有这样的保护,但它只能在词法分析器的上下文中工作,并且只能在没有其他类型内容的纯XML文件中工作。
因为我将XML嵌入到源代码文件中,所以还有其他要扫描的文本超出了词法分析器XML部分的范围,但XML PCDATA规则部分匹配所有源代码,因为它太笼统了。
是否有一些安全的内置方式允许解析器和词法分析器之间的这种通信?类似于范围和从解析器中引用词法分析器动态范围的方法?
答案 0 :(得分:2)
...允许解析器和词法分析器之间进行此类通信的方式?
不,不是没有创建自定义词法分析器。使用ANTLR中的默认词法分析器/解析器,它们之间存在严格的分离:词法分析器独立于解析器运行。
但是,据我所知,你不需要这个。您可以递归调用词法分析器规则。因此,每当您偶然发现标记的开头<element
时,您只需向前看并匹配/>
或>
。如果匹配的是>
,请尝试匹配<
以外的字符,或者递归调用整个词法分析器规则。最后,当然应该有一个</element>
。
快速演示(没有保持语法简单的属性):
grammar Test;
parse
: (t=. {System.out.printf("type=\%-15s text='\%s'\n", tokenNames[$t.type], $t.text);})* EOF
;
XML
: '<' Identifier ( '/>'
| '>' (~'<' | XML)* '</' Identifier '>'
)
;
Identifier
: ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
;
QAssign
: '?='
;
SCol
: ';'
;
Spaces
: (' ' | '\t' | '\r' | '\n')+ {skip();}
;
如果您现在解析输入:
X ?= <element>text<another/>more<i><b>text</b></i></element>;
您将看到以下内容正在打印到控制台:
type=Identifier text='X'
type=QAssign text='?='
type=XML text='<element>text<another/>more<i><b>text</b></i></element>'
type=SCol text=';'
如您所见,XML块现在被视为单个令牌。如果你想在它们之间使用PCDATA令牌打开和关闭标记令牌,你需要计算“开始标记”的数量,当这个数字大于0时,匹配你的PCDATA令牌,如下所示:{{1} }。使用这种方法,也不需要词法分析器和解析器之间的通信:将在词法分析器中定义跟踪开始标记的计数器。
演示:
~'<'+
现在解析输入如:
grammar Test;
@lexer::members {
private int openTags = 0;
}
parse
: any* EOF
;
any
: Identifier
| QAssign
| SCol
| xml
;
xml
: OTag (PCData | xml)* CTag
| Tag
;
PCData
: {openTags > 0}?=> ~'<'+
;
OTag
: '<' Identifier ('>' {openTags++;} | '/>' {$type=Tag;})
;
CTag
: '</' Identifier '>' {openTags--;}
;
Identifier
: ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
;
QAssign
: '?='
;
SCol
: ';'
;
Spaces
: (' ' | '\t' | '\r' | '\n')+ {skip();}
;
fragment Tag : ;
将导致以下解析: