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