let read ?start ?(fmt=default_fmt) r =
  let {allow_sharp_comments;
       allow_semicolon_comments;
       allow_empty_lines;
       comments_only_at_top;
       max_line_length;
       alphabet} = fmt
  in
  let error_string s = Some (Or_error.error_string s) in
  let item0s = read0 r
    ?start
    ~allow_sharp_comments
    ~allow_semicolon_comments
    ~allow_empty_lines
    ?max_line_length
    ?alphabet
  in
  match read_header ~allow_empty_lines item0s with
  | Error _ as e -> e
  | Ok header ->
    let rec f description partial_seqs : item Or_error.t option =
      match Stream.peek item0s with
      | Some (Ok (`Comment _)) -> begin
        if comments_only_at_top then
          error_string "comments_only_at_top = true but got comment later"
        else (
          Stream.junk item0s;
          f description partial_seqs
        )
      end

      | Some (Ok `Empty_line-> begin
        if allow_empty_lines then (
          Stream.junk item0s;
          f description partial_seqs
        )
        else
          error_string "allow_empty_lines = false but got empty line"
      end

      | Some (Ok (`Description x)) -> begin
        match description,partial_seqs with
        | None, [] -> (
          Stream.junk item0s;
          f (Some x) []
        )
        | None, _::_ ->
          (* `Partial_sequence branch assures this doesn't happen*)
          assert false
        | Some _, [] ->
          error_string "previous description line not followed by sequence"
        | Some description, partial_seqs ->
          Some (Ok {
            description;
            sequence = partial_seqs |> List.rev |> String.concat ~sep:"";
          })
      end

      | Some (Ok (`Partial_sequence x)) -> begin
        match description,partial_seqs with
        | None, _ ->
          error_string "sequence not preceded by description line"
        | Some _, partial_seqs -> (
          Stream.junk item0s;
          f description (x::partial_seqs)
        )
      end

      | Some (Error _ as e) -> begin
        Stream.junk item0s;
        Some e
      end

      | None -> begin
        match description,partial_seqs with
        | None, [] -> None
        | None, _::_ ->
          (* `Partial_sequence branch assures this doesn't happen*)
          assert false
        | Some _, [] ->
          error_string
            "description line not followed by sequence, reached end-of-file"
        | Some description, partial_seqs ->
          Some (Ok {
            description;
            sequence = partial_seqs |> List.rev |> String.concat ~sep:"";
          })
      end
    in
    Ok (header, Stream.from (fun _ -> f None []))