2019-12-06 14:49:19 +04:00
|
|
|
(** Regions of a file
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
A {e region} is a contiguous series of bytes, for example, in a
|
|
|
|
text file. It is here denoted by the object type [t].
|
|
|
|
*)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** {1 Definition} *)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** The start (included) of the region is given by the field [start],
|
|
|
|
which is a {e position}, and the end (excluded) is the position
|
|
|
|
given by the field [stop]. The convention of including the start
|
|
|
|
and excluding the end enables to have empty regions if, and only
|
|
|
|
if, [start = stop], which a fast and easy check. See module {!
|
|
|
|
Pos} for the definition of positions.
|
|
|
|
|
|
|
|
The first byte of a file starts at the offset zero (that is,
|
|
|
|
column one), and [start] is always lower than or equal to [stop],
|
|
|
|
and they must refer to the same file.
|
|
|
|
{ul
|
|
|
|
|
|
|
|
{li The call [region#shift_bytes n] evaluates in a region that
|
|
|
|
is the translation of region [region] of [n] bytes forward
|
|
|
|
in the file.}
|
|
|
|
|
|
|
|
{li The call [region#shift_one_uchar n] is similar to
|
|
|
|
[region#shift_bytes n], except that it assumes that [n] is
|
|
|
|
the number of bytes making up one unicode point.}
|
|
|
|
|
|
|
|
{li The call [region#set_file f] sets the file name to be [f].}
|
|
|
|
|
|
|
|
{li The method [file] returns the file name.}
|
|
|
|
|
|
|
|
{li The method [pos] returns the values of the fields [start]
|
|
|
|
and [stop].}
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
{li The method [byte_pos] returns the start and end positions of
|
|
|
|
the region at hand {e interpreting them as lexing
|
|
|
|
positions}, that is, the unit is the byte.}
|
|
|
|
|
|
|
|
{li The call [region#to_string ~file ~offsets mode] evaluates in
|
|
|
|
a string denoting the region [region], in the manner of the
|
|
|
|
OCaml compilers.}
|
|
|
|
|
|
|
|
{li The name of the file is present if, and only if, [file =
|
|
|
|
true] or [file] is missing.}
|
|
|
|
|
|
|
|
{li The positions in the file are expressed as horizontal
|
|
|
|
offsets if [offsets = true] or [offsets] is missing (the
|
|
|
|
default), otherwise as columns.}
|
|
|
|
|
|
|
|
{li If [mode = `Byte], those positions will be assumed to have
|
|
|
|
bytes as their unit, otherwise, if [mode = `Point], they
|
|
|
|
will be assumed to refer to code points.}
|
|
|
|
|
|
|
|
{li The method [compact] has the same signature as and calling
|
|
|
|
convention as [to_string], except that the resulting string
|
2020-01-20 13:57:07 +04:00
|
|
|
is shorter (usually for debugging or tracing).}}
|
2019-12-06 14:49:19 +04:00
|
|
|
*)
|
2020-01-21 21:35:36 +04:00
|
|
|
type t = <
|
2019-05-13 00:46:25 +04:00
|
|
|
start : Pos.t;
|
|
|
|
stop : Pos.t;
|
|
|
|
|
|
|
|
(* Setters *)
|
|
|
|
|
|
|
|
shift_bytes : int -> t;
|
|
|
|
shift_one_uchar : int -> t;
|
|
|
|
set_file : string -> t;
|
|
|
|
|
|
|
|
(* Getters *)
|
|
|
|
|
|
|
|
file : string;
|
|
|
|
pos : Pos.t * Pos.t;
|
|
|
|
byte_pos : Lexing.position * Lexing.position;
|
|
|
|
|
|
|
|
(* Predicates *)
|
|
|
|
|
|
|
|
is_ghost : bool;
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(* Conversions to type [string] *)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
|
|
|
to_string : ?file:bool -> ?offsets:bool -> [`Byte | `Point] -> string;
|
|
|
|
compact : ?file:bool -> ?offsets:bool -> [`Byte | `Point] -> string
|
|
|
|
>
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** The type [region] is a synonym of [t] to use after [open Region].
|
|
|
|
*)
|
2019-05-13 00:46:25 +04:00
|
|
|
type region = t
|
|
|
|
|
2020-01-20 13:57:07 +04:00
|
|
|
(** The type ['a reg] enables the concept of some value of type ['a] to
|
2019-12-06 14:49:19 +04:00
|
|
|
be related to a region in a source file.
|
2020-01-20 13:57:07 +04:00
|
|
|
*)
|
2019-05-13 00:46:25 +04:00
|
|
|
type 'a reg = {region: t; value: 'a}
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(* {1 Constructors} *)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
|
|
|
exception Invalid
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** The function [make] creates a region from two positions. If the
|
|
|
|
positions are not properly ordered or refer to different files,
|
|
|
|
the exception [Invalid] is raised.
|
|
|
|
@raise [Invalid]
|
|
|
|
*)
|
2019-05-13 00:46:25 +04:00
|
|
|
val make : start:Pos.t -> stop:Pos.t -> t
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** {1 Special regions} *)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** To deal with ghost expressions, that is, pieces of abstract syntax
|
|
|
|
that have not been built from excerpts of concrete syntax, we need
|
|
|
|
{e ghost regions}. The module {! Pos} provides a [ghost] position,
|
|
|
|
and we also provide a [ghost] region and, in type [t], the method
|
|
|
|
[is_ghost] to check it. It is implemented as two [Pos.ghost]
|
|
|
|
positions. *)
|
|
|
|
val ghost : t
|
2019-11-18 19:10:48 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** The call to [wrap_ghost] wraps a value within a ghost region.
|
|
|
|
*)
|
2019-11-18 19:10:48 +04:00
|
|
|
val wrap_ghost : 'a -> 'a reg
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** Occasionnally, we may need a minimum region. It is here made of
|
|
|
|
two minimal positions.
|
|
|
|
*)
|
2020-01-21 21:35:36 +04:00
|
|
|
val min : file:string -> t
|
2019-11-18 19:10:48 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** {1 Comparisons} *)
|
2019-05-13 00:46:25 +04:00
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** Two regions are equal if, and only if, they refer to the same file
|
|
|
|
and their start positions are equal and their stop positions are
|
|
|
|
equal. See {! Pos.equal}. Note that [r1] and [r2] can be
|
|
|
|
ghosts. *)
|
2019-05-13 00:46:25 +04:00
|
|
|
val equal : t -> t -> bool
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** The call [lt r1 r2] ("lower than") has the value [true] if, and
|
|
|
|
only if, regions [r1] and [r2] refer to the same file, none is a
|
|
|
|
ghost and the start position of [r1] is lower than that of
|
|
|
|
[r2]. (See {! Pos.lt}.) *)
|
2019-05-13 00:46:25 +04:00
|
|
|
val lt : t -> t -> bool
|
|
|
|
|
2020-05-22 05:22:35 +04:00
|
|
|
(** The call [compare r1 r2] has the value 0 if [equal r1 r2] returns
|
|
|
|
[true]. Otherwise it returns -1 if [lt r1 r2] returns [true], and 1
|
|
|
|
if [lt r1 r2] returns [false]. *)
|
|
|
|
val compare : t -> t -> int
|
|
|
|
|
2019-12-06 14:49:19 +04:00
|
|
|
(** Given two regions [r1] and [r2], we may want the region [cover r1
|
|
|
|
r2] that covers [r1] and [r2]. We have the property [equal (cover
|
|
|
|
r1 r2) (cover r2 r1)]. (In a sense, it is the maximum region, but
|
|
|
|
we avoid that name because of the [min] function above.) If [r1]
|
|
|
|
is a ghost, the cover is [r2], and if [r2] is a ghost, the cover
|
|
|
|
is [r1].
|
|
|
|
*)
|
2019-05-13 00:46:25 +04:00
|
|
|
val cover : t -> t -> t
|