///|
fn unescape_step_escape(text : String, index : Int) -> (Char, Int)? {
  match text.get_char(index) {
    Some('\\') => Some(('\\', index + 1))
    Some('"') => Some(('"', index + 1))
    Some('n') => Some(('\n', index + 1))
    Some('r') => Some(('\r', index + 1))
    Some('t') => Some(('\t', index + 1))
    Some('X') =>
      if index + 3 < text.length() && text.get_char(index + 1) is Some('2') {
        let hex = text[index + 2:index + 4].to_owned()
        match parse_hex_byte(hex) {
          Some(value) =>
            match value.to_int().to_char() {
              Some(character) => Some((character, index + 4))
              None => None
            }
          None => None
        }
      } else {
        None
      }
    _ => None
  }
}

///|
fn parse_hex_digit(character : Char) -> Int? {
  match character {
    '0'..='9' => Some(character.to_int() - '0'.to_int())
    'a'..='f' => Some(character.to_int() - 'a'.to_int() + 10)
    'A'..='F' => Some(character.to_int() - 'A'.to_int() + 10)
    _ => None
  }
}

///|
fn parse_hex_byte(text : String) -> Byte? {
  if text.length() != 2 {
    return None
  }
  match (text.get_char(0), text.get_char(1)) {
    (Some(a), Some(b)) =>
      match (parse_hex_digit(a), parse_hex_digit(b)) {
        (Some(high), Some(low)) => Some((high * 16 + low).to_byte())
        _ => None
      }
    _ => None
  }
}

///|
fn decode_step_string(text : String) -> String {
  let output = StringBuilder()
  let mut index = 0
  for ;; {
    match text.get_char(index) {
      None => break
      Some('\\') =>
        match unescape_step_escape(text, index + 1) {
          Some((character, next)) => {
            output.write_char(character)
            index = next
          }
          None => {
            output.write_char('\\')
            index += 1
          }
        }
      Some(character) => {
        output.write_char(character)
        index += 1
      }
    }
  }
  output.to_string()
}

///|
fn parse_int64(
  text : String,
  position : SourcePosition,
) -> Int64 raise ParseError {
  @strconv.from_str(text) catch {
    _ => raise ParseError::InvalidNumber(position~, text~)
  }
}

///|
fn parse_double(
  text : String,
  position : SourcePosition,
) -> Double raise ParseError {
  @strconv.from_str(text) catch {
    _ => raise ParseError::InvalidNumber(position~, text~)
  }
}

///|
fn value_to_string(value : Value) -> String {
  match value {
    Null => "$".to_string()
    Omitted => "*".to_string()
    Integer(number) => number.to_string()
    Real(number) => number.to_string()
    String(text) => step_quote(text)
    Enumeration(text) => "." + text + "."
    Reference(id) => "#" + id.to_string()
    Binary(text) => "\"" + text + "\""
    Typed(name~, value~) => name + "(" + value_to_string(value) + ")"
    List(values) => "(" + values.map(value_to_string).join(",") + ")"
  }
}

///|
pub fn Value::to_step(self : Value) -> String {
  value_to_string(self)
}