@@ -527,28 +527,34 @@ defmodule Module.Types.Descr do
527
527
{ :term , [ ] , [ ] }
528
528
else
529
529
# Dynamic always come first for visibility
530
- { dynamic , descr } =
530
+ { dynamic , static } =
531
531
case :maps . take ( :dynamic , descr ) do
532
- :error -> { [ ] , descr }
533
- { :term , descr } -> { to_quoted ( :dynamic , : term, opts ) , descr }
534
- { dynamic , descr } -> { to_quoted ( :dynamic , difference ( dynamic , descr ) , opts ) , descr }
532
+ :error -> { % { } , descr }
533
+ { :term , static } -> { : term, static }
534
+ { dynamic , static } -> { difference ( dynamic , static ) , static }
535
535
end
536
536
537
+ { static , dynamic , extra } = fun_denormalize ( static , dynamic , opts )
538
+
537
539
# Merge empty list and list together if they both exist
538
- { extra , descr } =
539
- case descr do
540
+ { extra , static } =
541
+ case static do
540
542
% { list: list , bitmap: bitmap } when ( bitmap &&& @ bit_empty_list ) != 0 ->
541
- descr = descr |> Map . delete ( :list ) |> Map . replace! ( :bitmap , bitmap - @ bit_empty_list )
542
- { list_to_quoted ( list , true , opts ) , descr }
543
+ static =
544
+ static
545
+ |> Map . delete ( :list )
546
+ |> Map . replace! ( :bitmap , bitmap - @ bit_empty_list )
547
+
548
+ { list_to_quoted ( list , true , opts ) ++ extra , static }
543
549
544
550
% { } ->
545
- { [ ] , descr }
551
+ { extra , static }
546
552
end
547
553
548
554
unions =
549
- dynamic ++
555
+ to_quoted ( : dynamic, dynamic , opts ) ++
550
556
Enum . sort (
551
- extra ++ Enum . flat_map ( descr , fn { key , value } -> to_quoted ( key , value , opts ) end )
557
+ extra ++ Enum . flat_map ( static , fn { key , value } -> to_quoted ( key , value , opts ) end )
552
558
)
553
559
554
560
case unions do
@@ -564,7 +570,7 @@ defmodule Module.Types.Descr do
564
570
defp to_quoted ( :map , dnf , opts ) , do: map_to_quoted ( dnf , opts )
565
571
defp to_quoted ( :list , dnf , opts ) , do: list_to_quoted ( dnf , false , opts )
566
572
defp to_quoted ( :tuple , dnf , opts ) , do: tuple_to_quoted ( dnf , opts )
567
- defp to_quoted ( :fun , dnf , opts ) , do: fun_to_quoted ( dnf , opts )
573
+ defp to_quoted ( :fun , bdd , opts ) , do: fun_to_quoted ( bdd , opts )
568
574
569
575
@ doc """
570
576
Converts a descr to its quoted string representation.
@@ -1488,21 +1494,140 @@ defmodule Module.Types.Descr do
1488
1494
end
1489
1495
end
1490
1496
1491
- # Converts a function BDD (Binary Decision Diagram) to its quoted representation.
1492
- defp fun_to_quoted ( bdd , opts ) do
1493
- arrows = fun_get ( bdd )
1497
+ # Converts the static and dynamic parts of descr to its quoted
1498
+ # representation. The goal here is to the opposite of fun_descr
1499
+ # and put static and dynamic parts back together to improve
1500
+ # pretty printing.
1501
+ defp fun_denormalize ( % { fun: static_bdd } = static , % { fun: dynamic_bdd } = dynamic , opts ) do
1502
+ static_pos = fun_get_pos ( static_bdd )
1503
+ dynamic_pos = fun_get_pos ( dynamic_bdd )
1504
+
1505
+ if static_pos != [ ] and dynamic_pos != [ ] do
1506
+ { dynamic_pos , static_pos } = fun_denormalize_pos ( dynamic_pos , static_pos )
1494
1507
1495
- for { positives , negatives } <- arrows , not fun_empty? ( positives , negatives ) do
1496
- fun_intersection_to_quoted ( positives , opts )
1508
+ quoted =
1509
+ if dynamic_pos == [ ] do
1510
+ fun_pos_to_quoted ( static_pos , opts )
1511
+ else
1512
+ { :or , [ ] ,
1513
+ [
1514
+ { :dynamic , [ ] , [ fun_pos_to_quoted ( dynamic_pos , opts ) ] } ,
1515
+ fun_pos_to_quoted ( static_pos , opts )
1516
+ ] }
1517
+ end
1518
+
1519
+ { Map . delete ( static , :fun ) , Map . delete ( dynamic , :fun ) , [ quoted ] }
1520
+ else
1521
+ { static , dynamic , [ ] }
1497
1522
end
1498
- |> case do
1523
+ end
1524
+
1525
+ defp fun_denormalize ( static , dynamic , _opts ) do
1526
+ { static , dynamic , [ ] }
1527
+ end
1528
+
1529
+ defp fun_denormalize_pos ( dynamic_unions , static_unions ) do
1530
+ Enum . reduce ( dynamic_unions , { [ ] , static_unions } , fn
1531
+ # Handle fun() types accordingly
1532
+ [ ] , { dynamic_unions , static_unions } ->
1533
+ { [ [ ] | dynamic_unions ] , static_unions }
1534
+
1535
+ dynamic_intersections , { dynamic_unions , static_unions } ->
1536
+ { dynamic_intersections , static_unions } =
1537
+ Enum . reduce ( dynamic_intersections , { [ ] , static_unions } , fn
1538
+ { args , return } , { acc , static_unions } ->
1539
+ case fun_denormalize_arrow ( args , return , static_unions ) do
1540
+ { :ok , static_unions } -> { acc , static_unions }
1541
+ :error -> { [ { args , return } | acc ] , static_unions }
1542
+ end
1543
+ end )
1544
+
1545
+ if dynamic_intersections == [ ] do
1546
+ { dynamic_unions , static_unions }
1547
+ else
1548
+ { [ dynamic_intersections | dynamic_unions ] , static_unions }
1549
+ end
1550
+ end )
1551
+ end
1552
+
1553
+ defp fun_denormalize_arrow ( dynamic_args , dynamic_return , static_unions ) do
1554
+ pivot ( static_unions , [ ] , fn static_intersections ->
1555
+ pivot ( static_intersections , [ ] , fn { static_args , static_return } ->
1556
+ if subtype? ( static_return , dynamic_return ) and args_subtype? ( dynamic_args , static_args ) do
1557
+ args =
1558
+ Enum . zip_with ( static_args , dynamic_args , fn static_arg , dynamic_arg ->
1559
+ union ( dynamic ( difference ( static_arg , dynamic_arg ) ) , dynamic_arg )
1560
+ end )
1561
+
1562
+ return = union ( dynamic ( difference ( dynamic_return , static_return ) ) , static_return )
1563
+ { :ok , { args , return } }
1564
+ else
1565
+ :error
1566
+ end
1567
+ end )
1568
+ end )
1569
+ end
1570
+
1571
+ defp arrow_subtype? ( left_args , left_return , right_args , right_return ) do
1572
+ subtype? ( right_return , left_return ) and args_subtype? ( left_args , right_args )
1573
+ end
1574
+
1575
+ defp args_subtype? ( left_args , right_args ) do
1576
+ Enum . zip_reduce ( left_args , right_args , true , fn left , right , acc ->
1577
+ acc and subtype? ( left , right )
1578
+ end )
1579
+ end
1580
+
1581
+ defp pivot ( [ head | tail ] , acc , fun ) do
1582
+ case fun . ( head ) do
1583
+ { :ok , value } -> { :ok , acc ++ [ value | tail ] }
1584
+ :error -> pivot ( tail , [ head | acc ] , fun )
1585
+ end
1586
+ end
1587
+
1588
+ defp pivot ( [ ] , _acc , _fun ) , do: :error
1589
+
1590
+ # Converts a function BDD (Binary Decision Diagram) to its quoted representation
1591
+ defp fun_to_quoted ( bdd , opts ) do
1592
+ case fun_get_pos ( bdd ) do
1499
1593
[ ] -> [ ]
1500
- multiple -> [ Enum . reduce ( multiple , & { :or , [ ] , [ & 2 , & 1 ] } ) ]
1594
+ pos -> [ fun_pos_to_quoted ( pos , opts ) ]
1501
1595
end
1502
1596
end
1503
1597
1598
+ defp fun_get_pos ( bdd ) do
1599
+ for { pos , negs } <- fun_get ( bdd ) , not fun_empty? ( pos , negs ) do
1600
+ fun_filter_subset ( pos , [ ] )
1601
+ end
1602
+ end
1603
+
1604
+ # If one arrow is the subset of another arrow in the intersection,
1605
+ # we just remove it.
1606
+ defp fun_filter_subset ( [ ] , acc ) , do: acc
1607
+
1608
+ defp fun_filter_subset ( [ { args , return } | tail ] , acc ) do
1609
+ if Enum . any? ( tail , fn { other_args , other_return } ->
1610
+ arrow_subtype? ( other_args , other_return , args , return )
1611
+ end ) or
1612
+ Enum . any? ( acc , fn { other_args , other_return } ->
1613
+ arrow_subtype? ( other_args , other_return , args , return )
1614
+ end ) do
1615
+ fun_filter_subset ( tail , acc )
1616
+ else
1617
+ fun_filter_subset ( tail , [ { args , return } | acc ] )
1618
+ end
1619
+ end
1620
+
1621
+ defp fun_pos_to_quoted ( [ _ | _ ] = pos , opts ) do
1622
+ pos
1623
+ |> Enum . sort ( )
1624
+ |> Enum . map ( & fun_intersection_to_quoted ( & 1 , opts ) )
1625
+ |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
1626
+ end
1627
+
1504
1628
defp fun_intersection_to_quoted ( intersection , opts ) do
1505
1629
intersection
1630
+ |> Enum . sort ( )
1506
1631
|> Enum . map ( fn { args , ret } ->
1507
1632
{ :__block__ , [ ] ,
1508
1633
[ [ { :-> , [ ] , [ Enum . map ( args , & to_quoted ( & 1 , opts ) ) , to_quoted ( ret , opts ) ] } ] ] }
@@ -1904,6 +2029,9 @@ defmodule Module.Types.Descr do
1904
2029
1905
2030
defp dynamic_to_quoted ( descr , opts ) do
1906
2031
cond do
2032
+ descr == % { } ->
2033
+ [ ]
2034
+
1907
2035
term_type? ( descr ) ->
1908
2036
[ { :dynamic , [ ] , [ ] } ]
1909
2037
0 commit comments