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