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