diff --git a/cl-ast/src/lib.rs b/cl-ast/src/lib.rs
index 0824635..c333f40 100644
--- a/cl-ast/src/lib.rs
+++ b/cl-ast/src/lib.rs
@@ -37,7 +37,7 @@ pub struct File {
}
// Metadata decorators
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Attrs {
pub meta: Vec,
}
diff --git a/cl-repl/Cargo.toml b/cl-repl/Cargo.toml
index 9cc11d3..e8caf3f 100644
--- a/cl-repl/Cargo.toml
+++ b/cl-repl/Cargo.toml
@@ -20,3 +20,4 @@ argh = "0.1.12"
[dev-dependencies]
cl-structures = { path = "../cl-structures" }
+cl-typeck = { path = "../cl-typeck" }
diff --git a/cl-repl/examples/typeck.rs b/cl-repl/examples/typeck.rs
new file mode 100644
index 0000000..3302886
--- /dev/null
+++ b/cl-repl/examples/typeck.rs
@@ -0,0 +1,109 @@
+use cl_lexer::Lexer;
+use cl_parser::Parser;
+use cl_repl::repline::{error::Error as RlError, Repline};
+use cl_typeck::{name_collector::NameCollector, project::Project};
+use std::error::Error;
+
+fn main() -> Result<(), Box> {
+ let mut prj = Project::default();
+ let mut tcol = NameCollector::new(&mut prj);
+
+ println!(
+ "--- {} v{} 💪🦈 ---",
+ env!("CARGO_BIN_NAME"),
+ env!("CARGO_PKG_VERSION"),
+ );
+
+ read_and(
+ "\x1b[33m",
+ "cl>",
+ "? >",
+ |line| -> Result<_, Box> {
+ if line.trim_start().is_empty() {
+ query(&tcol)?;
+ return Ok(Response::Deny);
+ }
+ let mut parser = Parser::new(Lexer::new(line));
+ let code = match parser.file() {
+ Ok(code) => code,
+ Err(e) => Err(e)?,
+ };
+ tcol.file(&code)?;
+ Ok(Response::Accept)
+ },
+ )
+}
+
+pub enum Response {
+ Accept,
+ Deny,
+ Break,
+}
+
+fn read_and(
+ color: &str,
+ begin: &str,
+ again: &str,
+ mut f: impl FnMut(&str) -> Result>,
+) -> Result<(), Box> {
+ let mut rl = Repline::new(color, begin, again);
+ loop {
+ let line = match rl.read() {
+ Err(RlError::CtrlC(_)) => break,
+ Err(RlError::CtrlD(line)) => {
+ rl.deny();
+ line
+ }
+ Ok(line) => line,
+ Err(e) => Err(e)?,
+ };
+ print!("\x1b[G\x1b[J");
+ match f(&line) {
+ Ok(Response::Accept) => rl.accept(),
+ Ok(Response::Deny) => rl.deny(),
+ Ok(Response::Break) => break,
+ Err(e) => print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m"),
+ }
+ }
+ Ok(())
+}
+
+fn query(prj: &Project) -> Result<(), Box> {
+ use cl_typeck::{
+ definition::{Def, DefKind},
+ type_kind::TypeKind,
+ };
+ read_and("\x1b[35m", "qy>", "? >", |line| {
+ if line.trim_start().is_empty() {
+ return Ok(Response::Break);
+ }
+ match line {
+ "$all\n" => println!("{prj:#?}"),
+ _ => {
+ // parse it as a path, and convert the path into a borrowed path
+ let path = Parser::new(Lexer::new(line)).path()?;
+
+ let Some((type_id, path)) = prj.get_type((&path).into(), prj.module_root) else {
+ return Ok(Response::Deny);
+ };
+ let Def { name, vis, meta: _, kind, source: _, module } = &prj[type_id];
+ match (kind, prj.get_value(path, type_id)) {
+ (_, Some((val, path))) => {
+ println!("value {}; {path}\n{:#?}", usize::from(val), prj[val])
+ }
+ (DefKind::Type(TypeKind::Module), None) => println!(
+ "{vis}mod \"{name}\" (#{}); {path}\n{:#?}",
+ usize::from(type_id),
+ module
+ ),
+ (_, None) => println!(
+ "type {name}(#{}); {path}\n{:#?}",
+ usize::from(type_id),
+ prj.pool[type_id]
+ ),
+ };
+ }
+ }
+ Ok(Response::Accept)
+ })
+}
diff --git a/cl-structures/src/intern_pool.rs b/cl-structures/src/intern_pool.rs
index ccd1f4d..dbba9bf 100644
--- a/cl-structures/src/intern_pool.rs
+++ b/cl-structures/src/intern_pool.rs
@@ -89,6 +89,13 @@ impl Pool {
self.pool.get_mut(index.get())
}
+ pub fn iter(&self) -> impl Iterator- {
+ self.pool.iter()
+ }
+ pub fn iter_mut(&mut self) -> impl Iterator
- {
+ self.pool.iter_mut()
+ }
+
pub fn insert(&mut self, value: T) -> ID {
let id = self.pool.len();
self.pool.push(value);
diff --git a/cl-typeck/src/lib.rs b/cl-typeck/src/lib.rs
index a7a6357..ad7a738 100644
--- a/cl-typeck/src/lib.rs
+++ b/cl-typeck/src/lib.rs
@@ -1,7 +1,7 @@
//! # The Conlang Type Checker
//!
//! As a statically typed language, Conlang requires a robust type checker to enforce correctness.
-
+#![feature(debug_closure_helpers)]
#![warn(clippy::all)]
/*
@@ -19,50 +19,161 @@ pub mod key {
// define the index types
make_intern_key! {
- /// Uniquely represents a TypeDef in the TypeDef [Pool]
- TypeID,
- /// Uniquely represents a ValueDef in the ValueDef [Pool]
- ValueID,
- /// Uniquely represents a Module in the Module [Pool]
- ModuleID,
+ /// Uniquely represents a [Def][1] in the [Def][1] [Pool]
+ ///
+ /// [1]: crate::definition::Def
+ DefID,
}
}
-pub mod typedef {
- //! A TypeDef represents an item in the Type Namespace (a component of a
- //! [Project](crate::project::Project)).
+pub mod definition {
+ use crate::{key::DefID, module::Module, type_kind::TypeKind, value_kind::ValueKind};
+ use cl_ast::{format::FmtPretty, Item, Meta, Visibility};
+ use std::fmt::Write;
- use crate::key::{TypeID, ValueID};
- use cl_ast::{Item, Visibility};
-
- /// A TypeDef represents an item in the Type Namespace (a component of a
- /// [Project](crate::project::Project)).
- #[derive(Clone, Debug, PartialEq, Eq)]
- pub struct TypeDef {
+ #[derive(Clone, PartialEq, Eq)]
+ pub struct Def {
pub name: String,
- /// The expanded form of the definition, with all fields properly typed.
- /// This is filled in once all types are been enumerated.
- pub kind: Option,
- pub definition: Item,
- pub associated_values: Vec,
- // TODO: Generic type parameters
+ pub vis: Visibility,
+ pub meta: Vec,
+ pub kind: DefKind,
+ pub source: Option
- ,
+ pub module: Module,
+ }
+
+ impl Default for Def {
+ fn default() -> Self {
+ Self {
+ name: Default::default(),
+ vis: Default::default(),
+ meta: Default::default(),
+ kind: DefKind::Type(TypeKind::Module),
+ source: Default::default(),
+ module: Default::default(),
+ }
+ }
+ }
+
+ impl Def {
+ pub fn new_module(
+ name: String,
+ vis: Visibility,
+ meta: Vec,
+ parent: Option,
+ ) -> Self {
+ Self {
+ name,
+ vis,
+ meta,
+ kind: DefKind::Type(TypeKind::Module),
+ source: None,
+ module: Module { parent, ..Default::default() },
+ }
+ }
+ }
+
+ impl std::fmt::Debug for Def {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let Self { name, vis, meta, kind, source, module } = self;
+ f.debug_struct("Def")
+ .field("name", &name)
+ .field("vis", &vis)
+ .field_with("meta", |f| write!(f, "{meta:?}"))
+ .field("kind", &kind)
+ .field_with("source", |f| match source {
+ Some(item) => write!(f.pretty(), "{{\n{item}\n}}"),
+ None => todo!(),
+ })
+ .field("module", &module)
+ .finish()
+ }
}
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub enum DefKind {
+ /// A type, such as a ``
+ Type(TypeKind),
+ /// A value, such as a `const`, `static`, or `fn`
+ Value(ValueKind),
+ }
+
+ impl DefKind {
+ pub fn is_type(&self) -> bool {
+ matches!(self, Self::Type(_))
+ }
+ pub fn ty(&self) -> Option<&TypeKind> {
+ match self {
+ DefKind::Type(t) => Some(t),
+ _ => None,
+ }
+ }
+ pub fn is_value(&self) -> bool {
+ matches!(self, Self::Value(_))
+ }
+ pub fn value(&self) -> Option<&ValueKind> {
+ match self {
+ DefKind::Value(v) => Some(v),
+ _ => None,
+ }
+ }
+ }
+}
+
+pub mod type_kind {
+ //! A [TypeKind] represents an item in the Type Namespace
+ //! (a component of a [Project](crate::project::Project)).
+
+ use cl_ast::Visibility;
+ use std::{fmt::Debug, str::FromStr};
+
+ use crate::key::DefID;
+
+ /// The kind of a type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeKind {
+ /// A type which has not yet been resolved
+ Undecided,
+ /// An alias for an already-defined type
+ Alias(Option),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
- /// A user-defined structural product type
- Struct(Vec<(String, Visibility, TypeID)>),
- /// A user-defined union-like enum type
- Enum(Vec<(String, TypeID)>),
- /// An alias for an already-defined type
- Alias(TypeID),
+ /// A user-defined abstract data type
+ Adt(Adt),
+ /// A reference to an already-defined type: &T
+ Ref(DefID),
+ /// A contiguous view of dynamically sized memory
+ Slice(DefID),
+ /// A function pointer which accepts multiple inputs and produces an output
+ FnPtr { args: Vec, rety: DefID },
/// The unit type
Empty,
+ /// The never type
+ Never,
/// The Self type
SelfTy,
- // TODO: union types, tuples, tuple structs maybe?
+ /// An untyped module
+ Module,
+ }
+
+ /// A user-defined Abstract Data Type
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub enum Adt {
+ /// A union-like enum type
+ Enum(Vec<(String, DefID)>),
+ CLikeEnum(Vec<(String, u128)>),
+ /// An enum with no fields, which can never be constructed
+ FieldlessEnum,
+
+ /// A structural product type with named members
+ Struct(Vec<(String, Visibility, DefID)>),
+ /// A structural product type with unnamed members
+ TupleStruct(Vec<(Visibility, DefID)>),
+ /// A structural product type of neither named nor unnamed members
+ UnitStruct,
+
+ /// A choose your own undefined behavior type
+ /// TODO: should unions be a language feature?
+ Union(Vec<(String, DefID)>),
}
/// The set of compiler-intrinsic types.
@@ -70,37 +181,70 @@ pub mod typedef {
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Intrinsic {
- /// A 32-bit two's complement integer
- i32,
- /// A 32-bit unsigned integer
- u32,
- /// A boolean (`true` or `false`)
- bool,
+ /// An 8-bit signed integer: `#[intrinsic = "i8"]`
+ I8,
+ /// A 16-bit signed integer: `#[intrinsic = "i16"]`
+ I16,
+ /// A 32-bit signed integer: `#[intrinsic = "i32"]`
+ I32,
+ /// A 64-bit signed integer: `#[intrinsic = "i32"]`
+ I64,
+ // /// A 128-bit signed integer: `#[intrinsic = "i32"]`
+ // I128,
+ /// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
+ U8,
+ /// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
+ U16,
+ /// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
+ U32,
+ /// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
+ U64,
+ // /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
+ // U128,
+ /// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
+ Bool,
+ }
+
+ impl FromStr for Intrinsic {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result {
+ Ok(match s {
+ "i8" => Intrinsic::I8,
+ "i16" => Intrinsic::I16,
+ "i32" => Intrinsic::I32,
+ "i64" => Intrinsic::I64,
+ "u8" => Intrinsic::U8,
+ "u16" => Intrinsic::U16,
+ "u32" => Intrinsic::U32,
+ "u64" => Intrinsic::U64,
+ "bool" => Intrinsic::Bool,
+ _ => Err(())?,
+ })
+ }
+ }
+
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub enum Float {
+ F32 = 0x20,
+ F64,
}
}
-pub mod valdef {
- //! A [ValueDef] represents an item in the Value Namespace (a component of a
- //! [Project](crate::project::Project)).
+pub mod value_kind {
+ //! A [ValueKind] represents an item in the Value Namespace
+ //! (a component of a [Project](crate::project::Project)).
use crate::typeref::TypeRef;
- use cl_ast::{Block, Item};
-
- /// A [ValueDef] represents an item in the Value Namespace (a component of a
- /// [Project](crate::project::Project)).
- #[derive(Clone, Debug, PartialEq, Eq)]
- pub struct ValueDef {
- pub name: String,
- /// The expanded form of the definition, with all fields properly typed
- pub kind: Option,
- pub definition: Item,
- }
+ use cl_ast::Block;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ValueKind {
+ Undecided,
Const(TypeRef),
Static(TypeRef),
Fn {
+ // TODO: Store the variable bindings here!
args: Vec,
rety: TypeRef,
body: Block,
@@ -111,57 +255,532 @@ pub mod valdef {
pub mod module {
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
- use crate::key::{ModuleID, TypeID, ValueID};
+ use crate::key::DefID;
use std::collections::HashMap;
/// A [Module] is a node in the Module Tree (a component of a
/// [Project](crate::project::Project)).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Module {
- pub parent: Option,
- pub types: HashMap,
- pub values: HashMap,
- pub submodules: HashMap,
+ pub parent: Option,
+ pub types: HashMap,
+ pub values: HashMap,
+ }
+
+ impl Module {
+ pub fn new(parent: DefID) -> Self {
+ Self { parent: Some(parent), ..Default::default() }
+ }
+ }
+}
+
+pub mod path {
+ use cl_ast::{Path as AstPath, PathPart};
+
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+ pub struct Path<'p> {
+ pub absolute: bool,
+ pub parts: &'p [PathPart],
+ }
+
+ impl<'p> Path<'p> {
+ pub fn new(path: &'p AstPath) -> Self {
+ let AstPath { absolute, parts } = path;
+ Self { absolute: *absolute, parts }
+ }
+ pub fn relative(self) -> Self {
+ Self { absolute: false, ..self }
+ }
+ pub fn pop_front(self) -> Option {
+ let Self { absolute, parts } = self;
+ Some(Self { absolute, parts: parts.get(1..)? })
+ }
+ pub fn is_empty(&self) -> bool {
+ self.parts.is_empty()
+ }
+ pub fn len(&self) -> usize {
+ self.parts.len()
+ }
+ pub fn front(&self) -> Option<&PathPart> {
+ self.parts.first()
+ }
+ }
+
+ impl<'p> From<&'p AstPath> for Path<'p> {
+ fn from(value: &'p AstPath) -> Self {
+ Self::new(value)
+ }
+ }
+ impl std::fmt::Display for Path<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ const SEPARATOR: &str = "::";
+ let Self { absolute, parts } = self;
+ if *absolute {
+ write!(f, "{SEPARATOR}")?
+ }
+ for (idx, part) in parts.iter().enumerate() {
+ write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
+ }
+ Ok(())
+ }
}
}
pub mod project {
+ use crate::{
+ definition::{Def, DefKind},
+ key::DefID,
+ path::Path,
+ type_kind::TypeKind,
+ };
+ use cl_ast::{Identifier, PathPart, Visibility};
use cl_structures::intern_pool::Pool;
+ use std::ops::{Index, IndexMut};
+
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub struct Project {
+ pub pool: Pool,
+ pub module_root: DefID,
+ }
+
+ impl Project {
+ pub fn new() -> Self {
+ Self::default()
+ }
+ }
+ impl Default for Project {
+ fn default() -> Self {
+ let mut pool = Pool::default();
+ let module_root = pool.insert(Def::default());
+ // Insert the Never(!) type
+ let never = pool.insert(Def {
+ name: String::from("!"),
+ vis: Visibility::Public,
+ kind: DefKind::Type(TypeKind::Never),
+ ..Default::default()
+ });
+ pool[module_root]
+ .module
+ .types
+ .insert(String::from("!"), never);
+ Self { pool, module_root }
+ }
+ }
+
+ impl Project {
+ pub fn parent_of(&self, module: DefID) -> Option {
+ self[module].module.parent
+ }
+ pub fn root_of(&self, module: DefID) -> DefID {
+ match self.parent_of(module) {
+ Some(module) => self.root_of(module),
+ None => module,
+ }
+ }
+ /// Resolves a path within a module tree, finding the innermost module.
+ /// Returns the remaining path parts.
+ pub fn get_type<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
+ // TODO: Cache module lookups
+ if path.absolute {
+ self.get_type(path.relative(), self.root_of(within))
+ } else if let Some(front) = path.front() {
+ let module = &self[within].module;
+ match front {
+ PathPart::SelfKw => self.get_type(path.pop_front()?, within),
+ PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?),
+ PathPart::Ident(Identifier(name)) => match module.types.get(name) {
+ Some(&submodule) => self.get_type(path.pop_front()?, submodule),
+ None => Some((within, path)),
+ },
+ }
+ } else {
+ Some((within, path))
+ }
+ }
+
+ pub fn get_value<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
+ match path.front()? {
+ PathPart::Ident(Identifier(name)) => Some((
+ self[within].module.values.get(name).copied()?,
+ path.pop_front()?,
+ )),
+ _ => None,
+ }
+ }
+
+ #[rustfmt::skip]
+ pub fn insert_type(&mut self, name: String, value: Def, parent: DefID) -> Option {
+ let id = self.pool.insert(value);
+ self[parent].module.types.insert(name, id)
+ }
+ #[rustfmt::skip]
+ pub fn insert_value(&mut self, name: String, value: Def, parent: DefID) -> Option {
+ let id = self.pool.insert(value);
+ self[parent].module.values.insert(name, id)
+ }
+ }
+
+ /// Implements [Index] and [IndexMut] for [Project]: `self.table[ID] -> Definition`
+ macro_rules! impl_index {
+ ($(self.$table:ident[$idx:ty] -> $out:ty),*$(,)?) => {$(
+ impl Index<$idx> for Project {
+ type Output = $out;
+ fn index(&self, index: $idx) -> &Self::Output {
+ &self.$table[index]
+ }
+ }
+ impl IndexMut<$idx> for Project {
+ fn index_mut(&mut self, index: $idx) -> &mut Self::Output {
+ &mut self.$table[index]
+ }
+ }
+ )*};
+ }
+ impl_index! {
+ self.pool[DefID] -> Def,
+ // self.types[TypeID] -> TypeDef,
+ // self.values[ValueID] -> ValueDef,
+ }
+}
+
+pub mod name_collector {
+ //! Performs step 1 of type checking: Collecting all the names of things into [Module] units
use crate::{
- key::{ModuleID, TypeID, ValueID},
- module::Module,
- typedef::TypeDef,
- valdef::ValueDef,
+ definition::{Def, DefKind},
+ key,
+ project::Project,
+ type_kind::{Adt, TypeKind},
+ value_kind::ValueKind,
};
+ use cl_ast::*;
+ use std::ops::{Deref, DerefMut};
- #[derive(Clone, Debug, Default, PartialEq, Eq)]
- pub struct Project {
- pub types: Pool,
- pub values: Pool,
- pub modules: Pool,
+ /// Collects types for future use
+ #[derive(Debug, PartialEq, Eq)]
+ pub struct NameCollector<'prj> {
+ /// A stack of the current modules
+ pub mod_stack: Vec,
+ /// The [Project], the type checker and resolver's central data store
+ pub project: &'prj mut Project,
+ }
+
+ impl<'prj> NameCollector<'prj> {
+ pub fn new(project: &'prj mut Project) -> Self {
+ // create a root module
+ Self { mod_stack: vec![project.module_root], project }
+ }
+
+ /// Gets the currently traversed parent module
+ pub fn parent(&self) -> Option {
+ self.mod_stack.last().copied()
+ }
+ }
+
+ impl Deref for NameCollector<'_> {
+ type Target = Project;
+ fn deref(&self) -> &Self::Target {
+ self.project
+ }
+ }
+ impl DerefMut for NameCollector<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.project
+ }
+ }
+
+ impl NameCollector<'_> {
+ pub fn file(&mut self, f: &File) -> Result<(), &'static str> {
+ let parent = self.parent().ok_or("No parent to add item to")?;
+
+ for item in &f.items {
+ let def = match &item.kind {
+ // modules
+ // types
+ ItemKind::Module(_) => {
+ self.module(item)?;
+ continue;
+ }
+ ItemKind::Enum(_) => Some(self.ty_enum(item)?),
+ ItemKind::Alias(_) => Some(self.ty_alias(item)?),
+ ItemKind::Struct(_) => Some(self.ty_struct(item)?),
+ // values processed by the value collector
+ ItemKind::Const(_) => Some(self.val_const(item)?),
+ ItemKind::Static(_) => Some(self.val_static(item)?),
+ ItemKind::Function(_) => Some(self.val_function(item)?),
+ ItemKind::Impl(_) => None,
+ };
+ let Some(def) = def else { continue };
+ match def.kind {
+ DefKind::Type(_) => {
+ if let Some(v) = self.insert_type(def.name.clone(), def, parent) {
+ panic!("Redefinition of type {} ({v:?})!", self[v].name)
+ }
+ }
+ DefKind::Value(_) => {
+ if let Some(v) = self.insert_value(def.name.clone(), def, parent) {
+ panic!("Redefinition of value {} ({v:?})!", self[v].name)
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Collects a [Module]
+ pub fn module(&mut self, m: &Item) -> Result<(), &'static str> {
+ let Item { kind: ItemKind::Module(Module { name, kind }), vis, attrs, .. } = m else {
+ Err("module called on Item which was not an ItemKind::Module")?
+ };
+ let ModuleKind::Inline(kind) = kind else {
+ Err("Out-of-line modules not yet supported")?
+ };
+ let parent = self.parent().ok_or("No parent to add module to")?;
+
+ let module = self.pool.insert(Def::new_module(
+ name.0.clone(),
+ *vis,
+ attrs.meta.clone(),
+ Some(parent),
+ ));
+
+ self[parent]
+ .module
+ .types
+ .insert(name.0.clone(), module)
+ .is_some()
+ .then(|| panic!("Error: redefinition of module {name}"));
+
+ self.mod_stack.push(module);
+ let out = self.file(kind);
+ self.mod_stack.pop();
+
+ out
+ }
+ }
+ /// Type collection
+ impl NameCollector<'_> {
+ /// Collects an [Item] of type [ItemKind::Enum]
+ pub fn ty_enum(&mut self, item: &Item) -> Result {
+ let Item { kind: ItemKind::Enum(Enum { name, kind }), vis, attrs, .. } = item else {
+ Err("Enum called on item which was not ItemKind::Enum")?
+ };
+ let kind = match kind {
+ EnumKind::NoVariants => DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)),
+ EnumKind::Variants(_) => DefKind::Type(TypeKind::Undecided),
+ };
+
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind,
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+
+ /// Collects an [Item] of type [ItemKind::Alias]
+ pub fn ty_alias(&mut self, item: &Item) -> Result {
+ let Item { kind: ItemKind::Alias(Alias { to: name, from }), vis, attrs, .. } = item
+ else {
+ Err("Alias called on Item which was not ItemKind::Alias")?
+ };
+
+ let mut kind = match from {
+ Some(_) => DefKind::Type(TypeKind::Undecided),
+ None => DefKind::Type(TypeKind::Alias(None)),
+ };
+
+ for meta in &attrs.meta {
+ let Meta { name: meta_name, kind: meta_kind } = meta;
+ match (meta_name.0.as_str(), meta_kind) {
+ ("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => {
+ kind = DefKind::Type(TypeKind::Intrinsic(
+ intrinsic.parse().map_err(|_| "unknown intrinsic type")?,
+ ));
+ }
+ ("intrinsic", MetaKind::Plain) => {
+ kind = DefKind::Type(TypeKind::Intrinsic(
+ name.0.parse().map_err(|_| "Unknown intrinsic type")?,
+ ))
+ }
+ _ => {}
+ }
+ }
+
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind,
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+
+ /// Collects an [Item] of type [ItemKind::Struct]
+ pub fn ty_struct(&mut self, item: &Item) -> Result {
+ let Item { kind: ItemKind::Struct(Struct { name, kind }), vis, attrs, .. } = item
+ else {
+ Err("Struct called on item which was not ItemKind::Struct")?
+ };
+ let kind = match kind {
+ StructKind::Empty => DefKind::Type(TypeKind::Adt(Adt::UnitStruct)),
+ StructKind::Tuple(_) => DefKind::Type(TypeKind::Undecided),
+ StructKind::Struct(_) => DefKind::Type(TypeKind::Undecided),
+ };
+
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind,
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+ }
+ /// Value collection
+ impl NameCollector<'_> {
+ pub fn val_const(&mut self, item: &Item) -> Result {
+ let Item { kind: ItemKind::Const(Const { name, .. }), vis, attrs, .. } = item else {
+ Err("Const called on Item which was not ItemKind::Const")?
+ };
+
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind: DefKind::Value(ValueKind::Undecided),
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+
+ pub fn val_static(&mut self, item: &Item) -> Result {
+ let Item { kind: ItemKind::Static(Static { name, .. }), vis, attrs, .. } = item else {
+ Err("Static called on Item which was not ItemKind::Static")?
+ };
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind: DefKind::Type(TypeKind::Undecided),
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+
+ pub fn val_function(&mut self, item: &Item) -> Result {
+ // TODO: treat function bodies like modules with internal items
+ let Item { kind: ItemKind::Function(Function { name, .. }), vis, attrs, .. } = item
+ else {
+ Err("val_function called on Item which was not ItemKind::Function")?
+ };
+ Ok(Def {
+ name: name.0.clone(),
+ vis: *vis,
+ meta: attrs.meta.clone(),
+ kind: DefKind::Value(ValueKind::Undecided),
+ source: Some(item.clone()),
+ module: Default::default(),
+ })
+ }
+ }
+}
+
+pub mod type_resolver {
+ //! Performs step 2 of type checking: Evaluating type definitions
+ #![allow(unused)]
+ use std::ops::{Deref, DerefMut};
+
+ use cl_ast::*;
+
+ use crate::{definition::Def, key::DefID, project::Project};
+
+ pub struct TypeResolver<'prj> {
+ pub project: &'prj mut Project,
+ }
+
+ impl Deref for TypeResolver<'_> {
+ type Target = Project;
+ fn deref(&self) -> &Self::Target {
+ self.project
+ }
+ }
+ impl DerefMut for TypeResolver<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.project
+ }
+ }
+
+ impl TypeResolver<'_> {
+ pub fn resolve(&mut self) -> Result {
+ #![allow(unused)]
+ for typedef in self.pool.iter_mut().filter(|v| v.kind.is_type()) {
+ let Def { name, vis, meta: attr, kind, source: Some(ref definition), module: _ } =
+ typedef
+ else {
+ continue;
+ };
+ match &definition.kind {
+ ItemKind::Alias(Alias { to: _, from: Some(from) }) => match &from.kind {
+ TyKind::Never => todo!(),
+ TyKind::Empty => todo!(),
+ TyKind::SelfTy => todo!(),
+ TyKind::Path(_) => todo!(),
+ TyKind::Tuple(_) => todo!(),
+ TyKind::Ref(_) => todo!(),
+ TyKind::Fn(_) => todo!(),
+ },
+ ItemKind::Alias(_) => {}
+ ItemKind::Const(_) => todo!(),
+ ItemKind::Static(_) => todo!(),
+ ItemKind::Module(_) => todo!(),
+ ItemKind::Function(_) => {}
+ ItemKind::Struct(_) => {}
+ ItemKind::Enum(_) => {}
+ ItemKind::Impl(_) => {}
+ }
+ }
+ Ok(true)
+ }
+
+ pub fn get_type(&self, kind: &TyKind) -> Option {
+ match kind {
+ TyKind::Never => todo!(),
+ TyKind::Empty => todo!(),
+ TyKind::SelfTy => todo!(),
+ TyKind::Path(_) => todo!(),
+ TyKind::Tuple(_) => todo!(),
+ TyKind::Ref(_) => todo!(),
+ TyKind::Fn(_) => todo!(),
+ }
+ None
+ }
}
}
pub mod typeref {
- //! Stores type and referencce info
+ //! Stores type and reference info
- use crate::key::TypeID;
+ use crate::key::DefID;
/// The Type struct represents all valid types, and can be trivially equality-compared
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypeRef {
/// You can only have a pointer chain 65535 pointers long.
ref_depth: u16,
- /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete)
+ /// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
kind: RefKind,
}
- /// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete)
+ /// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RefKind {
- /// A Concrete type has an associated [TypeDef](super::typedef::TypeDef)
- Concrete(TypeID),
+ /// A Concrete type has an associated [Def](super::definition::Def)
+ Concrete(DefID),
/// A Generic type is a *locally unique* comparable value,
/// valid only until the end of its typing context.
/// This is usually the surrounding function.
@@ -233,7 +852,58 @@ let rules: Hashmap> {
*/
-/*
- Potential solution:
- Store reference to type field of each type expression in the AST
-*/
+pub mod rule {
+ use crate::{key::DefID, typeref::TypeRef};
+
+ pub struct Rule {
+ /// What is this Rule for?
+ pub operation: (),
+ /// What inputs does it take?
+ pub inputs: Vec,
+ /// What output does it produce?
+ pub output: TypeRef,
+ /// Where did this rule come from?
+ pub through: Origin,
+ }
+
+ // TODO: Genericize
+ pub enum Operation {
+ Mul,
+ Div,
+ Rem,
+ Add,
+ Sub,
+
+ Deref,
+ Neg,
+ Not,
+ At,
+ Tilde,
+
+ Index,
+
+ If,
+ While,
+ For,
+ }
+
+ pub enum Origin {
+ /// This rule is built into the compiler
+ Intrinsic,
+ /// This rule is derived from an implementation on a type
+ Extrinsic(DefID),
+ }
+}
+
+pub mod typeck {
+ #![allow(unused)]
+ use cl_ast::*;
+
+ pub struct Context {
+ rules: (),
+ }
+
+ trait TypeCheck {}
+}
+
+//