@@ -2559,8 +2559,27 @@ def __repr__(self):
2559
2559
return "(initial)"
2560
2560
2561
2561
2562
+ class _FormatLike :
2563
+ def _as_format (self ) -> "Format" :
2564
+ raise NotImplementedError # :nocov:
2565
+
2566
+ def __add__ (self , other ):
2567
+ if not isinstance (other , _FormatLike ):
2568
+ return NotImplemented
2569
+ return Format ._from_chunks (self ._as_format ()._chunks + other ._as_format ()._chunks )
2570
+
2571
+ def __format__ (self , format_desc ):
2572
+ """Forbidden formatting.
2573
+
2574
+ ``Format`` objects cannot be directly formatted for the same reason as the ``Value``s
2575
+ they contain.
2576
+ """
2577
+ raise TypeError (f"Format object { self !r} cannot be converted to string. Use `repr` "
2578
+ f"to print the AST, or pass it to the `Print` statement." )
2579
+
2580
+
2562
2581
@final
2563
- class Format :
2582
+ class Format ( _FormatLike ) :
2564
2583
def __init__ (self , format , * args , ** kwargs ):
2565
2584
fmt = string .Formatter ()
2566
2585
chunks = []
@@ -2615,17 +2634,17 @@ def subformat(sub_string):
2615
2634
shape = obj .shape ()
2616
2635
if isinstance (shape , ShapeCastable ):
2617
2636
fmt = shape .format (obj , format_spec )
2618
- if not isinstance (fmt , Format ):
2637
+ if not isinstance (fmt , _FormatLike ):
2619
2638
raise TypeError (f"`ShapeCastable.format` must return a 'Format' instance, not { fmt !r} " )
2620
- chunks += fmt ._chunks
2639
+ chunks += fmt ._as_format (). _chunks
2621
2640
else :
2622
2641
obj = Value .cast (obj )
2623
2642
self ._parse_format_spec (format_spec , obj .shape ())
2624
2643
chunks .append ((obj , format_spec ))
2625
- elif isinstance (obj , Format ):
2644
+ elif isinstance (obj , _FormatLike ):
2626
2645
if format_spec != "" :
2627
2646
raise ValueError (f"Format specifiers ({ format_spec !r} ) cannot be used for 'Format' objects" )
2628
- chunks += obj ._chunks
2647
+ chunks += obj ._as_format (). _chunks
2629
2648
else :
2630
2649
chunks .append (fmt .format_field (obj , format_spec ))
2631
2650
@@ -2638,6 +2657,9 @@ def subformat(sub_string):
2638
2657
2639
2658
self ._chunks = self ._clean_chunks (chunks )
2640
2659
2660
+ def _as_format (self ):
2661
+ return self
2662
+
2641
2663
@classmethod
2642
2664
def _from_chunks (cls , chunks ):
2643
2665
res = object .__new__ (cls )
@@ -2671,25 +2693,11 @@ def _to_format_string(self):
2671
2693
format_string .append ("{}" )
2672
2694
return ("" .join (format_string ), tuple (args ))
2673
2695
2674
- def __add__ (self , other ):
2675
- if not isinstance (other , Format ):
2676
- return NotImplemented
2677
- return Format ._from_chunks (self ._chunks + other ._chunks )
2678
-
2679
2696
def __repr__ (self ):
2680
2697
format_string , args = self ._to_format_string ()
2681
2698
args = "" .join (f" { arg !r} " for arg in args )
2682
2699
return f"(format { format_string !r} { args } )"
2683
2700
2684
- def __format__ (self , format_desc ):
2685
- """Forbidden formatting.
2686
-
2687
- ``Format`` objects cannot be directly formatted for the same reason as the ``Value``s
2688
- they contain.
2689
- """
2690
- raise TypeError (f"Format object { self !r} cannot be converted to string. Use `repr` "
2691
- f"to print the AST, or pass it to the `Print` statement." )
2692
-
2693
2701
_FORMAT_SPEC_PATTERN = re .compile (r"""
2694
2702
(?:
2695
2703
(?P<fill>.)?
@@ -2760,6 +2768,90 @@ def _rhs_signals(self):
2760
2768
return res
2761
2769
2762
2770
2771
+ class Enum (_FormatLike ):
2772
+ def __init__ (self , value , / , variants ):
2773
+ self ._value = Value .cast (value )
2774
+ if isinstance (variants , EnumMeta ):
2775
+ self ._variants = {member .value : member .name for member in variants }
2776
+ else :
2777
+ self ._variants = dict (variants )
2778
+ for val , name in self ._variants .items ():
2779
+ if not isinstance (val , int ):
2780
+ raise TypeError (f"Variant values must be integers, not { val !r} " )
2781
+ if not isinstance (name , str ):
2782
+ raise TypeError (f"Variant names must be strings, not { name !r} " )
2783
+
2784
+ def _as_format (self ):
2785
+ def str_val (name ):
2786
+ name = name .encode ()
2787
+ return Const (int .from_bytes (name , "little" ), len (name ) * 8 )
2788
+ value = SwitchValue (self ._value , [
2789
+ (val , str_val (name ))
2790
+ for val , name in self ._variants .items ()
2791
+ ] + [(None , str_val ("[unknown]" ))])
2792
+ return Format ("{:s}" , value )
2793
+
2794
+ def __repr__ (self ):
2795
+ variants = "" .join (
2796
+ f" ({ val !r} { name !r} )"
2797
+ for val , name in self ._variants .items ()
2798
+ )
2799
+ return f"(format-enum { self ._value !r} { variants } )"
2800
+
2801
+
2802
+ class Struct (_FormatLike ):
2803
+ def __init__ (self , value , / , fields ):
2804
+ self ._value = Value .cast (value )
2805
+ self ._fields : dict [str , _FormatLike ] = dict (fields )
2806
+ for name , format in self ._fields .items ():
2807
+ if not isinstance (name , str ):
2808
+ raise TypeError (f"Field names must be strings, not { name !r} " )
2809
+ if not isinstance (format , _FormatLike ):
2810
+ raise TypeError (f"Field format must be a 'Format', not { format !r} " )
2811
+
2812
+ def _as_format (self ):
2813
+ chunks = ["{" ]
2814
+ for idx , (name , field ) in enumerate (self ._fields .items ()):
2815
+ if idx != 0 :
2816
+ chunks .append (", " )
2817
+ chunks .append (f"{ name } =" )
2818
+ chunks += field ._as_format ()._chunks
2819
+ chunks .append ("}" )
2820
+ return Format ._from_chunks (chunks )
2821
+
2822
+ def __repr__ (self ):
2823
+ fields = "" .join (
2824
+ f" ({ name !r} { field !r} )"
2825
+ for name , field in self ._fields .items ()
2826
+ )
2827
+ return f"(format-struct { self ._value !r} { fields } )"
2828
+
2829
+
2830
+ class Array (_FormatLike ):
2831
+ def __init__ (self , value , / , fields ):
2832
+ self ._value = Value .cast (value )
2833
+ self ._fields = list (fields )
2834
+ for format in self ._fields :
2835
+ if not isinstance (format , (Format , Format .Enum , Format .Struct , Format .Array )):
2836
+ raise TypeError (f"Field format must be a 'Format', not { format !r} " )
2837
+
2838
+ def _as_format (self ):
2839
+ chunks = ["[" ]
2840
+ for idx , field in enumerate (self ._fields ):
2841
+ if idx != 0 :
2842
+ chunks .append (", " )
2843
+ chunks += field ._as_format ()._chunks
2844
+ chunks .append ("]" )
2845
+ return Format ._from_chunks (chunks )
2846
+
2847
+ def __repr__ (self ):
2848
+ fields = "" .join (
2849
+ f" { field !r} "
2850
+ for field in self ._fields
2851
+ )
2852
+ return f"(format-array { self ._value !r} { fields } )"
2853
+
2854
+
2763
2855
class _StatementList (list ):
2764
2856
def __repr__ (self ):
2765
2857
return "({})" .format (" " .join (map (repr , self )))
@@ -2872,8 +2964,10 @@ def __init__(self, kind, test, message=None, *, src_loc_at=0):
2872
2964
self ._test = Value .cast (test )
2873
2965
if isinstance (message , str ):
2874
2966
message = Format ._from_chunks ([message ])
2875
- if message is not None and not isinstance (message , Format ):
2876
- raise TypeError (f"Property message must be None, str, or Format, not { message !r} " )
2967
+ if message is not None :
2968
+ if not isinstance (message , _FormatLike ):
2969
+ raise TypeError (f"Property message must be None, str, or Format, not { message !r} " )
2970
+ message = message ._as_format ()
2877
2971
self ._message = message
2878
2972
del self ._MustUse__silence
2879
2973
0 commit comments