text: from __future__ import absolute_import, division, print_function
  from libtbx import slots_getstate_setstate
  
  def escape_python_str(quote_char, string):
    return string.replace("\\", "\\\\").replace(quote_char, "\\"+quote_char)
  
  def quote_python_str(quote_token, string):
    return quote_token \
         + escape_python_str(quote_char=quote_token[0], string=string) \
         + quote_token
  
  class character_iterator(slots_getstate_setstate):
  
    __slots__ = [
      "input_string",
      "i_char",
      "line_number",
      "backup_i_char",
      "backup_line_number"]
  
    def __init__(O, input_string):
      O.input_string = input_string
      O.i_char = 0
      O.line_number = 1
      O.backup_i_char = None
      O.backup_line_number = None
  
    def mark_for_backup(O):
      O.backup_i_char = O.i_char
      O.backup_line_number = O.line_number
  
    def restore_backup(O):
      O.i_char = O.backup_i_char
      O.line_number = O.backup_line_number
  
    def look_ahead(O, n):
      end = O.i_char + n
      if (end > len(O.input_string)): return None
      return O.input_string[O.i_char:end]
  
    def look_ahead_1(O):
      if (O.i_char == len(O.input_string)): return None
      return O.input_string[O.i_char]
  
    def skip_ahead_1(O):
      if (O.input_string[O.i_char] == "\n"): O.line_number += 1
      O.i_char += 1
  
    def __next__(O):
      result = O.look_ahead_1()
      if (result is not None):
        O.i_char += 1
        if (result == "\n"): O.line_number += 1
      return result
  
    def scan_for_start(O, intro, followups):
      while True:
        if (O.i_char == len(O.input_string)): return 0
        if (O.input_string[O.i_char] != "\n"):
          O.i_char += 1
        else:
          while True:
            O.line_number += 1
            O.i_char += 1
            if (O.i_char == len(O.input_string)): return 0
            if (O.input_string[O.i_char] != "\n"): break
          if (O.input_string.find(
                intro, O.i_char, O.i_char+len(intro)) != O.i_char):
            O.i_char += 1
          else:
            O.i_char += len(intro)
            while True:
              if (O.i_char == len(O.input_string)): return 0
              if (O.input_string[O.i_char].isspace()): break
              O.i_char += 1
            while True:
              if (O.i_char == len(O.input_string)): return 0
              if (not O.input_string[O.i_char].isspace()): break
              O.i_char += 1
            for i_followup,followup in enumerate(followups):
              if (O.input_string.find(
                    followup,
                    O.i_char,
                    O.i_char+len(followup)) == O.i_char):
                O.i_char += len(followup)
                while True:
                  if (O.i_char == len(O.input_string)): return i_followup
                  c = O.input_string[O.i_char]
                  O.i_char += 1
                  if (c == "\n"):
                    O.line_number += 1
                    return i_followup
                  if (not c.isspace()): break
                break
      return 0
  
  def where(source_info, line_number):
    result = []
    if (source_info is not None):
      result.append(source_info)
      if (line_number is not None):
        result.append("line %d" % line_number)
    elif (line_number is not None):
      result.append("input line %d" % line_number)
    if (len(result) == 0): return None
    return ", ".join(result)
  
  def where_str(source_info, line_number):
    s = where(source_info, line_number)
    if (s is None): return ""
    return " (%s)" % s
  
  class word(slots_getstate_setstate):
  
    __slots__ = ["value", "quote_token", "line_number", "source_info"]
  
    def __init__(O,
          value,
          quote_token=None,
          line_number=None,
          source_info=None):
      O.value = value
      O.quote_token = quote_token
      O.line_number = line_number
      O.source_info = source_info
  
    def __str__(O):
      if (O.quote_token is None):
        return O.value
      return quote_python_str(quote_token=O.quote_token, string=O.value)
  
    def where(O):
      return where(O.source_info, O.line_number)
  
    def where_str(O):
      return where_str(O.source_info, O.line_number)
  
    def raise_syntax_error(O, message):
      raise RuntimeError(
        'Syntax error: %s"%s"%s' % (message, O.value, O.where_str()))
  
    def assert_expected(O, value):
      if (O.value != value):
        O.raise_syntax_error('expected "%s", found ' % value)
  
    def raise_unquoted_word_expected(O):
      raise RuntimeError('Unquoted word expected, found %s%s' % (
        str(O), O.where_str()))
  
  default_contiguous_word_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                                     + "abcdefghijklmnopqrstuvwxyz" \
                                     + "0123456789" \
                                     + "_"
  class settings(slots_getstate_setstate):
  
    __slots__ = [
      "unquoted_single_character_words",
      "contiguous_word_characters",
      "enable_unquoted_embedded_quotes",
      "comment_characters",
      "meta_comment"]
  
    def __init__(O,
          unquoted_single_character_words="",
          contiguous_word_characters=None,
          enable_unquoted_embedded_quotes=True,
          comment_characters="",
          meta_comment=None):
      O.unquoted_single_character_words = unquoted_single_character_words
      if (contiguous_word_characters is None):
        O.contiguous_word_characters = default_contiguous_word_characters
      else:
        O.contiguous_word_characters = contiguous_word_characters
      O.enable_unquoted_embedded_quotes = enable_unquoted_embedded_quotes
      O.comment_characters = comment_characters
      O.meta_comment = meta_comment
  
  class word_iterator(slots_getstate_setstate):
  
    __slots__ = [
      "char_iter",
      "source_info",
      "list_of_settings"]
  
    def __init__(O,
          input_string,
          source_info=None,
          file_name=None,
          list_of_settings=None):
      assert source_info is None or file_name is None
      O.char_iter = character_iterator(input_string)
      if (source_info is None and file_name is not None):
        O.source_info = 'file "%s"' % file_name
      else:
        O.source_info = source_info
      if (list_of_settings is None):
        O.list_of_settings = [settings()]
      else:
        O.list_of_settings = list_of_settings
  
    def __iter__(O):
      return O
  
    def __next__(O, settings_index=0):
      settings = O.list_of_settings[settings_index]
      char_iter = O.char_iter
      char_iter.mark_for_backup()
      while True:
        c = next(char_iter)
        if (c is None): break
        if (c.isspace()): continue
        if (c in settings.comment_characters
            and (settings.meta_comment is None
                 or char_iter.look_ahead(n=len(settings.meta_comment))
                    != settings.meta_comment)):
          while True:
            c = next(char_iter)
            if (c is None or c == "\n"): break
        elif (c in ['"', "'"]):
          quote_char = c
          word_value = []
          word_line_number = char_iter.line_number
          if (char_iter.look_ahead(n=2) == quote_char+quote_char):
            char_iter.skip_ahead_1()
            char_iter.skip_ahead_1()
            quote_token = quote_char+quote_char+quote_char
          else:
            quote_token = quote_char
          while True:
            c = next(char_iter)
            if (c == quote_char):
              if (quote_token is quote_char): break
              if (char_iter.look_ahead(n=2) == quote_char+quote_char):
                char_iter.skip_ahead_1()
                char_iter.skip_ahead_1()
                break
            if (c == "\\"):
              _ = char_iter.look_ahead_1()
              if (_ == "\\"):
                char_iter.skip_ahead_1()
              elif (_ == quote_char):
                c = next(char_iter)
              elif (_ == "\n"):
                char_iter.skip_ahead_1()
                continue
            if (c is None):
              raise RuntimeError(
                "Syntax error: missing closing quote%s" % (
                  where_str(O.source_info, char_iter.line_number)))
            word_value.append(c)
          return word(
            value="".join(word_value),
            quote_token=quote_token,
            line_number=word_line_number,
            source_info=O.source_info)
        else:
          word_value = [c]
          word_line_number = char_iter.line_number
          if (c not in settings.unquoted_single_character_words
              and (settings.contiguous_word_characters == ""
                   or c in settings.contiguous_word_characters)):
            while True:
              c = char_iter.look_ahead_1()
              if (c is None): break
              if (c.isspace()): break
              if (c in settings.unquoted_single_character_words): break
              if (settings.contiguous_word_characters != ""
                  and c not in settings.contiguous_word_characters
                  and (not settings.enable_unquoted_embedded_quotes
                       or c not in ['"', "'"])):
                break
              word_value.append(c)
              char_iter.skip_ahead_1()
          return word(
            value="".join(word_value),
            line_number=word_line_number,
            source_info=O.source_info)
      raise StopIteration
  
    def try_pop(O, settings_index=0):
      try: return O.__next__(settings_index)
      except StopIteration: return None
  
    def pop(O, settings_index=0):
      try: return O.__next__(settings_index)
      except StopIteration: raise RuntimeError("Unexpected end of input.")
  
    def try_pop_unquoted(O, settings_index=0):
      word = O.try_pop(settings_index)
      if (word is not None):
        if (word.quote_token is not None): word.raise_unquoted_word_expected()
      return word
  
    def pop_unquoted(O, settings_index=0):
      word = O.pop(settings_index)
      if (word.quote_token is not None): word.raise_unquoted_word_expected()
      return word
  
    def backup(O):
      O.char_iter.restore_backup()
  
    def scan_for_start(O, intro, followups):
      O.char_iter.scan_for_start(intro, followups)
  