1#[allow(deprecated)]
2use crate::get_full_help;
3use crate::{EvalBlockWithEarlyReturnFn, eval_ir::eval_ir_block};
4use nu_protocol::{
5 BlockId, Config, ENV_VARIABLE_ID, IntoPipelineData, PipelineData, PipelineExecutionData,
6 ShellError, Signature, Span, Value, VarId,
7 ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember},
8 debugger::{DebugContext, WithDebug, WithoutDebug},
9 engine::{Closure, EngineState, EnvName, EnvVars, Stack},
10 eval_base::Eval,
11};
12use nu_utils::IgnoreCaseExt;
13use std::borrow::Cow;
14use std::collections::{HashMap, HashSet};
15use std::sync::Arc;
16
17#[derive(Clone)]
29pub struct CallEval {
30 callee_stack: Stack,
31 head_span: Span,
32 callee_span: Span,
33 arg_index: usize,
34 named_args: Vec<String>,
35 rest_args: Vec<Value>,
36 eval: EvalBlockWithEarlyReturnFn,
37}
38
39impl CallEval {
40 pub fn new(
42 callee_stack: Stack,
43 call_head: Span,
44 callee_span: Span,
45 eval: EvalBlockWithEarlyReturnFn,
46 ) -> Self {
47 Self {
48 callee_stack,
49 head_span: call_head,
50 callee_span,
51 arg_index: 0,
52 named_args: Vec::new(),
53 rest_args: Vec::new(),
54 eval,
55 }
56 }
57
58 pub fn add_positional(
63 &mut self,
64 signature: &Signature,
65 value: Cow<Value>,
66 ) -> Result<&mut Self, ShellError> {
67 let maybe_param = match self
68 .arg_index
69 .checked_sub(signature.required_positional.len())
70 {
71 None => signature.required_positional.get(self.arg_index),
73 Some(opt_idx) if opt_idx < signature.optional_positional.len() => {
75 signature.optional_positional.get(opt_idx)
76 }
77 _ => None,
79 };
80
81 if let Some(param) = maybe_param {
82 let param_type = param.shape.to_type();
83 if !value.is_subtype_of(¶m_type) {
84 return Err(ShellError::CantConvert {
85 to_type: param_type.to_string(),
86 from_type: value.get_type().to_string(),
87 span: value.span(),
88 help: None,
89 });
90 }
91
92 let var_id = param
93 .var_id
94 .expect("internal error: all custom parameters must have var_ids");
95 self.callee_stack.add_var(var_id, value.into_owned());
96 self.arg_index += 1;
97 Ok(self)
98 } else {
99 let Some(rest_positional) = &signature.rest_positional else {
101 return Ok(self);
105 };
106
107 let param_type = rest_positional.shape.to_type();
108 if !value.is_subtype_of(¶m_type) {
109 return Err(ShellError::CantConvert {
110 to_type: param_type.to_string(),
111 from_type: value.get_type().to_string(),
112 span: value.span(),
113 help: None,
114 });
115 }
116
117 self.rest_args.push(value.into_owned());
118 Ok(self)
119 }
120 }
121
122 pub fn add_named(
124 &mut self,
125 signature: &Signature,
126 long: &str,
127 short: Option<String>,
128 value: Option<Cow<Value>>,
129 ) -> Result<&mut Self, ShellError> {
130 let named = signature.named.iter().find(|named| {
131 long == named.long
132 || short
133 .as_deref()
134 .zip(named.short)
135 .is_some_and(|(arg, param)| {
136 let mut buf = [0; 4];
137 param.encode_utf8(&mut buf) == arg
138 })
139 });
140
141 if let Some(named) = named {
142 let var_id = named
143 .var_id
144 .expect("internal error: all custom parameters must have var_ids");
145
146 let value = value
147 .or_else(|| named.default_value.as_ref().map(Cow::Borrowed))
148 .unwrap_or_else(|| Cow::Owned(Value::bool(true, self.head_span)));
149
150 self.callee_stack.add_var(var_id, value.into_owned());
151 self.named_args.push(long.to_string());
152 }
153
154 Ok(self)
155 }
156
157 pub fn with_env(
159 &mut self,
160 env_vars: &[Arc<EnvVars>],
161 env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
162 ) -> &mut Self {
163 self.callee_stack.with_env(env_vars, env_hidden);
164 self
165 }
166
167 pub fn debug(&mut self, debug: bool) -> &mut Self {
169 if debug {
170 self.eval = eval_block_with_early_return::<WithDebug>
171 } else {
172 self.eval = eval_block_with_early_return::<WithoutDebug>
173 };
174 self
175 }
176
177 pub fn run(
179 &mut self,
180 engine_state: &EngineState,
181 block: &Block,
182 input: PipelineData,
183 ) -> Result<PipelineData, ShellError> {
184 self.finalize_arguments(&block.signature)?;
185 self.arg_index = 0;
186 self.rest_args.clear();
187 (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
188 }
189
190 pub fn redirect_env(&self, engine_state: &EngineState, stack: &mut Stack) {
192 redirect_env(engine_state, stack, &self.callee_stack);
193 }
194
195 fn finalize_arguments(&mut self, signature: &Signature) -> Result<(), ShellError> {
198 let remaining_positionals = signature
199 .required_positional
200 .iter()
201 .map(|p| (p, true))
202 .chain(signature.optional_positional.iter().map(|p| (p, false)))
203 .skip(self.arg_index);
205
206 for (param, required) in remaining_positionals {
207 let var_id = param
208 .var_id
209 .expect("internal error: all custom parameters must have var_ids");
210
211 let maybe_value = param
212 .default_value
213 .clone()
214 .or((!required).then_some(Value::nothing(self.callee_span)));
215
216 if let Some(value) = maybe_value {
217 self.callee_stack.add_var(var_id, value);
218 } else {
219 return Err(ShellError::MissingParameter {
220 param_name: param.name.to_string(),
221 span: self.callee_span,
222 });
223 }
224 }
225
226 if let Some(rest_positional) = &signature.rest_positional {
227 let span = self
228 .rest_args
229 .first()
230 .map(|x| x.span())
231 .unwrap_or(self.callee_span);
232
233 self.callee_stack.add_var(
234 rest_positional
235 .var_id
236 .expect("Internal error: rest positional parameter lackes var_id"),
237 Value::list(self.rest_args.to_owned(), span),
238 );
239 }
240
241 let remaining_flags = signature
242 .named
243 .iter()
244 .filter(|flag| !self.named_args.contains(&flag.long))
246 .filter_map(|flag| Some((flag.var_id?, flag)));
249
250 for (var_id, flag) in remaining_flags {
251 if flag.arg.is_none() {
252 self.callee_stack
253 .add_var(var_id, Value::bool(false, self.head_span));
254 } else {
255 let value = flag
256 .default_value
257 .clone()
258 .unwrap_or(Value::nothing(self.head_span));
259 self.callee_stack.add_var(var_id, value);
260 }
261 }
262
263 Ok(())
264 }
265}
266
267pub fn eval_call<D: DebugContext>(
269 engine_state: &EngineState,
270 caller_stack: &mut Stack,
271 call: &Call,
272 input: PipelineData,
273) -> Result<PipelineData, ShellError> {
274 engine_state.signals().check(&call.head)?;
275 let decl = engine_state.get_decl(call.decl_id);
276
277 if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
278 let help = get_full_help(decl, engine_state, caller_stack, call.head);
279 Ok(Value::string(help, call.head).into_pipeline_data())
280 } else if let Some(block_id) = decl.block_id() {
281 let block = engine_state.get_block(block_id);
283 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
284
285 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
291 callee_stack.recursion_count += 1;
292 if callee_stack.recursion_count > maximum_call_stack_depth {
293 callee_stack.recursion_count = 0;
294 return Err(ShellError::RecursionLimitReached {
295 recursion_limit: maximum_call_stack_depth,
296 span: block.span,
297 });
298 }
299
300 let mut call_eval = CallEval::new(
301 callee_stack,
302 call.head,
303 block.span.unwrap_or(Span::unknown()),
304 eval_block_with_early_return::<D>,
305 );
306 for arg in call.positional_iter() {
307 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
308 call_eval.add_positional(&decl.signature(), Cow::Owned(result))?;
309 }
310 for call_named in call.named_iter() {
311 let result: Option<Cow<Value>> = if let Some(arg) = &call_named.2 {
312 Some(Cow::Owned(eval_expression::<D>(
313 engine_state,
314 caller_stack,
315 arg,
316 )?))
317 } else {
318 None
319 };
320 call_eval.add_named(
321 &decl.signature(),
322 &call_named.0.item,
323 call_named.1.clone().map(|x| x.item),
324 result,
325 )?;
326 }
327
328 let result = call_eval.run(engine_state, block, input);
329
330 if block.redirect_env {
331 call_eval.redirect_env(engine_state, caller_stack);
332 }
333
334 result
335 } else {
336 decl.run(engine_state, caller_stack, &call.into(), input)
341 }
342}
343
344pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
346 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
348
349 for var in caller_env_vars.iter() {
352 if !callee_stack.has_env_var(engine_state, var) {
353 caller_stack.hide_env_var(engine_state, var);
354 }
355 }
356
357 for (var, value) in callee_stack.get_stack_env_vars() {
359 caller_stack.add_env_var(var, value);
360 }
361
362 caller_stack.config.clone_from(&callee_stack.config);
364}
365
366fn eval_external(
367 engine_state: &EngineState,
368 stack: &mut Stack,
369 head: &Expression,
370 args: &[ExternalArgument],
371 input: PipelineData,
372) -> Result<PipelineData, ShellError> {
373 let decl_id = engine_state
374 .find_decl("run-external".as_bytes(), &[])
375 .ok_or(ShellError::ExternalNotSupported {
376 span: head.span(&engine_state),
377 })?;
378
379 let command = engine_state.get_decl(decl_id);
380
381 let mut call = Call::new(head.span(&engine_state));
382
383 call.add_positional(head.clone());
384
385 for arg in args {
386 match arg {
387 ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
388 ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
389 }
390 }
391
392 command.run(engine_state, stack, &(&call).into(), input)
393}
394
395pub fn eval_expression<D: DebugContext>(
396 engine_state: &EngineState,
397 stack: &mut Stack,
398 expr: &Expression,
399) -> Result<Value, ShellError> {
400 let stack = &mut stack.start_collect_value();
401 <EvalRuntime as Eval>::eval::<D>(engine_state, stack, expr)
402}
403
404pub fn eval_expression_with_input<D: DebugContext>(
411 engine_state: &EngineState,
412 stack: &mut Stack,
413 expr: &Expression,
414 mut input: PipelineData,
415) -> Result<PipelineData, ShellError> {
416 match &expr.expr {
417 Expr::Call(call) => {
418 input = eval_call::<D>(engine_state, stack, call, input)?;
419 }
420 Expr::ExternalCall(head, args) => {
421 input = eval_external(engine_state, stack, head, args, input)?;
422 }
423
424 Expr::Collect(var_id, expr) => {
425 input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
426 }
427
428 Expr::Subexpression(block_id) => {
429 let block = engine_state.get_block(*block_id);
430 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
432 }
433
434 Expr::FullCellPath(full_cell_path) => match &full_cell_path.head {
435 Expression {
436 expr: Expr::Subexpression(block_id),
437 span,
438 ..
439 } => {
440 let block = engine_state.get_block(*block_id);
441
442 if !full_cell_path.tail.is_empty() {
443 let stack = &mut stack.start_collect_value();
444 input = eval_subexpression::<D>(engine_state, stack, block, input)?
446 .into_value(*span)?
447 .follow_cell_path(&full_cell_path.tail)?
448 .into_owned()
449 .into_pipeline_data()
450 } else {
451 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
452 }
453 }
454 _ => {
455 let input_value = input.into_value(expr.span)?;
456 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
457 input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
458 }
459 },
460
461 Expr::StringInterpolation(_) | Expr::GlobInterpolation(_, _) => {
462 let input_value = input.into_value(expr.span)?;
463 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
464 let value = eval_expression::<D>(engine_state, stack, expr)?;
465 input = PipelineData::Value(value, None);
466 }
467
468 _ => {
469 let input_value = input.into_value(expr.span)?;
470 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
471 let value = eval_expression::<D>(engine_state, stack, expr)?;
472 input = PipelineData::Value(value, None);
473 }
474 };
475
476 Ok(input)
477}
478
479pub fn eval_block<D: DebugContext>(
480 engine_state: &EngineState,
481 stack: &mut Stack,
482 block: &Block,
483 input: PipelineData,
484) -> Result<PipelineExecutionData, ShellError> {
485 let result = eval_ir_block::<D>(engine_state, stack, block, input);
486 if let Err(ShellError::Exit { code }) = &result {
487 std::process::exit(*code)
488 }
489 if let Err(err) = &result {
490 stack.set_last_error(err);
491 }
492 result
493}
494
495pub fn eval_block_with_early_return<D: DebugContext>(
496 engine_state: &EngineState,
497 stack: &mut Stack,
498 block: &Block,
499 input: PipelineData,
500) -> Result<PipelineExecutionData, ShellError> {
501 match eval_block::<D>(engine_state, stack, block, input) {
502 Err(ShellError::Return { span: _, value }) => Ok(PipelineExecutionData::from(
503 PipelineData::value(*value, None),
504 )),
505 Err(ShellError::Exit { code }) => std::process::exit(code),
506 x => x,
507 }
508}
509
510pub fn eval_collect<D: DebugContext>(
511 engine_state: &EngineState,
512 stack: &mut Stack,
513 var_id: VarId,
514 expr: &Expression,
515 mut input: PipelineData,
516) -> Result<PipelineData, ShellError> {
517 let span = input.span().unwrap_or(expr.span);
519
520 let metadata = input.take_metadata().and_then(|m| m.for_collect());
521
522 let input = input.into_value(span)?;
523
524 stack.add_var(var_id, input.clone());
525
526 let result = eval_expression_with_input::<D>(
527 engine_state,
528 stack,
529 expr,
530 input.into_pipeline_data_with_metadata(metadata),
532 );
533
534 stack.remove_var(var_id);
535
536 result
537}
538
539pub fn eval_subexpression<D: DebugContext>(
540 engine_state: &EngineState,
541 stack: &mut Stack,
542 block: &Block,
543 input: PipelineData,
544) -> Result<PipelineData, ShellError> {
545 eval_block::<D>(engine_state, stack, block, input).map(|p| p.body)
546}
547
548pub fn eval_variable(
549 engine_state: &EngineState,
550 stack: &Stack,
551 var_id: VarId,
552 span: Span,
553) -> Result<Value, ShellError> {
554 match var_id {
555 nu_protocol::NU_VARIABLE_ID => {
557 if let Some(val) = engine_state.get_constant(var_id) {
558 Ok(val.clone())
559 } else {
560 Err(ShellError::VariableNotFoundAtRuntime { span })
561 }
562 }
563 ENV_VARIABLE_ID => {
565 let env_vars = stack.get_env_vars(engine_state);
566 let env_columns = env_vars.keys();
567 let env_values = env_vars.values();
568
569 let mut pairs = env_columns
570 .map(|x| x.to_string())
571 .zip(env_values.cloned())
572 .collect::<Vec<(String, Value)>>();
573
574 pairs.sort_by(|a, b| a.0.cmp(&b.0));
575
576 Ok(Value::record(pairs.into_iter().collect(), span))
577 }
578 var_id => stack.get_var(var_id, span),
579 }
580}
581
582struct EvalRuntime;
583
584impl Eval for EvalRuntime {
585 type State<'a> = &'a EngineState;
586
587 type MutState = Stack;
588
589 fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
590 stack.get_config(engine_state)
591 }
592
593 fn eval_var(
594 engine_state: &EngineState,
595 stack: &mut Stack,
596 var_id: VarId,
597 span: Span,
598 ) -> Result<Value, ShellError> {
599 eval_variable(engine_state, stack, var_id, span)
600 }
601
602 fn eval_call<D: DebugContext>(
603 engine_state: &EngineState,
604 stack: &mut Stack,
605 call: &Call,
606 _: Span,
607 ) -> Result<Value, ShellError> {
608 eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
610 }
611
612 fn eval_external_call(
613 engine_state: &EngineState,
614 stack: &mut Stack,
615 head: &Expression,
616 args: &[ExternalArgument],
617 _: Span,
618 ) -> Result<Value, ShellError> {
619 let span = head.span(&engine_state);
620 eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
622 }
623
624 fn eval_collect<D: DebugContext>(
625 engine_state: &EngineState,
626 stack: &mut Stack,
627 var_id: VarId,
628 expr: &Expression,
629 ) -> Result<Value, ShellError> {
630 eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
633 .into_value(expr.span)
634 }
635
636 fn eval_subexpression<D: DebugContext>(
637 engine_state: &EngineState,
638 stack: &mut Stack,
639 block_id: BlockId,
640 span: Span,
641 ) -> Result<Value, ShellError> {
642 let block = engine_state.get_block(block_id);
643 eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
645 }
646
647 fn regex_match(
648 engine_state: &EngineState,
649 op_span: Span,
650 lhs: &Value,
651 rhs: &Value,
652 invert: bool,
653 expr_span: Span,
654 ) -> Result<Value, ShellError> {
655 lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
656 }
657
658 fn eval_assignment<D: DebugContext>(
659 engine_state: &EngineState,
660 stack: &mut Stack,
661 lhs: &Expression,
662 rhs: &Expression,
663 assignment: Assignment,
664 op_span: Span,
665 _expr_span: Span,
666 ) -> Result<Value, ShellError> {
667 let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
668
669 let rhs = match assignment {
670 Assignment::Assign => rhs,
671 Assignment::AddAssign => {
672 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
673 lhs.add(op_span, &rhs, op_span)?
674 }
675 Assignment::SubtractAssign => {
676 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
677 lhs.sub(op_span, &rhs, op_span)?
678 }
679 Assignment::MultiplyAssign => {
680 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
681 lhs.mul(op_span, &rhs, op_span)?
682 }
683 Assignment::DivideAssign => {
684 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
685 lhs.div(op_span, &rhs, op_span)?
686 }
687 Assignment::ConcatenateAssign => {
688 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
689 lhs.concat(op_span, &rhs, op_span)?
690 }
691 };
692
693 match &lhs.expr {
694 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
695 let var_info = engine_state.get_var(*var_id);
696 if var_info.mutable {
697 stack.add_var(*var_id, rhs);
698 Ok(Value::nothing(lhs.span(&engine_state)))
699 } else {
700 Err(ShellError::AssignmentRequiresMutableVar {
701 lhs_span: lhs.span(&engine_state),
702 })
703 }
704 }
705 Expr::FullCellPath(cell_path) => {
706 match &cell_path.head.expr {
707 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
708 let is_env = var_id == &ENV_VARIABLE_ID;
711 if is_env || engine_state.get_var(*var_id).mutable {
712 let mut lhs =
713 eval_expression::<D>(engine_state, stack, &cell_path.head)?;
714 if is_env {
715 if cell_path.tail.is_empty() {
717 return Err(ShellError::CannotReplaceEnv {
718 span: cell_path.head.span(&engine_state),
719 });
720 }
721
722 let (key, span) = match &cell_path.tail[0] {
725 PathMember::String { val, span, .. } => (val.to_string(), span),
726 PathMember::Int { val, span, .. } => (val.to_string(), span),
727 };
728 let original_key = if let Value::Record { val: record, .. } = &lhs {
729 record
730 .iter()
731 .rev()
732 .map(|(k, _)| k)
733 .find(|x| x.eq_ignore_case(&key))
734 .cloned()
735 .unwrap_or(key)
736 } else {
737 key
738 };
739
740 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
742 let value = lhs.follow_cell_path(&[{
743 let mut pm = cell_path.tail[0].clone();
744 pm.make_insensitive();
745 pm
746 }])?;
747
748 if is_automatic_env_var(&original_key) {
750 return Err(ShellError::AutomaticEnvVarSetManually {
751 envvar_name: original_key,
752 span: *span,
753 });
754 }
755
756 let is_config = original_key == "config";
757
758 stack.add_env_var(original_key, value.into_owned());
759
760 if is_config {
762 stack.update_config(engine_state)?;
763 }
764 } else {
765 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
766 stack.add_var(*var_id, lhs);
767 }
768 Ok(Value::nothing(cell_path.head.span(&engine_state)))
769 } else {
770 Err(ShellError::AssignmentRequiresMutableVar {
771 lhs_span: lhs.span(&engine_state),
772 })
773 }
774 }
775 _ => Err(ShellError::AssignmentRequiresVar {
776 lhs_span: lhs.span(&engine_state),
777 }),
778 }
779 }
780 _ => Err(ShellError::AssignmentRequiresVar {
781 lhs_span: lhs.span(&engine_state),
782 }),
783 }
784 }
785
786 fn eval_row_condition_or_closure(
787 engine_state: &EngineState,
788 stack: &mut Stack,
789 block_id: BlockId,
790 span: Span,
791 ) -> Result<Value, ShellError> {
792 let captures = engine_state
793 .get_block(block_id)
794 .captures
795 .iter()
796 .map(|(id, span)| {
797 stack
798 .get_var(*id, *span)
799 .or_else(|_| {
800 engine_state
801 .get_var(*id)
802 .const_val
803 .clone()
804 .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
805 })
806 .map(|var| (*id, var))
807 })
808 .collect::<Result<_, _>>()?;
809
810 Ok(Value::closure(Closure { block_id, captures }, span))
811 }
812
813 fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
814 let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
815
816 Ok(Value::string(name, span))
817 }
818
819 fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
820 Ok(Value::nothing(expr.span(&engine_state)))
821 }
822}
823
824pub(crate) fn is_automatic_env_var(var: &str) -> bool {
830 let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
831 names.iter().any(|&name| {
832 if cfg!(windows) {
833 name.eq_ignore_case(var)
834 } else {
835 name.eq(var)
836 }
837 })
838}