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