(** Source locations. Every form carries one: error messages, the step debugger and nREPL's find-definition all need them, and retrofitting locations onto a reader is far worse than carrying them from the start. A location is a *span*, not a point. The start is what [file:line:col] prints and what every consumer that wants one place uses; the end is what lets an error underline the thing it is about. A column number cannot draw a squiggle and a span can, which is the whole reason the two extra fields are here. The end is *exclusive* and defaults to the start, so a location nobody widened is a zero-width span at a point and every old call site keeps its old meaning. Only the reader knows where a form ends, so only the reader fills these in; a location the checker invents for a node with no syntax stays a point. *) type t = { file : string; line : int; (* 1-based *) col : int; (* 1-based *) eline : int; (* 1-based, exclusive end *) ecol : int; } let make file line col = { file; line; col; eline = line; ecol = col } let unknown = make "" 0 0 (** [upto start stop] is [start] widened to end where [stop] begins. A [stop] that is not after [start], or is in another file, leaves it alone: a span that runs backwards would draw nonsense. *) let upto (start : t) (stop : t) = if stop.file = start.file && (stop.line > start.line || (stop.line = start.line && stop.col > start.col)) then { start with eline = stop.line; ecol = stop.col } else start (** True when the span covers more than its first line. The underline is drawn on the start line either way — a form that spans twenty lines is pointed at, not boxed — so this is what tells the renderer to run the underline to the end of that line rather than to [ecol]. *) let multiline (t : t) = t.eline > t.line (** How many columns to underline on the start line, or [None] when the span was never widened and there is nothing but a point to draw. *) let width (t : t) = if t.eline = t.line && t.ecol > t.col then Some (t.ecol - t.col) else None let to_string t = Printf.sprintf "%s:%d:%d" t.file t.line t.col (** Raised by every stage of the frontend. *) exception Error of t * string let fail loc fmt = Printf.ksprintf (fun msg -> raise (Error (loc, msg))) fmt