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