///|
// Execute replacement fields lazily in the same order as string.Formatter.
fn method_expand(
  format : String,
  value : NamedFormatValue,
  position : Int32?,
  depth : Int32,
  available~ : Int64 = 2147483647L,
) -> String raise @core.DataError {
  if depth < 0 {
    return named_format_error("ValueError", "Max string recursion exceeded")
  }
  let chars = format.to_array()
  let output = StringBuilder()
  let mut used = 0L
  let mut index = 0
  while index < chars.length() {
    let character = chars[index]
    if character != '{' && character != '}' {
      used += if character.to_int() > 65535 { 2L } else { 1L }
      if used > available {
        return named_format_error(
          "ResourceError", "formatted text exceeds String addressability",
        )
      }
      output.write_char(character)
      index += 1
      continue
    }
    if index + 1 < chars.length() && chars[index + 1] == character {
      used += 1L
      if used > available {
        return named_format_error(
          "ResourceError", "formatted text exceeds String addressability",
        )
      }
      output.write_char(character)
      index += 2
      continue
    }
    if character == '}' {
      return named_format_error(
        "ValueError", "Single '}' encountered in format string",
      )
    }
    index += 1
    if index == chars.length() {
      return named_format_error(
        "ValueError", "Single '{' encountered in format string",
      )
    }
    let field_start = index
    while index < chars.length() &&
          chars[index] != ':' &&
          chars[index] != '!' &&
          chars[index] != '}' {
      if chars[index] == '{' {
        return named_format_error("ValueError", "unexpected '{' in field name")
      }
      if chars[index] == '[' {
        index += 1
        while index < chars.length() && chars[index] != ']' {
          index += 1
        }
        if index == chars.length() {
          break
        }
      }
      index += 1
    }
    if index == chars.length() {
      return named_format_error(
        "ValueError", "expected '}' before end of string",
      )
    }
    let name = String::from_array(chars[field_start:index])
    let mut conversion : Char? = None
    if chars[index] == '!' {
      index += 1
      if index == chars.length() {
        return named_format_error(
          "ValueError", "end of string while looking for conversion specifier",
        )
      }
      conversion = Some(chars[index])
      index += 1
      if index == chars.length() {
        return named_format_error("ValueError", "unmatched '{' in format spec")
      }
      if chars[index] != ':' && chars[index] != '}' {
        return named_format_error(
          "ValueError", "expected ':' after conversion specifier",
        )
      }
    }
    let spec = if chars[index] == ':' {
      index += 1
      let start = index
      let mut nested = 0
      while index < chars.length() {
        if chars[index] == '{' {
          nested += 1
        }
        if chars[index] == '}' {
          if nested == 0 {
            break
          }
          nested -= 1
        }
        index += 1
      }
      if index == chars.length() {
        return named_format_error("ValueError", "unmatched '{' in format spec")
      }
      String::from_array(chars[start:index])
    } else {
      ""
    }
    index += 1
    let field = method_resolve_field(name, value, position)
    let field = match conversion {
      None => field
      Some('s') => Text(method_str(field))
      Some('r') => Text(method_repr(field))
      Some('a') => Text(method_repr(field, ascii=true))
      Some(character) => {
        ignore(
          named_format_error(
            "ValueError",
            "Unknown conversion specifier " + String::from_array([character]),
          ),
        )
        field
      }
    }
    let spec = method_expand(spec, value, position, depth - 1)
    let text = method_format_field(field, spec, available=available - used)
    used += Int64::from_int(text.length())
    output.write_string(BaseFormatter::fix_minus(text))
  }
  output.to_string()
}

///|
fn format_method_checked(
  format : String,
  value : NamedFormatValue,
  position : Int32?,
) -> String raise @core.DataError {
  method_expand(format, value, position, 2)
}