mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Adding some info functions and tests
This commit is contained in:
@ -39,6 +39,9 @@
|
||||
(def! list? (fn* [a]
|
||||
(= (type a) :list)))
|
||||
|
||||
(def! atom? (fn* [a]
|
||||
(= (type a) :atom)))
|
||||
|
||||
(def! empty? (fn* [l]
|
||||
(= (count l) 0)))
|
||||
|
||||
@ -61,3 +64,68 @@
|
||||
|
||||
;; variables
|
||||
(def! MAL_HISTORY (str MAL_HOME "/" ".mal-history"))
|
||||
|
||||
;; helper functions
|
||||
; these functions are never called, their symbols should always resolve
|
||||
; in a special form, they are here only to provide informations through
|
||||
; the "find" and "help" functions
|
||||
(def! def! (fn* [symbol value] "==SPECIAL FORM=="
|
||||
"<symbol>: Sym"
|
||||
"assign <value> to <symbol> in the current environment"
|
||||
"#returns: <value>"))
|
||||
|
||||
(def! let* (fn* [binding statement...] "==SPECIAL FORM=="
|
||||
"<bindings>: Vec"
|
||||
"create a new environment and assign values to symbols according"
|
||||
"to the <binding> vector then evaluate each <statement>"
|
||||
"#returns: result of the last evaluation"))
|
||||
|
||||
(def! do (fn* [statement...] "==SPECIAL FORM=="
|
||||
"evaluate each <statement> in the current environment"
|
||||
"#returns: result of the last evalutaion"))
|
||||
|
||||
(def! if (fn* [condition if-true if-false] "==SPECIAL FORM=="
|
||||
"first evaluate <condition>, based on the result of evaluation"
|
||||
"evaluates one of the two conditional branches, a missing branch"
|
||||
"evaluates to NIL"
|
||||
"#returns: result of the last evaluation"))
|
||||
|
||||
(def! fn* (fn* [arguments statement...] "==SPECIAL FORM=="
|
||||
"arguments: Vec"
|
||||
"#alias: λ" ; >:3
|
||||
"#returns: new lambda that accepts <arguments>, evaluates each"
|
||||
" : <statement> and returns the last evaluation's result"))
|
||||
|
||||
(def! help (fn* [symbol] "==SPECIAL FORM=="
|
||||
"symbol: Sym"
|
||||
"display an helper f or the specified symbol"
|
||||
"#alias: h"
|
||||
"#returns: NIL"))
|
||||
|
||||
(def! find (fn* [substring...] "==SPECIAL FORM=="
|
||||
"print all the known symbols partially matching <substring> in"
|
||||
"the current environment"
|
||||
"#alias f"
|
||||
"#returns: NIL"))
|
||||
|
||||
(def! quote (fn* [statement] "==SPECIAL FORM=="
|
||||
"prevents <statement> from being evaluated, it's possible to use"
|
||||
"the ' symbol: 'sym is equivalent to (quote sym)"))
|
||||
|
||||
(def! ok? (fn* [statement] "==SPECIAL FORM=="
|
||||
"evaluate <statement>"
|
||||
"#returns: true if evaluation succeeds, NIL otherwise"))
|
||||
|
||||
(def! eval (fn* [statement] "==SPECIAL FORM=="
|
||||
"evaluate <statement>"
|
||||
"#returns: the result of the evaluation"))
|
||||
|
||||
(def! BANNER
|
||||
(str
|
||||
"; rust-mal: a toy lisp interpreter written in rust\n"
|
||||
"; (find [pattern...]) : list symbols matching all patterns\n"
|
||||
"; (help <symbol>) : print information about a symbol\n"
|
||||
";\n"
|
||||
"; enjoy ^.^\n"))
|
||||
|
||||
(println BANNER)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::env;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::env::{any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env};
|
||||
|
||||
@ -34,7 +35,7 @@ macro_rules! env_init {
|
||||
use crate::parse_tools::read_file;
|
||||
use crate::printer::pr_str;
|
||||
use crate::reader::{read_str, Reader};
|
||||
use crate::types::MalType::{Fun, Int, List, Nil, Str};
|
||||
use crate::types::MalType::{Atom, Fun, Int, List, Nil, Str};
|
||||
use crate::types::{mal_assert, mal_equals, MalErr};
|
||||
|
||||
pub fn ns_init() -> Env {
|
||||
@ -62,6 +63,8 @@ pub fn ns_init() -> Env {
|
||||
"assert" => Fun(mal_assert, "Return an error if assertion fails"),
|
||||
"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"),
|
||||
"atom" => Fun(|a| Ok(Atom(Rc::new(car(a)?.clone()))), "Return an atom pointing to the given arg"),
|
||||
"deref" => Fun(|a| Ok(car(a)?.if_atom()?.clone()), "Return the content of the atom argumet"),
|
||||
"env" => Fun(|a| match env::var(car(a)?.if_string()?) {
|
||||
Ok(s) => Ok(Str(s.into())),
|
||||
_ => Ok(Nil),
|
||||
|
||||
@ -21,6 +21,7 @@ forms!(NAME_DEF : "def!",
|
||||
NAME_FN : "fn*",
|
||||
NAME_FN_ALT : "λ",
|
||||
NAME_HELP : "help",
|
||||
NAME_HELP_ALT: "h",
|
||||
NAME_FIND : "find",
|
||||
NAME_QUOTE : "quote",
|
||||
NAME_OK : "ok?",
|
||||
@ -188,7 +189,7 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
NAME_FN | NAME_FN_ALT /* :) */ => {
|
||||
return fn_star_form(args, env.clone())
|
||||
}
|
||||
NAME_HELP => return help_form(args, env.clone()),
|
||||
NAME_HELP | NAME_HELP_ALT => return help_form(args, env.clone()),
|
||||
NAME_FIND => return find_form(args, env.clone()),
|
||||
// Oh God, what have I done
|
||||
NAME_QUOTE => return Ok(car(args)?.clone()),
|
||||
|
||||
@ -45,4 +45,14 @@ mod functional {
|
||||
fn forms() {
|
||||
test!("forms")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lists() {
|
||||
test!("lists")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atoms() {
|
||||
test!("atoms")
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ pub fn interactive(env: Env) {
|
||||
// // Read line to compose program input
|
||||
// let mut line = String::new();
|
||||
// io::stdin().read_line(&mut line).unwrap();
|
||||
let line = rl.readline("user> ");
|
||||
let line = rl.readline("; user> ");
|
||||
|
||||
match line {
|
||||
Ok(line) => {
|
||||
@ -105,7 +105,7 @@ pub fn interactive(env: Env) {
|
||||
|
||||
// Perform rep on whole available input
|
||||
match rep(&parser, &env) {
|
||||
Ok(output) => output.iter().for_each(|el| eprintln!("[{}]> {}", num, el)),
|
||||
Ok(output) => output.iter().for_each(|el| println!("; [{}]> {}", num, el)),
|
||||
Err(error) => {
|
||||
if error.is_recoverable() {
|
||||
// && line != "\n" {
|
||||
|
||||
@ -53,6 +53,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
),
|
||||
M::Fun(..) => "#<builtin>".to_string(),
|
||||
M::MalFun { .. } => "#<function>".to_string(),
|
||||
M::Atom(sub) => format!("Atom({})", pr_str(sub, print_readably)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +62,7 @@ pub fn prt(ast: &MalType) -> String {
|
||||
}
|
||||
|
||||
pub fn print_malfun(sym: &str, params: Rc<MalType>, ast: Rc<MalType>) {
|
||||
println!("{}\t[function]: {}", sym, prt(¶ms));
|
||||
println!("; {}\t[function]: {}", sym, prt(¶ms));
|
||||
ast.as_ref()
|
||||
.if_list()
|
||||
.unwrap_or(&[])
|
||||
|
||||
13
src/types.rs
13
src/types.rs
@ -25,6 +25,7 @@ pub enum MalType {
|
||||
Str(MalStr),
|
||||
Int(isize),
|
||||
Bool(bool),
|
||||
Atom(Rc<MalType>),
|
||||
Nil,
|
||||
}
|
||||
|
||||
@ -74,6 +75,15 @@ impl MalType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_atom(&self) -> Result<&MalType, MalErr> {
|
||||
match self {
|
||||
Self::Atom(sym) => Ok(sym),
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!("{:?} is not an atom", prt(self)).as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label_type(&self) -> MalType {
|
||||
Key(match self {
|
||||
M::Nil => "ʞ:nil",
|
||||
@ -83,9 +93,10 @@ impl MalType {
|
||||
M::Key(_) => "ʞ:key",
|
||||
M::Str(_) => "ʞ:string",
|
||||
M::Sym(_) => "ʞ:symbol",
|
||||
M::List(_) => "ʞ:ist",
|
||||
M::List(_) => "ʞ:list",
|
||||
M::Vector(_) => "ʞ:vector",
|
||||
M::Map(_) => "ʞ:map",
|
||||
M::Atom(_) => "ʞ:atom",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
6
tests/atoms.mal
Normal file
6
tests/atoms.mal
Normal file
@ -0,0 +1,6 @@
|
||||
; atom?
|
||||
(assert (atom? (atom 1)))
|
||||
(assert (not (atom? 1)))
|
||||
|
||||
; deref
|
||||
(assert-eq (deref (atom 1)) 1)
|
||||
@ -31,7 +31,6 @@
|
||||
|
||||
(assert-eq (do) nil)
|
||||
|
||||
(println :ififififififififif)
|
||||
; if
|
||||
(assert (if true 1))
|
||||
(assert (not (if false 1)))
|
||||
|
||||
@ -5,5 +5,5 @@
|
||||
; empty?
|
||||
(assert (empty? ()))
|
||||
(assert (empty? (list)))
|
||||
(assert (not (empty? ())))
|
||||
(assert (not (empty? (list (1 2 3)))))
|
||||
(assert (not (empty? '(1 2 3))))
|
||||
(assert (not (empty? (list 1 2 3))))
|
||||
|
||||
Reference in New Issue
Block a user