Skip to main content

scheme_rs/
exceptions.rs

1//! Exceptional situations and conditions.
2//!
3//! Scheme has two distinct concepts: conditions and exceptions. Exceptions are
4//! values that values passed to the `raise` and `raise-continuable` procedures
5//! and can be any [`Value`]. Conditions are records that contain information
6//! describing an erroneous situation or _condition_.
7//!
8//! Conditions in Scheme are either [simple](`SimpleCondition`) or
9//! [compound](`CompoundCondition`). Scheme-rs provides the ability to inspect
10//! conditions without discerning whether they or simple or compound. Using the
11//! [`condition`](Exception::condition) method, a specific condition and thus
12//! its associated information can be extracted from the condition.
13//!
14//! For example, a common condition is the [`&trace`](StackTrace) condition,
15//! which can be used to extract a stack trace for the exception:
16//!
17//! ```
18//! # use scheme_rs::{exceptions::{Exception, Message, SyntaxViolation, StackTrace}, gc::Gc, syntax::Syntax};
19//! // Code from scheme-rs repl to print errors:
20//! fn print_exception(exception: Exception) {
21//!     let Ok(conditions) = exception.simple_conditions() else {
22//!         println!(
23//!             "Exception occurred with a non-condition value: {:?}",
24//!             exception.0
25//!         );
26//!         return;
27//!     };
28//!     println!("Uncaught exception:");
29//!     for condition in conditions.into_iter() {
30//!         if let Some(message) = condition.cast_to_rust_type::<Message>() {
31//!             println!(" - Message: {}", message.message);
32//!         } else if let Some(syntax) = condition.cast_to_rust_type::<SyntaxViolation>() {
33//!             println!(" - Syntax error in form: {:?}", syntax.form);
34//!             if let Some(subform) = syntax.subform.as_ref() {
35//!                 println!("   (subform: {subform:?})");
36//!             }
37//!         } else if let Some(trace) = condition.cast_to_rust_type::<StackTrace>() {
38//!             println!(" - Stack trace:");
39//!             for (i, trace) in trace.trace.iter().enumerate() {
40//!                 let syntax = trace.cast_to_scheme_type::<Gc<Syntax>>().unwrap();
41//!                 let span = syntax.span();
42//!                 let func_name = syntax.as_ident().unwrap().symbol();
43//!                 println!("{:>6}: {func_name}:{span}", i + 1);
44//!             }
45//!         } else {
46//!             println!(" - Condition: {condition:?}");
47//!         }
48//!     }
49//! }
50//! ```
51
52use crate::{
53    gc::{Gc, GcInner, Trace},
54    lists::slice_to_list,
55    ports::{IoDecodingError, IoEncodingError, IoError, IoReadError, IoWriteError},
56    proc::{Application, ContBarrier, DynStackElem, FuncPtr, Procedure, pop_dyn_stack},
57    records::{Record, RecordTypeDescriptor, SchemeCompatible, rtd},
58    registry::{bridge, cps_bridge},
59    runtime::{Runtime, RuntimeInner},
60    symbols::Symbol,
61    syntax::{Identifier, Span, Syntax, parse::ParseSyntaxError},
62    value::{UnpackedValue, Value},
63    vectors::Vector,
64};
65use by_address::ByAddress;
66use parking_lot::RwLock;
67use scheme_rs_macros::runtime_fn;
68use std::{collections::HashMap, convert::Infallible, fmt, ops::Range, sync::Arc};
69
70/// A macro for easily creating new condition types.
71pub use scheme_rs_macros::define_condition_type;
72
73impl fmt::Display for Exception {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        <Value as fmt::Debug>::fmt(&self.0, f)
76    }
77}
78
79/// A signal of some sort of erroneous condition.
80#[derive(Debug, Clone, Trace)]
81pub struct Exception(pub Value);
82
83impl Exception {
84    pub fn error(message: impl fmt::Display) -> Self {
85        Self(Value::from(Record::from_rust_type(
86            CompoundCondition::from((Assertion::new(), Message::new(message.to_string()))),
87        )))
88    }
89
90    pub fn syntax(form: Syntax, subform: Option<Syntax>) -> Self {
91        Self(Value::from(Record::from_rust_type(SyntaxViolation::new(
92            form, subform,
93        ))))
94    }
95
96    pub fn undefined(ident: Identifier) -> Self {
97        Self(Value::from(Record::from_rust_type(
98            CompoundCondition::from((
99                Undefined::new(),
100                Message::new(format!("undefined variable {}", ident.sym)),
101            )),
102        )))
103    }
104
105    pub fn type_error(expected: &str, provided: &str) -> Self {
106        Self(Value::from(Record::from_rust_type(
107            CompoundCondition::from((
108                Assertion::new(),
109                Message::new(format!(
110                    "expected value of type {expected}, provided {provided}"
111                )),
112            )),
113        )))
114    }
115
116    pub fn invalid_operator(provided: &str) -> Self {
117        Self(Value::from(Record::from_rust_type(
118            CompoundCondition::from((
119                Assertion::new(),
120                Message::new(format!(
121                    "invalid operator: expected procedure, provided {provided}"
122                )),
123            )),
124        )))
125    }
126
127    pub fn invalid_index(index: usize, len: usize) -> Self {
128        Self(Value::from(Record::from_rust_type(
129            CompoundCondition::from((
130                Assertion::new(),
131                Message::new(format!(
132                    "index {index} out of bounds for collection of size {len}"
133                )),
134            )),
135        )))
136    }
137
138    pub fn invalid_range(range: Range<usize>, len: usize) -> Self {
139        Self(Value::from(Record::from_rust_type(
140            CompoundCondition::from((
141                Assertion::new(),
142                Message::new(format!(
143                    "range {range:?} out of bounds for collection of size {len}"
144                )),
145            )),
146        )))
147    }
148
149    pub fn wrong_num_of_unicode_chars(expected: usize, provided: usize) -> Self {
150        Self(Value::from(Record::from_rust_type(
151            CompoundCondition::from((
152                Assertion::new(),
153                Message::new(format!(
154                    "expected {expected} unicode characters from transform, received {provided}"
155                )),
156            )),
157        )))
158    }
159
160    pub fn wrong_num_of_args(expected: usize, provided: usize) -> Self {
161        Self(Value::from(Record::from_rust_type(
162            CompoundCondition::from((
163                Assertion::new(),
164                Message::new(format!(
165                    "expected {expected} arguments, provided {provided}"
166                )),
167            )),
168        )))
169    }
170
171    pub fn wrong_num_of_var_args(expected: Range<usize>, provided: usize) -> Self {
172        Self(Value::from(Record::from_rust_type(
173            CompoundCondition::from((
174                Assertion::new(),
175                Message::new(format!(
176                    "expected {} to {} arguments, provided {provided}",
177                    expected.start, expected.end
178                )),
179            )),
180        )))
181    }
182
183    pub fn implementation_restriction(msg: impl fmt::Display) -> Self {
184        Self(Value::from_rust_type(CompoundCondition::from((
185            Assertion::new(),
186            ImplementationRestriction::new(),
187            Message::new(msg),
188        ))))
189    }
190
191    /// For when we cannot convert a value into the requested type.
192    ///
193    /// Example: Integer to a Complex
194    pub fn conversion_error(expected: &str, provided: &str) -> Self {
195        Self(Value::from(Record::from_rust_type(
196            CompoundCondition::from((
197                Assertion::new(),
198                Message::new(format!("cannot convert {provided} into {expected}")),
199            )),
200        )))
201    }
202
203    /// For when we cannot represent the value into the requested type.
204    ///
205    /// Example: an u128 number as an u8
206    pub fn not_representable(value: &str, r#type: &str) -> Self {
207        Self(Value::from(Record::from_rust_type(
208            CompoundCondition::from((
209                Assertion::new(),
210                Message::new(format!("cannot represent '{value}' as {type}")),
211            )),
212        )))
213    }
214
215    pub fn io_error(message: impl fmt::Display) -> Self {
216        Self(Value::from(Record::from_rust_type(
217            CompoundCondition::from((IoError::new(), Assertion::new(), Message::new(message))),
218        )))
219    }
220
221    pub fn io_read_error(message: impl fmt::Display) -> Self {
222        Self(Value::from(Record::from_rust_type(
223            CompoundCondition::from((IoReadError::new(), Assertion::new(), Message::new(message))),
224        )))
225    }
226
227    pub fn io_write_error(message: impl fmt::Display) -> Self {
228        Self(Value::from(Record::from_rust_type(
229            CompoundCondition::from((IoWriteError::new(), Assertion::new(), Message::new(message))),
230        )))
231    }
232
233    pub fn io_decoding_error(message: impl fmt::Display, port: Value) -> Self {
234        Self(Value::from(Record::from_rust_type(
235            CompoundCondition::from((
236                IoDecodingError::new(port),
237                Assertion::new(),
238                Message::new(message),
239            )),
240        )))
241    }
242
243    pub fn io_encoding_error(message: impl fmt::Display, port: Value, chr: char) -> Self {
244        Self(Value::from(Record::from_rust_type(
245            CompoundCondition::from((
246                IoEncodingError::new(port, chr),
247                Assertion::new(),
248                Message::new(message),
249            )),
250        )))
251    }
252
253    pub fn invalid_record_index(k: usize) -> Self {
254        Self::error(format!("invalid record index: {k}"))
255    }
256
257    pub fn add_condition(self, condition: impl SchemeCompatible) -> Self {
258        let mut conditions = if let Some(compound) = self.0.cast_to_rust_type::<CompoundCondition>()
259        {
260            compound.0.clone()
261        } else {
262            vec![self.0]
263        };
264
265        conditions.push(Value::from(Record::from_rust_type(condition)));
266
267        Self(Value::from(Record::from_rust_type(CompoundCondition(
268            conditions,
269        ))))
270    }
271
272    pub fn simple_conditions(&self) -> Result<Vec<Value>, Exception> {
273        if self.0.cast_to_rust_type::<SimpleCondition>().is_some() {
274            Ok(vec![self.0.clone()])
275        } else if let Some(compound_condition) = self.0.cast_to_rust_type::<CompoundCondition>() {
276            Ok(compound_condition.0.clone())
277        } else {
278            Err(Exception::error("not a simple or compound condition"))
279        }
280    }
281
282    pub fn condition<T: SchemeCompatible>(&self) -> Result<Option<Gc<T>>, Exception> {
283        for condition in self.simple_conditions()?.into_iter() {
284            if let Some(condition) = condition.cast_to_rust_type::<T>() {
285                return Ok(Some(condition));
286            }
287        }
288        Ok(None)
289    }
290
291    pub fn pretty_print(
292        &self,
293        source_cache: &mut SourceCache,
294        f: &mut impl fmt::Write,
295    ) -> fmt::Result {
296        let Ok(conditions) = self.simple_conditions() else {
297            return writeln!(
298                f,
299                "Exception occurred with a non-condition value: {:?}",
300                self.0
301            );
302        };
303
304        writeln!(f, "Uncaught exception:")?;
305        for condition in conditions.into_iter().rev() {
306            if let Some(message) = condition.cast_to_rust_type::<Message>() {
307                writeln!(f, " - Message: {}", message.message)?;
308            } else if let Some(syntax) = condition.cast_to_rust_type::<SyntaxViolation>() {
309                writeln!(f, " - Syntax error in form: {:?}", syntax.form)?;
310                source_cache.pretty_print_condition(syntax.as_ref(), f)?;
311            } else if let Some(trace) = condition.cast_to_rust_type::<StackTrace>() {
312                if !trace.trace.is_empty() {
313                    writeln!(f, " - Trace:")?;
314                    source_cache.pretty_print_condition(trace.as_ref(), f)?;
315                }
316            } else if condition.cast_to_rust_type::<Assertion>().is_some() {
317                writeln!(f, " - Assertion failed")?;
318            } else {
319                writeln!(f, " - Condition: {condition:?}")?;
320            }
321        }
322        Ok(())
323    }
324}
325
326impl From<&'_ Value> for Option<Exception> {
327    fn from(value: &'_ Value) -> Self {
328        if let UnpackedValue::Record(record) = &*value.unpacked_ref()
329            && let rtd = record.rtd()
330            && (RecordTypeDescriptor::is_subtype_of(&rtd, &SimpleCondition::rtd())
331                || RecordTypeDescriptor::is_subtype_of(&rtd, &CompoundCondition::rtd()))
332        {
333            Some(Exception(value.clone()))
334        } else {
335            None
336        }
337    }
338}
339
340impl From<std::io::Error> for Exception {
341    fn from(value: std::io::Error) -> Self {
342        Self::from((IoError::new(), Message::new(format!("{value:?}"))))
343    }
344}
345
346impl From<SimpleCondition> for Exception {
347    fn from(simple: SimpleCondition) -> Self {
348        Self(Value::from(Record::from_rust_type(simple)))
349    }
350}
351
352impl From<Warning> for Exception {
353    fn from(warning: Warning) -> Self {
354        Self(Value::from(Record::from_rust_type(warning)))
355    }
356}
357
358impl From<Serious> for Exception {
359    fn from(serious: Serious) -> Self {
360        Self(Value::from(Record::from_rust_type(serious)))
361    }
362}
363
364impl From<Message> for Exception {
365    fn from(message: Message) -> Self {
366        Self(Value::from(Record::from_rust_type(message)))
367    }
368}
369
370impl From<Infallible> for Exception {
371    fn from(infallible: Infallible) -> Self {
372        match infallible {}
373    }
374}
375
376impl From<ParseSyntaxError> for Exception {
377    fn from(error: ParseSyntaxError) -> Self {
378        Self::from((Lexical::new(), Message::new(error)))
379    }
380}
381
382macro_rules! impl_into_condition_for {
383    ($for:ty) => {
384        impl From<$for> for Exception {
385            fn from(e: $for) -> Self {
386                Self::error(e.to_string())
387            }
388        }
389    };
390}
391
392pub trait PrettyCondition {
393    fn span(&self) -> Span;
394
395    fn pretty_print(&self, _w: &mut impl fmt::Write) -> fmt::Result {
396        Ok(())
397    }
398}
399
400/// Pretty print a condition with source information, heavily inspired by
401/// [T8Err::render](https://github.com/xnacly/tango8/blob/master/shared/src/err.rs#L11)
402fn print_lines_with_offense_from_span(
403    span: &Span,
404    lines: Option<&[String]>,
405    w: &mut impl fmt::Write,
406) -> fmt::Result {
407    let Some(lines) = lines else {
408        return Ok(());
409    };
410
411    writeln!(w, "--> {}:{}:{}:", span.file, span.line, span.column)?;
412    let start = span.line.saturating_sub(2);
413    let end = (span.line + 3).min(lines.len() as u32);
414
415    for i in start..end {
416        //  <line num> | <line content>
417        //  +1, because 0 addressing
418        writeln!(w, "{:03} | {}", i + 1, lines[i as usize])?;
419        //  +1, because span.line is somehow not 0 addressed :O
420        if i + 1 == span.line {
421            writeln!(w, "    | {}~ here", " ".repeat(span.column))?; //  | <spacing until column>~
422        }
423    }
424    Ok(())
425}
426
427impl_into_condition_for!(std::num::TryFromIntError);
428
429#[derive(Copy, Clone, Default, Trace)]
430pub struct SimpleCondition;
431
432impl SimpleCondition {
433    pub fn new() -> Self {
434        Self
435    }
436}
437
438impl SchemeCompatible for SimpleCondition {
439    fn rtd() -> Arc<RecordTypeDescriptor> {
440        rtd!(
441            lib: "(rnrs conditions (6))",
442            name: "&condition",
443            constructor: || Ok(SimpleCondition)
444        )
445    }
446}
447
448impl fmt::Debug for SimpleCondition {
449    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        Ok(())
451    }
452}
453
454#[bridge(name = "condition?", lib = "(rnrs conditions (6))")]
455pub fn condition_pred(obj: &Value) -> Result<Vec<Value>, Exception> {
456    let is_condition = obj.cast_to_rust_type::<SimpleCondition>().is_some()
457        || obj.cast_to_rust_type::<CompoundCondition>().is_some();
458    Ok(vec![Value::from(is_condition)])
459}
460
461define_condition_type!(
462    lib: "(rnrs conditions (6))",
463    rust_name: Message,
464    scheme_name: "&message",
465    parent: SimpleCondition,
466    fields: {
467        message: String,
468    },
469    constructor: |message| {
470        Ok(Message { parent: Gc::new(SimpleCondition::new()), message: message.to_string() })
471    },
472    debug: |this, f| {
473        write!(f, " ")?;
474        this.message.fmt(f)
475    }
476);
477
478impl Message {
479    pub fn new(message: impl fmt::Display) -> Self {
480        Self {
481            parent: Gc::new(SimpleCondition::new()),
482            message: message.to_string(),
483        }
484    }
485}
486
487define_condition_type!(
488    lib: "(rnrs conditions (6))",
489    rust_name: Warning,
490    scheme_name: "&warning",
491    parent: SimpleCondition,
492);
493
494impl Warning {
495    pub fn new() -> Self {
496        Self {
497            parent: Gc::new(SimpleCondition::new()),
498        }
499    }
500}
501
502impl Default for Warning {
503    fn default() -> Self {
504        Self::new()
505    }
506}
507
508define_condition_type!(
509    lib: "(rnrs conditions (6))",
510    rust_name: Serious,
511    scheme_name: "&serious",
512    parent: SimpleCondition,
513);
514
515impl Serious {
516    pub fn new() -> Self {
517        Self {
518            parent: Gc::new(SimpleCondition::new()),
519        }
520    }
521}
522
523impl Default for Serious {
524    fn default() -> Self {
525        Self::new()
526    }
527}
528
529define_condition_type!(
530    lib: "(rnrs conditions (6))",
531    rust_name: StackTrace,
532    scheme_name: "&trace",
533    parent: SimpleCondition,
534    fields: {
535        trace: Vector,
536    },
537    constructor: |trace| {
538        Ok(StackTrace {
539            parent: Gc::new(SimpleCondition::new()),
540            trace: trace.clone().try_into()?,
541        })
542    },
543    debug: |this, f| {
544        for trace in &*this.trace.0.vec.read() {
545            write!(f, " {trace}")?;
546        }
547        Ok(())
548    }
549);
550
551impl PrettyCondition for StackTrace {
552    fn span(&self) -> Span {
553        let first = self.trace.first().unwrap();
554        let Some(syntax) = first.cast_to_scheme_type::<Gc<Syntax>>() else {
555            return Span::default();
556        };
557        syntax.span().clone()
558    }
559
560    fn pretty_print(&self, w: &mut impl fmt::Write) -> fmt::Result {
561        for (i, trace) in self.trace.iter().enumerate() {
562            let Some(syntax) = trace.cast_to_scheme_type::<Gc<Syntax>>() else {
563                continue;
564            };
565            let span = syntax.span();
566            let func_name = syntax.as_ident().unwrap().symbol();
567            writeln!(w, "{:>6}: {func_name}:{span}", i + 1)?;
568        }
569        Ok(())
570    }
571}
572
573impl StackTrace {
574    pub fn new(trace: Vec<Value>) -> Self {
575        Self {
576            parent: Gc::new(SimpleCondition::new()),
577            trace: Vector::from(trace),
578        }
579    }
580
581    pub fn trace(&self) -> Vec<Syntax> {
582        todo!()
583    }
584}
585
586define_condition_type!(
587    lib: "(rnrs conditions (6))",
588    rust_name: Error,
589    scheme_name: "&error",
590    parent: Serious,
591);
592
593impl Error {
594    pub fn new() -> Self {
595        Self {
596            parent: Gc::new(Serious::new()),
597        }
598    }
599}
600
601impl Default for Error {
602    fn default() -> Self {
603        Self::new()
604    }
605}
606
607define_condition_type!(
608    lib: "(rnrs conditions (6))",
609    rust_name: ImportError,
610    scheme_name: "&import",
611    parent: Error,
612    fields: {
613        library: String,
614    },
615    constructor: |lib| {
616        Ok(ImportError {  parent: Gc::new(Error::new()), library: lib.to_string() })
617    },
618    debug: |this, f| {
619        write!(f, " library: {}", this.library)
620    }
621);
622
623impl ImportError {
624    pub fn new(library: String) -> Self {
625        Self {
626            parent: Gc::new(Error::new()),
627            library,
628        }
629    }
630}
631
632define_condition_type!(
633    lib: "(rnrs conditions (6))",
634    rust_name: Violation,
635    scheme_name: "&violation",
636    parent: Serious,
637);
638
639impl Violation {
640    pub fn new() -> Self {
641        Self {
642            parent: Gc::new(Serious::new()),
643        }
644    }
645}
646
647impl Default for Violation {
648    fn default() -> Self {
649        Self::new()
650    }
651}
652
653define_condition_type!(
654    lib: "(rnrs conditions (6))",
655    rust_name: Assertion,
656    scheme_name: "&assertion",
657    parent: Violation
658);
659
660impl Assertion {
661    pub fn new() -> Self {
662        Self {
663            parent: Gc::new(Violation::new()),
664        }
665    }
666}
667
668impl Default for Assertion {
669    fn default() -> Self {
670        Self::new()
671    }
672}
673
674define_condition_type!(
675    lib: "(rnrs conditions (6))",
676    rust_name: Irritants,
677    scheme_name: "&irritants",
678    parent: SimpleCondition,
679    fields: {
680        irritants: Value,
681    },
682    constructor: |irritants| {
683        Ok(Irritants { parent: Gc::new(SimpleCondition::new()), irritants })
684    },
685    debug: |this, f| {
686        write!(f, " irritants: {:?}", this.irritants)
687    }
688);
689
690impl Irritants {
691    pub fn new(irritants: Value) -> Self {
692        Irritants {
693            parent: Gc::new(SimpleCondition::new()),
694            irritants,
695        }
696    }
697}
698
699define_condition_type!(
700    lib: "(rnrs conditions (6))",
701    rust_name: Who,
702    scheme_name: "&who",
703    parent: SimpleCondition,
704    fields: {
705        who: Value,
706    },
707    constructor: |who| {
708        Ok(Who { parent: Gc::new(SimpleCondition::new()), who, })
709    },
710    debug: |this, f| {
711        write!(f, " who: {:?}", this.who)
712    }
713);
714
715impl Who {
716    pub fn new(who: Value) -> Self {
717        Who {
718            parent: Gc::new(SimpleCondition::new()),
719            who,
720        }
721    }
722}
723
724define_condition_type!(
725    lib: "(rnrs conditions (6))",
726    rust_name: NonContinuable,
727    scheme_name: "&non-continuable",
728    parent: Violation,
729);
730
731impl Default for NonContinuable {
732    fn default() -> Self {
733        Self {
734            parent: Gc::new(Violation::new()),
735        }
736    }
737}
738define_condition_type!(
739    lib: "(rnrs conditions (6))",
740    rust_name: ImplementationRestriction,
741    scheme_name: "&implementation-restriction",
742    parent: Violation,
743);
744
745impl ImplementationRestriction {
746    pub fn new() -> Self {
747        Self::default()
748    }
749}
750
751impl Default for ImplementationRestriction {
752    fn default() -> Self {
753        Self {
754            parent: Gc::new(Violation::new()),
755        }
756    }
757}
758
759define_condition_type!(
760    lib: "(rnrs conditions (6))",
761    rust_name: Lexical,
762    scheme_name: "&lexical",
763    parent: Violation,
764);
765
766impl Lexical {
767    pub fn new() -> Self {
768        Self {
769            parent: Gc::new(Violation::new()),
770        }
771    }
772}
773
774impl Default for Lexical {
775    fn default() -> Self {
776        Self::new()
777    }
778}
779
780define_condition_type!(
781    lib: "(rnrs conditions (6))",
782    rust_name: SyntaxViolation,
783    scheme_name: "&syntax",
784    parent: Violation,
785    fields: {
786        form: Value,
787        subform: Option<Value>,
788    },
789    constructor: |form, subform| {
790        let subform = if subform.is_true() { Some(subform) } else { None };
791        Ok(SyntaxViolation { parent: Gc::new(Violation::new()), form, subform })
792    },
793);
794
795impl PrettyCondition for SyntaxViolation {
796    fn span(&self) -> Span {
797        self.subform
798            .as_ref()
799            .unwrap_or(&self.form)
800            .cast_to_scheme_type::<Gc<Syntax>>()
801            .unwrap()
802            .span()
803            .clone()
804    }
805}
806
807impl SyntaxViolation {
808    pub fn new(form: Syntax, subform: Option<Syntax>) -> Self {
809        Self {
810            parent: Gc::new(Violation::new()),
811            form: Value::from(form),
812            subform: subform.map(Value::from),
813        }
814    }
815
816    pub fn new_from_values(form: Value, subform: Option<Value>) -> Self {
817        Self {
818            parent: Gc::new(Violation::new()),
819            form,
820            subform,
821        }
822    }
823}
824
825define_condition_type!(
826    lib: "(rnrs conditions (6))",
827    rust_name: Undefined,
828    scheme_name: "&undefined",
829    parent: Violation
830);
831
832impl Undefined {
833    pub fn new() -> Self {
834        Self {
835            parent: Gc::new(Violation::new()),
836        }
837    }
838}
839
840impl Default for Undefined {
841    fn default() -> Self {
842        Self::new()
843    }
844}
845
846#[derive(Clone, Trace)]
847pub struct CompoundCondition(pub(crate) Vec<Value>);
848
849impl SchemeCompatible for CompoundCondition {
850    fn rtd() -> Arc<RecordTypeDescriptor> {
851        rtd!(
852            lib: "(rnrs conditions (6))",
853            name: "compound-condition",
854            sealed: true,
855            opaque: true
856        )
857    }
858}
859
860impl fmt::Debug for CompoundCondition {
861    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
862        for cond in self.0.iter() {
863            write!(f, " ")?;
864            cond.fmt(f)?;
865        }
866        Ok(())
867    }
868}
869
870impl<T> From<T> for Exception
871where
872    CompoundCondition: From<T>,
873{
874    fn from(value: T) -> Self {
875        Self(Value::from(Record::from_rust_type(
876            CompoundCondition::from(value),
877        )))
878    }
879}
880
881impl<A, B> From<(A, B)> for CompoundCondition
882where
883    A: SchemeCompatible,
884    B: SchemeCompatible,
885{
886    fn from(value: (A, B)) -> Self {
887        Self(vec![
888            Value::from(Record::from_rust_type(value.0)),
889            Value::from(Record::from_rust_type(value.1)),
890        ])
891    }
892}
893
894impl<A, B, C> From<(A, B, C)> for CompoundCondition
895where
896    A: SchemeCompatible,
897    B: SchemeCompatible,
898    C: SchemeCompatible,
899{
900    fn from(value: (A, B, C)) -> Self {
901        Self(vec![
902            Value::from(Record::from_rust_type(value.0)),
903            Value::from(Record::from_rust_type(value.1)),
904            Value::from(Record::from_rust_type(value.2)),
905        ])
906    }
907}
908
909#[bridge(name = "condition", lib = "(rnrs conditions (6))")]
910pub fn condition(conditions: &[Value]) -> Result<Vec<Value>, Exception> {
911    match conditions {
912        // TODO: Check if this is a condition
913        [simple_condition] => Ok(vec![simple_condition.clone()]),
914        conditions => Ok(vec![Value::from(Record::from_rust_type(
915            CompoundCondition(conditions.to_vec()),
916        ))]),
917    }
918}
919
920#[bridge(name = "simple-conditions", lib = "(rnrs conditions (6))")]
921pub fn simple_conditions(condition: &Value) -> Result<Vec<Value>, Exception> {
922    Ok(vec![slice_to_list(
923        &Exception(condition.clone()).simple_conditions()?,
924    )])
925}
926
927#[doc(hidden)]
928#[cps_bridge(
929    def = "with-exception-handler handler thunk",
930    lib = "(rnrs exceptions (6))"
931)]
932pub fn with_exception_handler(
933    runtime: &Runtime,
934    _env: &[Value],
935    args: &[Value],
936    _rest_args: &[Value],
937    barrier: &mut ContBarrier,
938    k: Value,
939) -> Result<Application, Exception> {
940    let [handler, thunk] = args else {
941        unreachable!();
942    };
943
944    let handler: Procedure = handler.clone().try_into()?;
945    let thunk: Procedure = thunk.clone().try_into()?;
946
947    barrier.push_dyn_stack(DynStackElem::ExceptionHandler(handler));
948
949    let k_proc: Procedure = k.clone().try_into().unwrap();
950    let (req_args, var) = k_proc.get_formals();
951
952    let k = barrier.new_k(
953        runtime.clone(),
954        vec![k.clone()],
955        pop_dyn_stack,
956        req_args,
957        var,
958    );
959
960    Ok(Application::new(thunk, vec![Value::from(k)]))
961}
962
963#[doc(hidden)]
964#[cps_bridge(def = "raise obj", lib = "(rnrs exceptions (6))")]
965pub fn raise_builtin(
966    runtime: &Runtime,
967    _env: &[Value],
968    args: &[Value],
969    _rest_args: &[Value],
970    barrier: &mut ContBarrier,
971    _k: Value,
972) -> Result<Application, Exception> {
973    Ok(raise(runtime.clone(), args[0].clone(), barrier))
974}
975
976/// Raises a non-continuable exception to the current exception handler.
977pub fn raise(runtime: Runtime, raised: Value, barrier: &mut ContBarrier) -> Application {
978    let raised = if let Some(condition) = raised.cast_to_scheme_type::<Exception>() {
979        let trace = barrier.current_marks(Symbol::intern("trace"));
980        Value::from(condition.add_condition(StackTrace::new(trace)))
981    } else {
982        raised
983    };
984
985    Application::new(
986        barrier.new_k(runtime, vec![raised], unwind_to_exception_handler, 0, false),
987        Vec::new(),
988    )
989}
990
991#[runtime_fn]
992unsafe extern "C" fn raise_rt(
993    runtime: *mut GcInner<RwLock<RuntimeInner>>,
994    raised: *const (),
995    barrier: *mut ContBarrier,
996) -> *mut Application {
997    unsafe {
998        let runtime = Runtime::from_raw_inc_rc(runtime);
999        let raised = Value::from_raw(raised);
1000        Box::into_raw(Box::new(raise(
1001            runtime,
1002            raised,
1003            barrier.as_mut().unwrap_unchecked(),
1004        )))
1005    }
1006}
1007
1008unsafe extern "C" fn unwind_to_exception_handler(
1009    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1010    env: *const Value,
1011    _args: *const Value,
1012    barrier: *mut ContBarrier,
1013) -> *mut Application {
1014    unsafe {
1015        // env[0] is the raised value:
1016        let raised = env.as_ref().unwrap().clone();
1017
1018        let barrier = barrier.as_mut().unwrap_unchecked();
1019
1020        loop {
1021            let app = match barrier.pop_dyn_stack() {
1022                None => {
1023                    // If the stack is empty, we should return the error
1024                    Application::halt_err(raised)
1025                }
1026                Some(DynStackElem::Winder(winder)) => {
1027                    // If this is a winder, we should call the out winder while unwinding
1028                    Application::new(
1029                        winder.out_thunk,
1030                        vec![Value::from(barrier.new_k(
1031                            Runtime::from_raw_inc_rc(runtime),
1032                            vec![raised],
1033                            unwind_to_exception_handler,
1034                            0,
1035                            false,
1036                        ))],
1037                    )
1038                }
1039                Some(DynStackElem::ExceptionHandler(handler)) => Application::new(
1040                    handler,
1041                    vec![
1042                        raised.clone(),
1043                        Value::from(barrier.new_k(
1044                            Runtime::from_raw_inc_rc(runtime),
1045                            vec![raised],
1046                            reraise_exception,
1047                            0,
1048                            true,
1049                        )),
1050                    ],
1051                ),
1052                _ => continue,
1053            };
1054            return Box::into_raw(Box::new(app));
1055        }
1056    }
1057}
1058
1059unsafe extern "C" fn reraise_exception(
1060    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1061    _env: *const Value,
1062    _args: *const Value,
1063    _barrier: *mut ContBarrier,
1064) -> *mut Application {
1065    unsafe {
1066        let runtime = Runtime(Gc::from_raw_inc_rc(runtime));
1067
1068        /*
1069        // env[0] is the exception
1070        let exception = env.as_ref().unwrap().clone();
1071         */
1072        let exception = Value::from_rust_type(NonContinuable::default());
1073
1074        Box::into_raw(Box::new(Application::new(
1075            Procedure::new(
1076                runtime,
1077                Vec::new(),
1078                FuncPtr::Bridge(raise_builtin),
1079                1,
1080                false,
1081            ),
1082            vec![exception, Value::undefined()],
1083        )))
1084    }
1085}
1086
1087/// Raises an exception to the current exception handler and continues with the
1088/// value returned by the handler.
1089#[doc(hidden)]
1090#[cps_bridge(def = "raise-continuable obj", lib = "(rnrs exceptions (6))")]
1091pub fn raise_continuable(
1092    _runtime: &Runtime,
1093    _env: &[Value],
1094    args: &[Value],
1095    _rest_args: &[Value],
1096    barrier: &mut ContBarrier,
1097    k: Value,
1098) -> Result<Application, Exception> {
1099    let [condition] = args else {
1100        unreachable!();
1101    };
1102
1103    let Some(handler) = barrier.current_exception_handler() else {
1104        return Ok(Application::halt_err(condition.clone()));
1105    };
1106
1107    Ok(Application::new(handler, vec![condition.clone(), k]))
1108}
1109
1110#[bridge(name = "error", lib = "(rnrs base builtins (6))")]
1111pub fn error(who: &Value, message: &Value, irritants: &[Value]) -> Result<Vec<Value>, Exception> {
1112    let mut conditions = Vec::new();
1113    if who.is_true() {
1114        conditions.push(Value::from_rust_type(Who::new(who.clone())));
1115    }
1116    conditions.push(Value::from_rust_type(Message::new(message)));
1117    conditions.push(Value::from_rust_type(Irritants::new(slice_to_list(
1118        irritants,
1119    ))));
1120    Err(Exception(Value::from(Exception::from(CompoundCondition(
1121        conditions,
1122    )))))
1123}
1124
1125#[bridge(name = "assertion-violation", lib = "(rnrs base builtins (6))")]
1126pub fn assertion_violation(
1127    who: &Value,
1128    message: &Value,
1129    irritants: &[Value],
1130) -> Result<Vec<Value>, Exception> {
1131    let mut conditions = Vec::new();
1132    conditions.push(Value::from_rust_type(Assertion::new()));
1133    if who.is_true() {
1134        conditions.push(Value::from_rust_type(Who::new(who.clone())));
1135    }
1136    conditions.push(Value::from_rust_type(Message::new(message)));
1137    conditions.push(Value::from_rust_type(Irritants::new(slice_to_list(
1138        irritants,
1139    ))));
1140    Err(Exception(Value::from(Exception::from(CompoundCondition(
1141        conditions,
1142    )))))
1143}
1144
1145/// Store of source files for pretty printing error messages.
1146#[derive(Default, Trace)]
1147pub struct SourceCache {
1148    cache: HashMap<ByAddress<Arc<str>>, Vec<String>>,
1149}
1150
1151impl SourceCache {
1152    pub fn store(&mut self, file_name: Arc<str>, lines: Vec<String>) {
1153        self.cache.insert(ByAddress(file_name), lines);
1154    }
1155
1156    // #[maybe_await]
1157    pub fn fetch(&mut self, file_name: &Arc<str>) -> Option<&[String]> {
1158        if !self.cache.contains_key(ByAddress::from_ref(file_name)) {
1159            let lines = std::fs::read_to_string(file_name.as_ref())
1160                .ok()?
1161                .lines()
1162                .map(|x| x.to_string())
1163                .collect::<Vec<_>>();
1164            self.cache.insert(ByAddress(file_name.clone()), lines);
1165        }
1166        self.cache
1167            .get(ByAddress::from_ref(file_name))
1168            .map(|lines| lines.as_ref())
1169    }
1170
1171    pub fn pretty_print_condition(
1172        &mut self,
1173        pe: &impl PrettyCondition,
1174        w: &mut impl fmt::Write,
1175    ) -> fmt::Result {
1176        let span = pe.span();
1177        let lines = self.fetch(&span.file);
1178        pe.pretty_print(w)?;
1179        print_lines_with_offense_from_span(&span, lines, w)
1180    }
1181}