1use std::{borrow::Cow, fs::File, sync::Arc};
2
3use nu_path::{expand_path, expand_path_with};
4#[cfg(feature = "os")]
5use nu_protocol::process::check_exit_status_future;
6use nu_protocol::{
7 DeclId, ENV_VARIABLE_ID, Flag, IntoPipelineData, IntoSpanned, ListStream, OutDest,
8 PipelineData, PipelineExecutionData, PositionalArg, Range, Record, RegId, ShellError, Signals,
9 Signature, Span, Spanned, Type, Value, VarId,
10 ast::{Bits, Block, Boolean, CellPath, Comparison, Math, Operator},
11 combined_type_string,
12 debugger::DebugContext,
13 engine::{
14 Argument, Closure, EngineState, EnvName, ErrorHandler, Matcher, Redirection, Stack,
15 StateWorkingSet,
16 },
17 ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
18 shell_error::io::IoError,
19};
20use nu_utils::IgnoreCaseExt;
21
22use crate::{
23 ENV_CONVERSIONS, convert_env_vars, eval::is_automatic_env_var, eval_block_with_early_return,
24};
25
26pub fn eval_ir_block<D: DebugContext>(
27 engine_state: &EngineState,
28 stack: &mut Stack,
29 block: &Block,
30 input: PipelineData,
31) -> Result<PipelineExecutionData, ShellError> {
32 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
37 if stack.recursion_count > maximum_call_stack_depth {
38 return Err(ShellError::RecursionLimitReached {
39 recursion_limit: maximum_call_stack_depth,
40 span: block.span,
41 });
42 }
43
44 if let Some(ir_block) = &block.ir_block {
45 D::enter_block(engine_state, block);
46
47 let args_base = stack.arguments.get_base();
48 let error_handler_base = stack.error_handlers.get_base();
49 let finally_handler_base = stack.finally_run_handlers.get_base();
50
51 let mut registers = Vec::with_capacity(ir_block.register_count as usize);
54 for _ in 0..ir_block.register_count {
55 registers.push(PipelineExecutionData::empty());
56 }
57
58 let mut files = vec![None; ir_block.file_count as usize];
60
61 let result = eval_ir_block_impl::<D>(
62 &mut EvalContext {
63 engine_state,
64 stack,
65 data: &ir_block.data,
66 block_span: &block.span,
67 args_base,
68 error_handler_base,
69 finally_handler_base,
70 redirect_out: None,
71 redirect_err: None,
72 matches: vec![],
73 registers: &mut registers[..],
74 files: &mut files[..],
75 },
76 ir_block,
77 input,
78 );
79
80 stack.error_handlers.leave_frame(error_handler_base);
81 stack.finally_run_handlers.leave_frame(finally_handler_base);
82 stack.arguments.leave_frame(args_base);
83
84 D::leave_block(engine_state, block);
85
86 result
87 } else {
88 Err(ShellError::GenericError {
90 error: "Can't evaluate block in IR mode".into(),
91 msg: "block is missing compiled representation".into(),
92 span: block.span,
93 help: Some("the IrBlock is probably missing due to a compilation error".into()),
94 inner: vec![],
95 })
96 }
97}
98
99struct EvalContext<'a> {
101 engine_state: &'a EngineState,
102 stack: &'a mut Stack,
103 data: &'a Arc<[u8]>,
104 block_span: &'a Option<Span>,
106 args_base: usize,
108 error_handler_base: usize,
110 finally_handler_base: usize,
112 redirect_out: Option<Redirection>,
114 redirect_err: Option<Redirection>,
116 matches: Vec<(VarId, Value)>,
118 registers: &'a mut [PipelineExecutionData],
120 files: &'a mut [Option<Arc<File>>],
122}
123
124impl<'a> EvalContext<'a> {
125 #[inline]
127 fn put_reg(&mut self, reg_id: RegId, new_value: PipelineExecutionData) {
128 self.registers[reg_id.get() as usize] = new_value;
130 }
131
132 #[inline]
134 fn borrow_reg(&self, reg_id: RegId) -> &PipelineData {
135 &self.registers[reg_id.get() as usize]
136 }
137
138 #[inline]
140 fn take_reg(&mut self, reg_id: RegId) -> PipelineExecutionData {
141 std::mem::replace(
143 &mut self.registers[reg_id.get() as usize],
144 PipelineExecutionData::empty(),
145 )
146 }
147
148 fn clone_reg(&mut self, reg_id: RegId, error_span: Span) -> Result<PipelineData, ShellError> {
150 match &self.registers[reg_id.get() as usize].body {
153 PipelineData::Empty => Ok(PipelineData::empty()),
154 PipelineData::Value(val, meta) => Ok(PipelineData::value(val.clone(), meta.clone())),
155 _ => Err(ShellError::IrEvalError {
156 msg: "Must collect to value before using instruction that clones from a register"
157 .into(),
158 span: Some(error_span),
159 }),
160 }
161 }
162
163 fn clone_reg_value(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
165 match self.clone_reg(reg_id, fallback_span)? {
166 PipelineData::Empty => Ok(Value::nothing(fallback_span)),
167 PipelineData::Value(val, _) => Ok(val),
168 _ => unreachable!("clone_reg should never return stream data"),
169 }
170 }
171
172 fn collect_reg(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
174 let data = self.take_reg(reg_id);
177 let body = data.body;
178 let span = body.span().unwrap_or(fallback_span);
179 let result = body.into_value(span);
180 #[cfg(feature = "os")]
181 if nu_experimental::PIPE_FAIL.get() {
182 check_exit_status_future(data.exit)?
183 }
184 result
185 }
186
187 fn get_str(&self, slice: DataSlice, error_span: Span) -> Result<&'a str, ShellError> {
189 std::str::from_utf8(&self.data[slice]).map_err(|_| ShellError::IrEvalError {
190 msg: format!("data slice does not refer to valid UTF-8: {slice:?}"),
191 span: Some(error_span),
192 })
193 }
194}
195
196fn eval_ir_block_impl<D: DebugContext>(
198 ctx: &mut EvalContext<'_>,
199 ir_block: &IrBlock,
200 input: PipelineData,
201) -> Result<PipelineExecutionData, ShellError> {
202 if !ctx.registers.is_empty() {
203 ctx.registers[0] = PipelineExecutionData::from(input);
204 }
205
206 let mut pc = 0;
208 let need_backtrace = ctx.engine_state.get_env_var("NU_BACKTRACE").is_some();
209 let mut ret_val = None;
210
211 while pc < ir_block.instructions.len() {
212 let instruction = &ir_block.instructions[pc];
213 let span = &ir_block.spans[pc];
214 let ast = &ir_block.ast[pc];
215
216 D::enter_instruction(ctx.engine_state, ir_block, pc, ctx.registers);
217
218 let result = eval_instruction::<D>(ctx, instruction, span, ast, need_backtrace);
219
220 D::leave_instruction(
221 ctx.engine_state,
222 ir_block,
223 pc,
224 ctx.registers,
225 result.as_ref().err(),
226 );
227
228 match result {
229 Ok(InstructionResult::Continue) => {
230 pc += 1;
231 }
232 Ok(InstructionResult::Branch(next_pc)) => {
233 pc = next_pc;
234 }
235 Ok(InstructionResult::Return(reg_id)) => {
236 match ret_val {
239 Some(err) => return Err(err),
240 None => return Ok(ctx.take_reg(reg_id)),
241 }
242 }
243 Err(err @ (ShellError::Continue { .. } | ShellError::Break { .. })) => {
244 return Err(err);
245 }
246 Err(err @ (ShellError::Return { .. } | ShellError::Exit { .. })) => {
247 if let Some(always_run_handler) =
248 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base)
249 {
250 prepare_error_handler(ctx, always_run_handler, None);
253 pc = always_run_handler.handler_index;
254 ret_val = Some(err);
255 } else {
256 return Err(err);
258 }
259 }
260 Err(err) => {
261 if let Some(error_handler) = ctx.stack.error_handlers.pop(ctx.error_handler_base) {
262 prepare_error_handler(ctx, error_handler, Some(err.into_spanned(*span)));
264 pc = error_handler.handler_index;
265 } else if let Some(always_run_handler) =
266 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base)
267 {
268 prepare_error_handler(ctx, always_run_handler, Some(err.into_spanned(*span)));
269 pc = always_run_handler.handler_index;
270 } else if need_backtrace {
271 let err = ShellError::into_chained(err, *span);
272 return Err(err);
273 } else {
274 return Err(err);
275 }
276 }
277 }
278 }
279
280 Err(ShellError::IrEvalError {
282 msg: format!(
283 "Program counter out of range (pc={pc}, len={len})",
284 len = ir_block.instructions.len(),
285 ),
286 span: *ctx.block_span,
287 })
288}
289
290fn prepare_error_handler(
292 ctx: &mut EvalContext<'_>,
293 error_handler: ErrorHandler,
294 error: Option<Spanned<ShellError>>,
295) {
296 if let Some(reg_id) = error_handler.error_register {
297 if let Some(error) = error {
298 ctx.stack.set_last_error(&error.item);
300 ctx.put_reg(
302 reg_id,
303 PipelineExecutionData::from(
304 error
305 .item
306 .into_full_value(
307 &StateWorkingSet::new(ctx.engine_state),
308 ctx.stack,
309 error.span,
310 )
311 .into_pipeline_data(),
312 ),
313 );
314 } else {
315 ctx.put_reg(reg_id, PipelineExecutionData::empty());
317 }
318 }
319}
320
321#[derive(Debug)]
323enum InstructionResult {
324 Continue,
325 Branch(usize),
326 Return(RegId),
327}
328
329fn eval_instruction<D: DebugContext>(
331 ctx: &mut EvalContext<'_>,
332 instruction: &Instruction,
333 span: &Span,
334 ast: &Option<IrAstRef>,
335 need_backtrace: bool,
336) -> Result<InstructionResult, ShellError> {
337 use self::InstructionResult::*;
338
339 instruction.check_interrupt(ctx.engine_state, span)?;
341
342 match instruction {
345 Instruction::Unreachable => Err(ShellError::IrEvalError {
346 msg: "Reached unreachable code".into(),
347 span: Some(*span),
348 }),
349 Instruction::LoadLiteral { dst, lit } => load_literal(ctx, *dst, lit, *span),
350 Instruction::LoadValue { dst, val } => {
351 ctx.put_reg(
352 *dst,
353 PipelineExecutionData::from(Value::clone(val).into_pipeline_data()),
354 );
355 Ok(Continue)
356 }
357 Instruction::Move { dst, src } => {
358 let val = ctx.take_reg(*src);
359 ctx.put_reg(*dst, val);
360 Ok(Continue)
361 }
362 Instruction::Clone { dst, src } => {
363 let data = ctx.clone_reg(*src, *span)?;
364 ctx.put_reg(*dst, PipelineExecutionData::from(data));
365 Ok(Continue)
366 }
367 Instruction::Collect { src_dst } => {
368 let data = ctx.take_reg(*src_dst);
369 let value = collect(data, *span)?;
370 ctx.put_reg(*src_dst, PipelineExecutionData::from(value));
371 Ok(Continue)
372 }
373 Instruction::Span { src_dst } => {
374 let mut data = ctx.take_reg(*src_dst);
375 data.body = data.body.with_span(*span);
376 ctx.put_reg(*src_dst, data);
377 Ok(Continue)
378 }
379 Instruction::Drop { src } => {
380 ctx.take_reg(*src);
381 Ok(Continue)
382 }
383 Instruction::Drain { src } => {
384 let data = ctx.take_reg(*src);
385 drain(ctx, data)
386 }
387 Instruction::DrainIfEnd { src } => {
388 let data = ctx.take_reg(*src);
389 let res = drain_if_end(ctx, data)?;
390 ctx.put_reg(*src, PipelineExecutionData::from(res));
391 Ok(Continue)
392 }
393 Instruction::LoadVariable { dst, var_id } => {
394 let value = get_var(ctx, *var_id, *span)?;
395 ctx.put_reg(
396 *dst,
397 PipelineExecutionData::from(value.into_pipeline_data()),
398 );
399 Ok(Continue)
400 }
401 Instruction::StoreVariable { var_id, src } => {
402 let value = ctx.collect_reg(*src, *span)?;
403 if nu_experimental::ENFORCE_RUNTIME_ANNOTATIONS.get() {
405 let variable = ctx.engine_state.get_var(*var_id);
406 let converted_value = check_assignment_type(value, &variable.ty)?;
407 ctx.stack.add_var(*var_id, converted_value);
408 } else {
409 ctx.stack.add_var(*var_id, value);
410 }
411 Ok(Continue)
412 }
413 Instruction::DropVariable { var_id } => {
414 ctx.stack.remove_var(*var_id);
415 Ok(Continue)
416 }
417 Instruction::LoadEnv { dst, key } => {
418 let key = ctx.get_str(*key, *span)?;
419 if let Some(value) = get_env_var(ctx, key) {
420 let new_value = value.clone().into_pipeline_data();
421 ctx.put_reg(*dst, PipelineExecutionData::from(new_value));
422 Ok(Continue)
423 } else {
424 Err(ShellError::CantFindColumn {
427 col_name: key.into(),
428 span: Some(*span),
429 src_span: *span,
430 })
431 }
432 }
433 Instruction::LoadEnvOpt { dst, key } => {
434 let key = ctx.get_str(*key, *span)?;
435 let value = get_env_var(ctx, key)
436 .cloned()
437 .unwrap_or(Value::nothing(*span));
438 ctx.put_reg(
439 *dst,
440 PipelineExecutionData::from(value.into_pipeline_data()),
441 );
442 Ok(Continue)
443 }
444 Instruction::StoreEnv { key, src } => {
445 let key = ctx.get_str(*key, *span)?;
446 let value = ctx.collect_reg(*src, *span)?;
447
448 let key = get_env_var_name(ctx, key);
449
450 if !is_automatic_env_var(&key) {
451 let is_config = key == "config";
452 let update_conversions = key == ENV_CONVERSIONS;
453
454 ctx.stack.add_env_var(key.into_owned(), value.clone());
455
456 if is_config {
457 ctx.stack.update_config(ctx.engine_state)?;
458 }
459 if update_conversions {
460 convert_env_vars(ctx.stack, ctx.engine_state, &value)?;
461 }
462 Ok(Continue)
463 } else {
464 Err(ShellError::AutomaticEnvVarSetManually {
465 envvar_name: key.into(),
466 span: *span,
467 })
468 }
469 }
470 Instruction::PushPositional { src } => {
471 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
472 ctx.stack.arguments.push(Argument::Positional {
473 span: *span,
474 val,
475 ast: ast.clone().map(|ast_ref| ast_ref.0),
476 });
477 Ok(Continue)
478 }
479 Instruction::AppendRest { src } => {
480 let vals = ctx.collect_reg(*src, *span)?.with_span(*span);
481 ctx.stack.arguments.push(Argument::Spread {
482 span: *span,
483 vals,
484 ast: ast.clone().map(|ast_ref| ast_ref.0),
485 });
486 Ok(Continue)
487 }
488 Instruction::PushFlag { name } => {
489 let data = ctx.data.clone();
490 ctx.stack.arguments.push(Argument::Flag {
491 data,
492 name: *name,
493 short: DataSlice::empty(),
494 span: *span,
495 });
496 Ok(Continue)
497 }
498 Instruction::PushShortFlag { short } => {
499 let data = ctx.data.clone();
500 ctx.stack.arguments.push(Argument::Flag {
501 data,
502 name: DataSlice::empty(),
503 short: *short,
504 span: *span,
505 });
506 Ok(Continue)
507 }
508 Instruction::PushNamed { name, src } => {
509 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
510 let data = ctx.data.clone();
511 ctx.stack.arguments.push(Argument::Named {
512 data,
513 name: *name,
514 short: DataSlice::empty(),
515 span: *span,
516 val,
517 ast: ast.clone().map(|ast_ref| ast_ref.0),
518 });
519 Ok(Continue)
520 }
521 Instruction::PushShortNamed { short, src } => {
522 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
523 let data = ctx.data.clone();
524 ctx.stack.arguments.push(Argument::Named {
525 data,
526 name: DataSlice::empty(),
527 short: *short,
528 span: *span,
529 val,
530 ast: ast.clone().map(|ast_ref| ast_ref.0),
531 });
532 Ok(Continue)
533 }
534 Instruction::PushParserInfo { name, info } => {
535 let data = ctx.data.clone();
536 ctx.stack.arguments.push(Argument::ParserInfo {
537 data,
538 name: *name,
539 info: info.clone(),
540 });
541 Ok(Continue)
542 }
543 Instruction::RedirectOut { mode } => {
544 ctx.redirect_out = eval_redirection(ctx, mode, *span, RedirectionStream::Out)?;
545 Ok(Continue)
546 }
547 Instruction::RedirectErr { mode } => {
548 ctx.redirect_err = eval_redirection(ctx, mode, *span, RedirectionStream::Err)?;
549 Ok(Continue)
550 }
551 Instruction::CheckErrRedirected { src } => match ctx.borrow_reg(*src) {
552 #[cfg(feature = "os")]
553 PipelineData::ByteStream(stream, _)
554 if matches!(stream.source(), nu_protocol::ByteStreamSource::Child(_)) =>
555 {
556 Ok(Continue)
557 }
558 _ => Err(ShellError::GenericError {
559 error: "Can't redirect stderr of internal command output".into(),
560 msg: "piping stderr only works on external commands".into(),
561 span: Some(*span),
562 help: None,
563 inner: vec![],
564 }),
565 },
566 Instruction::OpenFile {
567 file_num,
568 path,
569 append,
570 } => {
571 let path = ctx.collect_reg(*path, *span)?;
572 let file = open_file(ctx, &path, *append)?;
573 ctx.files[*file_num as usize] = Some(file);
574 Ok(Continue)
575 }
576 Instruction::WriteFile { file_num, src } => {
577 let src = ctx.take_reg(*src);
578 let file = ctx
579 .files
580 .get(*file_num as usize)
581 .cloned()
582 .flatten()
583 .ok_or_else(|| ShellError::IrEvalError {
584 msg: format!("Tried to write to file #{file_num}, but it is not open"),
585 span: Some(*span),
586 })?;
587 let is_external = if let PipelineData::ByteStream(stream, ..) = &src.body {
588 stream.source().is_external()
589 } else {
590 false
591 };
592 if let Err(err) = src.body.write_to(file.as_ref()) {
593 if is_external {
594 ctx.stack.set_last_error(&err);
595 }
596 Err(err)?
597 } else {
598 Ok(Continue)
599 }
600 }
601 Instruction::CloseFile { file_num } => {
602 if ctx.files[*file_num as usize].take().is_some() {
603 Ok(Continue)
604 } else {
605 Err(ShellError::IrEvalError {
606 msg: format!("Tried to close file #{file_num}, but it is not open"),
607 span: Some(*span),
608 })
609 }
610 }
611 Instruction::Call { decl_id, src_dst } => {
612 let input = ctx.take_reg(*src_dst);
613 let input_data = input.body;
615 let mut result = eval_call::<D>(ctx, *decl_id, *span, input_data)?;
616 if need_backtrace {
617 match &mut result {
618 PipelineData::ByteStream(s, ..) => s.push_caller_span(*span),
619 PipelineData::ListStream(s, ..) => s.push_caller_span(*span),
620 _ => (),
621 };
622 }
623 #[cfg(feature = "os")]
627 {
628 let mut original_exit = input.exit;
629 if ctx.engine_state.get_decl(*decl_id).name() == "complete" {
633 original_exit.clear();
634 }
635 let result_exit_status_future = result
636 .clone_exit_status_future()
637 .map(|f| f.with_span(*span));
638 original_exit.push(result_exit_status_future);
639 ctx.put_reg(
640 *src_dst,
641 PipelineExecutionData {
642 body: result,
643 exit: original_exit,
644 },
645 );
646 }
647 #[cfg(not(feature = "os"))]
648 ctx.put_reg(*src_dst, PipelineExecutionData { body: result });
649 Ok(Continue)
650 }
651 Instruction::StringAppend { src_dst, val } => {
652 let string_value = ctx.collect_reg(*src_dst, *span)?;
653 let operand_value = ctx.collect_reg(*val, *span)?;
654 let string_span = string_value.span();
655
656 let mut string = string_value.into_string()?;
657 let operand = if let Value::String { val, .. } = operand_value {
658 val
660 } else {
661 operand_value.to_expanded_string(", ", &ctx.stack.get_config(ctx.engine_state))
662 };
663 string.push_str(&operand);
664
665 let new_string_value = Value::string(string, string_span);
666 ctx.put_reg(
667 *src_dst,
668 PipelineExecutionData::from(new_string_value.into_pipeline_data()),
669 );
670 Ok(Continue)
671 }
672 Instruction::GlobFrom { src_dst, no_expand } => {
673 let string_value = ctx.collect_reg(*src_dst, *span)?;
674 let glob_value = if let Value::Glob { .. } = string_value {
675 string_value
677 } else {
678 let string = string_value.into_string()?;
680 Value::glob(string, *no_expand, *span)
681 };
682 ctx.put_reg(
683 *src_dst,
684 PipelineExecutionData::from(glob_value.into_pipeline_data()),
685 );
686 Ok(Continue)
687 }
688 Instruction::ListPush { src_dst, item } => {
689 let list_value = ctx.collect_reg(*src_dst, *span)?;
690 let item = ctx.collect_reg(*item, *span)?;
691 let list_span = list_value.span();
692 let mut list = list_value.into_list()?;
693 list.push(item);
694 ctx.put_reg(
695 *src_dst,
696 PipelineExecutionData::from(Value::list(list, list_span).into_pipeline_data()),
697 );
698 Ok(Continue)
699 }
700 Instruction::ListSpread { src_dst, items } => {
701 let list_value = ctx.collect_reg(*src_dst, *span)?;
702 let items = ctx.collect_reg(*items, *span)?;
703 let list_span = list_value.span();
704 let items_span = items.span();
705 let items = match items {
706 Value::List { vals, .. } => vals,
707 Value::Nothing { .. } => Vec::new(),
708 _ => return Err(ShellError::CannotSpreadAsList { span: items_span }),
709 };
710 let mut list = list_value.into_list()?;
711 list.extend(items);
712 ctx.put_reg(
713 *src_dst,
714 PipelineExecutionData::from(Value::list(list, list_span).into_pipeline_data()),
715 );
716 Ok(Continue)
717 }
718 Instruction::RecordInsert { src_dst, key, val } => {
719 let record_value = ctx.collect_reg(*src_dst, *span)?;
720 let key = ctx.collect_reg(*key, *span)?;
721 let val = ctx.collect_reg(*val, *span)?;
722 let record_span = record_value.span();
723 let mut record = record_value.into_record()?;
724
725 let key = key.coerce_into_string()?;
726 if let Some(old_value) = record.insert(&key, val) {
727 return Err(ShellError::ColumnDefinedTwice {
728 col_name: key,
729 second_use: *span,
730 first_use: old_value.span(),
731 });
732 }
733
734 ctx.put_reg(
735 *src_dst,
736 PipelineExecutionData::from(
737 Value::record(record, record_span).into_pipeline_data(),
738 ),
739 );
740 Ok(Continue)
741 }
742 Instruction::RecordSpread { src_dst, items } => {
743 let record_value = ctx.collect_reg(*src_dst, *span)?;
744 let items = ctx.collect_reg(*items, *span)?;
745 let record_span = record_value.span();
746 let items_span = items.span();
747 let mut record = record_value.into_record()?;
748 let items = match items {
749 Value::Record { val, .. } => val.into_owned(),
750 Value::Nothing { .. } => Record::new(),
751 _ => return Err(ShellError::CannotSpreadAsRecord { span: items_span }),
752 };
753 for (key, val) in items {
755 if let Some(first_value) = record.insert(&key, val) {
756 return Err(ShellError::ColumnDefinedTwice {
757 col_name: key,
758 second_use: *span,
759 first_use: first_value.span(),
760 });
761 }
762 }
763 ctx.put_reg(
764 *src_dst,
765 PipelineExecutionData::from(
766 Value::record(record, record_span).into_pipeline_data(),
767 ),
768 );
769 Ok(Continue)
770 }
771 Instruction::Not { src_dst } => {
772 let bool = ctx.collect_reg(*src_dst, *span)?;
773 let negated = !bool.as_bool()?;
774 ctx.put_reg(
775 *src_dst,
776 PipelineExecutionData::from(Value::bool(negated, bool.span()).into_pipeline_data()),
777 );
778 Ok(Continue)
779 }
780 Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
781 Instruction::FollowCellPath { src_dst, path } => {
782 let data = ctx.take_reg(*src_dst);
783 let path = ctx.take_reg(*path);
784 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
785 let value = data.body.follow_cell_path(&path.members, *span)?;
786 ctx.put_reg(
787 *src_dst,
788 PipelineExecutionData::from(value.into_pipeline_data()),
789 );
790 Ok(Continue)
791 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
792 Err(*error)
793 } else {
794 Err(ShellError::TypeMismatch {
795 err_message: "expected cell path".into(),
796 span: path.span().unwrap_or(*span),
797 })
798 }
799 }
800 Instruction::CloneCellPath { dst, src, path } => {
801 let value = ctx.clone_reg_value(*src, *span)?;
802 let path = ctx.take_reg(*path);
803 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
804 let value = value.follow_cell_path(&path.members)?;
805 ctx.put_reg(
806 *dst,
807 PipelineExecutionData::from(value.into_owned().into_pipeline_data()),
808 );
809 Ok(Continue)
810 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
811 Err(*error)
812 } else {
813 Err(ShellError::TypeMismatch {
814 err_message: "expected cell path".into(),
815 span: path.span().unwrap_or(*span),
816 })
817 }
818 }
819 Instruction::UpsertCellPath {
820 src_dst,
821 path,
822 new_value,
823 } => {
824 let data = ctx.take_reg(*src_dst);
825 let metadata = data.metadata();
826 let mut value = data.body.into_value(*span)?;
828 let path = ctx.take_reg(*path);
829 let new_value = ctx.collect_reg(*new_value, *span)?;
830 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
831 value.upsert_data_at_cell_path(&path.members, new_value)?;
832 ctx.put_reg(
833 *src_dst,
834 PipelineExecutionData::from(value.into_pipeline_data_with_metadata(metadata)),
835 );
836 Ok(Continue)
837 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
838 Err(*error)
839 } else {
840 Err(ShellError::TypeMismatch {
841 err_message: "expected cell path".into(),
842 span: path.span().unwrap_or(*span),
843 })
844 }
845 }
846 Instruction::Jump { index } => Ok(Branch(*index)),
847 Instruction::BranchIf { cond, index } => {
848 let data = ctx.take_reg(*cond);
849 let data_span = data.span();
850 let val = match data.body {
851 PipelineData::Value(Value::Bool { val, .. }, _) => val,
852 PipelineData::Value(Value::Error { error, .. }, _) => {
853 return Err(*error);
854 }
855 _ => {
856 return Err(ShellError::TypeMismatch {
857 err_message: "expected bool".into(),
858 span: data_span.unwrap_or(*span),
859 });
860 }
861 };
862 if val {
863 Ok(Branch(*index))
864 } else {
865 Ok(Continue)
866 }
867 }
868 Instruction::BranchIfEmpty { src, index } => {
869 let is_empty = matches!(
870 ctx.borrow_reg(*src),
871 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, _)
872 );
873
874 if is_empty {
875 Ok(Branch(*index))
876 } else {
877 Ok(Continue)
878 }
879 }
880 Instruction::Match {
881 pattern,
882 src,
883 index,
884 } => {
885 let value = ctx.clone_reg_value(*src, *span)?;
886 ctx.matches.clear();
887 if pattern.match_value(&value, &mut ctx.matches) {
888 for (var_id, match_value) in ctx.matches.drain(..) {
890 ctx.stack.add_var(var_id, match_value);
891 }
892 Ok(Branch(*index))
893 } else {
894 ctx.matches.clear();
896 Ok(Continue)
897 }
898 }
899 Instruction::CheckMatchGuard { src } => {
900 if matches!(
901 ctx.borrow_reg(*src),
902 PipelineData::Value(Value::Bool { .. }, _)
903 ) {
904 Ok(Continue)
905 } else {
906 Err(ShellError::MatchGuardNotBool { span: *span })
907 }
908 }
909 Instruction::Iterate {
910 dst,
911 stream,
912 end_index,
913 } => eval_iterate(ctx, *dst, *stream, *end_index),
914 Instruction::OnError { index } => {
915 ctx.stack.error_handlers.push(ErrorHandler {
916 handler_index: *index,
917 error_register: None,
918 });
919 Ok(Continue)
920 }
921 Instruction::OnErrorInto { index, dst } => {
922 ctx.stack.error_handlers.push(ErrorHandler {
923 handler_index: *index,
924 error_register: Some(*dst),
925 });
926 Ok(Continue)
927 }
928 Instruction::Finally { index } => {
929 ctx.stack.finally_run_handlers.push(ErrorHandler {
930 handler_index: *index,
931 error_register: None,
932 });
933 Ok(Continue)
934 }
935 Instruction::FinallyInto { index, dst } => {
936 ctx.stack.finally_run_handlers.push(ErrorHandler {
937 handler_index: *index,
938 error_register: Some(*dst),
939 });
940 Ok(Continue)
941 }
942 Instruction::PopErrorHandler => {
943 ctx.stack.error_handlers.pop(ctx.error_handler_base);
944 Ok(Continue)
945 }
946 Instruction::PopFinallyRun => {
947 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base);
948 Ok(Continue)
949 }
950 Instruction::ReturnEarly { src } => {
951 let val = ctx.collect_reg(*src, *span)?;
952 Err(ShellError::Return {
953 span: *span,
954 value: Box::new(val),
955 })
956 }
957 Instruction::Return { src } => Ok(Return(*src)),
958 }
959}
960
961fn load_literal(
963 ctx: &mut EvalContext<'_>,
964 dst: RegId,
965 lit: &Literal,
966 span: Span,
967) -> Result<InstructionResult, ShellError> {
968 if matches!(lit, Literal::Empty) {
974 ctx.put_reg(dst, PipelineExecutionData::empty());
975 } else {
976 let value = literal_value(ctx, lit, span)?;
977 ctx.put_reg(
978 dst,
979 PipelineExecutionData::from(PipelineData::value(value, None)),
980 );
981 }
982 Ok(InstructionResult::Continue)
983}
984
985fn literal_value(
986 ctx: &mut EvalContext<'_>,
987 lit: &Literal,
988 span: Span,
989) -> Result<Value, ShellError> {
990 Ok(match lit {
991 Literal::Bool(b) => Value::bool(*b, span),
992 Literal::Int(i) => Value::int(*i, span),
993 Literal::Float(f) => Value::float(*f, span),
994 Literal::Filesize(q) => Value::filesize(*q, span),
995 Literal::Duration(q) => Value::duration(*q, span),
996 Literal::Binary(bin) => Value::binary(&ctx.data[*bin], span),
997 Literal::Block(block_id) | Literal::RowCondition(block_id) | Literal::Closure(block_id) => {
998 let block = ctx.engine_state.get_block(*block_id);
999 let captures = block
1000 .captures
1001 .iter()
1002 .map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
1003 .collect::<Result<Vec<_>, ShellError>>()?;
1004 Value::closure(
1005 Closure {
1006 block_id: *block_id,
1007 captures,
1008 },
1009 span,
1010 )
1011 }
1012 Literal::Range {
1013 start,
1014 step,
1015 end,
1016 inclusion,
1017 } => {
1018 let start = ctx.collect_reg(*start, span)?;
1019 let step = ctx.collect_reg(*step, span)?;
1020 let end = ctx.collect_reg(*end, span)?;
1021 let range = Range::new(start, step, end, *inclusion, span)?;
1022 Value::range(range, span)
1023 }
1024 Literal::List { capacity } => Value::list(Vec::with_capacity(*capacity), span),
1025 Literal::Record { capacity } => Value::record(Record::with_capacity(*capacity), span),
1026 Literal::Filepath {
1027 val: path,
1028 no_expand,
1029 } => {
1030 let path = ctx.get_str(*path, span)?;
1031 if *no_expand {
1032 Value::string(path, span)
1033 } else {
1034 let path = expand_path(path, true);
1035 Value::string(path.to_string_lossy(), span)
1036 }
1037 }
1038 Literal::Directory {
1039 val: path,
1040 no_expand,
1041 } => {
1042 let path = ctx.get_str(*path, span)?;
1043 if path == "-" {
1044 Value::string("-", span)
1045 } else if *no_expand {
1046 Value::string(path, span)
1047 } else {
1048 let path = expand_path(path, true);
1049 Value::string(path.to_string_lossy(), span)
1050 }
1051 }
1052 Literal::GlobPattern { val, no_expand } => {
1053 Value::glob(ctx.get_str(*val, span)?, *no_expand, span)
1054 }
1055 Literal::String(s) => Value::string(ctx.get_str(*s, span)?, span),
1056 Literal::RawString(s) => Value::string(ctx.get_str(*s, span)?, span),
1057 Literal::CellPath(path) => Value::cell_path(CellPath::clone(path), span),
1058 Literal::Date(dt) => Value::date(**dt, span),
1059 Literal::Nothing => Value::nothing(span),
1060 Literal::Empty => Value::nothing(span),
1062 })
1063}
1064
1065fn binary_op(
1066 ctx: &mut EvalContext<'_>,
1067 lhs_dst: RegId,
1068 op: &Operator,
1069 rhs: RegId,
1070 span: Span,
1071) -> Result<InstructionResult, ShellError> {
1072 let lhs_val = ctx.collect_reg(lhs_dst, span)?;
1073 let rhs_val = ctx.collect_reg(rhs, span)?;
1074
1075 if let Value::Error { error, .. } = lhs_val {
1077 return Err(*error);
1078 }
1079 if let Value::Error { error, .. } = rhs_val {
1080 return Err(*error);
1081 }
1082
1083 let op_span = span;
1086
1087 let result = match op {
1088 Operator::Comparison(cmp) => match cmp {
1089 Comparison::Equal => lhs_val.eq(op_span, &rhs_val, span)?,
1090 Comparison::NotEqual => lhs_val.ne(op_span, &rhs_val, span)?,
1091 Comparison::LessThan => lhs_val.lt(op_span, &rhs_val, span)?,
1092 Comparison::GreaterThan => lhs_val.gt(op_span, &rhs_val, span)?,
1093 Comparison::LessThanOrEqual => lhs_val.lte(op_span, &rhs_val, span)?,
1094 Comparison::GreaterThanOrEqual => lhs_val.gte(op_span, &rhs_val, span)?,
1095 Comparison::RegexMatch => {
1096 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, false, span)?
1097 }
1098 Comparison::NotRegexMatch => {
1099 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, true, span)?
1100 }
1101 Comparison::In => lhs_val.r#in(op_span, &rhs_val, span)?,
1102 Comparison::NotIn => lhs_val.not_in(op_span, &rhs_val, span)?,
1103 Comparison::Has => lhs_val.has(op_span, &rhs_val, span)?,
1104 Comparison::NotHas => lhs_val.not_has(op_span, &rhs_val, span)?,
1105 Comparison::StartsWith => lhs_val.starts_with(op_span, &rhs_val, span)?,
1106 Comparison::NotStartsWith => lhs_val.not_starts_with(op_span, &rhs_val, span)?,
1107 Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
1108 Comparison::NotEndsWith => lhs_val.not_ends_with(op_span, &rhs_val, span)?,
1109 },
1110 Operator::Math(mat) => match mat {
1111 Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
1112 Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
1113 Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
1114 Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
1115 Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
1116 Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
1117 Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
1118 Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
1119 },
1120 Operator::Boolean(bl) => match bl {
1121 Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
1122 Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
1123 Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
1124 },
1125 Operator::Bits(bit) => match bit {
1126 Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
1127 Bits::BitXor => lhs_val.bit_xor(op_span, &rhs_val, span)?,
1128 Bits::BitAnd => lhs_val.bit_and(op_span, &rhs_val, span)?,
1129 Bits::ShiftLeft => lhs_val.bit_shl(op_span, &rhs_val, span)?,
1130 Bits::ShiftRight => lhs_val.bit_shr(op_span, &rhs_val, span)?,
1131 },
1132 Operator::Assignment(_asg) => {
1133 return Err(ShellError::IrEvalError {
1134 msg: "can't eval assignment with the `binary-op` instruction".into(),
1135 span: Some(span),
1136 });
1137 }
1138 };
1139
1140 ctx.put_reg(
1141 lhs_dst,
1142 PipelineExecutionData::from(PipelineData::value(result, None)),
1143 );
1144
1145 Ok(InstructionResult::Continue)
1146}
1147
1148fn eval_call<D: DebugContext>(
1150 ctx: &mut EvalContext<'_>,
1151 decl_id: DeclId,
1152 head: Span,
1153 mut input: PipelineData,
1154) -> Result<PipelineData, ShellError> {
1155 let EvalContext {
1156 engine_state,
1157 stack: caller_stack,
1158 args_base,
1159 redirect_out,
1160 redirect_err,
1161 ..
1162 } = ctx;
1163
1164 let args_len = caller_stack.arguments.get_len(*args_base);
1165 let decl = engine_state.get_decl(decl_id);
1166
1167 let mut caller_stack = caller_stack.push_redirection(redirect_out.take(), redirect_err.take());
1169
1170 let result = (|| {
1171 if let Some(block_id) = decl.block_id() {
1172 let block = engine_state.get_block(block_id);
1174
1175 check_input_types(&input, &block.signature, head)?;
1177
1178 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1180
1181 gather_arguments(
1182 engine_state,
1183 block,
1184 &mut caller_stack,
1185 &mut callee_stack,
1186 *args_base,
1187 args_len,
1188 head,
1189 )?;
1190
1191 callee_stack.recursion_count += 1;
1194
1195 let result =
1196 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input)
1197 .map(|p| p.body);
1198
1199 if block.redirect_env {
1201 redirect_env(engine_state, &mut caller_stack, &callee_stack);
1202 }
1203
1204 result
1205 } else {
1206 check_input_types(&input, &decl.signature(), head)?;
1207 let span = Span::merge_many(
1209 std::iter::once(head).chain(
1210 caller_stack
1211 .arguments
1212 .get_args(*args_base, args_len)
1213 .iter()
1214 .flat_map(|arg| arg.span()),
1215 ),
1216 );
1217
1218 let call = Call {
1219 decl_id,
1220 head,
1221 span,
1222 args_base: *args_base,
1223 args_len,
1224 };
1225
1226 if let PipelineData::Value(v, ..) = &mut input {
1229 v.inject_signals(engine_state);
1230 }
1231 decl.run(engine_state, &mut caller_stack, &(&call).into(), input)
1233 }
1234 })();
1235
1236 drop(caller_stack);
1237
1238 ctx.stack.arguments.leave_frame(ctx.args_base);
1240 ctx.redirect_out = None;
1241 ctx.redirect_err = None;
1242
1243 result
1244}
1245
1246fn find_named_var_id(
1247 sig: &Signature,
1248 name: &[u8],
1249 short: &[u8],
1250 span: Span,
1251) -> Result<VarId, ShellError> {
1252 sig.named
1253 .iter()
1254 .find(|n| {
1255 if !n.long.is_empty() {
1256 n.long.as_bytes() == name
1257 } else {
1258 n.short
1260 .is_some_and(|s| s.encode_utf8(&mut [0; 4]).as_bytes() == short)
1261 }
1262 })
1263 .ok_or_else(|| ShellError::IrEvalError {
1264 msg: format!(
1265 "block does not have an argument named `{}`",
1266 String::from_utf8_lossy(name)
1267 ),
1268 span: Some(span),
1269 })
1270 .and_then(|flag| expect_named_var_id(flag, span))
1271}
1272
1273fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1274 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1275 msg: format!(
1276 "block signature is missing var id for named arg `{}`",
1277 arg.long
1278 ),
1279 span: Some(span),
1280 })
1281}
1282
1283fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1284 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1285 msg: format!(
1286 "block signature is missing var id for positional arg `{}`",
1287 arg.name
1288 ),
1289 span: Some(span),
1290 })
1291}
1292
1293fn gather_arguments(
1295 engine_state: &EngineState,
1296 block: &Block,
1297 caller_stack: &mut Stack,
1298 callee_stack: &mut Stack,
1299 args_base: usize,
1300 args_len: usize,
1301 call_head: Span,
1302) -> Result<(), ShellError> {
1303 let mut positional_iter = block
1304 .signature
1305 .required_positional
1306 .iter()
1307 .map(|p| (p, true))
1308 .chain(
1309 block
1310 .signature
1311 .optional_positional
1312 .iter()
1313 .map(|p| (p, false)),
1314 );
1315
1316 let mut rest = vec![];
1318 let mut rest_span: Option<Span> = None;
1319
1320 let mut always_spread = false;
1322
1323 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1324 match arg {
1325 Argument::Positional { span, val, .. } => {
1326 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1328 if let Some((positional_arg, required)) = next {
1329 let var_id = expect_positional_var_id(positional_arg, span)?;
1330 if required {
1331 let variable = engine_state.get_var(var_id);
1334 check_type(&val, &variable.ty)?;
1335 }
1336 callee_stack.add_var(var_id, val);
1337 } else {
1338 rest_span = Some(rest_span.map_or(val.span(), |s| s.append(val.span())));
1339 rest.push(val);
1340 }
1341 }
1342 Argument::Spread {
1343 vals,
1344 span: spread_span,
1345 ..
1346 } => match vals {
1347 Value::List { vals, .. } => {
1348 rest.extend(vals);
1349 rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
1350 always_spread = true;
1351 }
1352 Value::Nothing { .. } => {
1353 rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
1354 always_spread = true;
1355 }
1356 Value::Error { error, .. } => return Err(*error),
1357 _ => return Err(ShellError::CannotSpreadAsList { span: vals.span() }),
1358 },
1359 Argument::Flag {
1360 data,
1361 name,
1362 short,
1363 span,
1364 } => {
1365 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1366 callee_stack.add_var(var_id, Value::bool(true, span))
1367 }
1368 Argument::Named {
1369 data,
1370 name,
1371 short,
1372 span,
1373 val,
1374 ..
1375 } => {
1376 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1377 callee_stack.add_var(var_id, val)
1378 }
1379 Argument::ParserInfo { .. } => (),
1380 }
1381 }
1382
1383 if let Some(rest_arg) = &block.signature.rest_positional {
1385 let rest_span = rest_span.unwrap_or(call_head);
1386 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1387 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1388 }
1389
1390 for (positional_arg, _) in positional_iter {
1392 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1393 callee_stack.add_var(
1394 var_id,
1395 positional_arg
1396 .default_value
1397 .clone()
1398 .unwrap_or(Value::nothing(call_head)),
1399 );
1400 }
1401
1402 for named_arg in &block.signature.named {
1403 if let Some(var_id) = named_arg.var_id {
1404 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1408 let val = if named_arg.arg.is_none() {
1409 Value::bool(false, call_head)
1410 } else if let Some(value) = &named_arg.default_value {
1411 value.clone()
1412 } else {
1413 Value::nothing(call_head)
1414 };
1415 callee_stack.add_var(var_id, val);
1416 }
1417 }
1418 }
1419
1420 Ok(())
1421}
1422
1423fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1425 match val {
1426 Value::Error { error, .. } => Err(*error.clone()),
1427 _ if val.is_subtype_of(ty) => Ok(()),
1428 _ => Err(ShellError::CantConvert {
1429 to_type: ty.to_string(),
1430 from_type: val.get_type().to_string(),
1431 span: val.span(),
1432 help: None,
1433 }),
1434 }
1435}
1436
1437fn check_assignment_type(val: Value, target_ty: &Type) -> Result<Value, ShellError> {
1439 match val {
1440 Value::Error { error, .. } => Err(*error),
1441 _ if val.is_subtype_of(target_ty) => Ok(val), _ => Err(ShellError::CantConvert {
1443 to_type: target_ty.to_string(),
1444 from_type: val.get_type().to_string(),
1445 span: val.span(),
1446 help: None,
1447 }),
1448 }
1449}
1450
1451fn check_input_types(
1453 input: &PipelineData,
1454 signature: &Signature,
1455 head: Span,
1456) -> Result<(), ShellError> {
1457 let io_types = &signature.input_output_types;
1458
1459 if io_types.is_empty() {
1461 return Ok(());
1462 }
1463
1464 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1466 return Ok(());
1467 }
1468
1469 match input {
1470 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1472 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1474 _ => (),
1475 }
1476
1477 if io_types
1479 .iter()
1480 .any(|(command_type, _)| input.is_subtype_of(command_type))
1481 {
1482 return Ok(());
1483 }
1484
1485 let input_types: Vec<Type> = io_types.iter().map(|(input, _)| input.clone()).collect();
1486 let expected_string = combined_type_string(&input_types, "and");
1487
1488 match (input, expected_string) {
1489 (PipelineData::Empty, _) => Err(ShellError::PipelineEmpty { dst_span: head }),
1490 (_, Some(expected_string)) => Err(ShellError::OnlySupportsThisInputType {
1491 exp_input_type: expected_string,
1492 wrong_type: input.get_type().to_string(),
1493 dst_span: head,
1494 src_span: input.span().unwrap_or(Span::unknown()),
1495 }),
1496 (_, None) => Err(ShellError::NushellFailed {
1498 msg: "Command input type strings is empty, despite being non-zero earlier".to_string(),
1499 }),
1500 }
1501}
1502
1503fn get_var(ctx: &EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1505 match var_id {
1506 ENV_VARIABLE_ID => {
1508 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1509 let env_columns = env_vars.keys();
1510 let env_values = env_vars.values();
1511
1512 let mut pairs = env_columns
1513 .map(|x| x.to_string())
1514 .zip(env_values.cloned())
1515 .collect::<Vec<(String, Value)>>();
1516
1517 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1518
1519 Ok(Value::record(pairs.into_iter().collect(), span))
1520 }
1521 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1522 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1524 Ok(const_val.with_span(span))
1525 } else {
1526 Err(err)
1527 }
1528 }),
1529 }
1530}
1531
1532fn get_env_var<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1534 for overlays in ctx
1536 .stack
1537 .env_vars
1538 .iter()
1539 .rev()
1540 .chain(std::iter::once(&ctx.engine_state.env_vars))
1541 {
1542 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1544 let Some(map) = overlays.get(overlay_name) else {
1545 continue;
1547 };
1548 let hidden = ctx.stack.env_hidden.get(overlay_name);
1549 let is_hidden = |key: &EnvName| hidden.is_some_and(|hidden| hidden.contains(key));
1550
1551 if let Some(val) = map
1552 .get(&EnvName::from(key))
1554 .filter(|_| !is_hidden(&EnvName::from(key)))
1556 {
1557 return Some(val);
1558 }
1559 }
1560 }
1561 None
1563}
1564
1565fn get_env_var_name<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1569 ctx.stack
1571 .env_vars
1572 .iter()
1573 .rev()
1574 .chain(std::iter::once(&ctx.engine_state.env_vars))
1575 .flat_map(|overlays| {
1576 ctx.stack
1578 .active_overlays
1579 .iter()
1580 .rev()
1581 .filter_map(|name| overlays.get(name))
1582 })
1583 .find_map(|map| {
1584 if map.contains_key(&EnvName::from(key)) {
1586 map.keys()
1588 .find(|k| k.as_str().eq_ignore_case(key))
1589 .map(|k| Cow::Owned(k.as_str().to_owned()))
1590 } else {
1591 None
1592 }
1593 })
1594 .unwrap_or(Cow::Borrowed(key))
1596}
1597
1598fn collect(pipe: PipelineExecutionData, fallback_span: Span) -> Result<PipelineData, ShellError> {
1602 let data = pipe.body;
1603 let span = data.span().unwrap_or(fallback_span);
1604 let metadata = data.metadata().and_then(|m| m.for_collect());
1605 let value = data.into_value(span)?;
1606 #[cfg(feature = "os")]
1607 if nu_experimental::PIPE_FAIL.get() {
1608 check_exit_status_future(pipe.exit)?
1609 }
1610 Ok(PipelineData::value(value, metadata))
1611}
1612
1613fn drain(
1615 ctx: &mut EvalContext<'_>,
1616 data: PipelineExecutionData,
1617) -> Result<InstructionResult, ShellError> {
1618 use self::InstructionResult::*;
1619
1620 match data.body {
1621 PipelineData::ByteStream(stream, ..) => {
1622 let span = stream.span();
1623 let callback_spans = stream.get_caller_spans().clone();
1624 if let Err(mut err) = stream.drain() {
1625 ctx.stack.set_last_error(&err);
1626 if callback_spans.is_empty() {
1627 return Err(err);
1628 } else {
1629 for s in callback_spans {
1630 err = ShellError::EvalBlockWithInput {
1631 span: s,
1632 sources: vec![err],
1633 }
1634 }
1635 return Err(err);
1636 }
1637 } else {
1638 ctx.stack.set_last_exit_code(0, span);
1639 }
1640 }
1641 PipelineData::ListStream(stream, ..) => {
1642 let callback_spans = stream.get_caller_spans().clone();
1643 if let Err(mut err) = stream.drain() {
1644 if callback_spans.is_empty() {
1645 return Err(err);
1646 } else {
1647 for s in callback_spans {
1648 err = ShellError::EvalBlockWithInput {
1649 span: s,
1650 sources: vec![err],
1651 }
1652 }
1653 return Err(err);
1654 }
1655 }
1656 }
1657 PipelineData::Value(..) | PipelineData::Empty => {}
1658 }
1659
1660 let pipefail = nu_experimental::PIPE_FAIL.get();
1661 if !pipefail {
1662 return Ok(Continue);
1663 }
1664 #[cfg(feature = "os")]
1665 {
1666 check_exit_status_future(data.exit).map(|_| Continue)
1667 }
1668 #[cfg(not(feature = "os"))]
1669 Ok(Continue)
1670}
1671
1672fn drain_if_end(
1674 ctx: &mut EvalContext<'_>,
1675 data: PipelineExecutionData,
1676) -> Result<PipelineData, ShellError> {
1677 let stack = &mut ctx
1678 .stack
1679 .push_redirection(ctx.redirect_out.clone(), ctx.redirect_err.clone());
1680 let result = data.body.drain_to_out_dests(ctx.engine_state, stack)?;
1681
1682 let pipefail = nu_experimental::PIPE_FAIL.get();
1683 if !pipefail {
1684 return Ok(result);
1685 }
1686 #[cfg(feature = "os")]
1687 {
1688 check_exit_status_future(data.exit).map(|_| result)
1689 }
1690 #[cfg(not(feature = "os"))]
1691 Ok(result)
1692}
1693
1694enum RedirectionStream {
1695 Out,
1696 Err,
1697}
1698
1699fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1701 let path_expanded =
1702 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1703 let mut options = File::options();
1704 if append {
1705 options.append(true);
1706 } else {
1707 options.write(true).truncate(true);
1708 }
1709 let file = options
1710 .create(true)
1711 .open(&path_expanded)
1712 .map_err(|err| IoError::new(err, path.span(), path_expanded))?;
1713 Ok(Arc::new(file))
1714}
1715
1716fn eval_redirection(
1718 ctx: &mut EvalContext<'_>,
1719 mode: &RedirectMode,
1720 span: Span,
1721 which: RedirectionStream,
1722) -> Result<Option<Redirection>, ShellError> {
1723 match mode {
1724 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1725 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1726 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1727 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1728 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1729 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1730 RedirectMode::File { file_num } => {
1731 let file = ctx
1732 .files
1733 .get(*file_num as usize)
1734 .cloned()
1735 .flatten()
1736 .ok_or_else(|| ShellError::IrEvalError {
1737 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
1738 span: Some(span),
1739 })?;
1740 Ok(Some(Redirection::File(file)))
1741 }
1742 RedirectMode::Caller => Ok(match which {
1743 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
1744 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
1745 }),
1746 }
1747}
1748
1749fn eval_iterate(
1751 ctx: &mut EvalContext<'_>,
1752 dst: RegId,
1753 stream: RegId,
1754 end_index: usize,
1755) -> Result<InstructionResult, ShellError> {
1756 let mut data = ctx.take_reg(stream);
1757 if let PipelineData::ListStream(list_stream, _) = &mut data.body {
1758 if let Some(val) = list_stream.next_value() {
1760 ctx.put_reg(dst, PipelineExecutionData::from(val.into_pipeline_data()));
1761 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
1763 } else {
1764 ctx.put_reg(dst, PipelineExecutionData::empty());
1765 Ok(InstructionResult::Branch(end_index))
1766 }
1767 } else {
1768 let metadata = data.metadata();
1771 let span = data.span().unwrap_or(Span::unknown());
1772 ctx.put_reg(
1773 stream,
1774 PipelineExecutionData::from(PipelineData::list_stream(
1775 ListStream::new(data.body.into_iter(), span, Signals::EMPTY),
1776 metadata,
1777 )),
1778 );
1779 eval_iterate(ctx, dst, stream, end_index)
1780 }
1781}
1782
1783fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
1785 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
1788
1789 for var in caller_env_vars.iter() {
1792 if !callee_stack.has_env_var(engine_state, var) {
1793 caller_stack.remove_env_var(engine_state, var);
1794 }
1795 }
1796
1797 for (var, value) in callee_stack.get_stack_env_vars() {
1799 caller_stack.add_env_var(var, value);
1800 }
1801
1802 caller_stack.config.clone_from(&callee_stack.config);
1804}