Skip to main content

nu_engine/
eval.rs

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        // Rust does not check recursion limits outside of const evaluation.
33        // But nu programs run in the same process as the shell.
34        // To prevent a stack overflow in user code from crashing the shell,
35        // we limit the recursion depth of function calls.
36        // Picked 50 arbitrarily, should work on all architectures.
37        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(&param_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) = &param.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        // We pass caller_stack here with the knowledge that internal commands
162        // are going to be specifically looking for global state in the stack
163        // rather than any local state.
164        decl.run(engine_state, caller_stack, &call.into(), input)
165    }
166}
167
168/// Redirect the environment from callee to the caller.
169pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
170    // Grab all environment variables from the callee
171    let caller_env_vars = caller_stack.get_env_var_names(engine_state);
172
173    // remove env vars that are present in the caller but not in the callee
174    // (the callee hid them)
175    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    // add new env vars from callee to caller
182    for (var, value) in callee_stack.get_stack_env_vars() {
183        caller_stack.add_env_var(var, value);
184    }
185
186    // set config to callee config, to capture any updates to that
187    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
228/// Checks the expression to see if it's a internal or external call. If so, passes the input
229/// into the call and gets out the result
230/// Otherwise, invokes the expression
231///
232/// It returns PipelineData with a boolean flag, indicating if the external failed to run.
233/// The boolean flag **may only be true** for external calls, for internal calls, it always to be false.
234pub 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            // FIXME: protect this collect with ctrl-c
255            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                    // FIXME: protect this collect with ctrl-c
269                    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    // Evaluate the expression with the variable set to the collected input
342    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        // We still have to pass it as input
355        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
380        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
388        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        // FIXME: protect this collect with ctrl-c
433        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        // FIXME: protect this collect with ctrl-c
445        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        // It's a little bizarre, but the expression can still have some kind of result even with
455        // nothing input
456        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        // FIXME: protect this collect with ctrl-c
468        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                        // The $env variable is considered "mutable" in Nushell.
533                        // As such, give it special treatment here.
534                        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                                // Reject attempts to assign to the entire $env
540                                if cell_path.tail.is_empty() {
541                                    return Err(ShellError::CannotReplaceEnv {
542                                        span: cell_path.head.span(&engine_state),
543                                    });
544                                }
545
546                                // Updating environment variables should be case-preserving,
547                                // so we need to figure out the original key before we do anything.
548                                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                                // Retrieve the updated environment value.
565                                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                                // Reject attempts to set automatic environment variables.
573                                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                                // Trigger the update to config, if we modified that.
585                                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
648/// Returns whether a string, when used as the name of an environment variable,
649/// is considered an automatic environment variable.
650///
651/// An automatic environment variable cannot be assigned to by user code.
652/// Current there are three of them: $env.PWD, $env.FILE_PWD, $env.CURRENT_FILE
653pub(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}