nu_engine/
eval.rs

1use crate::eval_ir_block;
2#[allow(deprecated)]
3use crate::get_full_help;
4use nu_path::{AbsolutePathBuf, expand_path_with};
5use nu_protocol::{
6    BlockId, Config, DataSource, ENV_VARIABLE_ID, IntoPipelineData, PipelineData, PipelineMetadata,
7    ShellError, Span, Value, VarId,
8    ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember},
9    debugger::DebugContext,
10    engine::{Closure, EngineState, Stack},
11    eval_base::Eval,
12};
13use nu_utils::IgnoreCaseExt;
14use std::sync::Arc;
15
16pub fn eval_call<D: DebugContext>(
17    engine_state: &EngineState,
18    caller_stack: &mut Stack,
19    call: &Call,
20    input: PipelineData,
21) -> Result<PipelineData, ShellError> {
22    engine_state.signals().check(call.head)?;
23    let decl = engine_state.get_decl(call.decl_id);
24
25    if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
26        let help = get_full_help(decl, engine_state, caller_stack);
27        Ok(Value::string(help, call.head).into_pipeline_data())
28    } else if let Some(block_id) = decl.block_id() {
29        let block = engine_state.get_block(block_id);
30
31        let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
32
33        // Rust does not check recursion limits outside of const evaluation.
34        // But nu programs run in the same process as the shell.
35        // To prevent a stack overflow in user code from crashing the shell,
36        // we limit the recursion depth of function calls.
37        // Picked 50 arbitrarily, should work on all architectures.
38        let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
39        callee_stack.recursion_count += 1;
40        if callee_stack.recursion_count > maximum_call_stack_depth {
41            callee_stack.recursion_count = 0;
42            return Err(ShellError::RecursionLimitReached {
43                recursion_limit: maximum_call_stack_depth,
44                span: block.span,
45            });
46        }
47
48        for (param_idx, (param, required)) in decl
49            .signature()
50            .required_positional
51            .iter()
52            .map(|p| (p, true))
53            .chain(
54                decl.signature()
55                    .optional_positional
56                    .iter()
57                    .map(|p| (p, false)),
58            )
59            .enumerate()
60        {
61            let var_id = param
62                .var_id
63                .expect("internal error: all custom parameters must have var_ids");
64
65            if let Some(arg) = call.positional_nth(param_idx) {
66                let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
67                let param_type = param.shape.to_type();
68                if required && !result.is_subtype_of(&param_type) {
69                    return Err(ShellError::CantConvert {
70                        to_type: param.shape.to_type().to_string(),
71                        from_type: result.get_type().to_string(),
72                        span: result.span(),
73                        help: None,
74                    });
75                }
76                callee_stack.add_var(var_id, result);
77            } else if let Some(value) = &param.default_value {
78                callee_stack.add_var(var_id, value.to_owned());
79            } else {
80                callee_stack.add_var(var_id, Value::nothing(call.head));
81            }
82        }
83
84        if let Some(rest_positional) = decl.signature().rest_positional {
85            let mut rest_items = vec![];
86
87            for result in call.rest_iter_flattened(
88                decl.signature().required_positional.len()
89                    + decl.signature().optional_positional.len(),
90                |expr| eval_expression::<D>(engine_state, caller_stack, expr),
91            )? {
92                rest_items.push(result);
93            }
94
95            let span = if let Some(rest_item) = rest_items.first() {
96                rest_item.span()
97            } else {
98                call.head
99            };
100
101            callee_stack.add_var(
102                rest_positional
103                    .var_id
104                    .expect("Internal error: rest positional parameter lacks var_id"),
105                Value::list(rest_items, span),
106            )
107        }
108
109        for named in decl.signature().named {
110            if let Some(var_id) = named.var_id {
111                let mut found = false;
112                for call_named in call.named_iter() {
113                    if let (Some(spanned), Some(short)) = (&call_named.1, named.short) {
114                        if spanned.item == short.to_string() {
115                            if let Some(arg) = &call_named.2 {
116                                let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
117
118                                callee_stack.add_var(var_id, result);
119                            } else if let Some(value) = &named.default_value {
120                                callee_stack.add_var(var_id, value.to_owned());
121                            } else {
122                                callee_stack.add_var(var_id, Value::bool(true, call.head))
123                            }
124                            found = true;
125                        }
126                    } else if call_named.0.item == named.long {
127                        if let Some(arg) = &call_named.2 {
128                            let result = eval_expression::<D>(engine_state, caller_stack, arg)?;
129
130                            callee_stack.add_var(var_id, result);
131                        } else if let Some(value) = &named.default_value {
132                            callee_stack.add_var(var_id, value.to_owned());
133                        } else {
134                            callee_stack.add_var(var_id, Value::bool(true, call.head))
135                        }
136                        found = true;
137                    }
138                }
139
140                if !found {
141                    if named.arg.is_none() {
142                        callee_stack.add_var(var_id, Value::bool(false, call.head))
143                    } else if let Some(value) = named.default_value {
144                        callee_stack.add_var(var_id, value);
145                    } else {
146                        callee_stack.add_var(var_id, Value::nothing(call.head))
147                    }
148                }
149            }
150        }
151
152        let result =
153            eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input);
154
155        if block.redirect_env {
156            redirect_env(engine_state, caller_stack, &callee_stack);
157        }
158
159        result
160    } else {
161        // 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                input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
280            }
281        },
282
283        _ => {
284            input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
285        }
286    };
287
288    Ok(input)
289}
290
291pub fn eval_block_with_early_return<D: DebugContext>(
292    engine_state: &EngineState,
293    stack: &mut Stack,
294    block: &Block,
295    input: PipelineData,
296) -> Result<PipelineData, ShellError> {
297    match eval_block::<D>(engine_state, stack, block, input) {
298        Err(ShellError::Return { span: _, value }) => Ok(PipelineData::Value(*value, None)),
299        x => x,
300    }
301}
302
303pub fn eval_block<D: DebugContext>(
304    engine_state: &EngineState,
305    stack: &mut Stack,
306    block: &Block,
307    input: PipelineData,
308) -> Result<PipelineData, ShellError> {
309    let result = eval_ir_block::<D>(engine_state, stack, block, input);
310    if let Err(err) = &result {
311        stack.set_last_error(err);
312    }
313    result
314}
315
316pub fn eval_collect<D: DebugContext>(
317    engine_state: &EngineState,
318    stack: &mut Stack,
319    var_id: VarId,
320    expr: &Expression,
321    input: PipelineData,
322) -> Result<PipelineData, ShellError> {
323    // Evaluate the expression with the variable set to the collected input
324    let span = input.span().unwrap_or(Span::unknown());
325
326    let metadata = match input.metadata() {
327        // Remove the `FilePath` metadata, because after `collect` it's no longer necessary to
328        // check where some input came from.
329        Some(PipelineMetadata {
330            data_source: DataSource::FilePath(_),
331            content_type: None,
332        }) => None,
333        other => other,
334    };
335
336    let input = input.into_value(span)?;
337
338    stack.add_var(var_id, input.clone());
339
340    let result = eval_expression_with_input::<D>(
341        engine_state,
342        stack,
343        expr,
344        // We still have to pass it as input
345        input.into_pipeline_data_with_metadata(metadata),
346    );
347
348    stack.remove_var(var_id);
349
350    result
351}
352
353pub fn eval_subexpression<D: DebugContext>(
354    engine_state: &EngineState,
355    stack: &mut Stack,
356    block: &Block,
357    input: PipelineData,
358) -> Result<PipelineData, ShellError> {
359    eval_block::<D>(engine_state, stack, block, input)
360}
361
362pub fn eval_variable(
363    engine_state: &EngineState,
364    stack: &Stack,
365    var_id: VarId,
366    span: Span,
367) -> Result<Value, ShellError> {
368    match var_id {
369        // $nu
370        nu_protocol::NU_VARIABLE_ID => {
371            if let Some(val) = engine_state.get_constant(var_id) {
372                Ok(val.clone())
373            } else {
374                Err(ShellError::VariableNotFoundAtRuntime { span })
375            }
376        }
377        // $env
378        ENV_VARIABLE_ID => {
379            let env_vars = stack.get_env_vars(engine_state);
380            let env_columns = env_vars.keys();
381            let env_values = env_vars.values();
382
383            let mut pairs = env_columns
384                .map(|x| x.to_string())
385                .zip(env_values.cloned())
386                .collect::<Vec<(String, Value)>>();
387
388            pairs.sort_by(|a, b| a.0.cmp(&b.0));
389
390            Ok(Value::record(pairs.into_iter().collect(), span))
391        }
392        var_id => stack.get_var(var_id, span),
393    }
394}
395
396struct EvalRuntime;
397
398impl Eval for EvalRuntime {
399    type State<'a> = &'a EngineState;
400
401    type MutState = Stack;
402
403    fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
404        stack.get_config(engine_state)
405    }
406
407    fn eval_filepath(
408        engine_state: &EngineState,
409        stack: &mut Stack,
410        path: String,
411        quoted: bool,
412        span: Span,
413    ) -> Result<Value, ShellError> {
414        if quoted {
415            Ok(Value::string(path, span))
416        } else {
417            let cwd = engine_state.cwd(Some(stack))?;
418            let path = expand_path_with(path, cwd, true);
419
420            Ok(Value::string(path.to_string_lossy(), span))
421        }
422    }
423
424    fn eval_directory(
425        engine_state: Self::State<'_>,
426        stack: &mut Self::MutState,
427        path: String,
428        quoted: bool,
429        span: Span,
430    ) -> Result<Value, ShellError> {
431        if path == "-" {
432            Ok(Value::string("-", span))
433        } else if quoted {
434            Ok(Value::string(path, span))
435        } else {
436            let cwd = engine_state
437                .cwd(Some(stack))
438                .map(AbsolutePathBuf::into_std_path_buf)
439                .unwrap_or_default();
440            let path = expand_path_with(path, cwd, true);
441
442            Ok(Value::string(path.to_string_lossy(), span))
443        }
444    }
445
446    fn eval_var(
447        engine_state: &EngineState,
448        stack: &mut Stack,
449        var_id: VarId,
450        span: Span,
451    ) -> Result<Value, ShellError> {
452        eval_variable(engine_state, stack, var_id, span)
453    }
454
455    fn eval_call<D: DebugContext>(
456        engine_state: &EngineState,
457        stack: &mut Stack,
458        call: &Call,
459        _: Span,
460    ) -> Result<Value, ShellError> {
461        // FIXME: protect this collect with ctrl-c
462        eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
463    }
464
465    fn eval_external_call(
466        engine_state: &EngineState,
467        stack: &mut Stack,
468        head: &Expression,
469        args: &[ExternalArgument],
470        _: Span,
471    ) -> Result<Value, ShellError> {
472        let span = head.span(&engine_state);
473        // FIXME: protect this collect with ctrl-c
474        eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
475    }
476
477    fn eval_collect<D: DebugContext>(
478        engine_state: &EngineState,
479        stack: &mut Stack,
480        var_id: VarId,
481        expr: &Expression,
482    ) -> Result<Value, ShellError> {
483        // It's a little bizarre, but the expression can still have some kind of result even with
484        // nothing input
485        eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
486            .into_value(expr.span)
487    }
488
489    fn eval_subexpression<D: DebugContext>(
490        engine_state: &EngineState,
491        stack: &mut Stack,
492        block_id: BlockId,
493        span: Span,
494    ) -> Result<Value, ShellError> {
495        let block = engine_state.get_block(block_id);
496        // FIXME: protect this collect with ctrl-c
497        eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
498    }
499
500    fn regex_match(
501        engine_state: &EngineState,
502        op_span: Span,
503        lhs: &Value,
504        rhs: &Value,
505        invert: bool,
506        expr_span: Span,
507    ) -> Result<Value, ShellError> {
508        lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
509    }
510
511    fn eval_assignment<D: DebugContext>(
512        engine_state: &EngineState,
513        stack: &mut Stack,
514        lhs: &Expression,
515        rhs: &Expression,
516        assignment: Assignment,
517        op_span: Span,
518        _expr_span: Span,
519    ) -> Result<Value, ShellError> {
520        let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
521
522        let rhs = match assignment {
523            Assignment::Assign => rhs,
524            Assignment::AddAssign => {
525                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
526                lhs.add(op_span, &rhs, op_span)?
527            }
528            Assignment::SubtractAssign => {
529                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
530                lhs.sub(op_span, &rhs, op_span)?
531            }
532            Assignment::MultiplyAssign => {
533                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
534                lhs.mul(op_span, &rhs, op_span)?
535            }
536            Assignment::DivideAssign => {
537                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
538                lhs.div(op_span, &rhs, op_span)?
539            }
540            Assignment::ConcatenateAssign => {
541                let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
542                lhs.concat(op_span, &rhs, op_span)?
543            }
544        };
545
546        match &lhs.expr {
547            Expr::Var(var_id) | Expr::VarDecl(var_id) => {
548                let var_info = engine_state.get_var(*var_id);
549                if var_info.mutable {
550                    stack.add_var(*var_id, rhs);
551                    Ok(Value::nothing(lhs.span(&engine_state)))
552                } else {
553                    Err(ShellError::AssignmentRequiresMutableVar {
554                        lhs_span: lhs.span(&engine_state),
555                    })
556                }
557            }
558            Expr::FullCellPath(cell_path) => {
559                match &cell_path.head.expr {
560                    Expr::Var(var_id) | Expr::VarDecl(var_id) => {
561                        // The $env variable is considered "mutable" in Nushell.
562                        // As such, give it special treatment here.
563                        let is_env = var_id == &ENV_VARIABLE_ID;
564                        if is_env || engine_state.get_var(*var_id).mutable {
565                            let mut lhs =
566                                eval_expression::<D>(engine_state, stack, &cell_path.head)?;
567                            if is_env {
568                                // Reject attempts to assign to the entire $env
569                                if cell_path.tail.is_empty() {
570                                    return Err(ShellError::CannotReplaceEnv {
571                                        span: cell_path.head.span(&engine_state),
572                                    });
573                                }
574
575                                // Updating environment variables should be case-preserving,
576                                // so we need to figure out the original key before we do anything.
577                                let (key, span) = match &cell_path.tail[0] {
578                                    PathMember::String { val, span, .. } => (val.to_string(), span),
579                                    PathMember::Int { val, span, .. } => (val.to_string(), span),
580                                };
581                                let original_key = if let Value::Record { val: record, .. } = &lhs {
582                                    record
583                                        .iter()
584                                        .rev()
585                                        .map(|(k, _)| k)
586                                        .find(|x| x.eq_ignore_case(&key))
587                                        .cloned()
588                                        .unwrap_or(key)
589                                } else {
590                                    key
591                                };
592
593                                // Retrieve the updated environment value.
594                                lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
595                                let value = lhs.follow_cell_path(&[{
596                                    let mut pm = cell_path.tail[0].clone();
597                                    pm.make_insensitive();
598                                    pm
599                                }])?;
600
601                                // Reject attempts to set automatic environment variables.
602                                if is_automatic_env_var(&original_key) {
603                                    return Err(ShellError::AutomaticEnvVarSetManually {
604                                        envvar_name: original_key,
605                                        span: *span,
606                                    });
607                                }
608
609                                let is_config = original_key == "config";
610
611                                stack.add_env_var(original_key, value.into_owned());
612
613                                // Trigger the update to config, if we modified that.
614                                if is_config {
615                                    stack.update_config(engine_state)?;
616                                }
617                            } else {
618                                lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
619                                stack.add_var(*var_id, lhs);
620                            }
621                            Ok(Value::nothing(cell_path.head.span(&engine_state)))
622                        } else {
623                            Err(ShellError::AssignmentRequiresMutableVar {
624                                lhs_span: lhs.span(&engine_state),
625                            })
626                        }
627                    }
628                    _ => Err(ShellError::AssignmentRequiresVar {
629                        lhs_span: lhs.span(&engine_state),
630                    }),
631                }
632            }
633            _ => Err(ShellError::AssignmentRequiresVar {
634                lhs_span: lhs.span(&engine_state),
635            }),
636        }
637    }
638
639    fn eval_row_condition_or_closure(
640        engine_state: &EngineState,
641        stack: &mut Stack,
642        block_id: BlockId,
643        span: Span,
644    ) -> Result<Value, ShellError> {
645        let captures = engine_state
646            .get_block(block_id)
647            .captures
648            .iter()
649            .map(|(id, span)| {
650                stack
651                    .get_var(*id, *span)
652                    .or_else(|_| {
653                        engine_state
654                            .get_var(*id)
655                            .const_val
656                            .clone()
657                            .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
658                    })
659                    .map(|var| (*id, var))
660            })
661            .collect::<Result<_, _>>()?;
662
663        Ok(Value::closure(Closure { block_id, captures }, span))
664    }
665
666    fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
667        let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
668
669        Ok(Value::string(name, span))
670    }
671
672    fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
673        Ok(Value::nothing(expr.span(&engine_state)))
674    }
675}
676
677/// Returns whether a string, when used as the name of an environment variable,
678/// is considered an automatic environment variable.
679///
680/// An automatic environment variable cannot be assigned to by user code.
681/// Current there are three of them: $env.PWD, $env.FILE_PWD, $env.CURRENT_FILE
682pub(crate) fn is_automatic_env_var(var: &str) -> bool {
683    let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
684    names.iter().any(|&name| {
685        if cfg!(windows) {
686            name.eq_ignore_case(var)
687        } else {
688            name.eq(var)
689        }
690    })
691}