package comby-kernel
A match engine for structural code search and replace that supports ~every language
Install
Dune Dependency
Authors
Maintainers
Sources
comby-kernel.1.7.0.tar.gz
md5=ee6556d8bd9b25ed0445ebe23862e48a
sha512=e6386c8ce5ef14bbcab2b0ead5b1edc39375438f56330d5f02e81e467afe6623a7e299f97f26008d77bbc62850c6dc63a7cbe5b81671b5183ff3adeee5946bb3
doc/src/comby-kernel.matchers/rewrite.ml.html
Source file rewrite.ml
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
open Vangstrom open Core_kernel open Match open Replacement let debug = match Sys.getenv "DEBUG_COMBY" with | exception Not_found -> false | _ -> true let counter = let uuid_for_id_counter = ref 0 in fun () -> uuid_for_id_counter := !uuid_for_id_counter + 1; Format.sprintf "%d" !uuid_for_id_counter let replacement_sentinel metasyntax = let open Types.Metasyntax in List.find_map metasyntax.syntax ~f:(function | Hole (Everything, Delimited (left, right)) -> let left = Option.value left ~default:"" in let right = Option.value right ~default:"" in Some (left, right) | Hole (Alphanum, Delimited (left, right)) -> let left = Option.value left ~default:"" in let right = Option.value right ~default:"" in Some (left, right) | Regex (left, _, right) -> Some (left, right) | _ -> None) |> function | Some v -> v | None -> failwith "A custom metasyntax must define syntax for an Everything or Alphanum hole or Regex to customize rewriting" (** Parse the first :[id(label)] label encountered in the template. *) let parse_first_label ?(metasyntax = Metasyntax.default_metasyntax) template = let label = take_while (function | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '_' -> true | _ -> false) in let left, right = replacement_sentinel metasyntax in let parser = many @@ choice [ lift3 (fun _ label _ -> Some label) (string (left^"id(")) label (string (")"^right)) ; any_char >>| fun _ -> None ] in parse_string ~consume:All parser template |> function | Ok label -> List.find_map label ~f:ident | Error _ -> None let substitute_fresh ?(metasyntax = Metasyntax.default_metasyntax) ?(fresh = counter) template = let label_table = String.Table.create () in let template_ref = ref template in let current_label_ref = ref (parse_first_label ~metasyntax !template_ref) in while Option.is_some !current_label_ref do let label = Option.value_exn !current_label_ref in let id = match String.Table.find label_table label with | Some id -> id | None -> let id = fresh () in if String.(label <> "") then String.Table.add_exn label_table ~key:label ~data:id; id in let left, right = replacement_sentinel metasyntax in let pattern = left ^ "id(" ^ label ^ ")" ^ right in template_ref := String.substr_replace_first !template_ref ~pattern ~with_:id; current_label_ref := parse_first_label ~metasyntax !template_ref; done; !template_ref let substitute_in_rewrite_template ?fresh ?(external_handler = External.default_external) ?(metasyntax = Metasyntax.default_metasyntax) ?filepath template environment = let (module M) = Metasyntax.create metasyntax in let module External = struct let handler = external_handler end in let module Template_parser = Template.Make(M)(External) in (* FIXME factor out Template_parser *) let template = substitute_fresh ~metasyntax ?fresh template in let terms = Template_parser.parse template in let replacement_content, environment = Template_parser.substitute ?filepath terms environment in { replacement_content ; environment ; range = { match_start = { Location.default with offset = 0 } ; match_end = Location.default } } let substitute ?(metasyntax = Metasyntax.default_metasyntax) ?external_handler ?fresh ?filepath template env = let { replacement_content; _ } = substitute_in_rewrite_template ~metasyntax ?external_handler ?fresh ?filepath template env in replacement_content let substitute_matches (matches: Match.t list) source replacements = if debug then Format.printf "Matches: %d | Replacements: %d@." (List.length matches) (List.length replacements); let rewritten_source, in_place_substitutions, _ = (* shift adjusts the difference of the matched part and the replacement part to the matched offsets *) List.fold2_exn matches replacements ~init:(source, [], 0) ~f:(fun (rolling_result, replacements, shift) { range; _ } ({ replacement_content; _ } as r) -> let start_index = range.match_start.offset + shift in let end_index = range.match_end.offset + shift in let before = if start_index = 0 then "" else String.slice rolling_result 0 start_index in let after = String.slice rolling_result end_index (String.length rolling_result) in let match_length = end_index - start_index in let difference = String.length replacement_content - match_length in let range = Range.{ match_start = Location.{ default with offset = start_index }; match_end = Location.{ default with offset = end_index + difference } } in let replacements = { r with range }::replacements in String.concat [before; replacement_content; after], replacements, shift + difference) in { rewritten_source ; in_place_substitutions } (* FIXME: all the functors help nothing if we end up calling this without parameterizing by metasyntax, etc. *) let all ?source ?metasyntax ?external_handler ?fresh ?filepath ~rewrite_template rev_matches : result option = Option.some_if (not (List.is_empty rev_matches)) @@ match source with (* in-place substitution *) | Some source -> rev_matches |> List.map ~f:(fun Match.{ environment; _ } -> substitute_in_rewrite_template ?filepath ?metasyntax ?external_handler ?fresh rewrite_template environment) |> substitute_matches rev_matches source (* no in place substitution, emit result separated by newlines *) | None -> let buf = Buffer.create 20 in List.iter rev_matches ~f:(fun m -> substitute_in_rewrite_template ?filepath ?metasyntax ?external_handler ?fresh rewrite_template m.environment |> fun { replacement_content; _ } -> Buffer.add_string buf replacement_content; Buffer.add_char buf '\n'); { rewritten_source = Buffer.contents buf; in_place_substitutions = [] }
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>