@@ -9,6 +9,7 @@ pub type StringId = string_interner::symbol::SymbolU16;
99
1010/// String interner.
1111pub struct StringInterner {
12+ alphabetic_names : Vec < StringId > ,
1213 tuple_labels : Vec < StringId > ,
1314 strings : string_interner:: StringInterner <
1415 string_interner:: backend:: BucketBackend < StringId > ,
@@ -37,25 +38,62 @@ impl StringInterner {
3738 /// Construct an empty string interner.
3839 pub fn new ( ) -> StringInterner {
3940 StringInterner {
41+ alphabetic_names : Vec :: new ( ) ,
4042 tuple_labels : Vec :: new ( ) ,
4143 strings : string_interner:: StringInterner :: new ( ) ,
4244 }
4345 }
4446
45- /// Allocate and intern all tuple labels upto `max_index` if they are not already present
47+ /// Allocate and intern all alphabetic names up-to and including `max_index`
48+ /// if they are not already present.
49+ pub fn reserve_alphabetic_names ( & mut self , max_index : usize ) {
50+ fill_vec ( & mut self . alphabetic_names , max_index, |index| {
51+ self . strings . get_or_intern ( alphabetic_name ( index) )
52+ } )
53+ }
54+
55+ /// Retrieve an alphabetic name based on a numeric count. This is useful for
56+ /// producing human-readable names for unnamed binders.
57+ ///
58+ /// ## Example
59+ ///
60+ /// ```rust
61+ /// use fathom::source::StringInterner;
62+ ///
63+ /// let mut interner = StringInterner::new();
64+ /// assert_eq!(interner.get_alphabetic_name(0), interner.get_or_intern("a"));
65+ /// // ...
66+ /// assert_eq!(interner.get_alphabetic_name(25), interner.get_or_intern("z"));
67+ /// assert_eq!(interner.get_alphabetic_name(26), interner.get_or_intern("a1"));
68+ /// // ...
69+ /// assert_eq!(interner.get_alphabetic_name(51), interner.get_or_intern("z1"));
70+ /// assert_eq!(interner.get_alphabetic_name(52), interner.get_or_intern("a2"));
71+ /// // ...
72+ /// ```
73+ pub fn get_alphabetic_name ( & mut self , index : usize ) -> StringId {
74+ self . reserve_alphabetic_names ( index) ;
75+ self . alphabetic_names [ index]
76+ }
77+
78+ /// Allocate and intern all tuple labels up-to and including `max_index`
79+ /// if they are not already present.
4680 pub fn reserve_tuple_labels ( & mut self , max_index : usize ) {
47- let len = self . tuple_labels . len ( ) ;
48- let cap = self . tuple_labels . capacity ( ) ;
49- if max_index >= len {
50- self . tuple_labels . reserve ( max_index. saturating_sub ( cap) ) ;
51- for index in len..=max_index {
52- let label = self . get_or_intern ( format ! ( "_{index}" ) ) ;
53- self . tuple_labels . push ( label) ;
54- }
55- }
81+ fill_vec ( & mut self . tuple_labels , max_index, |index| {
82+ self . strings . get_or_intern ( format ! ( "_{index}" ) )
83+ } )
5684 }
5785
5886 /// Get or intern a string in the form `_{index}`.
87+ ///
88+ /// ## Example
89+ ///
90+ /// ```rust
91+ /// use fathom::source::StringInterner;
92+ ///
93+ /// let mut interner = StringInterner::new();
94+ /// assert_eq!(interner.get_tuple_label(0), interner.get_or_intern("_0"));
95+ /// assert_eq!(interner.get_tuple_label(1), interner.get_or_intern("_1"));
96+ /// ```
5997 pub fn get_tuple_label ( & mut self , index : usize ) -> StringId {
6098 self . reserve_tuple_labels ( index) ;
6199 self . tuple_labels [ index]
@@ -78,6 +116,21 @@ impl StringInterner {
78116 }
79117}
80118
119+ fn alphabetic_name ( index : usize ) -> String {
120+ let base = index / 26 ;
121+ let letter = index % 26 ;
122+ let letter = ( letter as u8 + b'a' ) as char ;
123+ if base == 0 {
124+ format ! ( "{letter}" )
125+ } else {
126+ format ! ( "{letter}{base}" )
127+ }
128+ }
129+
130+ fn fill_vec < T > ( vec : & mut Vec < T > , max_index : usize , f : impl FnMut ( usize ) -> T ) {
131+ vec. extend ( ( vec. len ( ) ..=max_index) . map ( f) )
132+ }
133+
81134#[ derive( Debug , Clone ) ]
82135pub struct Spanned < T > {
83136 span : Span ,
0 commit comments