1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
open Core
open Match
open Replacement
let debug =
Sys.getenv "DEBUG_COMBY"
|> Option.is_some
let substitute_match_contexts ?sequential (matches: Match.t list) source replacements =
if debug then Format.printf "Matches: %d | Replacements: %d@." (List.length matches) (List.length replacements);
let rewrite_template, environment =
List.fold2_exn
matches replacements
~init:(source, Environment.create ())
~f:(fun (rewrite_template, accumulator_environment)
({ environment = _match_environment; _ } as match_)
{ replacement_content; _ } ->
let hole_id, rewrite_template = Rewrite_template.of_match_context match_ ~source:rewrite_template in
if debug then Format.printf "Hole: %s in %s@." hole_id rewrite_template;
let accumulator_environment = Environment.add accumulator_environment hole_id replacement_content in
rewrite_template, accumulator_environment)
in
if debug then Format.printf "Env:@.%s" (Environment.to_string environment);
if debug then Format.printf "Rewrite in:@.%s@." rewrite_template;
let rewritten_source = Rewrite_template.substitute ?sequential rewrite_template environment |> fst in
let offsets = Rewrite_template.get_offsets_for_holes rewrite_template (Environment.vars environment) in
if debug then
Format.printf "Replacements: %d | Offsets 1: %d@." (List.length replacements) (List.length offsets);
let offsets = Rewrite_template.get_offsets_after_substitution offsets environment in
if debug then
Format.printf "Replacements: %d | Offsets 2: %d@." (List.length replacements) (List.length offsets);
let in_place_substitutions =
List.map2_exn replacements offsets ~f:(fun replacement (_uid, offset) ->
let match_start = { Location.default with offset } in
let offset = offset + String.length replacement.replacement_content in
let match_end = { Location.default with offset } in
let range = Range.{ match_start; match_end } in
{ replacement with range })
in
{ rewritten_source
; in_place_substitutions
}
let substitute_in_rewrite_template ?sequential rewrite_template ({ environment; _ } : Match.t) =
let replacement_content, vars_substituted_for = Rewrite_template.substitute ?sequential rewrite_template environment in
let offsets = Rewrite_template.get_offsets_for_holes rewrite_template (Environment.vars environment) in
let offsets = Rewrite_template.get_offsets_after_substitution offsets environment in
let environment =
List.fold offsets ~init:(Environment.create ()) ~f:(fun acc (var, relative_offset) ->
if List.mem vars_substituted_for var ~equal:String.equal then
let value = Option.value_exn (Environment.lookup environment var) in
let start_location =
Location.{ default with offset = relative_offset }
in
let end_location =
let offset = relative_offset + String.length value in
Location.{ default with offset }
in
let range =
Range.
{ match_start = start_location
; match_end = end_location
}
in
Environment.add ~range acc var value
else
acc)
in
{ replacement_content
; environment
; range =
{ match_start = { Location.default with offset = 0 }
; match_end = Location.default
}
}
let all ?source ?sequential ~rewrite_template matches : result option =
if List.is_empty matches then None else
match source with
| Some source ->
let matches : Match.t list = List.rev matches in
matches
|> List.map ~f:(substitute_in_rewrite_template ?sequential rewrite_template)
|> substitute_match_contexts ?sequential matches source
|> Option.some
| None ->
matches
|> List.map ~f:(substitute_in_rewrite_template ?sequential rewrite_template)
|> List.map ~f:(fun { replacement_content; _ } -> replacement_content)
|> String.concat ~sep:"\n"
|> (fun rewritten_source -> { rewritten_source; in_place_substitutions = [] })
|> Option.some