protected void doAction(Action a, AssemblyParseToken tok, Set<AssemblyParseMachine> results,
			Deque<AssemblyParseMachine> visited) {
		try (DbgCtx dc = DBG.start("Action: " + a)) {
			if (a instanceof ShiftAction) {
				AssemblyParseMachine m = copy();
				m.stack.push(((ShiftAction) a).newStateNum);
				m.treeStack.push(tok);
				m.lastTok = tok;
				m.pos += tok.getString().length();
				m.exhaust(results, visited);
			}
			else if (a instanceof ReduceAction) {
				AssemblyProduction prod = ((ReduceAction) a).prod;
				AssemblyParseBranch branch = new AssemblyParseBranch(parser.grammar, prod);
				AssemblyParseMachine m = copy();
				m.output.add(prod.getIndex());
				DBG.println("Prod: " + prod);
				for (@SuppressWarnings("unused")
				AssemblySymbol sym : prod.getRHS()) {
					m.stack.pop();
					branch.addChild(m.treeStack.pop());
				}
				for (Action aa : m.parser.actions.get(m.stack.peek(), prod.getLHS())) {
					GotoAction ga = (GotoAction) aa;
					DBG.println("Goto: " + ga);
					AssemblyParseMachine n = m.copy();
					n.stack.push(ga.newStateNum);
					n.treeStack.push(branch);
					n.exhaust(results, visited);
				}
			}
			else if (a instanceof AcceptAction) {
				AssemblyParseMachine m = copy();
				m.accepted = true;
				results.add(m);
			}
		}
	}
Perform a given action and continue parsing, exhausting all results after the action

<p>
The visited list prevents infinite loops or stack overflows resulting from consuming epsilon
and going to the same state. Such loops may involve many states.

@param a the action
@param tok the token given by the terminal (column) of the entry containing this action
@param results a place to store all the parsing results (each must be accept or error state)
@param visited a collection of machine states already visited
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/parse/AssemblyParseMachine.java