Нет, простого пути нет. Поскольку NESTED_ML_COMMENT
является правилом лексера («простым» токеном), вы не можете позволить правилу синтаксического анализатора создавать больше структуры в источнике, например /*test2/*nested*/comment*/
: правила лексера всегда будут оставаться «плоской» последовательностью символов. Конечно, есть (простые) способы переписать эту последовательность символов (например, удалить /*
и */
), но создать иерархию родительских элементов, нет.
Чтобы создать иерархию, подобную той, которая отображается на вашем изображении 2 и , вам нужно будет "продвинуть" свое правило комментария в синтаксический анализатор (поэтому сделайте его правилом синтаксического анализатора). В этом случае ваш лексер будет иметь правило 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`
}
;
EDIT
Обратите внимание, что ANTLRWorks создает свое собственное внутреннее дерево разбора, к которому у вас нет доступа. Если вы не скажете ANTLR сгенерировать правильный AST, вы просто получите плоский список токенов (хотя ANTLRWorks предполагает, что это какое-то дерево).
Вот предыдущие вопросы и ответы, которые объясняют, как создать правильный AST: Как вывести AST, построенный с использованием ANTLR?
Теперь вернемся к грамматике «комментарий», которую я разместил выше. Я переименую правило ANY
в TEXT
. На данный момент это правило соответствует только одному символу за раз. Но удобнее, чтобы оно совпадало до следующего /*
или */
. Это может быть сделано путем введения простого метода Python в класс лексера, который выполняет эту проверку. Внутри правила TEXT
мы будем использовать этот метод внутри предиката, чтобы *
сопоставлялось, если за не непосредственно следует /
, а /
сопоставляется, если оно не , за которым сразу следует *
:
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 }?=>
, в этих вопросах и ответах: Что такое «семантический предикат» в ANTLR?
Чтобы проверить все это, убедитесь, что у вас установлены правильные библиотеки времени выполнения Python (см. ANTLR Wiki ). И обязательно используйте ANTLR версии 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
РЕДАКТИРОВАТЬ II
Я также не против показать, как вы можете вызывать анализатор комментариев из вашей грамматики CSS. Вот короткая демонстрация:
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)