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
(* Suggestions *)
open Mo_types
open Mo_config
open Type
let oneof sep lastsep ss =
let rest, last = Lib.List.split_last ss in
((if rest <> [] then (String.concat sep rest) ^ lastsep else "") ^ last)
let suggest_id desc id ids =
if !Flags.ai_errors then
Printf.sprintf
"\nThe %s %s is not available. Try something else?"
desc
id
else
let suggestions =
let limit = Lib.Int.log2 (String.length id) in
let distance = Lib.String.levenshtein_distance id in
let weighted_ids = List.filter_map (fun id0 ->
let d = distance id0 in
if Lib.String.starts_with id id0 || d <= limit then
Some (d, id0)
else None) ids in
List.sort compare weighted_ids |> List.map snd
in
if suggestions = [] then ""
else
Printf.sprintf "\nDid you mean %s %s?" desc (oneof ", " " or " suggestions)
let search_obj desc path ty ty1 ty2 =
let suggestions = ref [] in
let seen = ref S.empty in
let rec go path ty =
if S.mem ty !seen then ()
else begin
seen := S.add ty (!seen);
match promote ty with
| Obj(_, tfs) ->
tfs |>
List.iter (fun {lab;typ;_} ->
match normalize typ with
| Func _ when
(Lib.String.starts_with "to" lab ||
Lib.String.starts_with "from" lab) &&
sub typ (Func(Local, Returns, [], [ty1], [ty2])) ->
suggestions := Printf.sprintf "`%s.%s(_)`%s" path lab desc :: !suggestions
| Obj(_, tfs) as ty1 ->
go (path^"."^lab) ty1
| _ -> ())
| _ -> ()
end
in
go path ty;
!suggestions
let suggest_conversion libs vals ty1 ty2 =
match promote ty1, promote ty2 with
| Prim p1, Prim p2 ->
let suggestions = ref [] in
Env.iter (fun filename ty ->
if Lib.String.starts_with "@" filename
then () (* skip prim etc *)
else
let imported_name =
(* try to determine imported name, if any *)
Env.fold (fun id (ty1, _, _, _) acc ->
if ty == ty1 (*HACK*)
then Some id
else acc)
vals None
in
let lib_opt = match imported_name with
| Some id -> Some (id, "")
| None ->
(* search libs for suggested import *)
Flags.M.fold (fun package path acc ->
let base = Lib.FilePath.normalise path in
match Lib.FilePath.relative_to base filename with
| None -> acc
| Some rel_path ->
let rel_name = Filename.chop_extension rel_path in
let id = Filename.basename rel_name in
Some (
id,
Printf.sprintf " after adding `import %s = \"mo:%s/%s\"`" id package rel_name))
!Flags.package_urls None
in
match lib_opt with
| None -> ()
| Some (id, desc) ->
suggestions := (search_obj desc id ty ty1 ty2) @ !suggestions)
libs;
if !suggestions = []
then ""
else
Printf.sprintf "\nMaybe try conversion:\n %s?"
(oneof ",\n " " or\n " !suggestions)
(* not primitive types, make no suggestion *)
| _, _ -> ""