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