package b0

  1. Overview
  2. Docs
Software construction and deployment kit

Install

dune-project
 Dependency

Authors

Maintainers

Sources

b0-0.0.6.tbz
sha512=e9aa779e66c08fc763019f16d4706f465d16c05d6400b58fbd0313317ef33ddea51952e2b058db28e65f7ddb7012f328c8bf02d8f1da17bb543348541a2587f0

doc/src/b0.file/b0_def.ml.html

Source file b0_def.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
(*---------------------------------------------------------------------------
   Copyright (c) 2020 The b0 programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

open B0_std

(* Names *)

type t =
  { scope : B0_scope.t;
    name : string;
    basename : string;
    doc : string;
    meta : B0_meta.t }

type def = t
let scope d = d.scope
let file d = B0_scope.file d.scope
let scope_dir d = B0_scope.dir d.scope
let name d = d.name
let basename d = d.basename
let doc d = d.doc
let meta d = d.meta

(* Defining values *)

module type VALUE = sig
  type t
  val def_kind : string
  val def : t -> def
  val pp_name_str : string Fmt.t
end

module type S = sig
  val mangle_basename : string -> string
  type t
  val define : ?doc:string -> ?meta:B0_meta.t -> string -> def
  val def_kind : string
  val def : t -> def
  val name : t -> string
  val basename : t -> string
  val doc : t -> string
  val equal : t -> t -> bool
  val compare : t -> t -> int
  val meta : t -> B0_meta.t
  val mem_meta : 'a B0_meta.key -> t -> bool
  val has_tag : bool B0_meta.key -> t -> bool
  val find_meta : 'a B0_meta.key -> t -> 'a option
  val find_or_default_meta : 'a B0_meta.key -> t -> 'a
  val get_meta : 'a B0_meta.key -> t -> ('a, string) result
  val add : t -> unit
  val fold : (t -> 'a -> 'a) -> 'a -> 'a
  val list : unit -> t list
  val find : string -> t option
  val get : string -> t
  val get_or_suggest : string -> (t, t list) result
  val get_or_hint : string -> (t, string) result
  val get_list_or_hint :
    all_if_empty:bool -> string list -> (t list, string) result

  val scope_path : t -> string list
  val in_root_scope : t -> bool
  val in_current_scope : t -> bool
  val scope_dir : t -> Fpath.t option
  val scope_dir' : t -> (Fpath.t, string) result
  val in_scope_dir : t -> Fpath.t -> Fpath.t option
  val in_scope_dir' : t -> Fpath.t -> (Fpath.t, string) result

  val pp_name_str : string Fmt.t
  val pp_name : t Fmt.t
  val pp_doc : t Fmt.t
  val pp_synopsis : t Fmt.t
  val pp : t Fmt.t
  module Set : Set.S with type elt = t
  module Map : Map.S with type key = t
end

module Make (V : VALUE) = struct
  let mangle_basename s =
    try
      for i = 0 to String.length s - 1
      do if s.[i] = B0_scope.sep.[0] then raise Exit; done;
      s
    with
    | Exit -> String.map (function '.' -> '-' | c -> c) s

  type t = V.t
  let def_kind = V.def_kind
  let def = V.def
  let name v = name (V.def v)
  let basename v = basename (V.def v)
  let doc v = doc (V.def v)
  let scope v = scope (V.def v)
  let equal v0 v1 = String.equal (name v0) (name v1)
  let compare v0 v1 = String.compare (name v0) (name v1)

  let meta v = meta (V.def v)
  let mem_meta k v = B0_meta.mem k (meta v)
  let has_tag k v = B0_meta.has_tag k (meta v)
  let find_meta k v = B0_meta.find k (meta v)
  let find_or_default_meta k v = B0_meta.find_or_default k (meta v)
  let get_meta k v = match find_meta k v with
  | Some v -> Ok v
  | None ->
      Fmt.error "%s %a does not define metadata %a"
        (String.Ascii.capitalize V.def_kind)
        V.pp_name_str (name v) B0_meta.Key.pp_name k

  let pp_name_str = V.pp_name_str
  let pp_name = Fmt.using name pp_name_str
  let pp_doc = Fmt.using doc (Fmt.st [])
  let pp_synopsis ppf v = Fmt.pf ppf "%a  %a" pp_name v pp_doc v
  let pp ppf v =
    let pp_non_empty ppf m = match B0_meta.is_empty m with
    | true -> () | false -> Fmt.pf ppf "@, %a" B0_meta.pp m in
    Fmt.pf ppf "@[<v>@[%a@]%a@]" pp_synopsis v pp_non_empty (meta v)

  let defs = ref String.Map.empty
  let add v = defs := String.Map.add (name v) v !defs

  let raise_error_undefined name =
    B0_scope.raise_error "%s %a undefined in scope."
      (String.Ascii.capitalize V.def_kind) V.pp_name_str name

  let define ?(doc = "undocumented") ?(meta = B0_meta.empty) name =
    (* XXX with Printexc.get_callstack and a bit of munging
       we could maybe get the exact definition point. *)
    let kind = V.def_kind in
    let qname, scope =
      B0_scope.current_make_unique_qualified_name ~defs:!defs ~kind name
    in
    { scope; name = qname; basename = name; doc; meta }

  let scoped_find name =
    String.Map.find_opt
      (B0_scope.qualify_name_in_current ~kind:V.def_kind name) !defs

  let find = scoped_find
  let get name = match scoped_find name with
  | Some v -> v | None -> raise_error_undefined name

  let get_or_suggest name = match scoped_find name with
  | Some v -> Ok v
  | None ->
      let add_sugg k v acc =
        if String.edit_distance k name <= 2 then v :: acc else acc
      in
      Error (List.rev (String.Map.fold add_sugg !defs []))

  let get_or_hint candidate = match get_or_suggest candidate with
  | Ok _ as v -> v
  | Error suggs ->
      let kind ppf () = Fmt.pf ppf "%s" def_kind in
      let hint = Fmt.did_you_mean in
      let pp = Fmt.unknown' ~kind V.pp_name_str ~hint in
      Fmt.error "@[%a@]" pp (candidate, List.map name suggs)

  let fold f acc = match B0_scope.current_is_root () with
  | true ->
      let add _ v acc = f v acc in
      String.Map.fold add !defs acc
  | false ->
      let prefix = B0_scope.current_scope_prefix () in
      let add k v acc = if String.starts_with ~prefix k then f v acc else acc in
      String.Map.fold add !defs acc

  let list () = List.rev (fold List.cons [])

  let get_list_or_hint ~all_if_empty names =
    if all_if_empty && names = [] then Ok (List.sort compare (list ())) else
    let rec loop vs es = function
    | [] ->
        if es <> []
        then Error (String.concat "\n" (List.rev es))
        else Ok (List.rev vs)
    | n :: ns ->
        match get_or_hint n with
        | Ok v -> loop (v :: vs) es ns
        | Error e -> loop vs (e :: es) ns
    in
    loop [] [] names

  let scope_path v = B0_scope.path (scope v)
  let in_root_scope v = B0_scope.is_root (scope v)
  let in_current_scope v = B0_scope.is_current (scope v)

  let scope_dir v = scope_dir (def v)
  let scope_dir' v = match scope_dir v with
  | None -> Fmt.error "%s %a has no scope directory." def_kind pp_name v
  | Some dir -> Ok dir

  let in_scope_dir v path = match scope_dir v with
  | None -> None | Some dir -> Some Fpath.(dir // path)

  let in_scope_dir' v path = match in_scope_dir v path with
  | Some v -> Ok v
  | None ->
      Fmt.error "%s %a has no scope directory, cannot lookup %a in it."
        def_kind pp_name v Fpath.pp path

  module T = struct type nonrec t = t let compare = compare end
  module Set = Set.Make(T)
  module Map = Map.Make(T)
end

type value = V : (module S with type t = 'a) * 'a -> value