public List<GrammarRootAST> sortGrammarByTokenVocab(List<String> fileNames) {
//		System.out.println(fileNames);
		Graph<String> g = new Graph<String>();
		List<GrammarRootAST> roots = new ArrayList<GrammarRootAST>();
		for (String fileName : fileNames) {
			GrammarAST t = parseGrammar(fileName);
			if ( t==null || t instanceof GrammarASTErrorNode) continue; // came back as error node
			if ( ((GrammarRootAST)t).hasErrors ) continue;
			GrammarRootAST root = (GrammarRootAST)t;
			roots.add(root);
			root.fileName = fileName;
			String grammarName = root.getChild(0).getText();

			GrammarAST tokenVocabNode = findOptionValueAST(root, "tokenVocab");
			// Make grammars depend on any tokenVocab options
			if ( tokenVocabNode!=null ) {
				String vocabName = tokenVocabNode.getText();
				// Strip quote characters if any
				int len = vocabName.length();
				int firstChar = vocabName.charAt(0);
				int lastChar = vocabName.charAt(len - 1);
				if (len >= 2 && firstChar == '\'' && lastChar == '\'') {
					vocabName = vocabName.substring(1, len-1);
				}
				// If the name contains a path delimited by forward slashes,
				// use only the part after the last slash as the name
				int lastSlash = vocabName.lastIndexOf('/');
				if (lastSlash >= 0) {
					vocabName = vocabName.substring(lastSlash + 1);
				}
				g.addEdge(grammarName, vocabName);
			}
			// add cycle to graph so we always process a grammar if no error
			// even if no dependency
			g.addEdge(grammarName, grammarName);
		}

		List<String> sortedGrammarNames = g.sort();
//		System.out.println("sortedGrammarNames="+sortedGrammarNames);

		List<GrammarRootAST> sortedRoots = new ArrayList<GrammarRootAST>();
		for (String grammarName : sortedGrammarNames) {
			for (GrammarRootAST root : roots) {
				if ( root.getGrammarName().equals(grammarName) ) {
					sortedRoots.add(root);
					break;
				}
			}
		}

		return sortedRoots;
	}
Important enough to avoid multiple definitions that we do very early,
right after AST construction. Also check for undefined rules in
parser/lexer to avoid exceptions later. Return true if we find multiple
definitions of the same rule or a reference to an undefined rule or
parser rule ref in lexer rule.
tool/src/org/antlr/v4/Tool.java