omega matcher progress

This commit is contained in:
Rijnard van Tonder 2020-01-11 15:09:07 -07:00
parent 9c3bfa0468
commit 823b6005da
45 changed files with 1297 additions and 3646 deletions

4
dune
View File

@ -1,6 +1,6 @@
(env
(dev
(flags (:standard -w A-3-4-32-34-39-40-41-42-44-45-48-49-50-57)))
(flags (:standard -w A-3-4-32-34-39-40-41-42-44-45-48-49-50-57-60)))
(release
(flags (:standard -w A-3-4-32-34-39-40-41-42-44-45-48-49-50-57))
(flags (:standard -w A-3-4-32-34-39-40-41-42-44-45-48-49-50-57-60))
(ocamlopt_flags (-O3))))

View File

@ -1,3 +1,6 @@
(copy_files# alpha/*.ml{,i})
; (copy_files# omega/*.ml{,i})
(library
(name language)
(public_name comby.language)

View File

@ -0,0 +1,47 @@
open Core
open Angstrom
open Ast
let (|>>) p f =
p >>= fun x -> return (f x)
let alphanum =
satisfy (function
| 'a' .. 'z'
| 'A' .. 'Z'
| '0' .. '9' -> true
| _ -> false)
let variable_parser =
(string Syntax.variable_left_delimiter
*> (many (alphanum <|> char '_') |>> String.of_char_list)
<* string Syntax.variable_right_delimiter)
let escaped_char_s =
any_char
let char_token_s =
(char '\\' *> escaped_char_s >>= fun c -> return (Format.sprintf {|\%c|} c))
<|> (any_char |>> String.of_char)
let value_parser =
(string {|"|}
*> (many_till char_token_s (string {|"|})))
|>> String.concat
let operator_parser =
choice
[ string Syntax.equal
; string Syntax.not_equal
]
let atom_parser =
choice
[ (variable_parser >>= fun variable -> return (Variable variable))
; (value_parser >>= fun value -> return (String value))
]
let rewrite_template_parser =
value_parser >>= fun value -> return (RewriteTemplate value)

243
lib/language/omega/rule.ml Normal file
View File

@ -0,0 +1,243 @@
open Core
open Angstrom
open Match
open Rewriter
open Ast
open Parser
module Configuration = Matchers.Configuration
type t = Ast.t
type result = bool * environment option
let (|>>) p f =
p >>= fun x -> return (f x)
let sat = fst
let result_env = snd
let match_configuration_of_syntax template =
(* decide match configuration based on whether there are holes *)
let antecedent_contains_hole_syntax case =
String.is_substring case ~substring:Syntax.variable_left_delimiter
in
if antecedent_contains_hole_syntax template then
Configuration.create ~match_kind:Fuzzy ()
else
Configuration.create ~match_kind:Exact ()
let merge_match_environments matches environment' =
List.map matches ~f:(fun { environment; _ } ->
Environment.merge environment environment')
type rewrite_context =
{ variable : string }
let rec apply
?(matcher = (module Matchers.Generic : Matchers.Matcher))
?(substitute_in_place = true)
predicates
env =
let open Option in
let module Matcher = (val matcher : Matchers.Matcher) in
let equal_in_environment var value env =
match Environment.lookup env var with
| None -> false, Some env
| Some var_value -> String.equal var_value value, Some env
in
(* accepts only one expression *)
let rec rule_match ?(rewrite_context : rewrite_context option) env =
function
| True -> true, Some env
| False -> false, Some env
| Equal (Variable var, String value)
| Equal (String value, Variable var) ->
equal_in_environment var value env
| Equal (String left, String right) ->
String.equal left right, Some env
| Equal (Variable left, Variable right) ->
let result =
Environment.lookup env left >>= fun left ->
Environment.lookup env right >>= fun right ->
return (String.equal left right)
in
Option.value result ~default:false, Some env
| Not_equal (left, right) ->
let sat, env = rule_match env (Equal (left, right)) in
not sat, env
| Match (Variable variable, cases) ->
let result =
Environment.lookup env variable >>= fun source ->
List.find_map cases ~f:(fun (template, case_expression) ->
match template with
| String template ->
begin
let configuration = match_configuration_of_syntax template in
Matcher.all ~configuration ~template ~source |> function
| [] -> None
| matches ->
(* merge environments. overwrite behavior is undefined *)
let fold_matches (sat, out) { environment; _ } =
let fold_cases (sat, out) predicate =
if sat then
let env' = Environment.merge env environment in
rule_match ?rewrite_context env' predicate
else
(sat, out)
in
List.fold case_expression ~init:(sat, out) ~f:fold_cases
in
List.fold matches ~init:(true, None) ~f:fold_matches
|> Option.some
end
| Variable _ ->
failwith "| :[hole] is invalid. Maybe you meant to put quotes")
in
Option.value_map result ~f:ident ~default:(false, Some env)
| Match (String template, cases) ->
let source, _ = Rewriter.Rewrite_template.substitute template env in
let fresh_var = Uuid_unix.(Fn.compose Uuid.to_string create ()) in
let env = Environment.add env fresh_var source in
rule_match env (Match (Variable fresh_var, cases))
| RewriteTemplate rewrite_template ->
begin
match rewrite_context with
| None -> false, None
| Some { variable; _ } ->
(* FIXME(RVT) assumes only contextual rewrite for now. *)
let env =
Rewrite_template.substitute rewrite_template env
|> fst
|> fun replacement' ->
Environment.update env variable replacement'
|> Option.some
in
true, env
end
| Rewrite (Variable variable, (match_template, rewrite_expression)) ->
begin match rewrite_expression with
| RewriteTemplate rewrite_template ->
let template =
match match_template with
| Variable _ -> failwith "Invalid syntax in rewrite LHS"
| String template -> template
in
let result =
Environment.lookup env variable >>= fun source ->
let configuration = Configuration.create ~match_kind:Fuzzy () in
let matches = Matcher.all ~configuration ~template ~source in
let source = if substitute_in_place then Some source else None in
let result = Rewrite.all ?source ~rewrite_template matches in
match result with
| Some { rewritten_source; _ } ->
(* substitute for variables that are in the outside scope *)
let rewritten_source, _ = Rewrite_template.substitute rewritten_source env in
let env = Environment.update env variable rewritten_source in
return (true, Some env)
| None ->
return (true, Some env)
in
Option.value_map result ~f:ident ~default:(false, Some env)
| _ -> failwith "Not implemented yet"
end
| Rewrite _ -> failwith "TODO/Invalid: Have not decided whether rewrite \":[x]\" is useful."
in
List.fold predicates ~init:(true, None) ~f:(fun (sat, out) predicate ->
if sat then
let env =
Option.value_map out
~f:(fun out -> Environment.merge out env)
~default:env
in
rule_match env predicate
else
(sat, out))
let make_equality_expression operator left right =
if String.equal operator Syntax.equal then
return (Equal (left, right))
else if
String.equal operator Syntax.not_equal then
return (Not_equal (left, right))
else
let message =
Format.sprintf
"Unhandled operator %s. Did you mean %s or %s?"
operator
Syntax.equal
Syntax.not_equal in
fail message
let is_whitespace = function
| ' ' | '\t' | '\r' | '\n' -> true
| _ -> false
let spaces =
take_while is_whitespace >>= fun s ->
return s
let spaces1 =
satisfy is_whitespace >>= fun c ->
take_while is_whitespace >>= fun s ->
return (Format.sprintf "%c%s" c s)
let create rule =
let operator_parser =
spaces *> atom_parser >>= fun left ->
spaces *> operator_parser >>= fun operator ->
spaces *> atom_parser >>= fun right ->
make_equality_expression operator left right <* spaces
in
let true' = spaces *> string Syntax.true' <* spaces |>> fun _ -> True in
let false' = spaces *> string Syntax.false' <* spaces |>> fun _ -> False in
let expression_parser =
fix (fun expression_parser ->
let match_pattern_parser =
let case_parser =
spaces *> string Syntax.pipe_operator *>
spaces *> atom_parser <* spaces <* string Syntax.arrow <* spaces >>= fun antecedent ->
spaces *> sep_by (char ',') expression_parser <* spaces |>> fun consequent ->
antecedent, consequent
in
let pattern keyword =
string keyword *> spaces *> atom_parser <* spaces <* char '{' <* spaces
>>= fun atom ->
many1 case_parser
<* char '}' <* spaces
>>= fun cases -> return (atom, cases)
in
pattern Syntax.start_match_pattern |>> fun (atom, cases) ->
Match (atom, cases)
in
let rewrite_pattern_parser =
string Syntax.start_rewrite_pattern *> spaces *> atom_parser <* spaces <* char '{' <* spaces
>>= fun atom ->
atom_parser <* spaces <* string Syntax.arrow <* spaces >>= fun match_template ->
spaces *> rewrite_template_parser <* spaces <* char '}' <* spaces
|>> fun rewrite_template ->
Rewrite (atom, (match_template, rewrite_template))
in
choice
[ match_pattern_parser
; rewrite_pattern_parser
; operator_parser
; true'
; false'
])
in
let rule_parser =
spaces
*> string Syntax.rule_prefix
*> spaces1
*> sep_by1 (spaces *> char ',' <* spaces) expression_parser
<* end_of_input
in
match parse_string rule_parser rule with
| Ok rule -> Or_error.return rule
| Error error -> Or_error.error_string error

View File

@ -66,7 +66,7 @@ type t =
}
[@@deriving yojson]
val create : unit -> t
val create : ?range:range -> unit -> t
val pp : Format.formatter -> string option * t list -> unit

View File

@ -7,8 +7,8 @@ type t =
}
[@@deriving yojson]
let create () =
{ range = Range.default
let create ?(range = Range.default) () =
{ range
; environment = Environment.create ()
; matched = ""
}

View File

@ -922,4 +922,6 @@ module Make (Syntax : Syntax.S) (Info : Info.S) = struct
let matches = compute_nested_matches matches in
return matches
end
let set_rewrite_template _ = () (* Unused in alpha matcher *)
end

View File

@ -3,3 +3,6 @@ module Syntax = Types.Syntax
module type Matcher = Types.Matcher.S
include Languages
(** Omega only *)
module Template = Template

View File

@ -3,3 +3,6 @@ module Syntax = Types.Syntax
module type Matcher = Types.Matcher.S
include module type of Languages
(** Omega only *)
module Template = Template

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
open Core
open Angstrom
let (|>>) p f =
p >>= fun x -> return (f x)
let between left right p =
left *> p <* right
let zero =
fail ""
let cons x xs = x :: xs
let debug = true
let dont_use_any_char_except_parser p =
if debug then Format.printf "Entered@.";
let stop = ref false in
let set_stop v = stop := v in
let get_stop () = !stop in
let c =
choice
[ (p >>= fun reserved -> pos >>= fun po -> (if debug then Format.printf "1. stop @@ %s @@ %d@." reserved po; return (set_stop true)) >>= fun _ -> fail "stop")
; (return () >>= fun _ -> Format.printf "X@."; if get_stop () then (if debug then Format.printf "2. stop@."; fail "stop") else any_char)
]
in
c >>= fun c' -> if debug then Format.printf "Parsed: %c@." c'; if debug then Format.printf "Exit@."; return c'
let dont_use_is_not p =
dont_use_any_char_except_parser p
let many_till_stop p t =
let stop = ref false in
let set_stop v = stop := v in
let get_stop () = !stop in
fix (fun m ->
choice
[ (t >>= fun _ -> (return (set_stop true)) >>= fun _ -> fail "stop")
; (return () >>= fun _ -> if get_stop () then return [] else lift2 cons p m)
])
let many1_till_stop p t =
let stop = ref false in
let set_stop v = stop := v in
let get_stop () = !stop in
(* one needs to fail if p isn't successful so that it doesn't consume and advance one char *)
let one =
choice
[ (t >>= fun _ -> (return (set_stop true)) >>= fun _ -> fail "stop")
; (return () >>= fun _ -> if get_stop () then fail "stop" else p)
]
in
lift2 cons one (many_till_stop p t)
(* use many1_till_stop instead of "many1 (any_allowed_except_parser allowed until" *)
(*
let any_allowed_except_parser allowed p =
let rewind = ref false in
let set_rewind v = rewind := v in
let get_rewind () = !rewind in
choice
[ (p >>= fun _ -> (return (set_rewind true)) >>= fun _ -> fail "bad")
; (return () >>= fun _ -> if get_rewind () then fail "rewind" else allowed)
(* TODO this needs some kind of EOF condition to work for both template and match parsing *)
]
*)
let alphanum =
satisfy (function
| 'a' .. 'z'
| 'A' .. 'Z'
| '0' .. '9' -> true
| _ -> false)
let is_whitespace = function
| ' ' | '\t' | '\r' | '\n' -> true
| _ -> false
let blank =
choice
[ char ' '
; char '\t'
]
let many_till p t =
fix (fun m -> (t *> return []) <|> (lift2 cons p m))
let many1_till p t =
lift2 cons p (many_till p t)
let skip_unit p =
p |>> ignore
module Deprecate = struct
(* XXX can shortcircuit *)
(* what if you hit a reserved
seqence "{" and then attempt
":[[" and then say "end of
input" and then move ahead any_char. not good.
going from longest to shortest works though *)
let any_char_except ~reserved =
List.fold reserved
~init:(return `OK)
~f:(fun acc reserved_sequence ->
option `End_of_input
(peek_string (String.length reserved_sequence)
>>= fun s ->
if s = reserved_sequence then
return `Reserved_sequence
else
acc))
>>= function
| `OK -> any_char
| `End_of_input -> any_char
| `Reserved_sequence -> fail "reserved sequence hit"
end
(** must have at least one, otherwise spins on
the empty string *)
let spaces1 =
satisfy is_whitespace >>= fun c ->
(* XXX use skip_while once everything works.
we don't need the string *)
take_while is_whitespace >>= fun s ->
return (Format.sprintf "%c%s" c s)
let spaces =
take_while is_whitespace >>= fun s ->
return s
let identifier_parser () =
many (alphanum <|> char '_')
|>> String.of_char_list
let many1_till p t =
let cons x xs = x::xs in
lift2 cons p (many_till p t)

View File

@ -81,10 +81,12 @@ module Omega = struct
; identifier : string
; text : string
}
[@@deriving yojson]
type production =
| Unit
| String of string
| Template_string of string
| Hole of hole
| Match of omega_match_production
end
@ -105,6 +107,8 @@ module Matcher = struct
-> string
-> Match.t Or_error.t
val set_rewrite_template : string -> unit
val all
: ?configuration:Configuration.t
-> template:string

View File

@ -6,18 +6,30 @@ module Omega = struct
let (|>>) p f =
p >>= fun x -> return (f x)
let between left _right p =
left *> p (*<* right*)
let any_char_except ~reserved =
List.fold reserved
~init:(return `OK)
~f:(fun acc reserved_sequence ->
option `End_of_input
(peek_string (String.length reserved_sequence)
>>= fun s ->
if s = reserved_sequence then
return `Reserved_sequence
else
acc))
>>= function
| `OK -> any_char
| `End_of_input -> any_char
| `Reserved_sequence -> fail "reserved sequence hit"
let between left right p =
left *> p <* right
let to_string from until between : string =
from ^ (String.of_char_list between) ^ until
let anything_including_newlines ~until =
(* until is not consumed in Alpha. Angstrom consumes it. It needs to not
consume because we're doing that for 'between'; but now between is
changed to not expect it. The point is: it does the right thing but
diverges from Alpha and is a little bit... weird *)
(many_till any_char (string until))
many (any_char_except ~reserved:[until])
let anything_excluding_newlines () =
anything_including_newlines ~until:"\n"
@ -41,7 +53,8 @@ module Omega = struct
end
end
(* XXX consumes the newline *)
(* Consumes the newline if we don't reintroduce it. This can be improved, we
shouldn't need to reintroduce it.*)
let until_newline start =
(string start *> anything_excluding_newlines ()
|>> fun l -> start^(String.of_char_list l))

View File

@ -22,13 +22,31 @@ module Omega = struct
<|> (any_char |>> String.of_char)
)
let base_string_literal =
((string M.delimiter *> (many_till char_token_s (string M.delimiter))
|>> String.concat)
>>= fun result ->
return (Format.sprintf {|%s%s|} M.delimiter result)
(* unlike Alpha, do not suffix the ending delimiter, it was captured by the delimiter parser in many_till *)
return (Format.sprintf {|%s%s%s|} M.delimiter result M.delimiter)
)
end
end
module Raw = struct
module type S = sig
val left_delimiter : string
val right_delimiter : string
end
module Make (M : S) = struct
let char_token_s =
(any_char |>> String.of_char)
let base_string_literal =
((
string M.left_delimiter *> (many_till char_token_s (string M.right_delimiter))
|>> String.concat)
>>= fun result ->
return (Format.sprintf {|%s%s%s|} M.left_delimiter result M.right_delimiter)
)
end
end

View File

@ -119,8 +119,11 @@ let update_match f m =
let environment = update_environment m.environment f in
{ m with range; environment }
let timed_run matcher ?substitute_in_place ?rule ~configuration ~template ~source () =
let timed_run matcher ?rewrite_template ?substitute_in_place ?rule ~configuration ~template ~source () =
let module Matcher = (val matcher : Matchers.Matcher) in
(match rewrite_template with
| Some template -> Matcher.set_rewrite_template template;
| None -> ());
let matches = Matcher.all ~configuration ~template ~source in
let rule = Option.value rule ~default:[Ast.True] in
let matches = apply_rule ?substitute_in_place matcher rule matches in
@ -171,7 +174,7 @@ let process_single_source
in
let matches =
with_timeout timeout source ~f:(fun () ->
timed_run matcher ?rule ~substitute_in_place ~configuration ~template ~source:input_text ())
timed_run matcher ?rewrite_template ?rule ~substitute_in_place ~configuration ~template ~source:input_text ())
in
match rewrite_template with
| None -> Matches (matches, List.length matches)

View File

@ -2,18 +2,7 @@
(name alpha_test_integration)
(modules
test_optional_holes
test_match_rule
test_hole_extensions
test_python_string_literals
test_integration
test_statistics
test_cli
test_c_style_comments
test_string_literals
test_special_matcher_cases
test_generic
test_rewrite_rule
test_server
test_substring_disabled)
(inline_tests)
(preprocess (pps ppx_expect ppx_sexp_message ppx_deriving_yojson ppx_deriving_yojson.runtime))

View File

@ -1,6 +1,17 @@
(library
(name common_test_integration)
(modules
test_cli
test_rewrite_rule
test_server
test_statistics
test_integration
test_match_rule
test_python_string_literals
test_hole_extensions
test_generic
test_string_literals
test_c_style_comments
test_nested_comments
test_c
test_bash

View File

@ -145,7 +145,7 @@ let%expect_test "implicit_equals" =
let%expect_test "implicit_equals_does_not_apply_to_underscore" =
let run = run_all in
let source = {|a b c|} in
let match_template = {|:[[_]] :[[_]] :[[_]]|} in
let rewrite_template = {|:[_]|} in
let match_template = {|:[[x]] :[[_]] :[[_]]|} in
let rewrite_template = {|:[x]|} in
run source match_template rewrite_template;
[%expect_exact {|a|}]

View File

@ -46,9 +46,12 @@ let%expect_test "invalid_raw_string_in_python_but_matches_because_ignores_after"
print_matches matches;
[%expect_exact {|[ "\"\"\"\"\"\"" ]|}]
(* Disabled: this works by luck in Alpha, but it shouldn't. It is empty list in Omega. Should be explicitly supported *)
(*
let%expect_test "raw_string_captures_escape_sequences" =
let source = {|"""\""""|} in
let template = {|""":[1]"""|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"\"\"\\\"\"\"" ]|}]
*)

View File

@ -88,28 +88,29 @@ let%expect_test "post_request" =
"id": 0
} |}];
(* Disabled: Angstrom does not output similarly useful parse errors.
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where :[1] = "world"|} in
let language = "generic" in
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where :[1] = "world"|} in
let language = "generic" in
In.{ source; match_template; rule; language; id = 0 }
|> In.match_request_to_yojson
|> Yojson.Safe.to_string
|> post `Match
|> print_string;
In.{ source; match_template; rule; language; id = 0 }
|> In.match_request_to_yojson
|> Yojson.Safe.to_string
|> post `Match
|> print_string;
[%expect {|
Error in line 1, column 7:
where :[1] = "world"
^
Expecting "false", "match", "rewrite" or "true"
Backtracking occurred after:
Error in line 1, column 12:
[%expect {|
Error in line 1, column 7:
where :[1] = "world"
^
Expecting "!=" or "==" |}];
^
Expecting "false", "match", "rewrite" or "true"
Backtracking occurred after:
Error in line 1, column 12:
where :[1] = "world"
^
Expecting "!=" or "==" |}];
*)
let substitution_kind = "in_place" in
let source = "hello world" in
@ -167,24 +168,26 @@ let%expect_test "post_request" =
"rewritten_source": "world, hello\nworld, hello",
"in_place_substitutions": [],
"id": 0
} |}];
} |}]
(* test there must be at least one predicate in a rule *)
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where |} in
let language = "generic" in
(* Disabled: Angstrom does not output similarly useful parse errors.
(* test there must be at least one predicate in a rule *)
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where |} in
let language = "generic" in
let request = In.{ source; match_template; rule; language; id = 0 } in
let json = In.match_request_to_yojson request |> Yojson.Safe.to_string in
let result = post `Match json in
let request = In.{ source; match_template; rule; language; id = 0 } in
let json = In.match_request_to_yojson request |> Yojson.Safe.to_string in
let result = post `Match json in
print_string result;
[%expect {|
print_string result;
[%expect {|
Error in line 1, column 7:
where
^
Expecting ":[", "false", "match", "rewrite", "true" or string literal |}]
*)
let%expect_test "post_substitute" =

View File

@ -4,19 +4,8 @@
;
; TODO
;
test_generic
test_match_rule
test_hole_extensions
test_python_string_literals
test_integration
test_statistics
test_cli
test_c_style_comments
test_string_literals
test_optional_holes
test_special_matcher_cases
test_generic
test_rewrite_rule
test_server
test_substring_disabled)
(inline_tests)
(preprocess (pps ppx_expect ppx_sexp_message ppx_deriving_yojson ppx_deriving_yojson.runtime))

View File

@ -1 +0,0 @@
../example

View File

@ -1,222 +0,0 @@
(*open Core
open Matchers
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let all ?(configuration = configuration) template source =
C.all ~configuration ~template ~source
let print_matches matches =
List.map matches ~f:Match.to_yojson
|> (fun matches -> `List matches)
|> Yojson.Safe.pretty_to_string
|> print_string
let%expect_test "rewrite_comments_1" =
let template = "replace this :[1] end" in
let source = "/* don't replace this () end */ do replace this () end" in
let rewrite_template = "X" in
all template source
|> (fun matches ->
Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "/* don't replace this () end */ do X"]
let%expect_test "rewrite_comments_2" =
let template =
{|
if (:[1]) { :[2] }
|}
in
let source =
{|
/* if (fake_condition_body_must_be_non_empty) { fake_body; } */
// if (fake_condition_body_must_be_non_empty) { fake_body; }
if (real_condition_body_must_be_empty) {
int i;
int j;
}
|}
in
let rewrite_template =
{|
if (:[1]) {}
|}
in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact
{|
/* if (fake_condition_body_must_be_non_empty) { fake_body; } */
if (real_condition_body_must_be_empty) {}
|}]
let%expect_test "capture_comments" =
let template = {|if (:[1]) { :[2] }|} in
let source = {|if (true) { /* some comment */ console.log(z); }|} in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 48, "line": 1, "column": 49 }
},
"environment": [
{
"variable": "1",
"value": "true",
"range": {
"start": { "offset": 4, "line": 1, "column": 5 },
"end": { "offset": 8, "line": 1, "column": 9 }
}
},
{
"variable": "2",
"value": "console.log(z);",
"range": {
"start": { "offset": 31, "line": 1, "column": 32 },
"end": { "offset": 46, "line": 1, "column": 47 }
}
}
],
"matched": "if (true) { /* some comment */ console.log(z); }"
}
]|}]
let%expect_test "single_quote_in_comment" =
let template =
{| {:[1]} |}
in
let source =
{|
/*'*/
{test}
|}
in
let rewrite_template =
{|
{:[1]}
|}
in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact
{|
{test}
|}]
let%expect_test "single_quote_in_comment" =
let template =
{| {:[1]} |}
in
let source =
{|
{
a = 1;
/* Events with mask == AE_NONE are not set. So let's initiaize the
* vector with it. */
for (i = 0; i < setsize; i++)
}
|}
in
let rewrite_template =
{|
{:[1]}
|}
in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact
{|
{
a = 1;
/* Events with mask == AE_NONE are not set. So let's initiaize the
* vector with it. */
for (i = 0; i < setsize; i++)
}
|}]
let%expect_test "single_quote_in_comment" =
let template =
{| {:[1]} |}
in
let source =
{|
{
a = 1;
/* ' */
for (i = 0; i < setsize; i++)
}
|}
in
let rewrite_template =
{|
{:[1]}
|}
in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact
{|
{
a = 1;
/* ' */
for (i = 0; i < setsize; i++)
}
|}]
let%expect_test "give_back_the_comment_characters_for_newline_comments_too" =
let template =
{| {:[1]} |}
in
let source =
{|
{
// a comment
}
|}
in
let rewrite_template =
{|
{:[1]}
|}
in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact
{|
{
// a comment
}
|}]
*)

View File

@ -1,945 +0,0 @@
(*open Core
module Time = Core_kernel.Time_ns.Span
let binary_path = "../../../comby"
let read_with_timeout read_from_channels =
let read_from_fds = List.map ~f:Unix.descr_of_in_channel read_from_channels in
let read_from_channels =
Unix.select
~restart:true
~read:read_from_fds
~write:[]
~except:[]
~timeout:(`After (Time.of_int_sec 1))
()
|> (fun { Unix.Select_fds.read; _ } -> read)
|> List.map ~f:Unix.in_channel_of_descr
in
List.map read_from_channels ~f:In_channel.input_all
|> String.concat ~sep:"\n"
let read_output command =
let open Unix.Process_channels in
let { stdout; stderr; _ } =
Unix.open_process_full ~env:(Array.of_list ["COMBY_TEST=1"]) command
in
let stdout_result = In_channel.input_all stdout in
let stderr_result = In_channel.input_all stderr in
stdout_result ^ stderr_result
let read_expect_stdin_and_stdout command source =
let open Unix.Process_channels in
let { stdin; stdout; stderr } =
Unix.open_process_full ~env:(Array.of_list ["COMBY_TEST=1"]) command
in
Out_channel.output_string stdin source;
Out_channel.flush stdin;
Out_channel.close stdin;
let stdout_result = In_channel.input_all stdout in
let stderr_result = In_channel.input_all stderr in
stdout_result ^ stderr_result
let read_expect_stderr command source =
let open Unix.Process_channels in
let { stdin; stdout; stderr } =
Unix.open_process_full ~env:(Array.of_list ["COMBY_TEST=1"]) command
in
Out_channel.output_string stdin source;
Out_channel.flush stdin;
Out_channel.close stdin;
let _ = In_channel.input_all stdout in
let stderr_result = In_channel.input_all stderr in
stderr_result
let%expect_test "json_lines_separates_by_line" =
let source = "hello world" in
let match_template = "o" in
let rewrite_template = "i" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -json-lines" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|{"uri":null,"rewritten_source":"helli wirld","in_place_substitutions":[{"range":{"start":{"offset":7,"line":-1,"column":-1},"end":{"offset":8,"line":-1,"column":-1}},"replacement_content":"i","environment":[]},{"range":{"start":{"offset":4,"line":-1,"column":-1},"end":{"offset":5,"line":-1,"column":-1}},"replacement_content":"i","environment":[]}],"diff":"--- /dev/null\n+++ /dev/null\n@@ -1,1 +1,1 @@\n-hello world\n+helli wirld"}
|}]
let%expect_test "json_lines_json_pretty_do_not_output_when_diff_null" =
let source = "hello world" in
let match_template = "asdf" in
let rewrite_template = "asdf" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -json-lines" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{| |}]
let%expect_test "json_lines_do_not_output_when_diff_null" =
let source = "hello world" in
let match_template = "asdf" in
let rewrite_template = "asdf" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -json-lines" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{| |}]
let%expect_test "error_on_zip_and_stdin" =
let command_args = "-zip x -stdin" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command "none" in
print_string result;
[%expect_exact {|No templates specified. See -h to specify on the command line, or use -templates <directory-containing-templates>.
Next error: -zip may not be used with -stdin.
|}]
let%expect_test "error_on_stdout_and_diff" =
let command_args = "'' '' -stdout -diff" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command "none" in
print_string result;
[%expect_exact {|-stdout may not be used with -diff. Note: -stdout outputs the changed file contents and -diff outputs a unified diff. Choose one of these.
|}]
let%expect_test "error_on_invalid_templates_dir" =
let source = "hello world" in
let match_template = "hello :[1]" in
let rewrite_template = ":[1]" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -templates nonexistent" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|One or more directories specified with -templates is not a directory.
|}]
let%expect_test "warn_on_anonymous_and_templates_flag" =
let source = "hello world" in
let match_template = "hello :[1]" in
let rewrite_template = ":[1]" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -templates example/templates/identity" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|WARNING: Templates specified on the command line AND using -templates. Ignoring match
and rewrite templates on the command line and only using those in directories.
|}]
let%expect_test "stdin_command" =
let source = "hello world" in
let match_template = "hello :[1]" in
let rewrite_template = ":[1]" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
!|hello world
|}]
let%expect_test "with_match_rule" =
let source = "hello world" in
let match_template = "hello :[1]" in
let rewrite_template = ":[1]" in
let rule = {|where :[1] == "world"|} in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -rule '%s' -f .c "
match_template rewrite_template rule
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
!|hello world
|}];
let source = "hello world" in
let match_template = "hello :[1]" in
let rewrite_template = ":[1]" in
let rule = {|where :[1] != "world"|} in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -rule '%s' -f .c -stdout"
match_template rewrite_template rule
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
hello world |}]
let%expect_test "with_rewrite_rule" =
let source = "hello world" in
let match_template = ":[2] :[1]" in
let rewrite_template = ":[1]" in
let rule = {|where rewrite :[1] { ":[_]" -> ":[2]" }|} in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -rule '%s' -f .c "
match_template rewrite_template rule
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
!|hello world
|}]
let%expect_test "with_rewrite_rule_stdin_default_no_extension" =
let source = "hello world" in
let match_template = ":[2] :[1]" in
let rewrite_template = ":[1]" in
let rule = {|where rewrite :[1] { ":[_]" -> ":[2]" }|} in
let command_args =
Format.sprintf "-sequential '%s' '%s' -rule '%s' -stdin" match_template rewrite_template rule
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
!|hello world
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning.
|}]
let%expect_test "generic_matcher" =
let source = {|\footnote{\small \url{https://github.com}}|} in
let match_template = {|\footnote{\small :[1]}|} in
let rewrite_template = {|\footnote{\scriptsize :[1]}|} in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .generic" match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
-|\footnote{\small \url{https://github.com}}
+|\footnote{\scriptsize \url{https://github.com}}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning.
|}]
let%expect_test "json_output_option" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential -json-lines '%s' '%s' -f .c "
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|{"uri":null,"rewritten_source":"c X a c Y a","in_place_substitutions":[{"range":{"start":{"offset":6,"line":-1,"column":-1},"end":{"offset":11,"line":-1,"column":-1}},"replacement_content":"c Y a","environment":[{"variable":"1","value":"Y","range":{"start":{"offset":2,"line":-1,"column":-1},"end":{"offset":3,"line":-1,"column":-1}}}]},{"range":{"start":{"offset":0,"line":-1,"column":-1},"end":{"offset":5,"line":-1,"column":-1}},"replacement_content":"c X a","environment":[{"variable":"1","value":"X","range":{"start":{"offset":2,"line":-1,"column":-1},"end":{"offset":3,"line":-1,"column":-1}}}]}],"diff":"--- /dev/null\n+++ /dev/null\n@@ -1,1 +1,1 @@\n-a X c a Y c\n+c X a c Y a"}
|}];
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential -json-lines -match-only '%s' '%s' -f .c "
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|{"uri":null,"matches":[{"range":{"start":{"offset":0,"line":1,"column":1},"end":{"offset":5,"line":1,"column":6}},"environment":[{"variable":"1","value":"X","range":{"start":{"offset":2,"line":1,"column":3},"end":{"offset":3,"line":1,"column":4}}}],"matched":"a X c"},{"range":{"start":{"offset":6,"line":1,"column":7},"end":{"offset":11,"line":1,"column":12}},"environment":[{"variable":"1","value":"Y","range":{"start":{"offset":8,"line":1,"column":9},"end":{"offset":9,"line":1,"column":10}}}],"matched":"a Y c"}]}|}]
let with_zip f =
let file = Filename.temp_file "comby_" ".zip" in
let zip = Zip.open_out file in
let entry_name = "main.ml" in
let entry_content = "hello world" in
Zip.add_entry entry_content zip entry_name;
Zip.close_out zip;
f file;
Unix.remove file
let%expect_test "list_languages" =
let command_args = "-list" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_output command in
print_string result;
[%expect_exact {|Option Language
-matcher .s Assembly
-matcher .sh Bash
-matcher .c C
-matcher .cs C#
-matcher .css CSS
-matcher .dart Dart
-matcher .dyck Dyck
-matcher .clj Clojure
-matcher .elm Elm
-matcher .erl Erlang
-matcher .ex Elixir
-matcher .f Fortran
-matcher .fsx F#
-matcher .go Go
-matcher .html HTML
-matcher .hs Haskell
-matcher .java Java
-matcher .js Javascript/Typescript
-matcher .json JSON
-matcher .jl Julia
-matcher .kt Kotlin
-matcher .tex LaTeX
-matcher .lisp Lisp
-matcher .nim Nim
-matcher .ml OCaml
-matcher .paren Paren
-matcher .pas Pascal
-matcher .php PHP
-matcher .py Python
-matcher .re Reason
-matcher .rb Ruby
-matcher .rs Rust
-matcher .scala Scala
-matcher .sql SQL
-matcher .swift Swift
-matcher .txt Text
-matcher .xml XML
-matcher .generic Generic
|}]
let%expect_test "patdiff_and_zip" =
with_zip (fun file ->
let match_template = ":[2] :[1]" in
let rewrite_template = ":[1]" in
let command_args =
Format.sprintf "'%s' '%s' .ml -sequential -json-lines -zip %s"
match_template rewrite_template file
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_output command in
print_string result;
[%expect_exact {|{"uri":"main.ml","rewritten_source":"world","in_place_substitutions":[{"range":{"start":{"offset":0,"line":-1,"column":-1},"end":{"offset":5,"line":-1,"column":-1}},"replacement_content":"world","environment":[{"variable":"1","value":"world","range":{"start":{"offset":0,"line":-1,"column":-1},"end":{"offset":5,"line":-1,"column":-1}}}]}],"diff":"--- main.ml\n+++ main.ml\n@@ -1,1 +1,1 @@\n-hello world\n+world"}
|}]
)
let%expect_test "template_parsing_no_match_template" =
let source = "hello world" in
let template_dir = "example" ^/ "templates" ^/ "parse-no-match-template" in
let command_args = Format.sprintf "-stdin -sequential -f .c -templates %s" template_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|WARNING: Could not read required match file in example/templates/parse-no-match-template
|}]
let%expect_test "template_parsing_with_trailing_newline" =
let source = "hello world" in
let template_dir = "example" ^/ "templates" ^/ "parse-template-no-trailing-newline" in
let command_args = Format.sprintf "-stdin -sequential -f .c -templates %s -stdout" template_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
hello world |}]
let%expect_test "template_parsing_with_trailing_newline" =
let source = "hello world" in
let template_dir = "example" ^/ "templates" ^/ "parse-template-with-trailing-newline" in
let command_args = Format.sprintf "-stdin -sequential -f .c -templates %s -stdout" template_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
hello world |}]
let%expect_test "nested_templates" =
let source = "1 2 3" in
let template_dir = "example" ^/ "multiple-nested-templates" in
let command_args = Format.sprintf "-stdin -sequential -f .c -templates %s -stdout" template_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
+1 +2 +3WARNING: Could not read required match file in example/multiple-nested-templates/invalid-subdir |}]
let%expect_test "diff_is_default" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
-|a X c a Y c
+|c X a c Y a
|}]
let%expect_test "diff_option" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential -diff '%s' '%s' -f .c"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|--- /dev/null
+++ /dev/null
@@ -1,1 +1,1 @@
-a X c a Y c
+c X a c Y a
|}]
let%expect_test "stdout_option" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential -stdout '%s' '%s' -f .c"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|c X a c Y a|}]
let%expect_test "only_color_prints_colored_diff" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -color"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
-|a X c a Y c
+|c X a c Y a
|}]
let%expect_test "diff_explicit_color" =
let source = "a X c a Y c" in
let match_template = "a :[1] c" in
let rewrite_template = "c :[1] a" in
let command_args =
Format.sprintf "-stdin -sequential '%s' '%s' -f .c -diff -color"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
-|a X c a Y c
+|c X a c Y a
|}]
let%expect_test "is_real_directory" =
let source = "hello world" in
let src_dir = "example" ^/ "src" ^/ "main.c" in
let command_args = Format.sprintf "'main' 'pain' -sequential -d %s -exclude-dir 'ignore' -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
Directory specified with -d or -directory is not a directory. |}]
let%expect_test "exclude_dir_option" =
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'main' 'pain' -sequential -d %s -exclude-dir 'ignore' -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/honor-file-extensions/honor.pb.generic
+++ example/src/honor-file-extensions/honor.pb.generic
@@ -1,3 +1,3 @@
-func main() {
+func pain() {
// foo()
}
--- example/src/honor-file-extensions/honor.pb.go
+++ example/src/honor-file-extensions/honor.pb.go
@@ -1,4 +1,4 @@
-func main() {
+func pain() {
// in a comment foo()
foo()
}
--- example/src/main.c
+++ example/src/main.c
@@ -1,1 +1,1 @@
-int main() {}
+int pain() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}];
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'main' 'pain' -sequential -d %s -exclude-dir 'nonexist' -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/honor-file-extensions/honor.pb.generic
+++ example/src/honor-file-extensions/honor.pb.generic
@@ -1,3 +1,3 @@
-func main() {
+func pain() {
// foo()
}
--- example/src/honor-file-extensions/honor.pb.go
+++ example/src/honor-file-extensions/honor.pb.go
@@ -1,4 +1,4 @@
-func main() {
+func pain() {
// in a comment foo()
foo()
}
--- example/src/ignore-me/main.c
+++ example/src/ignore-me/main.c
@@ -1,1 +1,1 @@
-int main() {}
+int pain() {}
--- example/src/main.c
+++ example/src/main.c
@@ -1,1 +1,1 @@
-int main() {}
+int pain() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}]
let%expect_test "dir_depth_option" =
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'depth_' 'correct_depth_' -sequential -directory %s -depth %d -diff" src_dir (-1) in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{| -depth must be 0 or greater. |}];
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'depth_' 'correct_depth_' -sequential -directory %s -depth %d -diff" src_dir 0 in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/depth-0.c
+++ example/src/depth-0.c
@@ -1,1 +1,1 @@
-int depth_0() {}
+int correct_depth_0() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}];
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'depth_' 'correct_depth_' -sequential -directory %s -depth %d -diff" src_dir 1 in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/depth-0.c
+++ example/src/depth-0.c
@@ -1,1 +1,1 @@
-int depth_0() {}
+int correct_depth_0() {}
--- example/src/depth-1/depth-1.c
+++ example/src/depth-1/depth-1.c
@@ -1,1 +1,1 @@
-int depth_1() {}
+int correct_depth_1() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}];
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'depth_' 'correct_depth_' -sequential -directory %s -depth %d -diff" src_dir 2 in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/depth-0.c
+++ example/src/depth-0.c
@@ -1,1 +1,1 @@
-int depth_0() {}
+int correct_depth_0() {}
--- example/src/depth-1/depth-1.c
+++ example/src/depth-1/depth-1.c
@@ -1,1 +1,1 @@
-int depth_1() {}
+int correct_depth_1() {}
--- example/src/depth-1/depth-2/depth-2.c
+++ example/src/depth-1/depth-2/depth-2.c
@@ -1,1 +1,1 @@
-int depth_2() {}
+int correct_depth_2() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}];
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'depth_' 'correct_depth_' -sequential -directory %s -depth %d -diff" src_dir 1000 in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/depth-0.c
+++ example/src/depth-0.c
@@ -1,1 +1,1 @@
-int depth_0() {}
+int correct_depth_0() {}
--- example/src/depth-1/depth-1.c
+++ example/src/depth-1/depth-1.c
@@ -1,1 +1,1 @@
-int depth_1() {}
+int correct_depth_1() {}
--- example/src/depth-1/depth-2/depth-2.c
+++ example/src/depth-1/depth-2/depth-2.c
@@ -1,1 +1,1 @@
-int depth_2() {}
+int correct_depth_2() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}]
let%expect_test "matcher_override" =
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'(' '_unbalanced_match_' main.c -sequential -d %s -matcher .txt -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/ignore-me/main.c
+++ example/src/ignore-me/main.c
@@ -1,1 +1,1 @@
-int main() {}
+int main_unbalanced_match_) {}
--- example/src/main.c
+++ example/src/main.c
@@ -1,1 +1,1 @@
-int main() {}
+int main_unbalanced_match_) {} |}];
let source = "hello world" in
let src_dir = "example" ^/ "src" in
let command_args = Format.sprintf "'(' '_unbalanced_match_' main.c -sequential -d %s -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{| |}]
let%expect_test "infer_and_honor_extensions" =
let source = "doesn't matter" in
let src_dir = "example" ^/ "src" ^/ "honor-file-extensions" in
let command_args = Format.sprintf "'foo()' 'bar()' .go -sequential -d %s -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/honor-file-extensions/honor.pb.go
+++ example/src/honor-file-extensions/honor.pb.go
@@ -1,4 +1,4 @@
func main() {
// in a comment foo()
-foo()
+bar()
} |}];
let source = "doesn't matter" in
let src_dir = "example" ^/ "src" ^/ "honor-file-extensions" in
let command_args = Format.sprintf "'foo()' 'bar()' .generic -sequential -d %s -diff" src_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- example/src/honor-file-extensions/honor.pb.generic
+++ example/src/honor-file-extensions/honor.pb.generic
@@ -1,3 +1,3 @@
func main() {
-// foo()
+// bar()
}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}]
let%expect_test "diff_only" =
let source = "hello world" in
let command_args = Format.sprintf "'hello' 'world' -stdin -sequential -json-lines -json-only-diff" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
{"uri":null,"diff":"--- /dev/null\n+++ /dev/null\n@@ -1,1 +1,1 @@\n-hello world\n+world world"}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}];
let source = "hello world" in
let command_args = Format.sprintf "'hello' 'world' -stdin -sequential -json-only-diff" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
-json-only-diff can only be supplied with -json-lines. |}]
let%expect_test "zip_exclude_dir_with_extension" =
let source = "doesn't matter" in
let zip = "example" ^/ "zip-test" ^/ "sample-repo.zip" in
let exclude_dir = "sample-repo/vendor" in
let command_args = Format.sprintf "'main' 'pain' .go -zip %s -sequential -diff -exclude-dir %s" zip exclude_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- sample-repo/src/main.go
+++ sample-repo/src/main.go
@@ -1,2 +1,2 @@
// src
-func main() {}
+func pain() {} |}]
let%expect_test "zip_exclude_dir_no_extension" =
let source = "doesn't matter" in
let zip = "example" ^/ "zip-test" ^/ "sample-repo.zip" in
let exclude_dir = "sample-repo/vendor" in
let command_args = Format.sprintf "'main' 'pain' -zip %s -sequential -diff -exclude-dir %s" zip exclude_dir in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
--- sample-repo/src/main.go
+++ sample-repo/src/main.go
@@ -1,2 +1,2 @@
// src
-func main() {}
+func pain() {}
WARNING: the GENERIC matcher was used, because a language could not be inferred from the file extension(s). The GENERIC matcher may miss matches. See '-list' to set a matcher for a specific language and to remove this warning. |}]
let%expect_test "invalid_path_with_error_message" =
let source = "doesn't matter" in
let command_args = Format.sprintf "'a' 'b' ./invalid/path" in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect{|
No such file or directory: ./invalid/path. Comby interprets patterns containing '/' as file paths. If a pattern does not contain '/' (like '.ml'), it is considered a pattern where file endings must match the pattern. Please supply only valid file paths or patterns. |}]
let%expect_test "newline_separated_output"=
let source = "a b c" in
let match_template = ":[[1]]" in
let rewrite_template = ":[[1]]" in
let command_args =
Format.sprintf "-stdin -sequential -stdout '%s' '%s' -n -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|a
b
c
|}]
let%expect_test "warn_on_stdin_and_in_place_flags" =
let source = "a b c" in
let match_template = ":[[1]]" in
let rewrite_template = ":[[1]]" in
let command_args =
Format.sprintf "-stdin -in-place '%s' '%s' -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|WARNING: -in-place has no effect when -stdin is used. Ignoring -in-place.
|}]
let%expect_test "print_single_line_matches" =
let source = {|
let () = x in
let () = y in
|}
in
let match_template = "let ()" in
let rewrite_template = "dont care" in
let command_args =
Format.sprintf "-stdin '%s' '%s' -match-only -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|let ()
let ()
|}]
let%expect_test "print_multi_line_matches" =
let source = {|
let () = x in
let
()
in
let () = y in
|}
in
let match_template = "let ()" in
let rewrite_template = "dont care" in
let command_args =
Format.sprintf "-stdin '%s' '%s' -match-only -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|let ()
let\n\n ()
let ()
|}];
let command_args =
Format.sprintf "-stdin '%s' '%s' -match-only -count -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|3 matches
|}];
let command_args =
Format.sprintf "-stdin '%s' '%s' -match-only -count -json-lines -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|{"uri":null,"matches":[{"range":{"start":{"offset":5,"line":1,"column":6},"end":{"offset":11,"line":1,"column":12}},"environment":[],"matched":"let ()"},{"range":{"start":{"offset":23,"line":1,"column":24},"end":{"offset":34,"line":3,"column":7}},"environment":[],"matched":"let\n\n ()"},{"range":{"start":{"offset":42,"line":1,"column":43},"end":{"offset":48,"line":1,"column":49}},"environment":[],"matched":"let ()"}]}WARNING: -count and -json-lines is specified. Ignoring -count.
|}];
let command_args =
Format.sprintf "-stdin '%s' '%s' -count -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|3 matches
WARNING: -count only works with -match-only. Performing -match-only -count.
|}]
let%expect_test "unrecognized_matcher" =
let source = {|dont care|} in
let match_template = "dont care" in
let rewrite_template = "dont care" in
let command_args =
Format.sprintf "-stdin '%s' '%s' -matcher invalid"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|The matcher "invalid" is not supported. See -list for supported matchers
|}]
let%expect_test "generic_matcher_ok" =
let source = {|dont care|} in
let match_template = "dont care" in
let rewrite_template = "blah" in
let command_args =
Format.sprintf "-stdin '%s' '%s' -matcher .generic"
match_template rewrite_template
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|------ /dev/null
++++++ /dev/null
@|-1,1 +1,1 ============================================================
-|dont care
+|blah
|}]
let%expect_test "warn_on_anonymous_and_templates_flag" =
let source = "(fun i -> j) (fun x -> x)" in
let command_args =
Format.sprintf "-stdin -sequential 'ignore' 'ignore' -templates example/templates/implicit-equals -matcher .ml -stdout"
in
let command = Format.sprintf "%s %s" binary_path command_args in
let result = read_expect_stdin_and_stdout command source in
print_string result;
[%expect_exact {|(fun i -> j) identWARNING: Templates specified on the command line AND using -templates. Ignoring match
and rewrite templates on the command line and only using those in directories.
|}]
let%expect_test "dump_stats" =
let source = {|dont care|} in
let match_template = "care" in
let rewrite_template = "realy care" in
let command_args = Format.sprintf "-stdin '%s' '%s' -stats -matcher .txt" match_template rewrite_template in
let command = Format.sprintf "%s %s" binary_path command_args in
let stats_json = read_expect_stderr command source in
(match Statistics.of_yojson (Yojson.Safe.from_string stats_json) with
| Ok { number_of_files; lines_of_code; number_of_matches; _ } ->
Format.printf "number_of_files: %d, lines_of_code: %d, number_of_matches: %d"
number_of_files lines_of_code number_of_matches
| Error _ -> print_string "Unexpected error");
[%expect_exact {|number_of_files: 1, lines_of_code: 1, number_of_matches: 1|}]
let%expect_test "substitute_bad_parse" =
let source = "dont care" in
let match_template = "dont care" in
let rewrite_template = "dont care" in
let command_args = Format.sprintf "%s %s -substitute 'json'" match_template rewrite_template in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|Error, could not parse JSON to environment: Line 1, bytes 0-4:
Invalid token 'json'
|}]
let%expect_test "substitute_ok" =
let source = "a match1 c d a match2 c d" in
let match_template = "ignored" in
let rewrite_template = ":[1] :[2]" in
let environment = {|[{"variable":"1","value":"hole_1"},{"variable":"2","value":"hole_2"}]|} in
let command_args =
Format.sprintf "'%s' '%s' -stdin -match-only -matcher .txt -substitute '%s'" match_template rewrite_template environment
in
let command = Format.sprintf "%s %s" binary_path command_args in
read_expect_stdin_and_stdout command source
|> print_string;
[%expect_exact {|hole_1 hole_2
|}]
*)

View File

@ -1,434 +0,0 @@
open Core
open Matchers
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let format s =
let s = String.chop_prefix_exn ~prefix:"\n" s in
let leading_indentation = Option.value_exn (String.lfindi s ~f:(fun _ c -> c <> ' ')) in
s
|> String.split ~on:'\n'
|> List.map ~f:(Fn.flip String.drop_prefix leading_indentation)
|> String.concat ~sep:"\n"
|> String.chop_suffix_exn ~suffix:"\n"
let run ?(configuration = configuration) source match_template rewrite_template =
Generic.first ~configuration match_template source
|> function
| Ok result ->
Rewrite.all ~source ~rewrite_template [result]
|> (fun x -> Option.value_exn x)
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string
| Error _ ->
(* this is too annoying to fix everytime the grammar changes. *)
print_string ""
let run_all ?(configuration = configuration) source match_template rewrite_template =
Generic.all ~configuration ~template:match_template ~source
|> function
| [] -> print_string "No matches."
| results ->
Option.value_exn (Rewrite.all ~source ~rewrite_template results)
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string
let%expect_test "basic" =
let source = {|a b c d|} in
let match_template = {|:[1]|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b c d|}];
let source = {|a b c d|} in
let match_template = {|a :[1] c d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b|}];
let source = {|a b c d|} in
let match_template = {|a :[1] d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b c|}];
let source = {|a b c d|} in
let match_template = {|a :[1]|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b c d|}];
let source = {|a b c d|} in
let match_template = {|:[1] c d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b|}];
let source = {|a b c d|} in
let match_template = {|:[1] :[2]|} in
let rewrite_template = {|(:[1]) (:[2])|} in
run source match_template rewrite_template;
[%expect_exact {|(a) (b c d)|}];
let source = {|a b c d|} in
let match_template = {|:[2] :[1]|} in
let rewrite_template = {|(:[2]) (:[1])|} in
run source match_template rewrite_template;
[%expect_exact {|(a) (b c d)|}];
let source = {|a b c d|} in
let match_template = {|a :[2] :[1] d|} in
let rewrite_template = {|(:[2]) (:[1])|} in
run source match_template rewrite_template;
[%expect_exact {|(b) (c)|}];
let source = {|a b c d|} in
let match_template = {|a :[2] :[1]|} in
let rewrite_template = {|(:[2]) (:[1])|} in
run source match_template rewrite_template;
[%expect_exact {|(b) (c d)|}];
let source = {|a b c d|} in
let match_template = {|a :[2] c :[1]|} in
let rewrite_template = {|(:[2]) (:[1])|} in
run source match_template rewrite_template;
[%expect_exact {|(b) (d)|}];
let source = {|x:|} in
let match_template = {|:[1]:|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|x|}]
let%expect_test "basic_failures" =
let source = {|a x b bbq|} in
let match_template = {|a :[1] b c|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {||}];
let source = {|a b c d|} in
let match_template = {|a :[2] d :[1]|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact
{||}];
let source = {|a b c d|} in
let match_template = {|a :[2] b :[1]|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {||}]
let%expect_test "delimiter_matching" =
let source = {|(a b c) d|} in
let match_template = {|(:[1]) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b c|}];
let source = {|(a b c) d|} in
let match_template = {|(:[1] b :[2]) d|} in
let rewrite_template = {|(:[1]) (:[2])|} in
run source match_template rewrite_template;
[%expect_exact {|(a) (c)|}];
let source = {|q(a b c) d|} in
let match_template = {|q(:[1] b :[2]) d|} in
let rewrite_template = {|(:[1]) (:[2])|} in
run source match_template rewrite_template;
[%expect_exact {|(a) (c)|}];
let source = {|((a) b)|} in
let match_template = {|(:[1] b)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|(a)|}];
let source = {|((a b c)) d|} in
let match_template = {|(:[1]) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|(a b c)|}];
let source = {|((a b c)) d|} in
let match_template = {|(:[1]) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|(a b c)|}];
let source = {|((a b c) q) d|} in
let match_template = {|((:[1]) q) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b c|}];
let source = {|((a b c) q) d|} in
let match_template = {|((:[1] c) q) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b|}];
let source = {|((a b () c) q) d|} in
let match_template = {|((:[1] () c) q) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a b|}];
let source = {|((a ((x) d) b c)) d|} in
let match_template = {|((a :[1] :[2] c)) d|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|((x) d)|}];
let source = {|((a ((x) d) b c)) d|} in
let match_template = {|((a (:[1]) :[2] c)) d|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|(x) d b|}];
let source = {|(b (c) d)|} in
let match_template = {|(:[1])|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b (c) d|}];
let source = {|(b (c) d.)|} in
let match_template = {|(:[1].)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b (c) d|}];
let source = {|(b (c.) d.)|} in
let match_template = {|(:[1].)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b (c.) d|}];
let source = {|(b. (c) d.)|} in
let match_template = {|(:[1].)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b. (c) d|}];
let source = {|(b (c) d.)|} in
let match_template = {|(b :[1] d.)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|(c)|}];
let source = {|outer(inner(dst,src),src)|} in
let match_template = {|outer(:[1],src)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|inner(dst,src)|}];
let source = {|(b ((c)) d.)|} in
let match_template = {|(b :[1] d.)|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|((c))|}];
let source = {|a b c|} in
let match_template = {|a :[1] c|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|b|}];
let source = {|x = foo;|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|foo|}];
let source = {|((a {{x} d} b c)) d|} in
let match_template = {|((a {:[1] d} :[2] c)) d|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|{x} b|}];
let source = {|((a {([{x}]) d} b c)) d|} in
let match_template = {|((a {:[1] d} :[2] c)) d|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|([{x}]) b|}];
let source = {|(((((x)))))|} in
let match_template = {|(((:[1])))|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|((x))|}];
let source = {|((((y(x)z))))|} in
let match_template = {|(((:[1])))|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|(y(x)z)|}];
let source = {|((((y(x)z))))|} in
let match_template = {|(((:[1]):[2]))|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|(y(x)z) |}];
let source = {|(((x)z))|} in
let match_template = {|(((:[1]):[2]))|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|x z|}];
let source = {|((((x))z))|} in
let match_template = {|(((:[1]):[2]))|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|(x) z|}];
let source = {|lolwtfbbq|} in
let match_template = {|lol:[1]bbq|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|wtf|}];
let source = {|x = foo; x = bar;|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|foo x = bar;|}];
let source = {|[ no match prefix ] x = foo; [ no match suffix ]|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|[ no match prefix ] foo [ no match suffix ]|}];
let source = {|x = a; x = b; x = c|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|a x = b; x = c|}];
let source = {|x = ( x = x; );|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|( x = x; )|}];
let source = {|( x = x = x; )|} in
let match_template = {|x = :[1];|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|( x = x )|}];
let source = {|xxx a b d c 1 2 3 b d d blah|} in
let match_template = {|a :[1] c :[2] d|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|xxx b d 1 2 3 b d blah|}];
let source = {|howevenlolwtfbbqispossible|} in
let match_template = {|lol:[1]bbq|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|howevenwtfispossible|}];
let source = {|lolhowevenlolwtfbbqispossiblebbq|} in
let match_template = {|lol:[1]bbq|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|howevenlolwtfispossiblebbq|}];
let source = {|hello my name is bob the builder|} in
let match_template = {|:[alongidentifiername] :[2] :[3] :[xyz] :[5] :[6]|} in
let rewrite_template = {|:[alongidentifiername] :[2] :[3] :[xyz] :[5] :[6]|} in
run source match_template rewrite_template;
[%expect_exact {|hello my name is bob the builder|}];
let source = {|www.testdofooname.com/picsinsideit/stunningpictureofkays1381737242g8k4n-280x428.jpg|} in
let match_template = {|www.:[1]-:[2].jpg|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|testdofooname.com/picsinsideit/stunningpictureofkays1381737242g8k4n 280x428|}];
let source = {|https://api.github.com/repos/dmjacobsen/slurm/commits/716c1499695c68afcab848a1b49653574b4fc167|} in
let match_template = {|:[1]api.:[2]/repos/:[3]s/:[4]|} in
let rewrite_template = {|:[1] :[2] :[3] :[4]|} in
run source match_template rewrite_template;
[%expect_exact {|https:// github.com dmjacobsen/slurm/commit 716c1499695c68afcab848a1b49653574b4fc167|}];
let source =
{|
assert(stream->md_len + md_len -
si.foo_data_begin <= MAD_BUFFER_MDLEN);
memcpy(*stream->foo_data + stream->md_len,
mad_bit_nextbyte(&stream->ptr),
frame_used = md_len - si.foo_data_begin);
stream->md_len += frame_used;
|} |> format
in
let match_template = {|memcpy(:[1], :[2], :[3]);|} in
let rewrite_template = {|:[1], :[2], :[3]|} in
run source match_template rewrite_template;
[%expect_exact {|assert(stream->md_len + md_len -
si.foo_data_begin <= MAD_BUFFER_MDLEN);
*stream->foo_data + stream->md_len, mad_bit_nextbyte(&stream->ptr), frame_used = md_len - si.foo_data_begin
stream->md_len += frame_used;|}]
let%expect_test "significant_whitespace" =
let configuration = Configuration.create ~match_kind:Fuzzy ~significant_whitespace:true () in
let run = run ~configuration in
let source = {|two spaces|} in
let match_template = {|:[1] :[2]|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|two spaces|}];
(* FIXME: this should fail. also test case where separators do or do not need
whitespace. e.g., strict about strcpy(src,dst) matching a template
strcpy(:[1],:[2]) versus strcpy(:[1], :[2]) *)
let source = {|two spaces|} in
let match_template = {|:[1] :[2]|} in
let rewrite_template = {|:[1] :[2]|} in
run source match_template rewrite_template;
[%expect_exact {|two spaces|}]
let%expect_test "contextual_matching" =
let run = run_all in
let source = {|memcpy(dst1, src1, 1); memcpy(dst2, src2, 2);|} in
let match_template = {|memcpy(:[1], :[2], :[3])|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|dst1; dst2;|}];
let source = {|memcpy(dst1, src1, 1); memcpy(dst2, src2, 2);|} in
let match_template = {|memcpy(:[1], :[2], :[3])|} in
let rewrite_template = {|:[1]|} in
run source match_template rewrite_template;
[%expect_exact {|dst1; dst2;|}]
let%expect_test "contextual_matching_with_short_hole_syntax" =
let run = run_all in
let source = {|memcpy(dst1, src1, 1); memcpy(dst2, src2, 2);|} in
let match_template = {|memcpy(:[[1]], :[2], :[3])|} in
let rewrite_template = {|:[[1]]|} in
run source match_template rewrite_template;
[%expect_exact {|dst1; dst2;|}]
(*
let%expect_test "trivial_empty_case" =
let source = "" in
let match_template = "" in
begin
Generic.all ~configuration ~template:match_template ~source
|> function
| [] -> print_string "No matches."
| hd :: _ ->
print_string (Yojson.Safe.to_string (Match.to_yojson hd))
end;
[%expect_exact {|{"range":{"start":{"offset":0,"line":1,"column":1},"end":{"offset":0,"line":1,"column":1}},"environment":[],"matched":""}|}]
*)

View File

@ -1,144 +0,0 @@
(*open Core
open Matchers
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let run_all ?(configuration = configuration) source match_template rewrite_template =
Generic.all ~configuration ~template:match_template ~source
|> function
| [] -> print_string "No matches."
| results ->
Option.value_exn (Rewrite.all ~source ~rewrite_template results)
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string
let%expect_test "non_space" =
let run = run_all in
let source = {| foo. foo.bar.quux derp|} in
let match_template = {|:[x.]|} in
let rewrite_template = {|{:[x]}|} in
run source match_template rewrite_template;
[%expect_exact {| {foo.} {foo.bar.quux} {derp}|}]
let%expect_test "only_space" =
let run = run_all in
let source = {| foo. foo.bar.quux derp|} in
let match_template = {|:[ x]|} in
let rewrite_template = {|{:[x]}|} in
run source match_template rewrite_template;
[%expect_exact {|{ }foo.{ }foo.bar.quux{ }derp|}]
let%expect_test "up_to_newline" =
let run = run_all in
let source =
{|
foo.
foo.bar.quux
derp
|} in
let match_template = {|:[x\n]|} in
let rewrite_template = {|{:[x]}|} in
run source match_template rewrite_template;
[%expect_exact {|{
}{foo.
}{foo.bar.quux
}{derp
}|}]
let%expect_test "match_empty_in_newline_hole" =
let run = run_all in
let source =
{|stuff
after
|} in
let match_template = {|stuff:[x\n]|} in
let rewrite_template = {|{->:[x]<-}|} in
run source match_template rewrite_template;
[%expect_exact {|{->
<-}after
|}]
let%expect_test "leading_indentation" =
let run = run_all in
let source =
{|
foo. bar bazz
foo.bar.quux
derp
|} in
let match_template = {|:[ leading_indentation]:[rest\n]|} in
let rewrite_template = {|{:[leading_indentation]}:[rest]|} in
run source match_template rewrite_template;
[%expect_exact {|
{ }foo. bar bazz
{ }foo.bar.quux
{ }derp
|}]
let%expect_test "non_space_partial_match" =
let run = run_all in
let source = {| foo. foo.bar.quux derp|} in
let match_template = {|foo.:[x.]ux|} in
let rewrite_template = {|{:[x]}|} in
run source match_template rewrite_template;
[%expect_exact {| foo. {bar.qu} derp|}]
let%expect_test "non_space_does_not_match_reserved_delimiters" =
let run = run_all in
let source = {|fo.o(x)|} in
let match_template = {|:[f.]|} in
let rewrite_template = {|{:[f]}|} in
run source match_template rewrite_template;
[%expect_exact {|{fo.o}({x})|}]
let%expect_test "alphanum_partial_match" =
let run = run_all in
let source = {| foo. foo.bar.quux derp|} in
let match_template = {|foo.b:[x]r.quux|} in
let rewrite_template = {|{:[x]}|} in
run source match_template rewrite_template;
[%expect_exact {| foo. {a} derp|}]
let%expect_test "newline_matcher_should_not_be_sat_on_space" =
let run = run_all in
let source =
{|a b c d
e f g h|} in
let match_template = {|:[line\n] |} in
let rewrite_template = {|{:[line]}|} in
run source match_template rewrite_template;
[%expect_exact {|{a b c d
}e f g h|}];
let run = run_all in
let source =
{|a b c d
e f g h|} in
let match_template = {|:[line\n]:[next]|} in
let rewrite_template = {|{:[line]}|} in
run source match_template rewrite_template;
[%expect_exact {|{a b c d
}|}];
let run = run_all in
let source =
{|a b c d
e f g h
|} in
let match_template = {|:[line1\n]:[next\n]|} in
let rewrite_template = {|{:[line1]|:[next]}|} in
run source match_template rewrite_template;
[%expect_exact {|{a b c d
|e f g h
}|}]
let%expect_test "implicit_equals" =
let run = run_all in
let source = {|a b a|} in
let match_template = {|:[[x]] :[[m]] :[[x]]|} in
let rewrite_template = {|:[m]|} in
run source match_template rewrite_template;
[%expect_exact {|b|}]
*)

View File

@ -1,323 +0,0 @@
(*open Core
open Matchers
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let all ?(configuration = configuration) template source =
Generic.all ~configuration ~template ~source
let print_matches matches =
List.map matches ~f:Match.to_yojson
|> (fun matches -> `List matches)
|> Yojson.Safe.pretty_to_string
|> print_string
let%expect_test "dont_get_stuck" =
let template = "" in
let source = "a" in
let matches = all ~configuration template source in
print_matches matches;
[%expect_exact {|[]|}]
let%expect_test "dont_get_stuck" =
let template = "a" in
let source = "a" in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 1, "line": 1, "column": 2 }
},
"environment": [],
"matched": "a"
}
]|}]
let%expect_test "dont_get_stuck" =
let template = "a" in
let source = "aaa" in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 1, "line": 1, "column": 2 }
},
"environment": [],
"matched": "a"
},
{
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 2, "line": 1, "column": 3 }
},
"environment": [],
"matched": "a"
},
{
"range": {
"start": { "offset": 2, "line": 1, "column": 3 },
"end": { "offset": 3, "line": 1, "column": 4 }
},
"environment": [],
"matched": "a"
}
]|}]
let%expect_test "rewrite_awesome_1" =
let template = "replace this :[1] end" in
let source = "xreplace this () end" in
let rewrite_template = "X" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "xX"]
let%expect_test "rewrite_whole_template_matches" =
let template = {|rewrite :[1] <- this string|} in
let source = {|rewrite hello world <- this string|} in
let rewrite_template = "?" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "?"]
let%expect_test "single_token" =
let template = {|:[[1]] this|} in
let source = {|the problem is this|} in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 12, "line": 1, "column": 13 },
"end": { "offset": 19, "line": 1, "column": 20 }
},
"environment": [
{
"variable": "1",
"value": "is",
"range": {
"start": { "offset": 12, "line": 1, "column": 13 },
"end": { "offset": 14, "line": 1, "column": 15 }
}
}
],
"matched": "is this"
}
]|}]
let%expect_test "single_token_with_preceding_whitespace" =
let template = {| :[[1]] this|} in
let source = {|the problem is this|} in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 11, "line": 1, "column": 12 },
"end": { "offset": 19, "line": 1, "column": 20 }
},
"environment": [
{
"variable": "1",
"value": "is",
"range": {
"start": { "offset": 12, "line": 1, "column": 13 },
"end": { "offset": 14, "line": 1, "column": 15 }
}
}
],
"matched": " is this"
}
]|}]
let%expect_test "single_token_rewrite" =
let template = {| :[[1]] this|} in
let source = {|the problem is this|} in
let rewrite_template = ":[1]" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "the problemis"]
let%expect_test "single_token_match_inside_paren_no_succeeding_whitespace" =
let template = {|:[[1]](:[[2]])|} in
let source = {|foo(bar)|} in
let rewrite_template = ":[1] : :[2]" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "foo : bar"]
let%expect_test "whitespace_hole_rewrite" =
let template = {|:[ w]this|} in
let rewrite_template = "space:[ w]here" in
let source = {| this|} in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "space here"]
let%expect_test "punctuation_hole_rewrite" =
let template = {|:[x.]|} in
let rewrite_template = "->:[x.]<-" in
let source = {|now.this. is,pod|racing|} in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "->now.this.<- ->is,pod|racing<-"]
let%expect_test "newline_hole_rewrite" =
let template = {|:[x\n]|} in
let rewrite_template = {|->:[x\n]<-|} in
let source = {|now.this.
is,pod|racing
|} in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact "->now.this.
<-->is,pod|racing
<-"]
let%expect_test "shift_or_at_least_dont_get_stuck" =
let template = ":[1]" in
let source = "a" in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 1, "line": 1, "column": 2 }
},
"environment": [
{
"variable": "1",
"value": "a",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 1, "line": 1, "column": 2 }
}
}
],
"matched": "a"
}
]|}]
let%expect_test "shift_or_at_least_dont_get_stuck" =
let template = ":[1]" in
let source = "aa" in
let matches = all template source in
print_matches matches;
[%expect_exact {|[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 2, "line": 1, "column": 3 }
},
"environment": [
{
"variable": "1",
"value": "aa",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 2, "line": 1, "column": 3 }
}
}
],
"matched": "aa"
}
]|}]
let%expect_test "nested_rewrite1" =
let source =
{|
x x y strcpy(strcpy(dst1, src1), src2); blah blah XXX
|}
in
let template =
{|
strcpy(:[1], :[2])
|}
in
let matches = all template source in
print_matches matches;
[%expect_exact "[]"]
(* FIXME(RVT) nested rewrites *)
let%expect_test "nested_rewrite2" =
let template =
{|
if :[var_check] != nil {
for :[defines] := range :[var_use] {:[inner_body]}
}
|}
in
let source =
{|
if fields.List != nil {
for _, field := range fields.List {
if field.Names != nil {
for _, fieldName := range field.Names {
stuff with fields and things
}
}
}
}
|}
in
let rewrite_template = "for :[defines] := range :[var_use] {:[inner_body]}" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact {|for _, field := range fields.List {
if field.Names != nil {
for _, fieldName := range field.Names {
stuff with fields and things
}
}
}|}]
let%expect_test "match_:[[1]]" =
let template =
{|
:[[1]].next()
|}
in
let source =
{|
col_names = reader.next()
}
|}
in
let rewrite_template = "next(:[1])" in
all template source
|> (fun matches -> Option.value_exn (Rewrite.all ~source ~rewrite_template matches))
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string;
[%expect_exact {|
col_names =next(reader)}
|}]
*)

View File

@ -1,698 +0,0 @@
(*open Core
open Language
open Matchers
open Match
let rule_parses rule =
match Rule.create rule with
| Ok _ -> "true"
| Error _ -> "false"
let print_matches matches =
List.map matches ~f:Match.to_yojson
|> (fun matches -> `List matches)
|> Yojson.Safe.pretty_to_string
|> print_string
let%expect_test "parse_rule" =
let rule = {| where :[1] == :[2], :[3] == "y" |} in
rule_parses rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where :[1] == :[2], :[3] != "x" |} in
rule_parses rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where :[1] != :[3] |} in
rule_parses rule |> print_string;
[%expect_exact {|true|}]
let%expect_test "parse_basic" =
Rule.create {|where "a" == "a"|}
|> Or_error.ok_exn
|> fun rule -> print_s [%message (rule : Ast.expression list)];
[%expect_exact {|(rule ((Equal (String a) (String a))))
|}]
let%expect_test "parse_match_one_case" =
Rule.create {|where match "match_me" { | "case_one" -> true }|}
|> Or_error.ok_exn
|> fun rule -> print_s [%message (rule : Ast.expression list)];
[%expect_exact "(rule ((Match (String match_me) (((String case_one) (True))))))
"]
let%expect_test "parse_match_multi_case" =
Rule.create
{| where
match "match_me" {
| "case_one" -> true
| "case_two" -> false
}
|}
|> Or_error.ok_exn
|> fun rule -> print_s [%message (rule : Ast.expression list)];
[%expect_exact "(rule
((Match (String match_me)
(((String case_one) (True)) ((String case_two) (False))))))
"]
let sat ?(env = Environment.create ()) rule =
let rule = Rule.create rule |> Or_error.ok_exn in
Format.sprintf "%b" (Rule.(sat @@ apply rule env))
let make_env bindings =
List.fold bindings
~init:(Environment.create ())
~f:(fun env (var, value) -> Environment.add env var value)
let%expect_test "rule_sat" =
let rule = {| where "x" != "y" |} in
sat rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where "x" != "x" |} in
sat rule |> print_string;
[%expect_exact {|false|}];
let rule = {| where "x" == "x" |} in
sat rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where "x" == "y" |} in
sat rule |> print_string;
[%expect_exact {|false|}];
let rule = {| where :[x] == "y" |} in
sat rule |> print_string;
[%expect_exact {|false|}];
let rule = {| where :[x] == :[x] |} in
sat rule |> print_string;
[%expect_exact {|false|}]
let%expect_test "rule_sat_with_env" =
let env = make_env ["1", "x"; "2", "y"; "3", "x"] in
let rule = {| where :[1] == :[3], :[1] != :[2] |} in
sat ~env rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where :[1] == :[3], :[1] != "y" |} in
sat ~env rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where :[1] == :[3], :[1] == "x" |} in
sat ~env rule |> print_string;
[%expect_exact {|true|}];
let rule = {| where :[1] == :[2], :[1] != :[2] |} in
sat ~env rule |> print_string;
[%expect_exact {|false|}]
let configuration = Configuration.create ~match_kind:Fuzzy ()
let format s =
let s = s |> String.chop_prefix_exn ~prefix:"\n" in
let leading_indentation =
Option.value_exn (String.lfindi s ~f:(fun _ c -> c <> ' ')) in
s
|> String.split ~on:'\n'
|> List.map ~f:(Fn.flip String.drop_prefix leading_indentation)
|> String.concat ~sep:"\n"
|> String.chop_suffix_exn ~suffix:"\n"
let%expect_test "where_true" =
let template =
{|
(:[1]) => {}
|}
|> format
in
let source =
{|
(b,c) => {}
|}
|> format
in
let rule =
{| where true
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 11, "line": 1, "column": 12 }
},
"environment": [
{
"variable": "1",
"value": "b,c",
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 4, "line": 1, "column": 5 }
}
}
],
"matched": "(b,c) => {}"
}
] |}]
let%expect_test "match_sat" =
let template =
{|
(:[1]) => {}
|}
|> format
in
let source =
{|
(b,c) => {}
|}
|> format
in
let rule =
{| where
match :[1] {
| ":[_],:[_]" -> false
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}];
let rule =
{| where
match :[1] {
| ":[_],:[_]" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 11, "line": 1, "column": 12 }
},
"environment": [
{
"variable": "1",
"value": "b,c",
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 4, "line": 1, "column": 5 }
}
}
],
"matched": "(b,c) => {}"
}
] |}];
let source =
{|
(a) => {}
(b,c) => {}
|}
|> format
in
let rule =
{| where
match :[1] {
| ":[_],:[_]" -> false
| ":[_]" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 9, "line": 1, "column": 10 }
},
"environment": [
{
"variable": "1",
"value": "a",
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 2, "line": 1, "column": 3 }
}
}
],
"matched": "(a) => {}"
}
] |}];
let rule =
{|
where
match :[1] {
| ":[_],:[_]" -> false
| ":[_]" -> :[1] == "a"
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 9, "line": 1, "column": 10 }
},
"environment": [
{
"variable": "1",
"value": "a",
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 2, "line": 1, "column": 3 }
}
}
],
"matched": "(a) => {}"
}
] |}];
let rule =
{| where
match :[1] {
| ":[_],:[_]" -> false
| ":[_]" -> :[1] == "b"
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}]
let%expect_test "match_s_suffix" =
let template = ":[1]s" in
let source = "names" in
let rule =
{| where true
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 5, "line": 1, "column": 6 }
},
"environment": [
{
"variable": "1",
"value": "name",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 4, "line": 1, "column": 5 }
}
}
],
"matched": "names"
}
] |}]
let%expect_test "match_s_suffix" =
let template = ":[1]" in
let source = "names" in
let rule =
{| where true
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 5, "line": 1, "column": 6 }
},
"environment": [
{
"variable": "1",
"value": "names",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 5, "line": 1, "column": 6 }
}
}
],
"matched": "names"
}
] |}]
let%expect_test "configuration_choice_based_on_case" =
let template = ":[1]" in
let source = "names" in
let rule =
{| where match :[1] {
| "ame" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}];
let template = ":[1]" in
let source = "names" in
let rule =
{| where match :[1] {
| "names" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 5, "line": 1, "column": 6 }
},
"environment": [
{
"variable": "1",
"value": "names",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 5, "line": 1, "column": 6 }
}
}
],
"matched": "names"
}
] |}];
let template = ":[1]" in
let source = "namesXXXXX" in
let rule =
{| where match :[1] {
| "names" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}]
let%expect_test "match_using_environment_merge" =
let template = "{:[1]}" in
let source = "{{ a : a } { a : a }}" in
let rule =
{| where match :[1] { | "{ :[x] : :[y] }" -> :[x] == :[y] }
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 21, "line": 1, "column": 22 }
},
"environment": [
{
"variable": "1",
"value": "{ a : a } { a : a }",
"range": {
"start": { "offset": 1, "line": 1, "column": 2 },
"end": { "offset": 20, "line": 1, "column": 21 }
}
}
],
"matched": "{{ a : a } { a : a }}"
}
] |}];
let template = "{:[1]}" in
let source = "{{ a : a } { a : b }}" in
let rule =
{| where match :[1] { | "{ :[x] : :[y] }" -> :[x] == :[y] }
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}]
let%expect_test "nested_matches" =
let template = ":[1]" in
let source = "{ { foo : { bar : { baz : qux } } } }" in
let rule =
{| where match :[1] {
| "{ :[foo] : :[tail1] }" -> match :[tail1] {
| "{ :[bar] : :[tail2] }" -> match :[tail2] {
| "{ baz : :[qux] }" -> :[qux] == "qux", :[bar] == "bar"
}
}
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 37, "line": 1, "column": 38 }
},
"environment": [
{
"variable": "1",
"value": "{ { foo : { bar : { baz : qux } } } }",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 37, "line": 1, "column": 38 }
}
}
],
"matched": "{ { foo : { bar : { baz : qux } } } }"
}
] |}];
let template = ":[1]" in
let source = "{ { foo : { bar : { baz : qux } } } }" in
let rule =
{| where match :[1] {
| "{ :[foo] : :[tail1] }" -> match :[tail1] {
| "{ :[bar] : :[tail2] }" -> match :[tail2] {
| "{ baz : :[qux] }" -> :[qux] == "fail"
}
}
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}]
let%expect_test "match_on_template" =
let template = ":[1]" in
let source = "oodles" in
let rule =
{| where match "p:[1]" {
| "poodles" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 6, "line": 1, "column": 7 }
},
"environment": [
{
"variable": "1",
"value": "oodles",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 6, "line": 1, "column": 7 }
}
}
],
"matched": "oodles"
}
] |}];
let template = ":[1]" in
let source = "poodle" in
let rule =
{| where match ":[1]s" {
| "poodles" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 6, "line": 1, "column": 7 }
},
"environment": [
{
"variable": "1",
"value": "poodle",
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 6, "line": 1, "column": 7 }
}
}
],
"matched": "poodle"
}
] |}];
let template = ":[1]" in
let source = "poodle" in
let rule =
{| where match ":[1]," {
| "poodle" -> true
}
|}
|> Rule.create
|> Or_error.ok_exn
in
Generic.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } -> Rule.(sat @@ apply rule environment))
|> print_matches;
[%expect {|
[] |}];
*)

View File

@ -0,0 +1,218 @@
(*open Core
open Matchers
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let run ?(configuration = configuration) source match_template rewrite_template =
Generic.all ~configuration ~template:match_template ~source
|> function
| [] -> print_string "No matches."
| results ->
Option.value_exn (Rewrite.all ~source ~rewrite_template results)
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string
let%expect_test "optional_holes_basic_match" =
let source = {||} in
let match_template = {|:[[?x]]|} in
let rewrite_template = {|/:[?x]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
let source = {||} in
let match_template = {|:[[?x]]:[[?y]]|} in
let rewrite_template = {|/:[?x]/:[?y]/|} in
run source match_template rewrite_template;
[%expect_exact {|///|}];
let source = {|a |} in
let match_template = {|:[[x]] :[[?y]]|} in
let rewrite_template = {|/:[x]/:[?y]/|} in
run source match_template rewrite_template;
[%expect_exact {|/a//|}];
let source = {|a |} in
let match_template = {|:[[x]] :[[?y]]|} in
let rewrite_template = {|/:[x]/:[?y]/|} in
run source match_template rewrite_template;
[%expect_exact {|/a//|}];
let source = {|(foo )|} in
let match_template = {|(:[[?x]] :[[?y]])|} in
let rewrite_template = {|/:[?x]/:[?y]/|} in
run source match_template rewrite_template;
[%expect_exact {|/foo//|}];
let source = {|(foo)|} in
let match_template = {|(:[[?x]]:[? w])|} in
let rewrite_template = {|/:[?x]/:[?w]/|} in
run source match_template rewrite_template;
[%expect_exact {|/foo//|}];
let source = {|()|} in
let match_template = {|(:[[?x]]:[? w]:[?y]:[?z.])|} in
let rewrite_template = {|/:[?x]/:[?w]/:[?y]|} in
run source match_template rewrite_template;
[%expect_exact {|///|}];
let source = {|()|} in
let match_template = {|(:[?s\n])|} in
let rewrite_template = {|/:[?s]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}]
let%expect_test "optional_holes_match_over_coalesced_whitespace" =
let source = {|a c|} in
let match_template = {|:[[a]] :[[?b]] :[[c]]|} in
let rewrite_template = {|/:[?a]/:[?b]/:[?c]|} in
run source match_template rewrite_template;
[%expect_exact {|/a//c|}];
let source = {|a c|} in
let match_template = {|:[[a]] :[[?b]]:[[c]]|} in
let rewrite_template = {|/:[?a]/:[?b]/:[?c]|} in
run source match_template rewrite_template;
[%expect_exact {|/a//c|}];
let source = {|a c|} in
let match_template = {|:[[a]]:[[?b]]:[[c]]|} in
let rewrite_template = {|/:[?a]/:[?b]/:[?c]|} in
run source match_template rewrite_template;
[%expect_exact {|No matches.|}];
let source = {|a c|} in
let match_template = {|:[[a]]:[[?b]] :[[?c]]|} in
let rewrite_template = {|/:[?a]/:[?b]/:[?c]|} in
run source match_template rewrite_template;
[%expect_exact {|/a//c|}];
let source = {|a c|} in
let match_template = {|a :[?b] c|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
let source = {|a c|} in
let match_template = {|a :[?b] c|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
let source = {|
a
c
|} in
let match_template = {| a :[?b] c |} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
let source = {|func foo(bar) {}|} in
let match_template = {|func :[?receiver] foo(:[args])|} in
let rewrite_template = {|/:[receiver]/:[args]/|} in
run source match_template rewrite_template;
[%expect_exact {|//bar/ {}|}];
let source = {|func foo(bar) {}|} in
let match_template = {|func :[?receiver] foo(:[args])|} in
let rewrite_template = {|/:[receiver]/:[args]/|} in
run source match_template rewrite_template;
[%expect_exact {|//bar/ {}|}];
let source = {|func (r *receiver) foo(bar) {}|} in
let match_template = {|func :[?receiver] foo(:[args])|} in
let rewrite_template = {|/:[receiver]/:[args]/|} in
run source match_template rewrite_template;
[%expect_exact {|/(r *receiver)/bar/ {}|}];
let source = {|func foo()|} in
let match_template = {|func :[?receiver] foo()|} in
let rewrite_template = {|/:[receiver]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
let source = {|a l|} in
let match_template = {|a :[?b]asdfasdfsadf|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|No matches.|}];
let source = {|func foo (1, 3)|} in
let match_template = {|func :[?receiver] foo (1, :[?args] 3)|} in
let rewrite_template = {|/:[receiver]/:[args]/|} in
run source match_template rewrite_template;
[%expect_exact {|///|}];
let source = {|
try {
foo()
} catch (Exception e) {
logger.error(e)
hey
}
|} in
let match_template = {|
catch (:[type] :[var]) {
:[?anything]
logger.:[logMethod](:[var])
:[?something]
}
|} in
let rewrite_template = {|
catch (:[type] :[var]) {
:[anything]
logger.:[logMethod]("", :[var])
:[something]
}
|} in
run source match_template rewrite_template;
[%expect_exact {|
try {
foo()
}
catch (Exception e) {
logger.error("", e)
hey
}
|}];
let source = {|<p>content</p><p attr="attr">more content</p>|} in
let match_template = {|<p:[?attrs]>|} in
let rewrite_template = {|<p{:[?attrs]}>|} in
run source match_template rewrite_template;
[%expect_exact {|<p{}>content</p><p{ attr="attr"}>more content</p>|}]
let%expect_test "optional_holes_match_over_coalesced_whitespace_in_strings" =
let source = {|"a c"|} in
let match_template = {|"a :[?b] c"|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|No matches.|}];
let source = {|"a c"|} in
let match_template = {|"a :[?b] c"|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|//|}];
(* Uh, turns out whitespace is significant inside strings, so this is correct
until it is decided otherwise *)
let source = {|"a c"|} in
let match_template = {|"a :[?b] c"|} in
let rewrite_template = {|/:[?b]/|} in
run source match_template rewrite_template;
[%expect_exact {|/ /|}]
let%expect_test "optional_holes_substitute" =
let source = {|()|} in
let match_template = {|(:[[?x]]:[? w]:[?y]:[?z.])|} in
let rewrite_template = {|/:[x]/:[w]/:[y]/:[z]|} in
run source match_template rewrite_template;
[%expect_exact {|////|}]
*)

View File

@ -1,55 +0,0 @@
(*open Core
open Matchers
let configuration = Configuration.create ~match_kind:Fuzzy ()
let print_matches matches =
List.map matches ~f:(fun matched -> `String matched.Match.matched)
|> (fun matches -> `List matches)
|> Yojson.Safe.pretty_to_string
|> print_string
let%expect_test "matched_contains_raw_literal_quotes" =
let source = {|"""blah"""|} in
let template = {|""":[[1]]"""|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"\"\"blah\"\"\"" ]|}]
let%expect_test "interpreted_string_does_not_match_raw_literal" =
let source = {|"""blah""" "blah"|} in
let template = {|":[[1]]"|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"blah\"" ]|}]
let%expect_test "interpreted_string_does_not_match_raw_literal_containing_quote" =
let source = {|"""blah""" """bl"ah""" "blah"|} in
let template = {|":[[1]]"|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"blah\"" ]|}]
let%expect_test "raw_string_matches_string_containing_quote" =
let source = {|"""bl"ah"""|} in
let template = {|""":[1]"""|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"\"\"bl\"ah\"\"\"" ]|}]
let%expect_test "invalid_raw_string_in_python_but_matches_because_ignores_after" =
let source = {|"""""""|} in
let template = {|""":[1]"""|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"\"\"\"\"\"" ]|}]
let%expect_test "raw_string_captures_escape_sequences" =
let source = {|"""\""""|} in
let template = {|""":[1]"""|} in
let matches = Python.all ~configuration ~template ~source in
print_matches matches;
[%expect_exact {|[ "\"\"\"\\\"\"\"" ]|}]
*)

View File

@ -1,127 +0,0 @@
(*open Core
open Language
open Matchers
open Match
open Rewriter
let configuration = Configuration.create ~match_kind:Fuzzy ()
let format s =
let s = String.chop_prefix_exn ~prefix:"\n" s in
let leading_indentation = Option.value_exn (String.lfindi s ~f:(fun _ c -> c <> ' ')) in
s
|> String.split ~on:'\n'
|> List.map ~f:(Fn.flip String.drop_prefix leading_indentation)
|> String.concat ~sep:"\n"
|> String.chop_suffix_exn ~suffix:"\n"
let run_rule source match_template rewrite_template rule =
Generic.first ~configuration match_template source
|> function
| Error _ -> print_string "bad"
| Ok result ->
match result with
| ({ environment; _ } as m) ->
let e = Rule.(result_env @@ apply rule environment) in
match e with
| None -> print_string "bad bad"
| Some e ->
{ m with environment = e }
|> List.return
|> Rewrite.all ~source ~rewrite_template
|> (fun x -> Option.value_exn x)
|> (fun { rewritten_source; _ } -> rewritten_source)
|> print_string
let%expect_test "rewrite_rule" =
let source = {|int|} in
let match_template = {|:[1]|} in
let rewrite_template = {|:[1]|} in
let rule =
{|
where rewrite :[1] { "int" -> "expect" }
|}
|> Rule.create
|> Or_error.ok_exn
in
run_rule source match_template rewrite_template rule;
[%expect_exact {|expect|}]
let%expect_test "sequenced_rewrite_rule" =
let source = {|{ { a : { b : { c : d } } } }|} in
let match_template = {|{ :[a] : :[rest] }|} in
let rewrite_template = {|{ :[a] : :[rest] }|} in
let rule =
{|
where
rewrite :[a] { "a" -> "qqq" },
rewrite :[rest] { "{ b : { :[other] } }" -> "{ :[other] }" }
|}
|> Rule.create
|> Or_error.ok_exn
in
run_rule source match_template rewrite_template rule;
[%expect_exact {|{ { qqq : { c : d } } }|}]
let%expect_test "rewrite_rule_for_list" =
let source = {|[1, 2, 3, 4,]|} in
let match_template = {|[:[contents]]|} in
let rewrite_template = {|[:[contents]]|} in
let rule =
{|
where rewrite :[contents] { ":[[x]]," -> ":[[x]];" }
|}
|> Rule.create
|> Or_error.ok_exn
in
run_rule source match_template rewrite_template rule;
[%expect_exact {|[1; 2; 3; 4;]|}]
let%expect_test "rewrite_rule_for_list_strip_last" =
let source = {|[1, 2, 3, 4]|} in
let match_template = {|[:[contents]]|} in
let rewrite_template = {|[:[contents]]|} in
let rule =
{|
where rewrite :[contents] { ":[x], " -> ":[x]; " }
|}
|> Rule.create
|> Or_error.ok_exn
in
run_rule source match_template rewrite_template rule;
[%expect_exact {|[1; 2; 3; 4]|}]
let%expect_test "haskell_example" =
let source = {|
(concat
[ "blah blah blah"
, "blah"
])
|} in
let match_template = {|(concat [:[contents]])|} in
let rewrite_template = {|(:[contents])|} in
let rule =
{|
where rewrite :[contents] { "," -> "++" }
|}
|> Rule.create
|> Or_error.ok_exn
in
run_rule source match_template rewrite_template rule;
[%expect_exact {|
( "blah blah blah"
++ "blah"
)
|}]
*)

View File

@ -1,205 +0,0 @@
(*open Core
open Lwt.Infix
open Match
open Server_types
let binary_path = "../../../comby-server"
let port = "9991"
let pid' = ref None
let launch port =
Unix.create_process ~prog:binary_path ~args:["-p"; port]
|> fun { pid; _ } -> pid' := Some pid
let post endpoint json =
let uri =
let uri endpoint =
Uri.of_string ("http://127.0.0.1:" ^ port ^ "/" ^ endpoint)
in
match endpoint with
| `Match -> uri "match"
| `Rewrite -> uri "rewrite"
| `Substitute -> uri "substitute"
in
let thread =
Cohttp_lwt_unix.Client.post ~body:(`String json) uri >>= fun (_, response) ->
match response with
| `Stream response -> Lwt_stream.get response >>= fun result -> Lwt.return result
| _ -> Lwt.return None
in
match Lwt_unix.run thread with
| None -> "FAIL"
| Some result -> result
(* FIXME(RVT) use wait *)
let launch () =
launch port;
Unix.sleep 2
let kill () =
match !pid' with
| None -> ()
| Some pid ->
match Signal.send Signal.kill (`Pid pid) with
| `Ok -> ()
| `No_such_process -> ()
let () = launch ()
let%expect_test "post_request" =
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where :[1] == "world"|} in
let language = "generic" in
In.{ source; match_template; rule; language; id = 0 }
|> In.match_request_to_yojson
|> Yojson.Safe.to_string
|> post `Match
|> print_string;
[%expect {|
{
"matches": [
{
"range": {
"start": { "offset": 0, "line": 1, "column": 1 },
"end": { "offset": 11, "line": 1, "column": 12 }
},
"environment": [
{
"variable": "1",
"value": "world",
"range": {
"start": { "offset": 6, "line": 1, "column": 7 },
"end": { "offset": 11, "line": 1, "column": 12 }
}
}
],
"matched": "hello world"
}
],
"source": "hello world",
"id": 0
} |}];
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where :[1] = "world"|} in
let language = "generic" in
In.{ source; match_template; rule; language; id = 0 }
|> In.match_request_to_yojson
|> Yojson.Safe.to_string
|> post `Match
|> print_string;
[%expect {|
Error in line 1, column 7:
where :[1] = "world"
^
Expecting "false", "match", "rewrite" or "true"
Backtracking occurred after:
Error in line 1, column 12:
where :[1] = "world"
^
Expecting "!=" or "==" |}];
let substitution_kind = "in_place" in
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where :[1] == "world"|} in
let rewrite_template = ":[1], hello" in
let language = "generic" in
In.{ source; match_template; rewrite_template; rule; language; substitution_kind; id = 0}
|> In.rewrite_request_to_yojson
|> Yojson.Safe.to_string
|> post `Rewrite
|> print_string;
[%expect {|
{
"rewritten_source": "world, hello",
"in_place_substitutions": [
{
"range": {
"start": { "offset": 0, "line": -1, "column": -1 },
"end": { "offset": 12, "line": -1, "column": -1 }
},
"replacement_content": "world, hello",
"environment": [
{
"variable": "1",
"value": "world",
"range": {
"start": { "offset": 0, "line": -1, "column": -1 },
"end": { "offset": 5, "line": -1, "column": -1 }
}
}
]
}
],
"id": 0
} |}];
let substitution_kind = "newline_separated" in
let source = "hello world {} hello world" in
let match_template = "hello :[[1]]" in
let rule = Some {|where :[1] == "world"|} in
let rewrite_template = ":[1], hello" in
let language = "generic" in
In.{ source; match_template; rewrite_template; rule; language; substitution_kind; id = 0}
|> In.rewrite_request_to_yojson
|> Yojson.Safe.to_string
|> post `Rewrite
|> print_string;
[%expect {|
{
"rewritten_source": "world, hello\nworld, hello",
"in_place_substitutions": [],
"id": 0
} |}];
(* test there must be at least one predicate in a rule *)
let source = "hello world" in
let match_template = "hello :[1]" in
let rule = Some {|where |} in
let language = "generic" in
let request = In.{ source; match_template; rule; language; id = 0 } in
let json = In.match_request_to_yojson request |> Yojson.Safe.to_string in
let result = post `Match json in
print_string result;
[%expect {|
Error in line 1, column 7:
where
^
Expecting ":[", "false", "match", "rewrite", "true" or string literal |}]
let%expect_test "post_substitute" =
let rewrite_template = ":[1] hi :[2]" in
let environment = Environment.create () in
let environment = Environment.add environment "1" "oh" in
let environment = Environment.add environment "2" "there" in
In.{ rewrite_template; environment; id = 0 }
|> In.substitution_request_to_yojson
|> Yojson.Safe.to_string
|> post `Substitute
|> print_string;
[%expect {| { "result": "oh hi there", "id": 0 } |}]
let () = kill ()
*)

View File

@ -1,88 +0,0 @@
(*open Core
open Language
open Matchers
open Match
let configuration = Configuration.create ~match_kind:Fuzzy ()
let format s =
let s = s |> String.chop_prefix_exn ~prefix:"\n" in
let leading_indentation =
Option.value_exn (String.lfindi s ~f:(fun _ c -> c <> ' ')) in
s
|> String.split ~on:'\n'
|> List.map ~f:(Fn.flip String.drop_prefix leading_indentation)
|> String.concat ~sep:"\n"
|> String.chop_suffix_exn ~suffix:"\n"
let %expect_test "statistics" =
let template =
{|
def :[fn_name](:[fn_params])
|}
|> format
in
let source =
{|
def foo(bar):
pass
def bar(bazz):
pass
|}
|> format
in
let rule =
{| where true
|}
|> Rule.create
|> Or_error.ok_exn
in
Go.all ~configuration ~template ~source
|> List.filter ~f:(fun { environment; _ } ->
Rule.(sat @@ apply rule environment))
|> fun matches ->
let statistics =
Statistics.
{ number_of_files = 1
; lines_of_code = 5
; number_of_matches = List.length matches
; total_time = 0.0
}
in
statistics
|> Statistics.to_yojson
|> Yojson.Safe.pretty_to_string
|> print_string;
[%expect {|
{
"number_of_files": 1,
"lines_of_code": 5,
"number_of_matches": 2,
"total_time": 0.0
} |}];
let statistics' =
Statistics.merge
{ number_of_files = 1
; lines_of_code = 10
; number_of_matches = 1
; total_time = 1.5
}
statistics
in
statistics'
|> Statistics.to_yojson
|> Yojson.Safe.pretty_to_string
|> print_string;
[%expect {|
{
"number_of_files": 2,
"lines_of_code": 15,
"number_of_matches": 3,
"total_time": 1.5
} |}]
*)

View File

@ -1,4 +1,4 @@
(*open Core
open Core
open Matchers
open Rewriter
@ -410,4 +410,3 @@ let%expect_test "match_escaped_escaped" =
| Some { rewritten_source; _ } -> print_string rewritten_source
| None -> print_string "EXPECT SUCCESS");
[%expect_exact {|EXPECT SUCCESS|}]
*)