Skip to content

Commit 59d1d22

Browse files
committed
test: add repl test && add config & some debug cmd
1 parent 6cf37e8 commit 59d1d22

File tree

6 files changed

+280
-58
lines changed

6 files changed

+280
-58
lines changed

codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ ignore:
1717
- "src/lsp/lspserver.rs"
1818
- "src/lsp/wasm.rs"
1919
- "src/lsp/wasm.rs"
20-
- "src/repl/*.rs"
20+
- "src/repl/editor.rs"

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn main() {
7676
RunCommand::Version => cli.version(),
7777
RunCommand::Repl => {
7878
#[cfg(feature = "repl")]
79-
repl::start_repl();
79+
repl::start_repl(repl::editor::default_editor());
8080
#[cfg(not(feature = "repl"))]
8181
eprintln!("feature repl is not enabled, cannot use repl command");
8282
}

src/repl/editor.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::path::Path;
2+
3+
use rustyline::error::ReadlineError;
4+
5+
use super::completer::REPLCompleter;
6+
7+
pub trait TermEditor {
8+
fn set_helper(&mut self, helper: Option<REPLCompleter>);
9+
fn readline(&mut self, prompt: &str) -> Result<String, ReadlineError>;
10+
/// Load the history from the specified file.
11+
fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError>;
12+
13+
/// Save the history in the specified file.
14+
fn save_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError>;
15+
16+
/// Append new entries in the specified file.
17+
fn append_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError>;
18+
19+
/// Add a new entry in the history.
20+
fn add_history_entry<S: AsRef<str> + Into<String>>(
21+
&mut self,
22+
line: S,
23+
) -> Result<bool, ReadlineError>;
24+
25+
#[cfg(test)]
26+
fn assert_err(&mut self, _err: bool) {
27+
// noop
28+
}
29+
}
30+
31+
pub fn default_editor() -> rustyline::Editor<REPLCompleter, rustyline::history::FileHistory> {
32+
rustyline::Editor::<REPLCompleter, rustyline::history::FileHistory>::new().unwrap()
33+
}
34+
35+
impl TermEditor for rustyline::Editor<REPLCompleter, rustyline::history::FileHistory> {
36+
fn set_helper(&mut self, helper: Option<REPLCompleter>) {
37+
self.set_helper(helper);
38+
}
39+
40+
fn readline(&mut self, prompt: &str) -> Result<String, ReadlineError> {
41+
self.readline(prompt)
42+
}
43+
44+
fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError> {
45+
self.load_history(path)
46+
}
47+
48+
fn save_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError> {
49+
self.save_history(path)
50+
}
51+
52+
fn append_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<(), ReadlineError> {
53+
self.append_history(path)
54+
}
55+
56+
fn add_history_entry<S: AsRef<str> + Into<String>>(
57+
&mut self,
58+
line: S,
59+
) -> Result<bool, ReadlineError> {
60+
self.add_history_entry(line)
61+
}
62+
}

src/repl/mod.rs

Lines changed: 124 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use nom::combinator::{eof, map};
1414
use nom::sequence::terminated;
1515
use notify::{Event, EventKind, RecursiveMode, Watcher};
1616
use rustc_hash::{FxHashMap, FxHashSet};
17-
use rustyline::config::Configurer;
1817
use rustyline::error::ReadlineError;
1918
use ustr::{ustr, Ustr};
2019

@@ -50,7 +49,9 @@ lazy_static::lazy_static! {
5049
pub static ref LOADED_SET:Arc<Mutex<FxHashSet<PathBuf>>> = Arc::new(Mutex::new(FxHashSet::default()));
5150
}
5251

53-
pub fn start_repl() {
52+
pub mod editor;
53+
54+
pub fn start_repl<E: editor::TermEditor>(mut rl: E) {
5455
let mut db = Database::default();
5556
let mut db2 = Database::default();
5657

@@ -130,10 +131,9 @@ project = "repl"
130131

131132
// `()` can be used when no completer is required
132133
let completer = REPLCompleter::new(&mut db2 as _, mem_check);
133-
let mut rl =
134-
rustyline::Editor::<REPLCompleter, rustyline::history::FileHistory>::new().unwrap();
134+
// let mut rl =
135+
// rustyline::Editor::<REPLCompleter, rustyline::history::FileHistory>::new().unwrap();
135136
rl.set_helper(Some(completer));
136-
rl.set_completion_type(rustyline::CompletionType::Circular);
137137
let _ = rl.load_history(&PathBuf::from(&root).join("history.txt"));
138138

139139
loop {
@@ -151,6 +151,13 @@ project = "repl"
151151
if try_parse_commands(&line)
152152
.map(|repl| match repl.command {
153153
repl_cmd::Commands::Load { proj_path, _as } => {
154+
let proj_path = match crate::utils::canonicalize(proj_path) {
155+
Ok(p) => p,
156+
Err(e) => {
157+
eprintln!("Error: {:?}", e);
158+
return;
159+
}
160+
};
154161
if let ControlFlow::Break(_) =
155162
validate_proj_cfg(&proj_path, &db2, &docs2)
156163
{
@@ -170,6 +177,13 @@ project = "repl"
170177
);
171178
}
172179
repl_cmd::Commands::LoadDeps { proj_path } => {
180+
let proj_path = match crate::utils::canonicalize(proj_path) {
181+
Ok(p) => p,
182+
Err(e) => {
183+
eprintln!("Error: {:?}", e);
184+
return;
185+
}
186+
};
173187
match validate_proj_cfg(&proj_path, &db2, &docs2) {
174188
ControlFlow::Continue(cfg) => {
175189
for (as_name, path) in &cfg.deps.unwrap_or_default() {
@@ -181,14 +195,57 @@ project = "repl"
181195
}
182196
repl_cmd::Commands::Reload { file_path } => {
183197
let file_path = file_path.to_str().unwrap();
184-
docs2.lock().unwrap().remove(file_path);
185-
docs.lock().unwrap().remove(file_path);
198+
let file_path = match crate::utils::canonicalize(file_path) {
199+
Ok(p) => p.to_str().unwrap().to_owned(),
200+
Err(e) => {
201+
eprintln!("Error: {:?}", e);
202+
return;
203+
}
204+
};
205+
docs2.lock().unwrap().remove(&file_path);
206+
docs.lock().unwrap().remove(&file_path);
186207
}
187208
repl_cmd::Commands::Watch { dir } => {
209+
let dir = match crate::utils::canonicalize(dir) {
210+
Ok(p) => p.to_str().unwrap().to_owned(),
211+
Err(e) => {
212+
eprintln!("Error: {:?}", e);
213+
return;
214+
}
215+
};
188216
watcher
189217
.watch(Path::new(&dir), RecursiveMode::Recursive)
190218
.expect("watch failed");
191219
}
220+
repl_cmd::Commands::Config => {
221+
let src = docs2.lock().unwrap();
222+
let conf = src.get(REPL_VIRTUAL_CONF).unwrap();
223+
println!("{}", conf.text(&db2));
224+
}
225+
repl_cmd::Commands::Symbol => {
226+
let vars = REPL_VARIABLES.lock().unwrap();
227+
let mut src = "".to_string();
228+
for (k, _) in vars.iter() {
229+
src.push_str(&format!("println!(\"{}: \", {});\n", k, k));
230+
}
231+
drop(vars);
232+
let id =
233+
REPL_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
234+
let anon_fn = format!(
235+
"{}\n pub fn __anon__{}() void {{ \n{};\nreturn; }}",
236+
used_headers, id, src
237+
);
238+
docs.lock()
239+
.unwrap()
240+
.get(REPL_VIRTUAL_ENTRY)
241+
.unwrap()
242+
.set_text(&mut db)
243+
.to(anon_fn.clone());
244+
mem.set_docs(&mut db)
245+
.to(Arc::new(Mutex::new(docs.lock().unwrap().clone())));
246+
let _ = compiler::compile_dry(&db, mem).unwrap();
247+
load_mod_and_evaluate(&db, mem, &ctx);
248+
}
192249
})
193250
.is_some()
194251
{
@@ -268,6 +325,10 @@ project = "repl"
268325
let mut errs_num = 0;
269326
let mut warn_num = 0;
270327
print_diags(diags, mem_check, &db2, &mut errs_num, &mut warn_num, true);
328+
#[cfg(test)]
329+
{
330+
rl.assert_err(errs_num > 0);
331+
}
271332
if errs_num > 0 {
272333
REPL_COUNTER.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
273334
continue;
@@ -300,55 +361,7 @@ project = "repl"
300361
.unwrap()
301362
.extend(plmodule.global_table.clone());
302363

303-
let mods = compiler::compile_dry::accumulated::<ModBuffer>(&db, mem);
304-
305-
for m in &mods {
306-
if !m.name.starts_with("__anon__") {
307-
if LOADED_SET.lock().unwrap().contains(&m.path) {
308-
continue;
309-
}
310-
unsafe {
311-
let mo = Module::parse_bitcode_from_buffer(
312-
&MemoryBuffer::create_from_memory_range(
313-
&m.buf,
314-
m.path.file_name().unwrap().to_str().unwrap(),
315-
),
316-
&ctx,
317-
)
318-
.unwrap();
319-
let p = mo.as_mut_ptr();
320-
mo.strip_debug_info();
321-
log::trace!("Loaded module, content:\n{}", mo.to_string());
322-
LOADED_SET.lock().unwrap().insert(m.path.clone());
323-
immix::AddModuleToOrcJIT(p as _);
324-
// Owned by the JIT now
325-
std::mem::forget(mo);
326-
327-
log::info!("Loaded module: {:?}", m.name);
328-
};
329-
}
330-
}
331-
for m in &mods {
332-
if m.name.starts_with("__anon__") {
333-
unsafe {
334-
let m = Module::parse_bitcode_from_buffer(
335-
&MemoryBuffer::create_from_memory_range(
336-
&m.buf,
337-
m.path.file_name().unwrap().to_str().unwrap(),
338-
),
339-
&ctx,
340-
)
341-
.unwrap();
342-
343-
let p = m.as_mut_ptr();
344-
m.strip_debug_info();
345-
log::trace!("Evaluate module, content:\n{}", m.to_string());
346-
immix::RunExpr(p as _);
347-
// Owned by the JIT now
348-
std::mem::forget(m);
349-
}
350-
}
351-
}
364+
load_mod_and_evaluate(&db, mem, &ctx);
352365
}
353366
Err(ReadlineError::Interrupted) => {
354367
println!("CTRL-C");
@@ -368,6 +381,58 @@ project = "repl"
368381
// exit(0);
369382
}
370383

384+
fn load_mod_and_evaluate(db: &Database, mem: MemDocsInput, ctx: &Context) {
385+
let mods = compiler::compile_dry::accumulated::<ModBuffer>(db, mem);
386+
387+
for m in &mods {
388+
if !m.name.starts_with("__anon__") {
389+
if LOADED_SET.lock().unwrap().contains(&m.path) {
390+
continue;
391+
}
392+
unsafe {
393+
let mo = Module::parse_bitcode_from_buffer(
394+
&MemoryBuffer::create_from_memory_range(
395+
&m.buf,
396+
m.path.file_name().unwrap().to_str().unwrap(),
397+
),
398+
ctx,
399+
)
400+
.unwrap();
401+
let p = mo.as_mut_ptr();
402+
mo.strip_debug_info();
403+
log::trace!("Loaded module, content:\n{}", mo.to_string());
404+
LOADED_SET.lock().unwrap().insert(m.path.clone());
405+
immix::AddModuleToOrcJIT(p as _);
406+
// Owned by the JIT now
407+
std::mem::forget(mo);
408+
409+
log::info!("Loaded module: {:?}", m.name);
410+
};
411+
}
412+
}
413+
for m in &mods {
414+
if m.name.starts_with("__anon__") {
415+
unsafe {
416+
let m = Module::parse_bitcode_from_buffer(
417+
&MemoryBuffer::create_from_memory_range(
418+
&m.buf,
419+
m.path.file_name().unwrap().to_str().unwrap(),
420+
),
421+
ctx,
422+
)
423+
.unwrap();
424+
425+
let p = m.as_mut_ptr();
426+
m.strip_debug_info();
427+
log::trace!("Evaluate module, content:\n{}", m.to_string());
428+
immix::RunExpr(p as _);
429+
// Owned by the JIT now
430+
std::mem::forget(m);
431+
}
432+
}
433+
}
434+
}
435+
371436
fn validate_proj_cfg(
372437
proj_path: &PathBuf,
373438
db2: &Database,
@@ -477,3 +542,6 @@ fn new_watcher(sender: Sender<PathBuf>) -> notify::RecommendedWatcher {
477542
})
478543
.expect("create watcher failed")
479544
}
545+
546+
#[cfg(test)]
547+
mod test;

src/repl/repl_cmd.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ pub enum Commands {
3535
/// directory
3636
dir: String,
3737
},
38+
/// Print current config
39+
Config,
40+
/// Print symbol table
41+
Symbol,
3842
}
3943

4044
#[cfg(test)]

0 commit comments

Comments
 (0)