Skip to main content

nu_engine/
eval.rs

1#[allow(deprecated)]
2use crate::get_full_help;
3use crate::{EvalBlockWithEarlyReturnFn, eval_ir::eval_ir_block};
4use nu_protocol::{
5    BlockId, CompareTypes, Config, ENV_VARIABLE_ID, IntoPipelineData, PipelineData,
6    PipelineExecutionData, 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/// [`CallEval`] is used to evaluate a command or closure call.
18///
19/// It is intended as an internal interface in the engine, to make sure
20/// command and closure calls behave the same.
21/// If you want to evaluate a closure in a command, use [`ClosureEval`] or
22/// [`ClosureEvalOnce`]. If you want to call a command, use [`eval_call`].
23///
24/// [`CallEval`] has a builder API.
25/// It is first created vial [`CallEval::new`],
26/// then has arguments added via [`CallEval::add_positional`] and [`CallEval::add_named`],
27/// and then can be run using [`CallEval::run`].
28#[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    /// Create a new [`CallEval`] context
41    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    /// Add a positional argument to the call stack.
59    ///
60    /// Returns an error if the given `value` does not match the type of
61    /// the argument according to the signature (see [`CallEval::new`]).
62    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            // arg_index < required_len
72            None => signature.required_positional.get(self.arg_index),
73            // required_len <= arg_index < (required_len + optional_len)
74            Some(opt_idx) if opt_idx < signature.optional_positional.len() => {
75                signature.optional_positional.get(opt_idx)
76            }
77            // (required_len + optional_len) <= arg_index
78            _ => None,
79        };
80
81        if let Some(param) = maybe_param {
82            let param_type = param.shape.to_type();
83            if !value.is_subtype_of(&param_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            // assign arg to rest params
100            let Some(rest_positional) = &signature.rest_positional else {
101                // We do not consider it an error if more arguments
102                // are added than the closure takes. This makes it possible
103                // to omit any unused arguments in the closure definition.
104                return Ok(self);
105            };
106
107            let param_type = rest_positional.shape.to_type();
108            if !value.is_subtype_of(&param_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    /// Add a named parameter to the call stack.
123    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    /// Sets the environment variables for the call.
158    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    /// Sets whether to enable debugging when evaluating the closure.
168    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    /// Run the given block.
178    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    /// Finalize missing/default/rest arguments without evaluating the block yet.
191    ///
192    /// This is useful for callers that need to bind arguments against one signature and then
193    /// evaluate a modified copy of the block without re-running the argument finalization step.
194    pub fn finalize_for_signature(
195        &mut self,
196        signature: &Signature,
197    ) -> Result<&mut Self, ShellError> {
198        self.finalize_arguments(signature)?;
199        Ok(self)
200    }
201
202    /// Run a block after arguments have already been fully bound onto the callee stack.
203    pub fn run_prebound(
204        &mut self,
205        engine_state: &EngineState,
206        block: &Block,
207        input: PipelineData,
208    ) -> Result<PipelineData, ShellError> {
209        self.arg_index = 0;
210        self.rest_args.clear();
211        (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
212    }
213
214    /// Export the modified environment from callee to the caller.
215    pub fn redirect_env(&self, engine_state: &EngineState, stack: &mut Stack) {
216        redirect_env(engine_state, stack, &self.callee_stack);
217    }
218
219    /// Add default and rest values to the stack, raise error on
220    /// missing parameters.
221    fn finalize_arguments(&mut self, signature: &Signature) -> Result<(), ShellError> {
222        let remaining_positionals = signature
223            .required_positional
224            .iter()
225            .map(|p| (p, true))
226            .chain(signature.optional_positional.iter().map(|p| (p, false)))
227            // skip positional args added with add_positional
228            .skip(self.arg_index);
229
230        for (param, required) in remaining_positionals {
231            let var_id = param
232                .var_id
233                .expect("internal error: all custom parameters must have var_ids");
234
235            let maybe_value = param
236                .default_value
237                .clone()
238                .or((!required).then_some(Value::nothing(self.callee_span)));
239
240            if let Some(value) = maybe_value {
241                self.callee_stack.add_var(var_id, value);
242            } else {
243                return Err(ShellError::MissingParameter {
244                    param_name: param.name.to_string(),
245                    span: self.callee_span,
246                });
247            }
248        }
249
250        if let Some(rest_positional) = &signature.rest_positional {
251            let span = self
252                .rest_args
253                .first()
254                .map(|x| x.span())
255                .unwrap_or(self.callee_span);
256
257            self.callee_stack.add_var(
258                rest_positional
259                    .var_id
260                    .expect("Internal error: rest positional parameter lackes var_id"),
261                Value::list(self.rest_args.to_owned(), span),
262            );
263        }
264
265        let remaining_flags = signature
266            .named
267            .iter()
268            // Skip provided flags
269            .filter(|flag| !self.named_args.contains(&flag.long))
270            // Ignore named arguments without var_id.
271            // There is some code in nu_cli::completions that relies on this behavior of `eval_call`.
272            .filter_map(|flag| Some((flag.var_id?, flag)));
273
274        for (var_id, flag) in remaining_flags {
275            if flag.arg.is_none() {
276                self.callee_stack
277                    .add_var(var_id, Value::bool(false, self.head_span));
278            } else {
279                let value = flag
280                    .default_value
281                    .clone()
282                    .unwrap_or(Value::nothing(self.head_span));
283                self.callee_stack.add_var(var_id, value);
284            }
285        }
286
287        Ok(())
288    }
289}
290
291/// Evaluate a call to a command (builtin, custom or external)
292pub fn eval_call<D: DebugContext>(
293    engine_state: &EngineState,
294    caller_stack: &mut Stack,
295    call: &Call,
296    input: PipelineData,
297) -> Result<PipelineData, ShellError> {
298    engine_state.signals().check(&call.head)?;
299    let decl = engine_state.get_decl(call.decl_id);
300
301    if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
302        let help = get_full_help(decl, engine_state, caller_stack, call.head);
303        Ok(Value::string(help, call.head).into_pipeline_data())
304    } else if let Some(block_id) = decl.block_id() {
305        // call is a custom command
306        let block = engine_state.get_block(block_id);
307        let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
308
309        // Rust does not check recursion limits outside of const evaluation.
310        // But nu programs run in the same process as the shell.
311        // To prevent a stack overflow in user code from crashing the shell,
312        // we limit the recursion depth of function calls.
313        // Picked 50 arbitrarily, should work on all architectures.
314        let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
315        callee_stack.recursion_count += 1;
316        if callee_stack.recursion_count > maximum_call_stack_depth {
317            callee_stack.recursion_count = 0;
318            return Err(ShellError::RecursionLimitReached {
319                recursion_limit: maximum_call_stack_depth,
320                span: block.span,
321            });
322        }
323
324        let mut call_eval = CallEval::new(
325            callee_stack,
326            call.head,
327            block.span.unwrap_or(Span::unknown()),
328            eval_block_with_early_return::<D>,
329        );
330        for arg in call.positional_iter() {
331            let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
332            call_eval.add_positional(&decl.signature(), Cow::Owned(result))?;
333        }
334        for call_named in call.named_iter() {
335            let result: Option<Cow<Value>> = if let Some(arg) = &call_named.2 {
336                Some(Cow::Owned(eval_expression::<D>(
337                    engine_state,
338                    caller_stack,
339                    arg,
340                )?))
341            } else {
342                None
343            };
344            call_eval.add_named(
345                &decl.signature(),
346                &call_named.0.item,
347                call_named.1.clone().map(|x| x.item),
348                result,
349            )?;
350        }
351
352        let result = call_eval.run(engine_state, block, input);
353
354        if block.redirect_env {
355            call_eval.redirect_env(engine_state, caller_stack);
356        }
357
358        result
359    } else {
360        // call is a builtin or external command
361        // We pass caller_stack here with the knowledge that internal commands
362        // are going to be specifically looking for global state in the stack
363        // rather than any local state.
364        decl.run(engine_state, caller_stack, &call.into(), input)
365    }
366}
367
368/// Redirect the environment from callee to the caller.
369pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
370    // Grab all environment variables from the callee
371    let caller_env_vars = caller_stack.get_env_var_names(engine_state);
372
373    // remove env vars that are present in the caller but not in the callee
374    // (the callee hid them)
375    for var in caller_env_vars.iter() {
376        if !callee_stack.has_env_var(engine_state, var) {
377            caller_stack.hide_env_var(engine_state, var);
378        }
379    }
380
381    // add new env vars from callee to caller
382    for (var, value) in callee_stack.get_stack_env_vars() {
383        caller_stack.add_env_var(var, value);
384    }
385
386    // set config to callee config, to capture any updates to that
387    caller_stack.config.clone_from(&callee_stack.config);
388}
389
390fn eval_external(
391    engine_state: &EngineState,
392    stack: &mut Stack,
393    head: &Expression,
394    args: &[ExternalArgument],
395    input: PipelineData,
396) -> Result<PipelineData, ShellError> {
397    let decl_id = engine_state
398        .find_decl("run-external".as_bytes(), &[])
399        .ok_or(ShellError::ExternalNotSupported {
400            span: head.span(&engine_state),
401        })?;
402
403    let command = engine_state.get_decl(decl_id);
404
405    let mut call = Call::new(head.span(&engine_state));
406
407    call.add_positional(head.clone());
408
409    for arg in args {
410        match arg {
411            ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
412            ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
413        }
414    }
415
416    command.run(engine_state, stack, &(&call).into(), input)
417}
418
419pub fn eval_expression<D: DebugContext>(
420    engine_state: &EngineState,
421    stack: &mut Stack,
422    expr: &Expression,
423) -> Result<Value, ShellError> {
424    let stack = &mut stack.start_collect_value();
425    <EvalRuntime as Eval>::eval::<D>(engine_state, stack, expr)
426}
427
428/// Checks the expression to see if it's a internal or external call. If so, passes the input
429/// into the call and gets out the result
430/// Otherwise, invokes the expression
431///
432/// It returns PipelineData with a boolean flag, indicating if the external failed to run.
433/// The boolean flag **may only be true** for external calls, for internal calls, it always to be false.
434pub fn eval_expression_with_input<D: DebugContext>(
435    engine_state: &EngineState,
436    stack: &mut Stack,
437    expr: &Expression,
438    mut input: PipelineData,
439) -> Result<PipelineData, ShellError> {
440    match &expr.expr {
441        Expr::Call(call) => {
442            input = eval_call::<D>(engine_state, stack, call, input)?;
443        }
444        Expr::ExternalCall(head, args) => {
445            input = eval_external(engine_state, stack, head, args, input)?;
446        }
447
448        Expr::Collect(var_id, expr) => {
449            input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
450        }
451
452        Expr::Subexpression(block_id) => {
453            let block = engine_state.get_block(*block_id);
454            // FIXME: protect this collect with ctrl-c
455            input = eval_subexpression::<D>(engine_state, stack, block, input)?;
456        }
457
458        Expr::FullCellPath(full_cell_path) => match &full_cell_path.head {
459            Expression {
460                expr: Expr::Subexpression(block_id),
461                span,
462                ..
463            } => {
464                let block = engine_state.get_block(*block_id);
465
466                if !full_cell_path.tail.is_empty() {
467                    let stack = &mut stack.start_collect_value();
468                    // FIXME: protect this collect with ctrl-c
469                    input = eval_subexpression::<D>(engine_state, stack, block, input)?
470                        .into_value(*span)?
471                        .follow_cell_path(&full_cell_path.tail)?
472                        .into_owned()
473                        .into_pipeline_data()
474                } else {
475                    input = eval_subexpression::<D>(engine_state, stack, block, input)?;
476                }
477            }
478            _ => {
479                let input_value = input.into_value(expr.span)?;
480                stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
481                input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
482            }
483        },
484
485        Expr::StringInterpolation(_) | Expr::GlobInterpolation(_, _) => {
486            let input_value = input.into_value(expr.span)?;
487            stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
488            let value = eval_expression::<D>(engine_state, stack, expr)?;
489            input = PipelineData::Value(value, None);
490        }
491
492        _ => {
493            let input_value = input.into_value(expr.span)?;
494            stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
495            let value = eval_expression::<D>(engine_state, stack, expr)?;
496            input = PipelineData::Value(value, None);
497        }
498    };
499
500    Ok(input)
501}
502
503pub fn eval_block<D: DebugContext>(
504    engine_state: &EngineState,
505    stack: &mut Stack,
506    block: &Block,
507    input: PipelineData,
508) -> Result<PipelineExecutionData, ShellError> {
509    let result = eval_ir_block::<D>(engine_state, stack, block, input);
510    if let Err(err) = &result {
511        stack.set_last_error(err);
512    }
513    result
514}
515
516pub fn eval_block_with_early_return<D: DebugContext>(
517    engine_state: &EngineState,
518    stack: &mut Stack,
519    block: &Block,
520    input: PipelineData,
521) -> Result<PipelineExecutionData, ShellError> {
522    match eval_block::<D>(engine_state, stack, block, input) {
523        Err(ShellError::Return { span: _, value }) => Ok(PipelineExecutionData::from(
524            PipelineData::value(*value, None),
525        )),
526        x => x,
527    }
528}
529
530pub fn eval_collect<D: DebugContext>(
531    engine_state: &EngineState,
532    stack: &mut Stack,
533    var_id: VarId,
534    expr: &Expression,
535    mut input: PipelineData,
536) -> Result<PipelineData, ShellError> {
537    // Evaluate the expression with the variable set to the collected input
538    let span = input.span().unwrap_or(expr.span);
539
540    let metadata = input.take_metadata().and_then(|m| m.for_collect());
541
542    let input = input.into_value(span)?;
543
544    stack.add_var(var_id, input.clone());
545
546    let result = eval_expression_with_input::<D>(
547        engine_state,
548        stack,
549        expr,
550        // We still have to pass it as input
551        input.into_pipeline_data_with_metadata(metadata),
552    );
553
554    stack.remove_var(var_id);
555
556    result
557}
558
559pub fn eval_subexpression<D: DebugContext>(
560    engine_state: &EngineState,
561    stack: &mut Stack,
562    block: &Block,
563    input: PipelineData,
564) -> Result<PipelineData, ShellError> {
565    eval_block::<D>(engine_state, stack, block, input).map(|p| p.body)
566}
567
568pub fn eval_variable(
569    engine_state: &EngineState,
570    stack: &Stack,
571    var_id: VarId,
572    span: Span,
573) -> Result<Value, ShellError> {
574    match var_id {
575        // $nu
576        nu_protocol::NU_VARIABLE_ID => {
577            if let Some(val) = engine_state.get_constant(var_id) {
578                Ok(val.clone())
579            } else {
580                Err(ShellError::VariableNotFoundAtRuntime { span })
581            }
582        }
583        // $env
584        ENV_VARIABLE_ID => {
585            let env_vars = stack.get_env_vars(engine_state);
586            let env_columns = env_vars.keys();
587            let env_values = env_vars.values();
588
589            let mut pairs = env_columns
590                .map(|x| x.to_string())
591                .zip(env_values.cloned())
592                .collect::<Vec<(String, Value)>>();
593
594            pairs.sort_by(|a, b| a.0.cmp(&b.0));
595
596            Ok(Value::record(pairs.into_iter().collect(), span))
597        }
598        var_id => stack.get_var(var_id, span),
599    }
600}
601
602struct EvalRuntime;
603
604impl Eval for EvalRuntime {
605    type State<'a> = &'a EngineState;
606
607    type MutState = Stack;
608
609    fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
610        stack.get_config(engine_state)
611    }
612
613    fn eval_var(
614        engine_state: &EngineState,
615        stack: &mut Stack,
616        var_id: VarId,
617        span: Span,
618    ) -> Result<Value, ShellError> {
619        eval_variable(engine_state, stack, var_id, span)
620    }
621
622    fn eval_call<D: DebugContext>(
623        engine_state: &EngineState,
624        stack: &mut Stack,
625        call: &Call,
626        _: Span,
627    ) -> Result<Value, ShellError> {
628        // FIXME: protect this collect with ctrl-c
629        eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
630    }
631
632    fn eval_external_call(
633        engine_state: &EngineState,
634        stack: &mut Stack,
635        head: &Expression,
636        args: &[ExternalArgument],
637        _: Span,
638    ) -> Result<Value, ShellError> {
639        let span = head.span(&engine_state);
640        // FIXME: protect this collect with ctrl-c
641        eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
642    }
643
644    fn eval_collect<D: DebugContext>(
645        engine_state: &EngineState,
646        stack: &mut Stack,
647        var_id: VarId,
648        expr: &Expression,
649    ) -> Result<Value, ShellError> {
650        // It's a little bizarre, but the expression can still have some kind of result even with
651        // nothing input
652        eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
653            .into_value(expr.span)
654    }
655
656    fn eval_subexpression<D: DebugContext>(
657        engine_state: &EngineState,
658        stack: &mut Stack,
659        block_id: BlockId,
660        span: Span,
661    ) -> Result<Value, ShellError> {
662        let block = engine_state.get_block(block_id);
663        // FIXME: protect this collect with ctrl-c
664        eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
665    }
666
667    fn regex_match(
668        engine_state: &EngineState,
669        op_span: Span,
670        lhs: &Value,
671        rhs: &Value,
672        invert: bool,
673        expr_span: Span,
674    ) -> Result<Value, ShellError> {
675        lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
676    }
677
678    fn eval_assignment<D: DebugContext>(
679        engine_state: &EngineState,
680        stack: &mut Stack,
681        lhs: &Expression,
682        rhs: &Expression,
683        assignment: Assignment,
684        op_span: Span,
685        _expr_span: Span,
686    ) -> Result<Value, ShellError> {
687        let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
688
689        let rhs = match assignment {
690            Assignment::Assign => rhs,
691            Assignment::AddAssign => {
692                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
693                lhs.add(op_span, &rhs, op_span)?
694            }
695            Assignment::SubtractAssign => {
696                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
697                lhs.sub(op_span, &rhs, op_span)?
698            }
699            Assignment::MultiplyAssign => {
700                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
701                lhs.mul(op_span, &rhs, op_span)?
702            }
703            Assignment::DivideAssign => {
704                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
705                lhs.div(op_span, &rhs, op_span)?
706            }
707            Assignment::ConcatenateAssign => {
708                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
709                lhs.concat(op_span, &rhs, op_span)?
710            }
711        };
712
713        match &lhs.expr {
714            Expr::Var(var_id) | Expr::VarDecl(var_id) => {
715                let var_info = engine_state.get_var(*var_id);
716                if var_info.mutable {
717                    stack.add_var(*var_id, rhs);
718                    Ok(Value::nothing(lhs.span(&engine_state)))
719                } else {
720                    Err(ShellError::AssignmentRequiresMutableVar {
721                        lhs_span: lhs.span(&engine_state),
722                    })
723                }
724            }
725            Expr::FullCellPath(cell_path) => {
726                match &cell_path.head.expr {
727                    Expr::Var(var_id) | Expr::VarDecl(var_id) => {
728                        // The $env variable is considered "mutable" in Nushell.
729                        // As such, give it special treatment here.
730                        let is_env = var_id == &ENV_VARIABLE_ID;
731                        if is_env || engine_state.get_var(*var_id).mutable {
732                            let mut lhs =
733                                eval_expression::<D>(engine_state, stack, &cell_path.head)?;
734                            if is_env {
735                                // Reject attempts to assign to the entire $env
736                                if cell_path.tail.is_empty() {
737                                    return Err(ShellError::CannotReplaceEnv {
738                                        span: cell_path.head.span(&engine_state),
739                                    });
740                                }
741
742                                // Updating environment variables should be case-preserving,
743                                // so we need to figure out the original key before we do anything.
744                                let (key, span) = match &cell_path.tail[0] {
745                                    PathMember::String { val, span, .. } => (val.to_string(), span),
746                                    PathMember::Int { val, span, .. } => (val.to_string(), span),
747                                };
748                                let original_key = if let Value::Record { val: record, .. } = &lhs {
749                                    record
750                                        .iter()
751                                        .rev()
752                                        .map(|(k, _)| k)
753                                        .find(|x| x.eq_ignore_case(&key))
754                                        .cloned()
755                                        .unwrap_or(key)
756                                } else {
757                                    key
758                                };
759
760                                // Retrieve the updated environment value.
761                                lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
762                                let value = lhs.follow_cell_path(&[{
763                                    let mut pm = cell_path.tail[0].clone();
764                                    pm.make_insensitive();
765                                    pm
766                                }])?;
767
768                                // Reject attempts to set automatic environment variables.
769                                if is_automatic_env_var(&original_key) {
770                                    return Err(ShellError::AutomaticEnvVarSetManually {
771                                        envvar_name: original_key,
772                                        span: *span,
773                                    });
774                                }
775
776                                let is_config = original_key == "config";
777
778                                stack.add_env_var(original_key, value.into_owned());
779
780                                // Trigger the update to config, if we modified that.
781                                if is_config {
782                                    stack.update_config(engine_state)?;
783                                }
784                            } else {
785                                lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
786                                stack.add_var(*var_id, lhs);
787                            }
788                            Ok(Value::nothing(cell_path.head.span(&engine_state)))
789                        } else {
790                            Err(ShellError::AssignmentRequiresMutableVar {
791                                lhs_span: lhs.span(&engine_state),
792                            })
793                        }
794                    }
795                    _ => Err(ShellError::AssignmentRequiresVar {
796                        lhs_span: lhs.span(&engine_state),
797                    }),
798                }
799            }
800            _ => Err(ShellError::AssignmentRequiresVar {
801                lhs_span: lhs.span(&engine_state),
802            }),
803        }
804    }
805
806    fn eval_row_condition_or_closure(
807        engine_state: &EngineState,
808        stack: &mut Stack,
809        block_id: BlockId,
810        span: Span,
811    ) -> Result<Value, ShellError> {
812        let captures = engine_state
813            .get_block(block_id)
814            .captures
815            .iter()
816            .map(|(id, span)| {
817                stack
818                    .get_var(*id, *span)
819                    .or_else(|_| {
820                        engine_state
821                            .get_var(*id)
822                            .const_val
823                            .clone()
824                            .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
825                    })
826                    .map(|var| (*id, var))
827            })
828            .collect::<Result<_, _>>()?;
829
830        Ok(Value::closure(Closure { block_id, captures }, span))
831    }
832
833    fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
834        let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
835
836        Ok(Value::string(name, span))
837    }
838
839    fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
840        Ok(Value::nothing(expr.span(&engine_state)))
841    }
842}
843
844/// Returns whether a string, when used as the name of an environment variable,
845/// is considered an automatic environment variable.
846///
847/// An automatic environment variable cannot be assigned to by user code.
848/// Current there are three of them: $env.PWD, $env.FILE_PWD, $env.CURRENT_FILE
849pub(crate) fn is_automatic_env_var(var: &str) -> bool {
850    let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
851    names.iter().any(|&name| {
852        if cfg!(windows) {
853            name.eq_ignore_case(var)
854        } else {
855            name.eq(var)
856        }
857    })
858}