首先,抱歉我的英语,我还在学习。
我为我的框架编写Python模块,它解析CSS文件。我尝试使用正则表达式,ply(python lexer和parser),但我发现自己在ANTLR中。
首先尝试,我需要解析CSS文件中的注释。 这是我要解析的CSS字符串:
/*test*/
/*
test1
/*
/*test2/*nested*/comment/*
我知道CSS不允许嵌套注释,但我需要在我的框架中使用它。我写了简单的ANTLR语法:
grammar CSS;
options {
language = Python;
}
styleSheet
: comments EOF ;
comments
: NESTED_ML_COMMENT*
;
NESTED_ML_COMMENT
: '/*'
(options {greedy=false;} : (NESTED_ML_COMMENT | . ) )*
'*/'
;
LINEBREAK
: ('\n\r' | '\n')+{$channel=HIDDEN; };
我得到的结果是:
我的期望(油漆工作:D):
请注意,我不希望/ *和* /结果。
在纯ANTLR中有没有办法做到这一点?我在ANTLR中使用python没有问题,但是如果有任何方法没有python我会感激不尽。
答案 0 :(得分:2)
不,没有简单的方法。由于NESTED_ML_COMMENT
是词法分析器规则(“简单”标记),因此您不能让解析器规则在源代码中创建任何更多结构,如/*test2/*nested*/comment*/
:词法分析器规则将始终保持“平坦”字符序列。当然,有(简单)方法来重写这个字符序列(即删除/*
和*/
),但创建父兄弟层次结构,没有。
为了创建您在2 nd 图像中显示的层次结构,您必须将评论规则“提升”到解析器(因此将其作为解析器规则)。在这种情况下,您的词法分析器会有COMMENT_START : '/*';
和COMMENT_END : '*/';
规则。但这会打开一堆蠕虫:在你的词法分析器中,你现在还需要考虑/*
和*/
之间的所有字符。
你可以创建另一个解析(嵌套)注释并在CSS
语法中使用它的解析器。在你的CSS
语法中,你只需保持它原样,你的第二个解析器是一个专门的注释解析器,它从注释标记创建一个层次结构。
快速演示。语法:
grammar T;
parse
: comment EOF
;
comment
: COMMENT_START (ANY | comment)* COMMENT_END
;
COMMENT_START : '/*';
COMMENT_END : '*/';
ANY : . ;
会将源/*test2/*nested*/comment*/
解析为以下解析树:
您可以重写,以便删除/*
和*/
。
在CSS
语法中,您可以:
comment
: NESTED_ML_COMMENT
{
text = $NESTED_ML_COMMENT.text
# invoke the TParser (my demo grammar) on `text`
}
;
请注意,ANTLRWorks会创建您自己的内部分析树,您无权访问该分析树。如果你没有告诉ANTLR生成一个合适的AST,你最终会得到一个平坦的标记列表(即使ANTLRWorks建议它是某种树)。
这是以前的Q& A,解释了如何创建正确的AST:How to output the AST built using ANTLR?
现在让我们回到上面发布的“评论”语法。我将ANY
规则重命名为TEXT
。目前,此规则一次只匹配一个字符。但是让它一直匹配到下一个/*
或*/
会更方便。这可以通过在lexer类中引入一个执行此检查的普通Python方法来完成。在TEXT
规则中,我们会在谓词中使用该方法,以便{em} 后直接跟*
和{/
匹配/
{1}}如果不后直接跟*
匹配,则会匹配:
grammar Comment;
options {
output=AST;
language=Python;
}
tokens {
COMMENT;
}
@lexer::members {
def not_part_of_comment(self):
current = self.input.LA(1)
next = self.input.LA(2)
if current == ord('*'): return next != ord('/')
if current == ord('/'): return next != ord('*')
return True
}
parse
: comment EOF -> comment
;
comment
: COMMENT_START atom* COMMENT_END -> ^(COMMENT atom*)
;
atom
: TEXT
| comment
;
COMMENT_START : '/*';
COMMENT_END : '*/';
TEXT : ({self.not_part_of_comment()}?=> . )+ ;
在此问答语中了解有关谓词语法{ boolean_expression }?=>
的更多信息:What is a 'semantic predicate' in ANTLR?
要测试这一切,请确保安装了正确的Python运行时库(请参阅ANTLR Wiki)。并确保在此运行时使用ANTLR version 3.1.3。
像这样生成词法分析器和解析器:
java -cp antlr-3.1.3.jar org.antlr.Tool Comment.g
使用以下Python脚本测试词法分析器和解析器:
#!/usr/bin/env python
import antlr3
from antlr3 import *
from antlr3.tree import *
from CommentLexer import *
from CommentParser import *
# http://www.antlr.org/wiki/display/ANTLR3/Python+runtime
# http://www.antlr.org/download/antlr-3.1.3.jar
def print_level_order(tree, indent):
print '{0}{1}'.format(' '*indent, tree.text)
for child in tree.getChildren():
print_level_order(child, indent+1)
input = '/*aaa1/*bbb/*ccc*/*/aaa2*/'
char_stream = antlr3.ANTLRStringStream(input)
lexer = CommentLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = CommentParser(tokens)
tree = parser.parse().tree
print_level_order(tree, 0)
如您所见,从源"/*aaa1/*bbb/*ccc*/*/aaa2*/"
开始创建以下AST:
COMMENT
aaa1
COMMENT
bbb
COMMENT
ccc
aaa2
我还要介绍如何从CSS语法中调用Comment解析器。这是一个快速演示:
grammar CSS;
options {
output=AST;
language=Python;
}
tokens {
CSS_FILE;
RULE;
BLOCK;
DECLARATION;
}
@parser::header {
import antlr3
from antlr3 import *
from antlr3.tree import *
from CommentLexer import *
from CommentParser import *
}
@parser::members {
def parse_comment(self, text):
lexer = CommentLexer(antlr3.ANTLRStringStream(text))
parser = CommentParser(antlr3.CommonTokenStream(lexer))
return parser.parse().tree
}
parse
: atom+ EOF -> ^(CSS_FILE atom+)
;
atom
: rule
| Comment -> {self.parse_comment($Comment.text)}
;
rule
: Identifier declarationBlock -> ^(RULE Identifier declarationBlock)
;
declarationBlock
: '{' declaration+ '}' -> ^(BLOCK declaration+)
;
declaration
: a=Identifier ':' b=Identifier ';' -> ^(DECLARATION $a $b)
;
Identifier
: ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' | '0'..'9')*
;
Comment
: '/*' (options {greedy=false;} : Comment | . )* '*/'
;
Space
: (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
;
如果您解析来源:
h1 { a: b; c: d;}
/*aaa1/*bbb/*ccc*/*/aaa2*/
p {x : y;}
使用CSSParser,您将获得以下树:
CSS_FILE
RULE
h1
BLOCK
DECLARATION
a
b
DECLARATION
c
d
COMMENT
aaa1
COMMENT
bbb
COMMENT
ccc
aaa2
RULE
p
BLOCK
DECLARATION
x
y
通过运行此测试脚本可以看到:
#!/usr/bin/env python
import antlr3
from antlr3 import *
from antlr3.tree import *
from CSSLexer import *
from CSSParser import *
def print_level_order(tree, indent):
print '{0}{1}'.format(' '*indent, tree.text)
for child in tree.getChildren():
print_level_order(child, indent+1)
input = 'h1 { a: b; c: d;}\n\n/*aaa1/*bbb/*ccc*/*/aaa2*/\n\np {x : y;}'
char_stream = antlr3.ANTLRStringStream(input)
lexer = CSSLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = CSSParser(tokens)
tree = parser.parse().tree
print_level_order(tree, 0)
答案 1 :(得分:0)
您应该使用!
和^
AST提示。要使/*
不出现在您的AST中,请将!
放在其后面。要控制哪些元素成为AST子树的根,请附加^
。它可能看起来像这样:
NESTED_ML_COMMENT
: COMMENT_START!
(options {greedy=false;} : (NESTED_ML_COMMENT^ | . ) )*
COMMENT_END!
;
这里有一个关于这些运算符的问题,现在你知道存在了,我希望它会有用: What does ^ and ! stand for in ANTLR grammar