51
51
lrepr ,
52
52
)
53
53
from basilisp .lang .source import format_source_context
54
+ from basilisp .lang .tagged import TaggedLiteral , tagged_literal
54
55
from basilisp .lang .typing import IterableLispForm , LispForm , ReaderForm
55
56
from basilisp .lang .util import munge
56
57
from basilisp .util import Maybe , partition
@@ -363,6 +364,7 @@ class ReaderContext:
363
364
"_default_data_reader_fn" ,
364
365
"_features" ,
365
366
"_process_reader_cond" ,
367
+ "_process_tagged_literals" ,
366
368
"_reader" ,
367
369
"_resolve" ,
368
370
"_in_anon_fn" ,
@@ -389,6 +391,7 @@ def __init__( # pylint: disable=too-many-arguments
389
391
self ._process_reader_cond = process_reader_cond
390
392
self ._reader = reader
391
393
self ._resolve = Maybe (resolver ).or_else_get (lambda x : x )
394
+ self ._process_tagged_literals : collections .deque [bool ] = collections .deque ([])
392
395
self ._in_anon_fn : collections .deque [bool ] = collections .deque ([])
393
396
self ._syntax_quoted : collections .deque [bool ] = collections .deque ([])
394
397
self ._gensym_env : collections .deque [GenSymEnvironment ] = collections .deque ([])
@@ -434,6 +437,19 @@ def is_in_anon_fn(self) -> bool:
434
437
except IndexError :
435
438
return False
436
439
440
+ @contextlib .contextmanager
441
+ def process_tagged_literals (self , v : bool ):
442
+ self ._process_tagged_literals .append (v )
443
+ yield
444
+ self ._process_tagged_literals .pop ()
445
+
446
+ @property
447
+ def should_process_tagged_literals (self ) -> bool :
448
+ try :
449
+ return self ._process_tagged_literals [- 1 ] is True
450
+ except IndexError :
451
+ return True
452
+
437
453
@property
438
454
def gensym_env (self ) -> GenSymEnvironment :
439
455
return self ._gensym_env [- 1 ]
@@ -655,7 +671,7 @@ def _read_coll(
655
671
continue
656
672
elif _should_splice_reader_conditional (ctx , elem ):
657
673
assert isinstance (elem , ReaderConditional )
658
- selected_feature = elem . select_feature (ctx . reader_features )
674
+ selected_feature = _select_reader_conditional_branch (ctx , elem )
659
675
if selected_feature is ReaderConditional .FEATURE_NOT_PRESENT :
660
676
continue
661
677
elif isinstance (selected_feature , vec .PersistentVector ):
@@ -726,7 +742,7 @@ def __read_map_elems(ctx: ReaderContext) -> Iterable[RawReaderForm]:
726
742
continue
727
743
elif _should_splice_reader_conditional (ctx , v ):
728
744
assert isinstance (v , ReaderConditional )
729
- selected_feature = v . select_feature (ctx . reader_features )
745
+ selected_feature = _select_reader_conditional_branch (ctx , v )
730
746
if selected_feature is ReaderConditional .FEATURE_NOT_PRESENT :
731
747
continue
732
748
elif isinstance (selected_feature , vec .PersistentVector ):
@@ -1443,6 +1459,23 @@ def _read_numeric_constant(ctx: ReaderContext) -> float:
1443
1459
return c
1444
1460
1445
1461
1462
+ def _select_reader_conditional_branch (
1463
+ ctx : ReaderContext , reader_cond : ReaderConditional
1464
+ ) -> LispReaderForm :
1465
+ """Select the reader conditional branch by feature and then resolve any tagged
1466
+ literals for the selected feature."""
1467
+
1468
+ def resolve_tagged_literals (form : LispReaderForm ):
1469
+ if isinstance (form , TaggedLiteral ):
1470
+ resolved = _postwalk (resolve_tagged_literals , form .form )
1471
+ return _resolve_tagged_literal (ctx , form .tag , resolved )
1472
+ return form
1473
+
1474
+ return _postwalk (
1475
+ resolve_tagged_literals , reader_cond .select_feature (ctx .reader_features )
1476
+ )
1477
+
1478
+
1446
1479
def _should_splice_reader_conditional (ctx : ReaderContext , form : LispReaderForm ) -> bool :
1447
1480
"""Return True if and only if form is a ReaderConditional which should be spliced
1448
1481
into a surrounding collection context."""
@@ -1453,9 +1486,61 @@ def _should_splice_reader_conditional(ctx: ReaderContext, form: LispReaderForm)
1453
1486
)
1454
1487
1455
1488
1456
- def _read_reader_conditional_preserving (ctx : ReaderContext ) -> ReaderConditional :
1457
- """Read a reader conditional form and return the unprocessed reader
1458
- conditional object."""
1489
+ def _read_reader_conditional_preserving (
1490
+ ctx : ReaderContext , is_splicing : bool
1491
+ ) -> ReaderConditional :
1492
+ """Read a reader conditional form and return the reader conditional object."""
1493
+ coll : list = []
1494
+ reader = ctx .reader
1495
+ while True :
1496
+ char = reader .peek ()
1497
+ if char == "" :
1498
+ raise ctx .eof_error ("Unexpected EOF in reader conditional" )
1499
+ if whitespace_chars .match (char ):
1500
+ reader .advance ()
1501
+ continue
1502
+ if char == ")" :
1503
+ reader .next_char ()
1504
+ return ReaderConditional (llist .list (coll ), is_splicing = is_splicing )
1505
+
1506
+ with ctx .process_tagged_literals (False ):
1507
+ elem = _read_next (ctx )
1508
+
1509
+ if elem is COMMENT or isinstance (elem , Comment ):
1510
+ continue
1511
+ elif _should_splice_reader_conditional (ctx , elem ):
1512
+ assert isinstance (elem , ReaderConditional )
1513
+ selected_feature = _select_reader_conditional_branch (ctx , elem )
1514
+ if selected_feature is ReaderConditional .FEATURE_NOT_PRESENT :
1515
+ continue
1516
+ elif isinstance (selected_feature , vec .PersistentVector ):
1517
+ coll .extend (selected_feature )
1518
+ else :
1519
+ raise ctx .syntax_error (
1520
+ "Expecting Vector for splicing reader conditional "
1521
+ f"form; got { type (selected_feature )} "
1522
+ )
1523
+ else :
1524
+ assert (
1525
+ not isinstance (elem , ReaderConditional )
1526
+ or not ctx .should_process_reader_cond
1527
+ ), "Reader conditionals must be processed if specified"
1528
+ coll .append (elem )
1529
+
1530
+
1531
+ def _read_reader_conditional (ctx : ReaderContext ) -> LispReaderForm :
1532
+ """Read a reader conditional form and either return it or process it and
1533
+ return the resulting form.
1534
+
1535
+ If the reader is not set to process the reader conditional, it will always
1536
+ be returned as a ReaderConditional object.
1537
+
1538
+ If the reader is set to process reader conditionals, only non-splicing reader
1539
+ conditionals are processed here. If no matching feature is found in a
1540
+ non-splicing reader conditional, a comment will be emitted (which is ultimately
1541
+ discarded downstream in the reader).
1542
+
1543
+ Splicing reader conditionals are processed in the respective collection readers."""
1459
1544
reader = ctx .reader
1460
1545
start = reader .advance ()
1461
1546
assert start == "?"
@@ -1477,27 +1562,9 @@ def _read_reader_conditional_preserving(ctx: ReaderContext) -> ReaderConditional
1477
1562
f"Expected opening '(' for reader conditional; got '{ open_char } '"
1478
1563
)
1479
1564
1480
- feature_list = _read_coll (ctx , llist .list , ")" , "reader conditional" )
1481
- assert isinstance (feature_list , llist .PersistentList )
1482
- return ReaderConditional (feature_list , is_splicing = is_splicing )
1483
-
1484
-
1485
- def _read_reader_conditional (ctx : ReaderContext ) -> LispReaderForm :
1486
- """Read a reader conditional form and either return it or process it and
1487
- return the resulting form.
1488
-
1489
- If the reader is not set to process the reader conditional, it will always
1490
- be returned as a ReaderConditional object.
1491
-
1492
- If the reader is set to process reader conditionals, only non-splicing reader
1493
- conditionals are processed here. If no matching feature is found in a
1494
- non-splicing reader conditional, a comment will be emitted (which is ultimately
1495
- discarded downstream in the reader).
1496
-
1497
- Splicing reader conditionals are processed in the respective collection readers."""
1498
- reader_cond = _read_reader_conditional_preserving (ctx )
1565
+ reader_cond = _read_reader_conditional_preserving (ctx , is_splicing )
1499
1566
if ctx .should_process_reader_cond and not reader_cond .is_splicing :
1500
- form = reader_cond . select_feature (ctx . reader_features )
1567
+ form = _select_reader_conditional_branch (ctx , reader_cond )
1501
1568
return cast (
1502
1569
LispReaderForm ,
1503
1570
COMMENT if form is ReaderConditional .FEATURE_NOT_PRESENT else form ,
@@ -1544,9 +1611,32 @@ def _load_record_or_type(
1544
1611
raise ctx .syntax_error ("Records may only be constructed from Vectors and Maps" )
1545
1612
1546
1613
1614
+ def _resolve_tagged_literal (
1615
+ ctx : ReaderContext , s : sym .Symbol , v : RawReaderForm
1616
+ ) -> LispReaderForm :
1617
+ """Resolve a tagged literal into whatever value is returned by the associated data reader."""
1618
+ data_reader = None
1619
+ if s in ctx .data_readers :
1620
+ data_reader = ctx .data_readers [s ]
1621
+ elif s in ReaderContext ._DATA_READERS :
1622
+ data_reader = ReaderContext ._DATA_READERS [s ]
1623
+
1624
+ if data_reader is not None :
1625
+ try :
1626
+ return data_reader (v )
1627
+ except SyntaxError as e :
1628
+ raise ctx .syntax_error (e .message ).with_traceback (e .__traceback__ ) from None
1629
+ elif s .ns is None and "." in s .name :
1630
+ return _load_record_or_type (ctx , s , v )
1631
+ else :
1632
+ try :
1633
+ return ctx .default_data_reader_fn (s , v )
1634
+ except SyntaxError as e :
1635
+ raise ctx .syntax_error (e .message ).with_traceback (e .__traceback__ ) from None
1636
+
1637
+
1547
1638
def _read_reader_macro (ctx : ReaderContext ) -> LispReaderForm : # noqa: MC0001
1548
- """Return a data structure evaluated as a reader
1549
- macro from the input stream."""
1639
+ """Return a data structure evaluated as a reader macro from the input stream."""
1550
1640
start = ctx .reader .advance ()
1551
1641
assert start == "#"
1552
1642
char = ctx .reader .peek ()
@@ -1587,28 +1677,10 @@ def _read_reader_macro(ctx: ReaderContext) -> LispReaderForm: # noqa: MC0001
1587
1677
1588
1678
v = _read_next_consuming_comment (ctx )
1589
1679
1590
- data_reader = None
1591
- if s in ctx .data_readers :
1592
- data_reader = ctx .data_readers [s ]
1593
- elif s in ReaderContext ._DATA_READERS :
1594
- data_reader = ReaderContext ._DATA_READERS [s ]
1680
+ if not ctx .should_process_tagged_literals :
1681
+ return tagged_literal (s , v )
1595
1682
1596
- if data_reader is not None :
1597
- try :
1598
- return data_reader (v )
1599
- except SyntaxError as e :
1600
- raise ctx .syntax_error (e .message ).with_traceback (
1601
- e .__traceback__
1602
- ) from None
1603
- elif s .ns is None and "." in s .name :
1604
- return _load_record_or_type (ctx , s , v )
1605
- else :
1606
- try :
1607
- return ctx .default_data_reader_fn (s , v )
1608
- except SyntaxError as e :
1609
- raise ctx .syntax_error (e .message ).with_traceback (
1610
- e .__traceback__
1611
- ) from None
1683
+ return _resolve_tagged_literal (ctx , s , v )
1612
1684
1613
1685
raise ctx .syntax_error (f"Unexpected char '{ char } ' in reader macro" )
1614
1686
0 commit comments