conlang: Use interned strings (Sym) for all symbols

This commit is contained in:
John 2024-04-24 19:34:29 -05:00
parent ede00c3c86
commit 40ec9b30e4
19 changed files with 198 additions and 195 deletions

View File

@ -11,6 +11,8 @@
//! - [Path]: Path expressions
use cl_structures::span::*;
pub use cl_structures::arena::global_intern::Sym;
/// Whether a binding ([Static] or [Let]) or reference is mutable or not
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Mutability {
@ -30,7 +32,7 @@ pub enum Visibility {
// TODO: Capture token?
/// A name
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Identifier(pub String);
pub struct Identifier(pub Sym);
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
#[derive(Clone, Debug, PartialEq, Eq, Hash)]

View File

@ -6,6 +6,7 @@ use super::{
temp_type_impl::ConValue,
BuiltIn, Callable,
};
use cl_ast::Sym;
use std::{
io::{stdout, Write},
rc::Rc,
@ -68,7 +69,7 @@ builtins! {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
_ => Err(Error::TypeError)?
})
}
@ -230,7 +231,7 @@ macro builtins (
$(let [$($arg),*] = to_args(args)?;)?
$body
}
fn name(&self) -> &str { stringify!($name) }
fn name(&self) -> Sym { stringify!($name).into() }
}
)*
}
@ -242,7 +243,7 @@ macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(a.get() $op b.get())),
_ => Err(Error::TypeError)
}
}

View File

@ -112,7 +112,7 @@ impl Interpret for Let {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Let { mutable: _, name: Identifier(name), ty: _, init } = self;
let init = init.as_ref().map(|i| i.interpret(env)).transpose()?;
env.insert(name, init);
env.insert(*name, init);
Ok(ConValue::Empty)
}
}
@ -172,7 +172,7 @@ impl Interpret for Assign {
};
// Get the initializer and the tail
let init = tail.interpret(env)?;
let target = env.get_mut(head)?;
let target = env.get_mut(*head)?;
if let AssignKind::Plain = op {
use std::mem::discriminant as variant;
@ -187,7 +187,7 @@ impl Interpret for Assign {
return Ok(ConValue::Empty);
}
let Some(target) = target else {
return Err(Error::NotInitialized(head.into()));
return Err(Error::NotInitialized(*head));
};
match op {
@ -302,9 +302,9 @@ impl Interpret for Unary {
let Unary { kind, tail } = self;
let operand = tail.interpret(env)?;
match kind {
UnaryKind::Deref => env.call("deref", &[operand]),
UnaryKind::Neg => env.call("neg", &[operand]),
UnaryKind::Not => env.call("not", &[operand]),
UnaryKind::Deref => env.call("deref".into(), &[operand]),
UnaryKind::Neg => env.call("neg".into(), &[operand]),
UnaryKind::Not => env.call("not".into(), &[operand]),
UnaryKind::At => {
println!("{operand}");
Ok(operand)
@ -335,7 +335,7 @@ impl Interpret for Path {
if parts.len() == 1 {
match parts.last().expect("parts should not be empty") {
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"),
PathPart::Ident(Identifier(s)) => env.get(s),
PathPart::Ident(Identifier(name)) => env.get(*name),
}
} else {
todo!("Path navigation!")
@ -469,7 +469,7 @@ impl Interpret for For {
loop {
let mut env = env.frame("loop variable");
if let Some(loop_var) = bounds.next() {
env.insert(name, Some(loop_var.into()));
env.insert(*name, Some(loop_var.into()));
match pass.interpret(&mut env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,

View File

@ -2,6 +2,7 @@
#![warn(clippy::all)]
#![feature(decl_macro)]
use cl_ast::Sym;
use env::Environment;
use error::{Error, IResult};
use interpret::Interpret;
@ -13,7 +14,7 @@ pub trait Callable: std::fmt::Debug {
/// The Callable is responsible for checking the argument count and validating types
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
/// Returns the common name of this identifier.
fn name(&self) -> &str;
fn name(&self) -> Sym;
}
/// [BuiltIn]s are [Callable]s with bespoke definitions
@ -25,6 +26,8 @@ pub mod temp_type_impl {
//! Temporary implementations of Conlang values
//!
//! The most permanent fix is a temporary one.
use cl_ast::Sym;
use super::{
error::{Error, IResult},
function::Function,
@ -50,7 +53,7 @@ pub mod temp_type_impl {
/// A unicode character
Char(char),
/// A string
String(Rc<str>),
String(Sym),
/// A reference
Ref(Rc<ConValue>),
/// An Array
@ -120,11 +123,11 @@ pub mod temp_type_impl {
}
impl Callable for ConValue {
fn name(&self) -> &str {
fn name(&self) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::BuiltIn(func) => func.name(),
_ => "",
_ => "".into(),
}
}
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
@ -145,7 +148,7 @@ pub mod temp_type_impl {
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(a.get() $op b.get())),
_ => Err(Error::TypeError)
}
}
@ -162,10 +165,16 @@ pub mod temp_type_impl {
fn from(value: $T) -> Self { $v(value.into()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::String(*value)
}
}
from! {
Integer => ConValue::Int,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::String,
&str => ConValue::String,
String => ConValue::String,
Rc<str> => ConValue::String,
@ -202,7 +211,7 @@ pub mod temp_type_impl {
Add: add = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b).into(),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
@ -302,7 +311,7 @@ pub mod function {
//! Represents a block of code which lives inside the Interpreter
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Identifier, Param};
use cl_ast::{Function as FnDecl, Identifier, Param, Sym};
use std::rc::Rc;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
@ -323,8 +332,8 @@ pub mod function {
}
impl Callable for Function {
fn name(&self) -> &str {
let FnDecl { name: Identifier(ref name), .. } = *self.decl;
fn name(&self) -> Sym {
let FnDecl { name: Identifier(name), .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
@ -334,12 +343,12 @@ pub mod function {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(name.into()));
return Err(Error::NotDefined(*name));
};
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name: Identifier(name) }, value) in bind.iter().zip(args) {
frame.insert(name, Some(value.clone()));
frame.insert(*name, Some(value.clone()));
}
match body.interpret(&mut frame) {
Err(Error::Return(value)) => Ok(value),
@ -361,14 +370,14 @@ pub mod env {
temp_type_impl::ConValue,
BuiltIn, Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Identifier};
use cl_ast::{Function as FnDecl, Identifier, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
};
type StackFrame = HashMap<String, Option<ConValue>>;
type StackFrame = HashMap<Sym, Option<ConValue>>;
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
@ -404,10 +413,8 @@ pub mod env {
}
}
}
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<String, Option<ConValue>> {
from.iter()
.map(|&v| (v.name().into(), Some(v.into())))
.collect()
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<Sym, Option<ConValue>> {
from.iter().map(|&v| (v.name(), Some(v.into()))).collect()
}
impl Environment {
@ -425,7 +432,7 @@ pub mod env {
/// Calls a function inside the interpreter's scope,
/// and returns the result
pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult<ConValue> {
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
// FIXME: Clone to satisfy the borrow checker
let function = self.get(name)?.clone();
function.call(self, args)
@ -439,39 +446,39 @@ pub mod env {
/// Resolves a variable mutably.
///
/// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, id: &str) -> IResult<&mut Option<ConValue>> {
pub fn get_mut(&mut self, id: Sym) -> IResult<&mut Option<ConValue>> {
for (frame, _) in self.frames.iter_mut().rev() {
if let Some(var) = frame.get_mut(id) {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
Err(Error::NotDefined(id.into()))
Err(Error::NotDefined(id))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: &str) -> IResult<ConValue> {
pub fn get(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(id) {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id.into())),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotDefined(id.into()))
Err(Error::NotDefined(id))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: &str, value: Option<ConValue>) {
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id.into(), value);
frame.insert(id, value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name: Identifier(name), .. } = decl;
let (name, function) = (name.clone(), Some(Function::new(decl).into()));
let (name, function) = (name, Some(Function::new(decl).into()));
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(name, function);
frame.insert(*name, function);
}
}
}
@ -525,6 +532,8 @@ pub mod env {
pub mod error {
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::Sym;
use super::temp_type_impl::ConValue;
pub type IResult<T> = Result<T, Error>;
@ -556,9 +565,9 @@ pub mod error {
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(String),
NotDefined(Sym),
/// A name was defined but not initialized
NotInitialized(String),
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments

View File

@ -127,7 +127,7 @@ mod macros {
}
pub macro env_ne($env:ident.$var:ident, $expr:expr) {{
let evaluated = $env.get(stringify!($var))
let evaluated = $env.get(stringify!($var).into())
.expect(stringify!($var should be defined and initialized));
if !conv_cmp!(neq, evaluated, $expr) {
panic!("assertion {} ({evaluated}) != {} failed.", stringify!($var), stringify!($expr))
@ -135,7 +135,7 @@ mod macros {
}}
pub macro env_eq($env:ident.$var:ident, $expr:expr) {{
let evaluated = $env.get(stringify!($var))
let evaluated = $env.get(stringify!($var).into())
.expect(stringify!($var should be defined and initialized));
if !conv_cmp!(eq, evaluated, $expr) {
panic!("assertion {} ({evaluated}) == {} failed.", stringify!($var), stringify!($expr))
@ -190,7 +190,7 @@ mod fn_declarations {
"fn empty_fn () {\n \n}",
format!(
"{}",
env.get("empty_fn")
env.get("empty_fn".into())
.expect(stringify!(empty_fn should be defined and initialized))
)
)

View File

@ -64,7 +64,8 @@ impl Fold for ModuleInliner {
/// Traverses down the module tree, entering ever nested directories
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
self.path.push(&name.0); // cd ./name
let sym = name.0.get().expect("Could not get name!");
self.path.push(sym); // cd ./name
let kind = self.fold_module_kind(kind);

View File

@ -471,7 +471,7 @@ pub mod yamlify {
}
impl Yamlify for Fielder {
fn yaml(&self, y: &mut Yamler) {
let Self { name: Identifier(name), init } = self;
let Self { name, init } = self;
y.key("Fielder").pair("name", name).pair("init", init);
}
}

View File

@ -45,10 +45,7 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn load_file(
env: &mut Environment,
path: impl AsRef<Path>,
) -> Result<ConValue, Box<dyn Error>> {
fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue, Box<dyn Error>> {
let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).file()?;
Ok(env.eval(&code)?)
@ -79,8 +76,8 @@ fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
ConValue::Empty => {}
ret => println!("{ret}"),
}
if env.get("main").is_ok() {
match env.call("main", &[])? {
if env.get("main".into()).is_ok() {
match env.call("main".into(), &[])? {
ConValue::Empty => {}
ret => println!("{ret}"),
}

View File

@ -1,4 +1,4 @@
//! A global intern pool for strings, represented by the [GlobalSym] symbol
//! A global intern pool for strings, represented by the [Sym] symbol
use super::{intern::Interner, symbol::Symbol};
use std::{
@ -7,19 +7,19 @@ use std::{
sync::{OnceLock, RwLock},
};
/// Holds a globally accessible [Interner] which uses [GlobalSym] as its [Symbol]
static GLOBAL_INTERNER: OnceLock<RwLock<Interner<GlobalSym>>> = OnceLock::new();
/// Holds a globally accessible [Interner] which uses [Sym] as its [Symbol]
static GLOBAL_INTERNER: OnceLock<RwLock<Interner<Sym>>> = OnceLock::new();
/// A unique identifier corresponding to a particular interned [String].
/// A unique identifier corresponding to a particular globally-interned [String].
///
/// Copies of that string can be obtained with [GlobalSym::get] or [String::try_from].
/// Copies of that string can be obtained with [Sym::get] or [String::try_from].
///
/// New strings can be interned with [GlobalSym::new] or [GlobalSym::from]
/// New strings can be interned with [Sym::new] or [Sym::from]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GlobalSym(NonZeroU32);
pub struct Sym(NonZeroU32);
impl GlobalSym {
/// Gets the interned [GlobalSym] for the given value, or interns a new one.
impl Sym {
/// Gets the interned [Sym] for the given value, or interns a new one.
///
/// # Blocks
/// This conversion blocks if the Global Interner lock is held.
@ -33,25 +33,25 @@ impl GlobalSym {
.expect("global interner should not be poisoned in another thread")
.get_or_insert(value)
}
/// Gets a [GlobalSym] associated with the given string, if one already exists
/// Gets a [Sym] associated with the given string, if one already exists
pub fn try_from_str(value: &str) -> Option<Self> {
GLOBAL_INTERNER.get()?.read().ok()?.get(value)
}
/// Gets a copy of the value of the [GlobalSym]
/// Gets a copy of the value of the [Sym]
// TODO: Make this copy-less
pub fn get(self) -> Option<String> {
String::try_from(self).ok()
}
/// Looks up the string associated with this [GlobalSym],
/// Looks up the string associated with this [Sym],
/// and performs a transformation on it if it exists.
pub fn map<T>(&self, f: impl Fn(&str) -> T) -> Option<T> {
Some(f(GLOBAL_INTERNER.get()?.read().ok()?.get_str(*self)?))
}
}
impl Display for GlobalSym {
impl Display for Sym {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Some(interner) = GLOBAL_INTERNER.get() else {
return write!(f, "[sym@{} (uninitialized)]", self.0);
@ -66,7 +66,7 @@ impl Display for GlobalSym {
}
}
impl Symbol for GlobalSym {
impl Symbol for Sym {
const MAX: usize = u32::MAX as usize - 1;
fn try_from_usize(value: usize) -> Option<Self> {
Some(Self(NonZeroU32::try_from_usize(value)?))
@ -76,7 +76,7 @@ impl Symbol for GlobalSym {
}
}
impl<T: AsRef<str>> From<T> for GlobalSym {
impl<T: AsRef<str>> From<T> for Sym {
/// Converts to this type from the input type.
///
/// # Blocks
@ -89,36 +89,37 @@ impl<T: AsRef<str>> From<T> for GlobalSym {
}
}
impl TryFrom<GlobalSym> for String {
type Error = GlobalSymError;
impl TryFrom<Sym> for String {
type Error = SymError;
fn try_from(value: GlobalSym) -> Result<Self, Self::Error> {
fn try_from(value: Sym) -> Result<Self, Self::Error> {
let Some(interner) = GLOBAL_INTERNER.get() else {
Err(GlobalSymError::Uninitialized)?
Err(SymError::Uninitialized)?
};
let Ok(interner) = interner.write() else {
Err(GlobalSymError::Poisoned)?
Err(SymError::Poisoned)?
};
match interner.get_str(value) {
None => Err(GlobalSymError::Unseen(value)),
None => Err(SymError::Unseen(value)),
Some(string) => Ok(string.into()),
}
}
}
/// Describes an error in [Sym] to [String] lookup
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GlobalSymError {
pub enum SymError {
Uninitialized,
Poisoned,
Unseen(GlobalSym),
Unseen(Sym),
}
impl std::error::Error for GlobalSymError {}
impl Display for GlobalSymError {
impl std::error::Error for SymError {}
impl Display for SymError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GlobalSymError::Uninitialized => "String pool was not initialized".fmt(f),
GlobalSymError::Poisoned => "String pool was held by panicking thread".fmt(f),
GlobalSymError::Unseen(sym) => {
SymError::Uninitialized => "String pool was not initialized".fmt(f),
SymError::Poisoned => "String pool was held by panicking thread".fmt(f),
SymError::Unseen(sym) => {
write!(f, "Symbol {sym:?} not present in String pool")
}
}

View File

@ -3,32 +3,32 @@ use super::*;
#[test]
fn globalsym_from_returns_unique_value_for_unique_keys() {
let foo_bar = GlobalSym::from("foo_bar");
let foo_baz = GlobalSym::from("foo_baz");
let foo_bar = Sym::from("foo_bar");
let foo_baz = Sym::from("foo_baz");
assert_ne!(foo_bar, foo_baz);
assert_eq!(foo_bar, GlobalSym::from("foo_bar"));
assert_eq!(foo_baz, GlobalSym::from("foo_baz"));
assert_eq!(foo_bar, Sym::from("foo_bar"));
assert_eq!(foo_baz, Sym::from("foo_baz"));
}
#[test]
fn try_from_str_returns_none_before_init() {
if let Some(value) = GlobalSym::try_from_str("") {
if let Some(value) = Sym::try_from_str("") {
panic!("{value}")
}
}
#[test]
fn try_from_str_returns_some_when_key_exists() {
let _ = GlobalSym::from("foo_bar");
assert!(dbg!(GlobalSym::try_from_str("foo_bar")).is_some());
let _ = Sym::from("foo_bar");
assert!(dbg!(Sym::try_from_str("foo_bar")).is_some());
}
#[test]
fn try_from_str_returns_the_same_thing_as_globalsym_from() {
let foo_bar = GlobalSym::from("foo_bar");
assert_eq!(Some(foo_bar), GlobalSym::try_from_str("foo_bar"));
let foo_bar = Sym::from("foo_bar");
assert_eq!(Some(foo_bar), Sym::try_from_str("foo_bar"));
}
#[test]
fn map_works() {
let foo_bar = GlobalSym::from("foo_bar");
let foo_bar = Sym::from("foo_bar");
assert!(foo_bar.map(|sym| "foo_bar" == sym).unwrap());
}

View File

@ -131,9 +131,6 @@ fn list_types(prj: &mut Project) {
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}");
}
}

View File

@ -35,16 +35,16 @@ pub enum DefSource<'a> {
}
impl<'a> DefSource<'a> {
pub fn name(&self) -> Option<&'a str> {
pub fn name(&self) -> Option<Sym> {
match self {
DefSource::Module(v) => Some(v.name.0.as_str()),
DefSource::Alias(v) => Some(v.to.0.as_str()),
DefSource::Enum(v) => Some(v.name.0.as_str()),
DefSource::Struct(v) => Some(v.name.0.as_str()),
DefSource::Const(v) => Some(v.name.0.as_str()),
DefSource::Static(v) => Some(v.name.0.as_str()),
DefSource::Function(v) => Some(v.name.0.as_str()),
DefSource::Local(l) => Some(l.name.0.as_str()),
DefSource::Module(v) => Some(v.name.0),
DefSource::Alias(v) => Some(v.to.0),
DefSource::Enum(v) => Some(v.name.0),
DefSource::Struct(v) => Some(v.name.0),
DefSource::Const(v) => Some(v.name.0),
DefSource::Static(v) => Some(v.name.0),
DefSource::Function(v) => Some(v.name.0),
DefSource::Local(l) => Some(l.name.0),
DefSource::Impl(_) | DefSource::Use(_) | DefSource::Ty(_) => None,
}
}

View File

@ -1,24 +1,24 @@
use crate::{key::DefID, module::Module};
use cl_ast::{Item, Meta, Visibility};
use cl_ast::{Item, Meta, Sym, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Def<'a> {
pub name: &'a str,
pub name: Sym,
pub vis: Visibility,
pub meta: &'a [Meta],
pub kind: DefKind<'a>,
pub kind: DefKind,
pub source: Option<&'a Item>,
pub module: Module<'a>,
pub module: Module,
}
mod builder_functions {
use super::*;
impl<'a> Def<'a> {
pub fn set_name(&mut self, name: &'a str) -> &mut Self {
pub fn set_name(&mut self, name: Sym) -> &mut Self {
self.name = name;
self
}
@ -30,7 +30,7 @@ mod builder_functions {
self.meta = meta;
self
}
pub fn set_kind(&mut self, kind: DefKind<'a>) -> &mut Self {
pub fn set_kind(&mut self, kind: DefKind) -> &mut Self {
self.kind = kind;
self
}
@ -38,7 +38,7 @@ mod builder_functions {
self.source = Some(source);
self
}
pub fn set_module(&mut self, module: Module<'a>) -> &mut Self {
pub fn set_module(&mut self, module: Module) -> &mut Self {
self.module = module;
self
}
@ -48,7 +48,7 @@ mod builder_functions {
impl Default for Def<'_> {
fn default() -> Self {
Self {
name: Default::default(),
name: "".into(),
vis: Visibility::Public,
meta: Default::default(),
kind: Default::default(),
@ -59,7 +59,7 @@ impl Default for Def<'_> {
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub enum DefKind<'a> {
pub enum DefKind {
/// An unevaluated definition
#[default]
Undecided,
@ -68,7 +68,7 @@ pub enum DefKind<'a> {
/// A use tree, and its parent
Use(DefID),
/// A type, such as a `type`, `struct`, or `enum`
Type(TypeKind<'a>),
Type(TypeKind),
/// A value, such as a `const`, `static`, or `fn`
Value(ValueKind),
}
@ -84,13 +84,13 @@ pub enum ValueKind {
/// A [TypeKind] represents an item in the Type Namespace
/// (a component of a [Project](crate::project::Project)).
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind<'a> {
pub enum TypeKind {
/// An alias for an already-defined type
Alias(Option<DefID>),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
/// A user-defined aromatic data type
Adt(Adt<'a>),
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(u16, DefID),
/// A contiguous view of dynamically sized memory
@ -113,16 +113,16 @@ pub enum TypeKind<'a> {
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt<'a> {
pub enum Adt {
/// A union-like enum type
Enum(Vec<(&'a str, Option<DefID>)>),
Enum(Vec<(Sym, Option<DefID>)>),
/// A C-like enum
CLikeEnum(Vec<(&'a str, u128)>),
CLikeEnum(Vec<(Sym, u128)>),
/// An enum with no fields, which can never be constructed
FieldlessEnum,
/// A structural product type with named members
Struct(Vec<(&'a str, Visibility, DefID)>),
Struct(Vec<(Sym, Visibility, DefID)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, DefID)>),
/// A structural product type of neither named nor unnamed members
@ -130,7 +130,7 @@ pub enum Adt<'a> {
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(&'a str, DefID)>),
Union(Vec<(Sym, DefID)>),
}
/// The set of compiler-intrinsic types.

View File

@ -44,7 +44,7 @@ impl Display for Def<'_> {
}
}
impl Display for DefKind<'_> {
impl Display for DefKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DefKind::Undecided => write!(f, "undecided"),
@ -66,7 +66,7 @@ impl std::fmt::Display for ValueKind {
}
}
impl Display for TypeKind<'_> {
impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeKind::Alias(def) => match def {
@ -99,7 +99,7 @@ impl Display for TypeKind<'_> {
}
}
impl Display for Adt<'_> {
impl Display for Adt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Adt::Enum(variants) => {

View File

@ -1,5 +1,6 @@
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
use cl_ast::Sym;
use cl_structures::intern_pool::InternKey;
use crate::key::DefID;
@ -8,14 +9,14 @@ 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<'a> {
pub struct Module {
pub parent: Option<DefID>,
pub types: HashMap<&'a str, DefID>,
pub values: HashMap<&'a str, DefID>,
pub types: HashMap<Sym, DefID>,
pub values: HashMap<Sym, DefID>,
pub imports: Vec<DefID>,
}
impl<'a> Module<'a> {
impl Module {
pub fn new(parent: DefID) -> Self {
Self { parent: Some(parent), ..Default::default() }
}
@ -23,29 +24,29 @@ impl<'a> Module<'a> {
Self { parent, ..Default::default() }
}
pub fn get(&self, name: &'a str) -> (Option<DefID>, Option<DefID>) {
pub fn get(&self, name: Sym) -> (Option<DefID>, Option<DefID>) {
(self.get_type(name), self.get_value(name))
}
pub fn get_type(&self, name: &'a str) -> Option<DefID> {
self.types.get(name).copied()
pub fn get_type(&self, name: Sym) -> Option<DefID> {
self.types.get(&name).copied()
}
pub fn get_value(&self, name: &'a str) -> Option<DefID> {
self.values.get(name).copied()
pub fn get_value(&self, name: Sym) -> Option<DefID> {
self.values.get(&name).copied()
}
/// Inserts a type with the provided [name](str) and [id](DefID)
pub fn insert_type(&mut self, name: &'a str, id: DefID) -> Option<DefID> {
pub fn insert_type(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.types.insert(name, id)
}
/// Inserts a value with the provided [name](str) and [id](DefID)
pub fn insert_value(&mut self, name: &'a str, id: DefID) -> Option<DefID> {
pub fn insert_value(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.values.insert(name, id)
}
}
impl std::fmt::Display for Module<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl std::fmt::Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self { parent, types, values, imports } = self;
if let Some(parent) = parent {
writeln!(f, "Parent: {}", parent.get())?;

View File

@ -50,8 +50,8 @@ impl<'a> NameCollectable<'a> for Module {
let Self { name: Identifier(name), kind } = self;
let module =
c.pool
.insert(Def { name, module: Mod::new(parent), ..Default::default() });
c[parent].module.types.insert(name, module);
.insert(Def { name: *name, module: Mod::new(parent), ..Default::default() });
c[parent].module.types.insert(*name, module);
match kind {
ModuleKind::Inline(file) => file.collect(c, module)?,
@ -87,10 +87,10 @@ 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;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
c[parent].module.types.insert(*name, id);
Ok(id)
}
}
@ -98,10 +98,10 @@ impl<'a> NameCollectable<'a> for Enum {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Enum { name: Identifier(name), .. } = self;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
c[parent].module.types.insert(*name, id);
Ok(id)
}
}
@ -109,10 +109,10 @@ impl<'a> NameCollectable<'a> for Struct {
fn collect(&'a self, c: &mut Prj<'a>, parent: DefID) -> Result<DefID, &'static str> {
let Struct { name: Identifier(name), .. } = self;
let def = Def { name, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.types.insert(name, id);
c[parent].module.types.insert(*name, id);
Ok(id)
}
}
@ -122,10 +122,10 @@ impl<'a> NameCollectable<'a> for Const {
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
c[parent].module.values.insert(*name, id);
init.collect(c, id)?;
Ok(id)
@ -137,10 +137,10 @@ impl<'a> NameCollectable<'a> for Static {
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
c[parent].module.values.insert(*name, id);
init.collect(c, id)?;
Ok(id)
@ -152,10 +152,10 @@ impl<'a> NameCollectable<'a> for Function {
let kind = DefKind::Undecided;
let def = Def { name, kind, module: Mod::new(parent), ..Default::default() };
let def = Def { name: *name, kind, module: Mod::new(parent), ..Default::default() };
let id = c.pool.insert(def);
c[parent].module.values.insert(name, id);
c[parent].module.values.insert(*name, id);
body.collect(c, id)?;
Ok(id)

View File

@ -18,7 +18,7 @@ use self::evaluate::EvaluableTypeExpression;
pub struct Project<'a> {
pub pool: Pool<Def<'a>, DefID>,
/// Stores anonymous tuples, function pointer types, etc.
pub anon_types: HashMap<TypeKind<'a>, DefID>,
pub anon_types: HashMap<TypeKind, DefID>,
pub root: DefID,
}
@ -32,21 +32,21 @@ impl Default for Project<'_> {
fn default() -> Self {
let mut pool = Pool::default();
let root = pool.insert(Def {
name: "🌳 root 🌳",
name: "🌳 root 🌳".into(),
kind: DefKind::Type(TypeKind::Module),
..Default::default()
});
// Insert the Never(!) type
let never = pool.insert(Def {
name: "!",
name: "!".into(),
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::Never),
module: module::Module::new(root),
..Default::default()
});
let empty = pool.insert(Def {
name: "()",
name: "()".into(),
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::Empty),
module: module::Module::new(root),
@ -54,7 +54,7 @@ impl Default for Project<'_> {
});
// TODO: Self is not a real type!
let selfty = pool.insert(Def {
name: "Self",
name: "Self".into(),
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::SelfTy),
module: module::Module::new(root),
@ -92,11 +92,11 @@ impl<'a> Project<'a> {
match path.as_ref() {
[] => Some((Some(within), None, path)),
[PathPart::Ident(Identifier(name))] => {
let (ty, val) = self[within].module.get(name);
let (ty, val) = self[within].module.get(*name);
Some((ty, val, path.pop_front()?))
}
[PathPart::Ident(Identifier(name)), ..] => {
let ty = self[within].module.get_type(name)?;
let ty = self[within].module.get_type(*name)?;
self.get(path.pop_front()?, ty)
}
[PathPart::SelfKw, ..] => self.get(path.pop_front()?, within),
@ -114,7 +114,7 @@ impl<'a> Project<'a> {
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.as_str()) {
PathPart::Ident(Identifier(name)) => match module.types.get(name) {
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
None => Some((within, path)),
},
@ -127,7 +127,7 @@ impl<'a> Project<'a> {
pub fn get_value<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> {
match path.front()? {
PathPart::Ident(Identifier(name)) => Some((
self[within].module.values.get(name.as_str()).copied()?,
self[within].module.values.get(name).copied()?,
path.pop_front()?,
)),
_ => None,
@ -139,7 +139,7 @@ impl<'a> Project<'a> {
/// Assumes `kind` uniquely identifies the type!
pub fn insert_anonymous_type(
&mut self,
kind: TypeKind<'a>,
kind: TypeKind,
def: impl FnOnce() -> Def<'a>,
) -> DefID {
*(self
@ -171,7 +171,7 @@ pub mod evaluate {
//! or an intermediate result of expression evaluation.
use super::*;
use cl_ast::Ty;
use cl_ast::{Sym, Ty};
/// Things that can be evaluated as a type expression
pub trait EvaluableTypeExpression {
@ -203,8 +203,7 @@ pub mod evaluate {
if path.is_empty() {
id
} else {
let (id, path) =
prj.get_value(path, id).ok_or("Failed to get value")?;
let (id, path) = prj.get_value(path, id).ok_or("Failed to get value")?;
path.is_empty()
.then_some(id)
.ok_or("Path not fully resolved")?
@ -220,7 +219,7 @@ pub mod evaluate {
}
}
impl EvaluableTypeExpression for str {
impl EvaluableTypeExpression for Sym {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
@ -298,7 +297,7 @@ pub mod evaluate {
.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),
PathPart::Ident(Identifier(name)) => name.evaluate(prj, parent),
}
}
}

View File

@ -45,7 +45,7 @@ pub trait TypeResolvable<'a> {
}
impl<'a> TypeResolvable<'a> for Item {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { attrs: Attrs { meta }, kind, .. } = self;
for meta in meta {
@ -58,11 +58,12 @@ impl<'a> TypeResolvable<'a> for Item {
}
impl<'a> TypeResolvable<'a> for Meta {
type Out = DefKind<'a>;
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { name: Identifier(name), kind } = self;
let name = name.get().unwrap_or_default();
match (name.as_str(), kind) {
("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => Ok(DefKind::Type(
TypeKind::Intrinsic(intrinsic.parse().map_err(|_| "unknown intrinsic type")?),
@ -76,7 +77,7 @@ impl<'a> TypeResolvable<'a> for Meta {
}
impl<'a> TypeResolvable<'a> for ItemKind {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
if prj[id].source.map(|s| &s.kind as *const _) != Some(self as *const _) {
return Err("id is not self!");
@ -96,7 +97,7 @@ impl<'a> TypeResolvable<'a> for ItemKind {
}
impl<'a> TypeResolvable<'a> for Module {
type Out = DefKind<'a>;
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
Ok(DefKind::Type(TypeKind::Module))
@ -104,7 +105,7 @@ impl<'a> TypeResolvable<'a> for Module {
}
impl<'a> TypeResolvable<'a> for Impl {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
@ -122,7 +123,7 @@ impl<'a> TypeResolvable<'a> for Impl {
}
impl<'a> TypeResolvable<'a> for Use {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
todo!("Resolve types for {self} with ID {id} in {prj:?}")
@ -130,7 +131,7 @@ impl<'a> TypeResolvable<'a> for Use {
}
impl<'a> TypeResolvable<'a> for Alias {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
@ -149,7 +150,7 @@ impl<'a> TypeResolvable<'a> for Alias {
}
impl<'a> TypeResolvable<'a> for Enum {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { name: _, kind } = self;
@ -159,7 +160,7 @@ impl<'a> TypeResolvable<'a> for Enum {
let mut fields = vec![];
for v @ Variant { name: Identifier(name), kind: _ } in v {
let id = v.resolve_type(prj, id)?;
fields.push((name.as_str(), id))
fields.push((*name, id))
}
Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields))))
}
@ -185,7 +186,7 @@ impl<'a> TypeResolvable<'a> for Variant {
};
let def = Def {
name,
name: *name,
kind: DefKind::Type(TypeKind::Adt(adt)),
module: module::Module::new(id),
..Default::default()
@ -193,14 +194,14 @@ impl<'a> TypeResolvable<'a> for Variant {
let new_id = prj.pool.insert(def);
// Insert the struct variant type into the enum's namespace
prj[id].module.types.insert(name, new_id);
prj[id].module.types.insert(*name, new_id);
Ok(Some(new_id))
}
}
impl<'a> TypeResolvable<'a> for Struct {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Self { name: _, kind } = self;
@ -225,7 +226,7 @@ impl<'a> TypeResolvable<'a> for Struct {
}
impl<'a> TypeResolvable<'a> for StructMember {
type Out = (&'a str, Visibility, DefID);
type Out = (Sym, Visibility, DefID);
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
@ -235,12 +236,12 @@ impl<'a> TypeResolvable<'a> for StructMember {
.evaluate(prj, parent)
.map_err(|_| "Invalid type while resolving StructMember")?;
Ok((name, *vis, ty))
Ok((*name, *vis, ty))
}
}
impl<'a> TypeResolvable<'a> for Const {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Self { ty, .. } = self;
@ -251,7 +252,7 @@ impl<'a> TypeResolvable<'a> for Const {
}
}
impl<'a> TypeResolvable<'a> for Static {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
@ -264,7 +265,7 @@ impl<'a> TypeResolvable<'a> for Static {
}
impl<'a> TypeResolvable<'a> for Function {
type Out = DefKind<'a>;
type Out = DefKind;
fn resolve_type(&'a self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);

View File

@ -1,5 +1,5 @@
//! WIP use-item importer. This performs eager import resolution on the AST
//!
//!
//! # TODOs:
//! - [ ] Resolve imports using a graph traversal rather than linear iteration
//! - [ ] Separate imported items from natively declared items
@ -7,7 +7,6 @@
//! - [ ] Report errors in a meaningful way
//! - [ ] Lazy import resolution using graph-edge traversal during name lookup?
//! - It doesn't seem to me like the imports in a given scope *can change*.
//!
#![allow(unused)]
use std::fmt::format;
@ -74,20 +73,15 @@ impl<'a> Project<'a> {
Ok(())
}
pub fn visit_use_leaf(
&mut self,
part: &'a Identifier,
parent: DefID,
c: DefID,
) -> UseResult {
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,
from: &Sym,
name: &Sym,
parent: DefID,
c: DefID,
) -> UseResult {
@ -100,12 +94,12 @@ impl<'a> Project<'a> {
let parent = &mut self[parent].module;
if let Some(tid) = tid {
parent.types.insert(name, tid);
parent.types.insert(*name, tid);
imported = true;
}
if let Some(vid) = vid {
parent.values.insert(name, vid);
parent.values.insert(*name, vid);
imported = true;
}