Skip to main content

nifty_interp/
logic.rs

1//! The interpreter as the runtime engine's [`Logic`]: it evaluates the checked code each
2//! template points at, and starts the downloads and file dialogs that code asks for.
3
4use std::rc::{Rc, Weak};
5use std::sync::Arc;
6
7use nifty_check::hir::{Binding, Block, Dynamic, Expr, Loop, StateSet, ViewId};
8use nifty_runtime::{Backend, Dialogs, Engine, Logic, Shown, Templates, Ui, Waker, WindowRequest};
9
10use crate::machine::{
11    Answer, Frame, Machine, Result, RuntimeError, Slot, Targets, new_frame, rounds,
12};
13use crate::value::Value;
14
15pub(crate) struct Interpreter {
16    machine: Machine,
17    downloads: nifty_download::Downloads<Targets>,
18    dialogs: Dialogs<Answer>,
19    /// Indexed by `ViewId`.
20    views: Vec<ViewTemplate>,
21}
22
23/// A view's body, and how many locals its frame needs.
24#[derive(Debug)]
25pub(crate) struct ViewTemplate {
26    pub body: Templates<Interpreter>,
27    pub locals: usize,
28}
29
30#[derive(Debug)]
31pub(crate) enum Region {
32    /// Shows the first branch whose condition holds, or else `otherwise`.
33    If {
34        branches: Vec<(Rc<Expr>, Templates<Interpreter>)>,
35        otherwise: Templates<Interpreter>,
36    },
37    /// Shows `body` once per time round the loop, with the loop's variables set.
38    For {
39        head: Rc<Loop>,
40        body: Templates<Interpreter>,
41    },
42    /// Shows the view's body, with the arguments as its parameters.
43    View { view: ViewId, args: Rc<[Expr]> },
44}
45
46impl Interpreter {
47    /// Runs `machine` for `engine`, which shows file dialogs and receives their answers and
48    /// download reports through `ui`.
49    pub(crate) fn new<B: Backend>(
50        machine: Machine,
51        ui: &Ui<B>,
52        engine: Weak<Engine<Self, B>>,
53    ) -> Self {
54        let waker: Arc<dyn Waker> = Arc::new(ui.waker(Box::new(move || {
55            if let Some(engine) = engine.upgrade() {
56                engine.logic().dialogs.show_asked();
57                engine.handle_event(Self::receive);
58            }
59        })));
60        Self {
61            views: crate::lower::views(machine.program()),
62            machine,
63            downloads: nifty_download::Downloads::new(Arc::clone(&waker)),
64            dialogs: Dialogs::new(ui.dialogs(), waker),
65        }
66    }
67
68    pub(crate) fn machine(&self) -> &Machine {
69        &self.machine
70    }
71
72    /// Waits until every running download has sent its last report, without applying them.
73    #[cfg(test)]
74    pub(crate) fn wait_for_downloads(&self) {
75        self.downloads.wait();
76    }
77
78    /// Waits until a download sends a report, and wakes the UI thread to apply it.
79    #[cfg(test)]
80    pub(crate) fn wait_for_download_report(&self) {
81        self.downloads.wait_for_report();
82    }
83
84    /// Runs code for an event, then starts the downloads and asks for the dialogs it asked
85    /// for, even if it failed after asking.
86    fn event(&self, run: impl FnOnce(&Machine) -> Result<()>) -> Result<()> {
87        let result = run(&self.machine);
88        for request in self.machine.take_downloads() {
89            self.downloads.start(request);
90        }
91        for (command, id) in self.machine.take_download_commands() {
92            self.downloads.command(&id, command);
93        }
94        self.downloads
95            .cancel_unheld(|targets| self.machine.follow_download(targets));
96        for (dialog, answer) in self.machine.take_dialogs() {
97            self.dialogs.ask(dialog, answer);
98        }
99        result
100    }
101
102    fn receive(&self) -> Result<()> {
103        self.downloads.receive(
104            |targets| self.machine.follow_download(targets),
105            |targets, report| self.machine.report_download(targets, report),
106        )?;
107        self.dialogs
108            .receive(|answer, picked| self.machine.answer_dialog(answer, picked))
109    }
110
111    fn read(&self, binding: &Binding, frame: &Frame) -> Result<Value> {
112        let mut scratch = frame.clone();
113        let location = self.machine.locate(&binding.place, &mut scratch)?;
114        self.machine.read(&location, &scratch, binding.place.span)
115    }
116
117    fn write(&self, binding: &Binding, frame: &Frame, value: Value) -> Result<()> {
118        self.event(|machine| {
119            let mut scratch = frame.clone();
120            let location = machine.locate(&binding.place, &mut scratch)?;
121            machine.write(&location, &mut scratch, value, binding.place.span)
122        })
123    }
124
125    /// The frames a `for` region shows its body in, one per time round.
126    fn round_frames(&self, head: &Loop, frame: &Frame) -> Result<Vec<Rc<Frame>>> {
127        let mut scratch = frame.clone();
128        let walk = self.machine.walk(&head.iterable, &mut scratch)?;
129        let mut frames = Vec::new();
130        for round in rounds() {
131            let mut round_frame = frame.clone();
132            if !self
133                .machine
134                .enter_round(head, &walk, round, &mut round_frame)?
135            {
136                break;
137            }
138            frames.push(Rc::new(round_frame));
139        }
140        Ok(frames)
141    }
142}
143
144impl Logic for Interpreter {
145    type Frame = Frame;
146    type Value = Rc<Dynamic>;
147    type Binding = Rc<Binding>;
148    type Action = Block;
149    type Region = Region;
150    type Error = RuntimeError;
151
152    fn text(&self, text: &Rc<Dynamic>, frame: &Frame) -> Result<String> {
153        let value = self.machine.eval(&text.expr, &mut frame.clone())?;
154        Ok(value.as_str().to_owned())
155    }
156
157    fn number(&self, number: &Rc<Dynamic>, frame: &Frame) -> Result<i64> {
158        Ok(self
159            .machine
160            .eval(&number.expr, &mut frame.clone())?
161            .as_int())
162    }
163
164    fn flag(&self, flag: &Rc<Dynamic>, frame: &Frame) -> Result<bool> {
165        Ok(self.machine.eval(&flag.expr, &mut frame.clone())?.as_bool())
166    }
167
168    fn texts(&self, texts: &Rc<Dynamic>, frame: &Frame) -> Result<Vec<String>> {
169        Ok(match self.machine.eval(&texts.expr, &mut frame.clone())? {
170            Value::List(items) => items.iter().map(|item| item.as_str().to_owned()).collect(),
171            other => panic!("the checker only shows lists of text here, not {other:?}"),
172        })
173    }
174
175    fn read_number(&self, binding: &Rc<Binding>, frame: &Frame) -> Result<i64> {
176        Ok(self.read(binding, frame)?.as_int())
177    }
178
179    fn read_text(&self, binding: &Rc<Binding>, frame: &Frame) -> Result<String> {
180        Ok(self.read(binding, frame)?.as_str().to_owned())
181    }
182
183    fn read_checked(&self, binding: &Rc<Binding>, frame: &Frame) -> Result<bool> {
184        Ok(self.read(binding, frame)?.as_bool())
185    }
186
187    fn write_text(&self, binding: &Rc<Binding>, frame: &Frame, text: &str) -> Result<()> {
188        self.write(binding, frame, Value::String(Rc::from(text)))
189    }
190
191    fn write_checked(&self, binding: &Rc<Binding>, frame: &Frame, checked: bool) -> Result<()> {
192        self.write(binding, frame, Value::Bool(checked))
193    }
194
195    fn write_number(&self, binding: &Rc<Binding>, frame: &Frame, number: i64) -> Result<()> {
196        self.write(binding, frame, Value::Int(number))
197    }
198
199    fn write_texts(&self, binding: &Rc<Binding>, frame: &Frame, texts: Vec<String>) -> Result<()> {
200        self.write(binding, frame, Value::strings(texts))
201    }
202
203    fn run(&self, action: &Block, frame: &Frame) -> Result<()> {
204        self.event(|machine| {
205            machine.exec_block(action, &mut frame.clone())?;
206            Ok(())
207        })
208    }
209
210    fn expand(&self, region: &Region, frame: &Rc<Frame>) -> Result<Vec<Shown<Self>>> {
211        match region {
212            Region::If {
213                branches,
214                otherwise,
215            } => {
216                let mut scratch = Frame::clone(frame);
217                let mut shown = otherwise;
218                for (condition, body) in branches {
219                    if self.machine.eval(condition, &mut scratch)?.as_bool() {
220                        shown = body;
221                        break;
222                    }
223                }
224                Ok(vec![Shown {
225                    templates: Rc::clone(shown),
226                    frame: Rc::clone(frame),
227                }])
228            }
229            Region::For { head, body } => Ok(self
230                .round_frames(head, frame)?
231                .into_iter()
232                .map(|frame| Shown {
233                    templates: Rc::clone(body),
234                    frame,
235                })
236                .collect()),
237            Region::View { view, args } => {
238                let view = &self.views[view.0];
239                let mut scratch = Frame::clone(frame);
240                let mut view_frame = new_frame(view.locals);
241                for (slot, arg) in view_frame.iter_mut().zip(args.iter()) {
242                    *slot = Slot::Value(self.machine.eval(arg, &mut scratch)?);
243                }
244                Ok(vec![Shown {
245                    templates: Rc::clone(&view.body),
246                    frame: Rc::new(view_frame),
247                }])
248            }
249        }
250    }
251
252    fn take_changed(&self) -> StateSet {
253        self.machine.take_changed()
254    }
255
256    fn take_window_requests(&self) -> Vec<WindowRequest> {
257        self.machine.take_window_requests()
258    }
259}