Skip to main content

nifty_rt/
show.rs

1//! How compiled code says what a region shows: it calls `nf_show_*` on the sink it was
2//! given, once per template list to show, in order.
3
4use std::rc::Rc;
5
6use nifty_runtime::{Shown, Templates};
7
8use crate::logic::{Compiled, Frame, Region, ViewTemplate};
9use crate::value::Slot;
10
11pub struct Sink<'a> {
12    region: &'a Region,
13    views: &'a [ViewTemplate],
14    parent: &'a Rc<Frame>,
15    shown: Vec<(Templates<Compiled>, Pending)>,
16}
17
18impl std::fmt::Debug for Sink<'_> {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("Sink")
21            .field("shown", &self.shown.len())
22            .finish_non_exhaustive()
23    }
24}
25
26/// The frame a shown list gets. Its own frames stay open until the expansion returns, so
27/// compiled code can fill in their locals.
28enum Pending {
29    Parent,
30    Own(Frame),
31}
32
33impl<'a> Sink<'a> {
34    pub(crate) fn new(
35        region: &'a Region,
36        views: &'a [ViewTemplate],
37        parent: &'a Rc<Frame>,
38    ) -> Self {
39        Self {
40            region,
41            views,
42            parent,
43            shown: Vec::new(),
44        }
45    }
46
47    pub(crate) fn finish(self) -> Vec<Shown<Compiled>> {
48        self.shown
49            .into_iter()
50            .map(|(templates, frame)| Shown {
51                templates,
52                frame: match frame {
53                    Pending::Parent => Rc::clone(self.parent),
54                    Pending::Own(frame) => Rc::new(frame),
55                },
56            })
57            .collect()
58    }
59
60    /// # Panics
61    /// When the region has no list `list`, which only wrongly compiled code asks for.
62    fn list(&self, list: u32) -> Templates<Compiled> {
63        let list =
64            self.region.lists.get(list as usize).unwrap_or_else(|| {
65                panic!("compiled code showed list {list}, which its region lacks")
66            });
67        Rc::clone(list)
68    }
69
70    /// Shows `templates` with `frame`, returning its locals; they live on the heap, so they
71    /// stay put while the frame moves into the list.
72    fn show_own(&mut self, templates: Templates<Compiled>, mut frame: Frame) -> *mut Slot {
73        let locals = frame.as_mut_ptr();
74        self.shown.push((templates, Pending::Own(frame)));
75        locals
76    }
77}
78
79/// Shows the region's list `list` with the region's own frame, as an `if` does.
80///
81/// # Safety
82/// `sink` is the sink the runtime passed into the compiled code calling.
83#[unsafe(no_mangle)]
84pub(crate) unsafe extern "C" fn nf_show_same(sink: *mut Sink<'_>, list: u32) {
85    // SAFETY: by the caller's contract.
86    let sink = unsafe { &mut *sink };
87    let templates = sink.list(list);
88    sink.shown.push((templates, Pending::Parent));
89}
90
91/// Shows the region's list `list` with a copy of the region's frame, and returns that
92/// copy's locals for compiled code to fill in, as a `for` does for each item. They stay
93/// valid until the expansion returns.
94///
95/// # Safety
96/// `sink` is the sink the runtime passed into the compiled code calling.
97#[unsafe(no_mangle)]
98pub(crate) unsafe extern "C" fn nf_show_item(sink: *mut Sink<'_>, list: u32) -> *mut Slot {
99    // SAFETY: by the caller's contract.
100    let sink = unsafe { &mut *sink };
101    let templates = sink.list(list);
102    let frame = Frame::clone(sink.parent);
103    sink.show_own(templates, frame)
104}
105
106/// Shows view `view`'s body with a new frame of its locals, all empty, and returns them
107/// for compiled code to fill in with the arguments. They stay valid until the expansion
108/// returns.
109///
110/// # Safety
111/// `sink` is the sink the runtime passed into the compiled code calling.
112///
113/// # Panics
114/// When there is no view `view`, which only wrongly compiled code asks for.
115#[unsafe(no_mangle)]
116pub(crate) unsafe extern "C" fn nf_show_view(sink: *mut Sink<'_>, view: u32) -> *mut Slot {
117    // SAFETY: by the caller's contract.
118    let sink = unsafe { &mut *sink };
119    let view = sink
120        .views
121        .get(view as usize)
122        .unwrap_or_else(|| panic!("compiled code showed view {view}, which doesn't exist"));
123    let (templates, frame) = (Rc::clone(&view.body), Frame::empty(view.frame));
124    sink.show_own(templates, frame)
125}