mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
EPrintlns and libs
- Moving all repl prints to stderr - Adding small functions to module string
This commit is contained in:
@ -71,6 +71,10 @@
|
|||||||
"open a mal file and evaluate its content"
|
"open a mal file and evaluate its content"
|
||||||
(eval (read-string (str "(do " (slurp f) "\nnil)")))))
|
(eval (read-string (str "(do " (slurp f) "\nnil)")))))
|
||||||
|
|
||||||
|
(def! module (fn* [f]
|
||||||
|
"load a file from the library path"
|
||||||
|
(load-file (str MAL_HOME "/libs/" f ".mal"))))
|
||||||
|
|
||||||
(def! conf-reload (fn* []
|
(def! conf-reload (fn* []
|
||||||
"reload mal config file"
|
"reload mal config file"
|
||||||
(load-file (str MAL_HOME "/" "config.mal"))))
|
(load-file (str MAL_HOME "/" "config.mal"))))
|
||||||
@ -139,8 +143,8 @@
|
|||||||
(def! BANNER
|
(def! BANNER
|
||||||
(str
|
(str
|
||||||
"; rust-mal: a toy lisp interpreter written in rust\n"
|
"; rust-mal: a toy lisp interpreter written in rust\n"
|
||||||
"; $ mal [filename ...] : load specified modules at start\n"
|
"; $ mal [filename ...] : load specified files at start\n"
|
||||||
"; (load-file <name>) : load specified module while mal is running\n"
|
"; (load-file <name>) : load specified file while mal is running\n"
|
||||||
"; (find [pattern...]) : list symbols matching all patterns\n"
|
"; (find [pattern...]) : list symbols matching all patterns\n"
|
||||||
"; (help <symbol>) : print information about a symbol\n"
|
"; (help <symbol>) : print information about a symbol\n"
|
||||||
";\n"
|
";\n"
|
||||||
@ -186,3 +190,5 @@
|
|||||||
c
|
c
|
||||||
(collect-r (f c (car l)) (cdr l)))))
|
(collect-r (f c (car l)) (cdr l)))))
|
||||||
(collect-r i l)))
|
(collect-r i l)))
|
||||||
|
|
||||||
|
(conf-reload)
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
(def! char (fn* [l]
|
(def! char (fn* [l]
|
||||||
(car (boom l))))
|
(car (boom l))))
|
||||||
|
|
||||||
|
(def! #n (char "\n"))
|
||||||
|
(def! #s (char " "))
|
||||||
|
(def! #t (char "\t"))
|
||||||
|
|
||||||
(def! char? (fn* [a]
|
(def! char? (fn* [a]
|
||||||
(= (type a) :char)))
|
(= (type a) :char)))
|
||||||
|
|
||||||
(def! string? (fn* [a]
|
(def! string? (fn* [a]
|
||||||
(= (type a) :string)))
|
(= (type a) :string)))
|
||||||
|
|
||||||
|
(def! strlen (fn* [s] (count (boom s))))
|
||||||
|
|
||||||
(def! strc (fn* [l]
|
(def! strc (fn* [l]
|
||||||
(def! strc-r (fn* [l s]
|
(def! strc-r (fn* [l s]
|
||||||
(if (empty? l)
|
(if (empty? l)
|
||||||
@ -38,3 +44,5 @@
|
|||||||
|
|
||||||
(def! chsub (fn* [s c1 c2]
|
(def! chsub (fn* [s c1 c2]
|
||||||
(strc (map-if (fn* [x] (= x c1)) (fn* [x] c2) (boom s)))))
|
(strc (map-if (fn* [x] (= x c1)) (fn* [x] c2) (boom s)))))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,7 @@ pub fn ns_init() -> Env {
|
|||||||
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
||||||
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
||||||
// A tribute to PHP's explode (PHP, a language I never used)
|
// A tribute to PHP's explode (PHP, a language I never used)
|
||||||
"boom" => Fun(mal_boom, "Split a string into a list of string\n; BE CAREFUL WHEN USING"),
|
"boom" => Fun(mal_boom, "Split a string into a list of characters\n; BE CAREFUL WHEN USING"),
|
||||||
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
|
"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"),
|
"slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string"),
|
||||||
"atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a).unwrap_or_default().clone())))), "Return an atom pointing to the given arg"),
|
"atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a).unwrap_or_default().clone())))), "Return an atom pointing to the given arg"),
|
||||||
|
|||||||
@ -129,7 +129,7 @@ pub fn help_form(list: &[MalType], env: Env) -> MalRet {
|
|||||||
let (sym, _) = car_cdr(list)?;
|
let (sym, _) = car_cdr(list)?;
|
||||||
let sym_str = sym.if_symbol()?;
|
let sym_str = sym.if_symbol()?;
|
||||||
match eval(sym, env.clone())? {
|
match eval(sym, env.clone())? {
|
||||||
M::Fun(_, desc) => println!("{}\t[builtin]: {}\n", sym_str, desc),
|
M::Fun(_, desc) => eprintln!("{}\t[builtin]: {}\n", sym_str, desc),
|
||||||
M::MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
M::MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
||||||
_ => eprintln!("{}\t[symbol]: {}\n", sym_str, prt(&env_get(&env, sym_str)?)),
|
_ => eprintln!("{}\t[symbol]: {}\n", sym_str, prt(&env_get(&env, sym_str)?)),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,12 +24,15 @@ fn main() {
|
|||||||
load_home_file("core.mal", &reply_env, true);
|
load_home_file("core.mal", &reply_env, true);
|
||||||
// Load config files ($MAL_HOME/config.mal, or default $HOME/.config/mal/config.mal)
|
// Load config files ($MAL_HOME/config.mal, or default $HOME/.config/mal/config.mal)
|
||||||
// [warn: false] since this file is optional
|
// [warn: false] since this file is optional
|
||||||
load_home_file("config.mal", &reply_env, false);
|
// replaced by (ok? '(reload-config)) at the end of core.mal
|
||||||
|
// - I used this to overwrite BANNER to prevent it from displaying
|
||||||
|
// based on conf
|
||||||
|
//load_home_file("config.mal", &reply_env, false);
|
||||||
|
|
||||||
// load all files passed as arguments
|
// load all files passed as arguments
|
||||||
args().collect::<Vec<String>>()[1..].iter().for_each(|f| {
|
args().collect::<Vec<String>>()[1..].iter().for_each(|f| {
|
||||||
if let Err(e) = load_file(f, &reply_env) {
|
if let Err(e) = load_file(f, &reply_env) {
|
||||||
println!("{}", e.message())
|
eprintln!("{}", e.message())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,8 @@ mod functional {
|
|||||||
));
|
));
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
// TODO: modify to accept more parameters for test/libraries
|
||||||
|
// TODO: text 'boom' from within rust
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn assert() {
|
fn assert() {
|
||||||
|
|||||||
@ -109,16 +109,19 @@ pub fn interactive(env: Env) {
|
|||||||
|
|
||||||
// Perform rep on whole available input
|
// Perform rep on whole available input
|
||||||
match rep(&parser, &env) {
|
match rep(&parser, &env) {
|
||||||
Ok(output) => output.iter().for_each(|el| println!("; [{}]> {}", num, el)),
|
Ok(output) => output.iter().for_each(|el| {
|
||||||
|
eprintln!("; [{}]> {}", num, el);
|
||||||
|
num += 1;
|
||||||
|
}),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
if error.is_recoverable() {
|
if error.is_recoverable() {
|
||||||
// && line != "\n" {
|
// && line != "\n" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
eprintln!("; [{}]> Error @ {}", num, error.message());
|
eprintln!("; [{}]> Error @ {}", num, error.message());
|
||||||
|
num += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
num += 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
|
|||||||
@ -98,14 +98,16 @@ impl Reader {
|
|||||||
tk => {
|
tk => {
|
||||||
if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) {
|
if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) {
|
||||||
return Ok(Int(tk.parse::<isize>().unwrap()));
|
return Ok(Int(tk.parse::<isize>().unwrap()));
|
||||||
} else if tk.starts_with('\"') {
|
}
|
||||||
|
if tk.starts_with('\"') {
|
||||||
if tk.len() > 1 && tk.ends_with('\"') {
|
if tk.len() > 1 && tk.ends_with('\"') {
|
||||||
return Ok(Str(unescape_str(tk).into()));
|
return Ok(Str(unescape_str(tk).into()));
|
||||||
}
|
}
|
||||||
return Err(MalErr::unrecoverable(
|
return Err(MalErr::unrecoverable(
|
||||||
"End of line reached without closing string",
|
"End of line reached without closing string",
|
||||||
));
|
));
|
||||||
} else if tk.starts_with(':') {
|
}
|
||||||
|
if tk.starts_with(':') {
|
||||||
return Ok(Key(format!("ʞ{}", tk).into()));
|
return Ok(Key(format!("ʞ{}", tk).into()));
|
||||||
}
|
}
|
||||||
Ok(Sym(tk.into()))
|
Ok(Sym(tk.into()))
|
||||||
|
|||||||
@ -220,6 +220,8 @@ pub fn escape_str(s: &str) -> String {
|
|||||||
String::from(s)
|
String::from(s)
|
||||||
.replace('\\', "\\\\")
|
.replace('\\', "\\\\")
|
||||||
.replace('\n', "\\n")
|
.replace('\n', "\\n")
|
||||||
|
.replace('\u{000D}', "\\r")
|
||||||
|
.replace('\t', "\\t")
|
||||||
.replace('\"', "\\\"")
|
.replace('\"', "\\\"")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -228,6 +230,8 @@ pub fn unescape_str(s: &str) -> String {
|
|||||||
String::from(&s[1..s.len() - 1])
|
String::from(&s[1..s.len() - 1])
|
||||||
.replace("\\\\", "\\")
|
.replace("\\\\", "\\")
|
||||||
.replace("\\n", "\n")
|
.replace("\\n", "\n")
|
||||||
|
.replace("\\r", "\r")
|
||||||
|
.replace("\\t", "\t")
|
||||||
.replace("\\\"", "\"")
|
.replace("\\\"", "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user