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