1use std::ffi::OsString;
10use std::fs::create_dir_all;
11use std::io::Cursor;
12use std::path::PathBuf;
13use std::sync::Arc;
14use std::sync::RwLock;
15#[cfg(feature = "plot")]
16use std::thread;
17use rustyline::error::ReadlineError;
18use rustyline::DefaultEditor;
19#[cfg(feature = "plot")]
20use crate::winit::event_loop::ControlFlow;
21#[cfg(feature = "plot")]
22use crate::winit::event_loop::EventLoop;
23use crate::doc::*;
24use crate::env::*;
25use crate::error::*;
26use crate::interp::*;
27use crate::intr::*;
28use crate::lexer::*;
29use crate::mod_node::*;
30use crate::parser::*;
31#[cfg(feature = "plot")]
32use crate::plot::*;
33use crate::utils::*;
34use crate::value::*;
35
36#[cfg(feature = "plot")]
37fn run_plotter_app<F>(are_plotter_windows: bool, f: F) -> Option<i32>
38 where F: FnOnce(Option<EventLoopProxy>) -> Option<i32> + Send + 'static
39{
40 if are_plotter_windows {
41 let event_loop = match EventLoop::<PlotterAppEvent>::with_user_event().build() {
42 Ok(tmp_event_loop) => tmp_event_loop,
43 Err(err) => {
44 eprintln!("{}", err);
45 return Some(1);
46 },
47 };
48 let event_loop_proxy = event_loop.create_proxy();
49 let thr = thread::spawn(move || f(Some(event_loop_proxy)));
50 event_loop.set_control_flow(ControlFlow::Poll);
51 event_loop.set_control_flow(ControlFlow::Wait);
52 let mut plotter_app = PlotterApp::new(&event_loop);
53 match event_loop.run_app(&mut plotter_app) {
54 Ok(()) => (),
55 Err(err) => {
56 eprintln!("{}", err);
57 return Some(1);
58 },
59 }
60 match thr.join() {
61 Ok(res) => res,
62 Err(_) => {
63 eprintln!("can't join thread");
64 Some(1)
65 },
66 }
67 } else {
68 f(None)
69 }
70}
71
72#[cfg(feature = "plot")]
73fn quit_from_plotter_app(env: &Env) -> bool
74{
75 match rw_lock_read(env.shared_env()) {
76 Ok(shared_env_g) => {
77 match shared_env_g.event_loop_proxy() {
78 Some(event_loop_proxy) => {
79 match event_loop_proxy.send_event(PlotterAppEvent::Quit) {
80 Ok(()) => true,
81 Err(err) => {
82 eprintln!("{}", err);
83 false
84 },
85 }
86 },
87 None => true,
88 }
89 },
90 Err(err) => {
91 eprint_error(&err);
92 false
93 },
94 }
95}
96
97#[cfg(not(feature = "plot"))]
98fn run_plotter_app<F>(_are_plotter_windows: bool, f: F) -> Option<i32>
99 where F: FnOnce(Option<EventLoopProxy>) -> Option<i32> + Send + Sync + 'static
100{ f(None) }
101
102#[cfg(not(feature = "plot"))]
103fn quit_from_plotter_app(_env: &Env) -> bool
104{ true }
105
106fn non_interactive_main_loop(path: String, args: Vec<String>, root_mod: Arc<RwLock<ModNode<Value, ()>>>, lib_path: OsString, doc_path: OsString, is_ctrl_c_intr_checker: bool, are_plotter_windows: bool) -> Option<i32>
107{
108 run_plotter_app(are_plotter_windows, move |event_loop_proxy| {
109 let intr_checker: Arc<dyn IntrCheck + Send + Sync> = if is_ctrl_c_intr_checker {
110 match CtrlCIntrChecker::initialize() {
111 Ok(()) => (),
112 Err(err) => {
113 eprint_error(&err);
114 return Some(1);
115 },
116 }
117 Arc::new(CtrlCIntrChecker::new())
118 } else {
119 Arc::new(EmptyIntrChecker::new())
120 };
121 let shared_env = SharedEnv::new_with_intr_checker_and_event_loop_proxy(lib_path, doc_path, args, intr_checker, event_loop_proxy);
122 let mut env = Env::new_with_script_dir_and_domain_and_shared_env(root_mod, PathBuf::from("."), None, Arc::new(RwLock::new(shared_env)));
123 let mut interp = Interp::new();
124 let res = match parse(path) {
125 Ok(tree) => {
126 match interp.interpret(&mut env, &tree) {
127 Ok(()) => None,
128 Err(Error::Stop(Stop::ErrorPropagation)) => {
129 eprintln!("{}", interp.ret_value());
130 Some(1)
131 },
132 Err(Error::Stop(Stop::Quit)) => None,
133 Err(Error::Stop(Stop::Exit(code))) => Some(code),
134 Err(err @ Error::Intr) => {
135 eprint_error(&err);
136 Some(1)
137 },
138 Err(err) => {
139 eprint_error_with_stack_trace(&err, interp.stack_trace());
140 Some(1)
141 },
142 }
143 },
144 Err(err) => {
145 eprint_error(&err);
146 Some(1)
147 },
148 };
149 if !quit_from_plotter_app(&env) {
150 return Some(1);
151 }
152 res
153 })
154}
155
156fn interactive_main_loop(args: Vec<String>, history_file: PathBuf, root_mod: Arc<RwLock<ModNode<Value, ()>>>, lib_path: OsString, doc_path: OsString, is_ctrl_c_intr_checker: bool, are_plotter_windows: bool) -> Option<i32>
157{
158 run_plotter_app(are_plotter_windows, move |event_loop_proxy| {
159 let intr_checker: Arc<dyn IntrCheck + Send + Sync> = if is_ctrl_c_intr_checker {
160 match CtrlCIntrChecker::initialize() {
161 Ok(()) => (),
162 Err(err) => {
163 eprint_error(&err);
164 return Some(1);
165 },
166 }
167 Arc::new(CtrlCIntrChecker::new())
168 } else {
169 Arc::new(EmptyIntrChecker::new())
170 };
171 let shared_env = SharedEnv::new_with_intr_checker_and_event_loop_proxy(lib_path, doc_path, args, intr_checker, event_loop_proxy);
172 let mut env = Env::new_with_script_dir_and_domain_and_shared_env(root_mod, PathBuf::from("."), None, Arc::new(RwLock::new(shared_env)));
173 let mut interp = Interp::new();
174 let mut editor = match DefaultEditor::new() {
175 Ok(tmp_editor) => tmp_editor,
176 Err(err) => {
177 eprintln!("{}", err);
178 return Some(1);
179 },
180 };
181 let _res = editor.load_history(history_file.as_path());
182 let mut line_num = 1u64;
183 let mut res: Option<i32> = None;
184 loop {
185 match editor.readline(format!("unlab-gpu:{}> ", line_num).as_str()) {
186 Ok(line) => {
187 match editor.add_history_entry(line.as_str()) {
188 Ok(_) => (),
189 Err(err) => {
190 eprintln!("{}", err);
191 return Some(1);
192 },
193 }
194 let mut new_line_num = line_num;
195 let mut lines = line.clone();
196 lines.push('\n');
197 new_line_num += 1;
198 let tree = loop {
199 let mut cursor = Cursor::new(lines.as_str());
200 let mut lexer = Lexer::new_with_line(Arc::new(String::from("(stdin)")), &mut cursor, line_num);
201 let parser_path = lexer.path().clone();
202 let tokens: &mut dyn DocIterator<Item = Result<(Token, Pos)>> = &mut lexer;
203 let mut parser = Parser::new(parser_path, tokens);
204 match parser.parse() {
205 Ok(tree) => break Some(tree),
206 Err(err @ Error::ParserEof(_, ParserEofFlag::Repetition)) => {
207 match editor.readline("> ") {
208 Ok(next_line) => {
209 match editor.add_history_entry(next_line.as_str()) {
210 Ok(_) => (),
211 Err(err) => {
212 eprintln!("{}", err);
213 return Some(1);
214 },
215 }
216 lines.push_str(next_line.as_str());
217 lines.push('\n');
218 new_line_num += 1;
219 },
220 Err(ReadlineError::Interrupted) => (),
221 Err(ReadlineError::Eof) => {
222 eprint_error(&err);
223 break None;
224 },
225 Err(err) => {
226 eprintln!("{}", err);
227 return Some(1);
228 },
229 }
230 },
231 Err(err) => {
232 eprint_error(&err);
233 break None;
234 },
235 }
236 };
237 line_num = new_line_num;
238 if is_ctrl_c_intr_checker {
239 CtrlCIntrChecker::reset();
240 }
241 match tree {
242 Some(tree) => {
243 match interp.interpret(&mut env, &tree) {
244 Ok(()) => (),
245 Err(Error::Stop(Stop::ErrorPropagation)) => eprintln!("{}", interp.ret_value()),
246 Err(Error::Stop(Stop::Quit)) => break,
247 Err(Error::Stop(Stop::Exit(code))) => {
248 res = Some(code);
249 break;
250 },
251 Err(err @ Error::Intr) => eprint_error(&err),
252 Err(err) => eprint_error_with_stack_trace(&err, interp.stack_trace()),
253 }
254 interp.clear_stack_trace();
255 },
256 None => (),
257 }
258 },
259 Err(ReadlineError::Interrupted) => (),
260 Err(ReadlineError::Eof) => break,
261 Err(err) => {
262 eprintln!("{}", err);
263 return Some(1);
264 },
265 }
266 }
267 interp.clear_stack_trace();
268 let mut history_dir = history_file.clone();
269 history_dir.pop();
270 if history_dir != PathBuf::from("") {
271 match create_dir_all(history_dir.as_path()) {
272 Ok(()) => (),
273 Err(err) => {
274 eprintln!("{}", err);
275 return Some(1);
276 },
277 }
278 }
279 match editor.save_history(history_file.as_path()) {
280 Ok(()) => (),
281 Err(err) => {
282 eprintln!("{}", err);
283 return Some(1);
284 },
285 }
286 if !quit_from_plotter_app(&env) {
287 return Some(1);
288 }
289 res
290 })
291}
292
293pub fn main_loop(path: Option<String>, args: Vec<String>, history_file: PathBuf, root_mod: Arc<RwLock<ModNode<Value, ()>>>, lib_path: OsString, doc_path: OsString, is_ctrl_c_intr_checker: bool, are_plotter_windows: bool) -> Option<i32>
302{
303 match path {
304 Some(path) => non_interactive_main_loop(path, args, root_mod, lib_path, doc_path, is_ctrl_c_intr_checker, are_plotter_windows),
305 None => interactive_main_loop(args, history_file, root_mod, lib_path, doc_path, is_ctrl_c_intr_checker, are_plotter_windows),
306 }
307}