@@ -47,6 +47,12 @@ module Limit = struct
47
47
48
48
let hash (l : limit ) = l.pos_cnum
49
49
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
50
56
end
51
57
52
58
(* * Weak hashtable where keys are overlay limits (internal) *)
@@ -60,6 +66,7 @@ type manager =
60
66
corresponding right limit. *)
61
67
over_right_gap : limit Links .t ; (* * associates the right limit of a token to
62
68
the left limit of the next *)
69
+ cache : (srcloc * limit ) Links .t ;
63
70
id : string ; (* * manager identifier (for logging/debugging) *)
64
71
}
65
72
@@ -71,11 +78,13 @@ let new_manager: string -> manager =
71
78
{
72
79
right_of = Links. create 42 ;
73
80
over_right_gap = Links. create 42 ;
81
+ cache = Links. create 42 ;
74
82
id = Pretty. to_string " %s-%u" manager_name ! id;
75
83
}
76
84
77
85
(* * 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. *)
79
88
let limits: manager -> srcloc -> limit * limit = fun ctx loc ->
80
89
let s, e = match Cobol_common.Srcloc. as_unique_lexloc loc with
81
90
| Some lexloc -> lexloc
@@ -114,18 +123,35 @@ let join_limits: manager -> limit * limit -> srcloc = fun ctx (s, e) ->
114
123
Cobol_common.Srcloc. raw (pos, pos)
115
124
in
116
125
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
122
138
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
125
150
in
151
+
126
152
if s == e
127
153
then pointwise s
128
- else let loc, e' = Links. find ctx.right_of s in check loc e'
154
+ else try_cache_from s
129
155
in
130
156
let join_failure (s , e ) =
131
157
let loc = Cobol_common.Srcloc. raw (s, e) in
@@ -135,18 +161,19 @@ let join_limits: manager -> limit * limit -> srcloc = fun ctx (s, e) ->
135
161
(* Printexc.(print_raw_backtrace Stdlib.stderr @@ get_callstack 10); *)
136
162
loc
137
163
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 *)
140
166
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 *)
142
168
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 *)
146
172
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)
150
177
151
178
module New_manager (Id : sig val name: string end ) : MANAGER = struct
152
179
let ctx = new_manager Id. name
0 commit comments