(* Dill - Terminal I/O Driver with Eio * * This is the terminal driver for ship console interaction. * Uses Eio for async terminal I/O - non-blocking console operations! * * Key innovation vs C Vere: * - C Vere: Blocking terminal I/O, single-threaded input processing * - Overe: Async terminal I/O with Eio, concurrent input/output handling *) (* Dill configuration *) type config = { prompt: string; (* Command prompt to display *) } (* Dill driver state *) type t = { config: config; mutable stats: stats; } and stats = { mutable lines_read: int64; mutable lines_written: int64; mutable bytes_read: int64; mutable bytes_written: int64; } (* Create Dill driver *) let create config = { config; stats = { lines_read = 0L; lines_written = 0L; bytes_read = 0L; bytes_written = 0L; }; } (* Write output to terminal *) let write_output dill ~env text = let stdout = Eio.Stdenv.stdout env in (* Async write - doesn't block! *) Eio.Flow.copy_string text stdout; dill.stats.lines_written <- Int64.succ dill.stats.lines_written; dill.stats.bytes_written <- Int64.add dill.stats.bytes_written (Int64.of_int (String.length text)) (* Write prompt *) let write_prompt dill ~env = write_output dill ~env (dill.config.prompt ^ " ") (* Read input line from terminal *) let read_input dill ~env = let stdin = Eio.Stdenv.stdin env in (* Read line - async, doesn't block other operations! *) let buf = Buffer.create 256 in let chunk = Cstruct.create 1 in let rec read_until_newline () = match Eio.Flow.single_read stdin chunk with | 0 -> None (* EOF *) | _ -> let char = Cstruct.get_char chunk 0 in if char = '\n' then Some (Buffer.contents buf) else begin Buffer.add_char buf char; read_until_newline () end in match read_until_newline () with | Some line -> dill.stats.lines_read <- Int64.succ dill.stats.lines_read; dill.stats.bytes_read <- Int64.add dill.stats.bytes_read (Int64.of_int (String.length line)); Some line | None -> None (* Input fiber - continuously reads terminal input *) let input_fiber dill ~env ~sw:_ ~event_stream = Printf.printf "[Dill] Input fiber started\n%!"; let rec loop () = try (* Show prompt *) write_prompt dill ~env; (* Read input line - async! *) (match read_input dill ~env with | Some line -> Printf.printf "[Dill] Read input: %s\n%!" line; (* Create ovum for runtime *) let ovum = Nock_lib.Effects.make_ovum ~wire:(Nock_lib.Noun.atom 0) ~card:(Nock_lib.Noun.cell (Nock_lib.Noun.atom 4) (* dill tag *) (Nock_lib.Noun.atom 0)) (* simplified - would be parsed command *) in (* Send to runtime event queue *) Eio.Stream.add event_stream ovum; loop () | None -> Printf.printf "[Dill] EOF on input\n%!" ) with | End_of_file -> Printf.printf "[Dill] Input fiber closed\n%!" | Eio.Cancel.Cancelled _ -> Printf.printf "[Dill] Input fiber cancelled\n%!" in loop () (* Output fiber - handles terminal output *) let output_fiber dill ~env ~sw:_ output_stream = Printf.printf "[Dill] Output fiber started\n%!"; let rec loop () = try (* Wait for output from runtime *) let text = Eio.Stream.take output_stream in (* Write to terminal - async! *) write_output dill ~env text; loop () with | End_of_file -> Printf.printf "[Dill] Output fiber closed\n%!" | Eio.Cancel.Cancelled _ -> Printf.printf "[Dill] Output fiber cancelled\n%!" in loop () (* Run Dill driver - spawns input and output fibers *) let run dill ~env ~sw ~event_stream = Printf.printf "[Dill] Starting terminal driver\n%!"; (* Create output stream for terminal output *) let output_stream = Eio.Stream.create 100 in (* Spawn input fiber *) Eio.Fiber.fork ~sw (fun () -> input_fiber dill ~env ~sw ~event_stream ); (* Spawn output fiber *) Eio.Fiber.fork ~sw (fun () -> output_fiber dill ~env ~sw output_stream ); Printf.printf "[Dill] Terminal driver running!\n%!"; output_stream (* Return output stream so runtime can send output *) (* Get statistics *) let get_stats dill = dill.stats