Skip to main content

graphix_compiler/expr/
pattern.rs

1use super::Expr;
2use crate::{env::Env, typ::Type};
3use anyhow::Result;
4use arcstr::ArcStr;
5use netidx_value::{Typ, Value};
6use smallvec::{smallvec, SmallVec};
7use std::fmt;
8use triomphe::Arc;
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub enum StructurePattern {
12    Ignore,
13    Literal(Value),
14    Bind(ArcStr),
15    Slice {
16        all: Option<ArcStr>,
17        binds: Arc<[StructurePattern]>,
18    },
19    SlicePrefix {
20        all: Option<ArcStr>,
21        prefix: Arc<[StructurePattern]>,
22        tail: Option<ArcStr>,
23    },
24    SliceSuffix {
25        all: Option<ArcStr>,
26        head: Option<ArcStr>,
27        suffix: Arc<[StructurePattern]>,
28    },
29    Tuple {
30        all: Option<ArcStr>,
31        binds: Arc<[StructurePattern]>,
32    },
33    Variant {
34        all: Option<ArcStr>,
35        tag: ArcStr,
36        binds: Arc<[StructurePattern]>,
37    },
38    Struct {
39        exhaustive: bool,
40        all: Option<ArcStr>,
41        binds: Arc<[(ArcStr, StructurePattern)]>,
42    },
43}
44
45impl StructurePattern {
46    pub fn single_bind(&self) -> Option<&ArcStr> {
47        match self {
48            Self::Bind(s) => Some(s),
49            Self::Ignore
50            | Self::Literal(_)
51            | Self::Slice { .. }
52            | Self::SlicePrefix { .. }
53            | Self::SliceSuffix { .. }
54            | Self::Tuple { .. }
55            | Self::Struct { .. }
56            | Self::Variant { .. } => None,
57        }
58    }
59
60    pub fn with_names<'a>(&'a self, f: &mut impl FnMut(&'a ArcStr)) {
61        match self {
62            Self::Bind(n) => f(n),
63            Self::Ignore | Self::Literal(_) => (),
64            Self::Slice { all, binds } => {
65                if let Some(n) = all {
66                    f(n)
67                }
68                for t in binds.iter() {
69                    t.with_names(f)
70                }
71            }
72            Self::SlicePrefix { all, prefix, tail } => {
73                if let Some(n) = all {
74                    f(n)
75                }
76                if let Some(n) = tail {
77                    f(n)
78                }
79                for t in prefix.iter() {
80                    t.with_names(f)
81                }
82            }
83            Self::SliceSuffix { all, head, suffix } => {
84                if let Some(n) = all {
85                    f(n)
86                }
87                if let Some(n) = head {
88                    f(n)
89                }
90                for t in suffix.iter() {
91                    t.with_names(f)
92                }
93            }
94            Self::Tuple { all, binds } => {
95                if let Some(n) = all {
96                    f(n)
97                }
98                for t in binds.iter() {
99                    t.with_names(f)
100                }
101            }
102            Self::Variant { all, tag: _, binds } => {
103                if let Some(n) = all {
104                    f(n)
105                }
106                for t in binds.iter() {
107                    t.with_names(f)
108                }
109            }
110            Self::Struct { exhaustive: _, all, binds } => {
111                if let Some(n) = all {
112                    f(n)
113                }
114                for (_, t) in binds.iter() {
115                    t.with_names(f)
116                }
117            }
118        }
119    }
120
121    pub fn binds_uniq(&self) -> bool {
122        let mut names: SmallVec<[&ArcStr; 16]> = smallvec![];
123        self.with_names(&mut |s| names.push(s));
124        names.sort();
125        let len = names.len();
126        names.dedup();
127        names.len() == len
128    }
129
130    pub fn infer_type_predicate(&self, env: &Env) -> Result<Type> {
131        match self {
132            Self::Ignore => Ok(Type::Any),
133            Self::Bind(_) => Ok(Type::empty_tvar()),
134            Self::Literal(v) => Ok(Type::Primitive(Typ::get(v).into())),
135            Self::Tuple { all: _, binds } => {
136                let a = binds
137                    .iter()
138                    .map(|p| p.infer_type_predicate(env))
139                    .collect::<Result<SmallVec<[_; 8]>>>()?;
140                Ok(Type::Tuple(Arc::from_iter(a)))
141            }
142            Self::Variant { all: _, tag, binds } => {
143                let a = binds
144                    .iter()
145                    .map(|p| p.infer_type_predicate(env))
146                    .collect::<Result<SmallVec<[_; 8]>>>()?;
147                Ok(Type::Variant(tag.clone(), Arc::from_iter(a)))
148            }
149            Self::Slice { all: _, binds }
150            | Self::SlicePrefix { all: _, prefix: binds, tail: _ }
151            | Self::SliceSuffix { all: _, head: _, suffix: binds } => {
152                let t =
153                    binds.iter().fold(Ok::<_, anyhow::Error>(Type::Bottom), |t, p| {
154                        Ok(t?.union(env, &p.infer_type_predicate(env)?)?)
155                    })?;
156                let t = match t {
157                    Type::Bottom => Type::empty_tvar(),
158                    t => t,
159                };
160                Ok(Type::Array(Arc::new(t)))
161            }
162            Self::Struct { all: _, exhaustive: _, binds } => {
163                let mut typs = binds
164                    .iter()
165                    .map(|(n, p)| Ok((n.clone(), p.infer_type_predicate(env)?)))
166                    .collect::<Result<SmallVec<[(ArcStr, Type); 8]>>>()?;
167                typs.sort_by_key(|(n, _)| n.clone());
168                Ok(Type::Struct(Arc::from_iter(typs.into_iter())))
169            }
170        }
171    }
172}
173
174impl fmt::Display for StructurePattern {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        macro_rules! with_sep {
177            ($binds:expr) => {
178                for (i, b) in $binds.iter().enumerate() {
179                    write!(f, "{b}")?;
180                    if i < $binds.len() - 1 {
181                        write!(f, ", ")?
182                    }
183                }
184            };
185        }
186        match self {
187            StructurePattern::Ignore => write!(f, "_"),
188            StructurePattern::Literal(v) => write!(f, "{v}"),
189            StructurePattern::Bind(n) => write!(f, "{n}"),
190            StructurePattern::Slice { all, binds } => {
191                if let Some(all) = all {
192                    write!(f, "{all}@ ")?
193                }
194                write!(f, "[")?;
195                with_sep!(binds);
196                write!(f, "]")
197            }
198            StructurePattern::SlicePrefix { all, prefix, tail } => {
199                if let Some(all) = all {
200                    write!(f, "{all}@ ")?
201                }
202                write!(f, "[")?;
203                for b in prefix.iter() {
204                    write!(f, "{b}, ")?
205                }
206                match tail {
207                    None => write!(f, "..]"),
208                    Some(name) => write!(f, "{name}..]"),
209                }
210            }
211            StructurePattern::SliceSuffix { all, head, suffix } => {
212                if let Some(all) = all {
213                    write!(f, "{all}@ ")?
214                }
215                write!(f, "[")?;
216                match head {
217                    None => write!(f, ".., ")?,
218                    Some(name) => write!(f, "{name}.., ")?,
219                }
220                with_sep!(suffix);
221                write!(f, "]")
222            }
223            StructurePattern::Tuple { all, binds } => {
224                if let Some(all) = all {
225                    write!(f, "{all}@ ")?
226                }
227                write!(f, "(")?;
228                with_sep!(binds);
229                write!(f, ")")
230            }
231            StructurePattern::Variant { all, tag, binds } if binds.len() == 0 => {
232                if let Some(all) = all {
233                    write!(f, "{all}@")?
234                }
235                write!(f, "`{tag}")
236            }
237            StructurePattern::Variant { all, tag, binds } => {
238                if let Some(all) = all {
239                    write!(f, "{all}@")?
240                }
241                write!(f, "`{tag}(")?;
242                with_sep!(binds);
243                write!(f, ")")
244            }
245            StructurePattern::Struct { exhaustive, all, binds } => {
246                if let Some(all) = all {
247                    write!(f, "{all}@ ")?
248                }
249                write!(f, "{{")?;
250                for (i, (name, pat)) in binds.iter().enumerate() {
251                    write!(f, "{name}: {pat}")?;
252                    if !exhaustive || i < binds.len() - 1 {
253                        write!(f, ", ")?
254                    }
255                }
256                if !exhaustive {
257                    write!(f, "..")?
258                }
259                write!(f, "}}")
260            }
261        }
262    }
263}
264
265#[derive(Debug, Clone, PartialEq, PartialOrd)]
266pub struct Pattern {
267    pub type_predicate: Option<Type>,
268    pub structure_predicate: StructurePattern,
269    pub guard: Option<Expr>,
270}