// Source locations, modeled after OCaml's `Lexing.position`.
// All offsets are byte offsets into the UTF-8 encoded source, so that
// error messages report the same columns as the OCaml implementation.
///|
/// A position in a source file.
pub(all) struct Pos {
/// File name, used in error messages.
fname : String
/// Line number, starting from 1.
lnum : Int
/// Byte offset of the beginning of the line.
bol : Int
/// Byte offset of the position.
cnum : Int
} derive(Eq, Debug)
///|
/// A region in a source file.
pub(all) struct Loc {
start : Pos
end_ : Pos
} derive(Eq, Debug)
///|
/// The dummy position, as OCaml's `Lexing.dummy_pos`.
pub let dummy_pos : Pos = { fname: "", lnum: 0, bol: 0, cnum: -1, }
///|
/// Dummy value for predefined constructs that are not associated with a
/// useful source location.
pub let dummy_loc : Loc = { start: dummy_pos, end_: dummy_pos, }
///|
/// Build a location from two positions.
pub fn Loc::new(start : Pos, end_ : Pos) -> Loc {
{ start, end_, }
}
///|
fn compare_pos(a : Pos, b : Pos) -> Int {
let c = compare_strings(a.fname, b.fname)
if c != 0 {
c
} else {
a.cnum.compare(b.cnum)
}
}
///|
/// Compare two locations so as to sort them by file path, then start
/// position, then end position.
pub fn Loc::compare(a : Loc, b : Loc) -> Int {
let c = compare_pos(a.start, b.start)
if c != 0 {
c
} else {
compare_pos(a.end_, b.end_)
}
}
///|
/// Convert a location into a human-readable string such as
/// `File "foo.atd", line 123, characters 40-45`.
pub fn string_of_loc(loc : Loc) -> String {
let pos1 = loc.start
let pos2 = loc.end_
let start1 = pos1.bol
"File \{ocaml_quote(pos1.fname)}, line \{pos1.lnum}, characters \{pos1.cnum - start1}-\{pos2.cnum - start1}"
}