123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354(*---------------------------------------------------------------------------
Copyright (c) 2019 The b0 programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)(* Error message helpers. *)moduleErr_msg=structletpf=Format.fprintfletpp_sp=Format.pp_print_spaceletpp_nop_()=()letpp_anyfmtppf_=pfppffmtletpp_op_enumop?(empty=pp_nop)pp_vppf=function|[]->emptyppf()|[v]->pp_vppfv|_asvs->letrecloopppf=function|[v0;v1]->pfppf"%a@ %s@ %a"pp_vv0oppp_vv1|v::vs->pfppf"%a,@ "pp_vv;loopppfvs|[]->assertfalseinloopppfvsletpp_and_enum?emptypp_vppfvs=pp_op_enum"and"?emptypp_vppfvsletpp_or_enum?emptypp_vppfvs=pp_op_enum"or"?emptypp_vppfvsletpp_did_you_meanpp_vppf=function|[]->()|vs->pfppf"Did@ you@ mean %a ?"(pp_or_enumpp_v)vsletpp_must_bepp_vppf=function|[]->()|vs->pfppf"Must be %a."(pp_or_enumpp_v)vsletpp_unknown~kindpp_vppfv=pfppf"Unknown %a %a."kind()pp_vvletpp_unknown'~kindpp_v~hintppf(v,hints)=matchhintswith|[]->pp_unknown~kindpp_vppfv|hints->pp_unknown~kindpp_vppfv;pp_spppf();(hintpp_v)ppfhintsletmin_byfab=iffa<=fbthenaelsebletmax_byfab=iffa<=fbthenbelsealetedit_distances0s1=letminimumabc=mina(minbc)inlets0=min_byString.lengths0s1(* row *)ands1=max_byString.lengths0s1in(* column *)letm=String.lengths0andn=String.lengths1inletrecrowsrow0rowi=ifi>nthenrow0.(m)elsebeginrow.(0)<-i;forj=1tomdoifs0.[j-1]=s1.[i-1]thenrow.(j)<-row0.(j-1)elserow.(j)<-minimum(row0.(j-1)+1)(row0.(j)+1)(row.(j-1)+1)done;rowsrowrow0(i+1)endinrows(Array.init(m+1)(funx->x))(Array.make(m+1)0)1letsuggest?(dist=2)candidatess=letadd(min,acc)name=letd=edit_distancesnameinifd=minthenmin,(name::acc)elseifd<minthend,[name]elsemin,accinletd,suggs=List.fold_leftadd(max_int,[])candidatesinifd<=dist(* suggest only if not too far *)thenList.revsuggselse[]end(* Text locations *)moduleTloc=structtypefpath=stringletpp_path=Format.pp_print_stringtypepos=inttypeline=inttypeline_pos=line*pos(* For lines we keep the byte position just after the newlinexs. It
editors are still expecting tools to compute visual columns which
is stupid. By keeping these byte positions we can approximate
columns by subtracting the line byte position from the byte
location. This will only be correct on US-ASCII data though. Best
would be to be able to give them [sbyte] and [ebyte]. *)letlv=vtypet={file:fpath;sbyte:pos;ebyte:pos;sline:pos*line;eline:pos*line}letno_file="-"letv~file~sbyte~ebyte~sline~eline={file;sbyte;ebyte;sline;eline}letfilel=l.fileletsbytel=l.sbyteletebytel=l.ebyteletslinel=l.slineletelinel=l.elineletnil=letpnil=-1inletlnil=(-1,pnil)inv~file:no_file~sbyte:pnil~ebyte:pnil~sline:lnil~eline:lnilletmergel0l1=letsbyte,sline=ifl0.sbyte<l1.sbytethenl0.sbyte,l0.slineelsel1.sbyte,l1.slineinletebyte,eline=ifl0.ebyte<l1.ebytethenl1.ebyte,l1.elineelsel0.ebyte,l0.elineinv~file:l0.file~sbyte~ebyte~sline~elineletto_startl=v~file:l.file~sbyte:l.sbyte~ebyte:l.sbyte~sline:l.sline~eline:l.slineletto_endl=v~file:l.file~sbyte:l.ebyte~ebyte:l.ebyte~sline:l.eline~eline:l.elineletrestart~at:se=v~file:e.file~sbyte:s.sbyte~ebyte:e.ebyte~sline:s.sline~eline:e.elineletpf=Format.fprintfletpp_ocamlppfl=matchl.ebyte<0with|true->pfppf"File \"%a\", line n/a, characters n/a"pp_pathl.file|false->letpp_linesppfl=matchfstl.sline=fstl.elinewith|true->pfppf"line %d"(fstl.sline)|false->pfppf"lines %d-%d"(fstl.sline)(fstl.eline)in(* "characters" represent positions (insertion points) not columns *)letpos_s=l.sbyte-sndl.slineinletpos_e=l.ebyte-sndl.eline+1inpfppf"File \"%a\", %a, characters %d-%d"pp_pathl.filepp_lineslpos_spos_eletpp_gnuppfl=matchl.ebyte<0with|true->pfppf"%a:"pp_pathl.file|false->letpp_linesppfl=letcol_s=l.sbyte-sndl.sline+1inletcol_e=l.ebyte-sndl.eline+1inmatchfstl.sline=fstl.elinewith|true->pfppf"%d.%d-%d"(fstl.sline)col_scol_e|false->pfppf"%d.%d-%d.%d"(fstl.sline)col_s(fstl.eline)col_einpfppf"%a:%a"pp_pathl.filepp_lineslletpp_dumpppfl=pfppf"[bytes %d;%d][lines %d;%d][lbytes %d;%d]"l.sbytel.ebyte(fstl.sline)(fstl.eline)(sndl.sline)(sndl.eline)letpp=pp_gnu(* Insertions and substitutions *)letstring_subrange?(first=0)?lasts=letmax=String.lengths-1inletlast=matchlastwith|None->max|Somelwhenl>max->max|Somel->linletfirst=iffirst<0then0elsefirstiniffirst>lastthen""elseString.subsfirst(last-first+1)letstring_replace~start~stop~reps=letlen=String.lengthsinifstop<start||start<0||start>len||stop<0||stop>lentheninvalid_arg(Printf.sprintf"invalid start:%d stop:%d"startstop)elseletb=String.subs0startinleta=String.subsstop(len-stop)inString.concat""[b;rep;a]end(* UTF-8 decoding table. *)moduleUtf_8=structtypecase=|L1|L2|L3_E0|L3_E1_EC_or_EE_EF|L3_ED|L4_F0|L4_F1_F3|L4_F4|Eletcase=(*
(* See https://tools.ietf.org/html/rfc3629#section-4 *)
Printf.printf "[|";
for i = 0 to 255 do
if i mod 16 = 0 then Printf.printf "\n";
if 0x00 <= i && i <= 0x7F then Printf.printf "L1; " else
if 0xC2 <= i && i <= 0xDF then Printf.printf "L2; " else
if 0xE0 = i then Printf.printf "L3_E0; " else
if 0xE1 <= i && i <= 0xEC || 0xEE <= i && i <= 0xEF
then Printf.printf "L3_E1_EC_or_EE_EF; " else
if 0xED = i then Printf.printf "L3_ED;" else
if 0xF0 = i then Printf.printf "L4_F0; " else
if 0xF1 <= i && i <= 0xF3 then Printf.printf "L4_F1_F3; " else
if 0xF4 = i then Printf.printf "L4_F4; " else
Printf.printf "E; "
done;
Printf.printf "\n|]"
*)[|L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L3_E0;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_ED;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L4_F0;L4_F1_F3;L4_F1_F3;L4_F1_F3;L4_F4;E;E;E;E;E;E;E;E;E;E;E;|]end(* UTF-8 text decoder *)moduleTdec=structtype'afmt=Format.formatter->'a->unitletpp_did_you_mean=Err_msg.pp_did_you_meanletpp_and_enum=Err_msg.pp_and_enumletpp_or_enum=Err_msg.pp_or_enumletpp_did_you_mean=Err_msg.pp_did_you_meanletpp_must_be=Err_msg.pp_must_beletpp_unknown=Err_msg.pp_unknownletpp_unknown'=Err_msg.pp_unknown'(* Decoders *)typet={file:Tloc.fpath;i:string;tok:Buffer.t;mutablepos:int;mutableline:int;mutableline_pos:int;}letcreate?(file=Tloc.no_file)i={file;i;tok=Buffer.create255;pos=0;line=1;line_pos=0}(* Location *)letfiled=d.fileletposd=d.posletlined=d.line,d.line_posletlocd~sbyte~ebyte~sline~eline=Tloc.v~file:d.file~sbyte~ebyte~sline~elineletloc_to_hered~sbyte~sline=locd~sbyte~ebyte:d.pos~sline~eline:(d.line,d.line_pos)letloc_hered=loc_to_hered~sbyte:d.pos~sline:(d.line,d.line_pos)(* Errors *)exceptionErrofTloc.t*stringleterrlocmsg=raise_notrace(Err(loc,msg))leterr_to_hered~sbyte~slinefmt=Format.kasprintf(err(loc_to_hered~sbyte~sline))fmtleterr_heredfmt=Format.kasprintf(err(loc_hered))fmtleterr_suggest=Err_msg.suggest(* Lexing *)letincr_lined=matchd.i.[d.pos]with(* assert (not (eoi d)) *)|'\r'->d.line<-d.line+1;d.line_pos<-d.pos+1|'\n'->(ifd.pos=0||d.i.[d.pos-1]<>'\r'thend.line<-d.line+1);d.line_pos<-d.pos+1;|_->()[@@ocaml.inline]leteoid=d.pos>=String.lengthd.i[@@ocaml.inline]letbyted=ifeoidthen0xFFFFelseChar.coded.i.[d.pos][@@ocaml.inline]letaccept_byted=incr_lined;d.pos<-d.pos+1[@@ocaml.inline]letaccept_utf_8acceptd=leterrd=matchbytedwith|0xFFFF->err_hered"UTF-8 decoding error: unexpected end of input"|b->err_hered"UTF-8 decoding error: byte %02x illegal here"binletaccept_taild=if(bytedlsr6=0b10)thenacceptdelseerrdinmatchbytedwith|0xFFFF->errd|b->(* If a subsequent [byte d] invocation is 0xFFFF we get to [err]. *)matchUtf_8.case.(b)with|L1->acceptd|L2->acceptd;accept_taild|L3_E0->acceptd;if(byted-0xA0<0xBF-0xA0)thenacceptdelseerrd;accept_taild|L3_E1_EC_or_EE_EF->acceptd;accept_taild;accept_taild|L3_ED->acceptd;if(byted-0x80<0x9F-0x80)thenacceptdelseerrd;accept_taild|L4_F0->acceptd;if(byted-0x90<0xBF-0x90)thenacceptdelseerrd;accept_taild;accept_taild|L4_F1_F3->acceptd;accept_taild;accept_taild;accept_taild;|L4_F4->acceptd;if(byted-0x80<0x8F-0x80)thenacceptdelseerrd;|E->errdletaccept_uchard=accept_utf_8accept_byted(* Tokenizer *)lettok_resetd=Buffer.resetd.tok[@@ocaml.inline]lettok_popd=lett=Buffer.contentsd.tokintok_resetd;t[@@ocaml.inline]lettok_accept_byted=Buffer.add_chard.tokd.i.[d.pos];accept_byted;[@@ocaml.inline]lettok_accept_uchard=accept_utf_8tok_accept_byted[@@ocaml.inline]lettok_add_bytedb=Buffer.add_chard.tok(Char.chrb)[@@ocaml.inline]lettok_add_bytesds=Buffer.add_stringd.toks[@@ocaml.inline]lettok_add_chardc=Buffer.add_chard.tokc[@@ocaml.inline]letbuffer_add_ucharbu=matchUchar.to_intuwith(* XXX From 4.06 use Buffer.add_utf_8_uchar *)|uwhenu<0->assertfalse|uwhenu<=0x007F->Buffer.add_charb(Char.unsafe_chru)|uwhenu<=0x07FF->Buffer.add_charb(Char.unsafe_chr(0xC0lor(ulsr6)));Buffer.add_charb(Char.unsafe_chr(0x80lor(uland0x3F)));|uwhenu<=0xFFFF->Buffer.add_charb(Char.unsafe_chr(0xE0lor(ulsr12)));Buffer.add_charb(Char.unsafe_chr(0x80lor((ulsr6)land0x3F)));Buffer.add_charb(Char.unsafe_chr(0x80lor(uland0x3F)));|uwhenu<=0x10FFFF->Buffer.add_charb(Char.unsafe_chr(0xF0lor(ulsr18)));Buffer.add_charb(Char.unsafe_chr(0x80lor((ulsr12)land0x3F)));Buffer.add_charb(Char.unsafe_chr(0x80lor((ulsr6)land0x3F)));Buffer.add_charb(Char.unsafe_chr(0x80lor(uland0x3F)))|_->assertfalselettok_add_uchardu=buffer_add_uchard.tokuend