Skip to main content

graphix_compiler/expr/
mod.rs

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