use std::collections::{HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ModuleName {
pub components: Vec<String>,
}
impl ModuleName {
pub fn new(components: Vec<String>) -> Self {
Self { components }
}
pub fn from_str(s: &str) -> Self {
Self {
components: s.split('.').map(|c| c.to_string()).collect(),
}
}
pub fn is_empty(&self) -> bool {
self.components.is_empty()
}
}
impl fmt::Display for ModuleName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, comp) in self.components.iter().enumerate() {
if i > 0 {
write!(f, ".")?;
}
write!(f, "{}", comp)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModuleKind {
ModuleInterface,
ModuleImplementation,
ModulePartitionInterface,
ModulePartitionImplementation,
GlobalModuleFragment,
PrivateModuleFragment,
}
impl ModuleKind {
pub fn is_exported(&self) -> bool {
matches!(
self,
ModuleKind::ModuleInterface | ModuleKind::ModulePartitionInterface
)
}
pub fn is_partition(&self) -> bool {
matches!(
self,
ModuleKind::ModulePartitionInterface | ModuleKind::ModulePartitionImplementation
)
}
pub fn is_fragment(&self) -> bool {
matches!(
self,
ModuleKind::GlobalModuleFragment | ModuleKind::PrivateModuleFragment
)
}
}
#[derive(Debug, Clone)]
pub struct ModuleDecl {
pub name: ModuleName,
pub kind: ModuleKind,
pub partition: Option<String>,
pub source_loc: Option<String>,
}
impl ModuleDecl {
pub fn interface(name: ModuleName) -> Self {
Self {
name,
kind: ModuleKind::ModuleInterface,
partition: None,
source_loc: None,
}
}
pub fn implementation(name: ModuleName) -> Self {
Self {
name,
kind: ModuleKind::ModuleImplementation,
partition: None,
source_loc: None,
}
}
pub fn global_fragment() -> Self {
Self {
name: ModuleName::new(vec![]),
kind: ModuleKind::GlobalModuleFragment,
partition: None,
source_loc: None,
}
}
pub fn private_fragment() -> Self {
Self {
name: ModuleName::new(vec![]),
kind: ModuleKind::PrivateModuleFragment,
partition: None,
source_loc: None,
}
}
}
impl fmt::Display for ModuleDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ModuleKind::ModuleInterface => write!(f, "export module {}", self.name),
ModuleKind::ModuleImplementation => write!(f, "module {}", self.name),
ModuleKind::ModulePartitionInterface => {
write!(f, "export module {}:", self.name)?;
if let Some(ref p) = self.partition {
write!(f, "{}", p)?;
}
Ok(())
}
ModuleKind::ModulePartitionImplementation => {
write!(f, "module {}:", self.name)?;
if let Some(ref p) = self.partition {
write!(f, "{}", p)?;
}
Ok(())
}
ModuleKind::GlobalModuleFragment => write!(f, "module;"),
ModuleKind::PrivateModuleFragment => write!(f, "module :private;"),
}
}
}
#[derive(Debug, Clone)]
pub struct ModuleImport {
pub name: ModuleName,
pub is_exported: bool,
pub is_header_unit: bool,
pub header_name: Option<String>,
pub source_loc: Option<String>,
}
impl ModuleImport {
pub fn new(name: ModuleName) -> Self {
Self {
name,
is_exported: false,
is_header_unit: false,
header_name: None,
source_loc: None,
}
}
pub fn exported(name: ModuleName) -> Self {
Self {
name,
is_exported: true,
is_header_unit: false,
header_name: None,
source_loc: None,
}
}
pub fn header_unit(header: &str) -> Self {
Self {
name: ModuleName::from_str(header),
is_exported: false,
is_header_unit: true,
header_name: Some(header.to_string()),
source_loc: None,
}
}
}
impl fmt::Display for ModuleImport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_exported {
write!(f, "export ")?;
}
write!(f, "import ")?;
if self.is_header_unit {
if let Some(ref h) = self.header_name {
write!(f, "<{}>", h)
} else {
write!(f, "<unknown>")
}
} else {
write!(f, "{}", self.name)
}
}
}
#[derive(Debug, Clone)]
pub struct ModuleMap {
modules: HashMap<String, ModuleInfo>,
imports: HashMap<String, HashSet<String>>,
}
#[derive(Debug, Clone)]
pub struct ModuleInfo {
pub name: ModuleName,
pub is_interface: bool,
pub bmi_path: Option<String>,
pub source_path: Option<String>,
pub dependencies: Vec<ModuleName>,
}
impl ModuleInfo {
pub fn new(name: ModuleName, is_interface: bool) -> Self {
Self {
name,
is_interface,
bmi_path: None,
source_path: None,
dependencies: Vec::new(),
}
}
}
impl ModuleMap {
pub fn new() -> Self {
Self {
modules: HashMap::new(),
imports: HashMap::new(),
}
}
pub fn register_module(&mut self, info: ModuleInfo) {
let name_str = info.name.to_string();
self.modules.insert(name_str.clone(), info);
}
pub fn add_import(&mut self, importer: &str, imported: &str) {
self.imports
.entry(importer.to_string())
.or_insert_with(HashSet::new)
.insert(imported.to_string());
}
pub fn has_module(&self, name: &str) -> bool {
self.modules.contains_key(name)
}
pub fn get_module(&self, name: &str) -> Option<&ModuleInfo> {
self.modules.get(name)
}
pub fn get_imports(&self, name: &str) -> Vec<&str> {
self.imports
.get(name)
.map(|set| set.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
pub fn check_circular_imports(&self) -> Vec<Vec<String>> {
let mut cycles = Vec::new();
for module_name in self.modules.keys() {
let mut visited = HashSet::new();
let mut path = Vec::new();
self.dfs_find_cycle(module_name, &mut visited, &mut path, &mut cycles);
}
cycles
}
fn dfs_find_cycle(
&self,
current: &str,
visited: &mut HashSet<String>,
path: &mut Vec<String>,
cycles: &mut Vec<Vec<String>>,
) {
if path.contains(¤t.to_string()) {
if let Some(pos) = path.iter().position(|n| n == current) {
let mut cycle: Vec<String> = path[pos..].to_vec();
cycle.push(current.to_string());
cycles.push(cycle);
}
return;
}
if visited.contains(current) {
return;
}
visited.insert(current.to_string());
path.push(current.to_string());
if let Some(imports) = self.imports.get(current) {
for imported in imports {
self.dfs_find_cycle(imported, visited, path, cycles);
}
}
path.pop();
}
pub fn to_ir_metadata(&self) -> String {
let mut ir = String::new();
ir.push_str("; Module Map Metadata\n");
for (name, info) in &self.modules {
ir.push_str(&format!(
"!module.{} = !{{!\"{}\", i1 {}}}\n",
name,
name,
if info.is_interface { "true" } else { "false" }
));
}
for (importer, imported_set) in &self.imports {
for imported in imported_set {
ir.push_str(&format!(
"!module.import = !{{!\"{}\", !\"{}\"}}\n",
importer, imported
));
}
}
ir
}
}
impl Default for ModuleMap {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ModuleValidator {
errors: Vec<String>,
}
impl ModuleValidator {
pub fn new() -> Self {
Self { errors: Vec::new() }
}
pub fn validate_module_decl(&mut self, decl: &ModuleDecl) -> bool {
self.errors.clear();
if !decl.kind.is_fragment() && decl.name.is_empty() {
self.errors.push("module name must not be empty".into());
return false;
}
if decl.kind.is_partition() && decl.name.is_empty() {
self.errors
.push("module partition requires a module name".into());
return false;
}
if decl.kind == ModuleKind::GlobalModuleFragment {
}
if decl.kind == ModuleKind::PrivateModuleFragment {
}
true
}
pub fn validate_import(&mut self, import: &ModuleImport, module_map: &ModuleMap) -> bool {
self.errors.clear();
if import.is_header_unit {
return true;
}
let name_str = import.name.to_string();
if !module_map.has_module(&name_str) {
self.errors
.push(format!("module '{}' not found in module map", name_str));
return false;
}
true
}
pub fn errors(&self) -> &[String] {
&self.errors
}
}
impl Default for ModuleValidator {
fn default() -> Self {
Self::new()
}
}
pub fn parse_module_decl(source: &str) -> Option<ModuleDecl> {
let trimmed = source.trim();
if trimmed == "module;" {
return Some(ModuleDecl::global_fragment());
}
if trimmed == "module :private;" {
return Some(ModuleDecl::private_fragment());
}
let has_export = trimmed.starts_with("export ");
let rest = if has_export {
&trimmed["export ".len()..]
} else {
trimmed
};
if !rest.starts_with("module ") {
return None;
}
let module_part = rest["module ".len()..].trim_end_matches(';').trim();
if let Some(colon_pos) = module_part.find(':') {
let name = ModuleName::from_str(module_part[..colon_pos].trim());
let partition = module_part[colon_pos + 1..].trim().to_string();
let kind = if has_export {
ModuleKind::ModulePartitionInterface
} else {
ModuleKind::ModulePartitionImplementation
};
Some(ModuleDecl {
name,
kind,
partition: Some(partition),
source_loc: None,
})
} else {
let name = ModuleName::from_str(module_part);
let kind = if has_export {
ModuleKind::ModuleInterface
} else {
ModuleKind::ModuleImplementation
};
Some(ModuleDecl {
name,
kind,
partition: None,
source_loc: None,
})
}
}
pub fn parse_import_decl(source: &str) -> Option<ModuleImport> {
let trimmed = source.trim();
let has_export = trimmed.starts_with("export ");
let rest = if has_export {
&trimmed["export ".len()..]
} else {
trimmed
};
if !rest.starts_with("import ") {
return None;
}
let import_target = rest["import ".len()..].trim_end_matches(';').trim();
if import_target.starts_with('<') && import_target.ends_with('>') {
let header = &import_target[1..import_target.len() - 1];
Some(ModuleImport {
name: ModuleName::from_str(header),
is_exported: has_export,
is_header_unit: true,
header_name: Some(header.to_string()),
source_loc: None,
})
} else {
Some(ModuleImport {
name: ModuleName::from_str(import_target),
is_exported: has_export,
is_header_unit: false,
header_name: None,
source_loc: None,
})
}
}
#[derive(Debug, Clone)]
pub struct ModuleOwnership {
pub current_module: Option<ModuleName>,
owner_map: HashMap<usize, ModuleName>,
reachable_decls: HashSet<usize>,
}
impl ModuleOwnership {
pub fn new() -> Self {
Self {
current_module: None,
owner_map: HashMap::new(),
reachable_decls: HashSet::new(),
}
}
pub fn enter_module(&mut self, module: ModuleName) {
self.current_module = Some(module);
}
pub fn leave_module(&mut self) {
self.current_module = None;
}
pub fn record_decl_ownership(&mut self, decl_id: usize) {
if let Some(ref module) = self.current_module {
self.owner_map.insert(decl_id, module.clone());
}
}
pub fn owner_of(&self, decl_id: usize) -> Option<&ModuleName> {
self.owner_map.get(&decl_id)
}
pub fn mark_reachable(&mut self, decl_id: usize) {
self.reachable_decls.insert(decl_id);
}
pub fn is_reachable(&self, decl_id: usize) -> bool {
self.reachable_decls.contains(&decl_id)
}
pub fn is_visible(&self, decl_id: usize) -> bool {
if let Some(ref current) = self.current_module {
match self.owner_map.get(&decl_id) {
Some(owner) if owner == current => true,
Some(_) => self.reachable_decls.contains(&decl_id),
None => true, }
} else {
true }
}
}
impl Default for ModuleOwnership {
fn default() -> Self {
Self::new()
}
}
pub struct GlobalModuleFragment {
pub decls: Vec<String>,
pub includes: Vec<String>,
}
impl GlobalModuleFragment {
pub fn new() -> Self {
Self {
decls: Vec::new(),
includes: Vec::new(),
}
}
pub fn add_decl(&mut self, decl: &str) {
self.decls.push(decl.to_string());
}
pub fn add_include(&mut self, header: &str) {
self.includes.push(header.to_string());
}
pub fn is_empty(&self) -> bool {
self.decls.is_empty() && self.includes.is_empty()
}
}
pub struct PrivateModuleFragment {
pub decls: Vec<String>,
pub is_closed: bool,
}
impl PrivateModuleFragment {
pub fn new() -> Self {
Self {
decls: Vec::new(),
is_closed: false,
}
}
pub fn add_decl(&mut self, decl: &str) {
self.decls.push(decl.to_string());
}
pub fn close(&mut self) {
self.is_closed = true;
}
pub fn is_empty(&self) -> bool {
self.decls.is_empty()
}
pub fn validate_placement(&self, has_more_decls: bool) -> Result<(), String> {
if self.is_closed && has_more_decls {
Err(
"private module fragment must be the last declaration in the translation unit"
.to_string(),
)
} else {
Ok(())
}
}
}
pub struct HeaderUnitSynthesizer {
header_units: HashMap<String, ModuleInfo>,
system_include_paths: Vec<String>,
}
impl HeaderUnitSynthesizer {
pub fn new(system_include_paths: Vec<String>) -> Self {
Self {
header_units: HashMap::new(),
system_include_paths,
}
}
pub fn synthesize_header_unit(&mut self, header: &str) -> ModuleInfo {
if let Some(info) = self.header_units.get(header) {
return info.clone();
}
let source_path = self.locate_header(header);
let module_name = ModuleName::from_str(header);
let bmi_name = format!("{}.pcm", header.replace('/', "_").replace('.', "_"));
let info = ModuleInfo {
name: module_name,
is_interface: true,
bmi_path: Some(bmi_name),
source_path,
dependencies: Vec::new(),
};
self.header_units.insert(header.to_string(), info.clone());
info
}
fn locate_header(&self, header: &str) -> Option<String> {
for path in &self.system_include_paths {
let full = format!("{}/{}", path, header);
if !full.contains("..") {
return Some(full);
}
}
None
}
pub fn generate_synthesized_bmi(&self, header: &str) -> String {
format!("; Synthesized BMI for header unit <{}>/n", header)
}
pub fn is_synthesized(&self, header: &str) -> bool {
self.header_units.contains_key(header)
}
}
#[derive(Debug, Clone)]
pub struct ModuleMapFile {
pub modules: Vec<ModuleMapEntry>,
pub base_dir: String,
}
#[derive(Debug, Clone)]
pub struct ModuleMapEntry {
pub module_name: String,
pub umbrella: Option<String>,
pub headers: Vec<String>,
pub excluded_headers: Vec<String>,
pub submodules: Vec<ModuleMapEntry>,
pub is_framework: bool,
pub is_explicit: bool,
pub exports: Vec<String>,
pub link_libraries: Vec<String>,
}
impl ModuleMapFile {
pub fn new(base_dir: &str) -> Self {
Self {
modules: Vec::new(),
base_dir: base_dir.to_string(),
}
}
pub fn parse(source: &str, base_dir: &str) -> Result<Self, String> {
let mut map = Self::new(base_dir);
let mut current: Option<ModuleMapEntryBuilder> = None;
let mut in_module = false;
for line in source.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with("//") || line.starts_with('#') {
continue;
}
if line.starts_with("module ") && !line.starts_with("module *") {
if let Some(builder) = current.take() {
map.modules.push(builder.build());
}
let name = line["module ".len()..]
.trim_end_matches('{')
.trim()
.to_string();
current = Some(ModuleMapEntryBuilder::new(&name));
in_module = true;
} else if in_module && line == "}" {
if let Some(builder) = current.take() {
map.modules.push(builder.build());
}
in_module = false;
} else if let Some(ref mut builder) = current {
if line.starts_with("umbrella ") {
let umbrella = line["umbrella ".len()..].trim_matches('"').to_string();
builder.umbrella = Some(umbrella);
} else if line.starts_with("header ") {
let header = line["header ".len()..].trim_matches('"').to_string();
builder.headers.push(header);
} else if line.starts_with("excluded_header ") {
let h = line["excluded_header ".len()..]
.trim_matches('"')
.to_string();
builder.excluded_headers.push(h);
} else if line.starts_with("export ") {
let export = line["export ".len()..].to_string();
builder.exports.push(export);
} else if line.starts_with("link ") {
let lib = line["link ".len()..].trim_matches('"').to_string();
builder.link_libraries.push(lib);
} else if line == "explicit" {
builder.is_explicit = true;
} else if line == "framework" {
builder.is_framework = true;
}
}
}
if let Some(builder) = current {
map.modules.push(builder.build());
}
Ok(map)
}
pub fn find_module(&self, name: &str) -> Option<&ModuleMapEntry> {
self.modules.iter().find(|m| m.module_name == name)
}
pub fn module_names(&self) -> Vec<&str> {
self.modules
.iter()
.map(|m| m.module_name.as_str())
.collect()
}
pub fn to_module_map(&self) -> ModuleMap {
let mut map = ModuleMap::new();
for entry in &self.modules {
let info = ModuleInfo::new(ModuleName::from_str(&entry.module_name), true);
map.register_module(info);
}
map
}
}
struct ModuleMapEntryBuilder {
module_name: String,
umbrella: Option<String>,
headers: Vec<String>,
excluded_headers: Vec<String>,
submodules: Vec<ModuleMapEntry>,
is_framework: bool,
is_explicit: bool,
exports: Vec<String>,
link_libraries: Vec<String>,
}
impl ModuleMapEntryBuilder {
fn new(name: &str) -> Self {
Self {
module_name: name.to_string(),
umbrella: None,
headers: Vec::new(),
excluded_headers: Vec::new(),
submodules: Vec::new(),
is_framework: false,
is_explicit: false,
exports: Vec::new(),
link_libraries: Vec::new(),
}
}
fn build(self) -> ModuleMapEntry {
ModuleMapEntry {
module_name: self.module_name,
umbrella: self.umbrella,
headers: self.headers,
excluded_headers: self.excluded_headers,
submodules: self.submodules,
is_framework: self.is_framework,
is_explicit: self.is_explicit,
exports: self.exports,
link_libraries: self.link_libraries,
}
}
}
#[derive(Debug, Clone)]
pub struct ModuleDependencyGraph {
edges: HashMap<String, HashSet<String>>,
reverse_edges: HashMap<String, HashSet<String>>,
}
impl ModuleDependencyGraph {
pub fn new() -> Self {
Self {
edges: HashMap::new(),
reverse_edges: HashMap::new(),
}
}
pub fn add_dependency(&mut self, module: &str, dependency: &str) {
self.edges
.entry(module.to_string())
.or_insert_with(HashSet::new)
.insert(dependency.to_string());
self.reverse_edges
.entry(dependency.to_string())
.or_insert_with(HashSet::new)
.insert(module.to_string());
}
pub fn topological_sort(&self) -> Result<Vec<String>, String> {
let mut in_degree: HashMap<String, usize> = HashMap::new();
let all_nodes: HashSet<String> = self
.edges
.keys()
.chain(self.edges.values().flatten())
.cloned()
.collect();
for node in &all_nodes {
in_degree.entry(node.clone()).or_insert(0);
}
for (_, deps) in &self.edges {
for dep in deps {
*in_degree.entry(dep.clone()).or_insert(0) += 1;
}
}
let mut queue: Vec<String> = in_degree
.iter()
.filter(|&(_, °)| deg == 0)
.map(|(n, _)| n.clone())
.collect();
let mut result = Vec::new();
while let Some(node) = queue.pop() {
result.push(node.clone());
if let Some(dependents) = self.reverse_edges.get(&node) {
for dependent in dependents {
if let Some(deg) = in_degree.get_mut(dependent) {
*deg -= 1;
if *deg == 0 {
queue.push(dependent.clone());
}
}
}
}
}
if result.len() < all_nodes.len() {
Err("cycle detected in module dependency graph".to_string())
} else {
result.reverse();
Ok(result)
}
}
pub fn transitive_deps(&self, module: &str) -> HashSet<String> {
let mut visited = HashSet::new();
let mut stack = vec![module.to_string()];
while let Some(current) = stack.pop() {
if visited.contains(¤t) {
continue;
}
visited.insert(current.clone());
if let Some(deps) = self.edges.get(¤t) {
for dep in deps {
if !visited.contains(dep) {
stack.push(dep.clone());
}
}
}
}
visited.remove(module);
visited
}
pub fn find_redundant_edges(&self) -> Vec<(String, String)> {
let mut redundant = Vec::new();
for (module, deps) in &self.edges {
let _transitive = self.transitive_deps(module);
for dep in deps {
let other_deps: HashSet<&String> = deps.iter().filter(|d| *d != dep).collect();
for other in &other_deps {
let other_transitive = self.transitive_deps(other);
if other_transitive.contains(dep) {
redundant.push((module.clone(), dep.clone()));
break;
}
}
}
}
redundant
}
}
impl Default for ModuleDependencyGraph {
fn default() -> Self {
Self::new()
}
}
pub fn build_dependency_graph(map: &ModuleMap) -> ModuleDependencyGraph {
let mut graph = ModuleDependencyGraph::new();
for (module_name, _) in &map.modules {
let imports = map.get_imports(module_name);
for imported in imports {
graph.add_dependency(module_name, imported);
}
}
graph
}
pub const BMI_MAGIC: &[u8; 4] = b"CPMI";
pub const BMI_VERSION: u32 = 1;
#[derive(Debug, Clone)]
pub struct BmiFile {
pub module_name: ModuleName,
pub is_interface: bool,
pub dependencies: Vec<ModuleName>,
pub ast_data: Vec<u8>,
pub lookup_table: Vec<u8>,
pub source_map: Vec<u8>,
pub module_version: Option<String>,
}
impl BmiFile {
pub fn new(module_name: ModuleName, is_interface: bool) -> Self {
Self {
module_name,
is_interface,
dependencies: Vec::new(),
ast_data: Vec::new(),
lookup_table: Vec::new(),
source_map: Vec::new(),
module_version: None,
}
}
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(BMI_MAGIC);
buf.extend_from_slice(&BMI_VERSION.to_le_bytes());
let name_str = self.module_name.to_string();
buf.extend_from_slice(&(name_str.len() as u32).to_le_bytes());
buf.extend_from_slice(name_str.as_bytes());
let mut flags: u32 = 0;
if self.is_interface {
flags |= 0x01;
}
buf.extend_from_slice(&flags.to_le_bytes());
buf.extend_from_slice(&(self.dependencies.len() as u32).to_le_bytes());
for dep in &self.dependencies {
let dep_str = dep.to_string();
buf.extend_from_slice(&(dep_str.len() as u32).to_le_bytes());
buf.extend_from_slice(dep_str.as_bytes());
}
buf.extend_from_slice(&(self.ast_data.len() as u64).to_le_bytes());
buf.extend_from_slice(&self.ast_data);
buf.extend_from_slice(&(self.lookup_table.len() as u64).to_le_bytes());
buf.extend_from_slice(&self.lookup_table);
buf.extend_from_slice(&(self.source_map.len() as u64).to_le_bytes());
buf.extend_from_slice(&self.source_map);
if let Some(ref ver) = self.module_version {
buf.push(1);
buf.extend_from_slice(&(ver.len() as u32).to_le_bytes());
buf.extend_from_slice(ver.as_bytes());
} else {
buf.push(0);
}
buf
}
pub fn deserialize(data: &[u8]) -> Result<Self, String> {
if data.len() < 12 {
return Err("BMI data too short".to_string());
}
let mut pos = 0usize;
if &data[pos..pos + 4] != BMI_MAGIC {
return Err("invalid BMI magic bytes".to_string());
}
pos += 4;
let version = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
pos += 4;
if version != BMI_VERSION {
return Err(format!(
"unsupported BMI version {} (expected {})",
version, BMI_VERSION
));
}
let name_len =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
pos += 4;
if pos + name_len > data.len() {
return Err("truncated module name".to_string());
}
let name_str = std::str::from_utf8(&data[pos..pos + name_len])
.map_err(|e| format!("invalid UTF-8 in module name: {}", e))?;
pos += name_len;
let module_name = ModuleName::from_str(name_str);
let flags = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
pos += 4;
let is_interface = (flags & 0x01) != 0;
let dep_count =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
pos += 4;
let mut dependencies = Vec::new();
for _ in 0..dep_count {
let dep_len =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]])
as usize;
pos += 4;
if pos + dep_len > data.len() {
return Err("truncated dependency name".to_string());
}
let dep_str = std::str::from_utf8(&data[pos..pos + dep_len])
.map_err(|e| format!("invalid UTF-8 in dependency: {}", e))?;
pos += dep_len;
dependencies.push(ModuleName::from_str(dep_str));
}
if pos + 8 > data.len() {
return Err("truncated AST data size".to_string());
}
let ast_len = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]) as usize;
pos += 8;
if pos + ast_len > data.len() {
return Err("truncated AST data".to_string());
}
let ast_data = data[pos..pos + ast_len].to_vec();
pos += ast_len;
if pos + 8 > data.len() {
return Err("truncated lookup table size".to_string());
}
let lut_len = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]) as usize;
pos += 8;
if pos + lut_len > data.len() {
return Err("truncated lookup table".to_string());
}
let lookup_table = data[pos..pos + lut_len].to_vec();
pos += lut_len;
if pos + 8 > data.len() {
return Err("truncated source map size".to_string());
}
let sm_len = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]) as usize;
pos += 8;
if pos + sm_len > data.len() {
return Err("truncated source map".to_string());
}
let source_map = data[pos..pos + sm_len].to_vec();
pos += sm_len;
let module_version = if pos < data.len() && data[pos] == 1 {
pos += 1;
if pos + 3 >= data.len() {
return Err("truncated version string".to_string());
}
let ver_len =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]])
as usize;
pos += 4;
if pos + ver_len > data.len() {
return Err("truncated version string data".to_string());
}
let ver = std::str::from_utf8(&data[pos..pos + ver_len])
.map_err(|e| format!("invalid UTF-8 in version: {}", e))?
.to_string();
Some(ver)
} else {
None
};
Ok(BmiFile {
module_name,
is_interface,
dependencies,
ast_data,
lookup_table,
source_map,
module_version,
})
}
pub fn estimated_size(&self) -> usize {
let mut size = 4 + 4 + 4 + self.module_name.to_string().len() + 4;
for dep in &self.dependencies {
size += 4 + dep.to_string().len();
}
size += 8 + self.ast_data.len();
size += 8 + self.lookup_table.len();
size += 8 + self.source_map.len();
if let Some(ref ver) = self.module_version {
size += 1 + 4 + ver.len();
} else {
size += 1;
}
size
}
}
pub struct ModuleNameResolver {
modules: HashMap<String, Vec<String>>,
}
impl ModuleNameResolver {
pub fn new() -> Self {
Self {
modules: HashMap::new(),
}
}
pub fn register_partition(&mut self, module: &str, partition: &str) {
self.modules
.entry(module.to_string())
.or_insert_with(Vec::new)
.push(partition.to_string());
}
pub fn resolve(&self, name: &str) -> Option<(String, Option<String>)> {
if let Some(colon_pos) = name.find(':') {
let module = &name[..colon_pos];
let partition = &name[colon_pos + 1..];
if let Some(partitions) = self.modules.get(module) {
if partitions.iter().any(|p| p == partition) {
return Some((module.to_string(), Some(partition.to_string())));
}
}
None
} else if self.modules.contains_key(name) {
Some((name.to_string(), None))
} else {
None
}
}
pub fn is_valid_partition(&self, module: &str, partition: &str) -> bool {
self.modules
.get(module)
.map(|parts| parts.iter().any(|p| p == partition))
.unwrap_or(false)
}
pub fn partitions_of(&self, module: &str) -> Vec<&str> {
self.modules
.get(module)
.map(|parts| parts.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
}
impl Default for ModuleNameResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ModuleReachability {
pub module_name: String,
pub reachable_modules: HashSet<String>,
pub reachable_decls: HashSet<usize>,
}
impl ModuleReachability {
pub fn new(module_name: &str) -> Self {
Self {
module_name: module_name.to_string(),
reachable_modules: HashSet::new(),
reachable_decls: HashSet::new(),
}
}
pub fn mark_module_reachable(&mut self, module: &str) {
self.reachable_modules.insert(module.to_string());
}
pub fn is_module_reachable(&self, module: &str) -> bool {
self.reachable_modules.contains(module)
}
pub fn mark_decl_reachable(&mut self, decl_id: usize) {
self.reachable_decls.insert(decl_id);
}
pub fn compute_transitive_reachability(
&mut self,
module_map: &ModuleMap,
initial_imports: &[&str],
) {
let mut stack: Vec<String> = initial_imports.iter().map(|s| s.to_string()).collect();
while let Some(current) = stack.pop() {
if !self.reachable_modules.insert(current.clone()) {
continue; }
let imports = module_map.get_imports(¤t);
for imported in imports {
if !self.reachable_modules.contains(imported) {
stack.push(imported.to_string());
}
}
}
}
pub fn reachable_module_names(&self) -> Vec<&str> {
self.reachable_modules.iter().map(|s| s.as_str()).collect()
}
}
#[derive(Debug, Clone)]
pub struct ModuleInterfaceEmitter {
pub module_name: String,
pub is_interface: bool,
pub exported_decls: Vec<String>,
}
impl ModuleInterfaceEmitter {
pub fn new(module_name: &str, is_interface: bool) -> Self {
Self {
module_name: module_name.to_string(),
is_interface,
exported_decls: Vec::new(),
}
}
pub fn emit_interface_ir(&self) -> String {
let mut ir = String::new();
ir.push_str(&format!("; Module interface: {}\n", self.module_name));
ir.push_str(&format!(
"!llvm.module.flags = !{{!0}}\n!0 = !{{i32 1, !\"C++20 Modules\", i32 1}}\n"
));
ir.push_str(&format!("!module.name = !{{!\"{}\"}}\n", self.module_name));
if self.is_interface {
ir.push_str(&format!("!module.interface = !{{!{{i32 1}}}}\n"));
}
for decl in &self.exported_decls {
ir.push_str(&format!("!module.export = !{{!\"{}\"}}\n", decl));
}
ir
}
pub fn add_export(&mut self, decl_name: &str) {
self.exported_decls.push(decl_name.to_string());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_module_name_display() {
let name = ModuleName::from_str("std.core");
assert_eq!(format!("{}", name), "std.core");
}
#[test]
fn test_module_name_components() {
let name = ModuleName::new(vec!["std".into(), "core".into()]);
assert_eq!(name.components.len(), 2);
assert_eq!(name.components[0], "std");
assert_eq!(name.components[1], "core");
}
#[test]
fn test_module_decl_display() {
let decl = ModuleDecl::interface(ModuleName::from_str("mylib"));
assert_eq!(format!("{}", decl), "export module mylib");
let gmf = ModuleDecl::global_fragment();
assert_eq!(format!("{}", gmf), "module;");
let pmf = ModuleDecl::private_fragment();
assert_eq!(format!("{}", pmf), "module :private;");
}
#[test]
fn test_module_kind_is_exported() {
assert!(ModuleKind::ModuleInterface.is_exported());
assert!(!ModuleKind::ModuleImplementation.is_exported());
assert!(ModuleKind::ModulePartitionInterface.is_exported());
assert!(!ModuleKind::GlobalModuleFragment.is_exported());
}
#[test]
fn test_module_import_display() {
let import = ModuleImport::new(ModuleName::from_str("std.core"));
assert_eq!(format!("{}", import), "import std.core");
let exported = ModuleImport::exported(ModuleName::from_str("mylib"));
assert!(format!("{}", exported).starts_with("export import"));
let header = ModuleImport::header_unit("vector");
assert_eq!(format!("{}", header), "import <vector>");
}
#[test]
fn test_module_map_register() {
let mut map = ModuleMap::new();
let info = ModuleInfo::new(ModuleName::from_str("mylib"), true);
map.register_module(info);
assert!(map.has_module("mylib"));
assert!(map.get_module("mylib").unwrap().is_interface);
}
#[test]
fn test_module_map_imports() {
let mut map = ModuleMap::new();
map.register_module(ModuleInfo::new(ModuleName::from_str("A"), true));
map.register_module(ModuleInfo::new(ModuleName::from_str("B"), true));
map.add_import("A", "B");
let imports = map.get_imports("A");
assert_eq!(imports, vec!["B"]);
}
#[test]
fn test_module_map_circular_check() {
let mut map = ModuleMap::new();
map.register_module(ModuleInfo::new(ModuleName::from_str("X"), true));
map.register_module(ModuleInfo::new(ModuleName::from_str("Y"), true));
map.add_import("X", "Y");
map.add_import("Y", "X");
let cycles = map.check_circular_imports();
assert!(!cycles.is_empty());
}
#[test]
fn test_module_map_no_circular() {
let mut map = ModuleMap::new();
map.register_module(ModuleInfo::new(ModuleName::from_str("X"), true));
map.register_module(ModuleInfo::new(ModuleName::from_str("Y"), true));
map.register_module(ModuleInfo::new(ModuleName::from_str("Z"), true));
map.add_import("X", "Y");
map.add_import("Y", "Z");
let cycles = map.check_circular_imports();
assert!(cycles.is_empty());
}
#[test]
fn test_module_validator() {
let mut validator = ModuleValidator::new();
let decl = ModuleDecl::interface(ModuleName::from_str("test"));
assert!(validator.validate_module_decl(&decl));
}
#[test]
fn test_module_validator_empty_name() {
let mut validator = ModuleValidator::new();
let decl = ModuleDecl::interface(ModuleName::new(vec![]));
assert!(!validator.validate_module_decl(&decl));
assert!(!validator.errors().is_empty());
}
#[test]
fn test_module_validator_missing_module() {
let mut validator = ModuleValidator::new();
let import = ModuleImport::new(ModuleName::from_str("nonexistent"));
let map = ModuleMap::new();
assert!(!validator.validate_import(&import, &map));
}
}