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
module type Arg = Dolmen_intf.Id.Escape
let smap f s =
let b = Buffer.create (String.length s) in
let rec aux i =
if i >= String.length s then
Buffer.contents b
else begin
Buffer.add_string b (f (i + 1) s.[i]);
aux (i + 1)
end
in
aux 0
let get_num ~sep s =
let rec aux acc mult i =
if i < 0 then s, 0
else match s.[i] with
| ('0' .. '9') as c ->
let j = int_of_char c - 48 in
aux (acc + j * mult) (mult * 10) (i - 1)
| c when c = sep ->
if i = 0 then s, 0
else String.sub s 0 i, acc
| _ -> s, 0
in
aux 0 1 (String.length s - 1)
let rename ~sep s =
let base, n = get_num ~sep s in
Format.sprintf "%s%c%d" base sep (n + 1)
type status =
| Same
| Escaped
| Renamed
module Make(Id : Arg) = struct
module H = Hashtbl.Make(Id)
type t = {
lang : string;
name : Id.t -> string;
escape : string -> string;
rename : string -> string;
mutable table : (status * string) H.t;
mutable names : (string, Id.t) Hashtbl.t;
}
let mk ~lang ~name ~escape ~rename = {
lang; name;
escape; rename;
table = H.create 1013;
names = Hashtbl.create 1013;
}
let flush t =
Hashtbl.reset t.names;
H.reset t.table;
()
let rec add t id status name =
match Hashtbl.find t.names name with
| exception Not_found ->
add_success t id status name
| r ->
assert (not (Id.equal id r));
add_failure t id name
and add_success t any status name =
let () = H.add t.table any (status, name) in
let () = Hashtbl.add t.names name any in
name
and add_failure t id name =
let new_name = t.rename name in
assert (new_name <> name);
add t id Renamed new_name
let escape t id =
match H.find t.table id with
| (_, s) -> s
| exception Not_found ->
let name = t.name id in
let escaped = t.escape (t.name id) in
let status, new_name =
if (escaped = name)
then Same, name
else Escaped, escaped
in
add t id status new_name
let print t fmt id =
Format.fprintf fmt "%s" (escape t id)
end