use crate::core::{
clean::ast as ca,
compile::builtin::*,
compile::check::*,
preprocess::{self as pre, Module},
util::*,
};
use crate::match1;
use std::collections::{BTreeMap, VecDeque};
enum Error {
ImportNotFound(Vec<String>),
CircularImport(Vec<String>),
SymbolNotFound(String),
AutomaticImportsNotExported(Vec<String>),
NotDefType(Vec<String>),
NoSuchSymbol(Vec<String>),
}
impl Error {
fn core(self) -> CoreError {
match self {
Self::ImportNotFound(ext) => {
CoreError::make_raw(format!("module \"{}\" not found", path_to_string(&ext)), "")
}
Self::CircularImport(path) => CoreError::make_raw(
"circular import",
format!(
"Path: \"{}\".",
path_to_string(&path.into_iter().skip(1).collect::<Vec<_>>())
),
),
Self::SymbolNotFound(symbol) => {
CoreError::make_raw(format!("symbol \"{}\" not found", symbol), "")
}
Self::AutomaticImportsNotExported(path) => {
CoreError::make_raw(
"automatic import are not exported",
format!(
concat!(
"Help: this is an automatic import, which does not get exported from other modules.\n",
"You still have access to this object, just use its name: {}"
),
path.last().unwrap()
)
)
}
Self::NotDefType(path) => {
CoreError::make_raw(format!("\"{}\" is not a class", path_to_string(&path)), "")
}
Self::NoSuchSymbol(rel) => {
CoreError::make_raw(format!("symbol \"{}\" not found", path_to_string(&rel)), "")
}
}
}
}
pub fn path_to_string(path: &Vec<String>) -> String {
if path.len() == 0 {
return "".to_string();
}
let mut path_string = path[0].clone();
for part in path.iter().skip(1) {
path_string.push('.');
path_string.push_str(part.as_str());
}
return path_string;
}
#[derive(Clone, Debug)]
pub struct NamespaceOutput {
pub preprocessed: pre::ModuleRegistry,
pub tree: Tree<Namespace>,
}
pub type Namespace = BTreeMap<String, NamespacedObject>;
impl Tree<Namespace> {
pub fn advance_path(
&self,
rel: &Vec<String>,
abs: &Vec<String>,
) -> Option<(Vec<String>, Vec<String>)> {
self._advance_path(rel.clone().into(), abs)
}
fn _advance_path(
&self,
mut rel: VecDeque<String>,
abs: &Vec<String>,
) -> Option<(Vec<String>, Vec<String>)> {
let next = match rel.pop_front() {
Some(next) => next,
None => {
return Some((abs.clone(), vec![]));
}
};
match self.get(abs) {
Some(Tree::Leaf(namespace)) => match namespace.get(&next) {
Some(NamespacedObject::Automatic(..) | NamespacedObject::Item(..)) => {
let mut abs = abs.clone();
abs.push(next);
Some((abs, rel.into()))
}
Some(NamespacedObject::Import(Located(_, import))) => match import.import_type {
ImportType::Symbol => {
let mut abs = import.path.clone();
rel.push_front(abs.pop().unwrap());
self._advance_path(rel, &abs)
}
ImportType::Module | ImportType::Package => match self.get(&import.path) {
Some(..) => self._advance_path(rel, &import.path),
None => None,
},
},
None => None,
},
Some(Tree::Node(package)) => match package.get(&next) {
Some(..) => {
let mut abs = abs.clone();
abs.push(next);
self._advance_path(rel, &abs)
}
None => None,
},
None => None,
}
}
}
#[derive(Clone, Debug)]
pub enum NamespacedObject {
Automatic(Builtin),
Import(Import),
Item(Item),
}
pub type Import = Located<ImportObj>;
#[derive(Clone, Debug)]
pub struct ImportObj {
pub path: Vec<String>,
pub import_type: ImportType,
pub is_builtin: bool,
}
#[derive(Clone, Debug)]
pub enum ImportType {
Symbol,
Module,
Package,
}
#[derive(Clone, Debug)]
pub enum Item {
Defined(ca::TopLevelStatement),
Builtin(Builtin),
}
enum Wip {
Empty,
Pending,
Done(Namespace),
}
impl TryFrom<pre::ModuleRegistry> for NamespaceOutput {
type Error = CoreError;
fn try_from(registry: pre::ModuleRegistry) -> CResult<NamespaceOutput> {
let mut wip = registry.tree.clone().map(|module| match module {
Module::Python(..) => Wip::Empty,
Module::SeahorsePrelude => Wip::Done(prelude::namespace()),
Module::SeahorsePyth => Wip::Done(pyth::namespace()),
});
build_namespace(&mut wip, ®istry, ®istry.origin)?;
let tree = wip.map(|namespace| match namespace {
Wip::Done(namespace) => namespace,
_ => panic!(),
});
let registry = NamespaceOutput {
preprocessed: registry,
tree,
};
return Ok(registry);
}
}
impl Tree<Namespace> {
pub fn build_ty(&self, ty_expr: &ca::TyExpression, abs: &Vec<String>) -> CResult<Ty> {
let Located(loc, obj) = ty_expr;
match obj {
ca::TyExpressionObj::Generic { base, params } => {
let params = params
.iter()
.map(|param| self.build_ty(param, abs))
.collect::<Result<Vec<_>, _>>()?;
let base = match self.advance_path(base, abs) {
Some((path, rem)) => {
if rem.len() == 0 {
match self.get_leaf_ext(&path).unwrap() {
NamespacedObject::Automatic(builtin) => {
if !path.starts_with(&abs) {
Err(Error::AutomaticImportsNotExported(path)
.core()
.located(loc.clone()))
} else {
Ok(TyName::Builtin(builtin.clone()))
}
}
NamespacedObject::Item(Item::Defined(def)) => {
let Located(_, obj) = def;
match obj {
ca::TopLevelStatementObj::ClassDef { .. } => {
Ok(TyName::Defined(path, DefinedType::Struct))
}
_ => {
Err(Error::NotDefType(path).core().located(loc.clone()))
}
}
}
NamespacedObject::Item(Item::Builtin(builtin)) => {
builtin
.as_instance(¶ms)
.map_err(|err| err.located(loc.clone()))?;
Ok(TyName::Builtin(builtin.clone()))
}
_ => panic!(),
}
} else {
Err(Error::NoSuchSymbol(base.clone())
.core()
.located(loc.clone()))
}
}
_ => Err(Error::NoSuchSymbol(base.clone())
.core()
.located(loc.clone())),
}?;
Ok(Ty::Generic(base, params))
}
ca::TyExpressionObj::Const(n) => Ok(Ty::Const(*n)),
}
.map_err(|err: Error| err.core().located(loc.clone()))
}
}
fn build_namespace(
wip: &mut Tree<Wip>,
registry: &pre::ModuleRegistry,
path: &Vec<String>,
) -> CResult<()> {
let node = wip.get(path).unwrap();
match &node {
&Tree::Leaf(Wip::Pending) => return Err(Error::CircularImport(path.clone()).core()),
&Tree::Leaf(Wip::Done(..)) => return Ok(()),
&Tree::Leaf(Wip::Empty) => {
*wip.get_mut(path).unwrap() = Tree::Leaf(Wip::Pending);
let module = match1!(registry.tree.get(path).unwrap(), Tree::Leaf(module) => module);
let namespace = match module {
pre::Module::Python(module) => build_python_namespace(wip, registry, path, module)?,
_ => panic!(),
};
*wip.get_mut(path).unwrap() = Tree::Leaf(Wip::Done(namespace));
}
&Tree::Node(package) => {
let keys = package.keys().map(|key| key.clone()).collect::<Vec<_>>();
for key in keys.iter() {
let mut path_ = path.clone();
path_.push(key.clone());
build_namespace(wip, registry, &path_)?;
}
return Ok(());
}
}
Ok(())
}
fn build_python_namespace(
wip: &mut Tree<Wip>,
registry: &pre::ModuleRegistry,
path: &Vec<String>,
module: &ca::Module,
) -> CResult<Namespace> {
let mut namespace = BTreeMap::new();
namespace.append(&mut prelude::namespace());
namespace.append(&mut python::namespace());
for statement in module.statements.iter() {
let Located(loc, obj) = statement;
match obj {
ca::TopLevelStatementObj::Import { symbols } => {
for ca::ImportSymbol { symbol, alias } in symbols.iter() {
let ext = vec![symbol.clone()];
let abs = registry.get_abs_path(path, &ext).ok_or(
Error::ImportNotFound(ext.clone())
.core()
.located(loc.clone()),
)?;
build_namespace(wip, registry, &abs)?;
let obj =
get_import_obj(wip, &abs, None).map_err(|err| err.located(loc.clone()))?;
let name = alias.clone().unwrap_or(symbol.clone());
namespace.insert(name, NamespacedObject::Import(Located(loc.clone(), obj)));
}
}
ca::TopLevelStatementObj::ImportFrom {
path: ext,
symbols,
..
} => {
let abs = registry.get_abs_path(path, ext).ok_or(
Error::ImportNotFound(ext.clone())
.core()
.located(loc.clone()),
)?;
build_namespace(wip, registry, &abs)?;
for ca::ImportSymbol { symbol, alias } in symbols.iter() {
if symbol.as_str() == "*" {
let glob = match wip.get(&abs).unwrap() {
Tree::Leaf(Wip::Done(namespace)) => {
namespace
.iter()
.filter_map(|(name, object)| {
if let NamespacedObject::Automatic(..) = object {
None
} else {
Some(name)
}
})
.collect::<Vec<_>>()
}
Tree::Node(package) => package.keys().collect::<Vec<_>>(),
_ => panic!(),
};
for symbol in glob.into_iter() {
let obj = get_import_obj(wip, &abs, Some(symbol))
.map_err(|err| err.located(loc.clone()))?;
namespace.insert(
symbol.clone(),
NamespacedObject::Import(Located(loc.clone(), obj)),
);
}
} else {
let obj = get_import_obj(wip, &abs, Some(symbol))
.map_err(|err| err.located(loc.clone()))?;
let name = alias.clone().unwrap_or(symbol.clone());
namespace.insert(name, NamespacedObject::Import(Located(loc.clone(), obj)));
}
}
}
ca::TopLevelStatementObj::Constant { name, .. }
| ca::TopLevelStatementObj::ClassDef { name, .. }
| ca::TopLevelStatementObj::FunctionDef(ca::FunctionDef { name, .. }) => {
let export =
NamespacedObject::Item(Item::Defined(Located(loc.clone(), obj.clone())));
namespace.insert(name.clone(), export);
}
ca::TopLevelStatementObj::Expression(..) => {}
}
}
return Ok(namespace);
}
fn get_import_obj(
wip: &Tree<Wip>,
path: &Vec<String>,
symbol: Option<&String>,
) -> CResult<ImportObj> {
match wip.get(path).unwrap() {
Tree::Leaf(Wip::Done(namespace)) => match symbol {
Some(symbol) => {
if let Some(object) = namespace.get(symbol) {
let is_builtin = match object {
NamespacedObject::Item(Item::Defined(..)) => false,
NamespacedObject::Import(Located(
_,
ImportObj {
is_builtin: false, ..
},
)) => false,
_ => true,
};
let mut path = path.clone();
path.push(symbol.clone());
Ok(ImportObj {
path,
import_type: ImportType::Symbol,
is_builtin,
})
} else {
Err(Error::SymbolNotFound(symbol.clone()).core())
}
}
None => Ok(ImportObj {
path: path.clone(),
import_type: ImportType::Module,
is_builtin: path.starts_with(&["sh".to_string()]),
}),
},
Tree::Node(package) => match symbol {
Some(symbol) => match package.get(symbol) {
Some(Tree::Leaf(..)) => {
let mut path = path.clone();
path.push(symbol.clone());
let is_builtin = path.starts_with(&["sh".to_string()]);
Ok(ImportObj {
path: path,
import_type: ImportType::Module,
is_builtin,
})
}
Some(Tree::Node(..)) => {
let mut path = path.clone();
path.push(symbol.clone());
let is_builtin = path.starts_with(&["sh".to_string()]);
Ok(ImportObj {
path: path,
import_type: ImportType::Package,
is_builtin,
})
}
None => Err(Error::SymbolNotFound(symbol.clone()).core()),
},
None => Ok(ImportObj {
path: path.clone(),
import_type: ImportType::Package,
is_builtin: path.starts_with(&["sh".to_string()]),
}),
},
_ => panic!(),
}
}
pub fn namespace(registry: pre::ModuleRegistry) -> CResult<NamespaceOutput> {
registry.try_into()
}