Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file mli_parser.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262moduleCode_block=structtypemetadata={language_tag:stringOdoc_parser.Loc.with_location;labels:stringOdoc_parser.Loc.with_locationoption;}typet={location:Odoc_parser.Loc.span;metadata:metadata option;contents:string;}end(* odoc-parser adjusts for the initial [** *)letdocstring_start_adjustment=String.length"(**"letdrop_lastlst =matchList.revlstwith|[]->None|last::rev_tl->Some(List.revrev_tl,last)(* drop_first_and_last [1; 2; 3; 4] = Some (1, Some ([2; 3], 4)). *)letdrop_first_and_last=function|[]->None|first::tl->Some(first,drop_lasttl)letslicelines~(start:Odoc_parser.Loc.point)~(end_:Odoc_parser.Loc.point)=letlines_to_include =Util.Array.slicelines~from:(start.line-1)~to_:(end_.line-1)|>Array.to_listinmatchdrop_first_and_lastlines_to_includewith|None->""|Some(line,None)->String.sublinestart.column(end_.column-start.column)(* Imagine we were slicing the file from (Line 2, Column 3) to (Line 6, Column 7):
0123456789
1 ----------
2 ---[---
3 ---------
4 --
5 ----------
6 -------]--
7 ----------
8 ----------
The case below handles this multiline case, concatenating the included substrings
from lines 2-6 ([lines_to_include]). *)|Some(first_line,Some(stripped,last_line))->letfirst_line =String.subfirst_linestart.column(String.lengthfirst_line-start.column)inletlast_line=String.sublast_line0end_.columninString.concat"\n"([first_line]@stripped@[last_line])letis_newline c=c='\n'letfind_nth_lines=letmax_index=String.lengths-1inletindexes_of_newlines =s|>String.to_seqi|>Seq.filter_map(fun(i,c)->matchis_newlinecwithtrue->Somei|false->None)inletindexes_of_line_starts=indexes_of_newlines|>Seq.filter_map(funi->match i<max_indexwithtrue->Some(i+1)|false->None)in(* first line always starts at index zero, even if there is no preceeding newline *)letindexes=0::List.of_seqindexes_of_line_startsinfunnth->(* index starts at zero but lines go from 1 *)List.nth_optindexes(nth-1)letpoint_to_indexoffset_of_line_start(point:Odoc_parser.Loc.point)=letoffset=offset_of_line_start point.line+point.columnin(* online 1 odoc-parser adjusts by the start of the docstring, undo *)matchpoint.linewith1->offset-docstring_start_adjustment|_->offsetletinitial_line_number=1letdislocate_point ~(location:Lexing.position)(point:Odoc_parser.Loc.point)={pointwithline=point.line-location.pos_lnum+initial_line_number}letslice_location~(location:Lexing.position)offset_of_line_start(span:Odoc_parser.Loc.span)s=letstart=dislocate_point ~location span.startinletend_=dislocate_point ~locationspan.end_inletstart_index =point_to_indexoffset_of_line_startstartinletend_index=point_to_index offset_of_line_start end_inletlen=end_index-start_indexinString.subsstart_indexlenletextract_code_blocks~(location:Lexing.position)~docstring=letoffset_in_string=find_nth_linedocstringinletoffset_of_line_startnth=matchoffset_in_string nthwith|None->Fmt.failwith"Attempting to reach invalid line"|Someoffset->offsetinletrec accblocks=List.map(fun block->matchOdoc_parser.Loc.valueblockwith|`Code_block(metadata,{Odoc_parser.Loc.value =_;location=span})->letmetadata=Option.map(fun(language_tag,labels)->Code_block.{language_tag;labels})metadatainletcontents=slice_location ~locationoffset_of_line_startspandocstringin[{Code_block.location=block.location;metadata;contents }]|`List(_,_,lists)->List.mapacclists|>List.concat|_->[])blocks|>List.concatinletparsed=Odoc_parser.parse_comment~location~text:docstringinList.iter(funerror->failwith (Odoc_parser.Warning.to_stringerror))(Odoc_parser.warnings parsed);List.map(funelement ->matchelementwith|{Odoc_parser.Loc.value=#Odoc_parser.Ast.nestable_block_element;_}ase->acc[e]|{value=`Tagtag;_}->(matchtagwith|`Deprecatedblocks->accblocks|`Param(_,blocks)->accblocks|`Raise(_,blocks)->accblocks|`Returnblocks->accblocks|`See(_,_,blocks)->accblocks|`Before(_,blocks)->accblocks|_->[])|{value=`Heading_;_}->[])(Odoc_parser.astparsed)|>List.concatletdocstringslexbuf=letreclooplist=matchLexer.token_with_commentslexbufwith|Parser.EOF-> list|Parser.DOCSTRINGdocstring->letdocstring =(Docstrings.docstring_bodydocstring,Docstrings.docstring_locdocstring)inloop (docstring::list)|_->loop listinloop[]|>List.revletconvert_pos (p:Lexing.position)(pt:Odoc_parser.Loc.point)={pwithpos_lnum=pt.line;pos_cnum=pt.column}letconvert_loc(loc:Location.t)(sp:Odoc_parser.Loc.span)=letloc_start=convert_pos loc.loc_start sp.startinletloc_end=convert_posloc.loc_endsp.end_in{locwithloc_start;loc_end}letdocstring_code_blocksstr=Lexer.handle_docstrings:=true;Lexer.init();List.map(fun(docstring,(cmt_loc:Location.t))->letlocation={cmt_loc.loc_startwithpos_cnum=cmt_loc.loc_start.pos_cnum+docstring_start_adjustment;}inletblocks=extract_code_blocks~location~docstringinList.map(fun(b:Code_block.t)->(b,convert_loccmt_locb.location))blocks)(docstrings(Lexing.from_stringstr))|>List.concatletmake_block~loccode_block=lethandle_header=function|SomeCode_block.{language_tag;labels}->letopenUtil.Result.Infixinletlanguage_tag =Odoc_parser.Loc.valuelanguage_taginletheader=Block.Header.of_stringlanguage_taginlet*labels=matchlabels with|None-> Ok[]|Somelabels->(letlabels=Odoc_parser.Loc.valuelabels|>String.triminmatchLabel.of_stringlabelswith|Oklabels->Oklabels|Errormsgs->Error(List.hdmsgs)(* TODO: Report precise location *))inletlanguage_label=Label.Language_taglanguage_taginOk(header,language_label::labels)|None->(* If not specified, blocks are run as ocaml blocks *)Ok(SomeOCaml,[])inmatchhandle_headercode_block.Code_block.metadata with|Error_ase->e|Ok(header,labels)->letcontents =String.split_on_char'\n'code_block.contentsinBlock.mk~loc~section:None~labels~header~contents~legacy_labels:false~errors:[]letparse_mlifile_contents=(* Find the locations of the code blocks within [file_contents], then slice it up into
[Text] and [Block] parts by using the starts and ends of those blocks as
boundaries. *)letcode_blocks=docstring_code_blocksfile_contentsinletcursor={Odoc_parser.Loc.line=1;column=0}inletlines=String.split_on_char'\n'file_contents|>Array.of_listinletcursor,tokens=List.fold_left(fun(cursor,code_blocks)((code_block:Code_block.t),loc)->letpre_text=Document.Text(slicelines~start:cursor~end_:code_block.location.start)inletblock=matchmake_block~loccode_blockwith|Okblock ->Document.Blockblock|Error(`Msgmsg)->Fmt.failwith"Error creating block: %s"msginletcursor=code_block.location.end_in(* append them in reverse order, since this is a fold_left *)letcode_blocks=block::pre_text::code_blocksin(cursor,code_blocks))(cursor,[])code_blocksinlettokens=List.revtokensinleteof={Odoc_parser.Loc.line=Array.lengthlines;column=String.lengthlines.(Array.lengthlines-1);}inleteof_is_beyond_location(loc:Odoc_parser.Loc.point)=eof.line>loc.line ||(eof.line=loc.line&&eof.column>loc.column)inifeof_is_beyond_locationcursorthenletremainder=slicelines~start:cursor ~end_:eofinifnot(String.equalremainder"")thentokens@[Textremainder]elsetokenselsetokensletparse_mlifile_contents=tryOk(parse_mlifile_contents)withexn->Error[`Msg(Printexc.to_stringexn)]