docs: @Override
      public String readString() {
          final char[] text0 = this.text;
          int currpos = this.position;
          char expected = text0[++currpos];
          if (expected <= ' ') {
              for (;;) {
                  expected = text0[++currpos];
                  if (expected > ' ') break;
              }
          }
          if (expected != '"' && expected != '\'') {
              if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) {
                  if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') {
                      this.position = currpos;
                      if (text0.length > currpos + 4) {
                          char ch = text0[currpos + 1];
                          if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null;
                          final int start = currpos - 3;
                          for (;;) {
                              if (currpos >= text0.length) break;
                              ch = text0[currpos];
                              if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
                              currpos++;
                          }
                          if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
                          this.position = currpos - 1;
                          return new String(text0, start, currpos - start);
                      } else {
                          return null;
                      }
                  }
              } else {
                  final int start = currpos;
                  for (;;) {
                      if (currpos >= text0.length) break;
                      char ch = text0[currpos];
                      if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
                      currpos++;
                  }
                  if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
                  this.position = currpos - 1;
                  return new String(text0, start, currpos - start);
              }
              this.position = currpos;
              throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
          }
          final int start = ++currpos;
          for (;;) {
              char ch = text0[currpos];
              if (ch == expected) {
                  break;
              } else if (ch == '\\') {
                  this.position = currpos - 1;
                  return readEscapeValue(expected, start);
              }
              currpos++;
          }
          this.position = currpos;
          return new String(text0, start, currpos - start);
      }