1#![allow(warnings)]
2pub extern crate mech_core as core;
7pub extern crate mech_syntax as syntax;
8
9pub use mech_core::*;
10use mech_core::nodes::Program;
11pub use mech_interpreter::Interpreter;
12
13extern crate colored;
14use colored::*;
15
16extern crate bincode;
17use std::io::{Write, BufReader, BufWriter, stdout};
18use std::fs::{OpenOptions, File, canonicalize, create_dir};
19use crossterm::{
20 ExecutableCommand, QueueableCommand,
21 terminal, cursor, style::Print,
22};
23
24use tabled::{
25 builder::Builder,
26 grid::config::HorizontalLine,
27 settings::{object::Rows,Panel, Span, Alignment, Modify, Style},
28 Tabled,
29};
30use std::path::{Path, PathBuf};
31use std::io;
32use std::io::prelude::*;
33use std::time::{Duration, Instant, SystemTime};
34use std::thread::{self, JoinHandle};
35use std::sync::Mutex;
36use std::sync::RwLock;
37use std::net::{SocketAddr, UdpSocket, TcpListener, TcpStream};
39use std::collections::HashMap;
40use crossbeam_channel::Sender;
41use crossbeam_channel::{unbounded, Receiver};
42use std::{fs,env};
43#[macro_use]
44extern crate lazy_static;
45
46#[cfg(feature = "wasm")]
47use web_sys::{Crypto, Window, console};
48use rand::rngs::OsRng;
49use rand::RngCore;
50use notify::{recommended_watcher, Event, RecursiveMode, Result as NResult, Watcher};
51use std::sync::mpsc;
52use std::sync::Arc;
53use std::collections::HashSet;
54
55lazy_static! {
56 static ref CORE_MAP: Mutex<HashMap<SocketAddr, (String, SystemTime)>> = Mutex::new(HashMap::new());
57}
58
59mod repl;
60mod serve;
61mod run;
62mod mechfs;
63
64pub use self::repl::*;
65pub use self::serve::*;
66pub use self::run::*;
67pub use self::mechfs::*;
68
69pub use mech_core::*;
70pub use mech_syntax::*;
71
72pub fn print_prompt() {
75 stdout().flush();
76 print!("{}", ">: ".truecolor(246,192,78));
77 stdout().flush();
78}
79
80#[cfg(not(feature = "wasm"))]
82pub fn generate_uuid() -> u64 {
83 OsRng.next_u64()
84}
85
86#[cfg(feature = "wasm")]
87pub fn generate_uuid() -> u64 {
88 let mut rng = WebCryptoRng{};
89 rng.next_u64()
90}
91
92pub fn mech_table_style() -> Style<(),(),(),(),(),(),2,0> {
93 Style::empty()
94 .horizontals([
95 (1, HorizontalLine::filled('-').into()),
96 (2, HorizontalLine::filled('-').into()),
97 ])
98}
99
100pub fn help() -> String {
101 let mut builder = Builder::default();
102 builder.push_record(vec!["Command","Short","Parameters","Description"]);
103 builder.push_record(vec![
104 ":help".to_string(),
105 ":h".to_string(),
106 "".to_string(),
107 "Display this help message".to_string()
108 ]);
109 builder.push_record(vec![
110 ":quit".to_string(),
111 ":q".to_string(),
112 "".to_string(),
113 "Quit the REPL".to_string()
114 ]);
115 builder.push_record(vec![
116 ":symbols".to_string(),
117 ":s".to_string(),
118 "[search pattern]".to_string(),
119 "Search symbols".to_string()
120 ]);
121 builder.push_record(vec![
122 ":plan".to_string(),
123 ":p".to_string(),
124 "".to_string(),
125 "Display the plan".to_string()
126 ]);
127 builder.push_record(vec![
128 ":whos".to_string(),
129 ":w".to_string(),
130 "[search pattern]".to_string(),
131 "Search symbol directory".to_string()
132 ]);
133 builder.push_record(vec![
134 ":clc".to_string(),
135 ":c".to_string(),
136 "".to_string(),
137 "Clear the screen".to_string()
138 ]);
139 builder.push_record(vec![
140 ":clear".to_string(),
141 "".to_string(),
142 "[target variable]".to_string(),
143 "Clear the interpreter state".to_string()
144 ]);
145 builder.push_record(vec![
146 ":load".to_string(),
147 "".to_string(),
148 "[file path]".to_string(),
149 "Load a file".to_string()
150 ]);
151 builder.push_record(vec![
152 ":ls".to_string(),
153 "".to_string(),
154 "[target path]".to_string(),
155 "List directory contents".to_string()
156 ]);
157 builder.push_record(vec![
158 ":cd".to_string(),
159 "".to_string(),
160 "[target path]".to_string(),
161 "Change directory".to_string()
162 ]);
163 builder.push_record(vec![
164 ":step".to_string(),
165 "".to_string(),
166 "[step count]".to_string(),
167 "Iterate plan".to_string()
168 ]);
169 let mut table = builder.build();
170 table.with(mech_table_style())
171 .with(Panel::header(format!("{}","🧭 Help".truecolor(0xdf,0xb9,0x9f))));
172 format!("\n{table}\n")
173}
174
175
176pub fn ls() -> String {
177 let current_dir = env::current_dir().unwrap();
178 let mut builder = Builder::default();
179 builder.push_record(vec!["Mode","Last Write Time","Length","Name"]);
180 for entry in fs::read_dir("./").unwrap() {
181 let entry = entry.unwrap();
182 let path = entry.path();
183 let metadata = fs::metadata(&path).unwrap();
184 let file_type = if metadata.is_dir() { "d----" } else { "-a---" };
185 let last_write_time = metadata.modified().unwrap();
186 let last_write_time: chrono::DateTime<chrono::Local> = last_write_time.into();
187 let length = if metadata.is_file() { metadata.len().to_string() } else { "".to_string() };
188 let name = format!("{}", path.file_name().unwrap().to_str().unwrap());
189 builder.push_record(vec![file_type.to_string(), last_write_time.format("%m/%d/%Y %I:%M %p").to_string(), length, name.to_string()]);
190 }
191 let mut table = builder.build();
192 table.with(mech_table_style())
193 .with(Panel::header(format!("{}","📂 Directory Listing".truecolor(0xdf,0xb9,0x9f))));
194 format!("\nDirectory: {}\n\n{table}\n",current_dir.display())
195}
196
197pub fn pretty_print_tree(tree: &Program) -> String {
198 let tree_hash = hash_str(&format!("{:#?}", tree));
199 let formatted_tree = format_parse_tree(tree);
200 let mut builder = Builder::default();
201 builder.push_record(vec![format!("Hash: {}", tree_hash)]);
202 builder.push_record(vec![format!("{}", formatted_tree)]);
203 let mut table = builder.build();
204 table.with(Style::modern_rounded())
205 .with(Panel::header("🌳 Syntax Tree"));
206 format!("{table}")
207}
208
209pub fn whos(intrp: &Interpreter) -> String {
210 let mut builder = Builder::default();
211 builder.push_record(vec!["Name","Size","Bytes","Kind"]);
212 let dictionary = intrp.dictionary();
213 for (id,name) in dictionary.borrow().iter() {
214 let value = intrp.get_symbol(*id).unwrap();
215 let value_brrw = value.borrow();
216 builder.push_record(vec![
217 name.clone(),
218 format!("{:?}",value_brrw.shape()),
219 format!("{:?}",value_brrw.size_of()),
220 format!("{:?}",value_brrw.kind()),
221 ]);
222 }
223
224 let mut table = builder.build();
225 table.with(mech_table_style())
226 .with(Panel::header(format!("{}","🔍 Whos".truecolor(0xdf,0xb9,0x9f))));
227 format!("\n{table}\n")
228}
229
230pub fn pretty_print_symbols(intrp: &Interpreter) -> String {
231 let mut builder = Builder::default();
232 let symbol_table = intrp.pretty_print_symbols();
233 builder.push_record(vec![
234 format!("{}",symbol_table),
235 ]);
236
237 let mut table = builder.build();
238 table.with(mech_table_style())
239 .with(Panel::header(format!("{}","🔣 Symbols".truecolor(0xdf,0xb9,0x9f))));
240 format!("\n{table}\n")
241}
242
243pub fn clc() {
244 let mut stdo = stdout();
245 stdo.execute(terminal::Clear(terminal::ClearType::All));
246 stdo.execute(cursor::MoveTo(0,0));
247}
248
249pub fn pretty_print_plan(intrp: &Interpreter) -> String {
250 let mut builder = Builder::default();
251
252 let mut row = vec![];
253 let plan = intrp.plan();
254 let plan_brrw = plan.borrow();
255 if plan_brrw.is_empty() {
256 builder.push_record(vec!["".to_string()]);
257 } else {
258 for (ix, fxn) in plan_brrw.iter().enumerate() {
259 let plan_str = format!("{}. {}\n", ix + 1, fxn.to_string());
260 row.push(plan_str.clone());
261 if row.len() == 4 {
262 builder.push_record(row.clone());
263 row.clear();
264 }
265 }
266 }
267 if row.is_empty() == false {
268 builder.push_record(row.clone());
269 }
270 let mut table = builder.build();
271 table.with(Style::modern_rounded())
272 .with(Panel::header("📋 Plan"));
273 format!("{table}")
274}
275
276pub fn format_parse_tree(program: &Program) -> String {
277 let json_string = serde_json::to_string_pretty(&program).unwrap();
278
279 let depth = |line: &str|->usize{line.chars().take_while(|&c| c == ' ').count()};
280 let mut result = String::new();
281 let lines: Vec<&str> = json_string.lines().collect();
282 result.push_str("Program\n");
283 for (index, line) in lines.iter().enumerate() {
284 let trm = line.trim();
285 if trm == "}" ||
286 trm == "},"||
287 trm == "{" ||
288 trm == "[" ||
289 trm == "],"||
290 trm == "]" {
291 continue;
292 }
293
294 let d = depth(line);
296 let mut prefix = String::new();
298 for _ in 0..d {
299 prefix.push_str(" ");
300 }
301 if index == lines.len() {
302 prefix.push_str("└ ");
303 } else {
304 if depth(lines[index + 1]) != d {
305 prefix.push_str("└ ");
306 } else {
307 prefix.push_str("├ ");
308 }
309 }
310 let trm = line.trim();
311 let trm = trm.trim_end_matches('{')
312 .trim_start_matches('"')
313 .trim_end_matches(':')
314 .trim_end_matches('"')
315 .trim_end_matches('[');
316 prefix.push_str(trm);
317
318 result.push_str(&prefix);
320 result.push('\n');
321 result = result.replace("\":", "");
322 }
323 let mut indexed_str = IndexedString::new(&result);
324 'rows: for i in 0..indexed_str.rows {
325 let rowz = &indexed_str.index_map[i];
326 for j in 0..rowz.len() {
327 let c = match indexed_str.get(i,j) {
328 Some(c) => c,
329 None => continue,
330 };
331 if c == '└' {
332 for k in i+1..indexed_str.rows {
333 match indexed_str.get(k,j) {
334 Some(c2) => {
335 if c2 == '└' {
336 indexed_str.set(i,j,'├');
337 for l in i+1..k {
338 match indexed_str.get(l,j) {
339 Some(' ') => {indexed_str.set(l,j,'│');},
340 Some('└') => {indexed_str.set(l,j,'├');},
341 _ => (),
342 }
343 }
344 } else if c2 == ' ' {
345 continue;
346 } else {
347 continue 'rows;
348 }
349 },
350 None => continue,
351 }
352
353 }
354 } else if c == ' ' || c == '│' {
355 continue;
356 } else {
357 continue 'rows;
358 }
359 }
360 }
361 indexed_str.to_string()
362}
363
364#[derive(Clone, Debug, PartialEq, Eq)]
365struct IndexedString {
366 pub data: Vec<char>,
367 pub index_map: Vec<Vec<usize>>,
368 pub rows: usize,
369 pub cols: usize,
370}
371
372impl IndexedString {
373
374 fn new(input: &str) -> Self {
375 let mut data = Vec::new();
376 let mut index_map = Vec::new();
377 let mut current_row = 0;
378 let mut current_col = 0;
379 index_map.push(Vec::new());
380 for c in input.chars() {
381 data.push(c);
382 if c == '\n' {
383 index_map.push(Vec::new());
384 current_row += 1;
385 current_col = 0;
386 } else {
387 index_map[current_row].push(data.len() - 1);
388 current_col += 1;
389 }
390 }
391 let rows = index_map.len();
392 let cols = if rows > 0 { index_map[0].len() } else { 0 };
393 IndexedString {
394 data,
395 index_map,
396 rows,
397 cols,
398 }
399 }
400
401 fn to_string(&self) -> String {
402 self.data.iter().collect()
403 }
404
405 fn get(&self, row: usize, col: usize) -> Option<char> {
406 if row < self.rows {
407 let rowz = &self.index_map[row];
408 if col < rowz.len() {
409 let index = self.index_map[row][col];
410 Some(self.data[index])
411 } else {
412 None
413 }
414 } else {
415 None
416 }
417 }
418
419 fn set(&mut self, row: usize, col: usize, new_char: char) -> Result<(), String> {
420 if row < self.rows {
421 let row_indices = &mut self.index_map[row];
422 if col < row_indices.len() {
423 let index = row_indices[col];
424 self.data[index] = new_char;
425 Ok(())
426 } else {
427 Err("Column index out of bounds".to_string())
428 }
429 } else {
430 Err("Row index out of bounds".to_string())
431 }
432 }
433}