package comby-kernel

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file alpha_rule.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
open Core_kernel
open MParser
open MParser_PCRE.Tokens

open Match
open Rewriter

open Ast
open Alpha_parser

module Configuration = Matchers.Configuration

type t = Ast.t

type result = bool * environment option

type options = Options.t

let options = Options.of_rule

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 counter =
  let uuid_for_id_counter = ref 0 in
  fun () ->
    uuid_for_id_counter := !uuid_for_id_counter + 1;
    Format.sprintf "gu3ssme_%012d" !uuid_for_id_counter

let rec apply
    ?(matcher = (module Matchers.Alpha.Generic : Matchers.Matcher.S))
    ?(substitute_in_place = true)
    ?(fresh = counter)
    predicates
    env =
  let open Option in
  let (module Matcher) = 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
    | Option _ -> true, 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 = fresh () 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 create rule =
  let operator_parser =
    spaces >> atom_parser >>= fun left ->
    spaces >> operator_parser >>= fun operator ->
    spaces >> atom_parser << spaces >>= 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 option_parser = spaces >> string Syntax.option_nested << spaces |>> fun _ -> Option "nested" in
  let rec expression_parser s =
    choice
      [ match_pattern_parser
      ; rewrite_pattern_parser
      (* string literals are ambiguous, so attempt to parse operator first *)
      ; attempt operator_parser
      ; true'
      ; false'
      ; option_parser
      ]
      s
  and match_pattern_parser s =
    let case_parser : (atom * expression list, unit) parser =
      spaces >> string Syntax.pipe_operator >>
      spaces >> atom_parser << spaces << string Syntax.arrow << spaces >>= fun antecedent ->
      spaces >> comma_sep expression_parser << spaces |>> fun consequent ->
      antecedent, consequent
    in
    let match_pattern =
      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
    match_pattern s
  and rewrite_pattern_parser s =
    let rewrite_pattern =
      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 << char '}' << spaces
      |>> fun rewrite_template ->
      Rewrite (atom, (match_template, rewrite_template))
    in
    rewrite_pattern s
  in
  let rule_parser s =
    (spaces
     >> string Syntax.rule_prefix
     >> spaces1
     >> comma_sep1 expression_parser
        << eof)
      s
  in
  match parse_string rule_parser rule () with
  | Success rule -> Ok rule
  | Failed (error, _) -> Or_error.error_string error
OCaml

Innovation. Community. Security.