cl-typeck: Module imports v0.1
- NameCollects use items - Preprocesses them - Uses no fancy algorithms - Doesn't respect item visibility at all - *declaration-order-dependent* :( - works, though! :) - TODO: Lazy evaluation of literally any of this stuff.
This commit is contained in:
parent
ef190f2d66
commit
b796411742
@ -49,6 +49,7 @@ impl Display for DefKind<'_> {
|
||||
match self {
|
||||
DefKind::Undecided => write!(f, "undecided"),
|
||||
DefKind::Impl(id) => write!(f, "impl {id}"),
|
||||
DefKind::Use(id) => write!(f, "use (inside {id})"),
|
||||
DefKind::Type(kind) => write!(f, "{kind}"),
|
||||
DefKind::Value(kind) => write!(f, "{kind}"),
|
||||
}
|
||||
|
@ -110,6 +110,8 @@ pub mod definition {
|
||||
Undecided,
|
||||
/// An impl block
|
||||
Impl(DefID),
|
||||
/// A use tree, and its parent
|
||||
Use(DefID),
|
||||
/// A type, such as a `type`, `struct`, or `enum`
|
||||
Type(TypeKind<'a>),
|
||||
/// A value, such as a `const`, `static`, or `fn`
|
||||
@ -132,7 +134,7 @@ pub mod definition {
|
||||
Alias(Option<DefID>),
|
||||
/// A primitive type, built-in to the compiler
|
||||
Intrinsic(Intrinsic),
|
||||
/// A user-defined abstract data type
|
||||
/// A user-defined aromatic data type
|
||||
Adt(Adt<'a>),
|
||||
/// A reference to an already-defined type: &T
|
||||
Ref(u16, DefID),
|
||||
@ -154,7 +156,7 @@ pub mod definition {
|
||||
Module,
|
||||
}
|
||||
|
||||
/// A user-defined Abstract Data Type
|
||||
/// A user-defined Aromatic Data Type
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Adt<'a> {
|
||||
/// A union-like enum type
|
||||
@ -243,7 +245,7 @@ pub mod module {
|
||||
pub parent: Option<DefID>,
|
||||
pub types: HashMap<&'a str, DefID>,
|
||||
pub values: HashMap<&'a str, DefID>,
|
||||
pub imports: HashMap<&'a str, DefID>,
|
||||
pub imports: Vec<DefID>,
|
||||
}
|
||||
|
||||
impl Module<'_> {
|
||||
@ -261,7 +263,7 @@ pub mod module {
|
||||
if let Some(parent) = parent {
|
||||
writeln!(f, "Parent: {}", parent.get())?;
|
||||
}
|
||||
for (name, table) in [("Types", types), ("Values", values), ("Imports", imports)] {
|
||||
for (name, table) in [("Types", types), ("Values", values)] {
|
||||
if table.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@ -270,6 +272,12 @@ pub mod module {
|
||||
writeln!(f, " {name} => {id}")?;
|
||||
}
|
||||
}
|
||||
if !imports.is_empty() {
|
||||
write!(f, "Imports:")?;
|
||||
for id in imports {
|
||||
write!(f, "{id},")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -348,9 +356,8 @@ pub mod project {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Project<'a> {
|
||||
pub pool: Pool<Def<'a>, DefID>,
|
||||
/// Stores anonymous tuples, function pointer types, etc.\
|
||||
/// Stores anonymous tuples, function pointer types, etc.
|
||||
pub anon_types: HashMap<TypeKind<'a>, DefID>,
|
||||
pub impls_pending: Vec<(&'a cl_ast::Impl, DefID)>,
|
||||
pub root: DefID,
|
||||
}
|
||||
|
||||
@ -384,6 +391,7 @@ pub mod project {
|
||||
module: module::Module::new(root),
|
||||
..Default::default()
|
||||
});
|
||||
// TODO: Self is not a real type!
|
||||
let selfty = pool.insert(Def {
|
||||
name: "Self",
|
||||
vis: Visibility::Public,
|
||||
@ -397,7 +405,7 @@ pub mod project {
|
||||
anon_types.insert(TypeKind::Never, never);
|
||||
anon_types.insert(TypeKind::SelfTy, selfty);
|
||||
|
||||
Self { pool, root, anon_types, impls_pending: Default::default() }
|
||||
Self { pool, root, anon_types }
|
||||
}
|
||||
}
|
||||
|
||||
@ -608,9 +616,20 @@ pub mod project {
|
||||
Path::from(self).evaluate(prj, parent)
|
||||
}
|
||||
}
|
||||
impl EvaluableTypeExpression for PathPart {
|
||||
type Out = DefID;
|
||||
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
|
||||
match self {
|
||||
PathPart::SuperKw => prj
|
||||
.parent_of(parent)
|
||||
.ok_or_else(|| "Attempt to get super of root".into()),
|
||||
PathPart::SelfKw => Ok(parent),
|
||||
PathPart::Ident(Identifier(name)) => name.as_str().evaluate(prj, parent),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> EvaluableTypeExpression for Path<'a> {
|
||||
type Out = DefID;
|
||||
|
||||
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
|
||||
let (id, path) = prj.get_type(*self, parent).ok_or("Failed to get type")?;
|
||||
|
||||
@ -714,7 +733,7 @@ pub mod name_collector {
|
||||
|
||||
match kind {
|
||||
ModuleKind::Inline(file) => file.collect(c, module)?,
|
||||
ModuleKind::Outline => todo!("Out of line modules"),
|
||||
ModuleKind::Outline => todo!("Out of line modules: {name}"),
|
||||
};
|
||||
Ok(module)
|
||||
}
|
||||
@ -731,6 +750,17 @@ pub mod name_collector {
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Use {
|
||||
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
let def =
|
||||
Def { module: Mod::new(parent), kind: DefKind::Use(parent), ..Default::default() };
|
||||
|
||||
let id = c.pool.insert(def);
|
||||
c[parent].module.imports.push(id);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Alias {
|
||||
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
let Alias { to: Identifier(name), .. } = self;
|
||||
@ -809,17 +839,6 @@ pub mod name_collector {
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Use {
|
||||
fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
let Self { tree } = self;
|
||||
todo!("Use {tree} in {parent}")
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for UseTree {
|
||||
fn collect(&'a self, _c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
todo!("Use {self} in {parent}")
|
||||
}
|
||||
}
|
||||
impl<'a> NameCollectable<'a> for Block {
|
||||
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
|
||||
self.stmts.as_slice().collect(c, parent)
|
||||
@ -904,6 +923,135 @@ pub mod name_collector {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod use_importer {
|
||||
#![allow(unused)]
|
||||
use std::fmt::format;
|
||||
|
||||
use cl_ast::*;
|
||||
|
||||
use crate::{
|
||||
definition::{Def, DefKind},
|
||||
key::DefID,
|
||||
project::Project,
|
||||
};
|
||||
|
||||
type UseResult = Result<(), String>;
|
||||
|
||||
impl<'a> Project<'a> {
|
||||
pub fn resolve_imports(&mut self) -> UseResult {
|
||||
for id in self.pool.key_iter() {
|
||||
self.visit_def(id)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn visit_def(&mut self, id: DefID) -> UseResult {
|
||||
let Def { name, vis, meta, kind, source, module } = &self.pool[id];
|
||||
if let (DefKind::Use(parent), Some(source)) = (kind, source) {
|
||||
let Item { kind: ItemKind::Use(u), .. } = source else {
|
||||
Err(format!("Not a use item: {source}"))?
|
||||
};
|
||||
println!("Importing use item {u}");
|
||||
|
||||
self.visit_use(u, *parent);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn visit_use(&mut self, u: &'a Use, parent: DefID) -> UseResult {
|
||||
let Use { absolute, tree } = u;
|
||||
|
||||
self.visit_use_tree(tree, parent, if *absolute { self.root } else { parent })
|
||||
}
|
||||
|
||||
pub fn visit_use_tree(&mut self, tree: &'a UseTree, parent: DefID, c: DefID) -> UseResult {
|
||||
match tree {
|
||||
UseTree::Tree(trees) => {
|
||||
for tree in trees {
|
||||
self.visit_use_tree(tree, parent, c)?;
|
||||
}
|
||||
}
|
||||
UseTree::Path(part, rest) => {
|
||||
let c = self.evaluate(part, c)?;
|
||||
self.visit_use_tree(rest, parent, c)?;
|
||||
}
|
||||
|
||||
UseTree::Name(name) => self.visit_use_leaf(name, parent, c)?,
|
||||
UseTree::Alias(Identifier(from), Identifier(to)) => {
|
||||
self.visit_use_alias(from, to, parent, c)?
|
||||
}
|
||||
UseTree::Glob => self.visit_use_glob(parent, c)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn visit_use_path(&mut self) -> UseResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn visit_use_leaf(
|
||||
&mut self,
|
||||
part: &'a Identifier,
|
||||
parent: DefID,
|
||||
c: DefID,
|
||||
) -> UseResult {
|
||||
let Identifier(name) = part;
|
||||
self.visit_use_alias(name, name, parent, c)
|
||||
}
|
||||
|
||||
pub fn visit_use_alias(
|
||||
&mut self,
|
||||
from: &'a str,
|
||||
name: &'a str,
|
||||
parent: DefID,
|
||||
c: DefID,
|
||||
) -> UseResult {
|
||||
let mut imported = false;
|
||||
let c_mod = &self[c].module;
|
||||
let (tid, vid) = (
|
||||
c_mod.types.get(from).copied(),
|
||||
c_mod.values.get(from).copied(),
|
||||
);
|
||||
let parent = &mut self[parent].module;
|
||||
|
||||
if let Some(tid) = tid {
|
||||
parent.types.insert(name, tid);
|
||||
imported = true;
|
||||
}
|
||||
|
||||
if let Some(vid) = vid {
|
||||
parent.values.insert(name, vid);
|
||||
imported = true;
|
||||
}
|
||||
|
||||
if imported {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Identifier {name} not found in module {c}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_use_glob(&mut self, parent: DefID, c: DefID) -> UseResult {
|
||||
// Loop over all the items in c, and add them as items in the parent
|
||||
if parent == c {
|
||||
return Ok(());
|
||||
}
|
||||
let [parent, c] = self
|
||||
.pool
|
||||
.get_many_mut([parent, c])
|
||||
.expect("parent and c are not the same");
|
||||
|
||||
for (k, v) in &c.module.types {
|
||||
parent.module.types.entry(*k).or_insert(*v);
|
||||
}
|
||||
for (k, v) in &c.module.values {
|
||||
parent.module.values.entry(*k).or_insert(*v);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod type_resolver {
|
||||
//! Performs step 2 of type checking: Evaluating type definitions
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user