Conlang/cl-repl/examples/typeck.rs

191 lines
5.2 KiB
Rust
Raw Normal View History

use cl_lexer::Lexer;
use cl_parser::Parser;
use cl_repl::repline::{error::Error as RlError, Repline};
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
use cl_typeck::{
definition::Def, name_collector::NameCollectable, project::Project, type_resolver::resolve,
};
use std::error::Error;
// Path to display in standard library errors
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
// Statically included standard library
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
const STDLIB: &str = include_str!("../../stdlib/lib.cl");
// Colors
const C_MAIN: &str = "";
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
const C_RESV: &str = "\x1b[35m";
const C_CODE: &str = "\x1b[36m";
const C_LISTING: &str = "\x1b[38;5;117m";
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
/// A home for immutable intermediate ASTs
///
/// TODO: remove this.
static mut TREES: TreeManager = TreeManager::new();
fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Project::default();
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
let mut parser = Parser::new(Lexer::new(STDLIB));
let code = match parser.file() {
Ok(code) => code,
Err(e) => {
eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
Err(e)?
}
};
unsafe { TREES.push(code) }.collect_in_root(&mut prj)?;
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
main_menu(&mut prj)
}
pub enum Response {
Accept,
Deny,
Break,
}
fn read_and(
color: &str,
begin: &str,
mut f: impl FnMut(&str) -> Result<Response, Box<dyn Error>>,
) -> Result<(), Box<dyn Error>> {
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
let mut rl = Repline::new(color, begin, "? >");
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(())
}
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
fn main_menu(prj: &mut Project) -> Result<(), Box<dyn Error>> {
banner();
read_and(C_MAIN, "mu>", |line| {
match line.trim() {
"c" | "code" => enter_code(prj),
"clear" => clear(),
"e" | "exit" => return Ok(Response::Break),
"l" | "list" => list_types(prj),
"q" | "query" => query_type_expression(prj),
"r" | "resolve" => resolve_all(prj),
"h" | "help" => {
println!(
"Valid commands are:
code (c): Enter code to type-check
list (l): List all known types
query (q): Query the type system
resolve (r): Perform type resolution
help (h): Print this list
exit (e): Exit the program"
);
return Ok(Response::Deny);
}
_ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?,
}
.map(|_| Response::Accept)
})
}
fn enter_code(prj: &mut Project) -> Result<(), Box<dyn Error>> {
read_and(C_CODE, "cl>", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
let code = Parser::new(Lexer::new(line)).file()?;
// Safety: this is totally unsafe
unsafe { TREES.push(code) }.collect_in_root(prj)?;
Ok(Response::Accept)
})
}
fn query_type_expression(prj: &mut Project) -> Result<(), Box<dyn Error>> {
read_and(C_RESV, "ty>", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
// 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);
Ok(Response::Accept)
})
}
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> {
for id in prj.pool.key_iter() {
resolve(prj, id)?;
}
println!("Types resolved successfully!");
Ok(())
}
fn list_types(prj: &mut Project) -> Result<(), Box<dyn Error>> {
println!(" name\x1b[30G type");
for (idx, Def { name, vis, kind, .. }) in prj.pool.iter().enumerate() {
print!("{idx:3}: {vis}");
if name.is_empty() {
print!("\x1b[30m_\x1b[0m")
}
println!("{name}\x1b[30G| {kind}");
}
Ok(())
}
fn pretty_def(def: &Def, id: impl Into<usize>) {
let id = id.into();
let Def { vis, name, kind, module, meta, source } = def;
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
for meta in *meta {
println!("#[{meta}]")
}
println!("{vis}{name} [id: {id}] = {kind}");
if let Some(source) = source {
println!("Source:\n{C_LISTING}{source}\x1b[0m");
}
println!("\x1b[90m{module}\x1b[0m");
cl-typeck: Computer! Define "types!" WARNING: The type checker is still a MAJOR work in progress. You'll be unable to ignore the stringly-typed error handling, effort duplication, and general poor code quality all around. However, this makes leaps and bounds toward a functional, if primitive, type checker. Definitions now borrow their data from the AST, reducing needless copies. - This unfortunately means the REPL has to keep around old ASTs, but... Eh. Probably have to keep those around anyway. The "char" primitive type has been added. Semantics TBD. Modules definition, type_kind, and value_kind have been consolidated into one. Project now keeps track of the set of unnamed (anonymous) types (i.e. tuples, function pointers, references) and will yield them freely. - Project can now also evaluate arbitrary type expressions via the EvaluableTypeExpression trait. The NameCollector has been replaced with trait NameCollectable. - This pass visits each node in the AST which can have an item declaration inside it, and constructs an unevaluated module tree. The TypeResolver has been replaced with trait TypeResolvable and the function resolve() - This pass visits each *Def* in the project, and attempts to derive all subtypes. - It's important to note that for Items, unlike EvaluableTypeExpression, the ID passed into resolve_type is the ID of `self`, not of the parent! typeck.rs: - The Conlang "standard library" is included in the binary - There's a main menu now! Type "help" for options. - Queries have been upgraded from paths to full type expressions! - Querying doesn't currently trigger resolution, but it could!
2024-04-17 04:45:24 +00:00
}
fn clear() -> Result<(), Box<dyn Error>> {
println!("\x1b[H\x1b[2J");
banner();
Ok(())
}
fn banner() {
println!(
"--- {} v{} 💪🦈 ---",
env!("CARGO_BIN_NAME"),
env!("CARGO_PKG_VERSION"),
);
}
/// Keeps leaked references to past ASTs, for posterity:tm:
struct TreeManager {
trees: Vec<&'static cl_ast::File>,
}
impl TreeManager {
const fn new() -> Self {
Self { trees: vec![] }
}
fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File {
let ptr = Box::leak(Box::new(tree));
self.trees.push(ptr);
ptr
}
}