package dedukti

  1. Overview
  2. Docs

Source file confluence.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
open Format
open Basic
open Term
open Rule

let d_confluence = Debug.register_flag "Confluence"

let pp_name fmt cst = fprintf fmt "%a_%a" pp_mident (md cst) pp_ident (id cst)

type confluence_error =
  | NotConfluent of string
  | MaybeConfluent of string
  | CCFailure of string

exception Confluence_error of confluence_error

module IdMap = Map.Make (struct
  type t = ident

  let compare x y = String.compare (string_of_ident x) (string_of_ident y)
end)

let confluence_command = ref ""

let do_not_erase_confluence_file = ref false

let file_out = ref None

let try_out f = match !file_out with None -> () | Some x -> f x

let set_cmd cmd =
  if not (Sys.file_exists cmd) then
    raise (Sys_error ("'" ^ cmd ^ "' does not exist"))
  else confluence_command := cmd

let initialize () =
  do_not_erase_confluence_file := false;
  if String.compare !confluence_command "" == 0 then file_out := None
  else
    let file, out = Filename.open_temp_file "dkcheck" ".trs" in
    let fmt = formatter_of_out_channel out in
    Debug.(debug d_confluence "Temporary file:%s" file);
    file_out := Some (file, out);
    fprintf fmt
      "(FUN\n\
      \  lam : term -> (term -> term) -> term\n\
      \  app : term -> term -> term\n\
      \  pi  : term -> (term -> term) -> term\n\
      \  type : term\n\
       )\n\n\
       (COMMENT beta-reduction)\n\
       (VAR\n\
      \  v_x : term\n\
      \  m_typ : term\n\
      \  m_B : term\n\
      \  m_F : term -> term\n\
       )\n\
       (RULES\n\
      \  app( lam(m_typ,\\v_x. m_F v_x), m_B) -> m_F(m_B)\n\
       )@."

let print_name fmt cst =
  fprintf fmt "%a_%a" pp_mident (md cst) pp_ident (id cst)

let pp_pattern ar fmt pat =
  let nb = ref 0 in
  let rec aux k fmt = function
    | Var (_, x, n, args) when n < k ->
        List.iter (fun _ -> fprintf fmt "app(") args;
        fprintf fmt "v_%a" pp_ident x;
        List.iter (fun pat -> fprintf fmt ",%a)" (aux 0) pat) args
    | Pattern (_, cst, args) ->
        List.iter (fun _ -> fprintf fmt "app(") args;
        fprintf fmt "c_%a" print_name cst;
        List.iter (fun pat -> fprintf fmt ",%a)" (aux k) pat) args
    | Var (_, x, _, []) (* n>=k *) -> fprintf fmt "m_%a" pp_ident x
    | Var (_, x, _, a :: args) (* n>=k *) ->
        let arity = IdMap.find x ar in
        if arity == 0 then (
          List.iter (fun _ -> fprintf fmt "app(") (a :: args);
          fprintf fmt "m_%a" pp_ident x;
          List.iter (fprintf fmt ",%a)" (aux k)) (a :: args))
        else
          let args1, args2 = split (arity - 1) args in
          List.iter (fun _ -> fprintf fmt "app(") args2;
          fprintf fmt "m_%a(%a" pp_ident x (aux k) a;
          List.iter (fprintf fmt ",%a" (aux k)) args1;
          fprintf fmt ")";
          List.iter (fprintf fmt ",%a)" (aux k)) args2
    | Lambda (_, x, p) ->
        fprintf fmt "lam(m_typ,\\v_%a.%a)" pp_ident x (aux (k + 1)) p
    | Brackets _ -> incr nb; fprintf fmt "b_%i" !nb
  in
  aux 0 fmt pat

let rec pp_term (ar : int IdMap.t) k fmt term =
  match term with
  | Const (_, cst) -> fprintf fmt "c_%a" print_name cst
  | Lam (_, x, Some a, b) ->
      fprintf fmt "lam(%a,\\v_%a.%a)" (pp_term ar k) a pp_ident x
        (pp_term ar (k + 1))
        b
  | Lam (_, _, None, _) ->
      failwith
        "Not implemented: TPDB export for non-annotated abstractions." (*FIXME*)
  | Pi (_, x, a, b) ->
      fprintf fmt "pi(%a,\\v_%a.%a)" (pp_term ar k) a pp_ident x
        (pp_term ar (k + 1))
        b
  | DB (_, x, n) when n < k -> fprintf fmt "v_%a" pp_ident x
  | DB (_, x, _) -> fprintf fmt "m_%a" pp_ident x
  | App (DB (_, x, n), a, args) when n >= k ->
      let arity = IdMap.find x ar in
      if arity == 0 then (
        List.iter (fun _ -> fprintf fmt "app(") (a :: args);
        fprintf fmt "m_%a" pp_ident x;
        List.iter (fprintf fmt ",%a)" (pp_term ar k)) (a :: args))
      else
        let args1, args2 = split (arity - 1) args in
        List.iter (fun _ -> fprintf fmt "app(") args2;
        fprintf fmt "m_%a(%a" pp_ident x (pp_term ar k) a;
        List.iter (fprintf fmt ",%a" (pp_term ar k)) args1;
        fprintf fmt ")";
        List.iter (fprintf fmt ",%a)" (pp_term ar k)) args2
  | App (f, a, args) ->
      List.iter (fun _ -> fprintf fmt "app(") (a :: args);
      pp_term ar k fmt f;
      List.iter (fprintf fmt ",%a)" (pp_term ar k)) (a :: args)
  | Type _ -> fprintf fmt "type"
  | Kind -> assert false

let get_bvars r =
  let pat = pattern_of_rule_infos r in
  let rec aux_t k bvars = function
    | Const _ | Kind | Type _ | DB _ -> bvars
    | Lam (_, _, None, _) ->
        failwith "Not implemented: TPDB export for non-annotated abstractions."
        (*FIXME*)
    | Lam (_, x, Some a, b) | Pi (_, x, a, b) ->
        let bvars2 = aux_t k bvars a in
        aux_t (k + 1) (x :: bvars2) b
    | App (f, a, args) -> List.fold_left (aux_t k) bvars (f :: a :: args)
  in
  let rec aux_p k bvars = function
    | Var (_, _, _, args) | Pattern (_, _, args) ->
        List.fold_left (aux_p k) bvars args
    | Lambda (_, x, p) -> aux_p (k + 1) (x :: bvars) p
    | Brackets te -> aux_t k bvars te
  in
  let bvars0 = aux_p 0 [] pat in
  aux_t 0 bvars0 r.rhs

let get_arities (p : pattern) : int IdMap.t =
  let rec aux k map = function
    | Var (_, _, n, args) when n < k -> List.fold_left (aux k) map args
    | Pattern (_, _, args) -> List.fold_left (aux k) map args
    | Var (_, x, _, args) (* n>=k *) ->
        let map2 = List.fold_left (aux k) map args in
        let ar1 = List.length args in
        let ar =
          try
            let ar2 = IdMap.find x map2 in
            if ar2 < ar1 then ar2 else ar1
          with Not_found -> ar1
        in
        IdMap.add x ar map2
    | Lambda (_, _, p) -> aux (k + 1) map p
    | Brackets _ -> map
  in
  aux 0 IdMap.empty p

let pp_rule fmt (r : rule_infos) =
  let rec pp_type fmt n =
    fprintf fmt "term";
    if n > 0 then (
      fprintf fmt " -> ";
      pp_type fmt (n - 1))
  in
  let pat = pattern_of_rule_infos r in
  let arities = get_arities pat in
  (* Variables*)
  fprintf fmt "(VAR\n";
  IdMap.iter
    (fun x n -> fprintf fmt "  m_%a : %a\n" pp_ident x pp_type n)
    arities;
  List.iter (fun x -> fprintf fmt "  v_%a : term\n" pp_ident x) (get_bvars r);
  List.iteri (fun i _ -> fprintf fmt "  b_%i : term\n" (i + 1)) r.constraints;
  fprintf fmt ")@.";
  (* Rule *)
  fprintf fmt "(RULES %a -> %a )@.@." (pp_pattern arities) pat
    (pp_term arities 0) r.rhs

let check () =
  match !file_out with
  | None -> ()
  | Some (file, out) ->
      flush out;
      let cmd = !confluence_command ^ " -p " ^ file in
      Debug.(debug d_confluence "Checking confluence : %s" cmd);
      let input = Unix.open_process_in cmd in
      let answer =
        try
          let answer = input_line input in
          let _ = Unix.close_process_in input in
          answer
        with End_of_file -> raise (Confluence_error (CCFailure cmd))
      in
      if String.compare answer "YES" != 0 then (
        do_not_erase_confluence_file := true;
        let error =
          match answer with
          | "NO" -> NotConfluent cmd
          | "MAYBE" -> MaybeConfluent cmd
          | _ -> CCFailure cmd
        in
        raise (Confluence_error error))

let add_constant cst =
  try_out (fun (_, out) ->
      let fmt = formatter_of_out_channel out in
      fprintf fmt "(FUN c_%a : term)@." pp_name cst)

let add_rules lst =
  try_out (fun (_, out) ->
      let fmt = formatter_of_out_channel out in
      List.iter (pp_rule fmt) lst)

let finalize () =
  try_out (fun (file, out) ->
      close_out out;
      if not !do_not_erase_confluence_file then Sys.remove file)
OCaml

Innovation. Community. Security.