mod cli;
use std::{io::Write, path::Path, process::ExitCode};
use cli::build_cli;
use crate::Library;
pub struct VyderRunner {
libraries: Vec<Library>,
}
impl VyderRunner {
pub fn builder() -> VyderRunnerBuilder {
VyderRunnerBuilder { libraries: vec![] }
}
pub fn run_with_default_cli(self) -> ExitCode {
let matches = build_cli().get_matches();
match matches.subcommand() {
Some(("run", matches)) => {
let file_path = matches.get_one::<String>("file_path").expect(
"clap should ensure this is set because the argument has `.required(true)`",
);
let source = match std::fs::read_to_string(file_path) {
Ok(source) => source,
Err(e) => {
use std::io::ErrorKind;
let error_message = match e.kind() {
ErrorKind::NotFound => {
format!("file with path '{}' does not exist.", file_path)
}
ErrorKind::PermissionDenied => {
"permission to read the file was denied".to_string()
}
e => e.to_string(),
};
eprintln!("{}", error_message);
return ExitCode::FAILURE;
}
};
self.run_script(&source, file_path)
}
Some(("repl", _)) => self.run_repl(),
_ => unreachable!("clap should only generate valid subcommands"),
}
}
pub fn run_repl(&self) -> ExitCode {
let stdout = &mut std::io::stdout();
let stdin = std::io::stdin();
let mut interpreter = self.make_interpreter(".");
loop {
print!("> ");
stdout.flush().unwrap();
let mut source = String::new();
stdin.read_line(&mut source).unwrap();
if &source == ".exit\n" {
break;
}
let mut lexer = vyder_core::Lexer::new("repl", &source);
let tokens = match lexer.get_tokens() {
Ok(tokens) => tokens,
Err(e) => {
eprintln!("{}", e);
continue;
}
};
let mut parser = vyder_core::Parser::new("repl", &tokens);
let statements = match parser.get_statements() {
Ok(statements) => statements,
Err(e) => {
eprintln!("{}", e);
continue;
}
};
let control_flow = match interpreter.interpret_statements(statements) {
Ok(control_flow) => control_flow,
Err(e) => {
eprintln!("{}", e);
continue;
}
};
match control_flow {
vyder_core::ControlFlow::Return(value) | vyder_core::ControlFlow::Ev(value) => {
println!("{}", value);
}
vyder_core::ControlFlow::Exit(code) => return code,
_ => {}
};
}
ExitCode::SUCCESS
}
pub fn run_script(&self, source: &str, path_to_script: &str) -> ExitCode {
let mut lexer = vyder_core::Lexer::new(path_to_script, source);
let tokens = match lexer.get_tokens() {
Ok(tokens) => tokens,
Err(e) => {
eprintln!("{}", e);
return ExitCode::FAILURE;
}
};
let mut parser = vyder_core::Parser::new(path_to_script, &tokens);
let statements = match parser.get_statements() {
Ok(statements) => statements,
Err(e) => {
eprintln!("{}", e);
return ExitCode::FAILURE;
}
};
let mut interpreter = self.make_interpreter(path_to_script);
let control_flow = match interpreter.interpret_statements(statements) {
Ok(control_flow) => control_flow,
Err(e) => {
eprintln!("{}", e);
return ExitCode::FAILURE;
}
};
match control_flow {
vyder_core::ControlFlow::Return(value) | vyder_core::ControlFlow::Ev(value) => {
println!("{}", value);
}
vyder_core::ControlFlow::Exit(code) => return code,
_ => {}
};
ExitCode::SUCCESS
}
fn make_interpreter(&self, path_to_script: &str) -> vyder_core::Interpreter {
let work_dir = Path::new(path_to_script)
.parent()
.unwrap()
.to_str()
.unwrap();
let mut modules = vec![];
for library in &self.libraries {
for (identifier, module) in &library.modules {
let module = module.to_value_enum();
modules.push((identifier.to_string(), module));
}
}
vyder_core::Interpreter::new(path_to_script, work_dir, modules)
}
}
pub struct VyderRunnerBuilder {
libraries: Vec<Library>,
}
impl VyderRunnerBuilder {
pub fn build(self) -> VyderRunner {
VyderRunner {
libraries: self.libraries,
}
}
pub fn library(mut self, library: Library) -> Self {
self.libraries.push(library);
self
}
}