|
1 | 1 | package ast |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "hash/maphash" |
| 5 | + "iter" |
| 6 | + "slices" |
4 | 7 | "strings" |
5 | 8 | "sync/atomic" |
6 | 9 |
|
@@ -164,8 +167,211 @@ func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags { |
164 | 167 | } |
165 | 168 |
|
166 | 169 | // SymbolTable |
| 170 | +// |
| 171 | +// A SymbolTable maps names to symbols and iterates in insertion order, like Strada's Map, so that iterating a table |
| 172 | +// is deterministic and visits symbols in the order the binder and checker added them. A nil SymbolTable is an empty |
| 173 | +// table that can be read and iterated but not written, like a nil map. |
| 174 | +// |
| 175 | +// The entries are kept in insertion order in one slice. Most tables are small, so a table is searched linearly until |
| 176 | +// it has more than symbolTableLinearMax entries, and only then gets a hash index of entry positions. An entry does not |
| 177 | +// store its key as the key is almost always the symbol's own name. In the rare cases when a symbol has a different name, |
| 178 | +// the table stores a proxy symbol with the new name and a reference to the original symbol in the parent field. |
| 179 | +// |
| 180 | +// A table must not be changed with Set or Delete while it is being iterated. |
| 181 | +type SymbolTable = *SymbolMap |
167 | 182 |
|
168 | | -type SymbolTable map[string]*Symbol |
| 183 | +const symbolTableLinearMax = 16 |
| 184 | + |
| 185 | +type SymbolMap struct { |
| 186 | + entries []*Symbol |
| 187 | + index []int32 // open addressing table of entry positions plus one (0 is empty), only past symbolTableLinearMax |
| 188 | +} |
| 189 | + |
| 190 | +var symbolTableSeed = maphash.MakeSeed() |
| 191 | + |
| 192 | +func NewSymbolTable() SymbolTable { |
| 193 | + return NewSymbolTableWithCapacity(0) |
| 194 | +} |
| 195 | + |
| 196 | +func NewSymbolTableWithCapacity(capacity int) SymbolTable { |
| 197 | + m := &SymbolMap{} |
| 198 | + if capacity > 0 { |
| 199 | + m.entries = make([]*Symbol, 0, capacity) |
| 200 | + if capacity > symbolTableLinearMax { |
| 201 | + m.index = make([]int32, indexSizeFor(capacity)) |
| 202 | + } |
| 203 | + } |
| 204 | + return m |
| 205 | +} |
| 206 | + |
| 207 | +// SymbolTableOf returns a new table holding symbol under name. |
| 208 | +func SymbolTableOf(name string, symbol *Symbol) SymbolTable { |
| 209 | + m := NewSymbolTableWithCapacity(1) |
| 210 | + m.Set(name, symbol) |
| 211 | + return m |
| 212 | +} |
| 213 | + |
| 214 | +func (m *SymbolMap) keyAt(i int) string { |
| 215 | + return m.entries[i].Name() |
| 216 | +} |
| 217 | + |
| 218 | +func (m *SymbolMap) symbolAt(i int) *Symbol { |
| 219 | + symbol := m.entries[i] |
| 220 | + if symbol.flags == SymbolFlagsNameProxy { |
| 221 | + symbol = symbol.data.parent |
| 222 | + } |
| 223 | + return symbol |
| 224 | +} |
| 225 | + |
| 226 | +func (m *SymbolMap) find(key string) int { |
| 227 | + if m == nil { |
| 228 | + return -1 |
| 229 | + } |
| 230 | + if m.index != nil { |
| 231 | + mask := uint64(len(m.index) - 1) |
| 232 | + for slot := maphash.String(symbolTableSeed, key) & mask; ; slot = (slot + 1) & mask { |
| 233 | + p := m.index[slot] |
| 234 | + if p == 0 { |
| 235 | + return -1 |
| 236 | + } |
| 237 | + if i := int(p - 1); m.keyAt(i) == key { |
| 238 | + return i |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + for i := range m.entries { |
| 243 | + if m.keyAt(i) == key { |
| 244 | + return i |
| 245 | + } |
| 246 | + } |
| 247 | + return -1 |
| 248 | +} |
| 249 | + |
| 250 | +func (m *SymbolMap) Len() int { |
| 251 | + if m == nil { |
| 252 | + return 0 |
| 253 | + } |
| 254 | + return len(m.entries) |
| 255 | +} |
| 256 | + |
| 257 | +func (m *SymbolMap) Get(key string) *Symbol { |
| 258 | + if i := m.find(key); i >= 0 { |
| 259 | + return m.symbolAt(i) |
| 260 | + } |
| 261 | + return nil |
| 262 | +} |
| 263 | + |
| 264 | +func (m *SymbolMap) Lookup(key string) (*Symbol, bool) { |
| 265 | + if i := m.find(key); i >= 0 { |
| 266 | + return m.symbolAt(i), true |
| 267 | + } |
| 268 | + return nil, false |
| 269 | +} |
| 270 | + |
| 271 | +// Set replaces the symbol of an existing key in place, keeping its position, or adds the key at the end. |
| 272 | +func (m *SymbolMap) Set(key string, symbol *Symbol) { |
| 273 | + if key != symbol.Name() { |
| 274 | + proxy := NewSymbol() |
| 275 | + proxy.flags = SymbolFlagsNameProxy |
| 276 | + proxy.data.name = key |
| 277 | + proxy.data.parent = symbol |
| 278 | + symbol = proxy |
| 279 | + } |
| 280 | + if i := m.find(key); i >= 0 { |
| 281 | + m.entries[i] = symbol |
| 282 | + return |
| 283 | + } |
| 284 | + i := int32(len(m.entries)) |
| 285 | + if cap(m.entries) == 0 { |
| 286 | + // Most tables hold one or two entries, so start with room for two rather than growing from one. |
| 287 | + m.entries = make([]*Symbol, 0, 2) |
| 288 | + } |
| 289 | + m.entries = append(m.entries, symbol) |
| 290 | + if m.index != nil && len(m.entries)*4 <= len(m.index)*3 { |
| 291 | + m.insertIndex(key, i) |
| 292 | + } else if len(m.entries) > symbolTableLinearMax { |
| 293 | + m.buildIndex() |
| 294 | + } |
| 295 | +} |
| 296 | + |
| 297 | +func indexSizeFor(n int) int { |
| 298 | + size := 32 |
| 299 | + for size*3 < n*4 { |
| 300 | + size *= 2 |
| 301 | + } |
| 302 | + return size |
| 303 | +} |
| 304 | + |
| 305 | +func (m *SymbolMap) insertIndex(key string, i int32) { |
| 306 | + mask := uint64(len(m.index) - 1) |
| 307 | + slot := maphash.String(symbolTableSeed, key) & mask |
| 308 | + for m.index[slot] != 0 { |
| 309 | + slot = (slot + 1) & mask |
| 310 | + } |
| 311 | + m.index[slot] = i + 1 |
| 312 | +} |
| 313 | + |
| 314 | +func (m *SymbolMap) buildIndex() { |
| 315 | + m.index = make([]int32, indexSizeFor(len(m.entries))) |
| 316 | + for i := range m.entries { |
| 317 | + m.insertIndex(m.keyAt(i), int32(i)) |
| 318 | + } |
| 319 | +} |
| 320 | + |
| 321 | +func (m *SymbolMap) Delete(key string) { |
| 322 | + d := m.find(key) |
| 323 | + if d < 0 { |
| 324 | + return |
| 325 | + } |
| 326 | + m.entries = slices.Delete(m.entries, d, d+1) |
| 327 | + m.index = nil |
| 328 | + if len(m.entries) > symbolTableLinearMax { |
| 329 | + m.buildIndex() |
| 330 | + } |
| 331 | +} |
| 332 | + |
| 333 | +// All iterates over the keys and symbols of the table in insertion order. |
| 334 | +func (m *SymbolMap) All() iter.Seq2[string, *Symbol] { |
| 335 | + return func(yield func(string, *Symbol) bool) { |
| 336 | + if m == nil { |
| 337 | + return |
| 338 | + } |
| 339 | + entries := m.entries |
| 340 | + for i := range entries { |
| 341 | + if !yield(m.keyAt(i), m.symbolAt(i)) { |
| 342 | + return |
| 343 | + } |
| 344 | + } |
| 345 | + } |
| 346 | +} |
| 347 | + |
| 348 | +func (m *SymbolMap) Keys() iter.Seq[string] { |
| 349 | + return func(yield func(string) bool) { |
| 350 | + for key := range m.All() { |
| 351 | + if !yield(key) { |
| 352 | + return |
| 353 | + } |
| 354 | + } |
| 355 | + } |
| 356 | +} |
| 357 | + |
| 358 | +func (m *SymbolMap) Values() iter.Seq[*Symbol] { |
| 359 | + return func(yield func(*Symbol) bool) { |
| 360 | + for _, symbol := range m.All() { |
| 361 | + if !yield(symbol) { |
| 362 | + return |
| 363 | + } |
| 364 | + } |
| 365 | + } |
| 366 | +} |
| 367 | + |
| 368 | +func (m *SymbolMap) Clone() SymbolTable { |
| 369 | + if m == nil { |
| 370 | + return nil |
| 371 | + } |
| 372 | + clone := &SymbolMap{entries: slices.Clone(m.entries), index: slices.Clone(m.index)} |
| 373 | + return clone |
| 374 | +} |
169 | 375 |
|
170 | 376 | const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName |
171 | 377 |
|
|
0 commit comments