Skip to main content

vj_sim/
patterns.rs

1//! Pattern matching (IEEE 1800-2023 12.6): what the analysis side needs.
2//!
3//! A pattern variable (`.n` in `tagged Valid .n`) is bound when its pattern
4//! matches and read by the later conditions and the matched branch. slang
5//! gives it no declaration statement and, for a `?:` pattern, no scope
6//! either, so the declared-locals scans (`process::liveness`, a subroutine's
7//! locals) cannot find it as a block member: [`pattern_vars`] finds them in
8//! the patterns themselves.
9
10use std::collections::HashSet;
11
12use sv_lang::{SemNode, Symbol};
13use sv_lang_kinds::{ExpressionKind, PatternKind, StatementKind};
14
15use crate::symid::{SymbolId, SymbolUid};
16
17/// Every pattern variable bound anywhere in the subtree of `root`
18/// (`if (e matches p)`, `case (e) matches`, `e matches p ? a : b`), once
19/// each, in source order. Variables are told apart by [`SymbolUid::uid`]; a
20/// `?:` pattern's variable lives in no scope, so two of them with the same
21/// name share a key (and a slot) -- codegen refuses the pair when their types
22/// differ in width.
23pub fn pattern_vars<'d>(root: SemNode<'d>) -> Vec<Symbol<'d>> {
24    let mut out = Vec::new();
25    let mut seen: HashSet<SymbolId> = HashSet::new();
26    collect(root, &mut out, &mut seen);
27    out
28}
29
30fn collect<'d>(root: SemNode<'d>, out: &mut Vec<Symbol<'d>>, seen: &mut HashSet<SymbolId>) {
31    let mut stack = vec![root];
32    while let Some(node) = stack.pop() {
33        let mut kids = node.children();
34        if let Some(p) = node.as_pattern()
35            && p.kind() == PatternKind::Variable
36            && let Some(v) = p.variable()
37            && !v.name().is_empty()
38            && seen.insert(v.uid())
39        {
40            out.push(v);
41        }
42        if let Some(s) = node.as_statement() {
43            // Only these two statements hold patterns; every other kind's
44            // children are already in `node.children()`.
45            if s.kind() == StatementKind::Conditional {
46                for c in s.conditions() {
47                    kids.extend(c.pattern().map(|p| p.as_sem_node()));
48                }
49            } else if s.kind() == StatementKind::PatternCase {
50                for item in s.pattern_case_items() {
51                    kids.push(item.pattern().as_sem_node());
52                    kids.push(item.stmt().as_sem_node());
53                }
54            }
55        }
56        if let Some(e) = node.as_expression()
57            && e.kind() == ExpressionKind::ConditionalOp
58        {
59            for c in e.conditions() {
60                kids.extend(c.pattern().map(|p| p.as_sem_node()));
61            }
62        }
63        kids.reverse();
64        stack.extend(kids);
65    }
66}
67
68/// The case expression of a `case ... matches` statement. The bindings'
69/// `Statement::expr` has no `PatternCase` arm, so it is the first expression
70/// among the statement's semantic children (slang visits the case expression
71/// before the items); the other expression children are exactly the items'
72/// `&&&` filters, which is checked. `None` when `stmt` is not a `PatternCase`
73/// or the children do not have that shape.
74pub fn pattern_case_expr<'d>(stmt: sv_lang::Statement<'d>) -> Option<sv_lang::Expression<'d>> {
75    if stmt.kind() != StatementKind::PatternCase {
76        return None;
77    }
78    let exprs: Vec<_> = stmt
79        .as_sem_node()
80        .children()
81        .into_iter()
82        .filter_map(|c| c.as_expression())
83        .collect();
84    let filters = stmt
85        .pattern_case_items()
86        .iter()
87        .filter(|i| i.filter().is_some())
88        .count();
89    (exprs.len() == 1 + filters).then(|| exprs[0])
90}