/*
 * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

package org.antlr.v4.semantics;

import org.antlr.v4.parse.GrammarTreeVisitor;
import org.antlr.v4.tool.ErrorManager;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.LabelElementPair;
import org.antlr.v4.tool.Rule;
import org.antlr.v4.tool.ast.ActionAST;
import org.antlr.v4.tool.ast.AltAST;
import org.antlr.v4.tool.ast.GrammarAST;
import org.antlr.v4.tool.ast.GrammarASTWithOptions;
import org.antlr.v4.tool.ast.PredAST;
import org.antlr.v4.tool.ast.RuleAST;
import org.antlr.v4.tool.ast.TerminalAST;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

Collects (create) rules, terminals, strings, actions, scopes etc... from AST side-effects: sets resolver field of asts for actions and defines predicates via definePredicateInAlt(), collects actions and stores in alts. TODO: remove side-effects!
/** Collects (create) rules, terminals, strings, actions, scopes etc... from AST * side-effects: sets resolver field of asts for actions and * defines predicates via definePredicateInAlt(), collects actions and stores * in alts. * TODO: remove side-effects! */
public class SymbolCollector extends GrammarTreeVisitor {
which grammar are we checking
/** which grammar are we checking */
public Grammar g; // stuff to collect public List<GrammarAST> rulerefs = new ArrayList<GrammarAST>(); public List<GrammarAST> qualifiedRulerefs = new ArrayList<GrammarAST>(); public List<GrammarAST> terminals = new ArrayList<GrammarAST>(); public List<GrammarAST> tokenIDRefs = new ArrayList<GrammarAST>(); public Set<String> strings = new HashSet<String>(); public List<GrammarAST> tokensDefs = new ArrayList<GrammarAST>(); public List<GrammarAST> channelDefs = new ArrayList<GrammarAST>();
Track action name node in @parser::members {...} or @members {...}
/** Track action name node in @parser::members {...} or @members {...} */
List<GrammarAST> namedActions = new ArrayList<GrammarAST>(); public ErrorManager errMgr; // context public Rule currentRule; public SymbolCollector(Grammar g) { this.g = g; this.errMgr = g.tool.errMgr; } @Override public ErrorManager getErrorManager() { return errMgr; } public void process(GrammarAST ast) { visitGrammar(ast); } @Override public void globalNamedAction(GrammarAST scope, GrammarAST ID, ActionAST action) { namedActions.add((GrammarAST)ID.getParent()); action.resolver = g; } @Override public void defineToken(GrammarAST ID) { terminals.add(ID); tokenIDRefs.add(ID); tokensDefs.add(ID); } @Override public void defineChannel(GrammarAST ID) { channelDefs.add(ID); } @Override public void discoverRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, ActionAST arg, ActionAST returns, GrammarAST thrws, GrammarAST options, ActionAST locals, List<GrammarAST> actions, GrammarAST block) { currentRule = g.getRule(ID.getText()); } @Override public void discoverLexerRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, GrammarAST block) { currentRule = g.getRule(ID.getText()); } @Override public void discoverOuterAlt(AltAST alt) { currentRule.alt[currentOuterAltNumber].ast = alt; } @Override public void actionInAlt(ActionAST action) { currentRule.defineActionInAlt(currentOuterAltNumber, action); action.resolver = currentRule.alt[currentOuterAltNumber]; } @Override public void sempredInAlt(PredAST pred) { currentRule.definePredicateInAlt(currentOuterAltNumber, pred); pred.resolver = currentRule.alt[currentOuterAltNumber]; } @Override public void ruleCatch(GrammarAST arg, ActionAST action) { GrammarAST catchme = (GrammarAST)action.getParent(); currentRule.exceptions.add(catchme); action.resolver = currentRule; } @Override public void finallyAction(ActionAST action) { currentRule.finallyAction = action; action.resolver = currentRule; } @Override public void label(GrammarAST op, GrammarAST ID, GrammarAST element) { LabelElementPair lp = new LabelElementPair(g, ID, element, op.getType()); currentRule.alt[currentOuterAltNumber].labelDefs.map(ID.getText(), lp); } @Override public void stringRef(TerminalAST ref) { terminals.add(ref); strings.add(ref.getText()); if ( currentRule!=null ) { currentRule.alt[currentOuterAltNumber].tokenRefs.map(ref.getText(), ref); } } @Override public void tokenRef(TerminalAST ref) { terminals.add(ref); tokenIDRefs.add(ref); if ( currentRule!=null ) { currentRule.alt[currentOuterAltNumber].tokenRefs.map(ref.getText(), ref); } } @Override public void ruleRef(GrammarAST ref, ActionAST arg) { // if ( inContext("DOT ...") ) qualifiedRulerefs.add((GrammarAST)ref.getParent()); rulerefs.add(ref); if ( currentRule!=null ) { currentRule.alt[currentOuterAltNumber].ruleRefs.map(ref.getText(), ref); } } @Override public void grammarOption(GrammarAST ID, GrammarAST valueAST) { setActionResolver(valueAST); } @Override public void ruleOption(GrammarAST ID, GrammarAST valueAST) { setActionResolver(valueAST); } @Override public void blockOption(GrammarAST ID, GrammarAST valueAST) { setActionResolver(valueAST); } @Override public void elementOption(GrammarASTWithOptions t, GrammarAST ID, GrammarAST valueAST) { setActionResolver(valueAST); }
In case of option id={...}, set resolve in case they use $foo
/** In case of option id={...}, set resolve in case they use $foo */
private void setActionResolver(GrammarAST valueAST) { if ( valueAST instanceof ActionAST) { ((ActionAST)valueAST).resolver = currentRule.alt[currentOuterAltNumber]; } } }