В первом случае
void NonTerminal #Nonterminal: { Token t;}
{
t = <MULTIPLY>
OtherNonTerminal() {jjtThis.value = t.image;}
#Multiply
}
узел Multiply
будет иметь в качестве дочерних элементов все узлы, помещенные в стек во время его области видимости, за исключением тех, которые вытолкнуты до конца области. В этом случае это означает, что все узлы нажаты и не вытолкнуты во время синтаксического анализа OtherNonTerminal
.
Во втором примере
void NonTerminal #void : { Token t;}
{
t = <MULTIPLY>
OtherNonTerminal() {jjtThis.value = t.image;}
#Multiply(2)
}
узел Multiply
получит два верхних узла из стека в качестве своих дочерних элементов.
Так что, вероятно, есть разница.
Другое отличие состоит в том, что во втором примере не указан узел, связанный с Nonterminal
.
В первом случае это дерево будет сдвинуто
Nonterminal
|
Multiply
|
All nodes pushed (but not popped) during the parsing of OtherNonterminal
Во втором случае синтаксический анализ OtherNonterminal
сделает свое дело (выталкивание и подталкивание узлов), затем два узла будут вытолкнуты, и это дерево будет протолкнуто
Multiply
| |
A child Another child
По второму вопросу. Разница между
void NonTerminal() #void : { Token t; }
{
t = <MULTIPLY>
OtherNonTerminal()
{ jjtThis.value = t.image; }
#Mult(2)
|
t = <DIVIDE>
OtherNonTerminal()
{ jjtThis.value = t.image; }
#Div(2)
|
{}
}
и
void NonTerminal() #Nonterminal(2) : {
Token t; }
{
( t = <MULTIPLY> OtherNonTerminal()
| t = <DIVIDE> OtherNonTerminal()
| {}
)
{jjtThis.value = t.image;}
}
заключается в том, что первый не строит узел при совпадении пустой последовательности.
Рассмотрим второй способ в случае, когда следующий токен является чем-то отличным от *
или /
. Вы получите
Nonterminal
/ \
Some node Some other node
don't want you don't want
Я на самом деле удивлен, что второй даже проходит мимо компилятора Java, поскольку ссылка на t
является потенциально неинициализированной переменной.