123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114(** Source code position management. This module may be used to map sequences
of characters in a source file to an abstract syntax tree. *)(** Type of a position, corresponding to a continuous range of characters in a
(utf8-encoded) source. *)typepos={fname:stringoption(** File name for the position. *);start_line:int(** Line number of the starting point. *);start_col:int(** Column number (utf8) of the starting point. *);end_line:int(** Line number of the ending point. *);end_col:int(** Column number (utf8) of the ending point. *)}(** Convenient short name for an optional position. *)typepopt=posoption(** [equal p1 p2] tells whether [p1] and [p2] denote the same position. *)letequal:popt->popt->bool=funp1p2->match(p1,p2)with|(Some(p1),Some(p2))->p1=p2|(None,None)->true|(_,_)->false(** Type constructor extending a type (e.g. a piece of abstract syntax) with a
a source code position. *)type'aloc={elt:'a(** The element that is being localised. *);pos:popt(** Position of the element in the source code. *)}(** Localised string type (widely used). *)typestrloc=stringloc(** [make pos elt] associates the position [pos] to [elt]. *)letmake:popt->'a->'aloc=funposelt->{elt;pos}(** [none elt] wraps [elt] in a ['a loc] structure without any specific source
code position. *)letnone:'a->'aloc=funelt->makeNoneelt(** [in_pos pos elt] associates the position [pos] to [elt]. *)letin_pos:pos->'a->'aloc=funpelt->make(Somep)elt(** [end_pos po] creates a position from the end of position [po]. *)letend_pos:popt->popt=funpo->matchpowith|None->None|Somep->Some{pwithstart_line=p.end_line;start_col=p.end_col}(** [cat p1 p2] returns a position starting from [p1] start and ending with
[p2] end. [p1] and [p2] must have the same filename. *)letcat:pos->pos->pos=funp1p2->{fname=ifp1.fname<>p2.fnametheninvalid_arg__LOC__elsep1.fname;start_line=p1.start_line;start_col=p1.start_col;end_line=p2.end_line;end_col=p2.end_col}letcat:popt->popt->popt=funp1p2->matchp1,p2with|Somep1,Somep2->Some(catp1p2)|Somep,None|None,Somep->Somep|None,None->None(** [to_string ?print_fname pos] transforms [pos] into a readable string. If
[print_fname] is [true] (the default), the filename contained in [pos] is
printed. *)letto_string:?print_fname:bool->pos->string=fun?(print_fname=true){fname;start_line;start_col;end_line;end_col}->letfname=ifnotprint_fnamethen""elsematchfnamewith|None->""|Some(n)->n^":"inifstart_line<>end_linethenPrintf.sprintf"%s%d:%d-%d:%d"fnamestart_linestart_colend_lineend_colelseifstart_col=end_colthenPrintf.sprintf"%s%d:%d"fnamestart_linestart_colelsePrintf.sprintf"%s%d:%d-%d"fnamestart_linestart_colend_col(** [pp ppf pos] prints the optional position [pos] on [ppf]. *)letpp:poptLplib.Base.pp=funppfp->matchpwith|None->Format.pp_print_stringppf"unknown location"|Some(p)->Format.pp_print_stringppf(to_stringp)(** [pp_short ppf pos] prints the optional position [pos] on [ppf]. *)letpp_short:poptLplib.Base.pp=funppfp->matchpwith|None->Format.pp_print_stringppf"unknown location"|Some(p)->letprint_fname=falseinFormat.pp_print_stringppf(to_string~print_fnamep)(** [map f loc] applies function [f] on the value of [loc] and keeps the
position unchanged. *)letmap:('a->'b)->'aloc->'bloc=funfloc->{locwithelt=floc.elt}(** [locate ?fname loc] converts the pair of position [loc] and filename
[fname] of the Lexing library into a {!type:pos}. *)letlocate:?fname:string->Lexing.position*Lexing.position->pos=fun?fname(p1,p2)->letfname=ifp1.pos_fname=""thenfnameelseSome(p1.pos_fname)inletstart_line=p1.pos_lnuminletstart_col=p1.pos_cnum-p1.pos_bolinletend_line=p2.pos_lnuminletend_col=p2.pos_cnum-p2.pos_bolin{fname;start_line;start_col;end_line;end_col}(** [make_pos lps elt] creates a located element from the lexing positions
[lps] and the element [elt]. *)letmake_pos:Lexing.position*Lexing.position->'a->'aloc=funlpselt->in_pos(locatelps)elt