public ArrayList<TokenBin> buildTokenMap(ClangTokenGroup leftTokenGp,
			ClangTokenGroup rightTokenGp) {

		HashMap<Pair<DataVertex, DataVertex>, TokenBin> lvertToBin = new HashMap<>();
		HashMap<Pair<DataVertex, DataVertex>, TokenBin> rvertToBin = new HashMap<>();

		for (int side = 0; side < 2; side++) {
			// side == 0: set up the left side
			// side == 1: set up the right side
			ClangTokenGroup tokGp = (side == 0 ? leftTokenGp : rightTokenGp);
			DataGraph graph = (side == 0 ? graphLeft : graphRight);
			HashMap<Pair<DataVertex, DataVertex>, TokenBin> vertToBin =
				(side == 0 ? lvertToBin : rvertToBin);

			ArrayList<ClangNode> nodes = new ArrayList<>();
			tokGp.flatten(nodes);
			for (ClangNode node : nodes) {
				if (node instanceof ClangToken tok) {
					if (filterToken(tok)) {
						continue;
					}
					VarnodeAST vn = (VarnodeAST) DecompilerUtils.getVarnodeRef(tok);
					PcodeOpAST op = (PcodeOpAST) tok.getPcodeOp();

					DataVertex opNode = graph.opToVert.get(op);
					DataVertex vnNode = graph.vnToVert.get(vn);
					Pair<DataVertex, DataVertex> nodePair = new Pair<>(opNode, vnNode);

					if (!vertToBin.containsKey(nodePair)) {
						vertToBin.put(nodePair, new TokenBin(graph.getHighFunction()));
					}
					vertToBin.get(nodePair).add(tok);
				}
			}
		}

		// Match a TokenBin if its underlying DataVertex is paired
		ArrayList<TokenBin> highBins = new ArrayList<>();
		for (Pair<DataVertex, DataVertex> lNodePair : lvertToBin.keySet()) {
			TokenBin lbin = lvertToBin.get(lNodePair);
			DataVertex lkey = lNodePair.first;
			DataVertex lval = lNodePair.second;
			DataVertex rkey = null;
			DataVertex rval = null;
			if (lkey != null && lkey.paired) {
				rkey = pinMap.get(lkey);
			}
			if (lval != null && lval.paired) {
				rval = pinMap.get(lval);
			}

			if (((lkey == null) != (rkey == null)) || ((lval == null) != (rval == null))) {
				continue;
			}
			if ((rkey == null && rval == null) || (lkey == null && lval == null)) {
				continue;
			}

			Pair<DataVertex, DataVertex> rNodePair = new Pair<>(rkey, rval);
			if (rvertToBin.containsKey(rNodePair)) {
				TokenBin rbin = rvertToBin.get(rNodePair);
				lbin.sidekick = rbin;
				rbin.sidekick = lbin;
			}
		}

		// Put everything into the final list
		lvertToBin.remove(new Pair<DataVertex, DataVertex>(null, null));
		rvertToBin.remove(new Pair<DataVertex, DataVertex>(null, null));
		highBins.addAll(lvertToBin.values());
		highBins.addAll(rvertToBin.values());

		return highBins;
	}
Build a TokenBin for every DataVertex on both sides. Pair a TokenBin with its match,
if its underlying DataVertex is paired.
@param leftTokenGp are the tokens for LEFT side
@param rightTokenGp are the tokens for RIGHT side
@return a single list of LEFT side TokenBins then RIGHT side TokenBins
Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/graphanalysis/Pinning.java