cli11, bsd manual

This commit is contained in:
atagen 2026-03-21 21:22:41 +11:00
parent 9c7c528a0c
commit 18c97eacd0
7 changed files with 534 additions and 117 deletions

View file

@ -1,5 +1,9 @@
open Parser
module SSet = Set.Make(String)
module SMap = Map.Make(String)
module CSet = Set.Make(Char)
(* Nushell built-in commands and keywords *)
let nushell_builtins = [
"alias"; "all"; "ansi"; "any"; "append"; "ast"; "attr";
@ -40,13 +44,10 @@ let nushell_builtins = [
"zip";
]
let builtin_set = lazy (
let tbl = Hashtbl.create (List.length nushell_builtins) in
List.iter (fun s -> Hashtbl.replace tbl s true) nushell_builtins;
tbl)
let builtin_set = lazy (SSet.of_list nushell_builtins)
let is_nushell_builtin cmd =
Hashtbl.mem (Lazy.force builtin_set) cmd
SSet.mem cmd (Lazy.force builtin_set)
let dedup_entries entries =
let key_of entry =
@ -60,30 +61,24 @@ let dedup_entries entries =
let d = min 5 (String.length entry.desc / 10) in
sw + p + d
in
let best = Hashtbl.create 64 in
List.iter (fun e ->
let best = List.fold_left (fun acc e ->
let k = key_of e in
match Hashtbl.find_opt best k with
| Some prev when score prev >= score e -> ()
| _ -> Hashtbl.replace best k e
) entries;
let covered_shorts = Hashtbl.create 16 in
Hashtbl.iter (fun _ e ->
match SMap.find_opt k acc with
| Some prev when score prev >= score e -> acc
| _ -> SMap.add k e acc
) SMap.empty entries in
let covered = SMap.fold (fun _ e acc ->
match e.switch with
| Both (c, _) -> Hashtbl.replace covered_shorts c true
| _ -> ()
) best;
let seen = Hashtbl.create 64 in
List.filter_map (fun e ->
| Both (c, _) -> CSet.add c acc
| _ -> acc
) best CSet.empty in
List.fold_left (fun (seen, acc) e ->
let k = key_of e in
if Hashtbl.mem seen k then None
else
match e.switch with
| Short c when Hashtbl.mem covered_shorts c -> None
| _ ->
Hashtbl.add seen k true;
Hashtbl.find_opt best k
) entries
if SSet.mem k seen then (seen, acc)
else match e.switch with
| Short c when CSet.mem c covered -> (seen, acc)
| _ -> (SSet.add k seen, SMap.find k best :: acc)
) (SSet.empty, []) entries |> snd |> List.rev
let nushell_type_of_param = function
| "FILE" | "file" | "PATH" | "path" | "DIR" | "dir" | "DIRECTORY"
@ -120,22 +115,26 @@ let format_flag entry =
let pad_len = max 1 (40 - String.length flag) in
flag ^ String.make pad_len ' ' ^ "# " ^ entry.desc
let write_extern buf cmd_name result =
let entries = dedup_entries result.entries in
Printf.bprintf buf "export extern \"%s\" [\n" (escape_nu cmd_name);
List.iter (fun e ->
Buffer.add_string buf (format_flag e); Buffer.add_char buf '\n'
) entries;
Buffer.add_string buf "]\n";
List.iter (fun (sc : subcommand) ->
Printf.bprintf buf "\nexport extern \"%s %s\" [ # %s\n]\n"
(escape_nu cmd_name) (escape_nu sc.name) (escape_nu sc.desc)
) result.subcommands
let format_positional p =
let name = String.map (function '-' -> '_' | c -> c) p.pos_name in
let prefix = if p.variadic then "..." else "" in
let suffix = if p.optional && not p.variadic then "?" else "" in
let typ = nushell_type_of_param (String.uppercase_ascii p.pos_name) in
Printf.sprintf " %s%s%s: %s" prefix name suffix typ
let generate_extern cmd_name result =
let buf = Buffer.create 1024 in
write_extern buf cmd_name result;
Buffer.contents buf
let extern_of cmd_name result =
let entries = dedup_entries result.entries in
let cmd = escape_nu cmd_name in
let pos_lines = List.map (fun p -> format_positional p ^ "\n") result.positionals in
let flags = List.map (fun e -> format_flag e ^ "\n") entries in
let main = Printf.sprintf "export extern \"%s\" [\n%s%s]\n" cmd (String.concat "" pos_lines) (String.concat "" flags) in
let subs = List.map (fun (sc : subcommand) ->
Printf.sprintf "\nexport extern \"%s %s\" [ # %s\n]\n"
cmd (escape_nu sc.name) (escape_nu sc.desc)
) result.subcommands in
String.concat "" (main :: subs)
let generate_extern = extern_of
let module_name_of cmd_name =
let s = String.map (function
@ -144,11 +143,7 @@ let module_name_of cmd_name =
let generate_module cmd_name result =
let m = module_name_of cmd_name in
let buf = Buffer.create 1024 in
Printf.bprintf buf "module %s {\n" m;
write_extern buf cmd_name result;
Printf.bprintf buf "}\n\nuse %s *\n" m;
Buffer.contents buf
Printf.sprintf "module %s {\n%s}\n\nuse %s *\n" m (extern_of cmd_name result) m
let generate_extern_from_entries cmd_name entries =
generate_extern cmd_name { entries; subcommands = [] }
generate_extern cmd_name { entries; subcommands = []; positionals = [] }