88//! not; since this is about being a 'free-er' Clojure, especially since it can't compete with it in raw
99//! power, neither speed or ecosystem, it might be worth it to leave in reader macros.
1010
11- use nom:: combinator:: verify;
11+ use nom:: combinator:: { verify} ;
1212use nom:: {
1313 branch:: alt, bytes:: complete:: tag, combinator:: opt, map, sequence:: preceded, take_until,
14- terminated , AsChar , Err :: Incomplete , IResult ,
14+ Err :: Incomplete , IResult
1515} ;
1616
1717use crate :: keyword:: Keyword ;
1818use crate :: maps:: MapEntry ;
1919use crate :: persistent_list:: ToPersistentList ;
20- use crate :: persistent_list_map:: ToPersistentListMap ;
20+ use crate :: persistent_list_map:: { PersistentListMap , ToPersistentListMap , ToPersistentListMapIter } ;
2121use crate :: persistent_vector:: ToPersistentVector ;
22+ use crate :: protocol:: ProtocolCastable ;
23+ use crate :: protocol:: Protocol ;
2224use crate :: symbol:: Symbol ;
25+ use crate :: error_message;
2326use crate :: value:: { ToValue , Value } ;
2427use std:: rc:: Rc ;
25-
26- use nom:: Err :: Error ;
27- use std:: borrow:: Borrow ;
28+ use crate :: protocols;
29+ use crate :: traits:: IObj ;
2830use std:: io:: BufRead ;
2931//
3032// Note; the difference between ours 'parsers'
@@ -148,7 +150,7 @@ fn is_period_char(chr: char) -> bool {
148150///
149151/// Clojure defines a whitespace as either a comma or an unicode whitespace.
150152fn is_clojure_whitespace ( c : char ) -> bool {
151- c. is_whitespace ( ) || c == ','
153+ c. is_whitespace ( ) || c == ','
152154}
153155////////////////////////////////////////////////////////////////////////////////////////////////////
154156// End predicates
@@ -169,16 +171,16 @@ fn consume_clojure_whitespaces_parser(input: &str) -> IResult<&str, ()> {
169171
170172 named ! ( whitespace_parser<& str , ( ) >,
171173 value!( ( ) ,
172- many0!( alt!( comment_parser |
174+ many0!( alt!( comment_parser |
173175 take_while1!( is_clojure_whitespace) ) ) )
174176 ) ;
175177
176178 named ! ( no_whitespace_parser<& str , ( ) >, value!( ( ) , tag!( "" ) ) ) ;
177179
178- // @TODO rename / check that all parsers are consistent?
180+ // @TODO rename / check that all parsers are consistent?
179181 named ! ( parser<& str , ( ) >,
180182 // Because 'whitespace_parser' loops, we cannot include the case where there's no whitespace at all in
181- // its definition -- nom wouldn't allow it, as it would loop forever consuming no whitespace
183+ // its definition -- nom wouldn't allow it, as it would loop forever consuming no whitespace
182184 // So instead, we eat up all the whitespace first, and then use the no_whitespace_parser as our sort-of
183185 // base-case after
184186 alt!( whitespace_parser | no_whitespace_parser)
@@ -457,6 +459,60 @@ pub fn try_read_map(input: &str) -> IResult<&str, Value> {
457459 }
458460}
459461
462+ pub fn try_read_meta ( input : & str ) -> IResult < & str , Value > {
463+ named ! ( meta_start<& str , & str >, preceded!( consume_clojure_whitespaces_parser, tag!( "^" ) ) ) ;
464+ let ( rest_input, _) = meta_start ( input) ?;
465+
466+ let ( rest_input, meta_value) = try_read ( rest_input) ?;
467+ let mut meta = PersistentListMap :: Empty ;
468+ match & meta_value {
469+ Value :: Symbol ( symbol) => {
470+ // @TODO Note; do NOT hardcode this, make some global for TAG_KEY, like Clojure does
471+ meta = persistent_list_map ! { "tag" => symbol} ;
472+ } ,
473+ Value :: Keyword ( keyword) => {
474+ meta = persistent_list_map ! (
475+ MapEntry {
476+ key: meta_value. to_rc_value( ) ,
477+ val: true . to_rc_value( )
478+ }
479+ ) ;
480+ } ,
481+ Value :: String ( string) => {
482+ // @TODO Note; do NOT hardcode this, make some global for TAG_KEY, like Clojure does
483+ meta = persistent_list_map ! { "tag" => string} ;
484+ } ,
485+ Value :: PersistentListMap ( plist_map) => {
486+ meta = plist_map. clone ( ) ;
487+ // Then we're already set
488+ }
489+ _ => {
490+ // @TODO check instanceof IPersistentMap here instead
491+ // @TODO Clojure has basically this one off error here, but another thing we wish to do
492+ // is write clear errors
493+ return Ok ( ( rest_input, error_message:: custom ( "When trying to read meta: metadata must be Symbol, Keyword, String, or Map" ) ) )
494+ }
495+ }
496+ let ( rest_input, iobj_value) = try_read ( rest_input) ?;
497+
498+ // Extra clone, implement these functions for plain Values
499+ if let Some ( iobj_value) = iobj_value. to_rc_value ( ) . try_as_protocol :: < protocols:: IObj > ( ) {
500+ // @TODO get actual line and column info
501+ let line = 1 ;
502+ let column = 1 ;
503+ // @TODO merge the meta iobj_value *already* has
504+ // @TODO define some better macros and / or functions for map handling
505+ meta = merge ! (
506+ meta,
507+ map_entry!( "line" , line) ,
508+ map_entry!( "column" , column)
509+ ) ;
510+ Ok ( ( rest_input, iobj_value. with_meta ( meta) . unwrap ( ) . to_value ( ) ) )
511+ }
512+ else {
513+ Ok ( ( rest_input, error_message:: custom ( "In meta reader: metadata can only be applied to types who are an instance of IMeta" ) ) )
514+ }
515+ }
460516// @TODO use nom functions in place of macro
461517/// Tries to parse &str into Value::PersistentVector
462518/// Example Successes:
@@ -553,12 +609,15 @@ pub fn read<R: BufRead>(reader: &mut R) -> Value {
553609 // loop over and ask for more lines, accumulating them in input_buffer until we can read
554610 loop {
555611 let maybe_line = reader. by_ref ( ) . lines ( ) . next ( ) ;
556-
612+
557613 match maybe_line {
558614 Some ( Err ( e) ) => return Value :: Condition ( format ! ( "Reader error: {}" , e) ) ,
559615 // `lines` does not include \n, but \n is part of the whitespace given to the reader
560- // (and is important for reading comments) so we will push a newline as well
561- Some ( Ok ( line) ) => { input_buffer. push_str ( & line) ; input_buffer. push_str ( "\n " ) ; } ,
616+ // (and is important for reading comments) so we will push a newline as well
617+ Some ( Ok ( line) ) => {
618+ input_buffer. push_str ( & line) ;
619+ input_buffer. push_str ( "\n " ) ;
620+ }
562621 None => {
563622 return Value :: Condition ( String :: from ( "Tried to read empty stream; unexpected EOF" ) )
564623 }
@@ -764,11 +823,13 @@ mod tests {
764823 mod try_read_tests {
765824 use crate :: persistent_list;
766825 use crate :: persistent_list_map;
826+ use crate :: persistent_list_map:: IPersistentMap ;
827+ use crate :: keyword:: Keyword ;
767828 use crate :: persistent_vector;
768829 use crate :: reader:: try_read;
769830 use crate :: symbol:: Symbol ;
831+ use crate :: value:: { ToValue , Value } ;
770832 use crate :: value:: Value :: { PersistentList , PersistentListMap , PersistentVector } ;
771- use crate :: value:: { ToValue , Value } ;
772833
773834 #[ test]
774835 fn try_read_empty_map_test ( ) {
@@ -874,6 +935,59 @@ mod tests {
874935 fn try_read_bool_false_test ( ) {
875936 assert_eq ! ( Value :: Boolean ( false ) , try_read( "false " ) . ok( ) . unwrap( ) . 1 )
876937 }
938+ #[ test]
939+ fn try_read_meta_symbol ( ) {
940+ let with_meta = "^cat a" ;
941+ match try_read ( with_meta) . ok ( ) . unwrap ( ) . 1 {
942+ Value :: Symbol ( symbol) => {
943+ assert ! ( symbol. meta( ) . contains_key( & Keyword :: intern( "tag" ) . to_rc_value( ) ) ) ;
944+ assert_eq ! (
945+ Symbol :: intern( "cat" ) . to_value( ) ,
946+ * symbol. meta( ) . get( & Keyword :: intern( "tag" ) . to_rc_value( ) )
947+ ) ;
948+ } ,
949+ _ => panic ! ( "try_read_meta \" ^cat a\" should return a symbol" )
950+ }
951+ }
952+ #[ test]
953+ fn try_read_meta_string ( ) {
954+ let with_meta = "^\" cat\" a" ;
955+ match try_read ( with_meta) . ok ( ) . unwrap ( ) . 1 {
956+ Value :: Symbol ( symbol) => {
957+ assert_eq ! ( String :: from( "a" ) , symbol. name) ;
958+ assert ! ( symbol. meta( ) . contains_key( & Keyword :: intern( "tag" ) . to_rc_value( ) ) ) ;
959+ assert_eq ! (
960+ "cat" . to_value( ) ,
961+ * symbol. meta( ) . get( & Keyword :: intern( "tag" ) . to_rc_value( ) )
962+ ) ;
963+ } ,
964+ _ => panic ! ( "try_read_meta '^\" cat\" a' should return a symbol" )
965+ }
966+ }
967+ #[ test]
968+ fn try_read_meta_persistent_list_map ( ) {
969+ let with_meta = "^{:cat 1 :dog 2} a" ;
970+ match try_read ( with_meta) . ok ( ) . unwrap ( ) . 1 {
971+ Value :: Symbol ( symbol) => {
972+ assert ! ( symbol. meta( ) . contains_key( & Keyword :: intern( "cat" ) . to_rc_value( ) ) ) ;
973+ assert_eq ! ( Value :: I32 ( 1 ) , * symbol. meta( ) . get( & Keyword :: intern( "cat" ) . to_rc_value( ) ) ) ;
974+ assert ! ( symbol. meta( ) . contains_key( & Keyword :: intern( "dog" ) . to_rc_value( ) ) ) ;
975+ assert_eq ! ( Value :: I32 ( 2 ) , * symbol. meta( ) . get( & Keyword :: intern( "dog" ) . to_rc_value( ) ) ) ;
976+ assert ! ( !symbol. meta( ) . contains_key( & Keyword :: intern( "chicken" ) . to_rc_value( ) ) ) ;
977+ } ,
978+ _ => panic ! ( "try_read_meta \" ^{:cat 1 :dog 2} a\" should return a symbol" )
979+ }
980+ }
981+ #[ test]
982+ fn try_read_meta_keyword ( ) {
983+ let with_meta = "^:cat a" ;
984+ match try_read ( with_meta) . ok ( ) . unwrap ( ) . 1 {
985+ Value :: Symbol ( symbol) => {
986+ assert ! ( symbol. meta( ) . contains_key( & Keyword :: intern( "cat" ) . to_rc_value( ) ) ) ;
987+ } ,
988+ _ => panic ! ( "try_read_meta \" ^:cat a\" should return a symbol" )
989+ }
990+ }
877991 }
878992
879993 mod regex_tests {
0 commit comments