indexmap
The indexmap
crate provides hash maps and sets that maintain insertion order. In compiler development, preserving the order of definitions, declarations, and operations is often crucial for deterministic output, meaningful error messages, and correct code generation. While standard hash maps offer O(1) access, they randomize iteration order, which can lead to non-deterministic compiler behavior. IndexMap combines the performance of hash maps with predictable iteration order.
Compilers frequently need ordered collections for symbol tables, import lists, struct field definitions, function parameters, and type registries. IndexMap ensures that iterating over these collections always produces the same order as insertion, which is essential for reproducible builds and stable compiler output across different runs.
Symbol Tables with Scopes
Symbol tables are fundamental to compilers, tracking identifiers and their associated information:
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } }
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } }
The symbol table uses a stack of IndexMaps to represent nested scopes. When looking up a symbol, it searches from the innermost scope outward, implementing proper lexical scoping while maintaining declaration order within each scope.
Struct Field Layout
Struct field ordering is critical for memory layout and ABI compatibility:
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } }
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } }
IndexMap preserves field definition order while providing both name-based and index-based access. This is essential for generating correct struct layouts and for error messages that reference fields in source order.
Import Resolution
Managing imports requires both deduplication and order preservation:
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } }
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } }
The import resolver uses IndexMap for modules and IndexSet for imported items. This ensures imports are processed in a consistent order and duplicates are automatically removed while maintaining the first occurrence position.
Type Registry
Type systems benefit from ordered type definitions:
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } }
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } }
The registry maintains types in registration order, which is important for error messages, documentation generation, and ensuring primitive types are always processed before user-defined types.
Local Variable Bindings
Tracking local variables in their declaration order helps with debugging and error reporting:
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } }
#![allow(unused)] fn main() { use std::hash::Hash; use indexmap::map::Entry; use indexmap::{IndexMap, IndexSet}; #[derive(Debug, Clone, PartialEq)] pub struct Symbol { pub name: String, pub kind: SymbolKind, pub scope_level: usize, } #[derive(Debug, Clone, PartialEq)] pub enum SymbolKind { Variable { mutable: bool, ty: String }, Function { params: Vec<String>, ret_ty: String }, Type { definition: String }, } pub struct SymbolTable { scopes: Vec<IndexMap<String, Symbol>>, } impl Default for SymbolTable { fn default() -> Self { Self::new() } } impl SymbolTable { pub fn new() -> Self { Self { scopes: vec![IndexMap::new()], } } pub fn push_scope(&mut self) { self.scopes.push(IndexMap::new()); } pub fn pop_scope(&mut self) -> Option<IndexMap<String, Symbol>> { if self.scopes.len() > 1 { self.scopes.pop() } else { None } } pub fn insert(&mut self, name: String, symbol: Symbol) -> Option<Symbol> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); if let Some(scope) = self.scopes.last_mut() { scope.insert(name, symbol) } else { None } } pub fn lookup(&self, name: &str) -> Option<&Symbol> { for scope in self.scopes.iter().rev() { if let Some(symbol) = scope.get(name) { return Some(symbol); } } None } pub fn current_scope_symbols(&self) -> Vec<(&String, &Symbol)> { // We maintain the invariant that there's always at least one scope debug_assert!( !self.scopes.is_empty(), "SymbolTable should always have at least one scope" ); self.scopes .last() .map(|scope| scope.iter().collect()) .unwrap_or_default() } } #[derive(Debug, Clone)] pub struct StructField { pub name: String, pub ty: String, pub offset: usize, } pub fn create_struct_layout() -> IndexMap<String, StructField> { let mut fields = IndexMap::new(); fields.insert( "id".to_string(), StructField { name: "id".to_string(), ty: "u64".to_string(), offset: 0, }, ); fields.insert( "name".to_string(), StructField { name: "name".to_string(), ty: "String".to_string(), offset: 8, }, ); fields.insert( "data".to_string(), StructField { name: "data".to_string(), ty: "Vec<u8>".to_string(), offset: 32, }, ); fields } pub struct ImportResolver { imports: IndexMap<String, IndexSet<String>>, } impl Default for ImportResolver { fn default() -> Self { Self::new() } } impl ImportResolver { pub fn new() -> Self { Self { imports: IndexMap::new(), } } pub fn add_import(&mut self, module: String, items: Vec<String>) { match self.imports.entry(module) { Entry::Occupied(mut e) => { for item in items { e.get_mut().insert(item); } } Entry::Vacant(e) => { let mut set = IndexSet::new(); for item in items { set.insert(item); } e.insert(set); } } } pub fn get_imports(&self) -> Vec<(String, Vec<String>)> { self.imports .iter() .map(|(module, items)| (module.clone(), items.iter().cloned().collect())) .collect() } } #[derive(Debug, Clone)] pub struct TypeDefinition { pub name: String, pub kind: TypeKind, } #[derive(Debug, Clone)] pub enum TypeKind { Primitive, Struct { fields: IndexMap<String, String> }, Enum { variants: IndexSet<String> }, Alias { target: String }, } pub struct TypeRegistry { types: IndexMap<String, TypeDefinition>, } impl Default for TypeRegistry { fn default() -> Self { Self::new() } } impl TypeRegistry { pub fn new() -> Self { let mut registry = Self { types: IndexMap::new(), }; registry.types.insert( "i32".to_string(), TypeDefinition { name: "i32".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "bool".to_string(), TypeDefinition { name: "bool".to_string(), kind: TypeKind::Primitive, }, ); registry.types.insert( "String".to_string(), TypeDefinition { name: "String".to_string(), kind: TypeKind::Primitive, }, ); registry } pub fn register_type(&mut self, def: TypeDefinition) -> bool { match self.types.entry(def.name.clone()) { Entry::Vacant(e) => { e.insert(def); true } Entry::Occupied(_) => false, } } pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> { self.types.get(name) } pub fn iter_types(&self) -> impl Iterator<Item = (&String, &TypeDefinition)> { self.types.iter() } } pub fn demonstrate_field_ordering() { let fields = create_struct_layout(); println!("Struct fields in definition order:"); for (i, (name, field)) in fields.iter().enumerate() { println!( " {}: {} ({}) at offset {}", i, name, field.ty, field.offset ); } println!(); println!("Field access by name:"); if let Some(field) = fields.get("name") { println!(" fields[\"name\"] = {:?}", field); } println!(); println!("Field access by index:"); if let Some((_name, field)) = fields.get_index(1) { println!(" fields[1] = {:?}", field); } } pub fn demonstrate_import_resolution() { let mut resolver = ImportResolver::new(); resolver.add_import( "std::collections".to_string(), vec!["HashMap".to_string(), "Vec".to_string()], ); resolver.add_import( "std::io".to_string(), vec!["Read".to_string(), "Write".to_string()], ); resolver.add_import("std::collections".to_string(), vec!["HashSet".to_string()]); println!("Import resolution order:"); for (module, items) in resolver.get_imports() { println!(" use {} {{ {} }};", module, items.join(", ")); } } pub struct LocalScope<K: Hash + Eq, V> { bindings: IndexMap<K, V>, } impl<K: Hash + Eq + Clone, V> Default for LocalScope<K, V> { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_symbol_table_scoping() { let mut table = SymbolTable::new(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: true, ty: "i32".to_string(), }, scope_level: 0, }, ); table.push_scope(); table.insert( "x".to_string(), Symbol { name: "x".to_string(), kind: SymbolKind::Variable { mutable: false, ty: "bool".to_string(), }, scope_level: 1, }, ); assert_eq!(table.lookup("x").unwrap().scope_level, 1); table.pop_scope(); assert_eq!(table.lookup("x").unwrap().scope_level, 0); } #[test] fn test_struct_field_ordering() { let fields = create_struct_layout(); let keys: Vec<_> = fields.keys().cloned().collect(); assert_eq!(keys, vec!["id", "name", "data"]); assert_eq!(fields.get_index(0).unwrap().0, "id"); assert_eq!(fields.get_index(1).unwrap().0, "name"); assert_eq!(fields.get_index(2).unwrap().0, "data"); } #[test] fn test_type_registry() { let mut registry = TypeRegistry::new(); let struct_def = TypeDefinition { name: "Point".to_string(), kind: TypeKind::Struct { fields: IndexMap::from([ ("x".to_string(), "f64".to_string()), ("y".to_string(), "f64".to_string()), ]), }, }; assert!(registry.register_type(struct_def.clone())); assert!(!registry.register_type(struct_def)); assert!(registry.get_type("Point").is_some()); assert!(registry.get_type("i32").is_some()); } } impl<K: Hash + Eq + Clone, V> LocalScope<K, V> { pub fn new() -> Self { Self { bindings: IndexMap::new(), } } pub fn bind(&mut self, name: K, value: V) -> Option<V> { self.bindings.insert(name, value) } pub fn lookup(&self, name: &K) -> Option<&V> { self.bindings.get(name) } pub fn bindings_ordered(&self) -> Vec<(K, &V)> { self.bindings.iter().map(|(k, v)| (k.clone(), v)).collect() } } }
This generic scope structure can track any kind of bindings while preserving order. The ordered iteration is particularly useful for displaying variable dumps or generating debug information.
Best Practices
Use IndexMap for any collection where iteration order matters for correctness or user experience. This includes symbol tables, type definitions, struct fields, function parameters, and import lists. The small overhead compared to HashMap is usually negligible compared to the benefits of deterministic behavior.
Leverage both map and index access patterns. IndexMap allows you to look up entries by key in O(1) time and also access them by position. This is useful for positional parameters, struct field offsets, and anywhere you need both named and indexed access.
Use IndexSet for ordered unique collections. Import lists, keyword sets, and type parameter bounds are good candidates. IndexSet provides the same ordering guarantees as IndexMap while ensuring uniqueness.
Consider using the Entry
API for efficient insertions and updates. This avoids double lookups and clearly expresses the intent to either update existing entries or insert new ones.
For deterministic compilation, ensure all collections that affect output use ordered variants. This includes not just IndexMap but also considering BTreeMap for sorted output or Vec for purely sequential access.
When implementing compiler passes that transform data structures, preserve ordering information. If a pass reads from an IndexMap and produces a new collection, use IndexMap for the output to maintain order invariants throughout the compilation pipeline.
Remember that IndexMap is not a replacement for Vec when you need purely sequential access. Use Vec for instruction sequences, basic blocks, and other truly linear data. Use IndexMap when you need both key-based lookup and order preservation.