///|
fn parse_moon_mod_document(
  name : String,
  source : String,
) -> (Ast, Array[@basic.Report]) {
  let (ast, reports) = @moon_config.parse_moon_mod(name~, source)
  (convert_moon_config_ast(ast), convert_moon_config_reports(reports))
}

///|
/// The moon.mod AST conversion is intentionally lossy: `Null` maps to
/// `Ast::Str("")`, `Float` maps to `Ast::Int`, and key locations track the
/// value position, because the module only reads version/description/license
/// style fields today. Add proper Float/Null Ast cases and key positions if
/// richer moon.mod values ever need validation.
fn convert_moon_config_ast(ast : @moon_config.Ast) -> Ast {
  match ast {
    Null(loc~) => Ast::Str("", convert_moon_config_loc(loc))
    Bool(value, loc~) => Ast::Bool(value, convert_moon_config_loc(loc))
    Str(value, loc~) =>
      Ast::Str(decode_config_string(value), convert_moon_config_loc(loc))
    Float(value, loc~) => Ast::Int(value, convert_moon_config_loc(loc))
    Arr(items, loc~) => {
      let values : Array[Ast] = []
      for item in items {
        values.push(convert_moon_config_ast(item))
      }
      Ast::Arr(values, convert_moon_config_loc(loc))
    }
    Obj(fields, loc~) => {
      let values : Array[ConfigField] = []
      for field in fields {
        let (key, value) = field
        values.push(ConfigField::{
          key,
          key_loc: convert_moon_config_loc(value.loc()),
          value: convert_moon_config_ast(value),
        })
      }
      Ast::Obj(values, convert_moon_config_loc(loc))
    }
  }
}

///|
fn convert_moon_config_reports(
  reports : Array[@parser_basic.Report],
) -> Array[@basic.Report] {
  let out : Array[@basic.Report] = []
  for report in reports {
    out.push(@basic.Report::{
      loc: convert_moon_config_loc(report.loc),
      msg: report.msg,
    })
  }
  out
}

///|
fn convert_moon_config_loc(loc : @parser_basic.Location) -> @basic.Location {
  @basic.Location::{
    start: convert_moon_config_pos(loc.start),
    end: convert_moon_config_pos(loc.end),
  }
}

///|
fn convert_moon_config_pos(pos : @parser_basic.Position) -> @basic.Position {
  @basic.Position::{
    fname: pos.fname,
    lnum: pos.lnum,
    bol: pos.bol,
    cnum: pos.cnum,
  }
}