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