@@ -24,6 +24,82 @@ let run input_file =
2424    |  Ok  error_msg  -> Stdlib.Printf. eprintf " %s" 
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 %!" 
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 %!" 
60+             false 
61+           |  Unix. WSIGNALED  signal  ->
62+             Stdlib.Printf. eprintf " [Killed by signal %d]\n %!" 
63+             false 
64+           |  Unix. WSTOPPED  signal  ->
65+             Stdlib.Printf. eprintf " [Stopped by signal %d]\n %!" 
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 %!" 
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+ 
27103let  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+ 
52141let  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
56145let  ()  =  Stdlib. exit (Cmd. eval default_cmd)
0 commit comments