Skip to content

Commit d0bf4a8

Browse files
committed
Cache composed locations in source overlay module
1 parent 1d945c0 commit d0bf4a8

File tree

2 files changed

+61
-20
lines changed

2 files changed

+61
-20
lines changed

src/lsp/cobol_common/srcloc.ml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ let pp_file_loc ppf loc =
374374
let raw ?(in_area_a = false) ((s, e): lexloc) : srcloc =
375375
assert Lexing.(s.pos_cnum <= e.pos_cnum); (* ensure proper use *)
376376
let loc = Raw (s, e, in_area_a) in
377-
if Lexing.(s.pos_fname != e.pos_fname) then
377+
if Lexing.(s.pos_fname <> e.pos_fname) then
378378
Pretty.error
379379
"%a@\n>> Internal warning in `%s.raw`: file names mismatch (`%s` != `%s`)\
380380
" pp_srcloc loc __MODULE__ s.pos_fname e.pos_fname;
@@ -402,13 +402,27 @@ let replacement ~old ~new_ ~in_area_a ~replloc : srcloc =
402402
(* end *)
403403
(* | Cpy {copyloc = {filename; _}; _} -> Some filename *)
404404

405+
(** [may_join_as_single_raw a b] checks whether a lexloc {i l{_ a}} with a a
406+
left-hand lexing position [a] and a lexloc {i l{_ b}} with a right-hand
407+
position [b], may be joined to form a single raw source location
408+
(internal). *)
409+
let may_join_as_single_raw (a: Lexing.position) (b: Lexing.position) =
410+
a.pos_fname = b.pos_fname &&
411+
a.pos_lnum == b.pos_lnum && (* ensure we are stay on a single line *)
412+
a.pos_cnum >= b.pos_cnum - 1
413+
405414
(** [concat l1 l2] concatenates two adjacent source locations [l1] and [l2]. *)
406415
let rec concat: srcloc -> srcloc -> srcloc = fun l1 l2 -> match l1, l2 with
407416
| Raw (s1, e1, in_area_a),
408417
Raw (s2, e2, _)
409-
when e1.pos_fname = s2.pos_fname && e1.pos_cnum = s2.pos_cnum - 1 ->
418+
when may_join_as_single_raw e1 s2 ->
410419
Raw (s1, e2, in_area_a)
411420

421+
| Cat { left; right = Raw (s1, e1, in_area_a) },
422+
Raw (s2, e2, _)
423+
when may_join_as_single_raw e1 s2 ->
424+
Cat { left; right = Raw (s1, e2, in_area_a) }
425+
412426
| Cpy { copied = l1; copyloc = c1 },
413427
Cpy { copied = l2; copyloc = c2 }
414428
when same_copyloc c1 c2 ->

src/lsp/cobol_preproc/src_overlay.ml

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ module Limit = struct
4747

4848
let hash (l: limit) = l.pos_cnum
4949

50+
(* l1 = l2, or l1 was emitted before l2 *)
51+
let surely_predates (l1: limit) (l2: limit) = (* or equals *)
52+
if l1.pos_cnum < (-1)
53+
then l2.pos_cnum <= l1.pos_cnum
54+
else l1.pos_cnum > 0 && l1.pos_cnum <= l2.pos_cnum &&
55+
l1.pos_fname = l2.pos_fname
5056
end
5157

5258
(** Weak hashtable where keys are overlay limits (internal) *)
@@ -60,6 +66,7 @@ type manager =
6066
corresponding right limit. *)
6167
over_right_gap: limit Links.t; (** associates the right limit of a token to
6268
the left limit of the next *)
69+
cache: (srcloc * limit) Links.t;
6370
id: string; (** manager identifier (for logging/debugging) *)
6471
}
6572

@@ -71,11 +78,13 @@ let new_manager: string -> manager =
7178
{
7279
right_of = Links.create 42;
7380
over_right_gap = Links.create 42;
81+
cache = Links.create 42;
7482
id = Pretty.to_string "%s-%u" manager_name !id;
7583
}
7684

7785
(** Returns left and right (potentially fresh) limits for the given source
78-
location *)
86+
location; for any given file, must be called with the leftmost location
87+
first. *)
7988
let limits: manager -> srcloc -> limit * limit = fun ctx loc ->
8089
let s, e = match Cobol_common.Srcloc.as_unique_lexloc loc with
8190
| Some lexloc -> lexloc
@@ -114,18 +123,35 @@ let join_limits: manager -> limit * limit -> srcloc = fun ctx (s, e) ->
114123
Cobol_common.Srcloc.raw (pos, pos)
115124
in
116125
let try_limits (s, e) =
117-
let rec jump_right loc e' =
118-
let s' = Links.find ctx.over_right_gap e' in
119-
let loc', e' = Links.find ctx.right_of s' in
120-
check (Cobol_common.Srcloc.concat loc loc') e'
121-
and check loc e' =
126+
127+
let rec proceed_from ?loc s = (* start search from left limit [s] *)
128+
check ?loc @@ Links.find ctx.right_of s
129+
130+
and check ?loc (loc', e') =
131+
(* continue search with ([loc] concatenated with) [loc'] if [e'] is not
132+
the sought after right limit; raises {!Not_found} when reaching an
133+
unknown gap or limit *)
134+
let loc = match loc with
135+
| None -> loc'
136+
| Some loc -> Cobol_common.Srcloc.concat loc loc'
137+
in
122138
if e == e' (* physical comparison *)
123-
then loc
124-
else jump_right loc e'
139+
then (Links.replace ctx.cache s (loc, e); loc)
140+
else try_cache_from ~loc @@ Links.find ctx.over_right_gap e'
141+
142+
and try_cache_from ?loc s =
143+
(* attempt with cache first; proceed via small-step upon miss or
144+
failure *)
145+
match Links.find_opt ctx.cache s with
146+
| Some ((_, e') as hit) when Limit.surely_predates e' e ->
147+
(try check ?loc hit with Not_found -> proceed_from ?loc s)
148+
| Some _ | None ->
149+
proceed_from ?loc s
125150
in
151+
126152
if s == e
127153
then pointwise s
128-
else let loc, e' = Links.find ctx.right_of s in check loc e'
154+
else try_cache_from s
129155
in
130156
let join_failure (s, e) =
131157
let loc = Cobol_common.Srcloc.raw (s, e) in
@@ -135,18 +161,19 @@ let join_limits: manager -> limit * limit -> srcloc = fun ctx (s, e) ->
135161
(* Printexc.(print_raw_backtrace Stdlib.stderr @@ get_callstack 10); *)
136162
loc
137163
in
138-
(* first attempt assumes proper token limits: `s` is a left and `e` is a right
139-
of tokens *)
164+
(* first attempt assumes proper token limits: `s` is a left and `e` is a
165+
right of tokens *)
140166
try try_limits (s, e) with Not_found ->
141-
(* try assuming `s` is an end of token *)
167+
(* otherwise try assuming `s` is an end of token *)
142168
try try_limits (Links.find ctx.over_right_gap s, e) with Not_found ->
143-
if s.pos_cnum = 0 (* potential special case with left-position forged by the
144-
parser: retry with leftmost limit if it differs from
145-
s *)
169+
try if s.pos_cnum = 0 (* potential special case with left-position forged by
170+
the parser: retry with leftmost limit if it differs
171+
from s *)
146172
then match leftmost_limit_in ~filename:s.pos_fname ctx with
147-
| Some l when l != s -> try_limits (l, e) (* physical equality is enough *)
148-
| Some _ | None -> join_failure (s, e)
149-
else join_failure (s, e)
173+
| Some l when l != s -> try_limits (l, e) (* physical equality is enough *)
174+
| Some _ | None -> raise Not_found
175+
else raise Not_found
176+
with Not_found -> join_failure (s, e)
150177

151178
module New_manager (Id: sig val name: string end) : MANAGER = struct
152179
let ctx = new_manager Id.name

0 commit comments

Comments
 (0)