public static TraceSchedule parse(String spec, Source source, TimeRadix radix) {
		String[] parts = spec.split(":", 2);
		if (parts.length > 2) {
			throw new AssertionError();
		}
		final long snap;
		final Sequence ticks;
		final Sequence pTicks;
		try {
			snap = radix.decode(parts[0]);
		}
		catch (NumberFormatException e) {
			throw new IllegalArgumentException(PARSE_ERR_MSG, e);
		}
		if (parts.length > 1) {
			String[] subs = parts[1].split("\\.");
			try {
				ticks = Sequence.parse(subs[0], radix);
			}
			catch (IllegalArgumentException e) {
				throw new IllegalArgumentException(PARSE_ERR_MSG, e);
			}
			if (subs.length == 1) {
				pTicks = new Sequence();
			}
			else if (subs.length == 2) {
				try {
					pTicks = Sequence.parse(subs[1], radix);
				}
				catch (IllegalArgumentException e) {
					throw new IllegalArgumentException(PARSE_ERR_MSG, e);
				}
			}
			else {
				throw new IllegalArgumentException(PARSE_ERR_MSG);
			}
		}
		else {
			ticks = new Sequence();
			pTicks = new Sequence();
		}
		return new TraceSchedule(snap, ticks, pTicks, source);
	}
Parse schedule in the form "{@code snap[:steps[.pSteps]]}"

<p>
A schedule consists of a snap, a optional {@link Sequence} of thread instruction-level steps,
and optional p-code-level steps (pSteps). The form of {@code steps} and {@code pSteps} is
specified by {@link Sequence#parse(String, TimeRadix)}. Each sequence consists of stepping
selected threads forward, and/or patching machine state.

@param spec the string specification
@param source the presumed source of the schedule
@param radix the radix
@return the parsed schedule
Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TraceSchedule.java