use wlambda::vval::Env;
use wlambda::vval::{StackAction, VVal};
use wlambda::compiler::{GlobalEnv, EvalContext, EvalError};
const VERSION: &str = env!("CARGO_PKG_VERSION");
const DOCUMENTATION_JSON: &str = include_str!("cmdline_doc.json");
struct DocSection {
title: String,
body: Vec<String>,
}
impl DocSection {
fn print_title(&self) {
println!("*** {}", self.title);
}
fn print(&self) {
println!("*** {}", self.title);
for l in self.body.iter() {
println!(" {}", l);
}
}
}
struct Doc {
sections: Vec<DocSection>,
}
impl Doc {
fn new(doc: VVal) -> Self {
let mut sections = vec![];
doc.for_each(|v| {
let title = v.v_s_raw(0);
let mut body = vec![];
v.v_(1).for_each(|l| {
l.with_s_ref(|s|{
body.push(s.to_string());
});
});
sections.push(DocSection {
title,
body,
});
});
Self {
sections
}
}
fn print_sections_by_title(&self, parts: &[&str], with_body: bool) {
for section in self.sections.iter() {
let mut all_found = true;
for p in parts {
if !section.title.to_lowercase().contains(&p.to_lowercase()) {
all_found = false;
}
}
if all_found {
if with_body {
section.print();
} else {
section.print_title();
}
}
}
}
fn print_sections_by_title_or_body(&self, parts: &[&str], with_body: bool) {
for section in self.sections.iter() {
let mut all_found = true;
for p in parts {
if !section.title.to_lowercase().contains(&p.to_lowercase()) {
all_found = false;
}
}
if !all_found {
all_found = true;
for p in parts {
for l in section.body.iter() {
if !l.to_lowercase().contains(&p.to_lowercase()) {
all_found = false;
}
}
}
}
if all_found {
if with_body {
section.print();
} else {
section.print_title();
}
}
}
}
}
#[cfg(feature="serde_json")]
fn get_doc() -> VVal {
VVal::from_json(DOCUMENTATION_JSON).unwrap()
}
#[cfg(not(feature="serde_json"))]
fn get_doc() -> VVal {
VVal::None
}
fn main() {
let doc = get_doc();
let doc = Doc::new(doc);
let argv : Vec<String> = std::env::args().collect();
let global = GlobalEnv::new_default();
global.borrow_mut().add_func(
"dump_stack",
move |env: &mut Env, _argc: usize| {
env.dump_stack();
Ok(VVal::None)
}, Some(0), Some(0));
let mut ctx = EvalContext::new(global);
let v_argv = VVal::vec();
for a in argv.iter().skip(1) {
v_argv.push(VVal::new_str(a));
}
ctx.set_global_var("@@", &v_argv);
if argv.len() > 1 {
if argv[1] == "-parse" {
let contents = std::fs::read_to_string(&argv[2]).unwrap();
wlambda::parser::parse(&contents, &argv[2]).expect("successful parse");
} else if argv.len() > 2 && argv[1] == "-e" {
v_argv.delete_key(&VVal::Int(0)).expect("-e argument");
v_argv.delete_key(&VVal::Int(0)).expect("script argument");
match ctx.eval(&argv[2]) {
Ok(v) => { v.with_s_ref(|s| println!("{}", s)); },
Err(e) => { println!("*** {}", e); }
}
} else {
v_argv.delete_key(&VVal::Int(0)).expect("file argument");
match ctx.eval_file(&argv[1]) {
Ok(v) => {
std::process::exit(v.i() as i32);
},
Err(EvalError::ExecError(StackAction::Break(v))) => {
std::process::exit(v.i() as i32);
},
Err(EvalError::ExecError(StackAction::Return(v))) => {
std::process::exit(v.1.i() as i32);
},
Err(e) => {
eprintln!("ERROR: {}", e);
std::process::exit(1);
}
}
}
return;
}
fn handle_doc_commands(doc: &Doc, line: &str) -> bool {
let cmd = line.split_whitespace().collect::<Vec<&str>>();
if cmd.len() > 1 {
match cmd[0] {
"?#" | "?" => {
doc.print_sections_by_title(
&cmd[1..], cmd[0] == "?#");
return true;
},
"?#*" | "?*" => {
doc.print_sections_by_title_or_body(
&cmd[1..], cmd[0] == "?#");
return true;
},
_ => {}
}
} else if cmd.len() == 1 && cmd[0] == "?" {
println!("REPL Usage:");
println!("? <term1> <term2> ... - Search in section headers, display only headers");
println!("?# <term1> <term2> ... - Search in section headers");
println!("?* <term1> <term2> ... - Search in section bodies too, display only headers");
println!("?#* <term1> <term2> ... - Search in section bodies too");
return true;
}
false
}
#[cfg(feature="rustyline")]
{
let mut rl = rustyline::Editor::<()>::new();
if rl.load_history("wlambda.history").is_ok() {
println!("Loaded history from 'wlambda.history' file.");
}
eprintln!("WLambda Version {}", VERSION);
loop {
let readline = rl.readline(">> ");
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str());
if handle_doc_commands(&doc, &line) {
continue;
}
match ctx.eval(&line) {
Ok(v) => {
println!("> {}", v.s());
ctx.set_global_var("@@", &v);
},
Err(e) => { println!("*** {}", e); }
}
},
Err(_) => { break; },
}
}
if rl.save_history("wlambda.history").is_ok() {
println!("Saved history to 'wlambda.history'");
}
}
#[cfg(not (feature="rustyline"))]
{
eprintln!("WLambda Version {}", VERSION);
loop {
use std::io::{self, BufRead};
for line in io::stdin().lock().lines() {
let l = line.unwrap();
if handle_doc_commands(&doc, &l) {
continue;
}
match ctx.eval(&l) {
Ok(v) => {
println!("> {}", v.s());
ctx.set_global_var("@@", &v);
},
Err(e) => { println!("*** {}", e); }
}
}
}
}
}