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