docs: def from_fsm(f):
  	'''
  		Turn the supplied finite state machine into a `lego` object. This is
  		accomplished using the Brzozowski algebraic method.
  	'''
  	# Make sure the supplied alphabet is kosher. It must contain only single-
  	# character strings or `fsm.anything_else`.
  	for symbol in f.alphabet:
  		if symbol == fsm.anything_else:
  			continue
  		if isinstance(symbol, str) and len(symbol) == 1:
  			continue
  		raise Exception("Symbol " + repr(symbol) + " cannot be used in a regular expression")
  
  	# We need a new state not already used
  	outside = object()
  
  	# The set of strings that would be accepted by this FSM if you started
  	# at state i is represented by the regex R_i.
  	# If state i has a sole transition "a" to state j, then we know R_i = a R_j.
  	# If state i is final, then the empty string is also accepted by this regex.
  	# And so on...
  
  	# From this we can build a set of simultaneous equations in len(f.states)
  	# variables. This system is easily solved for all variables, but we only
  	# need one: R_a, where a is the starting state.
  
  	# The first thing we need to do is organise the states into order of depth,
  	# so that when we perform our back-substitutions, we can start with the
  	# last (deepest) state and therefore finish with R_a.
  	states = [f.initial]
  	i = 0
  	while i < len(states):
  		current = states[i]
  		if current in f.map:
  			for symbol in sorted(f.map[current], key=fsm.key):
  				next = f.map[current][symbol]
  				if next not in states:
  					states.append(next)
  		i += 1
  
  	# Our system of equations is represented like so:
  	brz = {}
  	for a in f.states:
  		brz[a] = {}
  		for b in f.states | {outside}:
  			brz[a][b] = nothing
  
  	# Populate it with some initial data.
  	for a in f.map:
  		for symbol in f.map[a]:
  			b = f.map[a][symbol]
  			if symbol == fsm.anything_else:
  				brz[a][b] |= ~charclass(f.alphabet - {fsm.anything_else})
  			else:
  				brz[a][b] |= charclass({symbol})
  		if a in f.finals:
  			brz[a][outside] |= emptystring
  
  	# Now perform our back-substitution
  	for i in reversed(range(len(states))):
  		a = states[i]
  
  		# Before the equation for R_a can be substituted into the other
  		# equations, we need to resolve the self-transition (if any).
  		# e.g.    R_a = 0 R_a |   1 R_b |   2 R_c
  		# becomes R_a =         0*1 R_b | 0*2 R_c
  		loop = brz[a][a] * star # i.e. "0*"
  		del brz[a][a]
  
  		for right in brz[a]:
  			brz[a][right] = loop + brz[a][right]
  
  		# Note: even if we're down to our final equation, the above step still
  		# needs to be performed before anything is returned.
  
  		# Now we can substitute this equation into all of the previous ones.
  		for j in range(i):
  			b = states[j]
  
  			# e.g. substituting R_a =  0*1 R_b |      0*2 R_c
  			# into              R_b =    3 R_a |        4 R_c | 5 R_d
  			# yields            R_b = 30*1 R_b | (30*2|4) R_c | 5 R_d
  			univ = brz[b][a] # i.e. "3"
  			del brz[b][a]
  
  			for right in brz[a]:
  				brz[b][right] |= univ + brz[a][right]
  
  	return brz[f.initial][outside].reduce()