docs: def gen_properties(output, ascii_props=False, append=False):
      """Generate the property table and dump it to the provided file."""
  
      files = {
          'gc': os.path.join(output, 'generalcategory.py'),
          'blk': os.path.join(output, 'block.py'),
          'sc': os.path.join(output, 'script.py'),
          'bc': os.path.join(output, 'bidiclass.py'),
          'binary': os.path.join(output, 'binary.py'),
          'posix': os.path.join(output, 'posix.py'),
          'age': os.path.join(output, 'age.py'),
          'ea': os.path.join(output, 'eastasianwidth.py'),
          'gcb': os.path.join(output, 'graphemeclusterbreak.py'),
          'lb': os.path.join(output, 'linebreak.py'),
          'sb': os.path.join(output, 'sentencebreak.py'),
          'wb': os.path.join(output, 'wordbreak.py'),
          'hst': os.path.join(output, 'hangulsyllabletype.py'),
          'dt': os.path.join(output, 'decompositiontype.py'),
          'jt': os.path.join(output, 'joiningtype.py'),
          'jg': os.path.join(output, 'joininggroup.py'),
          'nt': os.path.join(output, 'numerictype.py'),
          'nv': os.path.join(output, 'numericvalue.py'),
          'ccc': os.path.join(output, 'canonicalcombiningclass.py'),
          'qc': os.path.join(output, 'quickcheck.py'),
          'alias': os.path.join(output, 'alias.py')
      }
  
      files['scx'] = os.path.join(output, 'scriptextensions.py')
      files['insc'] = os.path.join(output, 'indicsyllabiccategory.py')
      if PY34:
          files['bpt'] = os.path.join(output, 'bidipairedbrackettype.py')
      if PY35:
          files['inpc'] = os.path.join(output, 'indicpositionalcategory.py')
      else:
          files['inmc'] = os.path.join(output, 'indicmatracategory.py')
      if PY37:
          files['vo'] = os.path.join(output, 'verticalorientation.py')
  
      prefix = "ascii" if ascii_props else 'unicode'
  
      # `L&` or `Lc` won't be found in the table,
      # so initialize 'c' at the start. `&` will have to be converted to 'c'
      # before sending it through.
      categories = [
          'generalcategory', 'script', 'block',
          'bidiclass', 'eastasianwidth', 'linebreak',
          'hangulsyllabletype', 'wordbreak', 'sentencebreak',
          'graphemeclusterbreak', 'decompositiontype', 'joiningtype',
          'joininggroup', 'numerictype', 'numericvalue',
          'canonicalcombiningclass', 'age'
      ]
      categories.append('scriptextensions')
      categories.append('indicsyllabiccategory')
      if PY34:
          categories.append('bidipairedbrackettype')
      if PY35:
          categories.append('indicpositionalcategory')
      else:
          categories.append('indicmatracategory')
      if PY37:
          categories.append('verticalorientation')
      if ascii_props:
          print('=========Ascii Tables=========')
      else:
          print('========Unicode Tables========')
      print('Building: General Category')
      max_range = ASCII_RANGE if ascii_props else UNICODE_RANGE
      all_chars = ALL_ASCII if ascii_props else ALL_CHARS
      table = {'l': {'c': []}}
      itable = {'l': {}}
      with codecs.open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'UnicodeData.txt'), 'r', 'utf-8') as uf:
          for line in uf:
              data = line.strip().split(';')
              if data:
                  i = int(data[0], 16)
                  if i > max_range[1]:
                      continue
                  p = data[2].lower()
                  if p[0] not in table:
                      table[p[0]] = {}
                      itable[p[0]] = {}
                  if p[1] not in table[p[0]]:
                      table[p[0]][p[1]] = []
                  table[p[0]][p[1]].append(i)
                  # Add LC which is a combo of Ll, Lu, and Lt
                  if p[0] == 'l' and p[1] in ('l', 'u', 't'):
                      table['l']['c'].append(i)
  
      # Create inverse of each category
      for k1, v1 in table.items():
          inverse_category = set()
          for k2, v2 in v1.items():
              s = set(v2)
              inverse_category |= s
          itable[k1]['^'] = list(all_chars - inverse_category)
  
      # Generate Unicode blocks
      print('Building: Blocks')
      gen_blocks(files['blk'], ascii_props, append, prefix)
  
      # Generate Unicode scripts
      print('Building: Scripts & Script Extensions')
      gen_scripts(
          'Scripts.txt', 'ScriptExtensions.txt', 'scripts', 'script_extensions', files['sc'], files['scx'],
          notexplicit='zzzz', ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      # Generate Unicode bidi classes
      print('Building: Bidi Classes')
      gen_bidi(files['bc'], ascii_props, append, prefix)
  
      if PY34:
          print('Building: Bidi Paired Bracket Type')
          gen_enum(
              'BidiBrackets.txt', 'bidi_paired_bracket_type', files['bpt'], notexplicit='n',
              field=2, ascii_props=ascii_props, append=append, prefix=prefix
          )
  
      # Generate Unicode binary
      print('Building: Binary')
      binary = gen_binary(table, files['binary'], ascii_props, append, prefix)
  
      # Generate posix table and write out to file.
      print('Building: Posix')
      gen_posix(files['posix'], is_bytes=ascii_props, append=append, prefix=prefix)
  
      print('Building: Age')
      gen_age(files['age'], ascii_props, append, prefix)
  
      print('Building: East Asian Width')
      gen_enum(
          'EastAsianWidth.txt', 'east_asian_width', files['ea'],
          notexplicit='n', ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Grapheme Cluster Break')
      gen_enum(
          'GraphemeBreakProperty.txt', 'grapheme_cluster_break', files['gcb'], notexplicit='other',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Line Break')
      gen_enum(
          'LineBreak.txt', 'line_break', files['lb'], notexplicit='unknown',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Sentence Break')
      gen_enum(
          'SentenceBreakProperty.txt', 'sentence_break', files['sb'], notexplicit='other',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Word Break')
      gen_enum(
          'WordBreakProperty.txt', 'word_break', files['wb'], notexplicit='other',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      if PY35:
          print('Building: Indic Positional Category')
          gen_enum(
              'IndicPositionalCategory.txt', 'indic_positional_category', files['inpc'], notexplicit='na',
              ascii_props=ascii_props, append=append, prefix=prefix
          )
      else:
          print('Building: Indic Matra Category')
          gen_enum(
              'IndicMatraCategory.txt', 'indic_matra_category', files['inmc'], notexplicit='na',
              ascii_props=ascii_props, append=append, prefix=prefix
          )
      print('Building: Indic Syllabic Category')
      gen_enum(
          'IndicSyllabicCategory.txt', 'indic_syllabic_category', files['insc'], notexplicit='other',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Hangul Syllable Type')
      gen_enum(
          'HangulSyllableType.txt', 'hangul_syllable_type', files['hst'], notexplicit='na',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Decomposition Type')
      gen_enum(
          'DerivedDecompositionType.txt', 'decomposition_type', files['dt'], notexplicit='none',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Joining Type')
      gen_enum(
          'DerivedJoiningType.txt', 'joining_type', files['jt'], notexplicit='u',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Joining Group')
      gen_enum(
          'DerivedJoiningGroup.txt', 'joining_group', files['jg'], notexplicit='nonjoining',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Numeric Type')
      gen_enum(
          'DerivedNumericType.txt', 'numeric_type', files['nt'], notexplicit='none',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Numeric Value')
      gen_enum(
          'DerivedNumericValues.txt', 'numeric_values', files['nv'], field=3, notexplicit='nan',
          ascii_props=ascii_props, append=append, prefix=prefix
      )
  
      print('Building: Canonical Combining Class')
      gen_ccc(files['ccc'], ascii_props, append, prefix)
  
      print('Building: NF* Quick Check')
      categories.extend(gen_nf_quick_check(files['qc'], ascii_props, append, prefix))
  
      if PY37:
          print('Building: Vertical Orientation')
          gen_enum(
              'VerticalOrientation.txt', 'vertical_orientation', files['vo'], notexplicit='R',
              ascii_props=ascii_props, append=append, prefix=prefix
          )
  
      if not ascii_props:
          print('Building: Aliases')
          gen_alias(categories, binary, files['alias'], ascii_props, append, prefix)
  
      # Convert char values to string ranges.
      char2range(table, is_bytes=ascii_props)
      char2range(itable, is_bytes=ascii_props, invert=False)
      for k1, v1 in itable.items():
          table[k1]['^'] = v1['^']
  
      with codecs.open(files['gc'], 'a' if append else 'w', 'utf-8') as f:
          if not append:
              f.write(HEADER)
          # Write out the Unicode properties
          f.write('%s_properties = {\n' % prefix)
          count = len(table) - 1
          i = 0
          for k1, v1 in sorted(table.items()):
              f.write('    "%s": {\n' % k1)
              count2 = len(v1) - 1
              j = 0
              for k2, v2 in sorted(v1.items()):
                  f.write('        "%s": "%s"' % (k2, v2))
                  if j == count2:
                      f.write('\n    }')
                  else:
                      f.write(',\n')
                  j += 1
              if i == count:
                  f.write('\n}\n')
              else:
                  f.write(',\n')
              i += 1
  
      if not append:
          with codecs.open(os.path.join(output, '__init__.py'), 'w') as f:
              f.write(HEADER)
              for x in sorted(files):
                  f.write('from .%s import *  # noqa\n' % os.path.basename(files[x])[:-3])