use std::{ collections::HashSet,
mem::{ replace, take }, path::{ Path, PathBuf } };
use crate::language::{ ast::{ AstImportStatement, AstStatement, AstTopLevel, AstType },
data::{ methods::method_key, types::TypeId },
interpreter::{ Interpreter, InterpreterResult,
modules::{ ModuleSource, PendingModule, module_error } },
text::location::Location,
typecheck::check_protocol };
fn copy_import(import: &AstImportStatement) -> AstImportStatement
{
AstImportStatement
{
public: import.public, location: import.location.clone(), module: import.module.clone(),
names: import.names.clone(), condition: None, enabled: import.enabled,
}
}
fn annotation_names(annotation: &AstType, names: &mut HashSet<String>)
{
match annotation
{
AstType::Named(name) => { names.insert(name.clone()); },
AstType::Array(item) => annotation_names(item, names),
AstType::Map(key, value) =>
{ annotation_names(key, names); annotation_names(value, names); },
AstType::FixedArray(items) | AstType::Union(items) =>
{ for item in items { annotation_names(item, names); } },
AstType::Function(arguments, result) =>
{
for argument in arguments { annotation_names(argument, names); }
annotation_names(result, names);
},
AstType::ReceiverMember(_) => {},
}
}
impl ModuleSource
{
fn parent(&self, location: &Location) -> InterpreterResult<Option<Self>>
{
match self
{
Self::File { path, library } =>
{
let mut directory = path.parent().unwrap();
if path.file_name().is_some_and(|name| name == "init.shy")
{
let Some(parent) = directory.parent() else { return Ok(None); };
directory = parent;
}
let mut files = Vec::new();
if directory.file_name().is_some()
{
let mut adjacent = directory.as_os_str().to_os_string();
adjacent.push(".shy");
files.push(PathBuf::from(adjacent));
}
files.push(directory.join("init.shy"));
Ok(Self::files(&files, library, location)?
.filter(|parent| parent.origin() != self.origin()))
},
Self::Bundled { name, .. } =>
{
let mut directory = Path::new(name).parent().unwrap_or(Path::new(""));
if Path::new(name).file_name().is_some_and(|name| name == "init")
{ directory = directory.parent().unwrap_or(Path::new("")); }
if directory.as_os_str().is_empty() { return Ok(None); }
Self::bundled(&directory.to_string_lossy(), Path::new(""), location)
},
}
}
pub(super) fn contains_child(&self, source: &Self) -> bool
{
if self.origin() == source.origin() { return false; }
match (self, source)
{
(Self::File { path, .. }, Self::File { path: child, .. }) =>
{
let directory = if path.file_name().is_some_and(|name| name == "init.shy")
{ path.parent().unwrap().to_path_buf() }
else { path.with_extension("") };
child.starts_with(directory)
},
(Self::Bundled { name, .. }, Self::Bundled { name: child, .. }) =>
{
let directory = name.strip_suffix("/init").unwrap_or(name);
child.starts_with(&format!("{}/", directory))
},
_ => false,
}
}
}
impl Interpreter
{
pub(super) fn discard_parent_scopes(&mut self, existing: &HashSet<String>)
{
loop
{
let discarded: Vec<_> = self.scopes.iter().filter(|(key, scope)|
!existing.contains(*key) && (self.pending_modules.contains_key(*key)
|| self.module_sources.get(*key).is_some_and(|source|
self.loading_modules.contains(&source.origin()))
|| scope.modules.values().any(|module| !self.scopes.contains_key(module))))
.map(|(key, _)| key.clone()).collect();
if discarded.is_empty() { break; }
for key in discarded { self.discard_module_scope(&key); }
}
}
pub(super) fn discard_module_scope(&mut self, key: &str)
{
if let Some(source) = self.module_sources.remove(key)
{ self.loading_modules.remove(&source.origin()); }
self.scopes.remove(key);
self.pending_modules.remove(key);
self.type_scopes.retain(|_, home| home != key);
}
pub(super) fn stage_parent_types(&mut self, statements: &mut AstTopLevel,
imports: &[AstImportStatement])
{
if !self.module_sources.contains_key(&self.current_scope) { return; }
let mut types = Vec::new();
let mut body = Vec::new();
let mut methods = Vec::new();
for statement in take(statements)
{
if matches!(statement, AstStatement::StructDeclaration(_)
| AstStatement::EnumDeclaration(_) | AstStatement::TypeDeclaration(_))
{ types.push(statement); continue; }
if let AstStatement::FunctionDefinition(function) = &statement
{
if let Some(receiver) = &function.receiver
{ methods.push((receiver.clone(), function.name.clone(), function.public)); }
else
{
self.scope_mut().announce_function(function.name.clone());
if function.public { self.scope_mut().exports.insert(function.name.clone()); }
else { self.scope_mut().exports.remove(&function.name); }
}
}
body.push(statement);
}
self.pending_modules.insert(self.current_scope.clone(), PendingModule
{
types: Some(types), imports: imports.iter().map(copy_import).collect(),
body: Some(body), methods, preparing: false, deferred: false,
});
}
pub(super) fn prepare_parent_types(&mut self, key: &str) -> InterpreterResult<()>
{
let Some(pending) = self.pending_modules.get_mut(key) else { return Ok(()); };
if pending.preparing
{
let origin = self.module_sources[key].origin();
return Err(module_error(&Location::new(origin.as_str(), 1, 1),
"Circular parent type dependency"));
}
let Some(mut types) = pending.types.take() else { return Ok(()); };
pending.preparing = true;
let imports: Vec<_> = pending.imports.iter().map(copy_import).collect();
let mut body = pending.body.take().unwrap_or_default();
let methods = pending.methods.clone();
let source = self.module_sources[key].clone();
let caller = replace(&mut self.current_scope, key.to_string());
let result = (||
{
self.prepare_visibility(&body)?;
let mut needed = HashSet::new();
let mut declared = HashSet::new();
for statement in &types
{
if let Some((name, _, _)) = statement.module_declaration()
{ declared.insert(name.to_string()); }
match statement
{
AstStatement::StructDeclaration(item) =>
{
for field in &item.fields
{ annotation_names(&field.annotation, &mut needed); }
},
AstStatement::TypeDeclaration(item) =>
annotation_names(&item.annotation, &mut needed),
_ => {},
}
}
for statement in &body
{
if let AstStatement::FunctionDefinition(function) = statement
&& function.receiver.is_some()
&& (function.name == "new" || function.name.starts_with("get_")
|| function.name.starts_with("set_"))
{
for parameter in &function.parameters
{
if let Some(annotation) = ¶meter.annotation
{ annotation_names(annotation, &mut needed); }
}
if let Some(annotation) = &function.return_annotation
{ annotation_names(annotation, &mut needed); }
}
}
needed.retain(|name| !declared.contains(name)
&& !self.scope().types.names.contains_key(name));
for import in &imports
{
if !import.enabled { continue; }
let required = needed.iter().any(|name|
if import.names.is_empty()
{ name.starts_with(&format!("{}::", import.module)) }
else { import.names.contains(name) });
if !required { continue; }
let dependency = self.load_import(import, &source.directory())?;
self.install_import(import, &dependency)?;
}
self.refresh_module_names()?;
let first = self.scope().types.definition_count();
self.scope_mut().compile(&mut types)?;
for index in first..self.scope().types.definition_count()
{ self.type_scopes.insert(TypeId(index), key.to_string()); }
for statement in &types
{
let Some((name, _, public)) = statement.module_declaration() else { continue; };
if public { self.scope_mut().exports.insert(name.to_string()); }
else { self.scope_mut().exports.remove(name); }
if let AstStatement::TypeDeclaration(declaration) = statement
&& let Some(factory) = &declaration.conversion
{
let name = method_key(factory.receiver_type.unwrap(), &factory.name);
if public { self.scope_mut().exports.insert(name); }
}
}
let names = self.scope().types.names.clone();
for statement in &mut body
{
if let AstStatement::FunctionDefinition(function) = statement
&& let Some(receiver) = &function.receiver
&& let Some(id) = names.get(receiver)
{
function.receiver_type = Some(*id);
check_protocol(&mut self.scope_mut().types, function, &names)
.map_err(|error| module_error(&function.location, error.to_string()))?;
}
}
for (receiver, name, public) in methods
{
let Some(id) = self.scope().types.names.get(&receiver).copied()
else { continue; };
self.scope_mut().types.declare_extension(id, &name);
let key = method_key(id, &name);
self.scope_mut().mask_implied_factory(&key);
if public { self.scope_mut().exports.insert(key); }
else { self.scope_mut().exports.remove(&key); }
}
Ok(())
})();
self.current_scope = caller;
if let Some(pending) = self.pending_modules.get_mut(key)
{
pending.preparing = false;
pending.body = Some(body);
}
result
}
pub(super) fn load_parent_import(&mut self, import: &AstImportStatement)
-> InterpreterResult<String>
{
let segments: Vec<_> = import.module.split("::").collect();
let ancestors = segments.iter().take_while(|name| **name == "super").count();
let mut source = self.module_sources.get(&self.current_scope).cloned()
.ok_or_else(|| module_error(&import.location, "This input has no containing module"))?;
let mut parent = String::new();
for _ in 0..ancestors
{
source = source.parent(&import.location)?.ok_or_else(||
module_error(&import.location, "The containing module has no parent module"))?;
let origin = source.origin();
parent = self.pending_modules.keys().find(|key|
self.module_sources[*key].origin() == origin).cloned().unwrap_or(origin.clone());
if !self.scopes.contains_key(&parent)
{
let (mut body, imports) =
self.open_module_scope(&source, &import.location, &parent)?;
self.loading_modules.insert(origin);
let caller = replace(&mut self.current_scope, parent.clone());
let result = self.install_prelude(&import.location);
if result.is_ok()
{
self.stage_parent_types(&mut body, &imports);
self.pending_modules.get_mut(&parent).unwrap().deferred = true;
}
self.current_scope = caller;
result?;
}
self.prepare_parent_types(&parent)?;
}
if ancestors < segments.len()
{
let mut nested = copy_import(import);
nested.module = segments[ancestors..].join("::");
return self.load_import(&nested, &source.directory());
}
Ok(parent)
}
pub(super) fn finish_parent_modules(&mut self) -> InterpreterResult<()>
{
let ready = self.pending_modules.iter().find(|(parent, pending)| pending.deferred
&& !self.pending_modules.iter().any(|(key, pending)| !pending.deferred
&& self.module_sources[*parent].contains_child(&self.module_sources[key])));
let Some((key, _)) = ready else { return Ok(()); };
let key = key.clone();
let mut pending = self.pending_modules.remove(&key).unwrap();
let source = self.module_sources[&key].clone();
let mut body = pending.body.take().unwrap_or_default();
let caller = replace(&mut self.current_scope, key.clone());
let previous = self.last_result.take();
let result = self.compile_module(&mut body, pending.imports, &source.directory())
.and_then(|code|
{
self.refresh_parent_children(&key)?;
self.execute_instructions(&code)
});
self.last_result = previous;
self.current_scope = caller;
self.loading_modules.remove(&source.origin());
if let Err(error) = result
{
self.discard_module_scope(&key);
return Err(error);
}
self.finish_parent_modules()
}
pub(super) fn refresh_parent_children(&mut self, parent: &str) -> InterpreterResult<()>
{
let children: Vec<_> = self.scopes.iter().filter(|(key, scope)| *key != parent
&& scope.parent_namespaces.iter().any(|name|
scope.modules.get(name).is_some_and(|key| key == parent)))
.map(|(key, _)| key.clone()).collect();
let caller = self.current_scope.clone();
let result = (||
{
for child in children
{
self.current_scope = child;
self.refresh_module_names()?;
}
Ok(())
})();
self.current_scope = caller;
result
}
}