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