diff --git a/core/core.mal b/core/core.mal index dbf8bfa..20943d2 100644 --- a/core/core.mal +++ b/core/core.mal @@ -7,7 +7,7 @@ ;; File-interaction functions (def! load-file (fn* [f] - (eval (read-string (str "(do " (slurp f) "\nnil)"))))) + (eval (read-string (str "(do\n" (slurp f) "\nnil)"))))) (def! conf-reload (fn* [] (load-file (str MAL_HOME "/" "config.mal")))) diff --git a/src/core.rs b/src/core.rs index aeb158a..87c4c3a 100644 --- a/src/core.rs +++ b/src/core.rs @@ -33,7 +33,7 @@ use crate::parse_tools::read_file; use crate::printer::pr_str; use crate::reader::{read_str, Reader}; use crate::types::MalType::{Bool, Fun, Int, List, Nil, Str}; -use crate::types::{mal_assert, mal_assert_eq, mal_comp, MalArgs}; +use crate::types::{mal_assert, mal_assert_eq, mal_comp, MalArgs, MalErr}; pub fn ns_init() -> Env { env_init!(None, @@ -62,7 +62,9 @@ pub fn ns_init() -> Env { "=" => Fun(mal_comp, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements"), "assert" => Fun(mal_assert, "Return an error if assertion fails"), "assert-eq" => Fun(mal_assert_eq, "Return an error if arguments are not the same"), - "read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)), "Tokenize and read the first argument"), + // Since the READ errors are Recoverable, when encountering one the reader continues appending to the previous status, + // making unreasonable output, to solve this "upgrade" any kind of error in the inner "read_str" to an unrecoverable one, so the reader breaks the cycle + "read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"), "slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string") ) } diff --git a/src/main.rs b/src/main.rs index f2b6ee4..67cf52c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ mod step6_file; mod types; use core::ns_init; -use parse_tools::{interactive, load_conf, load_core, load_file, set_home_path}; +use parse_tools::{interactive, load_file, load_home_file, set_home_path}; fn main() { // Initialize ns environment @@ -20,11 +20,11 @@ fn main() { // Set the "MAL_HOME" symbol to the specified directory or the default one set_home_path(&reply_env); - // load "$MAL_HOME/core.mal" - load_core(&reply_env); - + // load "$MAL_HOME/core.mal" [warn: true] since this has some core functionalities + load_home_file("core.mal", &reply_env, true); // Load config files ($MAL_HOME/config.mal, or default $HOME/.config/mal/config.mal) - load_conf(&reply_env); + // [warn: false] since this file is optional + load_home_file("config.mal", &reply_env, false); // load all files passed as arguments args().collect::>()[1..].iter().for_each(|f| { diff --git a/src/mal_tests/mod.rs b/src/mal_tests/mod.rs index fdd9b5a..0b980ad 100644 --- a/src/mal_tests/mod.rs +++ b/src/mal_tests/mod.rs @@ -5,10 +5,10 @@ mod functional { ($file:expr) => {{ use crate::core::ns_init; use crate::load_file; - use crate::parse_tools::{load_core, set_home_path}; + use crate::parse_tools::{load_home_file, set_home_path}; let env = ns_init(); set_home_path(&env); - load_core(&env); + load_home_file("core.mal", &env, false); assert!(matches!( load_file(format!("tests/{}.mal", $file).as_str(), &env), Ok(_) diff --git a/src/parse_tools.rs b/src/parse_tools.rs index 8fbcb19..90bd56d 100644 --- a/src/parse_tools.rs +++ b/src/parse_tools.rs @@ -20,28 +20,32 @@ pub fn set_home_path(env: &Env) { Err(_) => env::var("HOME").unwrap() + "/.config/mal", }; + if !Path::new(&home).exists() { + eprintln!("; WARNING: MAL_HOME: \"{}\" does not exist", home); + } + // Add config path to mal eval_str(format!("(def! MAL_HOME \"{home}\")").as_str(), env).unwrap(); } -fn get_home_path(env: &Env) -> Result { - Ok(env_get(env, "MAL_HOME")?.if_string()?.to_string()) +fn get_home_path(env: &Env) -> String { + env_get(env, "MAL_HOME") + .unwrap() + .if_string() + .unwrap() + .to_string() } -pub fn load_core(env: &Env) { - let mut home_path = get_home_path(env).unwrap(); - home_path.push_str("/core.mal"); - load_file(&home_path, env).unwrap(); -} +pub fn load_home_file(filename: &str, env: &Env, warn: bool) { + let full_filename = get_home_path(env) + "/" + filename; -pub fn load_conf(work_env: &Env) { - const CONFIG: &str = "config.mal"; - let config = get_home_path(work_env).unwrap() + "/" + CONFIG; - - if Path::new(&config).exists() { - if let Err(e) = load_file(&config, work_env) { - eprintln!("{}", e.message()) + if Path::new(&full_filename).exists() { + if let Err(e) = load_file(&full_filename, env) { + eprintln!("; reading \"{}\":", full_filename); + eprintln!("{}", e.message()); } + } else if warn { + eprintln!("; WARNING: file \"{}\" does not exist", full_filename); } } diff --git a/src/types.rs b/src/types.rs index 7bdb660..fdd185f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -167,6 +167,11 @@ impl MalErr { self.severity == Severity::Recoverable } + pub fn severe(mut self) -> Self { + self.severity = Severity::Unrecoverable; + self + } + pub fn recoverable(message: &str) -> Self { Self::new(message.to_owned(), Severity::Recoverable) }