Skip to main content

mech_wasm/
repl.rs

1use crate::*;
2use gloo_net::http::Request;
3use wasm_bindgen_futures::spawn_local;
4
5use crate::CURRENT_MECH;
6
7pub fn execute_repl_command(repl_cmd: ReplCommand) -> String {
8  match repl_cmd {
9    #[cfg(feature = "clear")]
10    ReplCommand::Clear(_) => {
11      CURRENT_MECH.with(|mech_ref| {
12        if let Some(ptr) = *mech_ref.borrow() {
13          unsafe {
14            let mut mech = &mut *ptr;
15            mech.interpreter.clear();
16          }
17        }
18      });
19      "".to_string()
20    }
21    #[cfg(feature = "clc")]
22    ReplCommand::Clc => {
23      CURRENT_MECH.with(|mech_ref| {
24        if let Some(ptr) = *mech_ref.borrow() {
25          unsafe {
26            let mut mech = &mut *ptr;
27            let window = web_sys::window().expect("global window does not exists");    
28            let document = window.document().expect("expecting a document on window");
29            let output_element = document.get_element_by_id(&mech.repl_id.as_ref().unwrap().clone()).expect("REPL output element not found");
30            // Remove all children
31            while output_element.child_nodes().length() > 0 {
32              let first_child = output_element
33                .first_child()
34                .expect("Expected a child node");
35              output_element
36                .remove_child(&first_child)
37                .expect("Failed to remove child");
38            }
39          }
40          return "".to_string();
41        }
42        "Error: No interpreter found.".to_string()
43      })
44    }
45    #[cfg(feature = "code")]
46    ReplCommand::Code(code) => {
47      CURRENT_MECH.with(|mech_ref| {
48        if let Some(ptr) = *mech_ref.borrow() {
49          unsafe {
50            let mut mech = &mut *ptr;
51            match run_mech_code(&mut mech.interpreter, &code)  {
52              Ok(output) => { 
53                let kind_str = html_escape(&format!("{}",output.kind()));
54                return format!("<div class=\"mech-output-kind\">{}</div><div class=\"mech-output-value\">{}</div>", kind_str, output.to_html());
55              },
56              Err(err) => { return format!("{:?}",err); }
57            }
58          }
59        }
60        "Error: No interpreter found.".to_string()
61      })
62    }
63    #[cfg(feature = "step")]
64    ReplCommand::Step(count) => {
65      CURRENT_MECH.with(|mech_ref| {
66        if let Some(ptr) = *mech_ref.borrow() {
67          unsafe {
68            let mut mech = &mut *ptr;
69            let n = match count {
70              Some(n) => n,
71              None => 1,
72            };
73            mech.interpreter.step(n as u64);
74            return format!("<div class=\"mech-output-kind\">Step</div><div class=\"mech-output-value\">Executed {} step(s).</div>",n);
75          }
76        }
77        "Error: No interpreter found.".to_string()
78      })
79    }
80    #[cfg(feature = "whos")]
81    ReplCommand::Whos(names) => {
82      CURRENT_MECH.with(|mech_ref| {
83        if let Some(ptr) = *mech_ref.borrow() {
84          unsafe {
85            let mut mech = &mut *ptr;
86            return whos_html(&mech.interpreter, names)
87          }
88        }
89        "Error: No interpreter found.".to_string()
90      })
91    }
92    #[cfg(feature = "help")]
93    ReplCommand::Help => {
94      help_html()
95    }
96    #[cfg(feature = "docs")]
97    ReplCommand::Docs(doc) => {
98      match doc {
99        Some(d) => {
100          CURRENT_MECH.with(|mech_ref| {
101            if let Some(ptr) = *mech_ref.borrow() {
102              unsafe {
103                let mut mech = &mut *ptr;
104                load_doc(&d, mech.repl_id.as_ref().unwrap().clone());
105              }
106            }
107            "Error: No interpreter found.".to_string()
108          });
109          format!("Fetching doc: {}...", d)
110        },
111        None => "Enter the name of a doc to load.".to_string(),
112      } 
113    }
114    x => format!("Command {:?} not implemented for wasm", x),
115  }
116}
117
118// Print out help information in HTML format
119#[cfg(feature = "help")]
120#[wasm_bindgen]
121pub fn help_html() -> String {
122  let text_logo = r#"
123┌─────────┐ ┌──────┐ ┌─┐ ┌──┐ ┌─┐  ┌─┐
124└───┐ ┌───┘ └──────┘ │ │ └┐ │ │ │  │ │
125┌─┐ │ │ ┌─┐ ┌──────┐ │ │  └─┘ │ └─┐│ │
126│ │ │ │ │ │ │ ┌────┘ │ │  ┌─┐ │ ┌─┘│ │
127│ │ └─┘ │ │ │ └────┐ │ └──┘ │ │ │  │ │
128└─┘     └─┘ └──────┘ └──────┘ └─┘  └─┘"#;
129  let version = env!("CARGO_PKG_VERSION");
130  let mut html = String::new();
131  
132  html.push_str("<div class=\"mech-help\">");
133  html.push_str(&format!("<div class=\"mech-text-logo\">{}</div>", text_logo));  
134  html.push_str(&format!("<div class=\"mech-version\">Version: <a href=\"https://github.com/mech-lang/mech/releases/tag/v{}-beta\">{}</a></div>", version, version));
135  html.push_str("<p>Welcome to the Mech REPL!</p>");
136  html.push_str("<p>Full documentation: <a href=\"https://docs.mech-lang.org\">docs.mech-lang.org</a>.</p>"); 
137  html.push_str("<table class=\"mech-help-table\">");
138    html.push_str("<thead><tr><th>Command</th><th>Short</th><th>Options</th><th>Description</th></tr></thead>");
139    html.push_str("<tbody>");
140    html.push_str("<tr><td><span class=\"mech-command\">:clc</span></td><td></span></td><td></td><td>Clear the REPL output.</td></tr>");
141    html.push_str("<tr><td><span class=\"mech-command\">:clear</span><td></span></td></td><td></td><td>Clear the interpreter state.</td></tr>");
142    html.push_str("<tr><td><span class=\"mech-command\">:docs</span></td><td></td><td><span class=\"mech-command\">[doc]</span></td><td>Display the given doc in the REPL.</td></tr>");
143    html.push_str("<tr><td><span class=\"mech-command\">:help</span></td><td><span class=\"mech-command\">:h</span></td><td></td><td>Show this help message.</td></tr>");
144    html.push_str("<tr><td><span class=\"mech-command\">:step</span></td><td></td><td><span class=\"mech-command\">[count]</span></td><td>Run the plan for a specified number of steps.</td></tr>");
145    html.push_str("<tr><td><span class=\"mech-command\">:whos</span></td><td><span class=\"mech-command\">:w</span></td><td><span class=\"mech-command\">[names...]</span></td><td>Show the current symbol directory.</td></tr>");
146    html.push_str("</tbody>");
147  html.push_str("</table>");
148  html.push_str("</div>");
149  html
150}
151
152#[cfg(feature = "whos")]
153pub fn whos_html(intrp: &Interpreter, names: Vec<String>) -> String {
154  let state_brrw = intrp.state.borrow();
155  let symbol_table = state_brrw.symbol_table.borrow();
156  let dictionary = symbol_table.dictionary.borrow();
157
158  // Collect matching symbols into a vector
159  let mut rows = Vec::new();
160
161  if !names.is_empty() {
162    for target_name in &names {
163      for (id, name) in dictionary.iter() {
164        if *name == *target_name {
165          if let Some(value_ref) = symbol_table.symbols.get(id) {
166            let value = value_ref.borrow();
167            rows.push((name.clone(), value.clone()));
168          }
169          break;
170        }
171      }
172    }
173  } else {
174    // Show all symbols
175    for (id, value_ref) in symbol_table.symbols.iter() {
176      if let Some(name) = dictionary.get(id) {
177        let value = value_ref.borrow();
178        rows.push((name.clone(), value.clone()));
179      }
180    }
181  }
182
183  if rows.is_empty() {
184    if names.is_empty() {
185      "No symbols found in the document.".to_string()
186    } else {
187      format!("Symbol(s) {:?} not found in the document.", names)
188    }
189  } else {
190    // Build the table only if there are entries
191    let mut html = String::new();
192    html.push_str("<table class=\"mech-table\">");
193    html.push_str("<thead class=\"mech-table-header\"><tr>");
194    html.push_str("<th class=\"mech-table-field\">Name</th>");
195    html.push_str("<th class=\"mech-table-field\">Size</th>");
196    html.push_str("<th class=\"mech-table-field\">Bytes</th>");
197    html.push_str("<th class=\"mech-table-field\">Kind</th>");
198    html.push_str("</tr></thead>");
199    html.push_str("<tbody class=\"mech-table-body\">");
200
201    for (name, value) in rows {
202      append_row(&mut html, &name, &value);
203    }
204
205    html.push_str("</tbody></table>");
206    html
207  }
208}
209
210
211fn append_row(html: &mut String, name: &str, value: &Value) {
212  let name = html_escape(name);
213  let size = html_escape(&format!("{:?}", value.shape()));
214  let bytes = html_escape(&format!("{}", value.size_of()));
215  let kind = html_escape(&format!("{}", value.kind()));
216
217  html.push_str("<tr class=\"mech-table-row\">");
218
219  let id = hash_str(&name);
220  html.push_str(&format!("<td class=\"mech-table-column\"><span class=\"mech-var-name mech-clickable\" id=\"{}:0\">{}</span></td>",id, name));
221  html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", size));
222  html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", bytes));
223  html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", kind));
224  html.push_str("</tr>");
225}
226
227pub fn html_escape(input: &str) -> String {
228  input
229    .replace('&', "&amp;")
230    .replace('<', "&lt;")
231    .replace('>', "&gt;")
232}