///|
pub fn trim_ascii(text : String) -> String {
  text.trim().to_owned()
}

///|
pub fn split_lines_preserve(text : String) -> Array[String] {
  let result : Array[String] = []
  let mut current = StringBuilder::new()
  for ch in text {
    if ch == '\n' {
      result.push(current.to_string())
      current = StringBuilder::new()
    } else if ch != '\r' {
      current.write_char(ch)
    }
  }
  result.push(current.to_string())
  result
}

///|
pub fn normalize_identifier(text : String) -> String {
  let out = StringBuilder::new()
  let mut pending_dash = false
  let mut wrote = false
  for ch in text {
    if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') {
      if pending_dash && wrote {
        out.write_char('-')
      }
      pending_dash = false
      wrote = true
      out.write_char(ch)
    } else if ch >= 'A' && ch <= 'Z' {
      if pending_dash && wrote {
        out.write_char('-')
      }
      pending_dash = false
      wrote = true
      out.write_char(lower_char(ch))
    } else if ch == '-' || ch == '_' || ch == '/' || ch == ' ' || ch == '.' {
      if wrote {
        pending_dash = true
      }
    }
  }
  out.to_string()
}

///|
pub fn is_quoted(text : String) -> Bool {
  let value = text.trim().to_owned()
  value.length() >= 2 &&
  (
    (value[0] == '"' && value[value.length() - 1] == '"') ||
    (value[0] == '\'' && value[value.length() - 1] == '\'')
  )
}

///|
fn lower_char(ch : Char) -> Char {
  match ch {
    'A' => 'a'
    'B' => 'b'
    'C' => 'c'
    'D' => 'd'
    'E' => 'e'
    'F' => 'f'
    'G' => 'g'
    'H' => 'h'
    'I' => 'i'
    'J' => 'j'
    'K' => 'k'
    'L' => 'l'
    'M' => 'm'
    'N' => 'n'
    'O' => 'o'
    'P' => 'p'
    'Q' => 'q'
    'R' => 'r'
    'S' => 's'
    'T' => 't'
    'U' => 'u'
    'V' => 'v'
    'W' => 'w'
    'X' => 'x'
    'Y' => 'y'
    'Z' => 'z'
    _ => ch
  }
}