1use anyhow::{Context, Result, ensure};
12
13use crate::{
14 debug,
15 formats::{
16 FormatLog,
17 eite::{
18 dc::dc_is_el_code,
19 eite_state::EiteState,
20 export_document,
21 formats::{
22 Format, PrefilterSettings, dca_from_format, dca_to_format,
23 },
24 util::string::int_arr_from_str_printed_arr,
25 },
26 },
27 json,
28};
29
30pub struct NdwEngine {}
32
33impl Default for NdwEngine {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl NdwEngine {
40 pub fn new() -> Self {
41 Self {}
42 }
43}
44
45pub fn ndw(_engine: &mut NdwEngine) -> i32 {
47 0
51}
52
53pub fn ndw_invoke(_engine: &mut NdwEngine, routine: &str) -> i32 {
55 match routine {
56 "main" | "tick" => 1,
57 "getExitCode" => 0,
58 _ => 0,
59 }
60}
61
62pub fn start_document_exec(
75 state: &mut EiteState,
76 exec_id: usize,
77) -> Result<()> {
78 ensure!(state.is_exec_id(exec_id), "Invalid exec id {exec_id}");
79
80 let stop_exec_at_tick: u32 = state
82 .get_exec_option(exec_id, "stopExecAtTick")?
83 .unwrap_or_else(|| "0".to_string())
84 .parse()
85 .unwrap_or(0);
86
87 let run_headless = state
88 .get_exec_option(exec_id, "runHeadless")?
89 .is_some_and(|v| v == "true");
90
91 let working_copy: Vec<u32> = {
93 let data_str = state
94 .document_exec_data
95 .get(exec_id)
96 .cloned()
97 .unwrap_or_default();
98 int_arr_from_str_printed_arr(&data_str)?
99 };
100
101 let mut continue_loop = true;
102 let mut current_tick: u32 = 0;
103 let mut wip_frame: Vec<u32> = Vec::new();
104 let mut last_char_was_escape = false;
105
106 let mut state_stack: Vec<&'static str> = vec!["normal"];
108
109 let out_fmt = state.get_env_preferred_format().to_string();
110 let out_fmt = Format::from_string(&out_fmt)?;
111
112 while continue_loop {
113 if current_tick >= stop_exec_at_tick - 1 {
114 continue_loop = false;
115 }
116 if !continue_loop {
117 break;
118 }
119 current_tick += 1;
120
121 let ptr_pos = usize::try_from(state.get_current_exec_ptr_pos(exec_id)?)
122 .expect("Should work to get usize");
123 if ptr_pos >= working_copy.len() {
124 continue_loop = false;
126 } else {
127 let dc = working_copy[ptr_pos];
128 debug!(
129 json!(state),
130 1,
131 &format!(
132 "Exec loop pos={ptr_pos} dc={dc} state={state_stack:?} tick={current_tick}"
133 )
134 );
135
136 if last_char_was_escape {
137 last_char_was_escape = false;
138 state.incr_exec_ptr_pos(exec_id)?;
139 } else {
140 if dc == 255 {
141 last_char_was_escape = true;
143 } else {
144 match state_stack.last().copied().unwrap_or("normal") {
145 "normal" => {
146 if dc == 246 || dc == 247 {
148 state_stack.push("single-line source comment");
149 } else if dc == 249 || dc == 250 {
150 state_stack.push("block source comment");
151 } else if dc_is_el_code(dc)? {
152 } else {
154 wip_frame.push(dc);
155 }
156 }
157 "single-line source comment" => {
158 if dc == 248 {
160 state_stack.pop();
161 }
162 }
163 "block source comment" => {
164 if dc == 251 {
165 state_stack.pop();
166 }
167 }
168 _ => {
169 wip_frame.push(dc);
171 }
172 }
173 }
174 state.incr_exec_ptr_pos(exec_id)?;
175 }
176 }
177
178 if !run_headless && current_tick.is_multiple_of(100) {
179 state.set_exec_frame(exec_id, &wip_frame)?;
181 let _ = dca_to_format(
183 state,
184 &out_fmt,
185 &wip_frame,
186 &PrefilterSettings::default(),
187 ); }
190 }
191
192 state.set_exec_frame(exec_id, &wip_frame)?;
194 let _ = dca_to_format(
195 state,
196 &out_fmt,
197 &wip_frame,
198 &PrefilterSettings::default(),
199 ); Ok(())
202}
203
204pub fn run_tests_document_exec(
214 _state: &mut EiteState,
215 _verbose: bool,
216) -> Result<()> {
217 Ok(())
230}
231
232pub fn run_exec_test(
237 _state: &mut EiteState,
238 _test_name: &str,
239 _ticks_needed: i32,
240 _verbose: bool,
241) -> Result<()> {
242 Ok(())
243}
244
245pub fn start_eite(state: &mut EiteState) -> Result<()> {
248 load_and_run(state, &Format::sems_default(), "eite.sems")
249}
250
251pub fn load_and_run(
255 state: &mut EiteState,
256 format: &Format,
257 path: &str,
258) -> Result<()> {
259 let doc = load_stored_document(state, format, path)?;
260 run_document(state, &doc)
261}
262
263pub fn run_document(state: &mut EiteState, dc_array: &[u32]) -> Result<()> {
265 let exec_id = run_document_prepare(state, dc_array)?;
266 run_document_go(state, exec_id)
267}
268
269pub fn run_document_prepare(
271 state: &mut EiteState,
272 dc_array: &[u32],
273) -> Result<usize> {
274 Ok(state.prepare_document_exec(dc_array))
275}
276
277pub fn run_document_go(state: &mut EiteState, exec_id: usize) -> Result<()> {
279 start_document_exec(state, exec_id)
280}
281
282pub fn load_and_convert(
284 state: &mut EiteState,
285 in_format: &Format,
286 out_format: &Format,
287 path: &str,
288 prefilter_settings: &PrefilterSettings,
289) -> Result<(Vec<u8>, FormatLog)> {
290 let doc = load_stored_document(state, in_format, path)?;
291 export_document(state, out_format, &doc, prefilter_settings)
292}
293
294pub fn load_stored_document(
298 state: &mut EiteState,
299 format: &Format,
300 path: &str,
301) -> Result<Vec<u32>> {
302 let bytes = try_load_asset(path)
303 .or_else(|_| try_load_asset(&format!("resources/data/eite/{path}")))
304 .with_context(|| format!("Failed to load document asset '{path}'"))?;
305 let (doc, log) = dca_from_format(state, format, &bytes)?;
306 log.auto_log();
307 Ok(doc)
308}
309
310fn try_load_asset(path: &str) -> Result<Vec<u8>> {
311 crate::storage::get_asset(path)
312 .with_context(|| format!("asset not found: {path}"))
313}
314
315pub fn get_desired_event_notifications(
317 _state: &EiteState,
318 _exec_id: usize,
319) -> Result<Vec<String>> {
320 Ok(Vec::new())
321}
322
323pub fn send_event(
325 _state: &mut EiteState,
326 _exec_id: usize,
327 _event_data: &[i32],
328) -> Result<()> {
329 Ok(())
331}
332
333pub fn get_document_frame(
336 state: &mut EiteState,
337 exec_id: usize,
338 out_format: &Format,
339) -> Result<(Vec<u8>, FormatLog)> {
340 match state.get_current_exec_frame(exec_id) {
341 Ok(frame) => dca_to_format(
342 state,
343 out_format,
344 &frame,
345 &PrefilterSettings::default(),
346 ),
347 Err(_) => Ok((Vec::new(), FormatLog::default())),
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[crate::ctb_test]
356 fn test_document_exec_state() {
357 let mut st = EiteState::default();
358 let exec_id = st.prepare_document_exec(&[1, 2, 3]);
359 assert!(st.is_exec_id(exec_id));
360
361 assert_eq!(st.get_current_exec_ptr_pos(exec_id).unwrap(), 0);
363 st.set_exec_ptr_pos(exec_id, 5).unwrap();
364 assert_eq!(st.get_current_exec_ptr_pos(exec_id).unwrap(), 5);
365 st.incr_exec_ptr_pos(exec_id).unwrap();
366 assert_eq!(st.get_current_exec_ptr_pos(exec_id).unwrap(), 6);
367
368 let data = st.get_current_exec_data(exec_id).unwrap();
370 assert_eq!(data, vec![1, 2, 3]);
371 }
372}