14
14
Collection ,
15
15
Deque ,
16
16
Dict ,
17
+ Hashable ,
17
18
Iterable ,
18
19
List ,
19
20
MutableMapping ,
@@ -470,9 +471,9 @@ def _with_loc(f: W) -> W:
470
471
information along with relevant forms."""
471
472
472
473
@functools .wraps (f )
473
- def with_lineno_and_col (ctx ):
474
+ def with_lineno_and_col (ctx , ** kwargs ):
474
475
line , col = ctx .reader .line , ctx .reader .col
475
- v = f (ctx )
476
+ v = f (ctx , ** kwargs ) # type: ignore[call-arg]
476
477
if isinstance (v , IWithMeta ):
477
478
new_meta = lmap .map ({READER_LINE_KW : line , READER_COL_KW : col })
478
479
old_meta = v .meta
@@ -643,15 +644,42 @@ def __read_map_elems(ctx: ReaderContext) -> Iterable[RawReaderForm]:
643
644
yield v
644
645
645
646
647
+ def _map_key_processor (namespace : Optional [str ],) -> Callable [[Hashable ], Hashable ]:
648
+ """Return a map key processor.
649
+
650
+ If no `namespace` is provided, return an identity function. If a `namespace`
651
+ is given, return a function that can apply namespaces to un-namespaced
652
+ keyword and symbol values."""
653
+ if namespace is None :
654
+ return lambda v : v
655
+
656
+ def process_key (k : Any ) -> Any :
657
+ if isinstance (k , keyword .Keyword ):
658
+ if k .ns is None :
659
+ return keyword .keyword (k .name , ns = namespace )
660
+ if k .ns == "_" :
661
+ return keyword .keyword (k .name )
662
+ if isinstance (k , symbol .Symbol ):
663
+ if k .ns is None :
664
+ return symbol .symbol (k .name , ns = namespace )
665
+ if k .ns == "_" :
666
+ return symbol .symbol (k .name )
667
+ return k
668
+
669
+ return process_key
670
+
671
+
646
672
@_with_loc
647
- def _read_map (ctx : ReaderContext ) -> lmap .Map :
673
+ def _read_map (ctx : ReaderContext , namespace : Optional [ str ] = None ) -> lmap .Map :
648
674
"""Return a map from the input stream."""
649
675
reader = ctx .reader
650
676
start = reader .advance ()
651
677
assert start == "{"
652
678
d : MutableMapping [Any , Any ] = {}
679
+ process_key = _map_key_processor (namespace )
653
680
try :
654
681
for k , v in partition (list (__read_map_elems (ctx )), 2 ):
682
+ k = process_key (k )
655
683
if k in d :
656
684
raise ctx .syntax_error (f"Duplicate key '{ k } ' in map literal" )
657
685
d [k ] = v
@@ -661,6 +689,30 @@ def _read_map(ctx: ReaderContext) -> lmap.Map:
661
689
return lmap .map (d )
662
690
663
691
692
+ def _read_namespaced_map (ctx : ReaderContext ) -> lmap .Map :
693
+ """Read a namespaced map from the input stream."""
694
+ start = ctx .reader .peek ()
695
+ assert start == ":"
696
+ ctx .reader .advance ()
697
+ if ctx .reader .peek () == ":" :
698
+ ctx .reader .advance ()
699
+ current_ns = get_current_ns ()
700
+ map_ns = current_ns .name
701
+ else :
702
+ kw_ns , map_ns = _read_namespaced (ctx )
703
+ if kw_ns is not None :
704
+ raise ctx .syntax_error (
705
+ f"Invalid map namespace '{ kw_ns } /{ map_ns } '; namespaces for maps must "
706
+ "be specified as keywords without namespaces"
707
+ )
708
+
709
+ token = ctx .reader .peek ()
710
+ while whitespace_chars .match (token ):
711
+ token = ctx .reader .next_token ()
712
+
713
+ return _read_map (ctx , namespace = map_ns )
714
+
715
+
664
716
# Due to some ambiguities that arise in parsing symbols, numbers, and the
665
717
# special keywords `true`, `false`, and `nil`, we have to have a looser
666
718
# type defined for the return from these reader functions.
@@ -835,8 +887,7 @@ def _read_kw(ctx: ReaderContext) -> keyword.Keyword:
835
887
"""Return a keyword from the input stream."""
836
888
start = ctx .reader .advance ()
837
889
assert start == ":"
838
- token = ctx .reader .peek ()
839
- if token == ":" :
890
+ if ctx .reader .peek () == ":" :
840
891
ctx .reader .advance ()
841
892
should_autoresolve = True
842
893
else :
@@ -1290,6 +1341,8 @@ def _read_reader_macro(ctx: ReaderContext) -> LispReaderForm:
1290
1341
return _read_set (ctx )
1291
1342
elif token == "(" :
1292
1343
return _read_function (ctx )
1344
+ elif token == ":" :
1345
+ return _read_namespaced_map (ctx )
1293
1346
elif token == "'" :
1294
1347
ctx .reader .advance ()
1295
1348
s = _read_sym (ctx )
0 commit comments