Насколько мне известно, синтаксис правил перезаписи не позволяет напрямую присваивать значения, как предполагает ваш предварительный фрагмент.Отчасти это связано с тем, что синтаксический анализатор не знал, к какой части дерева / узла следует добавить значения.
Однако одна из интересных особенностей созданных ANTLR AST - эточто синтаксический анализатор не делает никаких предположений о типе узлов.Нужно просто реализовать TreeAdapator, который служит фабрикой для новых узлов и навигатором древовидной структуры.Поэтому можно добавить любую информацию, которая может потребоваться в узлах, как описано ниже.
ANTLR обеспечивает реализацию узла дерева по умолчанию, CommonTree , и в большинстве случаев (как в текущей ситуации).) нам просто нужно
- подкласс CommonTree, добавив в него некоторые пользовательские поля
- подкласс CommonTreeAdaptor, чтобы переопределить его метод create (), то есть способ, которым он создает новые узлы.
но можно также создать новый тип узла altogher для какой-то нечетной структуры графа или еще чего-нибудь.Для рассматриваемого случая должно быть достаточно следующего (адаптировать для конкретного целевого языка, если это не Java)
import org.antlr.runtime.tree.*;
import org.antlr.runtime.Token;
public class NodeWithScope extends CommonTree {
/* Just declare the extra fields for the node */
public ArrayList symbols;
public string name;
public object whatever_else;
public NodeWithScope (Token t) {
super(t);
}
}
/* TreeAdaptor: we just need to override create method */
class NodeWithScopeAdaptor extends CommonTreeAdaptor {
public Object create(Token standardPayload) {
return new NodeWithScope(standardPayload);
}
}
Затем необходимо немного изменить способ запуска процесса синтаксического анализа, чтобыANTLR (или, точнее, созданный ANTLR синтаксический анализатор) знает, что нужно использовать NodeWithScopeAdaptor вместо CommnTree.
(шаг 4.1 ниже, остальное, если скорее стандартная тестовая установка ANTLR)
// ***** Typical ANTLR pipe rig *****
// ** 1. input stream
ANTLRInputStream input = new ANTLRInputStream(my_input_file);
// ** 2, Lexer
MyGrammarLexer lexer = new MyGrammarLexer(input);
// ** 3. token stream produced by lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
// ** 4. Parser
MyGrammarParser parser = new MyGrammarParser(tokens);
// 4.1 !!! Specify the TreeAdapter
NodeWithScopeAdaptor adaptor = new NodeWithScopeAdaptor();
parser.setTreeAdaptor(adaptor); // use my adaptor
// ** 5. Start process by invoking the root rule
r = parser.MyTopRule();
// ** 6. AST tree
NodeWithScope t = (NodeWithScope)r.getTree();
// ** 7. etc. parse the tree or do whatever is needed on it.
Наконец ваша грамматика будет иметьбыть приспособленным к чему-то похожему на то, что следует
(обратите внимание, что узел [для текущего правила] доступен только в разделе @after. Однако он может ссылаться на любой атрибут токена и другую контекстную переменную на уровне грамматики, используяобычная запись $ rule.atrribute)
composite_instruction
scope JScope;
@init {
$JScope::symbols = new ArrayList();
$JScope::name = "level "+ $JScope.size();
}
@after {
($composite_instruction.tree).symbols = $JScope::symbols;
($composite_instruction.tree).name = $JScope::name;
($composite_instruction.tree).whatever_else
= new myFancyObject($x.Text, $y.line, whatever, blah);
}
: '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction*)
;