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.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 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 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 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}