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#[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
115 .captures
116 .iter()
117 .any(|(var_id, _)| var_id == &IN_VARIABLE_ID)
118 || block
119 .pipelines
120 .iter()
121 .flat_map(|pipeline| pipeline.elements.first())
122 .any(|element| element.has_in_variable(working_set))
123 }
124 Expr::Binary(_) => false,
125 Expr::Bool(_) => false,
126 Expr::Call(call) => {
127 for arg in &call.arguments {
128 match arg {
129 Argument::Positional(expr)
130 | Argument::Unknown(expr)
131 | Argument::Spread(expr) => {
132 if expr.has_in_variable(working_set) {
133 return true;
134 }
135 }
136 Argument::Named(named) => {
137 if let Some(expr) = &named.2 {
138 if expr.has_in_variable(working_set) {
139 return true;
140 }
141 }
142 }
143 }
144 }
145 false
146 }
147 Expr::CellPath(_) => false,
148 Expr::DateTime(_) => false,
149 Expr::ExternalCall(head, args) => {
150 if head.has_in_variable(working_set) {
151 return true;
152 }
153 for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in
154 args.as_ref()
155 {
156 if expr.has_in_variable(working_set) {
157 return true;
158 }
159 }
160 false
161 }
162 Expr::ImportPattern(_) => false,
163 Expr::Overlay(_) => false,
164 Expr::Filepath(_, _) => false,
165 Expr::Directory(_, _) => false,
166 Expr::Float(_) => false,
167 Expr::FullCellPath(full_cell_path) => {
168 if full_cell_path.head.has_in_variable(working_set) {
169 return true;
170 }
171 false
172 }
173 Expr::Garbage => false,
174 Expr::Nothing => false,
175 Expr::GlobPattern(_, _) => false,
176 Expr::Int(_) => false,
177 Expr::Keyword(kw) => kw.expr.has_in_variable(working_set),
178 Expr::List(list) => {
179 for item in list {
180 if item.expr().has_in_variable(working_set) {
181 return true;
182 }
183 }
184 false
185 }
186 Expr::StringInterpolation(items) | Expr::GlobInterpolation(items, _) => {
187 for i in items {
188 if i.has_in_variable(working_set) {
189 return true;
190 }
191 }
192 false
193 }
194 Expr::Operator(_) => false,
195 Expr::MatchBlock(_) => false,
196 Expr::Range(range) => {
197 if let Some(left) = &range.from {
198 if left.has_in_variable(working_set) {
199 return true;
200 }
201 }
202 if let Some(middle) = &range.next {
203 if middle.has_in_variable(working_set) {
204 return true;
205 }
206 }
207 if let Some(right) = &range.to {
208 if right.has_in_variable(working_set) {
209 return true;
210 }
211 }
212 false
213 }
214 Expr::Record(items) => {
215 for item in items {
216 match item {
217 RecordItem::Pair(field_name, field_value) => {
218 if field_name.has_in_variable(working_set) {
219 return true;
220 }
221 if field_value.has_in_variable(working_set) {
222 return true;
223 }
224 }
225 RecordItem::Spread(_, record) => {
226 if record.has_in_variable(working_set) {
227 return true;
228 }
229 }
230 }
231 }
232 false
233 }
234 Expr::Signature(_) => false,
235 Expr::String(_) => false,
236 Expr::RawString(_) => false,
237 Expr::Collect(_, _) => false,
240 Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
241 let block = working_set.get_block(*block_id);
242
243 if let Some(pipeline) = block.pipelines.first() {
244 if let Some(expr) = pipeline.elements.first() {
245 expr.has_in_variable(working_set)
246 } else {
247 false
248 }
249 } else {
250 false
251 }
252 }
253 Expr::Table(table) => {
254 for header in table.columns.as_ref() {
255 if header.has_in_variable(working_set) {
256 return true;
257 }
258 }
259
260 for row in table.rows.as_ref() {
261 for cell in row.iter() {
262 if cell.has_in_variable(working_set) {
263 return true;
264 }
265 }
266 }
267
268 false
269 }
270
271 Expr::ValueWithUnit(value) => value.expr.has_in_variable(working_set),
272 Expr::Var(var_id) => *var_id == IN_VARIABLE_ID,
273 Expr::VarDecl(_) => false,
274 }
275 }
276
277 pub fn replace_span(
278 &mut self,
279 working_set: &mut StateWorkingSet,
280 replaced: Span,
281 new_span: Span,
282 ) {
283 if replaced.contains_span(self.span) {
284 self.span = new_span;
285 }
286 match &mut self.expr {
287 Expr::AttributeBlock(ab) => ab.item.replace_span(working_set, replaced, new_span),
288 Expr::BinaryOp(left, _, right) => {
289 left.replace_span(working_set, replaced, new_span);
290 right.replace_span(working_set, replaced, new_span);
291 }
292 Expr::UnaryNot(expr) => {
293 expr.replace_span(working_set, replaced, new_span);
294 }
295 Expr::Block(block_id) => {
296 let mut block = Block::clone(working_set.get_block(*block_id));
298
299 for pipeline in block.pipelines.iter_mut() {
300 for element in pipeline.elements.iter_mut() {
301 element.replace_span(working_set, replaced, new_span)
302 }
303 }
304
305 *block_id = working_set.add_block(Arc::new(block));
306 }
307 Expr::Closure(block_id) => {
308 let mut block = (**working_set.get_block(*block_id)).clone();
309
310 for pipeline in block.pipelines.iter_mut() {
311 for element in pipeline.elements.iter_mut() {
312 element.replace_span(working_set, replaced, new_span)
313 }
314 }
315
316 *block_id = working_set.add_block(Arc::new(block));
317 }
318 Expr::Binary(_) => {}
319 Expr::Bool(_) => {}
320 Expr::Call(call) => {
321 if replaced.contains_span(call.head) {
322 call.head = new_span;
323 }
324 for arg in call.arguments.iter_mut() {
325 match arg {
326 Argument::Positional(expr)
327 | Argument::Unknown(expr)
328 | Argument::Spread(expr) => {
329 expr.replace_span(working_set, replaced, new_span);
330 }
331 Argument::Named(named) => {
332 if let Some(expr) = &mut named.2 {
333 expr.replace_span(working_set, replaced, new_span);
334 }
335 }
336 }
337 }
338 }
339 Expr::CellPath(_) => {}
340 Expr::DateTime(_) => {}
341 Expr::ExternalCall(head, args) => {
342 head.replace_span(working_set, replaced, new_span);
343 for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in
344 args.as_mut()
345 {
346 expr.replace_span(working_set, replaced, new_span);
347 }
348 }
349 Expr::Filepath(_, _) => {}
350 Expr::Directory(_, _) => {}
351 Expr::Float(_) => {}
352 Expr::FullCellPath(full_cell_path) => {
353 full_cell_path
354 .head
355 .replace_span(working_set, replaced, new_span);
356 }
357 Expr::ImportPattern(_) => {}
358 Expr::Overlay(_) => {}
359 Expr::Garbage => {}
360 Expr::Nothing => {}
361 Expr::GlobPattern(_, _) => {}
362 Expr::MatchBlock(_) => {}
363 Expr::Int(_) => {}
364 Expr::Keyword(kw) => kw.expr.replace_span(working_set, replaced, new_span),
365 Expr::List(list) => {
366 for item in list {
367 item.expr_mut()
368 .replace_span(working_set, replaced, new_span);
369 }
370 }
371 Expr::Operator(_) => {}
372 Expr::Range(range) => {
373 if let Some(left) = &mut range.from {
374 left.replace_span(working_set, replaced, new_span)
375 }
376 if let Some(middle) = &mut range.next {
377 middle.replace_span(working_set, replaced, new_span)
378 }
379 if let Some(right) = &mut range.to {
380 right.replace_span(working_set, replaced, new_span)
381 }
382 }
383 Expr::Record(items) => {
384 for item in items {
385 match item {
386 RecordItem::Pair(field_name, field_value) => {
387 field_name.replace_span(working_set, replaced, new_span);
388 field_value.replace_span(working_set, replaced, new_span);
389 }
390 RecordItem::Spread(_, record) => {
391 record.replace_span(working_set, replaced, new_span);
392 }
393 }
394 }
395 }
396 Expr::Signature(_) => {}
397 Expr::String(_) => {}
398 Expr::RawString(_) => {}
399 Expr::StringInterpolation(items) | Expr::GlobInterpolation(items, _) => {
400 for i in items {
401 i.replace_span(working_set, replaced, new_span)
402 }
403 }
404 Expr::Collect(_, expr) => expr.replace_span(working_set, replaced, new_span),
405 Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
406 let mut block = (**working_set.get_block(*block_id)).clone();
407
408 for pipeline in block.pipelines.iter_mut() {
409 for element in pipeline.elements.iter_mut() {
410 element.replace_span(working_set, replaced, new_span)
411 }
412 }
413
414 *block_id = working_set.add_block(Arc::new(block));
415 }
416 Expr::Table(table) => {
417 for header in table.columns.as_mut() {
418 header.replace_span(working_set, replaced, new_span)
419 }
420
421 for row in table.rows.as_mut() {
422 for cell in row.iter_mut() {
423 cell.replace_span(working_set, replaced, new_span)
424 }
425 }
426 }
427
428 Expr::ValueWithUnit(value) => value.expr.replace_span(working_set, replaced, new_span),
429 Expr::Var(_) => {}
430 Expr::VarDecl(_) => {}
431 }
432 }
433
434 pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
435 match &mut self.expr {
436 Expr::AttributeBlock(ab) => ab.item.replace_in_variable(working_set, new_var_id),
437 Expr::Bool(_) => {}
438 Expr::Int(_) => {}
439 Expr::Float(_) => {}
440 Expr::Binary(_) => {}
441 Expr::Range(range) => {
442 if let Some(from) = &mut range.from {
443 from.replace_in_variable(working_set, new_var_id);
444 }
445 if let Some(next) = &mut range.next {
446 next.replace_in_variable(working_set, new_var_id);
447 }
448 if let Some(to) = &mut range.to {
449 to.replace_in_variable(working_set, new_var_id);
450 }
451 }
452 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
453 if *var_id == IN_VARIABLE_ID {
454 *var_id = new_var_id;
455 }
456 }
457 Expr::Call(call) => {
458 for arg in call.arguments.iter_mut() {
459 match arg {
460 Argument::Positional(expr)
461 | Argument::Unknown(expr)
462 | Argument::Named((_, _, Some(expr)))
463 | Argument::Spread(expr) => {
464 expr.replace_in_variable(working_set, new_var_id)
465 }
466 Argument::Named((_, _, None)) => {}
467 }
468 }
469 for expr in call.parser_info.values_mut() {
470 expr.replace_in_variable(working_set, new_var_id)
471 }
472 }
473 Expr::ExternalCall(head, args) => {
474 head.replace_in_variable(working_set, new_var_id);
475 for arg in args.iter_mut() {
476 match arg {
477 ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) => {
478 expr.replace_in_variable(working_set, new_var_id)
479 }
480 }
481 }
482 }
483 Expr::Operator(_) => {}
484 Expr::Collect(_, _) => {}
486 Expr::Block(block_id)
487 | Expr::Closure(block_id)
488 | Expr::RowCondition(block_id)
489 | Expr::Subexpression(block_id) => {
490 let mut block = Block::clone(working_set.get_block(*block_id));
491 block.replace_in_variable(working_set, new_var_id);
492 *working_set.get_block_mut(*block_id) = block;
493 }
494 Expr::UnaryNot(expr) => {
495 expr.replace_in_variable(working_set, new_var_id);
496 }
497 Expr::BinaryOp(lhs, op, rhs) => {
498 for expr in [lhs, op, rhs] {
499 expr.replace_in_variable(working_set, new_var_id);
500 }
501 }
502 Expr::MatchBlock(match_patterns) => {
503 for (_, expr) in match_patterns.iter_mut() {
504 expr.replace_in_variable(working_set, new_var_id);
505 }
506 }
507 Expr::List(items) => {
508 for item in items.iter_mut() {
509 match item {
510 ListItem::Item(expr) | ListItem::Spread(_, expr) => {
511 expr.replace_in_variable(working_set, new_var_id)
512 }
513 }
514 }
515 }
516 Expr::Table(table) => {
517 for col_expr in table.columns.iter_mut() {
518 col_expr.replace_in_variable(working_set, new_var_id);
519 }
520 for row in table.rows.iter_mut() {
521 for row_expr in row.iter_mut() {
522 row_expr.replace_in_variable(working_set, new_var_id);
523 }
524 }
525 }
526 Expr::Record(items) => {
527 for item in items.iter_mut() {
528 match item {
529 RecordItem::Pair(key, val) => {
530 key.replace_in_variable(working_set, new_var_id);
531 val.replace_in_variable(working_set, new_var_id);
532 }
533 RecordItem::Spread(_, expr) => {
534 expr.replace_in_variable(working_set, new_var_id)
535 }
536 }
537 }
538 }
539 Expr::Keyword(kw) => kw.expr.replace_in_variable(working_set, new_var_id),
540 Expr::ValueWithUnit(value_with_unit) => value_with_unit
541 .expr
542 .replace_in_variable(working_set, new_var_id),
543 Expr::DateTime(_) => {}
544 Expr::Filepath(_, _) => {}
545 Expr::Directory(_, _) => {}
546 Expr::GlobPattern(_, _) => {}
547 Expr::String(_) => {}
548 Expr::RawString(_) => {}
549 Expr::CellPath(_) => {}
550 Expr::FullCellPath(full_cell_path) => {
551 full_cell_path
552 .head
553 .replace_in_variable(working_set, new_var_id);
554 }
555 Expr::ImportPattern(_) => {}
556 Expr::Overlay(_) => {}
557 Expr::Signature(_) => {}
558 Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
559 for expr in exprs.iter_mut() {
560 expr.replace_in_variable(working_set, new_var_id);
561 }
562 }
563 Expr::Nothing => {}
564 Expr::Garbage => {}
565 }
566 }
567
568 pub fn new(working_set: &mut StateWorkingSet, expr: Expr, span: Span, ty: Type) -> Expression {
569 let span_id = working_set.add_span(span);
570 Expression {
571 expr,
572 span,
573 span_id,
574 ty,
575 custom_completion: None,
576 }
577 }
578
579 pub fn new_existing(expr: Expr, span: Span, span_id: SpanId, ty: Type) -> Expression {
580 Expression {
581 expr,
582 span,
583 span_id,
584 ty,
585 custom_completion: None,
586 }
587 }
588
589 pub fn new_unknown(expr: Expr, span: Span, ty: Type) -> Expression {
590 Expression {
591 expr,
592 span,
593 span_id: SpanId::new(0),
594 ty,
595 custom_completion: None,
596 }
597 }
598
599 pub fn with_span_id(self, span_id: SpanId) -> Expression {
600 Expression {
601 expr: self.expr,
602 span: self.span,
603 span_id,
604 ty: self.ty,
605 custom_completion: self.custom_completion,
606 }
607 }
608
609 pub fn span(&self, state: &impl GetSpan) -> Span {
610 state.get_span(self.span_id)
611 }
612}