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
(* src/packages/core/help.ml *)
open Ast

(*
--# Display documentation for a function
--#
--# Prints the help documentation for the specified function, including
--# signature, parameters, return value, and examples.
--#
--# @name help
--# @param name :: String | Symbol The name of the function to document.
--# @return :: Null
--# @example
--#   help("mean")
--#   help(print)
--# @family core
--# @seealso apropos
--# @export
*)
let rec help_impl args _env =
  match args with
  | [VString name] ->
      (match Tdoc_registry.lookup name with
      | Some doc ->
          let brief = if doc.description_brief <> "" then doc.description_brief else "No description available." in
          let sig_str = Printf.sprintf "%s -> %s" 
            (String.concat ", " (List.map (fun (p : Tdoc_types.param_doc) -> p.name) doc.params))
            (match doc.return_value with Some r -> (match r.type_info with Some t -> t | None -> "Any") | None -> "Any")
          in
          let output = Printf.sprintf "\n  %s\n\n  %s\n  Signature: %s(%s)\n" 
            name brief name sig_str in
          
          let output = output ^ "\n  Parameters:\n" ^ 
            (String.concat "\n" (List.map (fun (p : Tdoc_types.param_doc) -> 
              Printf.sprintf "    - %s%s: %s" 
                p.name 
                (match p.type_info with Some t -> " (" ^ t ^ ")" | None -> "")
                p.description
            ) doc.params)) ^ "\n" in
            
          let output = match doc.return_value with
            | Some r -> output ^ Printf.sprintf "\n  Returns:\n    %s%s\n" 
                (match r.type_info with Some t -> "(" ^ t ^ ") " | None -> "") r.description
            | None -> output
          in

          let output = if doc.examples <> [] then
            output ^ "\n  Examples:\n" ^
            (String.concat "\n" (List.map (fun e -> 
               let lines = String.split_on_char '\n' e in
               String.concat "\n" (List.map (fun l -> "    " ^ l) lines)
            ) doc.examples)) ^ "\n"
          else output in

          print_endline output;
          flush stdout;
          (VNA NAGeneric)
      | None ->
          Printf.printf "No documentation found for '%s'.\n" name;
          flush stdout;
          (VNA NAGeneric))
  | [VSymbol name] ->
      (* Support help(mean) as well as help("mean") *)
      help_impl [VString name] _env
  | [VBuiltin b] ->
      (match b.b_name with
      | Some name -> help_impl [VString name] _env
      | None ->
          Printf.printf "This builtin function is unnamed and has no documentation.\n";
          flush stdout;
          (VNA NAGeneric))
  | [VLambda _ as lam] ->
      let found_name =
        Ast.Env.fold (fun k k_val acc ->
          if acc = None && k_val == lam then Some k else acc
        ) _env None
      in
      (match found_name with
      | Some name -> help_impl [VString name] _env
      | None ->
          Printf.printf "No documentation found for this anonymous function.\n";
          flush stdout;
          (VNA NAGeneric))
  | [v] ->
      Error.type_error (Printf.sprintf "help expects a function name or value, got %s" (Utils.type_name v))
  | _ ->
      Error.arity_error_named "help" 1 (List.length args)

(*
--# Search for functions by keyword
--#
--# Searches all documented functions for names or descriptions matching the query.
--#
--# @name apropos
--# @param query :: String The keyword to search for.
--# @return :: Null
--# @example
--#   apropos("stat")
--#   -- Finds functions like "fit_stats", "mean", "sd", etc.
--# @family core
--# @seealso help
--# @export
*)
let apropos_impl args _env =
  match args with
  | [VString query] ->
      let all_docs = Tdoc_registry.get_all () in
      
      let query_low = String.lowercase_ascii query in
      let matches = List.filter (fun (doc : Tdoc_types.doc_entry) ->
        let name_low = String.lowercase_ascii doc.name in
        let desc_low = String.lowercase_ascii doc.description_brief in
        
        let contains s1 s2 = 
          try 
            let len1 = String.length s1 in
            let len2 = String.length s2 in
            if len2 > len1 then false else
            let rec loop i =
              if i > len1 - len2 then false
              else if String.sub s1 i len2 = s2 then true
              else loop (i + 1)
            in loop 0
          with _ -> false
        in
        contains name_low query_low || contains desc_low query_low
      ) all_docs in

      if matches = [] then begin
        Printf.printf "No documents found matching '%s'.\n" query;
        flush stdout
      end else begin
        Printf.printf "Found %d matches:\n" (List.length matches);
        List.iter (fun (doc : Tdoc_types.doc_entry) ->
          Printf.printf "  %-20s %s\n" doc.name doc.description_brief
        ) matches;
        flush stdout
      end;
      (VNA NAGeneric)
  | _ ->
      Error.type_error "apropos expects a query string."

let register ?(ensure_docs=ignore) env =
  let help_with_docs args env =
    ensure_docs ();
    help_impl args env
  in
  let apropos_with_docs args env =
    ensure_docs ();
    apropos_impl args env
  in
  let env = Env.add "help" (make_builtin ~name:"help" 1 help_with_docs) env in
  let env = Env.add "apropos" (make_builtin ~name:"apropos" 1 apropos_with_docs) env in
  env