1use crate::eval_ir::eval_ir_block;
2#[allow(deprecated)]
3use crate::get_full_help;
4use nu_protocol::{
5 BlockId, Config, ENV_VARIABLE_ID, IntoPipelineData, PipelineData, PipelineExecutionData,
6 ShellError, Span, Value, VarId,
7 ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember},
8 debugger::DebugContext,
9 engine::{Closure, EngineState, Stack},
10 eval_base::Eval,
11};
12use nu_utils::IgnoreCaseExt;
13use std::sync::Arc;
14
15pub fn eval_call<D: DebugContext>(
16 engine_state: &EngineState,
17 caller_stack: &mut Stack,
18 call: &Call,
19 input: PipelineData,
20) -> Result<PipelineData, ShellError> {
21 engine_state.signals().check(&call.head)?;
22 let decl = engine_state.get_decl(call.decl_id);
23
24 if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
25 let help = get_full_help(decl, engine_state, caller_stack);
26 Ok(Value::string(help, call.head).into_pipeline_data())
27 } else if let Some(block_id) = decl.block_id() {
28 let block = engine_state.get_block(block_id);
29
30 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
31
32 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
38 callee_stack.recursion_count += 1;
39 if callee_stack.recursion_count > maximum_call_stack_depth {
40 callee_stack.recursion_count = 0;
41 return Err(ShellError::RecursionLimitReached {
42 recursion_limit: maximum_call_stack_depth,
43 span: block.span,
44 });
45 }
46
47 for (param_idx, (param, required)) in decl
48 .signature()
49 .required_positional
50 .iter()
51 .map(|p| (p, true))
52 .chain(
53 decl.signature()
54 .optional_positional
55 .iter()
56 .map(|p| (p, false)),
57 )
58 .enumerate()
59 {
60 let var_id = param
61 .var_id
62 .expect("internal error: all custom parameters must have var_ids");
63
64 if let Some(arg) = call.positional_nth(param_idx) {
65 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
66 let param_type = param.shape.to_type();
67 if required && !result.is_subtype_of(¶m_type) {
68 return Err(ShellError::CantConvert {
69 to_type: param.shape.to_type().to_string(),
70 from_type: result.get_type().to_string(),
71 span: result.span(),
72 help: None,
73 });
74 }
75 callee_stack.add_var(var_id, result);
76 } else if let Some(value) = ¶m.default_value {
77 callee_stack.add_var(var_id, value.to_owned());
78 } else {
79 callee_stack.add_var(var_id, Value::nothing(call.head));
80 }
81 }
82
83 if let Some(rest_positional) = decl.signature().rest_positional {
84 let mut rest_items = vec![];
85
86 for result in call.rest_iter_flattened(
87 decl.signature().required_positional.len()
88 + decl.signature().optional_positional.len(),
89 |expr| eval_expression::<D>(engine_state, caller_stack, expr),
90 )? {
91 rest_items.push(result);
92 }
93
94 let span = if let Some(rest_item) = rest_items.first() {
95 rest_item.span()
96 } else {
97 call.head
98 };
99
100 callee_stack.add_var(
101 rest_positional
102 .var_id
103 .expect("Internal error: rest positional parameter lacks var_id"),
104 Value::list(rest_items, span),
105 )
106 }
107
108 for named in decl.signature().named {
109 if let Some(var_id) = named.var_id {
110 let mut found = false;
111 for call_named in call.named_iter() {
112 if let (Some(spanned), Some(short)) = (&call_named.1, named.short) {
113 if spanned.item == short.to_string() {
114 if let Some(arg) = &call_named.2 {
115 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
116
117 callee_stack.add_var(var_id, result);
118 } else if let Some(value) = &named.default_value {
119 callee_stack.add_var(var_id, value.to_owned());
120 } else {
121 callee_stack.add_var(var_id, Value::bool(true, call.head))
122 }
123 found = true;
124 }
125 } else if call_named.0.item == named.long {
126 if let Some(arg) = &call_named.2 {
127 let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
128
129 callee_stack.add_var(var_id, result);
130 } else if let Some(value) = &named.default_value {
131 callee_stack.add_var(var_id, value.to_owned());
132 } else {
133 callee_stack.add_var(var_id, Value::bool(true, call.head))
134 }
135 found = true;
136 }
137 }
138
139 if !found {
140 if named.arg.is_none() {
141 callee_stack.add_var(var_id, Value::bool(false, call.head))
142 } else if let Some(value) = named.default_value {
143 callee_stack.add_var(var_id, value);
144 } else {
145 callee_stack.add_var(var_id, Value::nothing(call.head))
146 }
147 }
148 }
149 }
150
151 let result =
152 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input)
153 .map(|p| p.body);
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 let input_value = input.into_value(expr.span)?;
280 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
281 input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
282 }
283 },
284
285 Expr::StringInterpolation(_) | Expr::GlobInterpolation(_, _) => {
286 let input_value = input.into_value(expr.span)?;
287 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
288 let value = eval_expression::<D>(engine_state, stack, expr)?;
289 input = PipelineData::Value(value, None);
290 }
291
292 _ => {
293 let input_value = input.into_value(expr.span)?;
294 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
295 let value = eval_expression::<D>(engine_state, stack, expr)?;
296 input = PipelineData::Value(value, None);
297 }
298 };
299
300 Ok(input)
301}
302
303pub fn eval_block<D: DebugContext>(
304 engine_state: &EngineState,
305 stack: &mut Stack,
306 block: &Block,
307 input: PipelineData,
308) -> Result<PipelineExecutionData, ShellError> {
309 let result = eval_ir_block::<D>(engine_state, stack, block, input);
310 if let Err(ShellError::Exit { code }) = &result {
311 std::process::exit(*code)
312 }
313 if let Err(err) = &result {
314 stack.set_last_error(err);
315 }
316 result
317}
318
319pub fn eval_block_with_early_return<D: DebugContext>(
320 engine_state: &EngineState,
321 stack: &mut Stack,
322 block: &Block,
323 input: PipelineData,
324) -> Result<PipelineExecutionData, ShellError> {
325 match eval_block::<D>(engine_state, stack, block, input) {
326 Err(ShellError::Return { span: _, value }) => Ok(PipelineExecutionData::from(
327 PipelineData::value(*value, None),
328 )),
329 Err(ShellError::Exit { code }) => std::process::exit(code),
330 x => x,
331 }
332}
333
334pub fn eval_collect<D: DebugContext>(
335 engine_state: &EngineState,
336 stack: &mut Stack,
337 var_id: VarId,
338 expr: &Expression,
339 input: PipelineData,
340) -> Result<PipelineData, ShellError> {
341 let span = input.span().unwrap_or(Span::unknown());
343
344 let metadata = input.metadata().and_then(|m| m.for_collect());
345
346 let input = input.into_value(span)?;
347
348 stack.add_var(var_id, input.clone());
349
350 let result = eval_expression_with_input::<D>(
351 engine_state,
352 stack,
353 expr,
354 input.into_pipeline_data_with_metadata(metadata),
356 );
357
358 stack.remove_var(var_id);
359
360 result
361}
362
363pub fn eval_subexpression<D: DebugContext>(
364 engine_state: &EngineState,
365 stack: &mut Stack,
366 block: &Block,
367 input: PipelineData,
368) -> Result<PipelineData, ShellError> {
369 eval_block::<D>(engine_state, stack, block, input).map(|p| p.body)
370}
371
372pub fn eval_variable(
373 engine_state: &EngineState,
374 stack: &Stack,
375 var_id: VarId,
376 span: Span,
377) -> Result<Value, ShellError> {
378 match var_id {
379 nu_protocol::NU_VARIABLE_ID => {
381 if let Some(val) = engine_state.get_constant(var_id) {
382 Ok(val.clone())
383 } else {
384 Err(ShellError::VariableNotFoundAtRuntime { span })
385 }
386 }
387 ENV_VARIABLE_ID => {
389 let env_vars = stack.get_env_vars(engine_state);
390 let env_columns = env_vars.keys();
391 let env_values = env_vars.values();
392
393 let mut pairs = env_columns
394 .map(|x| x.to_string())
395 .zip(env_values.cloned())
396 .collect::<Vec<(String, Value)>>();
397
398 pairs.sort_by(|a, b| a.0.cmp(&b.0));
399
400 Ok(Value::record(pairs.into_iter().collect(), span))
401 }
402 var_id => stack.get_var(var_id, span),
403 }
404}
405
406struct EvalRuntime;
407
408impl Eval for EvalRuntime {
409 type State<'a> = &'a EngineState;
410
411 type MutState = Stack;
412
413 fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
414 stack.get_config(engine_state)
415 }
416
417 fn eval_var(
418 engine_state: &EngineState,
419 stack: &mut Stack,
420 var_id: VarId,
421 span: Span,
422 ) -> Result<Value, ShellError> {
423 eval_variable(engine_state, stack, var_id, span)
424 }
425
426 fn eval_call<D: DebugContext>(
427 engine_state: &EngineState,
428 stack: &mut Stack,
429 call: &Call,
430 _: Span,
431 ) -> Result<Value, ShellError> {
432 eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
434 }
435
436 fn eval_external_call(
437 engine_state: &EngineState,
438 stack: &mut Stack,
439 head: &Expression,
440 args: &[ExternalArgument],
441 _: Span,
442 ) -> Result<Value, ShellError> {
443 let span = head.span(&engine_state);
444 eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
446 }
447
448 fn eval_collect<D: DebugContext>(
449 engine_state: &EngineState,
450 stack: &mut Stack,
451 var_id: VarId,
452 expr: &Expression,
453 ) -> Result<Value, ShellError> {
454 eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
457 .into_value(expr.span)
458 }
459
460 fn eval_subexpression<D: DebugContext>(
461 engine_state: &EngineState,
462 stack: &mut Stack,
463 block_id: BlockId,
464 span: Span,
465 ) -> Result<Value, ShellError> {
466 let block = engine_state.get_block(block_id);
467 eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
469 }
470
471 fn regex_match(
472 engine_state: &EngineState,
473 op_span: Span,
474 lhs: &Value,
475 rhs: &Value,
476 invert: bool,
477 expr_span: Span,
478 ) -> Result<Value, ShellError> {
479 lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
480 }
481
482 fn eval_assignment<D: DebugContext>(
483 engine_state: &EngineState,
484 stack: &mut Stack,
485 lhs: &Expression,
486 rhs: &Expression,
487 assignment: Assignment,
488 op_span: Span,
489 _expr_span: Span,
490 ) -> Result<Value, ShellError> {
491 let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
492
493 let rhs = match assignment {
494 Assignment::Assign => rhs,
495 Assignment::AddAssign => {
496 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
497 lhs.add(op_span, &rhs, op_span)?
498 }
499 Assignment::SubtractAssign => {
500 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
501 lhs.sub(op_span, &rhs, op_span)?
502 }
503 Assignment::MultiplyAssign => {
504 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
505 lhs.mul(op_span, &rhs, op_span)?
506 }
507 Assignment::DivideAssign => {
508 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
509 lhs.div(op_span, &rhs, op_span)?
510 }
511 Assignment::ConcatenateAssign => {
512 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
513 lhs.concat(op_span, &rhs, op_span)?
514 }
515 };
516
517 match &lhs.expr {
518 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
519 let var_info = engine_state.get_var(*var_id);
520 if var_info.mutable {
521 stack.add_var(*var_id, rhs);
522 Ok(Value::nothing(lhs.span(&engine_state)))
523 } else {
524 Err(ShellError::AssignmentRequiresMutableVar {
525 lhs_span: lhs.span(&engine_state),
526 })
527 }
528 }
529 Expr::FullCellPath(cell_path) => {
530 match &cell_path.head.expr {
531 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
532 let is_env = var_id == &ENV_VARIABLE_ID;
535 if is_env || engine_state.get_var(*var_id).mutable {
536 let mut lhs =
537 eval_expression::<D>(engine_state, stack, &cell_path.head)?;
538 if is_env {
539 if cell_path.tail.is_empty() {
541 return Err(ShellError::CannotReplaceEnv {
542 span: cell_path.head.span(&engine_state),
543 });
544 }
545
546 let (key, span) = match &cell_path.tail[0] {
549 PathMember::String { val, span, .. } => (val.to_string(), span),
550 PathMember::Int { val, span, .. } => (val.to_string(), span),
551 };
552 let original_key = if let Value::Record { val: record, .. } = &lhs {
553 record
554 .iter()
555 .rev()
556 .map(|(k, _)| k)
557 .find(|x| x.eq_ignore_case(&key))
558 .cloned()
559 .unwrap_or(key)
560 } else {
561 key
562 };
563
564 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
566 let value = lhs.follow_cell_path(&[{
567 let mut pm = cell_path.tail[0].clone();
568 pm.make_insensitive();
569 pm
570 }])?;
571
572 if is_automatic_env_var(&original_key) {
574 return Err(ShellError::AutomaticEnvVarSetManually {
575 envvar_name: original_key,
576 span: *span,
577 });
578 }
579
580 let is_config = original_key == "config";
581
582 stack.add_env_var(original_key, value.into_owned());
583
584 if is_config {
586 stack.update_config(engine_state)?;
587 }
588 } else {
589 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
590 stack.add_var(*var_id, lhs);
591 }
592 Ok(Value::nothing(cell_path.head.span(&engine_state)))
593 } else {
594 Err(ShellError::AssignmentRequiresMutableVar {
595 lhs_span: lhs.span(&engine_state),
596 })
597 }
598 }
599 _ => Err(ShellError::AssignmentRequiresVar {
600 lhs_span: lhs.span(&engine_state),
601 }),
602 }
603 }
604 _ => Err(ShellError::AssignmentRequiresVar {
605 lhs_span: lhs.span(&engine_state),
606 }),
607 }
608 }
609
610 fn eval_row_condition_or_closure(
611 engine_state: &EngineState,
612 stack: &mut Stack,
613 block_id: BlockId,
614 span: Span,
615 ) -> Result<Value, ShellError> {
616 let captures = engine_state
617 .get_block(block_id)
618 .captures
619 .iter()
620 .map(|(id, span)| {
621 stack
622 .get_var(*id, *span)
623 .or_else(|_| {
624 engine_state
625 .get_var(*id)
626 .const_val
627 .clone()
628 .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
629 })
630 .map(|var| (*id, var))
631 })
632 .collect::<Result<_, _>>()?;
633
634 Ok(Value::closure(Closure { block_id, captures }, span))
635 }
636
637 fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
638 let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
639
640 Ok(Value::string(name, span))
641 }
642
643 fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
644 Ok(Value::nothing(expr.span(&engine_state)))
645 }
646}
647
648pub(crate) fn is_automatic_env_var(var: &str) -> bool {
654 let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
655 names.iter().any(|&name| {
656 if cfg!(windows) {
657 name.eq_ignore_case(var)
658 } else {
659 name.eq(var)
660 }
661 })
662}