1(**************************************************************************) 2(* *) 3(* OCaml *) 4(* *) 5(* Maxence Guesdon, projet Cristal, INRIA Rocquencourt *) 6(* *) 7(* Copyright 2001 Institut National de Recherche en Informatique et *) 8(* en Automatique. *) 9(* *) 10(* All rights reserved. This file is distributed under the terms of *) 11(* the GNU Lesser General Public License version 2.1, with the *) 12(* special exception on linking described in the file LICENSE. *) 13(* *) 14(**************************************************************************) 15 16let no_blanks s = 17 let len = String.length s in 18 let buf = Buffer.create len in 19 for i = 0 to len - 1 do 20 match s.[i] with 21 ' ' | '\n' | '\t' | '\r' -> () 22 | c -> Buffer.add_char buf c 23 done; 24 Buffer.contents buf 25 26let input_file_as_string nom = 27 let chanin = open_in_bin nom in 28 let len = 1024 in 29 let s = Bytes.create len in 30 let buf = Buffer.create len in 31 let rec iter () = 32 try 33 let n = input chanin s 0 len in 34 if n = 0 then 35 () 36 else 37 ( 38 Buffer.add_subbytes buf s 0 n; 39 iter () 40 ) 41 with 42 End_of_file -> () 43 in 44 iter (); 45 close_in chanin; 46 Buffer.contents buf 47 48let split_string s chars = 49 let len = String.length s in 50 let rec iter acc pos = 51 if pos >= len then 52 match acc with 53 "" -> [] 54 | _ -> [acc] 55 else 56 if List.mem s.[pos] chars then 57 match acc with 58 "" -> iter "" (pos + 1) 59 | _ -> acc :: (iter "" (pos + 1)) 60 else 61 iter (Printf.sprintf "%s%c" acc s.[pos]) (pos + 1) 62 in 63 iter "" 0 64 65let split_with_blanks s = split_string s [' ' ; '\n' ; '\r' ; '\t' ] 66 67let list_concat sep = 68 let rec iter = function 69 [] -> [] 70 | [h] -> [h] 71 | h :: q -> h :: sep :: iter q 72 in 73 iter 74 75let rec string_of_longident li = 76 match li with 77 | Longident.Lident s -> s 78 | Longident.Ldot(li, s) -> string_of_longident li ^ "." ^ s 79 | Longident.Lapply(l1, l2) -> 80 string_of_longident l1 ^ "(" ^ string_of_longident l2 ^ ")" 81 82let get_fields type_expr = 83 let (fields, _) = Ctype.flatten_fields (Ctype.object_fields type_expr) in 84 List.fold_left 85 (fun acc -> fun (label, field_kind, typ) -> 86 match field_kind with 87 Types.Fabsent -> 88 acc 89 | _ -> 90 if label = "*dummy method*" then 91 acc 92 else 93 acc @ [label, typ] 94 ) 95 [] 96 fields 97 98let rec string_of_text t = 99 let rec iter t_ele = 100 match t_ele with 101 | Odoc_types.Raw s 102 | Odoc_types.Code s 103 | Odoc_types.CodePre s 104 | Odoc_types.Verbatim s -> s 105 | Odoc_types.Bold t 106 | Odoc_types.Italic t 107 | Odoc_types.Center t 108 | Odoc_types.Left t 109 | Odoc_types.Right t 110 | Odoc_types.Emphasize t -> string_of_text t 111 | Odoc_types.List l -> 112 (String.concat "" 113 (List.map (fun t -> "\n- "^(string_of_text t)) l))^ 114 "\n" 115 | Odoc_types.Enum l -> 116 let rec f n = function 117 [] -> "\n" 118 | t :: q -> 119 "\n"^(string_of_int n)^". "^(string_of_text t)^ 120 (f (n + 1) q) 121 in 122 f 1 l 123 | Odoc_types.Newline -> "\n" 124 | Odoc_types.Block t -> "\t"^(string_of_text t)^"\n" 125 | Odoc_types.Title (_, _, t) -> "\n"^(string_of_text t)^"\n" 126 | Odoc_types.Latex s -> "{% "^s^" %}" 127 | Odoc_types.Link (s, t) -> 128 "["^s^"]"^(string_of_text t) 129 | Odoc_types.Ref (_name, _, Some text) -> 130 Printf.sprintf "[%s]" (string_of_text text) 131 | Odoc_types.Ref (name, _, None) -> 132 iter (Odoc_types.Code name) 133 | Odoc_types.Superscript t -> 134 "^{"^(string_of_text t)^"}" 135 | Odoc_types.Subscript t -> 136 "^{"^(string_of_text t)^"}" 137 | Odoc_types.Module_list l -> 138 string_of_text 139 (list_concat (Odoc_types.Raw ", ") 140 (List.map (fun s -> Odoc_types.Code s) l) 141 ) 142 | Odoc_types.Index_list -> 143 "" 144 | Odoc_types.Custom (_, t) -> string_of_text t 145 | Odoc_types.Target _ -> "" 146 in 147 String.concat "" (List.map iter t) 148 149let string_of_author_list l = 150 match l with 151 [] -> 152 "" 153 | _ -> 154 "* "^Odoc_messages.authors^":\n"^ 155 (String.concat ", " l)^ 156 "\n" 157 158let string_of_version_opt v_opt = 159 match v_opt with 160 None -> "" 161 | Some v -> Odoc_messages.version^": "^v^"\n" 162 163let string_of_since_opt s_opt = 164 match s_opt with 165 None -> "" 166 | Some s -> Odoc_messages.since^" "^s^"\n" 167 168let string_of_raised_exceptions l = 169 match l with 170 [] -> "" 171 | (s, t) :: [] -> Odoc_messages.raises^" "^s^" "^(string_of_text t)^"\n" 172 | _ -> 173 Odoc_messages.raises^"\n"^ 174 (String.concat "" 175 (List.map 176 (fun (ex, desc) -> "- "^ex^" "^(string_of_text desc)^"\n") 177 l 178 ) 179 )^"\n" 180 181let string_of_see (see_ref, t) = 182 let t_ref = 183 match see_ref with 184 Odoc_types.See_url s -> [ Odoc_types.Link (s, t) ] 185 | Odoc_types.See_file s -> (Odoc_types.Code s) :: (Odoc_types.Raw " ") :: t 186 | Odoc_types.See_doc s -> (Odoc_types.Italic [Odoc_types.Raw s]) :: (Odoc_types.Raw " ") :: t 187 in 188 string_of_text t_ref 189 190let string_of_sees l = 191 match l with 192 [] -> "" 193 | see :: [] -> Odoc_messages.see_also^" "^(string_of_see see)^" \n" 194 | _ -> 195 Odoc_messages.see_also^"\n"^ 196 (String.concat "" 197 (List.map 198 (fun see -> "- "^(string_of_see see)^"\n") 199 l 200 ) 201 )^"\n" 202 203let string_of_return_opt return_opt = 204 match return_opt with 205 None -> "" 206 | Some s -> Odoc_messages.returns^" "^(string_of_text s)^"\n" 207 208let string_of_info i = 209 let module M = Odoc_types in 210 (match i.M.i_deprecated with 211 None -> "" 212 | Some d -> Odoc_messages.deprecated^"! "^(string_of_text d)^"\n")^ 213 (match i.M.i_desc with 214 None -> "" 215 | Some d when d = [Odoc_types.Raw ""] -> "" 216 | Some d -> (string_of_text d)^"\n" 217 )^ 218 (string_of_author_list i.M.i_authors)^ 219 (string_of_version_opt i.M.i_version)^ 220 (string_of_since_opt i.M.i_since)^ 221 (string_of_raised_exceptions i.M.i_raised_exceptions)^ 222 (string_of_return_opt i.M.i_return_value) 223 224let apply_opt f v_opt = 225 match v_opt with 226 None -> None 227 | Some v -> Some (f v) 228 229let string_of_date ?(absolute=false) ?(hour=true) d = 230 let add_0 s = if String.length s < 2 then "0"^s else s in 231 let t = (if absolute then Unix.gmtime else Unix.localtime) d in 232 (string_of_int (t.Unix.tm_year + 1900))^"-"^ 233 (add_0 (string_of_int (t.Unix.tm_mon + 1)))^"-"^ 234 (add_0 (string_of_int t.Unix.tm_mday))^ 235 ( 236 if hour then 237 " "^ 238 (add_0 (string_of_int t.Unix.tm_hour))^":"^ 239 (add_0 (string_of_int t.Unix.tm_min)) 240 else 241 "" 242 ) 243 244let current_date = 245 let time = 246 try 247 float_of_string (Sys.getenv "SOURCE_DATE_EPOCH") 248 with 249 Not_found -> Unix.time () 250 in string_of_date ~absolute: true ~hour: false time 251 252 253let rec text_list_concat sep l = 254 match l with 255 [] -> [] 256 | [t] -> t 257 | t :: q -> 258 t @ (sep :: (text_list_concat sep q)) 259 260let rec text_no_title_no_list t = 261 let iter t_ele = 262 match t_ele with 263 | Odoc_types.Title (_,_,t) -> text_no_title_no_list t 264 | Odoc_types.List l 265 | Odoc_types.Enum l -> 266 (Odoc_types.Raw " ") :: 267 (text_list_concat 268 (Odoc_types.Raw ", ") 269 (List.map text_no_title_no_list l)) 270 | Odoc_types.Raw _ 271 | Odoc_types.Code _ 272 | Odoc_types.CodePre _ 273 | Odoc_types.Verbatim _ 274 | Odoc_types.Ref _ 275 | Odoc_types.Target _ -> [t_ele] 276 | Odoc_types.Newline -> [Odoc_types.Newline] 277 | Odoc_types.Block t -> [Odoc_types.Block (text_no_title_no_list t)] 278 | Odoc_types.Bold t -> [Odoc_types.Bold (text_no_title_no_list t)] 279 | Odoc_types.Italic t -> [Odoc_types.Italic (text_no_title_no_list t)] 280 | Odoc_types.Center t -> [Odoc_types.Center (text_no_title_no_list t)] 281 | Odoc_types.Left t -> [Odoc_types.Left (text_no_title_no_list t)] 282 | Odoc_types.Right t -> [Odoc_types.Right (text_no_title_no_list t)] 283 | Odoc_types.Emphasize t -> [Odoc_types.Emphasize (text_no_title_no_list t)] 284 | Odoc_types.Latex s -> [Odoc_types.Latex s] 285 | Odoc_types.Link (s, t) -> [Odoc_types.Link (s, (text_no_title_no_list t))] 286 | Odoc_types.Superscript t -> [Odoc_types.Superscript (text_no_title_no_list t)] 287 | Odoc_types.Subscript t -> [Odoc_types.Subscript (text_no_title_no_list t)] 288 | Odoc_types.Module_list l -> 289 list_concat (Odoc_types.Raw ", ") 290 (List.map 291 (fun s -> Odoc_types.Ref (s, Some Odoc_types.RK_module, None)) 292 l 293 ) 294 | Odoc_types.Index_list -> [] 295 | Odoc_types.Custom (s,t) -> [Odoc_types.Custom (s, text_no_title_no_list t)] 296 in 297 List.flatten (List.map iter t) 298 299let get_titles_in_text t = 300 let l = ref [] in 301 let rec iter_ele ele = 302 match ele with 303 | Odoc_types.Title (n,lopt,t) -> l := (n,lopt,t) :: !l 304 | Odoc_types.List l 305 | Odoc_types.Enum l -> List.iter iter_text l 306 | Odoc_types.Raw _ 307 | Odoc_types.Code _ 308 | Odoc_types.CodePre _ 309 | Odoc_types.Verbatim _ 310 | Odoc_types.Ref _ -> () 311 | Odoc_types.Newline -> () 312 | Odoc_types.Block t 313 | Odoc_types.Bold t 314 | Odoc_types.Italic t 315 | Odoc_types.Center t 316 | Odoc_types.Left t 317 | Odoc_types.Right t 318 | Odoc_types.Emphasize t -> iter_text t 319 | Odoc_types.Latex _ -> () 320 | Odoc_types.Link (_, t) 321 | Odoc_types.Superscript t 322 | Odoc_types.Subscript t -> iter_text t 323 | Odoc_types.Module_list _ -> () 324 | Odoc_types.Index_list -> () 325 | Odoc_types.Custom (_, t) -> iter_text t 326 | Odoc_types.Target _ -> () 327 and iter_text txt = 328 List.iter iter_ele txt 329 in 330 iter_text t; 331 List.rev !l 332 333let text_concat (sep : Odoc_types.text) l = 334 let rec iter = function 335 [] -> [] 336 | [last] -> last 337 | h :: q -> h @ sep @ (iter q) 338 in 339 iter l 340 341(*********************************************************) 342let rec get_before_dot s = 343 try 344 let len = String.length s in 345 let n = String.index s '.' in 346 if n + 1 >= len then 347 (* The dot is the last character *) 348 (true, s, "") 349 else 350 match s.[n+1] with 351 ' ' | '\n' | '\r' | '\t' -> 352 (true, String.sub s 0 (n+1), 353 String.sub s (n+1) (len - n - 1)) 354 | _ -> 355 let b, s2, s_after = get_before_dot (String.sub s (n + 1) (len - n - 1)) in 356 (b, (String.sub s 0 (n+1))^s2, s_after) 357 with 358 Not_found -> (false, s, "") 359 360let rec first_sentence_text t = 361 match t with 362 [] -> (false, [], []) 363 | ele :: q -> 364 let (stop, ele2, ele3_opt) = first_sentence_text_ele ele in 365 if stop then 366 (stop, [ele2], 367 match ele3_opt with None -> q | Some e -> e :: q) 368 else 369 let (stop2, q2, rest) = first_sentence_text q in 370 (stop2, ele2 :: q2, rest) 371 372 373and first_sentence_text_ele text_ele = 374 match text_ele with 375 | Odoc_types.Raw s -> 376 let b, s2, s_after = get_before_dot s in 377 (b, Odoc_types.Raw s2, Some (Odoc_types.Raw s_after)) 378 | Odoc_types.Code _ 379 | Odoc_types.CodePre _ 380 | Odoc_types.Verbatim _ -> (false, text_ele, None) 381 | Odoc_types.Bold t -> 382 let (b, t2, t3) = first_sentence_text t in 383 (b, Odoc_types.Bold t2, Some (Odoc_types.Bold t3)) 384 | Odoc_types.Italic t -> 385 let (b, t2, t3) = first_sentence_text t in 386 (b, Odoc_types.Italic t2, Some (Odoc_types.Italic t3)) 387 | Odoc_types.Center t -> 388 let (b, t2, t3) = first_sentence_text t in 389 (b, Odoc_types.Center t2, Some (Odoc_types.Center t3)) 390 | Odoc_types.Left t -> 391 let (b, t2, t3) = first_sentence_text t in 392 (b, Odoc_types.Left t2, Some (Odoc_types.Left t3)) 393 | Odoc_types.Right t -> 394 let (b, t2, t3) = first_sentence_text t in 395 (b, Odoc_types.Right t2, Some (Odoc_types.Right t3)) 396 | Odoc_types.Emphasize t -> 397 let (b, t2, t3) = first_sentence_text t in 398 (b, Odoc_types.Emphasize t2, Some (Odoc_types.Emphasize t3)) 399 | Odoc_types.Block t -> 400 let (b, t2, t3) = first_sentence_text t in 401 (b, Odoc_types.Block t2, Some (Odoc_types.Block t3)) 402 | Odoc_types.Title (n, l_opt, t) -> 403 let (b, t2, t3) = first_sentence_text t in 404 (b, 405 Odoc_types.Title (n, l_opt, t2), 406 Some (Odoc_types.Title (n, l_opt, t3))) 407 | Odoc_types.Newline -> 408 (true, Odoc_types.Raw "", Some Odoc_types.Newline) 409 | Odoc_types.List _ 410 | Odoc_types.Enum _ 411 | Odoc_types.Latex _ 412 | Odoc_types.Link _ 413 | Odoc_types.Ref _ 414 | Odoc_types.Superscript _ 415 | Odoc_types.Subscript _ 416 | Odoc_types.Module_list _ 417 | Odoc_types.Index_list -> (false, text_ele, None) 418 | Odoc_types.Custom _ 419 | Odoc_types.Target _ -> (false, text_ele, None) 420 421 422let first_sentence_of_text t = 423 let (_,t2,_) = first_sentence_text t in 424 t2 425 426let first_sentence_and_rest_of_text t = 427 let (_,t1, t2) = first_sentence_text t in 428 (t1, t2) 429 430let remove_ending_newline s = 431 let len = String.length s in 432 if len <= 0 then 433 s 434 else 435 match s.[len-1] with 436 '\n' -> String.sub s 0 (len-1) 437 | _ -> s 438 439let search_string_backward ~pat = 440 let lenp = String.length pat in 441 let rec iter s = 442 let len = String.length s in 443 match compare len lenp with 444 -1 -> raise Not_found 445 | 0 -> if pat = s then 0 else raise Not_found 446 | _ -> 447 let pos = len - lenp in 448 let s2 = String.sub s pos lenp in 449 if s2 = pat then 450 pos 451 else 452 iter (String.sub s 0 pos) 453 in 454 fun ~s -> iter s 455 456 457 458(*********************************************************) 459 460let create_index_lists elements string_of_ele = 461 let rec f current acc0 acc1 acc2 = function 462 [] -> (acc0 :: acc1) @ [acc2] 463 | ele :: q -> 464 let s = string_of_ele ele in 465 match s with 466 "" -> f current acc0 acc1 (acc2 @ [ele]) q 467 | _ -> 468 let first = Char.uppercase_ascii s.[0] in 469 match first with 470 'A' .. 'Z' -> 471 if current = first then 472 f current acc0 acc1 (acc2 @ [ele]) q 473 else 474 f first acc0 (acc1 @ [acc2]) [ele] q 475 | _ -> 476 f current (acc0 @ [ele]) acc1 acc2 q 477 in 478 f '_' [] [] [] elements 479 480 481(*** for labels *) 482 483let is_optional = Btype.is_optional 484let label_name = Btype.label_name 485 486let remove_option typ = 487 let rec iter t = 488 match t with 489 | Types.Tconstr(path, [ty], _) when Path.same path Predef.path_option -> ty.Types.desc 490 | Types.Tconstr _ 491 | Types.Tvar _ 492 | Types.Tunivar _ 493 | Types.Tpoly _ 494 | Types.Tarrow _ 495 | Types.Ttuple _ 496 | Types.Tobject _ 497 | Types.Tfield _ 498 | Types.Tnil 499 | Types.Tvariant _ 500 | Types.Tpackage _ -> t 501 | Types.Tlink t2 502 | Types.Tsubst t2 -> iter t2.Types.desc 503 in 504 { typ with Types.desc = iter typ.Types.desc } 505