nu_protocol/ast/
expression.rs

1use crate::{
2    ast::{Argument, Block, Expr, ExternalArgument, ImportPattern, MatchPattern, RecordItem},
3    engine::StateWorkingSet,
4    BlockId, DeclId, GetSpan, Signature, Span, SpanId, Type, VarId, IN_VARIABLE_ID,
5};
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8
9use super::ListItem;
10
11/// Wrapper around [`Expr`]
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct Expression {
14    pub expr: Expr,
15    pub span: Span,
16    pub span_id: SpanId,
17    pub ty: Type,
18    pub custom_completion: Option<DeclId>,
19}
20
21impl Expression {
22    pub fn garbage(working_set: &mut StateWorkingSet, span: Span) -> Expression {
23        let span_id = working_set.add_span(span);
24        Expression {
25            expr: Expr::Garbage,
26            span,
27            span_id,
28            ty: Type::Any,
29            custom_completion: None,
30        }
31    }
32
33    pub fn precedence(&self) -> u8 {
34        match &self.expr {
35            Expr::Operator(operator) => operator.precedence(),
36            _ => 0,
37        }
38    }
39
40    pub fn as_block(&self) -> Option<BlockId> {
41        match self.expr {
42            Expr::Block(block_id) => Some(block_id),
43            Expr::Closure(block_id) => Some(block_id),
44            _ => None,
45        }
46    }
47
48    pub fn as_row_condition_block(&self) -> Option<BlockId> {
49        match self.expr {
50            Expr::RowCondition(block_id) => Some(block_id),
51            _ => None,
52        }
53    }
54
55    pub fn as_match_block(&self) -> Option<&[(MatchPattern, Expression)]> {
56        match &self.expr {
57            Expr::MatchBlock(matches) => Some(matches),
58            _ => None,
59        }
60    }
61
62    pub fn as_signature(&self) -> Option<Box<Signature>> {
63        match &self.expr {
64            Expr::Signature(sig) => Some(sig.clone()),
65            _ => None,
66        }
67    }
68
69    pub fn as_keyword(&self) -> Option<&Expression> {
70        match &self.expr {
71            Expr::Keyword(kw) => Some(&kw.expr),
72            _ => None,
73        }
74    }
75
76    pub fn as_var(&self) -> Option<VarId> {
77        match self.expr {
78            Expr::Var(var_id) => Some(var_id),
79            Expr::VarDecl(var_id) => Some(var_id),
80            _ => None,
81        }
82    }
83
84    pub fn as_string(&self) -> Option<String> {
85        match &self.expr {
86            Expr::String(string) => Some(string.clone()),
87            _ => None,
88        }
89    }
90
91    pub fn as_filepath(&self) -> Option<(String, bool)> {
92        match &self.expr {
93            Expr::Filepath(string, quoted) => Some((string.clone(), *quoted)),
94            _ => None,
95        }
96    }
97
98    pub fn as_import_pattern(&self) -> Option<ImportPattern> {
99        match &self.expr {
100            Expr::ImportPattern(pattern) => Some(*pattern.clone()),
101            _ => None,
102        }
103    }
104
105    pub fn has_in_variable(&self, working_set: &StateWorkingSet) -> bool {
106        match &self.expr {
107            Expr::AttributeBlock(ab) => ab.item.has_in_variable(working_set),
108            Expr::BinaryOp(left, _, right) => {
109                left.has_in_variable(working_set) || right.has_in_variable(working_set)
110            }
111            Expr::UnaryNot(expr) => expr.has_in_variable(working_set),
112            Expr::Block(block_id) | Expr::Closure(block_id) => {
113                let block = working_set.get_block(*block_id);
114                block.captures.contains(&IN_VARIABLE_ID)
115                    || block
116                        .pipelines
117                        .iter()
118                        .flat_map(|pipeline| pipeline.elements.first())
119                        .any(|element| element.has_in_variable(working_set))
120            }
121            Expr::Binary(_) => false,
122            Expr::Bool(_) => false,
123            Expr::Call(call) => {
124                for arg in &call.arguments {
125                    match arg {
126                        Argument::Positional(expr)
127                        | Argument::Unknown(expr)
128                        | Argument::Spread(expr) => {
129                            if expr.has_in_variable(working_set) {
130                                return true;
131                            }
132                        }
133                        Argument::Named(named) => {
134                            if let Some(expr) = &named.2 {
135                                if expr.has_in_variable(working_set) {
136                                    return true;
137                                }
138                            }
139                        }
140                    }
141                }
142                false
143            }
144            Expr::CellPath(_) => false,
145            Expr::DateTime(_) => false,
146            Expr::ExternalCall(head, args) => {
147                if head.has_in_variable(working_set) {
148                    return true;
149                }
150                for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in
151                    args.as_ref()
152                {
153                    if expr.has_in_variable(working_set) {
154                        return true;
155                    }
156                }
157                false
158            }
159            Expr::ImportPattern(_) => false,
160            Expr::Overlay(_) => false,
161            Expr::Filepath(_, _) => false,
162            Expr::Directory(_, _) => false,
163            Expr::Float(_) => false,
164            Expr::FullCellPath(full_cell_path) => {
165                if full_cell_path.head.has_in_variable(working_set) {
166                    return true;
167                }
168                false
169            }
170            Expr::Garbage => false,
171            Expr::Nothing => false,
172            Expr::GlobPattern(_, _) => false,
173            Expr::Int(_) => false,
174            Expr::Keyword(kw) => kw.expr.has_in_variable(working_set),
175            Expr::List(list) => {
176                for item in list {
177                    if item.expr().has_in_variable(working_set) {
178                        return true;
179                    }
180                }
181                false
182            }
183            Expr::StringInterpolation(items) | Expr::GlobInterpolation(items, _) => {
184                for i in items {
185                    if i.has_in_variable(working_set) {
186                        return true;
187                    }
188                }
189                false
190            }
191            Expr::Operator(_) => false,
192            Expr::MatchBlock(_) => false,
193            Expr::Range(range) => {
194                if let Some(left) = &range.from {
195                    if left.has_in_variable(working_set) {
196                        return true;
197                    }
198                }
199                if let Some(middle) = &range.next {
200                    if middle.has_in_variable(working_set) {
201                        return true;
202                    }
203                }
204                if let Some(right) = &range.to {
205                    if right.has_in_variable(working_set) {
206                        return true;
207                    }
208                }
209                false
210            }
211            Expr::Record(items) => {
212                for item in items {
213                    match item {
214                        RecordItem::Pair(field_name, field_value) => {
215                            if field_name.has_in_variable(working_set) {
216                                return true;
217                            }
218                            if field_value.has_in_variable(working_set) {
219                                return true;
220                            }
221                        }
222                        RecordItem::Spread(_, record) => {
223                            if record.has_in_variable(working_set) {
224                                return true;
225                            }
226                        }
227                    }
228                }
229                false
230            }
231            Expr::Signature(_) => false,
232            Expr::String(_) => false,
233            Expr::RawString(_) => false,
234            // A `$in` variable found within a `Collect` is local, as it's already been wrapped
235            // This is probably unlikely to happen anyway - the expressions are wrapped depth-first
236            Expr::Collect(_, _) => false,
237            Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
238                let block = working_set.get_block(*block_id);
239
240                if let Some(pipeline) = block.pipelines.first() {
241                    if let Some(expr) = pipeline.elements.first() {
242                        expr.has_in_variable(working_set)
243                    } else {
244                        false
245                    }
246                } else {
247                    false
248                }
249            }
250            Expr::Table(table) => {
251                for header in table.columns.as_ref() {
252                    if header.has_in_variable(working_set) {
253                        return true;
254                    }
255                }
256
257                for row in table.rows.as_ref() {
258                    for cell in row.iter() {
259                        if cell.has_in_variable(working_set) {
260                            return true;
261                        }
262                    }
263                }
264
265                false
266            }
267
268            Expr::ValueWithUnit(value) => value.expr.has_in_variable(working_set),
269            Expr::Var(var_id) => *var_id == IN_VARIABLE_ID,
270            Expr::VarDecl(_) => false,
271        }
272    }
273
274    pub fn replace_span(
275        &mut self,
276        working_set: &mut StateWorkingSet,
277        replaced: Span,
278        new_span: Span,
279    ) {
280        if replaced.contains_span(self.span) {
281            self.span = new_span;
282        }
283        match &mut self.expr {
284            Expr::AttributeBlock(ab) => ab.item.replace_span(working_set, replaced, new_span),
285            Expr::BinaryOp(left, _, right) => {
286                left.replace_span(working_set, replaced, new_span);
287                right.replace_span(working_set, replaced, new_span);
288            }
289            Expr::UnaryNot(expr) => {
290                expr.replace_span(working_set, replaced, new_span);
291            }
292            Expr::Block(block_id) => {
293                // We are cloning the Block itself, rather than the Arc around it.
294                let mut block = Block::clone(working_set.get_block(*block_id));
295
296                for pipeline in block.pipelines.iter_mut() {
297                    for element in pipeline.elements.iter_mut() {
298                        element.replace_span(working_set, replaced, new_span)
299                    }
300                }
301
302                *block_id = working_set.add_block(Arc::new(block));
303            }
304            Expr::Closure(block_id) => {
305                let mut block = (**working_set.get_block(*block_id)).clone();
306
307                for pipeline in block.pipelines.iter_mut() {
308                    for element in pipeline.elements.iter_mut() {
309                        element.replace_span(working_set, replaced, new_span)
310                    }
311                }
312
313                *block_id = working_set.add_block(Arc::new(block));
314            }
315            Expr::Binary(_) => {}
316            Expr::Bool(_) => {}
317            Expr::Call(call) => {
318                if replaced.contains_span(call.head) {
319                    call.head = new_span;
320                }
321                for arg in call.arguments.iter_mut() {
322                    match arg {
323                        Argument::Positional(expr)
324                        | Argument::Unknown(expr)
325                        | Argument::Spread(expr) => {
326                            expr.replace_span(working_set, replaced, new_span);
327                        }
328                        Argument::Named(named) => {
329                            if let Some(expr) = &mut named.2 {
330                                expr.replace_span(working_set, replaced, new_span);
331                            }
332                        }
333                    }
334                }
335            }
336            Expr::CellPath(_) => {}
337            Expr::DateTime(_) => {}
338            Expr::ExternalCall(head, args) => {
339                head.replace_span(working_set, replaced, new_span);
340                for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in
341                    args.as_mut()
342                {
343                    expr.replace_span(working_set, replaced, new_span);
344                }
345            }
346            Expr::Filepath(_, _) => {}
347            Expr::Directory(_, _) => {}
348            Expr::Float(_) => {}
349            Expr::FullCellPath(full_cell_path) => {
350                full_cell_path
351                    .head
352                    .replace_span(working_set, replaced, new_span);
353            }
354            Expr::ImportPattern(_) => {}
355            Expr::Overlay(_) => {}
356            Expr::Garbage => {}
357            Expr::Nothing => {}
358            Expr::GlobPattern(_, _) => {}
359            Expr::MatchBlock(_) => {}
360            Expr::Int(_) => {}
361            Expr::Keyword(kw) => kw.expr.replace_span(working_set, replaced, new_span),
362            Expr::List(list) => {
363                for item in list {
364                    item.expr_mut()
365                        .replace_span(working_set, replaced, new_span);
366                }
367            }
368            Expr::Operator(_) => {}
369            Expr::Range(range) => {
370                if let Some(left) = &mut range.from {
371                    left.replace_span(working_set, replaced, new_span)
372                }
373                if let Some(middle) = &mut range.next {
374                    middle.replace_span(working_set, replaced, new_span)
375                }
376                if let Some(right) = &mut range.to {
377                    right.replace_span(working_set, replaced, new_span)
378                }
379            }
380            Expr::Record(items) => {
381                for item in items {
382                    match item {
383                        RecordItem::Pair(field_name, field_value) => {
384                            field_name.replace_span(working_set, replaced, new_span);
385                            field_value.replace_span(working_set, replaced, new_span);
386                        }
387                        RecordItem::Spread(_, record) => {
388                            record.replace_span(working_set, replaced, new_span);
389                        }
390                    }
391                }
392            }
393            Expr::Signature(_) => {}
394            Expr::String(_) => {}
395            Expr::RawString(_) => {}
396            Expr::StringInterpolation(items) | Expr::GlobInterpolation(items, _) => {
397                for i in items {
398                    i.replace_span(working_set, replaced, new_span)
399                }
400            }
401            Expr::Collect(_, expr) => expr.replace_span(working_set, replaced, new_span),
402            Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
403                let mut block = (**working_set.get_block(*block_id)).clone();
404
405                for pipeline in block.pipelines.iter_mut() {
406                    for element in pipeline.elements.iter_mut() {
407                        element.replace_span(working_set, replaced, new_span)
408                    }
409                }
410
411                *block_id = working_set.add_block(Arc::new(block));
412            }
413            Expr::Table(table) => {
414                for header in table.columns.as_mut() {
415                    header.replace_span(working_set, replaced, new_span)
416                }
417
418                for row in table.rows.as_mut() {
419                    for cell in row.iter_mut() {
420                        cell.replace_span(working_set, replaced, new_span)
421                    }
422                }
423            }
424
425            Expr::ValueWithUnit(value) => value.expr.replace_span(working_set, replaced, new_span),
426            Expr::Var(_) => {}
427            Expr::VarDecl(_) => {}
428        }
429    }
430
431    pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
432        match &mut self.expr {
433            Expr::AttributeBlock(ab) => ab.item.replace_in_variable(working_set, new_var_id),
434            Expr::Bool(_) => {}
435            Expr::Int(_) => {}
436            Expr::Float(_) => {}
437            Expr::Binary(_) => {}
438            Expr::Range(range) => {
439                if let Some(from) = &mut range.from {
440                    from.replace_in_variable(working_set, new_var_id);
441                }
442                if let Some(next) = &mut range.next {
443                    next.replace_in_variable(working_set, new_var_id);
444                }
445                if let Some(to) = &mut range.to {
446                    to.replace_in_variable(working_set, new_var_id);
447                }
448            }
449            Expr::Var(var_id) | Expr::VarDecl(var_id) => {
450                if *var_id == IN_VARIABLE_ID {
451                    *var_id = new_var_id;
452                }
453            }
454            Expr::Call(call) => {
455                for arg in call.arguments.iter_mut() {
456                    match arg {
457                        Argument::Positional(expr)
458                        | Argument::Unknown(expr)
459                        | Argument::Named((_, _, Some(expr)))
460                        | Argument::Spread(expr) => {
461                            expr.replace_in_variable(working_set, new_var_id)
462                        }
463                        Argument::Named((_, _, None)) => {}
464                    }
465                }
466                for expr in call.parser_info.values_mut() {
467                    expr.replace_in_variable(working_set, new_var_id)
468                }
469            }
470            Expr::ExternalCall(head, args) => {
471                head.replace_in_variable(working_set, new_var_id);
472                for arg in args.iter_mut() {
473                    match arg {
474                        ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) => {
475                            expr.replace_in_variable(working_set, new_var_id)
476                        }
477                    }
478                }
479            }
480            Expr::Operator(_) => {}
481            // `$in` in `Collect` has already been handled, so we don't need to check further
482            Expr::Collect(_, _) => {}
483            Expr::Block(block_id)
484            | Expr::Closure(block_id)
485            | Expr::RowCondition(block_id)
486            | Expr::Subexpression(block_id) => {
487                let mut block = Block::clone(working_set.get_block(*block_id));
488                block.replace_in_variable(working_set, new_var_id);
489                *working_set.get_block_mut(*block_id) = block;
490            }
491            Expr::UnaryNot(expr) => {
492                expr.replace_in_variable(working_set, new_var_id);
493            }
494            Expr::BinaryOp(lhs, op, rhs) => {
495                for expr in [lhs, op, rhs] {
496                    expr.replace_in_variable(working_set, new_var_id);
497                }
498            }
499            Expr::MatchBlock(match_patterns) => {
500                for (_, expr) in match_patterns.iter_mut() {
501                    expr.replace_in_variable(working_set, new_var_id);
502                }
503            }
504            Expr::List(items) => {
505                for item in items.iter_mut() {
506                    match item {
507                        ListItem::Item(expr) | ListItem::Spread(_, expr) => {
508                            expr.replace_in_variable(working_set, new_var_id)
509                        }
510                    }
511                }
512            }
513            Expr::Table(table) => {
514                for col_expr in table.columns.iter_mut() {
515                    col_expr.replace_in_variable(working_set, new_var_id);
516                }
517                for row in table.rows.iter_mut() {
518                    for row_expr in row.iter_mut() {
519                        row_expr.replace_in_variable(working_set, new_var_id);
520                    }
521                }
522            }
523            Expr::Record(items) => {
524                for item in items.iter_mut() {
525                    match item {
526                        RecordItem::Pair(key, val) => {
527                            key.replace_in_variable(working_set, new_var_id);
528                            val.replace_in_variable(working_set, new_var_id);
529                        }
530                        RecordItem::Spread(_, expr) => {
531                            expr.replace_in_variable(working_set, new_var_id)
532                        }
533                    }
534                }
535            }
536            Expr::Keyword(kw) => kw.expr.replace_in_variable(working_set, new_var_id),
537            Expr::ValueWithUnit(value_with_unit) => value_with_unit
538                .expr
539                .replace_in_variable(working_set, new_var_id),
540            Expr::DateTime(_) => {}
541            Expr::Filepath(_, _) => {}
542            Expr::Directory(_, _) => {}
543            Expr::GlobPattern(_, _) => {}
544            Expr::String(_) => {}
545            Expr::RawString(_) => {}
546            Expr::CellPath(_) => {}
547            Expr::FullCellPath(full_cell_path) => {
548                full_cell_path
549                    .head
550                    .replace_in_variable(working_set, new_var_id);
551            }
552            Expr::ImportPattern(_) => {}
553            Expr::Overlay(_) => {}
554            Expr::Signature(_) => {}
555            Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
556                for expr in exprs.iter_mut() {
557                    expr.replace_in_variable(working_set, new_var_id);
558                }
559            }
560            Expr::Nothing => {}
561            Expr::Garbage => {}
562        }
563    }
564
565    pub fn new(working_set: &mut StateWorkingSet, expr: Expr, span: Span, ty: Type) -> Expression {
566        let span_id = working_set.add_span(span);
567        Expression {
568            expr,
569            span,
570            span_id,
571            ty,
572            custom_completion: None,
573        }
574    }
575
576    pub fn new_existing(expr: Expr, span: Span, span_id: SpanId, ty: Type) -> Expression {
577        Expression {
578            expr,
579            span,
580            span_id,
581            ty,
582            custom_completion: None,
583        }
584    }
585
586    pub fn new_unknown(expr: Expr, span: Span, ty: Type) -> Expression {
587        Expression {
588            expr,
589            span,
590            span_id: SpanId::new(0),
591            ty,
592            custom_completion: None,
593        }
594    }
595
596    pub fn with_span_id(self, span_id: SpanId) -> Expression {
597        Expression {
598            expr: self.expr,
599            span: self.span,
600            span_id,
601            ty: self.ty,
602            custom_completion: self.custom_completion,
603        }
604    }
605
606    pub fn span(&self, state: &impl GetSpan) -> Span {
607        state.get_span(self.span_id)
608    }
609}