yash_semantics/
runner_legacy.rs1use crate::Handle;
20use crate::command::Command;
21use crate::trap::run_traps_for_caught_signals;
22use std::cell::Cell;
23use std::ops::ControlFlow::Continue;
24use std::rc::Rc;
25use yash_env::Env;
26use yash_env::option::Option::Verbose;
27use yash_env::option::State;
28use yash_env::semantics::ExitStatus;
29use yash_env::semantics::Result;
30use yash_syntax::parser::Parser;
31use yash_syntax::parser::lex::Lexer;
32
33#[deprecated = "use the `read_eval_loop` function instead"]
67#[derive(Debug)]
68#[must_use = "the loop must be run to execute commands"]
69pub struct ReadEvalLoop<'a, 'b> {
70 env: &'a mut Env,
71 lexer: &'a mut Lexer<'b>,
72 verbose: Option<Rc<Cell<State>>>,
73}
74
75#[allow(deprecated)]
76impl<'a, 'b> ReadEvalLoop<'a, 'b> {
77 pub fn new(env: &'a mut Env, lexer: &'a mut Lexer<'b>) -> Self {
82 Self {
83 env,
84 lexer,
85 verbose: None,
86 }
87 }
88
89 #[deprecated = "use yash_env::input::Echo instead"]
127 pub fn set_verbose(&mut self, verbose: Option<Rc<Cell<State>>>) {
128 self.verbose = verbose;
129 }
130
131 pub async fn run(self) -> Result {
133 let mut executed = false;
134
135 loop {
136 if !self.lexer.pending() {
137 self.lexer.flush();
138 }
139 if let Some(verbose) = &self.verbose {
140 verbose.set(self.env.options.get(Verbose));
141 }
142
143 let mut parser = Parser::config()
144 .aliases(&self.env)
145 .declaration_utilities(&self.env)
146 .input(self.lexer);
147 match parser.command_line().await {
148 Ok(Some(command)) => {
149 run_traps_for_caught_signals(self.env).await?;
150 self.env.update_all_subshell_statuses();
151 command.execute(self.env).await?
152 }
153 Ok(None) => break,
154 Err(error) => error.handle(self.env).await?,
155 };
156 executed = true;
157 }
158
159 if !executed {
160 self.env.exit_status = ExitStatus::SUCCESS;
161 }
162
163 Continue(())
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::tests::echo_builtin;
171 use crate::tests::return_builtin;
172 use futures_util::FutureExt;
173 use std::cell::Cell;
174 use std::ops::ControlFlow::Break;
175 use std::rc::Rc;
176 use yash_env::input::FdReader;
177 use yash_env::io::Fd;
178 use yash_env::option::Option::Verbose;
179 use yash_env::option::State::{Off, On};
180 use yash_env::semantics::Divert;
181 use yash_env::system::r#virtual::FileBody;
182 use yash_env::system::r#virtual::SIGUSR1;
183 use yash_env::system::r#virtual::VirtualSystem;
184 use yash_env::trap::Action;
185 use yash_env_test_helper::assert_stderr;
186 use yash_env_test_helper::assert_stdout;
187 use yash_syntax::source::Location;
188
189 #[test]
190 fn exit_status_zero_with_no_commands() {
191 let mut env = Env::new_virtual();
192 env.exit_status = ExitStatus(5);
193 let mut lexer = Lexer::with_code("");
194 #[allow(deprecated)]
195 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
196 let result = rel.run().now_or_never().unwrap();
197 assert_eq!(result, Continue(()));
198 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
199 }
200
201 #[test]
202 fn exit_status_in_out() {
203 let system = VirtualSystem::new();
204 let state = Rc::clone(&system.state);
205 let mut env = Env::with_system(Box::new(system));
206 env.exit_status = ExitStatus(42);
207 env.builtins.insert("echo", echo_builtin());
208 env.builtins.insert("return", return_builtin());
209 let mut lexer = Lexer::with_code("echo $?; return -n 7");
210 #[allow(deprecated)]
211 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
212 let result = rel.run().now_or_never().unwrap();
213 assert_eq!(result, Continue(()));
214 assert_eq!(env.exit_status, ExitStatus(7));
215 assert_stdout(&state, |stdout| assert_eq!(stdout, "42\n"));
216 }
217
218 #[test]
219 fn executing_many_lines_of_code() {
220 let system = VirtualSystem::new();
221 let state = Rc::clone(&system.state);
222 let mut env = Env::with_system(Box::new(system));
223 env.builtins.insert("echo", echo_builtin());
224 let mut lexer = Lexer::with_code("echo 1\necho 2\necho 3;");
225 #[allow(deprecated)]
226 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
227 let result = rel.run().now_or_never().unwrap();
228 assert_eq!(result, Continue(()));
229 assert_stdout(&state, |stdout| assert_eq!(stdout, "1\n2\n3\n"));
230 }
231
232 #[test]
233 fn parsing_with_aliases() {
234 use yash_syntax::alias::{Alias, HashEntry};
235 let system = VirtualSystem::new();
236 let state = Rc::clone(&system.state);
237 let mut env = Env::with_system(Box::new(system));
238 env.aliases.insert(HashEntry(Rc::new(Alias {
239 name: "echo".to_string(),
240 replacement: "echo alias\necho ok".to_string(),
241 global: false,
242 origin: Location::dummy(""),
243 })));
244 env.builtins.insert("echo", echo_builtin());
245 let mut lexer = Lexer::with_code("echo");
246 #[allow(deprecated)]
247 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
248 let result = rel.run().now_or_never().unwrap();
249 assert_eq!(result, Continue(()));
250 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
251 assert_stdout(&state, |stdout| assert_eq!(stdout, "alias\nok\n"));
252 }
253
254 #[test]
255 fn verbose_option() {
256 let system = VirtualSystem::new();
257 let state = Rc::clone(&system.state);
258 state
259 .borrow_mut()
260 .file_system
261 .get("/dev/stdin")
262 .unwrap()
263 .borrow_mut()
264 .body = FileBody::new(*b"case _ in esac\n");
265 let mut env = Env::with_system(Box::new(system));
266 env.options.set(Verbose, On);
267 let mut input = Box::new(FdReader::new(Fd::STDIN, Clone::clone(&env.system)));
268 let verbose = Rc::new(Cell::new(Off));
269 #[allow(deprecated)]
270 input.set_echo(Some(Rc::clone(&verbose)));
271 let mut lexer = Lexer::new(input);
272 #[allow(deprecated)]
273 let mut rel = ReadEvalLoop::new(&mut env, &mut lexer);
274 #[allow(deprecated)]
275 rel.set_verbose(Some(Rc::clone(&verbose)));
276
277 let result = rel.run().now_or_never().unwrap();
278 assert_eq!(result, Continue(()));
279 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
280 assert_eq!(verbose.get(), On);
281 assert_stderr(&state, |stderr| assert_eq!(stderr, "case _ in esac\n"));
282 }
283
284 #[test]
285 fn handling_syntax_error() {
286 let system = VirtualSystem::new();
287 let state = Rc::clone(&system.state);
288 let mut env = Env::with_system(Box::new(system));
289 let mut lexer = Lexer::with_code(";;");
290 #[allow(deprecated)]
291 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
292 let result = rel.run().now_or_never().unwrap();
293 assert_eq!(result, Break(Divert::Interrupt(Some(ExitStatus::ERROR))));
294 assert_stderr(&state, |stderr| assert_ne!(stderr, ""));
295 }
296
297 #[test]
298 fn syntax_error_aborts_loop() {
299 let system = VirtualSystem::new();
300 let state = Rc::clone(&system.state);
301 let mut env = Env::with_system(Box::new(system));
302 env.builtins.insert("echo", echo_builtin());
303 let mut lexer = Lexer::with_code(";;\necho !");
304 #[allow(deprecated)]
305 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
306 let result = rel.run().now_or_never().unwrap();
307 assert_eq!(result, Break(Divert::Interrupt(Some(ExitStatus::ERROR))));
308 assert_stdout(&state, |stdout| assert_eq!(stdout, ""));
309 }
310
311 #[test]
312 fn running_traps_between_parsing_and_executing() {
313 let system = VirtualSystem::new();
314 let state = Rc::clone(&system.state);
315 let mut env = Env::with_system(Box::new(system.clone()));
316 env.builtins.insert("echo", echo_builtin());
317 env.traps
318 .set_action(
319 &mut env.system,
320 SIGUSR1,
321 Action::Command("echo USR1".into()),
322 Location::dummy(""),
323 false,
324 )
325 .unwrap();
326 let _ = state
327 .borrow_mut()
328 .processes
329 .get_mut(&system.process_id)
330 .unwrap()
331 .raise_signal(SIGUSR1);
332 let mut lexer = Lexer::with_code("echo $?");
333 #[allow(deprecated)]
334 let rel = ReadEvalLoop::new(&mut env, &mut lexer);
335 let result = rel.run().now_or_never().unwrap();
336 assert_eq!(result, Continue(()));
337 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
338 assert_stdout(&state, |stdout| assert_eq!(stdout, "USR1\n0\n"));
339 }
340}