vyder 0.3.4

Create custom libraries for vyder.
Documentation
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();

        // TODO: error when multiple modules with same name
        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)
    }
}

/// Helper struct to build a VyderRunner
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
    }
}