@@ -32,6 +32,8 @@ public class EdnReader{
3232static Pattern floatPat = Pattern .compile ("([-+]?[0-9]+(\\ .[0-9]*)?([eE][-+]?[0-9]+)?)(M)?" );
3333static final Symbol SLASH = Symbol .intern ("/" );
3434
35+ static IFn taggedReader = new TaggedReader ();
36+
3537static
3638 {
3739 macros ['"' ] = new StringReader ();
@@ -54,10 +56,14 @@ public class EdnReader{
5456 dispatchMacros ['_' ] = new DiscardReader ();
5557 }
5658
57- static public Object readString (String s ){
59+ static boolean nonConstituent (int ch ){
60+ return ch == '@' || ch == '`' || ch == '~' ;
61+ }
62+
63+ static public Object readString (String s , IPersistentMap opts ){
5864 PushbackReader r = new PushbackReader (new java .io .StringReader (s ));
5965 try {
60- return EdnReader . read (r , true , null , false );
66+ return read (r , opts );
6167 }
6268 catch (Exception e ) {
6369 throw Util .sneakyThrow (e );
@@ -102,7 +108,14 @@ static public int read1(Reader r){
102108 }
103109}
104110
105- static public Object read (PushbackReader r , boolean eofIsError , Object eofValue , boolean isRecursive )
111+ static final Keyword EOF = Keyword .intern (null ,"eof" );
112+
113+ static public Object read (PushbackReader r , IPersistentMap opts ){
114+ return read (r ,!opts .containsKey (EOF ),opts .valAt (EOF ),false ,opts );
115+ }
116+
117+ static public Object read (PushbackReader r , boolean eofIsError , Object eofValue , boolean isRecursive ,
118+ Object opts )
106119{
107120
108121 try
@@ -132,7 +145,7 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue,
132145 IFn macroFn = getMacro (ch );
133146 if (macroFn != null )
134147 {
135- Object ret = macroFn .invoke (r , (char ) ch );
148+ Object ret = macroFn .invoke (r , (char ) ch , opts );
136149 if (RT .suppressRead ())
137150 return null ;
138151 //no op macros return the reader
@@ -173,16 +186,22 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue,
173186
174187static private String readToken (PushbackReader r , char initch ) {
175188 StringBuilder sb = new StringBuilder ();
189+ if (nonConstituent (initch ))
190+ throw Util .runtimeException ("Invalid leading character: " + (char )initch );
191+
176192 sb .append (initch );
177193
178194 for (; ;)
179195 {
180196 int ch = read1 (r );
197+
181198 if (ch == -1 || isWhitespace (ch ) || isTerminatingMacro (ch ))
182199 {
183200 unread (r , ch );
184201 return sb .toString ();
185202 }
203+ else if (nonConstituent (ch ))
204+ throw Util .runtimeException ("Invalid constituent character: " + (char )ch );
186205 sb .append ((char ) ch );
187206 }
188207}
@@ -391,7 +410,7 @@ public Object invoke(Object reader, Object doublequote) {
391410*/
392411
393412public static class StringReader extends AFn {
394- public Object invoke (Object reader , Object doublequote ) {
413+ public Object invoke (Object reader , Object doublequote , Object opts ) {
395414 StringBuilder sb = new StringBuilder ();
396415 Reader r = (Reader ) reader ;
397416
@@ -453,7 +472,7 @@ public Object invoke(Object reader, Object doublequote) {
453472}
454473
455474public static class CommentReader extends AFn {
456- public Object invoke (Object reader , Object semicolon ) {
475+ public Object invoke (Object reader , Object semicolon , Object opts ) {
457476 Reader r = (Reader ) reader ;
458477 int ch ;
459478 do
@@ -466,29 +485,36 @@ public Object invoke(Object reader, Object semicolon) {
466485}
467486
468487public static class DiscardReader extends AFn {
469- public Object invoke (Object reader , Object underscore ) {
488+ public Object invoke (Object reader , Object underscore , Object opts ) {
470489 PushbackReader r = (PushbackReader ) reader ;
471- read (r , true , null , true );
490+ read (r , true , null , true , opts );
472491 return r ;
473492 }
474493}
475494
476495public static class DispatchReader extends AFn {
477- public Object invoke (Object reader , Object hash ) {
496+ public Object invoke (Object reader , Object hash , Object opts ) {
478497 int ch = read1 ((Reader ) reader );
479498 if (ch == -1 )
480499 throw Util .runtimeException ("EOF while reading character" );
481500 IFn fn = dispatchMacros [ch ];
482501
483502 if (fn == null ) {
503+ //try tagged reader
504+ if (Character .isLetter (ch ))
505+ {
506+ unread ((PushbackReader ) reader , ch );
507+ return taggedReader .invoke (reader , ch , opts );
508+ }
509+
484510 throw Util .runtimeException (String .format ("No dispatch macro for: %c" , (char ) ch ));
485511 }
486- return fn .invoke (reader , ch );
512+ return fn .invoke (reader , ch , opts );
487513 }
488514}
489515
490516public static class MetaReader extends AFn {
491- public Object invoke (Object reader , Object caret ) {
517+ public Object invoke (Object reader , Object caret , Object opts ) {
492518 PushbackReader r = (PushbackReader ) reader ;
493519 int line = -1 ;
494520 int column = -1 ;
@@ -497,15 +523,15 @@ public Object invoke(Object reader, Object caret) {
497523 line = ((LineNumberingPushbackReader ) r ).getLineNumber ();
498524 column = ((LineNumberingPushbackReader ) r ).getColumnNumber ()-1 ;
499525 }
500- Object meta = read (r , true , null , true );
526+ Object meta = read (r , true , null , true , opts );
501527 if (meta instanceof Symbol || meta instanceof String )
502528 meta = RT .map (RT .TAG_KEY , meta );
503529 else if (meta instanceof Keyword )
504530 meta = RT .map (meta , RT .T );
505531 else if (!(meta instanceof IPersistentMap ))
506532 throw new IllegalArgumentException ("Metadata must be Symbol,Keyword,String or Map" );
507533
508- Object o = read (r , true , null , true );
534+ Object o = read (r , true , null , true , opts );
509535 if (o instanceof IMeta )
510536 {
511537 if (line != -1 && o instanceof ISeq )
@@ -531,7 +557,7 @@ else if(!(meta instanceof IPersistentMap))
531557}
532558
533559public static class CharacterReader extends AFn {
534- public Object invoke (Object reader , Object backslash ) {
560+ public Object invoke (Object reader , Object backslash , Object opts ) {
535561 PushbackReader r = (PushbackReader ) reader ;
536562 int ch = read1 (r );
537563 if (ch == -1 )
@@ -574,7 +600,7 @@ else if(token.startsWith("o"))
574600}
575601
576602public static class ListReader extends AFn {
577- public Object invoke (Object reader , Object leftparen ) {
603+ public Object invoke (Object reader , Object leftparen , Object opts ) {
578604 PushbackReader r = (PushbackReader ) reader ;
579605 int line = -1 ;
580606 int column = -1 ;
@@ -583,7 +609,7 @@ public Object invoke(Object reader, Object leftparen) {
583609 line = ((LineNumberingPushbackReader ) r ).getLineNumber ();
584610 column = ((LineNumberingPushbackReader ) r ).getColumnNumber ()-1 ;
585611 }
586- List list = readDelimitedList (')' , r , true );
612+ List list = readDelimitedList (')' , r , true , opts );
587613 if (list .isEmpty ())
588614 return PersistentList .EMPTY ;
589615 IObj s = (IObj ) PersistentList .create (list );
@@ -599,17 +625,17 @@ public Object invoke(Object reader, Object leftparen) {
599625}
600626
601627public static class VectorReader extends AFn {
602- public Object invoke (Object reader , Object leftparen ) {
628+ public Object invoke (Object reader , Object leftparen , Object opts ) {
603629 PushbackReader r = (PushbackReader ) reader ;
604- return LazilyPersistentVector .create (readDelimitedList (']' , r , true ));
630+ return LazilyPersistentVector .create (readDelimitedList (']' , r , true , opts ));
605631 }
606632
607633}
608634
609635public static class MapReader extends AFn {
610- public Object invoke (Object reader , Object leftparen ) {
636+ public Object invoke (Object reader , Object leftparen , Object opts ) {
611637 PushbackReader r = (PushbackReader ) reader ;
612- Object [] a = readDelimitedList ('}' , r , true ).toArray ();
638+ Object [] a = readDelimitedList ('}' , r , true , opts ).toArray ();
613639 if ((a .length & 1 ) == 1 )
614640 throw Util .runtimeException ("Map literal must contain an even number of forms" );
615641 return RT .map (a );
@@ -618,27 +644,27 @@ public Object invoke(Object reader, Object leftparen) {
618644}
619645
620646public static class SetReader extends AFn {
621- public Object invoke (Object reader , Object leftbracket ) {
647+ public Object invoke (Object reader , Object leftbracket , Object opts ) {
622648 PushbackReader r = (PushbackReader ) reader ;
623- return PersistentHashSet .createWithCheck (readDelimitedList ('}' , r , true ));
649+ return PersistentHashSet .createWithCheck (readDelimitedList ('}' , r , true , opts ));
624650 }
625651
626652}
627653
628654public static class UnmatchedDelimiterReader extends AFn {
629- public Object invoke (Object reader , Object rightdelim ) {
655+ public Object invoke (Object reader , Object rightdelim , Object opts ) {
630656 throw Util .runtimeException ("Unmatched delimiter: " + rightdelim );
631657 }
632658
633659}
634660
635661public static class UnreadableReader extends AFn {
636- public Object invoke (Object reader , Object leftangle ) {
662+ public Object invoke (Object reader , Object leftangle , Object opts ) {
637663 throw Util .runtimeException ("Unreadable form" );
638664 }
639665}
640666
641- public static List readDelimitedList (char delim , PushbackReader r , boolean isRecursive ) {
667+ public static List readDelimitedList (char delim , PushbackReader r , boolean isRecursive , Object opts ) {
642668 final int firstline =
643669 (r instanceof LineNumberingPushbackReader ) ?
644670 ((LineNumberingPushbackReader ) r ).getLineNumber () : -1 ;
@@ -666,7 +692,7 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
666692 IFn macroFn = getMacro (ch );
667693 if (macroFn != null )
668694 {
669- Object mret = macroFn .invoke (r , (char ) ch );
695+ Object mret = macroFn .invoke (r , (char ) ch , opts );
670696 //no op macros return the reader
671697 if (mret != r )
672698 a .add (mret );
@@ -675,7 +701,7 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
675701 {
676702 unread (r , ch );
677703
678- Object o = read (r , true , null , isRecursive );
704+ Object o = read (r , true , null , isRecursive , opts );
679705 if (o != r )
680706 a .add (o );
681707 }
@@ -685,5 +711,37 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
685711 return a ;
686712}
687713
714+ public static class TaggedReader extends AFn {
715+ public Object invoke (Object reader , Object firstChar , Object opts ){
716+ PushbackReader r = (PushbackReader ) reader ;
717+ Object name = read (r , true , null , false , opts );
718+ if (!(name instanceof Symbol ))
719+ throw new RuntimeException ("Reader tag must be a symbol" );
720+ Symbol sym = (Symbol )name ;
721+ return readTagged (r , sym , (IPersistentMap ) opts );
722+ }
723+
724+ static Keyword READERS = Keyword .intern (null ,"readers" );
725+ static Keyword DEFAULT = Keyword .intern (null ,"default" );
726+
727+ private Object readTagged (PushbackReader reader , Symbol tag , IPersistentMap opts ){
728+ Object o = read (reader , true , null , true , opts );
729+
730+ ILookup readers = (ILookup )RT .get (opts , READERS );
731+ IFn dataReader = (IFn )RT .get (readers , tag );
732+ if (dataReader == null )
733+ dataReader = (IFn )RT .get (RT .DEFAULT_DATA_READERS .deref (),tag );
734+ if (dataReader == null ){
735+ IFn defaultReader = (IFn )RT .get (opts , DEFAULT );
736+ if (defaultReader != null )
737+ return defaultReader .invoke (tag , o );
738+ else
739+ throw new RuntimeException ("No reader function for tag " + tag .toString ());
740+ }
741+ else
742+ return dataReader .invoke (o );
743+ }
744+
745+ }
688746}
689747
0 commit comments