nu_protocol/
eval_base.rs

1//! Foundational [`Eval`] trait allowing dispatch between const-eval and regular evaluation
2use nu_path::expand_path;
3
4use crate::{
5    BlockId, Config, ENV_VARIABLE_ID, GetSpan, Range, Record, ShellError, Span, Value, VarId,
6    ast::{
7        Assignment, Bits, Boolean, Call, Comparison, Expr, Expression, ExternalArgument, ListItem,
8        Math, Operator, RecordItem, eval_operator,
9    },
10    debugger::DebugContext,
11};
12use std::{borrow::Cow, collections::HashMap, sync::Arc};
13
14/// To share implementations for regular eval and const eval
15pub trait Eval {
16    /// State that doesn't need to be mutated.
17    /// EngineState for regular eval and StateWorkingSet for const eval
18    type State<'a>: Copy + GetSpan;
19
20    /// State that needs to be mutated.
21    /// This is the stack for regular eval, and unused by const eval
22    type MutState;
23
24    fn eval<D: DebugContext>(
25        state: Self::State<'_>,
26        mut_state: &mut Self::MutState,
27        expr: &Expression,
28    ) -> Result<Value, ShellError> {
29        let expr_span = expr.span(&state);
30
31        match &expr.expr {
32            Expr::AttributeBlock(ab) => Self::eval::<D>(state, mut_state, &ab.item),
33            Expr::Bool(b) => Ok(Value::bool(*b, expr_span)),
34            Expr::Int(i) => Ok(Value::int(*i, expr_span)),
35            Expr::Float(f) => Ok(Value::float(*f, expr_span)),
36            Expr::Binary(b) => Ok(Value::binary(b.clone(), expr_span)),
37            Expr::Filepath(path, quoted) => {
38                if *quoted {
39                    Ok(Value::string(path, expr_span))
40                } else {
41                    let path = expand_path(path, true);
42                    Ok(Value::string(path.to_string_lossy(), expr_span))
43                }
44            }
45            Expr::Directory(path, quoted) => {
46                if path == "-" {
47                    Ok(Value::string("-", expr_span))
48                } else if *quoted {
49                    Ok(Value::string(path, expr_span))
50                } else {
51                    let path = expand_path(path, true);
52                    Ok(Value::string(path.to_string_lossy(), expr_span))
53                }
54            }
55            Expr::Var(var_id) => Self::eval_var(state, mut_state, *var_id, expr_span),
56            Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr_span)),
57            Expr::FullCellPath(cell_path) => {
58                let value = Self::eval::<D>(state, mut_state, &cell_path.head)?;
59
60                // Cell paths are usually case-sensitive, but we give $env
61                // special treatment.
62                let tail = if cell_path.head.expr == Expr::Var(ENV_VARIABLE_ID) {
63                    let mut tail = cell_path.tail.clone();
64                    if let Some(pm) = tail.first_mut() {
65                        pm.make_insensitive();
66                    }
67                    Cow::Owned(tail)
68                } else {
69                    Cow::Borrowed(&cell_path.tail)
70                };
71                value.follow_cell_path(&tail).map(Cow::into_owned)
72            }
73            Expr::DateTime(dt) => Ok(Value::date(*dt, expr_span)),
74            Expr::List(list) => {
75                let mut output = vec![];
76                for item in list {
77                    match item {
78                        ListItem::Item(expr) => output.push(Self::eval::<D>(state, mut_state, expr)?),
79                        ListItem::Spread(_, expr) => match Self::eval::<D>(state, mut_state, expr)? {
80                            Value::List { vals, .. } => output.extend(vals),
81                            Value::Nothing { .. } => (),
82                            _ => return Err(ShellError::CannotSpreadAsList { span: expr_span }),
83                        },
84                    }
85                }
86                Ok(Value::list(output, expr_span))
87            }
88            Expr::Record(items) => {
89                let mut record = Record::new();
90                let mut col_names = HashMap::new();
91                for item in items {
92                    match item {
93                        RecordItem::Pair(col, val) => {
94                            // avoid duplicate cols
95                            let col_name = Self::eval::<D>(state, mut_state, col)?.coerce_into_string()?;
96                            let col_span = col.span(&state);
97                            if let Some(orig_span) = col_names.get(&col_name) {
98                                return Err(ShellError::ColumnDefinedTwice {
99                                    col_name,
100                                    second_use: col_span,
101                                    first_use: *orig_span,
102                                });
103                            } else {
104                                col_names.insert(col_name.clone(), col_span);
105                                record.push(col_name, Self::eval::<D>(state, mut_state, val)?);
106                            }
107                        }
108                        RecordItem::Spread(_, inner) => {
109                            let inner_span = inner.span(&state);
110                            match Self::eval::<D>(state, mut_state, inner)? {
111                                Value::Record { val: inner_val, .. } => {
112                                    for (col_name, val) in inner_val.into_owned() {
113                                        if let Some(orig_span) = col_names.get(&col_name) {
114                                            return Err(ShellError::ColumnDefinedTwice {
115                                                col_name,
116                                                second_use: inner_span,
117                                                first_use: *orig_span,
118                                            });
119                                        } else {
120                                            col_names.insert(col_name.clone(), inner_span);
121                                            record.push(col_name, val);
122                                        }
123                                    }
124                                }
125                                _ => {
126                                    return Err(ShellError::CannotSpreadAsRecord {
127                                        span: inner_span,
128                                    })
129                                }
130                            }
131                        }
132                    }
133                }
134
135                Ok(Value::record(record, expr_span))
136            }
137            Expr::Table(table) => {
138                let mut output_headers = vec![];
139                for expr in table.columns.as_ref() {
140                    let header = Self::eval::<D>(state, mut_state, expr)?.coerce_into_string()?;
141                    if let Some(idx) = output_headers
142                        .iter()
143                        .position(|existing| existing == &header)
144                    {
145                        let first_use = table.columns[idx].span(&state);
146                        return Err(ShellError::ColumnDefinedTwice {
147                            col_name: header,
148                            second_use: expr_span,
149                            first_use,
150                        });
151                    } else {
152                        output_headers.push(header);
153                    }
154                }
155
156                let mut output_rows = vec![];
157                for val in table.rows.as_ref() {
158                    let record = output_headers.iter().zip(val.as_ref()).map(|(col, expr)| {
159                        Self::eval::<D>(state, mut_state, expr).map(|val| (col.clone(), val))
160                    }).collect::<Result<_,_>>()?;
161
162                    output_rows.push(Value::record(
163                        record,
164                        expr_span,
165                    ));
166                }
167                Ok(Value::list(output_rows, expr_span))
168            }
169            Expr::Keyword(kw) => Self::eval::<D>(state, mut_state, &kw.expr),
170            Expr::String(s) | Expr::RawString(s) => Ok(Value::string(s.clone(), expr_span)),
171            Expr::Nothing => Ok(Value::nothing(expr_span)),
172            Expr::ValueWithUnit(value) => match Self::eval::<D>(state, mut_state, &value.expr)? {
173                Value::Int { val, .. } => value.unit.item.build_value(val, value.unit.span),
174                x => Err(ShellError::CantConvert {
175                    to_type: "unit value".into(),
176                    from_type: x.get_type().to_string(),
177                    span: value.expr.span(&state),
178                    help: None,
179                }),
180            },
181            Expr::Call(call) => Self::eval_call::<D>(state, mut_state, call, expr_span),
182            Expr::ExternalCall(head, args) => {
183                Self::eval_external_call(state, mut_state, head, args, expr_span)
184            }
185            Expr::Collect(var_id, expr) => {
186                Self::eval_collect::<D>(state, mut_state, *var_id, expr)
187            }
188            Expr::Subexpression(block_id) => {
189                Self::eval_subexpression::<D>(state, mut_state, *block_id, expr_span)
190            }
191            Expr::Range(range) => {
192                let from = if let Some(f) = &range.from {
193                    Self::eval::<D>(state, mut_state, f)?
194                } else {
195                    Value::nothing(expr_span)
196                };
197
198                let next = if let Some(s) = &range.next {
199                    Self::eval::<D>(state, mut_state, s)?
200                } else {
201                    Value::nothing(expr_span)
202                };
203
204                let to = if let Some(t) = &range.to {
205                    Self::eval::<D>(state, mut_state, t)?
206                } else {
207                    Value::nothing(expr_span)
208                };
209
210                Ok(Value::range(
211                    Range::new(from, next, to, range.operator.inclusion, expr_span)?,
212                    expr_span,
213                ))
214            }
215            Expr::UnaryNot(expr) => {
216                let lhs = Self::eval::<D>(state, mut_state, expr)?;
217                match lhs {
218                    Value::Bool { val, .. } => Ok(Value::bool(!val, expr_span)),
219                    other => Err(ShellError::TypeMismatch {
220                        err_message: format!("expected bool, found {}", other.get_type()),
221                        span: expr_span,
222                    }),
223                }
224            }
225            Expr::BinaryOp(lhs, op, rhs) => {
226                let op_span = op.span(&state);
227                let op = eval_operator(op)?;
228
229                match op {
230                    Operator::Boolean(boolean) => {
231                        let lhs = Self::eval::<D>(state, mut_state, lhs)?;
232                        match boolean {
233                            Boolean::And => {
234                                if lhs.is_false() {
235                                    Ok(Value::bool(false, expr_span))
236                                } else {
237                                    let rhs = Self::eval::<D>(state, mut_state, rhs)?;
238                                    lhs.and(op_span, &rhs, expr_span)
239                                }
240                            }
241                            Boolean::Or => {
242                                if lhs.is_true() {
243                                    Ok(Value::bool(true, expr_span))
244                                } else {
245                                    let rhs = Self::eval::<D>(state, mut_state, rhs)?;
246                                    lhs.or(op_span, &rhs, expr_span)
247                                }
248                            }
249                            Boolean::Xor => {
250                                let rhs = Self::eval::<D>(state, mut_state, rhs)?;
251                                lhs.xor(op_span, &rhs, expr_span)
252                            }
253                        }
254                    }
255                    Operator::Math(math) => {
256                        let lhs = Self::eval::<D>(state, mut_state, lhs)?;
257                        let rhs = Self::eval::<D>(state, mut_state, rhs)?;
258
259                        match math {
260                            Math::Add => lhs.add(op_span, &rhs, expr_span),
261                            Math::Subtract => lhs.sub(op_span, &rhs, expr_span),
262                            Math::Multiply => lhs.mul(op_span, &rhs, expr_span),
263                            Math::Divide => lhs.div(op_span, &rhs, expr_span),
264                            Math::FloorDivide => lhs.floor_div(op_span, &rhs, expr_span),
265                            Math::Modulo => lhs.modulo(op_span, &rhs, expr_span),
266                            Math::Pow => lhs.pow(op_span, &rhs, expr_span),
267                            Math::Concatenate => lhs.concat(op_span, &rhs, expr_span),
268                        }
269                    }
270                    Operator::Comparison(comparison) => {
271                        let lhs = Self::eval::<D>(state, mut_state, lhs)?;
272                        let rhs = Self::eval::<D>(state, mut_state, rhs)?;
273                        match comparison {
274                            Comparison::LessThan => lhs.lt(op_span, &rhs, expr_span),
275                            Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr_span),
276                            Comparison::GreaterThan => lhs.gt(op_span, &rhs, expr_span),
277                            Comparison::GreaterThanOrEqual => lhs.gte(op_span, &rhs, expr_span),
278                            Comparison::Equal => lhs.eq(op_span, &rhs, expr_span),
279                            Comparison::NotEqual => lhs.ne(op_span, &rhs, expr_span),
280                            Comparison::In => lhs.r#in(op_span, &rhs, expr_span),
281                            Comparison::NotIn => lhs.not_in(op_span, &rhs, expr_span),
282                            Comparison::Has => lhs.has(op_span, &rhs, expr_span),
283                            Comparison::NotHas => lhs.not_has(op_span, &rhs, expr_span),
284                            Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr_span),
285                            Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr_span),
286                            Comparison::RegexMatch => {
287                                Self::regex_match(state, op_span, &lhs, &rhs, false, expr_span)
288                            }
289                            Comparison::NotRegexMatch => {
290                                Self::regex_match(state, op_span, &lhs, &rhs, true, expr_span)
291                            }
292                        }
293                    }
294                    Operator::Bits(bits) => {
295                        let lhs = Self::eval::<D>(state, mut_state, lhs)?;
296                        let rhs = Self::eval::<D>(state, mut_state, rhs)?;
297                        match bits {
298                            Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr_span),
299                            Bits::BitOr => lhs.bit_or(op_span, &rhs, expr_span),
300                            Bits::BitXor => lhs.bit_xor(op_span, &rhs, expr_span),
301                            Bits::ShiftLeft => lhs.bit_shl(op_span, &rhs, expr_span),
302                            Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr_span),
303                        }
304                    }
305                    Operator::Assignment(assignment) => Self::eval_assignment::<D>(
306                        state, mut_state, lhs, rhs, assignment, op_span, expr_span
307                    ),
308                }
309            }
310            Expr::RowCondition(block_id) | Expr::Closure(block_id) => {
311                Self::eval_row_condition_or_closure(state, mut_state, *block_id, expr_span)
312            }
313            Expr::StringInterpolation(exprs) => {
314                let config = Self::get_config(state, mut_state);
315                let str = exprs
316                    .iter()
317                    .map(|expr| Self::eval::<D>(state, mut_state, expr).map(|v| v.to_expanded_string(", ", &config)))
318                    .collect::<Result<String, _>>()?;
319
320                Ok(Value::string(str, expr_span))
321            }
322            Expr::GlobInterpolation(exprs, quoted) => {
323                let config = Self::get_config(state, mut_state);
324                let str = exprs
325                    .iter()
326                    .map(|expr| Self::eval::<D>(state, mut_state, expr).map(|v| v.to_expanded_string(", ", &config)))
327                    .collect::<Result<String, _>>()?;
328
329                Ok(Value::glob(str, *quoted, expr_span))
330            }
331            Expr::Overlay(_) => Self::eval_overlay(state, expr_span),
332            Expr::GlobPattern(pattern, quoted) => {
333                // GlobPattern is similar to Filepath
334                // But we don't want to expand path during eval time, it's required for `nu_engine::glob_from` to run correctly
335                Ok(Value::glob(pattern, *quoted, expr_span))
336            }
337            Expr::MatchBlock(_) // match blocks are handled by `match`
338            | Expr::Block(_) // blocks are handled directly by core commands
339            | Expr::VarDecl(_)
340            | Expr::ImportPattern(_)
341            | Expr::Signature(_)
342            | Expr::Operator(_)
343            | Expr::Garbage => Self::unreachable(state, expr),
344        }
345    }
346
347    fn get_config(state: Self::State<'_>, mut_state: &mut Self::MutState) -> Arc<Config>;
348
349    fn eval_var(
350        state: Self::State<'_>,
351        mut_state: &mut Self::MutState,
352        var_id: VarId,
353        span: Span,
354    ) -> Result<Value, ShellError>;
355
356    fn eval_call<D: DebugContext>(
357        state: Self::State<'_>,
358        mut_state: &mut Self::MutState,
359        call: &Call,
360        span: Span,
361    ) -> Result<Value, ShellError>;
362
363    fn eval_external_call(
364        state: Self::State<'_>,
365        mut_state: &mut Self::MutState,
366        head: &Expression,
367        args: &[ExternalArgument],
368        span: Span,
369    ) -> Result<Value, ShellError>;
370
371    fn eval_collect<D: DebugContext>(
372        state: Self::State<'_>,
373        mut_state: &mut Self::MutState,
374        var_id: VarId,
375        expr: &Expression,
376    ) -> Result<Value, ShellError>;
377
378    fn eval_subexpression<D: DebugContext>(
379        state: Self::State<'_>,
380        mut_state: &mut Self::MutState,
381        block_id: BlockId,
382        span: Span,
383    ) -> Result<Value, ShellError>;
384
385    fn regex_match(
386        state: Self::State<'_>,
387        op_span: Span,
388        lhs: &Value,
389        rhs: &Value,
390        invert: bool,
391        expr_span: Span,
392    ) -> Result<Value, ShellError>;
393
394    #[allow(clippy::too_many_arguments)]
395    fn eval_assignment<D: DebugContext>(
396        state: Self::State<'_>,
397        mut_state: &mut Self::MutState,
398        lhs: &Expression,
399        rhs: &Expression,
400        assignment: Assignment,
401        op_span: Span,
402        expr_span: Span,
403    ) -> Result<Value, ShellError>;
404
405    fn eval_row_condition_or_closure(
406        state: Self::State<'_>,
407        mut_state: &mut Self::MutState,
408        block_id: BlockId,
409        span: Span,
410    ) -> Result<Value, ShellError>;
411
412    fn eval_overlay(state: Self::State<'_>, span: Span) -> Result<Value, ShellError>;
413
414    /// For expressions that should never actually be evaluated
415    fn unreachable(state: Self::State<'_>, expr: &Expression) -> Result<Value, ShellError>;
416}