diff --git a/src/reader.rs b/src/reader.rs index 92aa278..f84e4ee 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -8,27 +8,27 @@ //! not; since this is about being a 'free-er' Clojure, especially since it can't compete with it in raw //! power, neither speed or ecosystem, it might be worth it to leave in reader macros. -use nom::combinator::{verify}; +use nom::combinator::verify; use nom::{ branch::alt, bytes::complete::tag, combinator::opt, map, sequence::preceded, take_until, - Err::Incomplete, IResult + Err::Incomplete, IResult, }; +use crate::error_message; use crate::keyword::Keyword; use crate::maps::MapEntry; use crate::persistent_list::ToPersistentList; -use crate::persistent_list_map::{PersistentListMap,ToPersistentListMap, ToPersistentListMapIter}; +use crate::persistent_list_map::{PersistentListMap, ToPersistentListMap, ToPersistentListMapIter}; use crate::persistent_vector::ToPersistentVector; -use crate::protocol::ProtocolCastable; use crate::protocol::Protocol; -use crate::symbol::Symbol; -use crate::error_message; -use crate::value::{ToValue, Value}; -use std::rc::Rc; +use crate::protocol::ProtocolCastable; use crate::protocols; +use crate::symbol::Symbol; use crate::traits::IObj; +use crate::value::{ToValue, Value}; use crate::traits::IMeta; use std::io::BufRead; +use std::rc::Rc; // // Note; the difference between ours 'parsers' // identifier_parser @@ -36,6 +36,7 @@ use std::io::BufRead; // integer_parser // And our 'try readers' // try_read_i32 +// try_read_char // try_read_string // try_read_map // try_read_list @@ -288,10 +289,10 @@ pub fn identifier_parser(input: &str) -> IResult<&str, String> { /// namespace.subnamespace/a cat/b a.b.c/|ab123| pub fn symbol_parser(input: &str) -> IResult<&str, Symbol> { named!(namespace_parser <&str,String>, - do_parse!( - ns: identifier_parser >> - complete!(tag!("/")) >> - (ns))); + do_parse!( + ns: identifier_parser >> + complete!(tag!("/")) >> + (ns))); let (rest_input, ns) = opt(namespace_parser)(input)?; let (rest_input, name) = identifier_parser(rest_input)?; @@ -466,6 +467,52 @@ pub fn try_read_nil(input: &str) -> IResult<&str, Value> { Ok((rest_input, Value::Nil)) } +/// Tries to parse &str into Value::Char +/// Example Successes: +/// "\newline" => Value::Char("\n") +/// Example Failures: +/// +pub fn try_read_char(input: &str) -> IResult<&str, Value> { + named!(backslash<&str, &str>, preceded!(consume_clojure_whitespaces_parser, tag!("\\"))); + + fn str_to_unicode(s: &str) -> char { + u32::from_str_radix(s, 16) + .ok() + .and_then(std::char::from_u32) + .unwrap() + } + + named!(unicode < &str, char>, alt!( + preceded!( + tag!("u"), + alt!( + map!(take_while_m_n!(4,4, |c :char| c.is_digit(16)), str_to_unicode) + ) + ) + )); + + named!(special_escapes < &str, char>, complete!( alt!( + tag!("newline") => { |_| '\n'} | + tag!("space") => { |_| ' ' } | + tag!("tab") => { |_| '\t'} | + //tag!("formfeed") => { |_| '\f'} | + //tag!("backspace") => { |_| '\b'} | + tag!("return") => { |_| '\r' } ))); + + named!(normal_char < &str, char>, + // accept anything after \ + map!(take_while_m_n!(1,1,|_| true), first_char)); + + named!(char_parser<&str,char>, + alt!(unicode | special_escapes | normal_char)); + + let (rest_input, _) = backslash(input)?; + + let (rest_input, char_value) = char_parser(rest_input)?; + + Ok((rest_input, Value::Char(char_value))) +} + // @TODO allow escaped strings /// Tries to parse &str into Value::String /// Example Successes: @@ -492,7 +539,7 @@ pub fn try_read_var(input: &str) -> IResult<&str, Value> { let (rest_input, val) = try_read(rest_input)?; // #'x just expands to (var x), just like 'x is just a shorthand for (quote x) // So here we return (var val) - Ok((rest_input,list_val!(sym!("var") val))) + Ok((rest_input, list_val!(sym!("var") val))) } // @TODO Perhaps generalize this, or even generalize it as a reader macro @@ -524,40 +571,46 @@ pub fn try_read_meta(input: &str) -> IResult<&str, Value> { named!(meta_start<&str, &str>, preceded!(consume_clojure_whitespaces_parser, tag!("^"))); let (rest_input, _) = meta_start(input)?; - let (rest_input,meta_value) = try_read(rest_input)?; + let (rest_input, meta_value) = try_read(rest_input)?; let mut meta = PersistentListMap::Empty; match &meta_value { Value::Symbol(symbol) => { // @TODO Note; do NOT hardcode this, make some global for TAG_KEY, like Clojure does - meta = persistent_list_map!{"tag" => symbol}; - }, + meta = persistent_list_map! {"tag" => symbol}; + } Value::Keyword(keyword) => { - meta = persistent_list_map!( - MapEntry { - key: meta_value.to_rc_value(), - val: true.to_rc_value() - } - ); - }, + meta = persistent_list_map!(MapEntry { + key: meta_value.to_rc_value(), + val: true.to_rc_value() + }); + } Value::String(string) => { // @TODO Note; do NOT hardcode this, make some global for TAG_KEY, like Clojure does - meta = persistent_list_map!{"tag" => string}; - }, + meta = persistent_list_map! {"tag" => string}; + } Value::PersistentListMap(plist_map) => { meta = plist_map.clone(); - // Then we're already set + // Then we're already set } _ => { // @TODO check instanceof IPersistentMap here instead // @TODO Clojure has basically this one off error here, but another thing we wish to do - // is write clear errors - return Ok((rest_input,error_message::custom("When trying to read meta: metadata must be Symbol, Keyword, String, or Map"))) + // is write clear errors + return Ok(( + rest_input, + error_message::custom( + "When trying to read meta: metadata must be Symbol, Keyword, String, or Map", + ), + )); } } - let (rest_input,iobj_value) = try_read(rest_input)?; + let (rest_input, iobj_value) = try_read(rest_input)?; - // Extra clone, implement these functions for plain Values - if let Some(iobj_value) = iobj_value.to_rc_value().try_as_protocol::() { + // Extra clone, implement these functions for plain Values + if let Some(iobj_value) = iobj_value + .to_rc_value() + .try_as_protocol::() + { // @TODO get actual line and column info let line = 1; let column = 1; @@ -638,6 +691,7 @@ pub fn try_read(input: &str) -> IResult<&str, Value> { try_read_quoted, try_read_nil, try_read_map, + try_read_char, try_read_string, try_read_f64, try_read_i32, @@ -869,6 +923,47 @@ mod tests { } } + mod try_read_char_tests { + use crate::reader::try_read_char; + use crate::value::Value; + + // #[test] + // fn try_read_char_test() { + // assert_eq!(Value::Char("\\f"), try_read_char("\\formfeed")) + // } + + #[test] + fn try_read_char_space() { + assert_eq!(Value::Char(' '), try_read_char("\\space").ok().unwrap().1); + } + + #[test] + fn try_read_char_return() { + assert_eq!(Value::Char('\r'), try_read_char("\\return").ok().unwrap().1); + } + + #[test] + fn try_read_char_hashtag() { + assert_eq!(Value::Char('#'), try_read_char("\\#").ok().unwrap().1); + } + #[test] + fn try_read_char_n() { + assert_eq!(Value::Char('n'), try_read_char("\\n").ok().unwrap().1); + } + #[test] + fn try_read_char_f() { + assert_eq!(Value::Char('r'), try_read_char("\\r").ok().unwrap().1); + } + #[test] + fn try_read_unicode() { + assert_eq!(Value::Char('张'), try_read_char("\\u5F20").ok().unwrap().1); + } + #[test] + fn try_read_char_fail() { + assert!(try_read_char("d").is_err()); + } + } + mod try_read_symbol_tests { use crate::reader::try_read_symbol; use crate::symbol::Symbol; @@ -883,16 +978,31 @@ mod tests { } } + // mod try_read_char_tests { + // use crate::reader::try_read_char; + // use crate::value::Value; + // + // #[test] + // fn try_read_char_test() { + // assert_eq!(Value::Char('f'), try_read_character("\\f")) + // } + // + // #[test] + // fn try_read_newline_test() { + // assert_eq!(Value::Char('\n'), try_read_character("\newline")) + // } + // } + mod try_read_tests { + use crate::keyword::Keyword; use crate::persistent_list; use crate::persistent_list_map; use crate::persistent_list_map::IPersistentMap; - use crate::keyword::Keyword; use crate::persistent_vector; use crate::reader::try_read; use crate::symbol::Symbol; - use crate::value::{ToValue,Value}; use crate::value::Value::{PersistentList, PersistentListMap, PersistentVector}; + use crate::value::{ToValue, Value}; #[test] fn try_read_empty_map_test() { @@ -1000,16 +1110,18 @@ mod tests { } #[test] fn try_read_meta_symbol() { - let with_meta = "^cat a"; + let with_meta = "^cat a"; match try_read(with_meta).ok().unwrap().1 { Value::Symbol(symbol) => { - assert!(symbol.meta().contains_key(&Keyword::intern("tag").to_rc_value())); + assert!(symbol + .meta() + .contains_key(&Keyword::intern("tag").to_rc_value())); assert_eq!( Symbol::intern("cat").to_value(), *symbol.meta().get(&Keyword::intern("tag").to_rc_value()) ); - }, - _ => panic!("try_read_meta \"^cat a\" should return a symbol") + } + _ => panic!("try_read_meta \"^cat a\" should return a symbol"), } } #[test] @@ -1017,14 +1129,16 @@ mod tests { let with_meta = "^\"cat\" a"; match try_read(with_meta).ok().unwrap().1 { Value::Symbol(symbol) => { - assert_eq!(String::from("a"),symbol.name); - assert!(symbol.meta().contains_key(&Keyword::intern("tag").to_rc_value())); + assert_eq!(String::from("a"), symbol.name); + assert!(symbol + .meta() + .contains_key(&Keyword::intern("tag").to_rc_value())); assert_eq!( "cat".to_value(), *symbol.meta().get(&Keyword::intern("tag").to_rc_value()) ); - }, - _ => panic!("try_read_meta '^\"cat\" a' should return a symbol") + } + _ => panic!("try_read_meta '^\"cat\" a' should return a symbol"), } } #[test] @@ -1032,13 +1146,25 @@ mod tests { let with_meta = "^{:cat 1 :dog 2} a"; match try_read(with_meta).ok().unwrap().1 { Value::Symbol(symbol) => { - assert!(symbol.meta().contains_key(&Keyword::intern("cat").to_rc_value())); - assert_eq!(Value::I32(1),*symbol.meta().get(&Keyword::intern("cat").to_rc_value())); - assert!(symbol.meta().contains_key(&Keyword::intern("dog").to_rc_value())); - assert_eq!(Value::I32(2),*symbol.meta().get(&Keyword::intern("dog").to_rc_value())); - assert!(!symbol.meta().contains_key(&Keyword::intern("chicken").to_rc_value())); - }, - _ => panic!("try_read_meta \"^{:cat 1 :dog 2} a\" should return a symbol") + assert!(symbol + .meta() + .contains_key(&Keyword::intern("cat").to_rc_value())); + assert_eq!( + Value::I32(1), + *symbol.meta().get(&Keyword::intern("cat").to_rc_value()) + ); + assert!(symbol + .meta() + .contains_key(&Keyword::intern("dog").to_rc_value())); + assert_eq!( + Value::I32(2), + *symbol.meta().get(&Keyword::intern("dog").to_rc_value()) + ); + assert!(!symbol + .meta() + .contains_key(&Keyword::intern("chicken").to_rc_value())); + } + _ => panic!("try_read_meta \"^{:cat 1 :dog 2} a\" should return a symbol"), } } #[test] @@ -1057,9 +1183,11 @@ mod tests { let with_meta = "^:cat a"; match try_read(with_meta).ok().unwrap().1 { Value::Symbol(symbol) => { - assert!(symbol.meta().contains_key(&Keyword::intern("cat").to_rc_value())); - }, - _ => panic!("try_read_meta \"^:cat a\" should return a symbol") + assert!(symbol + .meta() + .contains_key(&Keyword::intern("cat").to_rc_value())); + } + _ => panic!("try_read_meta \"^:cat a\" should return a symbol"), } } #[test] diff --git a/src/type_tag.rs b/src/type_tag.rs index a4cba11..f25c102 100644 --- a/src/type_tag.rs +++ b/src/type_tag.rs @@ -7,6 +7,7 @@ pub enum TypeTag { Boolean, Symbol, Var, + Char, Keyword, IFn, Condition, @@ -28,10 +29,11 @@ impl fmt::Display for TypeTag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str = match self { I32 => std::string::String::from("rust.std.i32"), - Boolean => std::string::String::from("rust.std.bool"), F64 => std::string::String::from("rust.std.f64"), + Boolean => std::string::String::from("rust.std.bool"), Symbol => std::string::String::from("clojure.lang.Symbol"), Var => std::string::String::from("clojure.lang.Var"), + Char => std::string::String::from("clojure.lang.Char"), Keyword => std::string::String::from("clojure.lang.Keyword"), IFn => std::string::String::from("clojure.lang.Function"), Condition => std::string::String::from("clojure.lang.Condition"), diff --git a/src/value.rs b/src/value.rs index 060fae4..d672c3b 100644 --- a/src/value.rs +++ b/src/value.rs @@ -8,8 +8,8 @@ use crate::persistent_list::{PersistentList, ToPersistentList, ToPersistentListI use crate::persistent_list_map::{PersistentListMap, ToPersistentListMapIter}; use crate::persistent_vector::PersistentVector; use crate::symbol::Symbol; -use crate::var::Var; use crate::type_tag::TypeTag; +use crate::var::Var; use core::fmt::Display; extern crate rand; @@ -34,6 +34,7 @@ pub enum Value { Boolean(bool), Symbol(Symbol), Var(Var), + Char(char), Keyword(Keyword), IFn(Rc), // @@ -78,6 +79,7 @@ impl PartialEq for Value { (Boolean(b), Boolean(b2)) => b == b2, (Symbol(sym), Symbol(sym2)) => sym == sym2, (Var(var), Var(var2)) => var == var2, + (Char(c), Char(c2)) => c == c2, (Keyword(kw), Keyword(kw2)) => kw == kw2, // Equality not defined on functions, similar to Clojure // Change this perhaps? Diverge? @@ -122,6 +124,7 @@ impl Hash for Value { Boolean(b) => b.hash(state), Symbol(sym) => sym.hash(state), Var(var) => var.hash(state), + Char(c) => c.hash(state), Keyword(kw) => kw.hash(state), IFn(_) => { let mut rng = rand::thread_rng(); @@ -163,6 +166,7 @@ impl fmt::Display for Value { Boolean(val) => val.to_string(), Symbol(sym) => sym.to_string(), Var(var) => var.to_string(), + Char(c) => c.to_string(), Keyword(kw) => kw.to_string(), IFn(_) => std::string::String::from("#function[]"), LexicalEvalFn => std::string::String::from("#function[lexical-eval*]"), @@ -208,6 +212,7 @@ impl Value { Value::Boolean(_) => TypeTag::Boolean, Value::Symbol(_) => TypeTag::Symbol, Value::Var(_) => TypeTag::Var, + Value::Char(_) => TypeTag::Char, Value::Keyword(_) => TypeTag::Keyword, Value::IFn(_) => TypeTag::IFn, Value::LexicalEvalFn => TypeTag::IFn, @@ -393,9 +398,9 @@ impl Value { .into_list() .eval(Rc::clone(&environment)); let macro_value = match ¯o_invokable_body { - Value::IFn(ifn) => Rc::new(Value::Macro(Rc::clone(&ifn))), - _ => Rc::new(Value::Condition(std::string::String::from("Compiler Error: your macro_value somehow compiled into something else entirely. I don't even know how that happened, this behavior is hardcoded, that's impressive"))) - }; + Value::IFn(ifn) => Rc::new(Value::Macro(Rc::clone(&ifn))), + _ => Rc::new(Value::Condition(std::string::String::from("Compiler Error: your macro_value somehow compiled into something else entirely. I don't even know how that happened, this behavior is hardcoded, that's impressive"))) + }; Some( vec![ Symbol::intern("def").to_rc_value(), @@ -439,23 +444,23 @@ impl Value { } let fn_body = - // (fn [x y] ) -> nil - if arg_rc_values.len() <= 1 { - Rc::new(Value::Nil) - // (fn [x y] expr) -> expr - } else if arg_rc_values.len() == 2 { - Rc::clone(arg_rc_values.get(1).unwrap()) - // (fn [x y] expr1 expr2 expr3) -> (do expr1 expr2 expr3) - } else { - // (&[expr1 expr2 expr3] - let body_exprs = arg_rc_values.get(1..).unwrap(); - // vec![do] - let mut do_body = vec![Symbol::intern("do").to_rc_value()]; - // vec![do expr1 expr2 expr3] - do_body.extend_from_slice(body_exprs); - // (do expr1 expr2 expr3) - do_body.into_list().to_rc_value() - }; + // (fn [x y] ) -> nil + if arg_rc_values.len() <= 1 { + Rc::new(Value::Nil) + // (fn [x y] expr) -> expr + } else if arg_rc_values.len() == 2 { + Rc::clone(arg_rc_values.get(1).unwrap()) + // (fn [x y] expr1 expr2 expr3) -> (do expr1 expr2 expr3) + } else { + // (&[expr1 expr2 expr3] + let body_exprs = arg_rc_values.get(1..).unwrap(); + // vec![do] + let mut do_body = vec![Symbol::intern("do").to_rc_value()]; + // vec![do expr1 expr2 expr3] + do_body.extend_from_slice(body_exprs); + // (do expr1 expr2 expr3) + do_body.into_list().to_rc_value() + }; Some(Rc::new( lambda::Fn {