<Purpose>
    Command parser for user variables.  Takes the raw userinput and replaces
    each user variable with its set value.
  <Arguments>
    userinput: A raw user string
  <Side Effects>
    Each user variable will be replaced by the value that it was previously
    set to.
  <Exceptions>
    UserError: User typed an unrecognized or invalid variable name
  <Returns>
    The preprocessed string

def preprocess_user_variables(userinput):
  """
  <Purpose>
    Command parser for user variables.  Takes the raw userinput and replaces
    each user variable with its set value.
  <Arguments>
    userinput: A raw user string
  <Side Effects>
    Each user variable will be replaced by the value that it was previously
    set to.
  <Exceptions>
    UserError: User typed an unrecognized or invalid variable name
  <Returns>
    The preprocessed string
  """
  retstr = ""
  while '$' in userinput:
    text_before_variable , variable_delimiter, userinput = userinput.partition('$')
    retstr += text_before_variable

    # Treat $$ as an escape for a single $.
    # Also escape if there is nothing left
    if not userinput or userinput.startswith('$'):
      retstr += '$'
      userinput = userinput[1:]
      continue

    # Look for the next space, or the next $.  The closest one of these will be
    # used as the delimiter.  Then update the remaining user input.
    space_variable_length = userinput.find(' ')
    dollarsign_variable_length = userinput.find('$')

    # If the length is -1, then the delimiter was not found.
    # We use the length of the entire string to represent this.
    # If there was one delimiter found, then that delimiter's value
    # will always be less than the string's length.
    # If it is a tie, then it simply means that the entire string
    # is the variable name.
    if space_variable_length == -1:
      space_variable_length = len(userinput)
    if dollarsign_variable_length == -1:
      dollarsign_variable_length = len(userinput)

    variable_length = min(space_variable_length, dollarsign_variable_length)
    variable_name = userinput[:variable_length]
    userinput = userinput[variable_length + 1:]  # Skip the actual delimiter

    # Perform the replacement!
    # User may type in a variable that has not yet been defined
    try:
      retstr += uservariables[variable_name]
    except KeyError:
      raise seash_exceptions.UserError("Variable does not exist: "+variable_name)

    # The user expects a space before the string right after the variable.
    # e.g. 'loadkeys $myname as awesome' should turn into
    #      'loadkeys theusername as awesome'
    if space_variable_length < dollarsign_variable_length:
      retstr += ' '

  # Now add the remaining text after the last variable
  else:
    retstr += userinput

  return retstr