use crate::*;
use mech_syntax::*;
use mech_core::*;
use mech_interpreter::*;
use std::collections::HashMap;
use std::process;
use nom::{
IResult,
bytes::complete::tag,
branch::alt,
bytes::complete::{take_while, take_until},
combinator::{opt, not},
multi::separated_list1,
character::complete::{space0,space1,digit1},
};
use bincode::serde::encode_to_vec;
use bincode::config::standard;
use include_dir::{include_dir, Dir};
use std::time::{Instant, Duration};
static DOCS_DIR: Dir = include_dir!("docs");
static EXAMPLES_DIR: Dir = include_dir!("examples/working");
pub struct MechRepl {
pub docs: Dir<'static>,
pub examples: Dir<'static>,
pub active: u64,
pub interpreters: HashMap<u64,Interpreter>,
}
impl MechRepl {
pub fn new() -> MechRepl {
let intrp_id = generate_uuid();
let intrp = Interpreter::new(intrp_id);
let mut interpreters = HashMap::new();
interpreters.insert(intrp_id,intrp);
MechRepl {
active: intrp_id,
interpreters,
docs: DOCS_DIR.clone(),
examples: EXAMPLES_DIR.clone(),
}
}
pub fn from(interpreter: Interpreter) -> MechRepl {
let intrp_id = generate_uuid();
let mut interpreters = HashMap::new();
interpreters.insert(intrp_id,interpreter);
MechRepl {
docs: DOCS_DIR.clone(),
examples: EXAMPLES_DIR.clone(),
active: intrp_id,
interpreters,
}
}
pub fn execute_repl_command(&mut self, repl_cmd: ReplCommand) -> MResult<String> {
let mut intrp = self.interpreters.get_mut(&self.active).unwrap();
let mut mechfs = MechFileSystem::new();
match repl_cmd {
ReplCommand::Help => {
return Ok(help());
}
ReplCommand::Quit => {
process::exit(0);
}
ReplCommand::Docs(name) => {
if let Some(name) = name {
let glob = format!("*{}*",name);
for entry in self.docs.find(&glob).unwrap() {
println!("Found {}", entry.path().display());
match entry.as_file() {
Some(file) => {
match file.contents_utf8() {
Some(doc_content) => {
return Ok(format!("{}", doc_content));
},
None => {
return Ok(format!("No documentation found for {}", name));
}
}
},
None => {
return Ok(format!("No documentation found for {}", name));
}
}
}
Ok(format!("No documentation found for {}", name))
} else {
Ok("Enter a doc to search for.".to_string())
}
}
ReplCommand::Symbols(name) => {
#[cfg(feature = "pretty_print")]
let out = intrp.pretty_print_symbols();
#[cfg(not(feature = "pretty_print"))]
let out = format!("{:#?}", intrp.state.borrow().symbols());
return Ok(out);
}
ReplCommand::Plan => {
#[cfg(feature = "pretty_print")]
let out = intrp.plan().pretty_print();
#[cfg(not(feature = "pretty_print"))]
let out = format!("{:#?}", intrp.plan());
return Ok(out);
}
ReplCommand::Whos(names) => {return Ok(whos(&intrp,names));}
ReplCommand::Clear(name) => {
let id = intrp.id;
*intrp = Interpreter::new(id);
return Ok("".to_string());
}
ReplCommand::Ls => {
return Ok(ls());
}
ReplCommand::Cd(path) => {
let path = PathBuf::from(path);
match env::set_current_dir(&path) {
Ok(_) => {
match env::current_dir() {
Ok(current_path) => {
return Ok(format!("{}", current_path.display()));
}
Err(e) => {
return Err(MechError::new(PathNotFound{ file_path: path.display().to_string() }, None).with_compiler_loc());
}
}
}
Err(e) => {
return Err(MechError::new(PathNotFound{ file_path: path.display().to_string() }, None).with_compiler_loc());
}
}
}
#[cfg(feature = "serde")]
ReplCommand::Save(path) => {
let path = PathBuf::from(path);
let intrp = self.interpreters.get(&self.active).unwrap();
let encoded = encode_to_vec(&MechSourceCode::Program(intrp.code.clone()), standard()).unwrap();
let mut file = File::create(&path)?;
file.write_all(&encoded)?;
return Ok(format!("Saved interpreter state to {}", path.display()));
}
ReplCommand::Clc => {
clc();
Ok("".to_string())
},
ReplCommand::Load(paths) => {
for source in paths {
mechfs.watch_source(&source)?;
}
match run_mech_code(&mut intrp, &mechfs, false,false,false,false) {
Ok(r) => {
#[cfg(feature = "pretty_print")]
let out = r.pretty_print();
#[cfg(not(feature = "pretty_print"))]
let out = format!("{:#?}", r);
return Ok(format!("\n{}\n{}\n", r.kind(), r));
},
Err(err) => {return Err(err);}
}
}
ReplCommand::Code(code) => {
for (_,src) in code {
mechfs.add_code(&src)?;
}
match run_mech_code(&mut intrp, &mechfs, false,false,false,false) {
Ok(r) => {
#[cfg(feature = "pretty_print")]
let out = r.pretty_print();
#[cfg(not(feature = "pretty_print"))]
let out = format!("{:#?}", r);
let kind_formatted = format!("{}", r.kind()).ansi_color(218);
return Ok(format!("\n{}\n{}\n", kind_formatted, r));
},
Err(err) => { return Err(err); }
}
}
ReplCommand::Profile(on) => {
intrp.profile = on;
if on {
Ok("Profiling enabled.".to_string())
} else {
Ok("Profiling disabled.".to_string())
}
}
ReplCommand::Step(step_id, step_count) => {
let n: u64 = match step_count {
Some(n) => n,
None => 1,
};
let step_id: usize = match step_id {
Some(id) => id,
None => 0,
};
let now = Instant::now();
intrp.step(step_id, n)?;
let elapsed_time = now.elapsed();
return Ok(format_cycles(n, elapsed_time));
}
x => {
return Err(MechError::new(FeatureNotEnabledError, None).with_compiler_loc());
}
}
}
}
fn format_cycles(n: u64, total_duration: Duration) -> String {
let total_ns = total_duration.as_nanos() as f64;
let total_s = total_ns / 1_000_000_000.0;
let formatted_total = if total_ns >= 1_000_000_000.0 {
format!("{:.3} s", total_s)
} else if total_ns >= 1_000_000.0 {
format!("{:.3} ms", total_ns / 1_000_000.0)
} else if total_ns >= 1_000.0 {
format!("{:.3} µs", total_ns / 1_000.0)
} else {
format!("{:.3} ns", total_ns)
};
let cycle_ns = total_ns / n as f64;
let cycle_s = cycle_ns / 1_000_000_000.0;
let formatted_cycle = if cycle_ns >= 1_000_000_000.0 {
format!("{:.3} s", cycle_s)
} else if cycle_ns >= 1_000_000.0 {
format!("{:.3} ms", cycle_ns / 1_000_000.0)
} else if cycle_ns >= 1_000.0 {
format!("{:.3} µs", cycle_ns / 1_000.0)
} else {
format!("{:.3} ns", cycle_ns)
};
let freq_hz = 1.0 / cycle_s;
let formatted_freq = if freq_hz >= 1_000_000_000.0 {
format!("{:.3} GHz", freq_hz / 1_000_000_000.0)
} else if freq_hz >= 1_000_000.0 {
format!("{:.3} MHz", freq_hz / 1_000_000.0)
} else if freq_hz >= 1_000.0 {
format!("{:.3} kHz", freq_hz / 1_000.0)
} else {
format!("{:.3} Hz", freq_hz)
};
format!(
"{} cycles in {} ({} per cycle, {})",
n, formatted_total, formatted_cycle, formatted_freq
)
}