Skip to content

Commit 1d1c407

Browse files
authored
Merge pull request #132 from engboris/interactive
Add program watcher
2 parents b3136ef + ce34510 commit 1d1c407

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-2
lines changed

bin/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(executable
22
(public_name sgen)
33
(name sgen)
4-
(libraries stellogen base cmdliner))
4+
(libraries stellogen base cmdliner unix))
55

66
(env
77
(dev

bin/sgen.ml

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,82 @@ let run input_file =
2424
| Ok error_msg -> Stdlib.Printf.eprintf "%s" error_msg
2525
| Error _ -> () )
2626

27+
let run_with_timeout input_file timeout =
28+
let pid = Unix.fork () in
29+
if pid = 0 then (
30+
(* Child process *)
31+
try
32+
run input_file;
33+
Stdlib.exit 0
34+
with e ->
35+
Stdlib.Printf.eprintf "Error: %s\n" (Exn.to_string e);
36+
Stdlib.exit 1 )
37+
else
38+
(* Parent process *)
39+
let start_time = Unix.time () in
40+
let rec wait_with_timeout () =
41+
let elapsed = Unix.time () -. start_time in
42+
if Float.(elapsed > timeout) then (
43+
(* Timeout - kill child process *)
44+
Unix.kill pid Stdlib.Sys.sigkill;
45+
let _ = Unix.waitpid [] pid in
46+
Stdlib.Printf.eprintf
47+
"\n[Timeout: execution exceeded %.1fs - killed]\n%!" timeout;
48+
false )
49+
else
50+
match Unix.waitpid [ Unix.WNOHANG ] pid with
51+
| 0, _ ->
52+
(* Still running *)
53+
Unix.sleepf 0.1;
54+
wait_with_timeout ()
55+
| _, status -> (
56+
match status with
57+
| Unix.WEXITED 0 -> true
58+
| Unix.WEXITED code ->
59+
Stdlib.Printf.eprintf "[Exited with code %d]\n%!" code;
60+
false
61+
| Unix.WSIGNALED signal ->
62+
Stdlib.Printf.eprintf "[Killed by signal %d]\n%!" signal;
63+
false
64+
| Unix.WSTOPPED signal ->
65+
Stdlib.Printf.eprintf "[Stopped by signal %d]\n%!" signal;
66+
false )
67+
in
68+
wait_with_timeout ()
69+
70+
let watch input_file timeout =
71+
let abs_path =
72+
if Stdlib.Filename.is_relative input_file then
73+
Stdlib.Filename.concat (Stdlib.Sys.getcwd ()) input_file
74+
else input_file
75+
in
76+
77+
Stdlib.Printf.printf "Watching %s (timeout: %.1fs)\n%!" abs_path timeout;
78+
Stdlib.Printf.printf "Press Ctrl+C to stop.\n\n%!";
79+
80+
(* Initial run *)
81+
let _ = run_with_timeout input_file timeout in
82+
83+
(* Polling approach - check file modification time *)
84+
let rec poll_loop last_mtime =
85+
Unix.sleepf 0.5;
86+
try
87+
let stat = Unix.stat abs_path in
88+
let current_mtime = stat.Unix.st_mtime in
89+
if Float.(current_mtime > last_mtime) then (
90+
Stdlib.Printf.printf "\n\n--- File changed, re-running ---\n%!";
91+
let _ = run_with_timeout input_file timeout in
92+
poll_loop current_mtime )
93+
else poll_loop last_mtime
94+
with Unix.Unix_error _ ->
95+
Stdlib.Printf.eprintf "Error accessing file, retrying...\n%!";
96+
Unix.sleepf 1.0;
97+
poll_loop last_mtime
98+
in
99+
100+
let initial_stat = Unix.stat abs_path in
101+
poll_loop initial_stat.Unix.st_mtime
102+
27103
let preprocess_only input_file =
28104
let expr = parse input_file in
29105
let preprocessed = Expr.preprocess expr in
@@ -49,8 +125,21 @@ let preprocess_cmd =
49125
in
50126
Cmd.v (Cmd.info "preprocess" ~doc) term
51127

128+
let timeout_arg =
129+
let doc = "Timeout in seconds for each execution (default: 5.0)" in
130+
Arg.(value & opt float 5.0 & info [ "t"; "timeout" ] ~docv:"SECONDS" ~doc)
131+
132+
let watch_cmd =
133+
let doc = "Watch and re-run the Stellogen program on file changes" in
134+
let term =
135+
Term.(
136+
const (fun input timeout -> wrap (fun i -> watch i timeout) input)
137+
$ input_file_arg $ timeout_arg |> term_result )
138+
in
139+
Cmd.v (Cmd.info "watch" ~doc) term
140+
52141
let default_cmd =
53142
let doc = "Stellogen: code generator and evaluator" in
54-
Cmd.group (Cmd.info "sgen" ~doc) [ run_cmd; preprocess_cmd ]
143+
Cmd.group (Cmd.info "sgen" ~doc) [ run_cmd; preprocess_cmd; watch_cmd ]
55144

56145
let () = Stdlib.exit (Cmd.eval default_cmd)

0 commit comments

Comments
 (0)