Skip to main content

nu_engine/
eval.rs

1#[allow(deprecated)]
2use crate::get_full_help;
3use crate::named_flags::{
4    expand_flag_record, flag_type_accepts_nothing, list_spread_before_required_error,
5};
6use crate::{EvalBlockWithEarlyReturnFn, eval_ir::eval_ir_block};
7use nu_protocol::{
8    BlockId, CompareTypes, Config, ENV_VARIABLE_ID, IntoPipelineData, PipelineData,
9    PipelineExecutionData, ShellError, Signature, Span, Value, VarId,
10    ast::{
11        Argument as AstArgument, Assignment, Block, Call, Expr, Expression, ExternalArgument,
12        PathMember,
13    },
14    debugger::{DebugContext, WithDebug, WithoutDebug},
15    engine::{Argument as EngineArgument, Closure, EngineState, EnvName, EnvVars, Stack},
16    eval_base::Eval,
17    shell_error::generic::GenericError,
18};
19use nu_utils::IgnoreCaseExt;
20use std::borrow::Cow;
21use std::collections::{HashMap, HashSet};
22use std::sync::Arc;
23
24/// [`CallEval`] is used to evaluate a command or closure call.
25///
26/// It is intended as an internal interface in the engine, to make sure
27/// command and closure calls behave the same.
28/// If you want to evaluate a closure in a command, use [`ClosureEval`] or
29/// [`ClosureEvalOnce`]. If you want to call a command, use [`eval_call`].
30///
31/// [`CallEval`] has a builder API.
32/// It is first created vial [`CallEval::new`],
33/// then has arguments added via [`CallEval::add_positional`] and [`CallEval::add_named`],
34/// and then can be run using [`CallEval::run`].
35#[derive(Clone)]
36pub struct CallEval {
37    callee_stack: Stack,
38    head_span: Span,
39    callee_span: Span,
40    arg_index: usize,
41    named_args: Vec<String>,
42    rest_args: Vec<Value>,
43    /// After a list/null rest spread, further positionals go to rest (matches IR `always_spread`).
44    always_spread: bool,
45    eval: EvalBlockWithEarlyReturnFn,
46}
47
48impl CallEval {
49    /// Create a new [`CallEval`] context
50    pub fn new(
51        callee_stack: Stack,
52        call_head: Span,
53        callee_span: Span,
54        eval: EvalBlockWithEarlyReturnFn,
55    ) -> Self {
56        Self {
57            callee_stack,
58            head_span: call_head,
59            callee_span,
60            arg_index: 0,
61            named_args: Vec::new(),
62            rest_args: Vec::new(),
63            always_spread: false,
64            eval,
65        }
66    }
67
68    /// Add a positional argument to the call stack.
69    ///
70    /// Returns an error if the given `value` does not match the type of
71    /// the argument according to the signature (see [`CallEval::new`]).
72    pub fn add_positional(
73        &mut self,
74        signature: &Signature,
75        value: Cow<Value>,
76    ) -> Result<&mut Self, ShellError> {
77        // After a rest spread, further positionals always go to rest (IR parity).
78        if self.always_spread {
79            return self.push_rest(signature, value);
80        }
81
82        let maybe_param = match self
83            .arg_index
84            .checked_sub(signature.required_positional.len())
85        {
86            // arg_index < required_len
87            None => signature.required_positional.get(self.arg_index),
88            // required_len <= arg_index < (required_len + optional_len)
89            Some(opt_idx) if opt_idx < signature.optional_positional.len() => {
90                signature.optional_positional.get(opt_idx)
91            }
92            // (required_len + optional_len) <= arg_index
93            _ => None,
94        };
95
96        if let Some(param) = maybe_param {
97            let param_type = param.shape.to_type();
98            if !value.is_subtype_of(&param_type) {
99                return Err(ShellError::CantConvert {
100                    to_type: param_type.to_string(),
101                    from_type: value.get_type().to_string(),
102                    span: value.span(),
103                    help: None,
104                });
105            }
106
107            let var_id = param
108                .var_id
109                .expect("internal error: all custom parameters must have var_ids");
110            self.callee_stack.add_var(var_id, value.into_owned());
111            self.arg_index += 1;
112            Ok(self)
113        } else {
114            self.push_rest(signature, value)
115        }
116    }
117
118    fn push_rest(
119        &mut self,
120        signature: &Signature,
121        value: Cow<Value>,
122    ) -> Result<&mut Self, ShellError> {
123        let Some(rest_positional) = &signature.rest_positional else {
124            // We do not consider it an error if more arguments
125            // are added than the closure takes. This makes it possible
126            // to omit any unused arguments in the closure definition.
127            return Ok(self);
128        };
129
130        let param_type = rest_positional.shape.to_type();
131        if !value.is_subtype_of(&param_type) {
132            return Err(ShellError::CantConvert {
133                to_type: param_type.to_string(),
134                from_type: value.get_type().to_string(),
135                span: value.span(),
136                help: None,
137            });
138        }
139
140        self.rest_args.push(value.into_owned());
141        Ok(self)
142    }
143
144    /// Spread a list into rest (IR parity). Errors if required positionals remain unbound.
145    pub fn add_list_spread(
146        &mut self,
147        signature: &Signature,
148        vals: Vec<Value>,
149        spread_span: Span,
150    ) -> Result<&mut Self, ShellError> {
151        if self.arg_index < signature.required_positional.len() && !self.always_spread {
152            return Err(list_spread_before_required_error(spread_span));
153        }
154        for v in vals {
155            self.push_rest(signature, Cow::Owned(v))?;
156        }
157        self.always_spread = true;
158        Ok(self)
159    }
160
161    /// Null rest-mode spread (IR parity). Errors if required positionals remain unbound.
162    pub fn add_null_spread(
163        &mut self,
164        signature: &Signature,
165        spread_span: Span,
166    ) -> Result<&mut Self, ShellError> {
167        if self.arg_index < signature.required_positional.len() && !self.always_spread {
168            return Err(list_spread_before_required_error(spread_span));
169        }
170        self.always_spread = true;
171        Ok(self)
172    }
173
174    /// Add a named parameter to the call stack.
175    pub fn add_named(
176        &mut self,
177        signature: &Signature,
178        long: &str,
179        short: Option<String>,
180        value: Option<Cow<Value>>,
181    ) -> Result<&mut Self, ShellError> {
182        let named = signature.named.iter().find(|named| {
183            long == named.long
184                || short
185                    .as_deref()
186                    .zip(named.short)
187                    .is_some_and(|(arg, param)| {
188                        let mut buf = [0; 4];
189                        param.encode_utf8(&mut buf) == arg
190                    })
191        });
192
193        if let Some(named) = named {
194            let var_id = named
195                .var_id
196                .expect("internal error: all custom parameters must have var_ids");
197
198            let value = value
199                .or_else(|| named.default_value.as_ref().map(Cow::Borrowed))
200                .unwrap_or_else(|| Cow::Owned(Value::bool(true, self.head_span)));
201
202            self.callee_stack.add_var(var_id, value.into_owned());
203            self.named_args.push(long.to_string());
204        }
205
206        Ok(self)
207    }
208
209    /// Sets the environment variables for the call.
210    pub fn with_env(
211        &mut self,
212        env_vars: &[Arc<EnvVars>],
213        env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
214    ) -> &mut Self {
215        self.callee_stack.with_env(env_vars, env_hidden);
216        self
217    }
218
219    /// Sets whether to enable debugging when evaluating the closure.
220    pub fn debug(&mut self, debug: bool) -> &mut Self {
221        if debug {
222            self.eval = eval_block_with_early_return::<WithDebug>
223        } else {
224            self.eval = eval_block_with_early_return::<WithoutDebug>
225        };
226        self
227    }
228
229    /// Run the given block.
230    pub fn run(
231        &mut self,
232        engine_state: &EngineState,
233        block: &Block,
234        input: PipelineData,
235    ) -> Result<PipelineData, ShellError> {
236        self.finalize_arguments(&block.signature)?;
237        self.arg_index = 0;
238        self.rest_args.clear();
239        (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
240    }
241
242    /// Finalize missing/default/rest arguments without evaluating the block yet.
243    ///
244    /// This is useful for callers that need to bind arguments against one signature and then
245    /// evaluate a modified copy of the block without re-running the argument finalization step.
246    pub fn finalize_for_signature(
247        &mut self,
248        signature: &Signature,
249    ) -> Result<&mut Self, ShellError> {
250        self.finalize_arguments(signature)?;
251        Ok(self)
252    }
253
254    /// Run a block after arguments have already been fully bound onto the callee stack.
255    pub fn run_prebound(
256        &mut self,
257        engine_state: &EngineState,
258        block: &Block,
259        input: PipelineData,
260    ) -> Result<PipelineData, ShellError> {
261        self.arg_index = 0;
262        self.rest_args.clear();
263        (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
264    }
265
266    /// Export the modified environment from callee to the caller.
267    pub fn redirect_env(&self, engine_state: &EngineState, stack: &mut Stack) {
268        redirect_env(engine_state, stack, &self.callee_stack);
269    }
270
271    /// Add default and rest values to the stack, raise error on
272    /// missing parameters.
273    fn finalize_arguments(&mut self, signature: &Signature) -> Result<(), ShellError> {
274        let remaining_positionals = signature
275            .required_positional
276            .iter()
277            .map(|p| (p, true))
278            .chain(signature.optional_positional.iter().map(|p| (p, false)))
279            // skip positional args added with add_positional
280            .skip(self.arg_index);
281
282        for (param, required) in remaining_positionals {
283            let var_id = param
284                .var_id
285                .expect("internal error: all custom parameters must have var_ids");
286
287            let maybe_value = param
288                .default_value
289                .clone()
290                .or((!required).then_some(Value::nothing(self.callee_span)));
291
292            if let Some(value) = maybe_value {
293                self.callee_stack.add_var(var_id, value);
294            } else {
295                return Err(ShellError::MissingParameter {
296                    param_name: param.name.to_string(),
297                    span: self.callee_span,
298                });
299            }
300        }
301
302        if let Some(rest_positional) = &signature.rest_positional {
303            let span = self
304                .rest_args
305                .first()
306                .map(|x| x.span())
307                .unwrap_or(self.callee_span);
308
309            self.callee_stack.add_var(
310                rest_positional
311                    .var_id
312                    .expect("Internal error: rest positional parameter lackes var_id"),
313                Value::list(self.rest_args.to_owned(), span),
314            );
315        }
316
317        let remaining_flags = signature
318            .named
319            .iter()
320            // Skip provided flags
321            .filter(|flag| !self.named_args.contains(&flag.long))
322            // Ignore named arguments without var_id.
323            // There is some code in nu_cli::completions that relies on this behavior of `eval_call`.
324            .filter_map(|flag| Some((flag.var_id?, flag)));
325
326        for (var_id, flag) in remaining_flags {
327            if flag.arg.is_none() {
328                self.callee_stack
329                    .add_var(var_id, Value::bool(false, self.head_span));
330            } else {
331                let value = flag
332                    .default_value
333                    .clone()
334                    .unwrap_or(Value::nothing(self.head_span));
335                self.callee_stack.add_var(var_id, value);
336            }
337        }
338
339        Ok(())
340    }
341}
342
343/// Evaluate a call to a command (builtin, custom or external)
344pub fn eval_call<D: DebugContext>(
345    engine_state: &EngineState,
346    caller_stack: &mut Stack,
347    call: &Call,
348    input: PipelineData,
349) -> Result<PipelineData, ShellError> {
350    engine_state.signals().check(&call.head)?;
351    let decl = engine_state.get_decl(call.decl_id);
352
353    if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
354        let help = get_full_help(decl, engine_state, caller_stack, call.head);
355        Ok(Value::string(help, call.head).into_pipeline_data())
356    } else if let Some(block_id) = decl.block_id() {
357        // call is a custom command
358        let block = engine_state.get_block(block_id);
359        let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
360
361        // Rust does not check recursion limits outside of const evaluation.
362        // But nu programs run in the same process as the shell.
363        // To prevent a stack overflow in user code from crashing the shell,
364        // we limit the recursion depth of function calls.
365        // Picked 50 arbitrarily, should work on all architectures.
366        let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
367        callee_stack.recursion_count += 1;
368        if callee_stack.recursion_count > maximum_call_stack_depth {
369            callee_stack.recursion_count = 0;
370            return Err(ShellError::RecursionLimitReached {
371                recursion_limit: maximum_call_stack_depth,
372                span: block.span,
373            });
374        }
375
376        let mut call_eval = CallEval::new(
377            callee_stack,
378            call.head,
379            block.span.unwrap_or(Span::unknown()),
380            eval_block_with_early_return::<D>,
381        );
382        let signature = decl.signature();
383        // Walk all arguments in order so record/list spreads interleave with positionals/flags
384        // the same way the IR path does after normalize_call_arguments.
385        for arg in &call.arguments {
386            match arg {
387                AstArgument::Positional(expr) | AstArgument::Unknown(expr) => {
388                    let result = eval_expression::<D>(engine_state, caller_stack, expr)?;
389                    call_eval.add_positional(&signature, Cow::Owned(result))?;
390                }
391                AstArgument::Named((long, short, maybe_expr)) => {
392                    let result: Option<Cow<Value>> = if let Some(expr) = maybe_expr {
393                        let value = eval_expression::<D>(engine_state, caller_stack, expr)?;
394                        if value.is_nothing() {
395                            let accepts_nothing = signature
396                                .get_long_flag(&long.item)
397                                .or_else(|| {
398                                    short
399                                        .as_ref()
400                                        .and_then(|s| s.item.chars().next())
401                                        .and_then(|c| signature.get_short_flag(c))
402                                })
403                                .is_some_and(|flag| flag_type_accepts_nothing(&flag));
404                            if !accepts_nothing {
405                                continue;
406                            }
407                        }
408                        Some(Cow::Owned(value))
409                    } else {
410                        None
411                    };
412                    call_eval.add_named(
413                        &signature,
414                        &long.item,
415                        short.as_ref().map(|s| s.item.clone()),
416                        result,
417                    )?;
418                }
419                // Light AST path: keep custom-command spreads aligned with IR gather/normalize
420                // semantics. Normal scripts use IR (`eval_block` → IR); this path still matters
421                // for residual AST `eval_call` consumers.
422                AstArgument::Spread(expr) => {
423                    let val = eval_expression::<D>(engine_state, caller_stack, expr)?;
424                    match val {
425                        Value::Record { val, .. } => {
426                            for engine_arg in
427                                expand_flag_record(&signature, val.into_owned(), expr.span)?
428                            {
429                                match engine_arg {
430                                    EngineArgument::Flag { data, name, .. } => {
431                                        let long =
432                                            std::str::from_utf8(&data[name]).unwrap_or_default();
433                                        call_eval.add_named(&signature, long, None, None)?;
434                                    }
435                                    EngineArgument::Named {
436                                        data, name, val, ..
437                                    } => {
438                                        let long =
439                                            std::str::from_utf8(&data[name]).unwrap_or_default();
440                                        call_eval.add_named(
441                                            &signature,
442                                            long,
443                                            None,
444                                            Some(Cow::Owned(val)),
445                                        )?;
446                                    }
447                                    _ => {}
448                                }
449                            }
450                        }
451                        Value::List { vals, .. } => {
452                            if signature.rest_positional.is_none() && !signature.allows_unknown_args
453                            {
454                                return Err(ShellError::Generic(GenericError::new(
455                                    "Cannot spread a list into this command",
456                                    "This command has no ...rest parameter to receive a list spread. Use a record to spread named flags, e.g. ...{flag: value}",
457                                    expr.span,
458                                )));
459                            }
460                            call_eval.add_list_spread(&signature, vals.into_owned(), expr.span)?;
461                        }
462                        Value::Nothing { .. } => {
463                            call_eval.add_null_spread(&signature, expr.span)?;
464                        }
465                        Value::Error { error, .. } => return Err(*error),
466                        other => {
467                            return Err(ShellError::CannotSpreadAsList { span: other.span() });
468                        }
469                    }
470                }
471            }
472        }
473
474        let result = call_eval.run(engine_state, block, input);
475
476        if block.redirect_env {
477            call_eval.redirect_env(engine_state, caller_stack);
478        }
479
480        result
481    } else {
482        // Builtin/plugin/external: normal scripts evaluate via IR, which runs
483        // `normalize_engine_arguments` for null omit and record flag spreads. This AST
484        // `eval_call` branch does not re-implement that path (IR-first).
485        decl.run(engine_state, caller_stack, &call.into(), input)
486    }
487}
488
489/// Redirect the environment from callee to the caller.
490pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
491    // Grab all environment variables from the callee
492    let caller_env_vars = caller_stack.get_env_var_names(engine_state);
493
494    // remove env vars that are present in the caller but not in the callee
495    // (the callee hid them)
496    for var in caller_env_vars.iter() {
497        if !callee_stack.has_env_var(engine_state, var) {
498            caller_stack.hide_env_var(engine_state, var);
499        }
500    }
501
502    // add new env vars from callee to caller
503    for (var, value) in callee_stack.get_stack_env_vars() {
504        caller_stack.add_env_var(var, value);
505    }
506
507    // set config to callee config, to capture any updates to that
508    caller_stack.config.clone_from(&callee_stack.config);
509}
510
511fn eval_external(
512    engine_state: &EngineState,
513    stack: &mut Stack,
514    head: &Expression,
515    args: &[ExternalArgument],
516    input: PipelineData,
517) -> Result<PipelineData, ShellError> {
518    let decl_id = engine_state
519        .find_decl("run-external".as_bytes(), &[])
520        .ok_or(ShellError::ExternalNotSupported {
521            span: head.span(&engine_state),
522        })?;
523
524    let command = engine_state.get_decl(decl_id);
525
526    let mut call = Call::new(head.span(&engine_state));
527
528    call.add_positional(head.clone());
529
530    for arg in args {
531        match arg {
532            ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
533            ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
534        }
535    }
536
537    command.run(engine_state, stack, &(&call).into(), input)
538}
539
540pub fn eval_expression<D: DebugContext>(
541    engine_state: &EngineState,
542    stack: &mut Stack,
543    expr: &Expression,
544) -> Result<Value, ShellError> {
545    let stack = &mut stack.start_collect_value();
546    <EvalRuntime as Eval>::eval::<D>(engine_state, stack, expr)
547}
548
549/// Checks the expression to see if it's a internal or external call. If so, passes the input
550/// into the call and gets out the result
551/// Otherwise, invokes the expression
552///
553/// It returns PipelineData with a boolean flag, indicating if the external failed to run.
554/// The boolean flag **may only be true** for external calls, for internal calls, it always to be false.
555pub fn eval_expression_with_input<D: DebugContext>(
556    engine_state: &EngineState,
557    stack: &mut Stack,
558    expr: &Expression,
559    mut input: PipelineData,
560) -> Result<PipelineData, ShellError> {
561    match &expr.expr {
562        Expr::Call(call) => {
563            input = eval_call::<D>(engine_state, stack, call, input)?;
564        }
565        Expr::ExternalCall(head, args) => {
566            input = eval_external(engine_state, stack, head, args, input)?;
567        }
568
569        Expr::Collect(var_id, expr) => {
570            input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
571        }
572
573        Expr::Subexpression(block_id) => {
574            let block = engine_state.get_block(*block_id);
575            // FIXME: protect this collect with ctrl-c
576            input = eval_subexpression::<D>(engine_state, stack, block, input)?;
577        }
578
579        Expr::FullCellPath(full_cell_path) => match &full_cell_path.head {
580            Expression {
581                expr: Expr::Subexpression(block_id),
582                span,
583                ..
584            } => {
585                let block = engine_state.get_block(*block_id);
586
587                if !full_cell_path.tail.is_empty() {
588                    let stack = &mut stack.start_collect_value();
589                    // FIXME: protect this collect with ctrl-c
590                    input = eval_subexpression::<D>(engine_state, stack, block, input)?
591                        .into_value(*span)?
592                        .follow_cell_path(&full_cell_path.tail)?
593                        .into_owned()
594                        .into_pipeline_data()
595                } else {
596                    input = eval_subexpression::<D>(engine_state, stack, block, input)?;
597                }
598            }
599            _ => {
600                let input_value = input.into_value(expr.span)?;
601                stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
602                input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
603            }
604        },
605
606        Expr::StringInterpolation(_) | Expr::GlobInterpolation(_, _) => {
607            let input_value = input.into_value(expr.span)?;
608            stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
609            let value = eval_expression::<D>(engine_state, stack, expr)?;
610            input = PipelineData::Value(value, None);
611        }
612
613        _ => {
614            let input_value = input.into_value(expr.span)?;
615            stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
616            let value = eval_expression::<D>(engine_state, stack, expr)?;
617            input = PipelineData::Value(value, None);
618        }
619    };
620
621    Ok(input)
622}
623
624pub fn eval_block<D: DebugContext>(
625    engine_state: &EngineState,
626    stack: &mut Stack,
627    block: &Block,
628    input: PipelineData,
629) -> Result<PipelineExecutionData, ShellError> {
630    let result = eval_ir_block::<D>(engine_state, stack, block, input);
631    if let Err(err) = &result {
632        stack.set_last_error(err);
633    }
634    result
635}
636
637/// Evaluate a block as an early return boundary.
638///
639/// A `return` is meant to end the command or closure it appears in and go no further. The
640/// "boundary" is the point where such a `return` stops propagating outward and becomes the
641/// block's normal result, instead of escaping to whatever called the command. This function is
642/// that point: an early `return` inside the block produces the block's result here, exactly like
643/// a value in tail position.
644///
645/// Concretely, [`eval_block`] runs the block and sets
646/// [`early_return`](PipelineExecutionData::early_return) to mark a result that came from a
647/// `return`; this function clears that flag, which is what "absorbs" the `return` so callers see
648/// an ordinary result.
649///
650/// This is used for blocks that `return` should not escape from, such as custom command bodies
651/// and closures: clearing the flag keeps an early `return` from leaking into the calling block.
652/// In contrast, [`eval_block`] leaves the flag intact, so its one consumer (top-level file
653/// evaluation) can see a top-level `return` and skip running `main`.
654pub fn eval_block_with_early_return<D: DebugContext>(
655    engine_state: &EngineState,
656    stack: &mut Stack,
657    block: &Block,
658    input: PipelineData,
659) -> Result<PipelineExecutionData, ShellError> {
660    let mut result = eval_block::<D>(engine_state, stack, block, input)?;
661    result.early_return = false;
662    Ok(result)
663}
664
665pub fn eval_collect<D: DebugContext>(
666    engine_state: &EngineState,
667    stack: &mut Stack,
668    var_id: VarId,
669    expr: &Expression,
670    mut input: PipelineData,
671) -> Result<PipelineData, ShellError> {
672    // Evaluate the expression with the variable set to the collected input
673    let span = input.span().unwrap_or(expr.span);
674
675    let metadata = input.take_metadata().and_then(|m| m.for_collect());
676
677    let input = input.into_value(span)?;
678
679    stack.add_var(var_id, input.clone());
680
681    let result = eval_expression_with_input::<D>(
682        engine_state,
683        stack,
684        expr,
685        // We still have to pass it as input
686        input.into_pipeline_data_with_metadata(metadata),
687    );
688
689    stack.remove_var(var_id);
690
691    result
692}
693
694pub fn eval_subexpression<D: DebugContext>(
695    engine_state: &EngineState,
696    stack: &mut Stack,
697    block: &Block,
698    input: PipelineData,
699) -> Result<PipelineData, ShellError> {
700    eval_block::<D>(engine_state, stack, block, input).map(|p| p.body)
701}
702
703pub fn eval_variable(
704    engine_state: &EngineState,
705    stack: &Stack,
706    var_id: VarId,
707    span: Span,
708) -> Result<Value, ShellError> {
709    match var_id {
710        // $nu
711        nu_protocol::NU_VARIABLE_ID => {
712            if let Some(val) = engine_state.get_constant(var_id) {
713                Ok(val.clone())
714            } else {
715                Err(ShellError::VariableNotFoundAtRuntime { span })
716            }
717        }
718        // $env
719        ENV_VARIABLE_ID => {
720            let env_vars = stack.get_env_vars(engine_state);
721            let env_columns = env_vars.keys();
722            let env_values = env_vars.values();
723
724            let mut pairs = env_columns
725                .map(|x| x.to_string())
726                .zip(env_values.cloned())
727                .collect::<Vec<(String, Value)>>();
728
729            pairs.sort_by(|a, b| a.0.cmp(&b.0));
730
731            Ok(Value::record(pairs.into_iter().collect(), span))
732        }
733        // interactive last-result (e.g. `$ans`)
734        // Truncation warning is deferred until after print so data is visible first.
735        id if id == nu_protocol::LAST_VARIABLE_ID => {
736            stack.defer_last_result_truncation_warning();
737            stack.get_var(var_id, span)
738        }
739        var_id => stack.get_var(var_id, span),
740    }
741}
742
743struct EvalRuntime;
744
745impl Eval for EvalRuntime {
746    type State<'a> = &'a EngineState;
747
748    type MutState = Stack;
749
750    fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
751        stack.get_config(engine_state)
752    }
753
754    fn eval_var(
755        engine_state: &EngineState,
756        stack: &mut Stack,
757        var_id: VarId,
758        span: Span,
759    ) -> Result<Value, ShellError> {
760        eval_variable(engine_state, stack, var_id, span)
761    }
762
763    fn eval_call<D: DebugContext>(
764        engine_state: &EngineState,
765        stack: &mut Stack,
766        call: &Call,
767        _: Span,
768    ) -> Result<Value, ShellError> {
769        // FIXME: protect this collect with ctrl-c
770        eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
771    }
772
773    fn eval_external_call(
774        engine_state: &EngineState,
775        stack: &mut Stack,
776        head: &Expression,
777        args: &[ExternalArgument],
778        _: Span,
779    ) -> Result<Value, ShellError> {
780        let span = head.span(&engine_state);
781        // FIXME: protect this collect with ctrl-c
782        eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
783    }
784
785    fn eval_collect<D: DebugContext>(
786        engine_state: &EngineState,
787        stack: &mut Stack,
788        var_id: VarId,
789        expr: &Expression,
790    ) -> Result<Value, ShellError> {
791        // It's a little bizarre, but the expression can still have some kind of result even with
792        // nothing input
793        eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
794            .into_value(expr.span)
795    }
796
797    fn eval_subexpression<D: DebugContext>(
798        engine_state: &EngineState,
799        stack: &mut Stack,
800        block_id: BlockId,
801        span: Span,
802    ) -> Result<Value, ShellError> {
803        let block = engine_state.get_block(block_id);
804        // FIXME: protect this collect with ctrl-c
805        eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
806    }
807
808    fn regex_match(
809        engine_state: &EngineState,
810        op_span: Span,
811        lhs: &Value,
812        rhs: &Value,
813        invert: bool,
814        expr_span: Span,
815    ) -> Result<Value, ShellError> {
816        lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
817    }
818
819    fn eval_assignment<D: DebugContext>(
820        engine_state: &EngineState,
821        stack: &mut Stack,
822        lhs: &Expression,
823        rhs: &Expression,
824        assignment: Assignment,
825        op_span: Span,
826        _expr_span: Span,
827    ) -> Result<Value, ShellError> {
828        let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
829
830        let rhs = match assignment {
831            Assignment::Assign => rhs,
832            Assignment::AddAssign => {
833                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
834                lhs.add(op_span, &rhs, op_span)?
835            }
836            Assignment::SubtractAssign => {
837                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
838                lhs.sub(op_span, &rhs, op_span)?
839            }
840            Assignment::MultiplyAssign => {
841                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
842                lhs.mul(op_span, &rhs, op_span)?
843            }
844            Assignment::DivideAssign => {
845                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
846                lhs.div(op_span, &rhs, op_span)?
847            }
848            Assignment::ConcatenateAssign => {
849                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
850                lhs.concat(op_span, &rhs, op_span)?
851            }
852        };
853
854        match &lhs.expr {
855            Expr::Var(var_id) | Expr::VarDecl(var_id) => {
856                let var_info = engine_state.get_var(*var_id);
857                if var_info.mutable {
858                    stack.add_var(*var_id, rhs);
859                    Ok(Value::nothing(lhs.span(&engine_state)))
860                } else {
861                    Err(ShellError::AssignmentRequiresMutableVar {
862                        lhs_span: lhs.span(&engine_state),
863                    })
864                }
865            }
866            Expr::FullCellPath(cell_path) => {
867                match &cell_path.head.expr {
868                    Expr::Var(var_id) | Expr::VarDecl(var_id) => {
869                        // The $env variable is considered "mutable" in Nushell.
870                        // As such, give it special treatment here.
871                        let is_env = var_id == &ENV_VARIABLE_ID;
872                        if is_env || engine_state.get_var(*var_id).mutable {
873                            if is_env {
874                                let mut lhs =
875                                    eval_expression::<D>(engine_state, stack, &cell_path.head)?;
876
877                                // Reject attempts to assign to the entire $env
878                                if cell_path.tail.is_empty() {
879                                    return Err(ShellError::CannotReplaceEnv {
880                                        span: cell_path.head.span(&engine_state),
881                                    });
882                                }
883
884                                // Updating environment variables should be case-preserving,
885                                // so we need to figure out the original key before we do anything.
886                                let (key, span) = match &cell_path.tail[0] {
887                                    PathMember::String { val, span, .. } => (val.to_string(), span),
888                                    PathMember::Int { val, span, .. } => (val.to_string(), span),
889                                };
890                                let original_key = if let Value::Record { val: record, .. } = &lhs {
891                                    record
892                                        .iter()
893                                        .rev()
894                                        .map(|(k, _)| k)
895                                        .find(|x| x.eq_ignore_case(&key))
896                                        .cloned()
897                                        .unwrap_or(key)
898                                } else {
899                                    key
900                                };
901
902                                // Retrieve the updated environment value.
903                                lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
904                                let value = lhs.follow_cell_path(&[{
905                                    let mut pm = cell_path.tail[0].clone();
906                                    pm.make_insensitive();
907                                    pm
908                                }])?;
909
910                                // Reject attempts to set automatic environment variables.
911                                if is_automatic_env_var(&original_key) {
912                                    return Err(ShellError::AutomaticEnvVarSetManually {
913                                        envvar_name: original_key,
914                                        span: *span,
915                                    });
916                                }
917
918                                let is_config = original_key == "config";
919
920                                stack.add_env_var(original_key, value.into_owned());
921
922                                // Trigger the update to config, if we modified that.
923                                if is_config {
924                                    stack.update_config(engine_state)?;
925                                }
926                            } else {
927                                // Optimized: mutate the variable in-place on the stack,
928                                // avoiding the clone from lookup_var and the move-back from add_var.
929                                stack.upsert_var_cell_path(
930                                    *var_id,
931                                    &cell_path.tail,
932                                    rhs,
933                                    cell_path.head.span(&engine_state),
934                                )?;
935                            }
936                            Ok(Value::nothing(cell_path.head.span(&engine_state)))
937                        } else {
938                            Err(ShellError::AssignmentRequiresMutableVar {
939                                lhs_span: lhs.span(&engine_state),
940                            })
941                        }
942                    }
943                    _ => Err(ShellError::AssignmentRequiresVar {
944                        lhs_span: lhs.span(&engine_state),
945                    }),
946                }
947            }
948            _ => Err(ShellError::AssignmentRequiresVar {
949                lhs_span: lhs.span(&engine_state),
950            }),
951        }
952    }
953
954    fn eval_row_condition_or_closure(
955        engine_state: &EngineState,
956        stack: &mut Stack,
957        block_id: BlockId,
958        span: Span,
959    ) -> Result<Value, ShellError> {
960        let captures = engine_state
961            .get_block(block_id)
962            .captures
963            .iter()
964            .map(|(id, span)| {
965                stack
966                    .get_var(*id, *span)
967                    .or_else(|_| {
968                        engine_state
969                            .get_var(*id)
970                            .const_val
971                            .clone()
972                            .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
973                    })
974                    .map(|var| (*id, var))
975            })
976            .collect::<Result<_, _>>()?;
977
978        Ok(Value::closure(Closure { block_id, captures }, span))
979    }
980
981    fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
982        let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
983
984        Ok(Value::string(name, span))
985    }
986
987    fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
988        Ok(Value::nothing(expr.span(&engine_state)))
989    }
990}
991
992/// Returns whether a string, when used as the name of an environment variable,
993/// is considered an automatic environment variable.
994///
995/// An automatic environment variable cannot be assigned to by user code.
996/// Current there are three of them: $env.PWD, $env.FILE_PWD, $env.CURRENT_FILE
997pub(crate) fn is_automatic_env_var(var: &str) -> bool {
998    let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
999    names.iter().any(|&name| {
1000        if cfg!(windows) {
1001            name.eq_ignore_case(var)
1002        } else {
1003            name.eq(var)
1004        }
1005    })
1006}