diff --git a/.gitignore b/.gitignore index cfbed0e..7cf503f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ src/#* src/*~ # history file -.mal-history \ No newline at end of file +.mal-history + +# stupid macos +.DS_Store diff --git a/src/core.rs b/src/core.rs index 9a5a871..8f24f0f 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,5 +1,4 @@ -use std::env; -use std::rc::Rc; +use std::{cell::RefCell, env, rc::Rc}; use crate::env::{any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env}; @@ -36,7 +35,7 @@ use crate::parse_tools::read_file; use crate::printer::pr_str; use crate::reader::{read_str, Reader}; use crate::types::MalType::{Atom, Fun, Int, List, Nil, Str}; -use crate::types::{mal_assert, mal_equals, MalErr}; +use crate::types::{mal_assert, mal_equals, reset_bang, MalErr}; pub fn ns_init() -> Env { env_init!(None, @@ -63,8 +62,9 @@ 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"), + "atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a)?.clone())))), "Return an atom pointing to the given arg"), + "deref" => Fun(|a| match car(a)? { Atom(a) => Ok(a.borrow().clone()), _ => todo!("Cacca suprema") }, "Return the content of the atom argumet"), + "reset!" => Fun(reset_bang, "Set the value of the first argument to the second argument"), "env" => Fun(|a| match env::var(car(a)?.if_string()?) { Ok(s) => Ok(Str(s.into())), _ => Ok(Nil), diff --git a/src/printer.rs b/src/printer.rs index 459c128..c2a3d40 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -53,7 +53,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String { ), M::Fun(..) => "#".to_string(), M::MalFun { .. } => "#".to_string(), - M::Atom(sub) => format!("Atom({})", pr_str(sub, print_readably)), + M::Atom(sub) => format!("Atom({})", pr_str(&sub.borrow(), print_readably)), } } diff --git a/src/reader.rs b/src/reader.rs index 6b77eaf..c19bfd7 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -133,6 +133,13 @@ impl Reader { self.read_form()?, ]))) } + "@" => { + self.next()?; + Ok(List(Rc::new([ + MalType::Sym("deref".into()), + self.read_form()?, + ]))) + } _ => self.read_atom(), } } diff --git a/src/types.rs b/src/types.rs index f93952e..c05be7a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,5 @@ use crate::env::{car_cdr, Env}; -use std::{collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; pub type MalStr = Rc; pub type MalArgs = Rc<[MalType]>; @@ -25,7 +25,7 @@ pub enum MalType { Str(MalStr), Int(isize), Bool(bool), - Atom(Rc), + Atom(Rc>), Nil, } @@ -75,29 +75,21 @@ 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", - M::Bool(_) => "ʞ:bool", - M::Int(_) => "ʞ:int", - M::Fun(_, _) | M::MalFun { .. } => "ʞ:lambda", - M::Key(_) => "ʞ:key", - M::Str(_) => "ʞ:string", - M::Sym(_) => "ʞ:symbol", - M::List(_) => "ʞ:list", - M::Vector(_) => "ʞ:vector", - M::Map(_) => "ʞ:map", - M::Atom(_) => "ʞ:atom", - } + Key(("ʞ:".to_owned() + + match self { + M::Nil => "nil", + M::Bool(_) => "bool", + M::Int(_) => "int", + M::Fun(_, _) | M::MalFun { .. } => "lambda", + M::Key(_) => "key", + M::Str(_) => "string", + M::Sym(_) => "symbol", + M::List(_) => "list", + M::Vector(_) => "vector", + M::Map(_) => "map", + M::Atom(_) => "atom", + }) .into()) } } @@ -135,6 +127,22 @@ pub fn mal_assert(args: &[MalType]) -> MalRet { Ok(M::Nil) } +pub fn reset_bang(args: &[MalType]) -> MalRet { + if args.len() < 2 { + return Err(MalErr::unrecoverable("reset requires two arguments")); + } + let val = &args[1]; + match &args[0] { + M::Atom(sym) => { + *std::cell::RefCell::<_>::borrow_mut(sym) = val.clone(); + Ok(val.clone()) + } + _ => Err(MalErr::unrecoverable( + format!("{:?} is not an atom", prt(&args[1])).as_str(), + )), + } +} + #[derive(PartialEq, Clone, Copy, Debug)] pub enum Severity { Recoverable,