nu_protocol/
eval_base.rs

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