From 9d7ab779993da8a5ccdee445010898c81860a7a7 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 21 Jul 2024 05:45:40 -0500 Subject: [PATCH] cl-typeck: Add `Handle` type - Holds a DefID and a reference to the Project - Pretty-prints def signatures - Use handles when printing types (WIP) Known issues: - Printing recursive types recurses forever - There's some unrelated name resolution stuff going on that needs investigating. TODO: - Better name - HandleMut? - More interfaces! - Migrate everything to use handles when oop semantics are easiest - Reject plain recursive types, and don't recurse through reference types when printing --- compiler/cl-typeck/examples/typeck.rs | 55 +++++---- compiler/cl-typeck/src/handle.rs | 169 +++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 24 deletions(-) diff --git a/compiler/cl-typeck/examples/typeck.rs b/compiler/cl-typeck/examples/typeck.rs index 03f97c0..be3cd01 100644 --- a/compiler/cl-typeck/examples/typeck.rs +++ b/compiler/cl-typeck/examples/typeck.rs @@ -6,6 +6,7 @@ use cl_lexer::Lexer; use cl_parser::{inliner::ModuleInliner, Parser}; use cl_typeck::{ definition::Def, + handle::Handle, name_collector::NameCollector, node::{Node, NodeSource}, project::Project, @@ -118,8 +119,8 @@ fn query_type_expression(prj: &mut Project) -> Result<(), RlError> { } // parse it as a path, and convert the path into a borrowed path let ty = Parser::new(Lexer::new(line)).ty()?.kind; - let id = prj.evaluate(&ty, prj.root)?; - pretty_def(&prj[id], id); + let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj); + pretty_handle(id)?; Ok(Response::Accept) }) } @@ -135,31 +136,43 @@ fn resolve_all(prj: &mut Project) -> Result<(), Box> { fn list_types(prj: &mut Project) { println!(" name\x1b[30G type"); - for (idx, Def { kind, node: Node { vis, kind: source, .. }, .. }) in - prj.pool.values().enumerate() - { - print!("{idx:3}: {vis}"); - if let Some(Some(name)) = source.as_ref().map(NodeSource::name) { - print!("{name}"); - } - println!("\x1b[30G| {kind}") + for (idx, key) in prj.pool.keys().enumerate() { + let Def { node: Node { vis, kind: source, .. }, .. } = &prj[key]; + let name = match source.as_ref().map(NodeSource::name) { + Some(Some(name)) => name, + _ => "".into(), + }; + print!( + "{idx:3}: {vis}{name}\x1b[30G = {}", + key.handle_unchecked(prj) + ); + println!(); } } -fn pretty_def(def: &Def, id: impl Into) { - let id = id.into(); - let Def { module, kind, node: Node { meta, vis, kind: source, .. } } = def; - for meta in *meta { - println!("#[{meta}]") +fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> { + use std::io::Write; + let mut stdout = std::io::stdout().lock(); + let Some(Def { module, node: Node { vis, .. }, .. }) = handle.get() else { + return writeln!(stdout, "Invalid handle: {handle}"); + }; + writeln!(stdout, "{C_LISTING}{vis}{handle}\x1b[0m: {}", handle.id())?; + if let Some(parent) = module.parent { + writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {}", handle.with(parent))?; } - if let Some(Some(name)) = source.as_ref().map(NodeSource::name) { - print!("{vis}{name} "); + if !module.types.is_empty() { + writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?; + for (name, def) in &module.types { + writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? + } } - println!("[id: {id}] = {kind}"); - if let Some(source) = source { - println!("Source:\n{C_LISTING}{source}\x1b[0m"); + if !module.values.is_empty() { + writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?; + for (name, def) in &module.values { + writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? + } } - println!("\x1b[90m{module}\x1b[0m"); + write!(stdout, "\x1b[0m") } fn inline_modules(code: cl_ast::File, path: impl AsRef) -> cl_ast::File { diff --git a/compiler/cl-typeck/src/handle.rs b/compiler/cl-typeck/src/handle.rs index 7cebd05..f0705d9 100644 --- a/compiler/cl-typeck/src/handle.rs +++ b/compiler/cl-typeck/src/handle.rs @@ -1,3 +1,4 @@ +use crate::{definition::Def, project::Project}; use cl_structures::index_map::*; // define the index types @@ -8,8 +9,170 @@ make_index! { DefID, } -impl std::fmt::Display for DefID { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) +/// A handle to a certain [Def] within a [Project] +#[derive(Clone, Copy, Debug)] +pub struct Handle<'prj, 'code> { + id: DefID, + prj: &'prj Project<'code>, +} + +impl DefID { + /// Constructs a new [Handle] from this DefID and the provided [Project]. + pub fn handle<'p, 'c>(self, prj: &'p Project<'c>) -> Option> { + Handle::new(self, prj) + } + + /// Constructs a new [Handle] from this DefID and the provided [Project] + pub fn handle_unchecked<'p, 'c>(self, prj: &'p Project<'c>) -> Handle<'p, 'c> { + Handle::new_unchecked(self, prj) + } +} + +impl<'p, 'c> Handle<'p, 'c> { + /// Constructs a new Handle from the provided [DefID] and [Project]. + /// Returns [Some]\(Handle) if the [DefID] exists in the [Project]. + pub fn new(id: DefID, prj: &'p Project<'c>) -> Option { + prj.pool.get(id).is_some().then_some(Self { id, prj }) + } + + /// Constructs a new Handle from the provided [DefID] and [Project] without checking membership. + /// Using the handle may cause panics or other unwanted (but defined) behavior. + pub fn new_unchecked(id: DefID, prj: &'p Project<'c>) -> Self { + Self { id, prj } + } + + /// Gets the [Def] this handle points to. + pub fn get(self) -> Option<&'p Def<'c>> { + self.prj.pool.get(self.id) + } + + /// Gets the [Project] this handle points to. + pub fn project(self) -> &'p Project<'c> { + self.prj + } + + pub fn id(self) -> DefID { + self.id + } + + // TODO: get parent, children, etc. + + /// Gets a handle to the other ID within the same project + pub fn with(self, id: DefID) -> Self { + Self { id, ..self } + } +} + +mod display { + use super::*; + use crate::{definition::*, format_utils::*}; + use std::fmt::{self, Display, Write}; + + impl Display for DefID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl Display for Handle<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { id, prj } = *self; + let Some(def) = prj.pool.get(id) else { + return write!(f, ""); + }; + + // Match the type + match &def.kind { + DefKind::Undecided => write!(f, ""), + DefKind::Impl(id) => write!(f, "impl {}", self.with(*id)), + DefKind::Use(id) => write!(f, "use inside {}", self.with(*id)), + DefKind::Type(kind) => match kind { + TypeKind::Alias(None) => write!(f, "type"), + TypeKind::Alias(Some(id)) => write!(f, "{}", self.with(*id)), + TypeKind::Intrinsic(intrinsic) => write!(f, "{intrinsic}"), + TypeKind::Adt(adt) => display_adt(self, adt, f), + TypeKind::Ref(count, id) => { + for _ in 0..*count { + f.write_char('&')?; + } + self.with(*id).fmt(f) + } + TypeKind::Array(id, size) => { + write!(f.delimit_with("[", "]"), "{}; {size}", self.with(*id)) + } + TypeKind::Slice(id) => write!(f.delimit_with("[", "]"), "{}", self.with(*id)), + TypeKind::Tuple(ids) => { + let mut ids = ids.iter(); + separate(", ", || { + let id = ids.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "{}", self.with(*id))) + })(f.delimit_with("(", ")")) + } + TypeKind::FnSig { args, rety } => { + write!(f, "fn {} -> {}", self.with(*args), self.with(*rety)) + } + TypeKind::Empty => write!(f, "()"), + TypeKind::Never => write!(f, "!"), + TypeKind::Module => match def.name() { + Some(name) => write!(f, "mod {name}"), + None => write!(f, "mod"), + }, + }, + + DefKind::Value(kind) => match kind { + ValueKind::Const(id) => write!(f, "const {}", self.with(*id)), + ValueKind::Static(id) => write!(f, "static {}", self.with(*id)), + ValueKind::Local(id) => write!(f, "local {}", self.with(*id)), + ValueKind::Fn(id) => write!(f, "{}", self.with(*id)), + }, + } + } + } + + /// Formats an ADT: a continuation of [Handle::fmt] + fn display_adt(handle: &Handle, adt: &Adt, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match adt { + Adt::Enum(variants) => { + let mut variants = variants.iter(); + separate(",", || { + variants.next().map(|(name, def)| { + move |f: &mut Delimit<_>| match def { + Some(def) => write!(f, "\n{name}: {}", handle.with(*def)), + None => write!(f, "\n{name}"), + } + }) + })(f.delimit_with("enum {", "\n}")) + } + Adt::CLikeEnum(variants) => { + let mut variants = variants.iter(); + separate(",", || { + let (name, descrim) = variants.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}")) + })(f.delimit_with("enum {", "\n}")) + } + Adt::FieldlessEnum => write!(f, "enum"), + Adt::Struct(members) => { + let mut members = members.iter(); + separate(",", || { + let (name, vis, id) = members.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "\n{vis}{name}: {}", handle.with(*id))) + })(f.delimit_with("struct {", "\n}")) + } + Adt::TupleStruct(members) => { + let mut members = members.iter(); + separate(", ", || { + let (vis, def) = members.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", handle.with(*def))) + })(f.delimit_with("struct (", ")")) + } + Adt::UnitStruct => write!(f, "struct;"), + Adt::Union(variants) => { + let mut variants = variants.iter(); + separate(",", || { + let (name, def) = variants.next()?; + Some(move |f: &mut Delimit<_>| write!(f, "\n{name}: {}", handle.with(*def))) + })(f.delimit_with("union {", "\n}")) + } + } } }