///|
using @basic {type Position, type Report}

///|
pub(all) enum Parser {
  MoonYacc
  Handrolled
  // Mixed
}

///|
pub fn parse_string(
  source : String,
  name~ : String = "",
  parser~ : Parser = Handrolled,
) -> (@syntax.Impls, Array[Report]) {
  parse_string_at(source, name~, parser~, start_pos={
    fname: name,
    lnum: 1,
    bol: 0,
    cnum: 0,
  })
}

///|
fn parse_string_at(
  source : String,
  start_pos~ : Position,
  name~ : String = "",
  parser~ : Parser = Handrolled,
) -> (@syntax.Impls, Array[Report]) {
  let { tokens, docstrings, errors, } = @lexer.tokens_from_string(
    source,
    comment=true,
    name~,
    start_pos~,
  )
  fn parse_by_moonyacc(tokens : @tokens.Triples) {
    let tokens = tokens.filter(fn(triple) {
      !(triple.0 is NEWLINE || triple.0 is COMMENT(_))
    })
    (@yacc_parser.structure(tokens), []) catch {
      UnexpectedEndOfInput(pos, _) =>
        (
          List([]),
          [
            Report::{
              loc: { start: pos, end: pos, },
              msg: "Unexpected end of file.",
            },
          ],
        )
      UnexpectedToken(token, (start, end), _) =>
        (
          List([]),
          [
            {
              loc: { start, end, },
              msg: "Unexpected `\{@debug.render(Debug::to_repr(token))}`.",
            },
          ],
        )
    }
  }

  let (impls, diagnostics) = match parser {
    MoonYacc => parse_by_moonyacc(tokens)
    Handrolled => @handrolled_parser.parse(tokens)
  }
  @docstring.attach_docstrings_to_impls(docstrings[:], impls)
  let reports = errors.filter_map(fn(error) {
    let (start, end, err) = error
    match err {
      Reserved_keyword(_) => None
      _ => Some(Report::{ loc: { start, end, }, msg: Show::to_string(err), })
    }
  })
  reports.append(diagnostics)
  (impls, reports)
}