rukalang/meta/
quote_match.rs

1use super::*;
2
3pub(super) fn match_quote_pattern_expr(
4    pattern: &Expr,
5    target: &Expr,
6    captures: &mut BTreeMap<String, MetaValue>,
7) -> bool {
8    match (pattern, target) {
9        (Expr::Splice(capture), _) => {
10            if let MetaExpr::Ident(name) = capture.as_ref() {
11                let value = MetaValue::Expr {
12                    expr: target.clone(),
13                    ty: TypeExpr::Named("any".to_owned()),
14                };
15                if let Some(existing) = captures.get(name) {
16                    existing == &value
17                } else {
18                    captures.insert(name.clone(), value);
19                    true
20                }
21            } else {
22                false
23            }
24        }
25        (Expr::Ident { name: left, .. }, Expr::Ident { name: right, .. }) => left == right,
26        (Expr::Int(left), Expr::Int(right)) => left == right,
27        (Expr::Int(left), Expr::Number(right)) => number_literal_to_i64(&right.raw) == Some(*left),
28        (Expr::Number(left), Expr::Int(right)) => number_literal_to_i64(&left.raw) == Some(*right),
29        (Expr::Number(left), Expr::Number(right)) => left == right,
30        (Expr::String(left), Expr::String(right)) => left == right,
31        (Expr::Bool(left), Expr::Bool(right)) => left == right,
32        (Expr::Array { items: left, .. }, Expr::Array { items: right, .. }) => {
33            left.len() == right.len()
34                && left
35                    .iter()
36                    .zip(right.iter())
37                    .all(|(a, b)| match_quote_pattern_expr(a, b, captures))
38        }
39        (Expr::Tuple { items: left, .. }, Expr::Tuple { items: right, .. }) => {
40            left.len() == right.len()
41                && left
42                    .iter()
43                    .zip(right.iter())
44                    .all(|(a, b)| match_quote_pattern_expr(a, b, captures))
45        }
46        (
47            Expr::StructLit {
48                name: left_name,
49                fields: left_fields,
50                ..
51            },
52            Expr::StructLit {
53                name: right_name,
54                fields: right_fields,
55                ..
56            },
57        ) => {
58            left_name == right_name
59                && left_fields.len() == right_fields.len()
60                && left_fields
61                    .iter()
62                    .zip(right_fields.iter())
63                    .all(|(left_field, right_field)| {
64                        left_field.name == right_field.name
65                            && match_quote_pattern_expr(
66                                &left_field.value,
67                                &right_field.value,
68                                captures,
69                            )
70                    })
71        }
72        (
73            Expr::Call {
74                callee: left_callee,
75                args: left_args,
76                ..
77            },
78            Expr::Call {
79                callee: right_callee,
80                args: right_args,
81                ..
82            },
83        ) => {
84            left_args.len() == right_args.len()
85                && match_quote_pattern_expr(left_callee, right_callee, captures)
86                && left_args
87                    .iter()
88                    .zip(right_args.iter())
89                    .all(|(a, b)| match_quote_pattern_call_arg(a, b, captures))
90        }
91        (
92            Expr::Field {
93                base: left_base,
94                field: left_field,
95            },
96            Expr::Field {
97                base: right_base,
98                field: right_field,
99            },
100        ) => left_field == right_field && match_quote_pattern_expr(left_base, right_base, captures),
101        (
102            Expr::Index {
103                base: left_base,
104                index: left_index,
105            },
106            Expr::Index {
107                base: right_base,
108                index: right_index,
109            },
110        ) => {
111            match_quote_pattern_expr(left_base, right_base, captures)
112                && match_quote_pattern_expr(left_index, right_index, captures)
113        }
114        (
115            Expr::SliceRange {
116                base: left_base,
117                start: left_start,
118                end: left_end,
119            },
120            Expr::SliceRange {
121                base: right_base,
122                start: right_start,
123                end: right_end,
124            },
125        ) => {
126            match_quote_pattern_expr(left_base, right_base, captures)
127                && match (left_start, right_start) {
128                    (Some(a), Some(b)) => match_quote_pattern_expr(a, b, captures),
129                    (None, None) => true,
130                    _ => false,
131                }
132                && match (left_end, right_end) {
133                    (Some(a), Some(b)) => match_quote_pattern_expr(a, b, captures),
134                    (None, None) => true,
135                    _ => false,
136                }
137        }
138        (
139            Expr::Prefix {
140                op: left_op,
141                value: left_value,
142            },
143            Expr::Prefix {
144                op: right_op,
145                value: right_value,
146            },
147        ) => left_op == right_op && match_quote_pattern_expr(left_value, right_value, captures),
148        (
149            Expr::Binary {
150                op: left_op,
151                lhs: left_lhs,
152                rhs: left_rhs,
153            },
154            Expr::Binary {
155                op: right_op,
156                lhs: right_lhs,
157                rhs: right_rhs,
158            },
159        ) => {
160            left_op == right_op
161                && match_quote_pattern_expr(left_lhs, right_lhs, captures)
162                && match_quote_pattern_expr(left_rhs, right_rhs, captures)
163        }
164        (
165            Expr::Relational {
166                op: left_op,
167                lhs: left_lhs,
168                rhs: left_rhs,
169            },
170            Expr::Relational {
171                op: right_op,
172                lhs: right_lhs,
173                rhs: right_rhs,
174            },
175        ) => {
176            left_op == right_op
177                && match_quote_pattern_expr(left_lhs, right_lhs, captures)
178                && match_quote_pattern_expr(left_rhs, right_rhs, captures)
179        }
180        (Expr::Block(left), Expr::Block(right)) => {
181            left.statements.len() == right.statements.len()
182                && left
183                    .statements
184                    .iter()
185                    .zip(right.statements.iter())
186                    .all(|(a, b)| match_stmt_pattern(a, b, captures))
187        }
188        _ => false,
189    }
190}
191
192pub(super) fn number_literal_to_i64(raw: &str) -> Option<i64> {
193    if raw.contains('.')
194        || raw.ends_with("u8")
195        || raw.ends_with("u16")
196        || raw.ends_with("u32")
197        || raw.ends_with("u64")
198        || raw.ends_with("i8")
199        || raw.ends_with("i16")
200        || raw.ends_with("i32")
201        || raw.ends_with("i64")
202        || raw.ends_with("f32")
203        || raw.ends_with("f64")
204    {
205        return None;
206    }
207    let digits: String = raw.chars().filter(|ch| *ch != '_').collect();
208    digits.parse::<i64>().ok()
209}
210
211pub(super) fn match_quote_pattern_call_arg(
212    pattern: &CallArg,
213    target: &CallArg,
214    captures: &mut BTreeMap<String, MetaValue>,
215) -> bool {
216    match (pattern, target) {
217        (CallArg::Expr(pattern), CallArg::Expr(target)) => {
218            match_quote_pattern_expr(pattern, target, captures)
219        }
220        (CallArg::Spread(pattern), CallArg::Spread(target)) => {
221            match_quote_pattern_expr(pattern, target, captures)
222        }
223        (CallArg::Type(left), CallArg::Type(right)) => left == right,
224        _ => false,
225    }
226}
227
228pub(super) fn match_stmt_pattern(
229    pattern: &Stmt,
230    target: &Stmt,
231    captures: &mut BTreeMap<String, MetaValue>,
232) -> bool {
233    match (pattern, target) {
234        (
235            Stmt::Let {
236                name: left_name,
237                name_span: left_span,
238                ownership: left_ownership,
239                mode: left_mode,
240                value: left_value,
241            },
242            Stmt::Let {
243                name: right_name,
244                name_span: right_span,
245                ownership: right_ownership,
246                mode: right_mode,
247                value: right_value,
248            },
249        ) => {
250            left_name == right_name
251                && left_span == right_span
252                && left_ownership == right_ownership
253                && left_mode == right_mode
254                && match_quote_pattern_expr(left_value, right_value, captures)
255        }
256        (
257            Stmt::Assign {
258                target: left_target,
259                mode: left_mode,
260                value: left_value,
261            },
262            Stmt::Assign {
263                target: right_target,
264                mode: right_mode,
265                value: right_value,
266            },
267        ) => {
268            left_target == right_target
269                && left_mode == right_mode
270                && match_quote_pattern_expr(left_value, right_value, captures)
271        }
272        (
273            Stmt::If {
274                condition: left_condition,
275                then_block: left_then,
276                else_block: left_else,
277            },
278            Stmt::If {
279                condition: right_condition,
280                then_block: right_then,
281                else_block: right_else,
282            },
283        ) => {
284            match_quote_pattern_expr(left_condition, right_condition, captures)
285                && match_quote_pattern_expr(
286                    &Expr::Block(left_then.clone()),
287                    &Expr::Block(right_then.clone()),
288                    captures,
289                )
290                && match (left_else, right_else) {
291                    (None, None) => true,
292                    (Some(left_block), Some(right_block)) => match_quote_pattern_expr(
293                        &Expr::Block(left_block.clone()),
294                        &Expr::Block(right_block.clone()),
295                        captures,
296                    ),
297                    _ => false,
298                }
299        }
300        (
301            Stmt::For {
302                binding: left_binding,
303                iterable: left_iterable,
304                body: left_body,
305            },
306            Stmt::For {
307                binding: right_binding,
308                iterable: right_iterable,
309                body: right_body,
310            },
311        ) => {
312            left_binding == right_binding
313                && match_quote_pattern_expr(left_iterable, right_iterable, captures)
314                && match_quote_pattern_expr(
315                    &Expr::Block(left_body.clone()),
316                    &Expr::Block(right_body.clone()),
317                    captures,
318                )
319        }
320        (
321            Stmt::While {
322                condition: left_condition,
323                body: left_body,
324            },
325            Stmt::While {
326                condition: right_condition,
327                body: right_body,
328            },
329        ) => {
330            match_quote_pattern_expr(left_condition, right_condition, captures)
331                && match_quote_pattern_expr(
332                    &Expr::Block(left_body.clone()),
333                    &Expr::Block(right_body.clone()),
334                    captures,
335                )
336        }
337        (Stmt::Meta { body: left_body }, Stmt::Meta { body: right_body }) => {
338            left_body == right_body
339        }
340        (
341            Stmt::Expr {
342                expr: left_expr,
343                has_semi: left_has_semi,
344            },
345            Stmt::Expr {
346                expr: right_expr,
347                has_semi: right_has_semi,
348            },
349        ) => {
350            left_has_semi == right_has_semi
351                && match_quote_pattern_expr(left_expr, right_expr, captures)
352        }
353        _ => false,
354    }
355}
356
357pub(super) fn match_quote_pattern_type(pattern: &TypeExpr, target: &TypeExpr) -> bool {
358    match (pattern, target) {
359        (TypeExpr::Named(left), TypeExpr::Tuple(right)) if left == "Unit" && right.is_empty() => {
360            true
361        }
362        (TypeExpr::Tuple(left), TypeExpr::Named(right)) if left.is_empty() && right == "Unit" => {
363            true
364        }
365        _ => crate::meta_types::meta_types_compatible(pattern, target),
366    }
367}
368
369pub(super) fn kind_name(value: &MetaValue) -> &'static str {
370    match value {
371        MetaValue::Int(_) => "int",
372        MetaValue::Bool(_) => "bool",
373        MetaValue::String(_) => "string",
374        MetaValue::Type(_) => "type",
375        MetaValue::Expr { .. } => "expr",
376        MetaValue::Code(_) => "code",
377    }
378}
379
380pub(super) fn meta_value_type_name(value: &MetaValue) -> String {
381    match value {
382        MetaValue::Int(_) => "i64".to_owned(),
383        MetaValue::Bool(_) => "Bool".to_owned(),
384        MetaValue::String(_) => "String".to_owned(),
385        MetaValue::Type(_) => "type".to_owned(),
386        MetaValue::Expr { ty, .. } => format!("Expr[{}]", format_type_expr(ty)),
387        MetaValue::Code(CodeValue::Expr(_)) => "Code[expr]".to_owned(),
388        MetaValue::Code(CodeValue::Type(_)) => "Code[type]".to_owned(),
389    }
390}
391
392pub(super) fn format_meta_type(ty: &MetaType) -> String {
393    match ty {
394        MetaType::Int => "i64".to_owned(),
395        MetaType::Bool => "Bool".to_owned(),
396        MetaType::String => "String".to_owned(),
397        MetaType::Type => "type".to_owned(),
398        MetaType::Expr(item) => format!("Expr[{}]", format_type_expr(item)),
399    }
400}