nu_protocol/
eval_base.rs

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