1use crate::eval_ir_block;
2#[allow(deprecated)]
3use crate::get_full_help;
4use nu_path::{AbsolutePathBuf, expand_path_with};
5use nu_protocol::{
6 BlockId, Config, DataSource, ENV_VARIABLE_ID, IntoPipelineData, PipelineData, PipelineMetadata,
7 ShellError, Span, Value, VarId,
8 ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember},
9 debugger::DebugContext,
10 engine::{Closure, EngineState, Stack},
11 eval_base::Eval,
12};
13use nu_utils::IgnoreCaseExt;
14use std::sync::Arc;
15
16pub fn eval_call<D: DebugContext>(
17 engine_state: &EngineState,
18 caller_stack: &mut Stack,
19 call: &Call,
20 input: PipelineData,
21) -> Result<PipelineData, ShellError> {
22 engine_state.signals().check(call.head)?;
23 let decl = engine_state.get_decl(call.decl_id);
24
25 if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
26 let help = get_full_help(decl, engine_state, caller_stack);
27 Ok(Value::string(help, call.head).into_pipeline_data())
28 } else if let Some(block_id) = decl.block_id() {
29 let block = engine_state.get_block(block_id);
30
31 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
32
33 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
39 callee_stack.recursion_count += 1;
40 if callee_stack.recursion_count > maximum_call_stack_depth {
41 callee_stack.recursion_count = 0;
42 return Err(ShellError::RecursionLimitReached {
43 recursion_limit: maximum_call_stack_depth,
44 span: block.span,
45 });
46 }
47
48 for (param_idx, (param, required)) in decl
49 .signature()
50 .required_positional
51 .iter()
52 .map(|p| (p, true))
53 .chain(
54 decl.signature()
55 .optional_positional
56 .iter()
57 .map(|p| (p, false)),
58 )
59 .enumerate()
60 {
61 let var_id = param
62 .var_id
63 .expect("internal error: all custom parameters must have var_ids");
64
65 if let Some(arg) = call.positional_nth(param_idx) {
66 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
67 let param_type = param.shape.to_type();
68 if required && !result.is_subtype_of(¶m_type) {
69 return Err(ShellError::CantConvert {
70 to_type: param.shape.to_type().to_string(),
71 from_type: result.get_type().to_string(),
72 span: result.span(),
73 help: None,
74 });
75 }
76 callee_stack.add_var(var_id, result);
77 } else if let Some(value) = ¶m.default_value {
78 callee_stack.add_var(var_id, value.to_owned());
79 } else {
80 callee_stack.add_var(var_id, Value::nothing(call.head));
81 }
82 }
83
84 if let Some(rest_positional) = decl.signature().rest_positional {
85 let mut rest_items = vec![];
86
87 for result in call.rest_iter_flattened(
88 decl.signature().required_positional.len()
89 + decl.signature().optional_positional.len(),
90 |expr| eval_expression::<D>(engine_state, caller_stack, expr),
91 )? {
92 rest_items.push(result);
93 }
94
95 let span = if let Some(rest_item) = rest_items.first() {
96 rest_item.span()
97 } else {
98 call.head
99 };
100
101 callee_stack.add_var(
102 rest_positional
103 .var_id
104 .expect("Internal error: rest positional parameter lacks var_id"),
105 Value::list(rest_items, span),
106 )
107 }
108
109 for named in decl.signature().named {
110 if let Some(var_id) = named.var_id {
111 let mut found = false;
112 for call_named in call.named_iter() {
113 if let (Some(spanned), Some(short)) = (&call_named.1, named.short) {
114 if spanned.item == short.to_string() {
115 if let Some(arg) = &call_named.2 {
116 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
117
118 callee_stack.add_var(var_id, result);
119 } else if let Some(value) = &named.default_value {
120 callee_stack.add_var(var_id, value.to_owned());
121 } else {
122 callee_stack.add_var(var_id, Value::bool(true, call.head))
123 }
124 found = true;
125 }
126 } else if call_named.0.item == named.long {
127 if let Some(arg) = &call_named.2 {
128 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
129
130 callee_stack.add_var(var_id, result);
131 } else if let Some(value) = &named.default_value {
132 callee_stack.add_var(var_id, value.to_owned());
133 } else {
134 callee_stack.add_var(var_id, Value::bool(true, call.head))
135 }
136 found = true;
137 }
138 }
139
140 if !found {
141 if named.arg.is_none() {
142 callee_stack.add_var(var_id, Value::bool(false, call.head))
143 } else if let Some(value) = named.default_value {
144 callee_stack.add_var(var_id, value);
145 } else {
146 callee_stack.add_var(var_id, Value::nothing(call.head))
147 }
148 }
149 }
150 }
151
152 let result =
153 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input);
154
155 if block.redirect_env {
156 redirect_env(engine_state, caller_stack, &callee_stack);
157 }
158
159 result
160 } else {
161 decl.run(engine_state, caller_stack, &call.into(), input)
165 }
166}
167
168pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
170 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
172
173 for var in caller_env_vars.iter() {
176 if !callee_stack.has_env_var(engine_state, var) {
177 caller_stack.remove_env_var(engine_state, var);
178 }
179 }
180
181 for (var, value) in callee_stack.get_stack_env_vars() {
183 caller_stack.add_env_var(var, value);
184 }
185
186 caller_stack.config.clone_from(&callee_stack.config);
188}
189
190fn eval_external(
191 engine_state: &EngineState,
192 stack: &mut Stack,
193 head: &Expression,
194 args: &[ExternalArgument],
195 input: PipelineData,
196) -> Result<PipelineData, ShellError> {
197 let decl_id = engine_state
198 .find_decl("run-external".as_bytes(), &[])
199 .ok_or(ShellError::ExternalNotSupported {
200 span: head.span(&engine_state),
201 })?;
202
203 let command = engine_state.get_decl(decl_id);
204
205 let mut call = Call::new(head.span(&engine_state));
206
207 call.add_positional(head.clone());
208
209 for arg in args {
210 match arg {
211 ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
212 ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
213 }
214 }
215
216 command.run(engine_state, stack, &(&call).into(), input)
217}
218
219pub fn eval_expression<D: DebugContext>(
220 engine_state: &EngineState,
221 stack: &mut Stack,
222 expr: &Expression,
223) -> Result<Value, ShellError> {
224 let stack = &mut stack.start_collect_value();
225 <EvalRuntime as Eval>::eval::<D>(engine_state, stack, expr)
226}
227
228pub fn eval_expression_with_input<D: DebugContext>(
235 engine_state: &EngineState,
236 stack: &mut Stack,
237 expr: &Expression,
238 mut input: PipelineData,
239) -> Result<PipelineData, ShellError> {
240 match &expr.expr {
241 Expr::Call(call) => {
242 input = eval_call::<D>(engine_state, stack, call, input)?;
243 }
244 Expr::ExternalCall(head, args) => {
245 input = eval_external(engine_state, stack, head, args, input)?;
246 }
247
248 Expr::Collect(var_id, expr) => {
249 input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
250 }
251
252 Expr::Subexpression(block_id) => {
253 let block = engine_state.get_block(*block_id);
254 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
256 }
257
258 Expr::FullCellPath(full_cell_path) => match &full_cell_path.head {
259 Expression {
260 expr: Expr::Subexpression(block_id),
261 span,
262 ..
263 } => {
264 let block = engine_state.get_block(*block_id);
265
266 if !full_cell_path.tail.is_empty() {
267 let stack = &mut stack.start_collect_value();
268 input = eval_subexpression::<D>(engine_state, stack, block, input)?
270 .into_value(*span)?
271 .follow_cell_path(&full_cell_path.tail)?
272 .into_owned()
273 .into_pipeline_data()
274 } else {
275 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
276 }
277 }
278 _ => {
279 input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
280 }
281 },
282
283 _ => {
284 input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
285 }
286 };
287
288 Ok(input)
289}
290
291pub fn eval_block_with_early_return<D: DebugContext>(
292 engine_state: &EngineState,
293 stack: &mut Stack,
294 block: &Block,
295 input: PipelineData,
296) -> Result<PipelineData, ShellError> {
297 match eval_block::<D>(engine_state, stack, block, input) {
298 Err(ShellError::Return { span: _, value }) => Ok(PipelineData::Value(*value, None)),
299 x => x,
300 }
301}
302
303pub fn eval_block<D: DebugContext>(
304 engine_state: &EngineState,
305 stack: &mut Stack,
306 block: &Block,
307 input: PipelineData,
308) -> Result<PipelineData, ShellError> {
309 let result = eval_ir_block::<D>(engine_state, stack, block, input);
310 if let Err(err) = &result {
311 stack.set_last_error(err);
312 }
313 result
314}
315
316pub fn eval_collect<D: DebugContext>(
317 engine_state: &EngineState,
318 stack: &mut Stack,
319 var_id: VarId,
320 expr: &Expression,
321 input: PipelineData,
322) -> Result<PipelineData, ShellError> {
323 let span = input.span().unwrap_or(Span::unknown());
325
326 let metadata = match input.metadata() {
327 Some(PipelineMetadata {
330 data_source: DataSource::FilePath(_),
331 content_type: None,
332 }) => None,
333 other => other,
334 };
335
336 let input = input.into_value(span)?;
337
338 stack.add_var(var_id, input.clone());
339
340 let result = eval_expression_with_input::<D>(
341 engine_state,
342 stack,
343 expr,
344 input.into_pipeline_data_with_metadata(metadata),
346 );
347
348 stack.remove_var(var_id);
349
350 result
351}
352
353pub fn eval_subexpression<D: DebugContext>(
354 engine_state: &EngineState,
355 stack: &mut Stack,
356 block: &Block,
357 input: PipelineData,
358) -> Result<PipelineData, ShellError> {
359 eval_block::<D>(engine_state, stack, block, input)
360}
361
362pub fn eval_variable(
363 engine_state: &EngineState,
364 stack: &Stack,
365 var_id: VarId,
366 span: Span,
367) -> Result<Value, ShellError> {
368 match var_id {
369 nu_protocol::NU_VARIABLE_ID => {
371 if let Some(val) = engine_state.get_constant(var_id) {
372 Ok(val.clone())
373 } else {
374 Err(ShellError::VariableNotFoundAtRuntime { span })
375 }
376 }
377 ENV_VARIABLE_ID => {
379 let env_vars = stack.get_env_vars(engine_state);
380 let env_columns = env_vars.keys();
381 let env_values = env_vars.values();
382
383 let mut pairs = env_columns
384 .map(|x| x.to_string())
385 .zip(env_values.cloned())
386 .collect::<Vec<(String, Value)>>();
387
388 pairs.sort_by(|a, b| a.0.cmp(&b.0));
389
390 Ok(Value::record(pairs.into_iter().collect(), span))
391 }
392 var_id => stack.get_var(var_id, span),
393 }
394}
395
396struct EvalRuntime;
397
398impl Eval for EvalRuntime {
399 type State<'a> = &'a EngineState;
400
401 type MutState = Stack;
402
403 fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
404 stack.get_config(engine_state)
405 }
406
407 fn eval_filepath(
408 engine_state: &EngineState,
409 stack: &mut Stack,
410 path: String,
411 quoted: bool,
412 span: Span,
413 ) -> Result<Value, ShellError> {
414 if quoted {
415 Ok(Value::string(path, span))
416 } else {
417 let cwd = engine_state.cwd(Some(stack))?;
418 let path = expand_path_with(path, cwd, true);
419
420 Ok(Value::string(path.to_string_lossy(), span))
421 }
422 }
423
424 fn eval_directory(
425 engine_state: Self::State<'_>,
426 stack: &mut Self::MutState,
427 path: String,
428 quoted: bool,
429 span: Span,
430 ) -> Result<Value, ShellError> {
431 if path == "-" {
432 Ok(Value::string("-", span))
433 } else if quoted {
434 Ok(Value::string(path, span))
435 } else {
436 let cwd = engine_state
437 .cwd(Some(stack))
438 .map(AbsolutePathBuf::into_std_path_buf)
439 .unwrap_or_default();
440 let path = expand_path_with(path, cwd, true);
441
442 Ok(Value::string(path.to_string_lossy(), span))
443 }
444 }
445
446 fn eval_var(
447 engine_state: &EngineState,
448 stack: &mut Stack,
449 var_id: VarId,
450 span: Span,
451 ) -> Result<Value, ShellError> {
452 eval_variable(engine_state, stack, var_id, span)
453 }
454
455 fn eval_call<D: DebugContext>(
456 engine_state: &EngineState,
457 stack: &mut Stack,
458 call: &Call,
459 _: Span,
460 ) -> Result<Value, ShellError> {
461 eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
463 }
464
465 fn eval_external_call(
466 engine_state: &EngineState,
467 stack: &mut Stack,
468 head: &Expression,
469 args: &[ExternalArgument],
470 _: Span,
471 ) -> Result<Value, ShellError> {
472 let span = head.span(&engine_state);
473 eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
475 }
476
477 fn eval_collect<D: DebugContext>(
478 engine_state: &EngineState,
479 stack: &mut Stack,
480 var_id: VarId,
481 expr: &Expression,
482 ) -> Result<Value, ShellError> {
483 eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
486 .into_value(expr.span)
487 }
488
489 fn eval_subexpression<D: DebugContext>(
490 engine_state: &EngineState,
491 stack: &mut Stack,
492 block_id: BlockId,
493 span: Span,
494 ) -> Result<Value, ShellError> {
495 let block = engine_state.get_block(block_id);
496 eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
498 }
499
500 fn regex_match(
501 engine_state: &EngineState,
502 op_span: Span,
503 lhs: &Value,
504 rhs: &Value,
505 invert: bool,
506 expr_span: Span,
507 ) -> Result<Value, ShellError> {
508 lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
509 }
510
511 fn eval_assignment<D: DebugContext>(
512 engine_state: &EngineState,
513 stack: &mut Stack,
514 lhs: &Expression,
515 rhs: &Expression,
516 assignment: Assignment,
517 op_span: Span,
518 _expr_span: Span,
519 ) -> Result<Value, ShellError> {
520 let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
521
522 let rhs = match assignment {
523 Assignment::Assign => rhs,
524 Assignment::AddAssign => {
525 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
526 lhs.add(op_span, &rhs, op_span)?
527 }
528 Assignment::SubtractAssign => {
529 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
530 lhs.sub(op_span, &rhs, op_span)?
531 }
532 Assignment::MultiplyAssign => {
533 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
534 lhs.mul(op_span, &rhs, op_span)?
535 }
536 Assignment::DivideAssign => {
537 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
538 lhs.div(op_span, &rhs, op_span)?
539 }
540 Assignment::ConcatenateAssign => {
541 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
542 lhs.concat(op_span, &rhs, op_span)?
543 }
544 };
545
546 match &lhs.expr {
547 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
548 let var_info = engine_state.get_var(*var_id);
549 if var_info.mutable {
550 stack.add_var(*var_id, rhs);
551 Ok(Value::nothing(lhs.span(&engine_state)))
552 } else {
553 Err(ShellError::AssignmentRequiresMutableVar {
554 lhs_span: lhs.span(&engine_state),
555 })
556 }
557 }
558 Expr::FullCellPath(cell_path) => {
559 match &cell_path.head.expr {
560 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
561 let is_env = var_id == &ENV_VARIABLE_ID;
564 if is_env || engine_state.get_var(*var_id).mutable {
565 let mut lhs =
566 eval_expression::<D>(engine_state, stack, &cell_path.head)?;
567 if is_env {
568 if cell_path.tail.is_empty() {
570 return Err(ShellError::CannotReplaceEnv {
571 span: cell_path.head.span(&engine_state),
572 });
573 }
574
575 let (key, span) = match &cell_path.tail[0] {
578 PathMember::String { val, span, .. } => (val.to_string(), span),
579 PathMember::Int { val, span, .. } => (val.to_string(), span),
580 };
581 let original_key = if let Value::Record { val: record, .. } = &lhs {
582 record
583 .iter()
584 .rev()
585 .map(|(k, _)| k)
586 .find(|x| x.eq_ignore_case(&key))
587 .cloned()
588 .unwrap_or(key)
589 } else {
590 key
591 };
592
593 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
595 let value = lhs.follow_cell_path(&[{
596 let mut pm = cell_path.tail[0].clone();
597 pm.make_insensitive();
598 pm
599 }])?;
600
601 if is_automatic_env_var(&original_key) {
603 return Err(ShellError::AutomaticEnvVarSetManually {
604 envvar_name: original_key,
605 span: *span,
606 });
607 }
608
609 let is_config = original_key == "config";
610
611 stack.add_env_var(original_key, value.into_owned());
612
613 if is_config {
615 stack.update_config(engine_state)?;
616 }
617 } else {
618 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
619 stack.add_var(*var_id, lhs);
620 }
621 Ok(Value::nothing(cell_path.head.span(&engine_state)))
622 } else {
623 Err(ShellError::AssignmentRequiresMutableVar {
624 lhs_span: lhs.span(&engine_state),
625 })
626 }
627 }
628 _ => Err(ShellError::AssignmentRequiresVar {
629 lhs_span: lhs.span(&engine_state),
630 }),
631 }
632 }
633 _ => Err(ShellError::AssignmentRequiresVar {
634 lhs_span: lhs.span(&engine_state),
635 }),
636 }
637 }
638
639 fn eval_row_condition_or_closure(
640 engine_state: &EngineState,
641 stack: &mut Stack,
642 block_id: BlockId,
643 span: Span,
644 ) -> Result<Value, ShellError> {
645 let captures = engine_state
646 .get_block(block_id)
647 .captures
648 .iter()
649 .map(|(id, span)| {
650 stack
651 .get_var(*id, *span)
652 .or_else(|_| {
653 engine_state
654 .get_var(*id)
655 .const_val
656 .clone()
657 .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
658 })
659 .map(|var| (*id, var))
660 })
661 .collect::<Result<_, _>>()?;
662
663 Ok(Value::closure(Closure { block_id, captures }, span))
664 }
665
666 fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
667 let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
668
669 Ok(Value::string(name, span))
670 }
671
672 fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
673 Ok(Value::nothing(expr.span(&engine_state)))
674 }
675}
676
677pub(crate) fn is_automatic_env_var(var: &str) -> bool {
683 let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
684 names.iter().any(|&name| {
685 if cfg!(windows) {
686 name.eq_ignore_case(var)
687 } else {
688 name.eq(var)
689 }
690 })
691}