graphix_compiler/expr/
mod.rs

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