1use crate::command::Command;
20use crate::trap::run_traps_for_caught_signals;
21use crate::{Handle, Runtime};
22use std::cell::RefCell;
23use std::ops::ControlFlow::{Break, Continue};
24use yash_env::Env;
25use yash_env::semantics::Divert;
26use yash_env::semantics::ExitStatus;
27use yash_env::semantics::Result;
28use yash_syntax::parser::lex::Lexer;
29use yash_syntax::parser::{ErrorCause, Parser};
30use yash_syntax::syntax::List;
31
32pub async fn read_eval_loop<S: Runtime + 'static>(
102 env: &RefCell<&mut Env<S>>,
103 lexer: &mut Lexer<'_>,
104) -> Result {
105 read_eval_loop_impl(env, lexer, false).await
106}
107
108pub async fn interactive_read_eval_loop<S: Runtime + 'static>(
131 env: &RefCell<&mut Env<S>>,
132 lexer: &mut Lexer<'_>,
133) -> Result {
134 read_eval_loop_impl(env, lexer, true).await
135}
136
137#[allow(clippy::await_holding_refcell_ref)]
140async fn read_eval_loop_impl<S: Runtime + 'static>(
141 env: &RefCell<&mut Env<S>>,
142 lexer: &mut Lexer<'_>,
143 is_interactive: bool,
144) -> Result {
145 let mut executed = false;
146
147 loop {
148 if !lexer.pending() {
149 lexer.flush();
150 }
151
152 let command = Parser::config()
153 .aliases(env)
154 .declaration_utilities(env)
155 .input(lexer)
156 .command_line()
157 .await;
158
159 let env = &mut **env.borrow_mut();
160
161 let (mut result, error_recoverable) = match command {
162 Ok(None) => {
164 if !executed {
165 env.exit_status = ExitStatus::SUCCESS;
166 }
167 return Continue(());
168 }
169
170 Ok(Some(command)) => (run_command(env, &command).await, true),
172
173 Err(error) => {
175 let result = error.handle(env).await;
176 let error_recoverable = matches!(error.cause, ErrorCause::Syntax(_));
177 (result, error_recoverable)
178 }
179 };
180
181 if is_interactive && error_recoverable {
182 if let Break(Divert::Interrupt(exit_status)) = result {
184 if let Some(exit_status) = exit_status {
185 env.exit_status = exit_status;
186 }
187 result = Continue(());
188 lexer.flush();
189 }
190 }
191
192 result?;
194
195 executed = true;
196 }
197}
198
199async fn run_command<S: Runtime + 'static>(env: &mut Env<S>, command: &List) -> Result {
200 run_traps_for_caught_signals(env).await?;
201 env.update_all_subshell_statuses();
202 command.execute(env).await
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::tests::echo_builtin;
209 use crate::tests::return_builtin;
210 use futures_util::FutureExt;
211 use std::rc::Rc;
212 use yash_env::input::Echo;
213 use yash_env::input::Memory;
214 use yash_env::option::Option::Verbose;
215 use yash_env::option::State::On;
216 use yash_env::system::r#virtual::SIGUSR1;
217 use yash_env::system::r#virtual::VirtualSystem;
218 use yash_env::trap::Action;
219 use yash_env_test_helper::assert_stderr;
220 use yash_env_test_helper::assert_stdout;
221 use yash_syntax::input::Context;
222 use yash_syntax::source::Location;
223
224 #[test]
225 fn exit_status_zero_with_no_commands() {
226 let mut env = Env::new_virtual();
227 env.exit_status = ExitStatus(5);
228 let mut lexer = Lexer::with_code("");
229 let ref_env = RefCell::new(&mut env);
230
231 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
232 assert_eq!(result, Continue(()));
233 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
234 }
235
236 #[test]
237 fn exit_status_in_out() {
238 let system = VirtualSystem::new();
239 let state = Rc::clone(&system.state);
240 let mut env = Env::with_system(system);
241 env.exit_status = ExitStatus(42);
242 env.builtins.insert("echo", echo_builtin());
243 env.builtins.insert("return", return_builtin());
244 let mut lexer = Lexer::with_code("echo $?; return -n 7");
245 let ref_env = RefCell::new(&mut env);
246
247 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
248 assert_eq!(result, Continue(()));
249 assert_eq!(env.exit_status, ExitStatus(7));
250 assert_stdout(&state, |stdout| assert_eq!(stdout, "42\n"));
251 }
252
253 #[test]
254 fn executing_many_lines_of_code() {
255 let system = VirtualSystem::new();
256 let state = Rc::clone(&system.state);
257 let mut env = Env::with_system(system);
258 env.builtins.insert("echo", echo_builtin());
259 let mut lexer = Lexer::with_code("echo 1\necho 2\necho 3;");
260 let ref_env = RefCell::new(&mut env);
261
262 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
263 assert_eq!(result, Continue(()));
264 assert_stdout(&state, |stdout| assert_eq!(stdout, "1\n2\n3\n"));
265 }
266
267 #[test]
268 fn parsing_with_aliases() {
269 use yash_syntax::alias::{Alias, HashEntry};
270 let system = VirtualSystem::new();
271 let state = Rc::clone(&system.state);
272 let mut env = Env::with_system(system);
273 env.aliases.insert(HashEntry(Rc::new(Alias {
274 name: "echo".to_string(),
275 replacement: "echo alias\necho ok".to_string(),
276 global: false,
277 origin: Location::dummy(""),
278 })));
279 env.builtins.insert("echo", echo_builtin());
280 let mut lexer = Lexer::with_code("echo");
281 let ref_env = RefCell::new(&mut env);
282
283 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
284 assert_eq!(result, Continue(()));
285 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
286 assert_stdout(&state, |stdout| assert_eq!(stdout, "alias\nok\n"));
287 }
288
289 #[test]
290 fn verbose_option() {
291 let system = VirtualSystem::new();
292 let state = Rc::clone(&system.state);
293 let mut env = Env::with_system(system);
294 env.options.set(Verbose, On);
295 let ref_env = RefCell::new(&mut env);
296 let input = Box::new(Echo::new(Memory::new("case _ in esac"), &ref_env));
297 let mut lexer = Lexer::new(input);
298
299 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
300 drop(lexer);
301 assert_eq!(result, Continue(()));
302 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
303 assert_stderr(&state, |stderr| assert_eq!(stderr, "case _ in esac"));
304 }
305
306 #[test]
307 fn command_interrupt_interactive() {
308 let system = VirtualSystem::new();
311 let state = Rc::clone(&system.state);
312 let mut env = Env::with_system(system);
313 env.builtins.insert("echo", echo_builtin());
314 let mut lexer = Lexer::with_code("${X?}\necho $?\n");
315 let ref_env = RefCell::new(&mut env);
316
317 let result = interactive_read_eval_loop(&ref_env, &mut lexer)
318 .now_or_never()
319 .unwrap();
320 assert_eq!(result, Continue(()));
321 assert_stdout(&state, |stdout| assert_eq!(stdout, "2\n"));
322 }
323
324 #[test]
325 fn command_other_divert_interactive() {
326 let system = VirtualSystem::new();
329 let state = Rc::clone(&system.state);
330 let mut env = Env::with_system(system);
331 env.builtins.insert("echo", echo_builtin());
332 env.builtins.insert("return", return_builtin());
333 let mut lexer = Lexer::with_code("return 123\necho $?\n");
334 let ref_env = RefCell::new(&mut env);
335
336 let result = interactive_read_eval_loop(&ref_env, &mut lexer)
337 .now_or_never()
338 .unwrap();
339 assert_eq!(result, Break(Divert::Return(Some(ExitStatus(123)))));
340 assert_stdout(&state, |stdout| assert_eq!(stdout, ""));
341 }
342
343 #[test]
344 fn command_interrupt_non_interactive() {
345 let system = VirtualSystem::new();
348 let state = Rc::clone(&system.state);
349 let mut env = Env::with_system(system);
350 env.builtins.insert("echo", echo_builtin());
351 let mut lexer = Lexer::with_code("${X?}\necho $?\n");
352 let ref_env = RefCell::new(&mut env);
353
354 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
355 assert_eq!(result, Break(Divert::Interrupt(Some(ExitStatus::ERROR))));
356 assert_stdout(&state, |stdout| assert_eq!(stdout, ""));
357 }
358
359 #[test]
360 fn handling_syntax_error() {
361 let system = VirtualSystem::new();
362 let state = Rc::clone(&system.state);
363 let mut env = Env::with_system(system);
364 let mut lexer = Lexer::with_code(";;");
365 let ref_env = RefCell::new(&mut env);
366 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
367 assert_eq!(result, Break(Divert::Interrupt(Some(ExitStatus::ERROR))));
368 assert_stderr(&state, |stderr| assert_ne!(stderr, ""));
369 }
370
371 #[test]
372 fn syntax_error_aborts_non_interactive_loop() {
373 let system = VirtualSystem::new();
374 let state = Rc::clone(&system.state);
375 let mut env = Env::with_system(system);
376 env.builtins.insert("echo", echo_builtin());
377 let mut lexer = Lexer::with_code(";;\necho !");
378 let ref_env = RefCell::new(&mut env);
379
380 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
381 assert_eq!(result, Break(Divert::Interrupt(Some(ExitStatus::ERROR))));
382 assert_stdout(&state, |stdout| assert_eq!(stdout, ""));
383 }
384
385 #[test]
386 fn syntax_error_continues_interactive_loop() {
387 let system = VirtualSystem::new();
388 let state = Rc::clone(&system.state);
389 let mut env = Env::with_system(system);
390 env.builtins.insert("echo", echo_builtin());
391 let mut lexer = Lexer::with_code(";; (\necho $?");
394 let ref_env = RefCell::new(&mut env);
395
396 let result = interactive_read_eval_loop(&ref_env, &mut lexer)
397 .now_or_never()
398 .unwrap();
399 assert_eq!(result, Continue(()));
400 assert_stdout(&state, |stdout| assert_eq!(stdout, "2\n"));
401 }
402
403 #[test]
404 fn input_error_aborts_loop() {
405 struct BrokenInput;
406 impl yash_syntax::input::Input for BrokenInput {
407 async fn next_line(&mut self, _context: &Context) -> std::io::Result<String> {
408 Err(std::io::Error::other("broken"))
409 }
410 }
411
412 let mut lexer = Lexer::new(Box::new(BrokenInput));
413 let mut env = Env::new_virtual();
414 let ref_env = RefCell::new(&mut env);
415
416 let result = interactive_read_eval_loop(&ref_env, &mut lexer)
417 .now_or_never()
418 .unwrap();
419 assert_eq!(
420 result,
421 Break(Divert::Interrupt(Some(ExitStatus::READ_ERROR)))
422 );
423 }
424
425 #[test]
426 fn running_traps_between_parsing_and_executing() {
427 let system = VirtualSystem::new();
428 let state = Rc::clone(&system.state);
429 let mut env = Env::with_system(system.clone());
430 env.builtins.insert("echo", echo_builtin());
431 env.traps
432 .set_action(
433 &mut env.system,
434 SIGUSR1,
435 Action::Command("echo USR1".into()),
436 Location::dummy(""),
437 false,
438 )
439 .unwrap();
440 let _ = state
441 .borrow_mut()
442 .processes
443 .get_mut(&system.process_id)
444 .unwrap()
445 .raise_signal(SIGUSR1);
446 let mut lexer = Lexer::with_code("echo $?");
447 let ref_env = RefCell::new(&mut env);
448
449 let result = read_eval_loop(&ref_env, &mut lexer).now_or_never().unwrap();
450 assert_eq!(result, Continue(()));
451 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
452 assert_stdout(&state, |stdout| assert_eq!(stdout, "USR1\n0\n"));
453 }
454}