graphix_compiler/expr/
mod.rs

1use crate::{
2    typ::{TVar, Type},
3    PrintFlag, PRINT_FLAGS,
4};
5use anyhow::Result;
6use arcstr::ArcStr;
7use combine::stream::position::SourcePosition;
8pub use modpath::ModPath;
9use netidx::{path::Path, subscriber::Value, utils::Either};
10pub use pattern::{Pattern, StructurePattern};
11use regex::Regex;
12pub use resolver::ModuleResolver;
13use serde::{
14    de::{self, Visitor},
15    Deserialize, Deserializer, Serialize, Serializer,
16};
17use std::{
18    cell::RefCell,
19    cmp::{Ordering, PartialEq, PartialOrd},
20    fmt,
21    path::PathBuf,
22    result,
23    str::FromStr,
24    sync::LazyLock,
25};
26use triomphe::Arc;
27
28mod modpath;
29pub mod parser;
30mod pattern;
31mod print;
32mod resolver;
33#[cfg(test)]
34mod test;
35
36pub const VNAME: LazyLock<Regex> =
37    LazyLock::new(|| Regex::new("^[a-z][a-z0-9_]*$").unwrap());
38
39atomic_id!(ExprId);
40
41const DEFAULT_ORIGIN: LazyLock<Arc<Origin>> =
42    LazyLock::new(|| Arc::new(Origin::default()));
43
44thread_local! {
45    static ORIGIN: RefCell<Option<Arc<Origin>>> = RefCell::new(None);
46}
47
48pub(crate) fn set_origin(ori: Arc<Origin>) {
49    ORIGIN.with_borrow_mut(|global| *global = Some(ori))
50}
51
52pub(crate) fn get_origin() -> Arc<Origin> {
53    ORIGIN.with_borrow(|ori| {
54        ori.as_ref().cloned().unwrap_or_else(|| DEFAULT_ORIGIN.clone())
55    })
56}
57
58#[derive(Debug, Clone, PartialEq, PartialOrd)]
59pub struct Arg {
60    pub labeled: Option<Option<Expr>>,
61    pub pattern: StructurePattern,
62    pub constraint: Option<Type>,
63}
64
65#[derive(Debug, Clone, PartialEq, PartialOrd)]
66pub enum ModuleKind {
67    Inline(Arc<[Expr]>),
68    Resolved(Arc<[Expr]>),
69    Unresolved,
70}
71
72#[derive(Debug, Clone, PartialEq, PartialOrd)]
73pub struct Bind {
74    pub doc: Option<ArcStr>,
75    pub pattern: StructurePattern,
76    pub typ: Option<Type>,
77    pub export: bool,
78    pub value: Expr,
79}
80
81#[derive(Debug, Clone, PartialEq, PartialOrd)]
82pub struct Lambda {
83    pub args: Arc<[Arg]>,
84    pub vargs: Option<Option<Type>>,
85    pub rtype: Option<Type>,
86    pub constraints: Arc<[(TVar, Type)]>,
87    pub body: Either<Expr, ArcStr>,
88}
89
90#[derive(Debug, Clone, PartialEq, PartialOrd)]
91pub enum ExprKind {
92    Constant(Value),
93    Module { name: ArcStr, export: bool, value: ModuleKind },
94    Do { exprs: Arc<[Expr]> },
95    Use { name: ModPath },
96    Bind(Arc<Bind>),
97    Ref { name: ModPath },
98    Connect { name: ModPath, value: Arc<Expr>, deref: bool },
99    StringInterpolate { args: Arc<[Expr]> },
100    StructRef { source: Arc<Expr>, field: ArcStr },
101    TupleRef { source: Arc<Expr>, field: usize },
102    ArrayRef { source: Arc<Expr>, i: Arc<Expr> },
103    ArraySlice { source: Arc<Expr>, start: Option<Arc<Expr>>, end: Option<Arc<Expr>> },
104    StructWith { source: Arc<Expr>, replace: Arc<[(ArcStr, Expr)]> },
105    Lambda(Arc<Lambda>),
106    TypeDef { name: ArcStr, params: Arc<[(TVar, Option<Type>)]>, typ: Type },
107    TypeCast { expr: Arc<Expr>, typ: Type },
108    Apply { args: Arc<[(Option<ArcStr>, Expr)]>, function: Arc<Expr> },
109    Any { args: Arc<[Expr]> },
110    Array { args: Arc<[Expr]> },
111    Tuple { args: Arc<[Expr]> },
112    Variant { tag: ArcStr, args: Arc<[Expr]> },
113    Struct { args: Arc<[(ArcStr, Expr)]> },
114    Select { arg: Arc<Expr>, arms: Arc<[(Pattern, Expr)]> },
115    Qop(Arc<Expr>),
116    ByRef(Arc<Expr>),
117    Deref(Arc<Expr>),
118    Eq { lhs: Arc<Expr>, rhs: Arc<Expr> },
119    Ne { lhs: Arc<Expr>, rhs: Arc<Expr> },
120    Lt { lhs: Arc<Expr>, rhs: Arc<Expr> },
121    Gt { lhs: Arc<Expr>, rhs: Arc<Expr> },
122    Lte { lhs: Arc<Expr>, rhs: Arc<Expr> },
123    Gte { lhs: Arc<Expr>, rhs: Arc<Expr> },
124    And { lhs: Arc<Expr>, rhs: Arc<Expr> },
125    Or { lhs: Arc<Expr>, rhs: Arc<Expr> },
126    Not { expr: Arc<Expr> },
127    Add { lhs: Arc<Expr>, rhs: Arc<Expr> },
128    Sub { lhs: Arc<Expr>, rhs: Arc<Expr> },
129    Mul { lhs: Arc<Expr>, rhs: Arc<Expr> },
130    Div { lhs: Arc<Expr>, rhs: Arc<Expr> },
131    Mod { lhs: Arc<Expr>, rhs: Arc<Expr> },
132    Sample { lhs: Arc<Expr>, rhs: Arc<Expr> },
133}
134
135impl ExprKind {
136    pub fn to_expr(self, pos: SourcePosition) -> Expr {
137        Expr { id: ExprId::new(), ori: get_origin(), pos, kind: self }
138    }
139
140    /// does not provide any position information or comment
141    pub fn to_expr_nopos(self) -> Expr {
142        Expr { id: ExprId::new(), ori: get_origin(), pos: Default::default(), kind: self }
143    }
144}
145
146#[derive(Debug, Clone, PartialEq, PartialOrd)]
147pub enum Source {
148    File(PathBuf),
149    Netidx(Path),
150    Internal(ArcStr),
151    Unspecified,
152}
153
154impl Default for Source {
155    fn default() -> Self {
156        Self::Unspecified
157    }
158}
159
160impl Source {
161    pub fn is_file(&self) -> bool {
162        match self {
163            Self::File(_) => true,
164            Self::Netidx(_) | Self::Internal(_) | Self::Unspecified => false,
165        }
166    }
167}
168
169// hallowed are the ori
170#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
171pub struct Origin {
172    pub parent: Option<Arc<Origin>>,
173    pub source: Source,
174    pub text: ArcStr,
175}
176
177impl fmt::Display for Origin {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        let flags = PRINT_FLAGS.with(|f| f.get());
180        match &self.source {
181            Source::Unspecified => {
182                if flags.contains(PrintFlag::NoSource) {
183                    write!(f, "in expr")?
184                } else {
185                    write!(f, "in expr {}", self.text)?
186                }
187            }
188            Source::File(n) => write!(f, "in file {n:?}")?,
189            Source::Netidx(n) => write!(f, "in netidx {n}")?,
190            Source::Internal(n) => write!(f, "in module {n}")?,
191        }
192        let mut p = &self.parent;
193        if flags.contains(PrintFlag::NoParents) {
194            Ok(())
195        } else {
196            loop {
197                match p {
198                    None => break Ok(()),
199                    Some(parent) => {
200                        writeln!(f, "")?;
201                        write!(f, "    ")?;
202                        match &parent.source {
203                            Source::Unspecified => {
204                                if flags.contains(PrintFlag::NoSource) {
205                                    write!(f, "included from expr")?
206                                } else {
207                                    write!(f, "included from expr {}", parent.text)?
208                                }
209                            }
210                            Source::File(n) => write!(f, "included from file {n:?}")?,
211                            Source::Netidx(n) => write!(f, "included from netidx {n}")?,
212                            Source::Internal(n) => write!(f, "included from module {n}")?,
213                        }
214                        p = &parent.parent;
215                    }
216                }
217            }
218        }
219    }
220}
221
222#[derive(Debug, Clone)]
223pub struct Expr {
224    pub id: ExprId,
225    pub ori: Arc<Origin>,
226    pub pos: SourcePosition,
227    pub kind: ExprKind,
228}
229
230impl fmt::Display for Expr {
231    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        write!(f, "{}", self.kind)
233    }
234}
235
236impl PartialOrd for Expr {
237    fn partial_cmp(&self, rhs: &Expr) -> Option<Ordering> {
238        self.kind.partial_cmp(&rhs.kind)
239    }
240}
241
242impl PartialEq for Expr {
243    fn eq(&self, rhs: &Expr) -> bool {
244        self.kind.eq(&rhs.kind)
245    }
246}
247
248impl Eq for Expr {}
249
250impl Serialize for Expr {
251    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
252    where
253        S: Serializer,
254    {
255        serializer.serialize_str(&self.to_string())
256    }
257}
258
259impl Default for Expr {
260    fn default() -> Self {
261        ExprKind::Constant(Value::Null).to_expr(Default::default())
262    }
263}
264
265impl FromStr for Expr {
266    type Err = anyhow::Error;
267
268    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
269        parser::parse_one(s)
270    }
271}
272
273#[derive(Clone, Copy)]
274struct ExprVisitor;
275
276impl<'de> Visitor<'de> for ExprVisitor {
277    type Value = Expr;
278
279    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
280        write!(formatter, "expected expression")
281    }
282
283    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
284    where
285        E: de::Error,
286    {
287        Expr::from_str(s).map_err(de::Error::custom)
288    }
289
290    fn visit_borrowed_str<E>(self, s: &'de str) -> Result<Self::Value, E>
291    where
292        E: de::Error,
293    {
294        Expr::from_str(s).map_err(de::Error::custom)
295    }
296
297    fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
298    where
299        E: de::Error,
300    {
301        Expr::from_str(&s).map_err(de::Error::custom)
302    }
303}
304
305impl<'de> Deserialize<'de> for Expr {
306    fn deserialize<D>(de: D) -> Result<Self, D::Error>
307    where
308        D: Deserializer<'de>,
309    {
310        de.deserialize_str(ExprVisitor)
311    }
312}
313
314impl Expr {
315    pub fn new(kind: ExprKind, pos: SourcePosition) -> Self {
316        Expr { id: ExprId::new(), ori: get_origin(), pos, kind }
317    }
318
319    pub fn to_string_pretty(&self, col_limit: usize) -> String {
320        self.kind.to_string_pretty(col_limit)
321    }
322
323    /// fold over self and all of self's sub expressions
324    pub fn fold<T, F: FnMut(T, &Self) -> T>(&self, init: T, f: &mut F) -> T {
325        let init = f(init, self);
326        match &self.kind {
327            ExprKind::Constant(_)
328            | ExprKind::Use { .. }
329            | ExprKind::Ref { .. }
330            | ExprKind::StructRef { .. }
331            | ExprKind::TupleRef { .. }
332            | ExprKind::TypeDef { .. } => init,
333            ExprKind::Module { value: ModuleKind::Inline(e), .. } => {
334                e.iter().fold(init, |init, e| e.fold(init, f))
335            }
336            ExprKind::Module { value: ModuleKind::Resolved(exprs), .. } => {
337                exprs.iter().fold(init, |init, e| e.fold(init, f))
338            }
339            ExprKind::Module { value: ModuleKind::Unresolved, .. } => init,
340            ExprKind::Do { exprs } => exprs.iter().fold(init, |init, e| e.fold(init, f)),
341            ExprKind::Bind(b) => b.value.fold(init, f),
342            ExprKind::StructWith { replace, .. } => {
343                replace.iter().fold(init, |init, (_, e)| e.fold(init, f))
344            }
345            ExprKind::Connect { value, .. } => value.fold(init, f),
346            ExprKind::Lambda(l) => match &l.body {
347                Either::Left(e) => e.fold(init, f),
348                Either::Right(_) => init,
349            },
350            ExprKind::TypeCast { expr, .. } => expr.fold(init, f),
351            ExprKind::Apply { args, function: _ } => {
352                args.iter().fold(init, |init, (_, e)| e.fold(init, f))
353            }
354            ExprKind::Any { args }
355            | ExprKind::Array { args }
356            | ExprKind::Tuple { args }
357            | ExprKind::Variant { args, .. }
358            | ExprKind::StringInterpolate { args } => {
359                args.iter().fold(init, |init, e| e.fold(init, f))
360            }
361            ExprKind::ArrayRef { source, i } => {
362                let init = source.fold(init, f);
363                i.fold(init, f)
364            }
365            ExprKind::ArraySlice { source, start, end } => {
366                let init = source.fold(init, f);
367                let init = match start {
368                    None => init,
369                    Some(e) => e.fold(init, f),
370                };
371                match end {
372                    None => init,
373                    Some(e) => e.fold(init, f),
374                }
375            }
376            ExprKind::Struct { args } => {
377                args.iter().fold(init, |init, (_, e)| e.fold(init, f))
378            }
379            ExprKind::Select { arg, arms } => {
380                let init = arg.fold(init, f);
381                arms.iter().fold(init, |init, (p, e)| {
382                    let init = match p.guard.as_ref() {
383                        None => init,
384                        Some(g) => g.fold(init, f),
385                    };
386                    e.fold(init, f)
387                })
388            }
389            ExprKind::Qop(e)
390            | ExprKind::ByRef(e)
391            | ExprKind::Deref(e)
392            | ExprKind::Not { expr: e } => e.fold(init, f),
393            ExprKind::Add { lhs, rhs }
394            | ExprKind::Sub { lhs, rhs }
395            | ExprKind::Mul { lhs, rhs }
396            | ExprKind::Div { lhs, rhs }
397            | ExprKind::Mod { lhs, rhs }
398            | ExprKind::And { lhs, rhs }
399            | ExprKind::Or { lhs, rhs }
400            | ExprKind::Eq { lhs, rhs }
401            | ExprKind::Ne { lhs, rhs }
402            | ExprKind::Gt { lhs, rhs }
403            | ExprKind::Lt { lhs, rhs }
404            | ExprKind::Gte { lhs, rhs }
405            | ExprKind::Lte { lhs, rhs }
406            | ExprKind::Sample { lhs, rhs } => {
407                let init = lhs.fold(init, f);
408                rhs.fold(init, f)
409            }
410        }
411    }
412}
413
414pub struct ErrorContext(pub Expr);
415
416impl fmt::Display for ErrorContext {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        use std::fmt::Write;
419        const MAX: usize = 38;
420        thread_local! {
421            static BUF: RefCell<String> = RefCell::new(String::new());
422        }
423        BUF.with_borrow_mut(|buf| {
424            buf.clear();
425            write!(buf, "{}", self.0).unwrap();
426            if buf.len() <= MAX {
427                write!(f, "at: {}, in: {buf}", self.0.pos)
428            } else {
429                let mut end = MAX;
430                while !buf.is_char_boundary(end) {
431                    end += 1
432                }
433                let buf = &buf[0..end];
434                write!(f, "at: {}, in: {buf}..", self.0.pos)
435            }
436        })
437    }
438}