pub fn parse(input : String) -> Result[JSONPath, String] {
  let len = input.length()
  if len == 0 || input[0].to_int() != '$'.to_int() {
    return Err("JSONPath must start with '$'")
  }
  let segments = [PathSegment::Root]
  let mut i = 1
  while i < len {
    let c = input[i].to_int()
    if c == '.'.to_int() {
      i = i + 1
      if i < len && input[i].to_int() == '.'.to_int() {
        i = i + 1
        let (name, next_i) = read_ident(input, i)
        if name == "" {
          return Err("Expected identifier after '..'")
        }
        segments.push(PathSegment::Descendant(name))
        i = next_i
      } else if i < len && input[i].to_int() == '*'.to_int() {
        segments.push(PathSegment::Wildcard)
        i = i + 1
      } else {
        let (name, next_i) = read_ident(input, i)
        if name == "" {
          return Err("Expected identifier after '.'")
        }
        segments.push(PathSegment::Child(name))
        i = next_i
      }
    } else if c == '['.to_int() {
      i = i + 1
      if i < len && input[i].to_int() == '*'.to_int() {
        segments.push(PathSegment::Wildcard)
        i = i + 1
        if i < len && input[i].to_int() == ']'.to_int() {
          i = i + 1
        } else {
          return Err("Expected ']' after '*'")
        }
      } else if i < len && (input[i].to_int() == '\''.to_int() || input[i].to_int() == '"'.to_int()) {
        let quote = input[i].to_int()
        i = i + 1
        let mut name = ""
        while i < len && input[i].to_int() != quote {
          name = name + input[i].to_int().unsafe_to_char().to_string()
          i = i + 1
        }
        if i < len && input[i].to_int() == quote {
          i = i + 1
        } else {
          return Err("Unterminated string literal")
        }
        if i < len && input[i].to_int() == ']'.to_int() {
          i = i + 1
        } else {
          return Err("Expected ']' after string literal")
        }
        segments.push(PathSegment::Child(name))
      } else if i < len && is_digit(input[i].to_int()) {
        let mut num = 0
        while i < len && is_digit(input[i].to_int()) {
          num = num * 10 + (input[i].to_int() - 48)
          i = i + 1
        }
        if i < len && input[i].to_int() == ']'.to_int() {
          i = i + 1
        } else {
          return Err("Expected ']' after number")
        }
        segments.push(PathSegment::Index(num))
      } else {
        return Err("Unsupported syntax inside '['")
      }
    } else {
      return Err("Unexpected character")
    }
  }
  Ok(segments)
}

fn read_ident(input : String, start : Int) -> (String, Int) {
  let len = input.length()
  let mut i = start
  let mut ident = ""
  while i < len && is_ident_char(input[i].to_int()) {
    ident = ident + input[i].to_int().unsafe_to_char().to_string()
    i = i + 1
  }
  (ident, i)
}

fn is_ident_char(c : Int) -> Bool {
  (c >= 'a'.to_int() && c <= 'z'.to_int()) || (c >= 'A'.to_int() && c <= 'Z'.to_int()) || (c >= '0'.to_int() && c <= '9'.to_int()) || c == '_'.to_int() || c == '-'.to_int()
}

fn is_digit(c : Int) -> Bool {
  c >= '0'.to_int() && c <= '9'.to_int()
}