docs: def parse_chars(bels: list, errors: Errors) -> Tuple[CharLocs, Errors]:
      """Scan BEL string to map parens, quotes, commas
  
      Args:
          bels: bel string as an array of characters
          errors: list of error tuples ('<type>', '<msg>')
  
      Returns:
          (char_locs, errors): character locations and errors
      """
      pstack, qstack, nested_pstack = [], [], []
      parens, nested_parens, quotes, commas = {}, {}, {}, {}
  
      notquoted_flag = True
  
      for i, c in enumerate(bels):
          prior_char = i - 1
          # print('BEL', prior_char, b[prior_char])
  
          # Find starting quote
          if c == '"' and bels[prior_char] != "\\" and len(qstack) == 0:
              qstack.append(i)
              notquoted_flag = False
  
          # Find closing quote
          elif c == '"' and bels[prior_char] != "\\":
              quotes[qstack.pop()] = i
              notquoted_flag = True
  
          # Find all escaped quotes outside of quoted string
          elif c == '"' and bels[prior_char] == "\\" and len(qstack) == 0:
              errors.append(
                  (
                      "ERROR",
                      f"Escaped quote outside of quoted string at location: {i - 1}",
                      (i - 1, i - 1),
                  )
              )
  
          # Find all nested object opening parens
          elif notquoted_flag and c == "(" and bels[prior_char] == " ":
              if len(nested_pstack) > 1:
                  errors.append(
                      (
                          "ERROR",
                          f"More than one nested parenthesis or left parenthesis following a space character",
                          (i, i),
                      )
                  )
  
              nested_pstack.append(i)
  
          # Find all opening parens
          elif notquoted_flag and c == "(" and bels[prior_char] not in ["\\"]:
              pstack.append(i)
  
          # Find all closing parens
          elif notquoted_flag and c == ")" and bels[prior_char] != "\\":
              if len(pstack):
                  if len(pstack) > 1:
                      parens[pstack.pop()] = (i, "child")
                  else:
                      parens[pstack.pop()] = (i, "top")
              elif len(nested_pstack):
                  nested_parens[nested_pstack.pop()] = (i, "top")
              else:
                  errors.append(
                      (
                          "ERROR",
                          f"Missing left parenthesis for right parenthesis at location {i}",
                          (i, i),
                      )
                  )
          # Find comma outside of quoted string
          elif notquoted_flag and c == "," and len(qstack) == 0:
              sparen = pstack[-1]
              if sparen not in commas:
                  commas[sparen] = [i]
              else:
                  commas[sparen].append(i)
  
      while len(pstack):
          errors.append(
              (
                  "ERROR",
                  f"Missing right parenthesis for left parenthesis at location {pstack[-1]}",
                  (pstack[-1], pstack[-1]),
              )
          )
          if len(pstack) > 1:
              parens[pstack.pop()] = (-1, "child")
          else:
              parens[pstack.pop()] = (-1, "top")
  
      while len(nested_pstack):
          errors.append(
              (
                  "ERROR",
                  f"Missing right parenthesis for nested object left parenthesis at location {nested_pstack[-1]}",
                  (nested_pstack[-1], nested_pstack[-1]),
              )
          )
          nested_parens[nested_pstack.pop()] = (-1, "top")
  
      if len(qstack):
          missing_quote = qstack.pop()
          errors.append(
              (
                  "ERROR",
                  f"Missing right quote for left quote at location {missing_quote}",
                  (missing_quote, missing_quote),
              )
          )
  
      return (
          {
              "parens": parens,
              "nested_parens": nested_parens,
              "quotes": quotes,
              "commas": commas,
          },
          errors,
      )