conlang: Move all cl-libs into the compiler directory
This commit is contained in:
191
compiler/cl-structures/src/intern_pool.rs
Normal file
191
compiler/cl-structures/src/intern_pool.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
//! Trivially-copyable, easily comparable typed indices, and a [Pool] to contain them
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use cl_structures::intern_pool::*;
|
||||
//! // first, create a new InternKey type (this ensures type safety)
|
||||
//! make_intern_key!{
|
||||
//! NumbersKey
|
||||
//! }
|
||||
//!
|
||||
//! // then, create a pool with that type
|
||||
//! let mut numbers: Pool<i32, NumbersKey> = Pool::new();
|
||||
//! let first = numbers.insert(1);
|
||||
//! let second = numbers.insert(2);
|
||||
//! let third = numbers.insert(3);
|
||||
//!
|
||||
//! // You can access elements immutably with `get`
|
||||
//! assert_eq!(Some(&3), numbers.get(third));
|
||||
//! assert_eq!(Some(&2), numbers.get(second));
|
||||
//! // or by indexing
|
||||
//! assert_eq!(1, numbers[first]);
|
||||
//!
|
||||
//! // Or mutably
|
||||
//! *numbers.get_mut(first).unwrap() = 100000;
|
||||
//!
|
||||
//! assert_eq!(Some(&100000), numbers.get(first));
|
||||
//! ```
|
||||
|
||||
/// Creates newtype indices over [`usize`] for use as [Pool] keys.
|
||||
#[macro_export]
|
||||
macro_rules! make_intern_key {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
|
||||
$(#[$meta])*
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct $name(usize);
|
||||
|
||||
impl $crate::intern_pool::InternKey for $name {
|
||||
#[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.\n")]
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided value should be within the bounds of its associated container
|
||||
unsafe fn from_raw_unchecked(value: usize) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
fn get(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl From< $name > for usize {
|
||||
fn from(value: $name) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
)*}}
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
pub use make_intern_key;
|
||||
|
||||
use self::iter::InternKeyIter;
|
||||
|
||||
/// An index into a [Pool]. For full type-safety,
|
||||
/// there should be a unique [InternKey] for each [Pool]
|
||||
pub trait InternKey: std::fmt::Debug {
|
||||
/// Constructs an [`InternKey`] from a [`usize`] without checking bounds.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided value should be within the bounds of its associated container.
|
||||
// ID::from_raw_unchecked here isn't *actually* unsafe, since bounds should always be
|
||||
// checked, however, the function has unverifiable preconditions.
|
||||
unsafe fn from_raw_unchecked(value: usize) -> Self;
|
||||
/// Gets the index of the [`InternKey`] by value
|
||||
fn get(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Pool<T, ID: InternKey> {
|
||||
pool: Vec<T>,
|
||||
id_type: std::marker::PhantomData<ID>,
|
||||
}
|
||||
|
||||
impl<T, ID: InternKey> Pool<T, ID> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn get(&self, index: ID) -> Option<&T> {
|
||||
self.pool.get(index.get())
|
||||
}
|
||||
pub fn get_mut(&mut self, index: ID) -> Option<&mut T> {
|
||||
self.pool.get_mut(index.get())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.pool.iter()
|
||||
}
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
self.pool.iter_mut()
|
||||
}
|
||||
pub fn key_iter(&self) -> iter::InternKeyIter<ID> {
|
||||
// Safety: Pool currently has pool.len() entries, and data cannot be removed
|
||||
unsafe { InternKeyIter::new(0..self.pool.len()) }
|
||||
}
|
||||
|
||||
/// Constructs an [ID](InternKey) from a [usize], if it's within bounds
|
||||
#[doc(hidden)]
|
||||
pub fn try_key_from(&self, value: usize) -> Option<ID> {
|
||||
(value < self.pool.len()).then(|| unsafe { ID::from_raw_unchecked(value) })
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, value: T) -> ID {
|
||||
let id = self.pool.len();
|
||||
self.pool.push(value);
|
||||
|
||||
// Safety: value was pushed to `self.pool[id]`
|
||||
unsafe { ID::from_raw_unchecked(id) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, ID: InternKey> Default for Pool<T, ID> {
|
||||
fn default() -> Self {
|
||||
Self { pool: vec![], id_type: std::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, ID: InternKey> Index<ID> for Pool<T, ID> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: ID) -> &Self::Output {
|
||||
match self.pool.get(index.get()) {
|
||||
None => panic!("Index {:?} out of bounds in pool!", index),
|
||||
Some(value) => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, ID: InternKey> IndexMut<ID> for Pool<T, ID> {
|
||||
fn index_mut(&mut self, index: ID) -> &mut Self::Output {
|
||||
match self.pool.get_mut(index.get()) {
|
||||
None => panic!("Index {:?} out of bounds in pool!", index),
|
||||
Some(value) => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod iter {
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
|
||||
use super::InternKey;
|
||||
|
||||
/// Iterates over the keys of a [Pool](super::Pool) independently of the pool itself.
|
||||
///
|
||||
/// This is guaranteed to never overrun the length of the pool,
|
||||
/// but is *NOT* guaranteed to iterate over all elements of the pool
|
||||
/// if the pool is extended during iteration.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct InternKeyIter<ID: InternKey> {
|
||||
range: Range<usize>,
|
||||
_id: PhantomData<ID>,
|
||||
}
|
||||
|
||||
impl<ID: InternKey> InternKeyIter<ID> {
|
||||
/// Creates a new [InternKeyIter] producing the given [InternKey]
|
||||
///
|
||||
/// # Safety:
|
||||
/// - Range must not exceed bounds of the associated [Pool](super::Pool)
|
||||
/// - Items must not be removed from the pool
|
||||
/// - Items must be contiguous within the pool
|
||||
pub(super) unsafe fn new(range: Range<usize>) -> Self {
|
||||
Self { range, _id: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<ID: InternKey> Iterator for InternKeyIter<ID> {
|
||||
type Item = ID;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Safety: InternKeyIter can only be created by InternKeyIter::new()
|
||||
Some(unsafe { ID::from_raw_unchecked(self.range.next()?) })
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.range.size_hint()
|
||||
}
|
||||
}
|
||||
impl<ID: InternKey> DoubleEndedIterator for InternKeyIter<ID> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
// Safety: see above
|
||||
Some(unsafe { ID::from_raw_unchecked(self.range.next_back()?) })
|
||||
}
|
||||
}
|
||||
impl<ID: InternKey> ExactSizeIterator for InternKeyIter<ID> {}
|
||||
}
|
||||
14
compiler/cl-structures/src/lib.rs
Normal file
14
compiler/cl-structures/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
//! # Universally useful structures
|
||||
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
|
||||
//! - [Loc](struct@span::Loc): Stores the index in a stream
|
||||
#![warn(clippy::all)]
|
||||
#![feature(inline_const, dropck_eyepatch, decl_macro)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod span;
|
||||
|
||||
pub mod tree;
|
||||
|
||||
pub mod stack;
|
||||
|
||||
pub mod intern_pool;
|
||||
38
compiler/cl-structures/src/span.rs
Normal file
38
compiler/cl-structures/src/span.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
//! - [struct@Span]: Stores the start and end [struct@Loc] of a notable AST node
|
||||
//! - [struct@Loc]: Stores the line/column of a notable AST node
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
/// Stores the start and end [locations](struct@Loc) within the token stream
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Span {
|
||||
pub head: Loc,
|
||||
pub tail: Loc,
|
||||
}
|
||||
pub fn Span(head: Loc, tail: Loc) -> Span {
|
||||
Span { head, tail }
|
||||
}
|
||||
|
||||
/// Stores a read-only (line, column) location in a token stream
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Loc {
|
||||
line: u32,
|
||||
col: u32,
|
||||
}
|
||||
pub fn Loc(line: u32, col: u32) -> Loc {
|
||||
Loc { line, col }
|
||||
}
|
||||
impl Loc {
|
||||
pub fn line(self) -> u32 {
|
||||
self.line
|
||||
}
|
||||
pub fn col(self) -> u32 {
|
||||
self.col
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Loc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Loc { line, col } = self;
|
||||
write!(f, "{line}:{col}:")
|
||||
}
|
||||
}
|
||||
747
compiler/cl-structures/src/stack.rs
Normal file
747
compiler/cl-structures/src/stack.rs
Normal file
@@ -0,0 +1,747 @@
|
||||
//! A contiguous collection with constant capacity.
|
||||
//!
|
||||
//! Since the capacity of a [Stack] may be [*known at compile time*](Sized),
|
||||
//! it may live on the call stack.
|
||||
//!
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Unlike a [Vec], the [Stack] doesn't grow when it reaches capacity.
|
||||
//! ```should_panic
|
||||
//! # use cl_structures::stack::*;
|
||||
//! let mut v = stack![1];
|
||||
//! v.push("This should work");
|
||||
//! v.push("This will panic!");
|
||||
//! ```
|
||||
//! To get around this limitation, the methods [try_push](Stack::try_push) and
|
||||
//! [try_insert](Stack::try_insert) are provided:
|
||||
//! ```
|
||||
//! # use cl_structures::stack::*;
|
||||
//! let mut v = stack![1];
|
||||
//! v.push("This should work");
|
||||
//! v.try_push("This should produce an err").unwrap_err();
|
||||
//! ```
|
||||
//!
|
||||
//! As the name suggests, a [Stack] enforces a stack discipline:
|
||||
//! ```
|
||||
//! # use cl_structures::stack::*;
|
||||
//! let mut v = stack![100];
|
||||
//!
|
||||
//! assert_eq!(100, v.capacity());
|
||||
//! assert_eq!(0, v.len());
|
||||
//!
|
||||
//! // Elements are pushed one at a time onto the stack
|
||||
//! v.push("foo");
|
||||
//! v.push("bar");
|
||||
//! assert_eq!(2, v.len());
|
||||
//!
|
||||
//! // The stack can be used anywhere a slice is expected
|
||||
//! assert_eq!(Some(&"foo"), v.get(0));
|
||||
//! assert_eq!(Some(&"bar"), v.last());
|
||||
//!
|
||||
//! // Elements are popped from the stack in reverse order
|
||||
//! assert_eq!(Some("bar"), v.pop());
|
||||
//! assert_eq!(Some("foo"), v.pop());
|
||||
//! assert_eq!(None, v.pop());
|
||||
//! ```
|
||||
|
||||
// yar har! here there be unsafe code! Tread carefully.
|
||||
|
||||
use core::slice;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
mem::{ManuallyDrop, MaybeUninit},
|
||||
ops::{Deref, DerefMut},
|
||||
ptr,
|
||||
};
|
||||
|
||||
/// Creates a [`stack`] containing the arguments
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creates a *full* [`Stack`] containing a list of elements
|
||||
/// ```
|
||||
/// # use cl_structures::stack::stack;
|
||||
/// let mut v = stack![1, 2, 3];
|
||||
///
|
||||
/// assert_eq!(Some(3), v.pop());
|
||||
/// assert_eq!(Some(2), v.pop());
|
||||
/// assert_eq!(Some(1), v.pop());
|
||||
/// assert_eq!(None, v.pop());
|
||||
/// ```
|
||||
///
|
||||
/// Creates a *full* [`Stack`] from a given element and size
|
||||
/// ```
|
||||
/// # use cl_structures::stack::stack;
|
||||
/// let mut v = stack![1; 2];
|
||||
///
|
||||
/// assert_eq!(Some(1), v.pop());
|
||||
/// assert_eq!(Some(1), v.pop());
|
||||
/// assert_eq!(None, v.pop());
|
||||
/// ```
|
||||
///
|
||||
/// Creates an *empty* [`Stack`] from a given size
|
||||
/// ```
|
||||
/// # use cl_structures::stack::{Stack, stack};
|
||||
/// let mut v = stack![10];
|
||||
///
|
||||
/// assert_eq!(0, v.len());
|
||||
/// assert_eq!(10, v.capacity());
|
||||
///
|
||||
/// v.push(10);
|
||||
/// assert_eq!(Some(&10), v.last());
|
||||
/// ```
|
||||
pub macro stack {
|
||||
($count:literal) => {
|
||||
Stack::<_, $count>::new()
|
||||
},
|
||||
($value:expr ; $count:literal) => {{
|
||||
let mut stack: Stack<_, $count> = Stack::new();
|
||||
for _ in 0..$count {
|
||||
stack.push($value)
|
||||
}
|
||||
stack
|
||||
}},
|
||||
($($values:expr),* $(,)?) => {
|
||||
Stack::from([$($values),*])
|
||||
}
|
||||
}
|
||||
|
||||
/// A contiguous collection with constant capacity
|
||||
pub struct Stack<T, const N: usize> {
|
||||
_data: PhantomData<T>,
|
||||
buf: [MaybeUninit<T>; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> Clone for Stack<T, N> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::new();
|
||||
for value in self.iter() {
|
||||
new.push(value.clone())
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug, const N: usize> Debug for Stack<T, N> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Default for Stack<T, N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Deref for Stack<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// Safety:
|
||||
// - We have ensured all elements from 0 to len have been initialized
|
||||
// - self.elem[0] came from a reference, and so is aligned to T
|
||||
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
|
||||
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> DerefMut for Stack<T, N> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// Safety:
|
||||
// - See Deref
|
||||
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
// requires dropck-eyepatch for elements with contravariant lifetimes
|
||||
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We have ensured that all elements in the list are
|
||||
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Extend<T> for Stack<T, N> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
for value in iter {
|
||||
self.push(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> From<[T; N]> for Stack<T, N> {
|
||||
fn from(value: [T; N]) -> Self {
|
||||
let value = ManuallyDrop::new(value);
|
||||
if std::mem::size_of::<[T; N]>() == 0 {
|
||||
// Safety: since [T; N] is zero-sized, and there are no other fields,
|
||||
// it should be okay to interpret N as Self
|
||||
unsafe { ptr::read(&N as *const _ as *const _) }
|
||||
} else {
|
||||
// Safety:
|
||||
// - `value` is ManuallyDrop, so its destructor won't run
|
||||
// - All elements are assumed to be initialized (so len is N)
|
||||
Self {
|
||||
buf: unsafe { ptr::read(&value as *const _ as *const _) },
|
||||
len: N,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Stack<T, N> {
|
||||
/// Constructs a new, empty [Stack]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v: Stack<_, 3> = Stack::new();
|
||||
///
|
||||
/// v.try_push(1).unwrap();
|
||||
/// v.try_push(2).unwrap();
|
||||
/// v.try_push(3).unwrap();
|
||||
/// // Trying to push a 4th element will fail, and return the failed element
|
||||
/// assert_eq!(4, v.try_push(4).unwrap_err());
|
||||
///
|
||||
/// assert_eq!(Some(3), v.pop());
|
||||
/// ```
|
||||
pub const fn new() -> Self {
|
||||
Self { buf: [const { MaybeUninit::uninit() }; N], len: 0, _data: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructs a new [Stack] from an array of [`MaybeUninit<T>`] and an initialized length
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Elements from `0..len` must be initialized
|
||||
/// - len must not exceed the length of the array
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// # use core::mem::MaybeUninit;
|
||||
/// let mut v = unsafe { Stack::from_raw_parts([MaybeUninit::new(100)], 1) };
|
||||
///
|
||||
/// assert_eq!(1, v.len());
|
||||
/// assert_eq!(1, v.capacity());
|
||||
/// assert_eq!(Some(100), v.pop());
|
||||
/// assert_eq!(None, v.pop());
|
||||
/// ```
|
||||
pub const unsafe fn from_raw_parts(buf: [MaybeUninit<T>; N], len: usize) -> Self {
|
||||
Self { buf, len, _data: PhantomData }
|
||||
}
|
||||
|
||||
/// Converts a [Stack] into an array of [`MaybeUninit<T>`] and the initialized length
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v: Stack<_, 10> = Stack::new();
|
||||
/// v.push(0);
|
||||
/// v.push(1);
|
||||
///
|
||||
/// let (buf, len) = v.into_raw_parts();
|
||||
///
|
||||
/// assert_eq!(0, unsafe { buf[0].assume_init() });
|
||||
/// assert_eq!(1, unsafe { buf[1].assume_init() });
|
||||
/// assert_eq!(2, len);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||
let this = ManuallyDrop::new(self);
|
||||
// Safety: since
|
||||
(unsafe { ptr::read(&this.buf) }, this.len)
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the stack's buffer
|
||||
pub const fn as_ptr(&self) -> *const T {
|
||||
self.buf.as_ptr().cast()
|
||||
}
|
||||
|
||||
/// Returns an unsafe mutable pointer to the stack's buffer
|
||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||
self.buf.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
/// Extracts a slice containing the entire vector
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
self
|
||||
}
|
||||
|
||||
/// Extracts a mutable slice containing the entire vector
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the total number of elements the stack can hold
|
||||
pub const fn capacity(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
/// Moves an existing stack into an allocation of a (potentially) different size,
|
||||
/// truncating if necessary.
|
||||
///
|
||||
/// This can be used to easily construct a half-empty stack
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// You can grow a stack to fit more elements
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let v = Stack::from([0, 1, 2, 3, 4]);
|
||||
/// assert_eq!(5, v.capacity());
|
||||
///
|
||||
/// let mut v = v.resize::<10>();
|
||||
/// assert_eq!(10, v.capacity());
|
||||
///
|
||||
/// v.push(5);
|
||||
/// ```
|
||||
///
|
||||
/// You can truncate a stack, dropping elements off the end
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let v = Stack::from([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
/// assert_eq!(8, v.capacity());
|
||||
///
|
||||
/// let v = v.resize::<5>();
|
||||
/// assert_eq!(5, v.capacity());
|
||||
/// ```
|
||||
pub fn resize<const M: usize>(mut self) -> Stack<T, M> {
|
||||
// Drop elements until new length is reached
|
||||
while self.len > M {
|
||||
drop(self.pop());
|
||||
}
|
||||
let (old, len) = self.into_raw_parts();
|
||||
let mut new: Stack<T, M> = Stack::new();
|
||||
|
||||
// Safety:
|
||||
// - new and old are separate allocations
|
||||
// - len <= M
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(old.as_ptr(), new.buf.as_mut_ptr(), len);
|
||||
}
|
||||
|
||||
new.len = len;
|
||||
new
|
||||
}
|
||||
|
||||
/// Push a new element onto the end of the stack
|
||||
///
|
||||
/// # May Panic
|
||||
///
|
||||
/// Panics if the new length exceeds capacity
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v: Stack<_, 4> = Stack::new();
|
||||
///
|
||||
/// v.push(0);
|
||||
/// v.push(1);
|
||||
/// v.push(2);
|
||||
/// v.push(3);
|
||||
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
|
||||
/// ```
|
||||
pub fn push(&mut self, value: T) {
|
||||
if self.len >= N {
|
||||
panic!("Attempted to push into full stack")
|
||||
}
|
||||
// Safety: len is confirmed to be less than capacity
|
||||
unsafe { self.push_unchecked(value) };
|
||||
}
|
||||
|
||||
/// Push a new element onto the end of the stack
|
||||
///
|
||||
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
|
||||
pub fn try_push(&mut self, value: T) -> Result<(), T> {
|
||||
if self.len >= N {
|
||||
return Err(value);
|
||||
}
|
||||
// Safety: len is confirmed to be less than capacity
|
||||
unsafe { self.push_unchecked(value) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Push a new element onto the end of the stack, without checking capacity
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// len after push must not exceed capacity N
|
||||
#[inline]
|
||||
unsafe fn push_unchecked(&mut self, value: T) {
|
||||
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) }
|
||||
self.len += 1; // post inc
|
||||
}
|
||||
|
||||
/// Pops the last element off the end of the stack, and returns it
|
||||
///
|
||||
/// Returns None if the stack is empty
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v = Stack::from([0, 1, 2, 3]);
|
||||
///
|
||||
/// assert_eq!(Some(3), v.pop());
|
||||
/// assert_eq!(Some(2), v.pop());
|
||||
/// assert_eq!(Some(1), v.pop());
|
||||
/// assert_eq!(Some(0), v.pop());
|
||||
/// assert_eq!(None, v.pop());
|
||||
/// ```
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
if self.len == 0 {
|
||||
None
|
||||
} else {
|
||||
self.len -= 1;
|
||||
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
|
||||
// therefore should not get dropped twice
|
||||
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes and returns the element at the given index,
|
||||
/// shifting other elements toward the start
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// assert_eq!(2, v.remove(2));
|
||||
/// assert_eq!(&[0, 1, 3, 4], v.as_slice());
|
||||
/// ```
|
||||
pub fn remove(&mut self, index: usize) -> T {
|
||||
if index >= self.len {
|
||||
panic!("Index {index} exceeded length {}", self.len)
|
||||
}
|
||||
let len = self.len - 1;
|
||||
let base = self.as_mut_ptr();
|
||||
let out = unsafe { ptr::read(base.add(index)) };
|
||||
|
||||
unsafe { ptr::copy(base.add(index + 1), base.add(index), len - index) };
|
||||
self.len = len;
|
||||
out
|
||||
}
|
||||
|
||||
/// Removes and returns the element at the given index,
|
||||
/// swapping it with the last element.
|
||||
///
|
||||
/// # May Panic
|
||||
///
|
||||
/// Panics if `index >= len`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// assert_eq!(2, v.swap_remove(2));
|
||||
///
|
||||
/// assert_eq!(&[0, 1, 4, 3], v.as_slice());
|
||||
/// ```
|
||||
pub fn swap_remove(&mut self, index: usize) -> T {
|
||||
if index >= self.len {
|
||||
panic!("Index {index} exceeds length {}", self.len);
|
||||
}
|
||||
let len = self.len - 1;
|
||||
let ptr = self.as_mut_ptr();
|
||||
let out = unsafe { ptr::read(ptr.add(index)) };
|
||||
|
||||
unsafe { ptr::copy(ptr.add(len), ptr.add(index), 1) };
|
||||
self.len = len;
|
||||
out
|
||||
}
|
||||
|
||||
/// Inserts an element at position `index` in the stack,
|
||||
/// shifting all elements after it to the right.
|
||||
///
|
||||
/// # May Panic
|
||||
///
|
||||
/// Panics if `index > len` or [`self.is_full()`](Stack::is_full)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
/// let mut v = Stack::from([0, 1, 2, 3, 4]).resize::<6>();
|
||||
///
|
||||
/// v.insert(3, 0xbeef);
|
||||
/// assert_eq!(&[0, 1, 2, 0xbeef, 3, 4], v.as_slice());
|
||||
/// ```
|
||||
pub fn insert(&mut self, index: usize, data: T) {
|
||||
if index > self.len {
|
||||
panic!("Index {index} exceeded length {}", self.len)
|
||||
}
|
||||
if self.is_full() {
|
||||
panic!("Attempted to insert into full stack")
|
||||
}
|
||||
unsafe { self.insert_unchecked(index, data) };
|
||||
}
|
||||
|
||||
/// Attempts to insert an element at position `index` in the stack,
|
||||
/// shifting all elements after it to the right.
|
||||
///
|
||||
/// If the stack is at capacity, returns the original element and an [InsertFailed] error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use cl_structures::stack::Stack;
|
||||
/// let mut v: Stack<_, 2> = Stack::new();
|
||||
///
|
||||
/// assert_eq!(Ok(()), v.try_insert(0, 0));
|
||||
/// ```
|
||||
pub fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
|
||||
if index > self.len {
|
||||
return Err((data, InsertFailed::Bounds(index)));
|
||||
}
|
||||
if self.is_full() {
|
||||
return Err((data, InsertFailed::Full));
|
||||
}
|
||||
// Safety: index < self.len && !self.is_full()
|
||||
unsafe { self.insert_unchecked(index, data) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Safety:
|
||||
/// - index must be less than self.len
|
||||
/// - length after insertion must be <= N
|
||||
#[inline]
|
||||
unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
|
||||
let base = self.as_mut_ptr();
|
||||
|
||||
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
|
||||
|
||||
self.len += 1;
|
||||
self.buf[index] = MaybeUninit::new(data);
|
||||
}
|
||||
|
||||
/// Clears the stack
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::Stack;
|
||||
///
|
||||
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
|
||||
/// assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// v.clear();
|
||||
/// assert_eq!(v.as_slice(), &[]);
|
||||
/// ```
|
||||
pub fn clear(&mut self) {
|
||||
// Hopefully copy elision takes care of this lmao
|
||||
drop(std::mem::take(self))
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the stack
|
||||
/// ```
|
||||
/// # use cl_structures::stack::*;
|
||||
/// let v = Stack::from([0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// assert_eq!(5, v.len());
|
||||
/// ```
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Returns true if the stack is at (or over) capacity
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::*;
|
||||
/// let v = Stack::from([(); 10]);
|
||||
///
|
||||
/// assert!(v.is_full());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.len >= N
|
||||
}
|
||||
|
||||
/// Returns true if the stack contains no elements
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use cl_structures::stack::*;
|
||||
/// let v: Stack<(), 10> = Stack::new();
|
||||
///
|
||||
/// assert!(v.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum InsertFailed<const N: usize> {
|
||||
Bounds(usize),
|
||||
Full,
|
||||
}
|
||||
impl<const N: usize> std::error::Error for InsertFailed<N> {}
|
||||
impl<const N: usize> std::fmt::Display for InsertFailed<N> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InsertFailed::Bounds(idx) => write!(f, "Index {idx} exceeded length {N}"),
|
||||
InsertFailed::Full => {
|
||||
write!(f, "Attempt to insert into full stack (length {N})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn zero_sized() {
|
||||
let v: Stack<(), { usize::MAX }> = Stack::new();
|
||||
assert_eq!(usize::MAX, v.capacity());
|
||||
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of_val(&v))
|
||||
}
|
||||
#[test]
|
||||
#[cfg_attr(debug_assertions, ignore = "calls ().drop() usize::MAX times")]
|
||||
fn from_usize_max_zst_array() {
|
||||
let mut v = Stack::from([(); usize::MAX]);
|
||||
assert_eq!(v.len(), usize::MAX);
|
||||
v.pop();
|
||||
assert_eq!(v.len(), usize::MAX - 1);
|
||||
}
|
||||
#[test]
|
||||
fn new() {
|
||||
let v: Stack<(), 255> = Stack::new();
|
||||
assert_eq!(0, v.len());
|
||||
assert_eq!(255, v.capacity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push() {
|
||||
let mut v: Stack<_, 64> = Stack::new();
|
||||
v.push(10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Attempted to push into full stack"]
|
||||
fn push_overflow() {
|
||||
let mut v = Stack::from([]);
|
||||
v.push(10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pop() {
|
||||
let mut v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(Some(9), v.pop());
|
||||
assert_eq!(Some(8), v.pop());
|
||||
assert_eq!(Some(7), v.pop());
|
||||
assert_eq!(Some(6), v.pop());
|
||||
assert_eq!(Some(5), v.pop());
|
||||
assert_eq!(Some(4), v.pop());
|
||||
assert_eq!(Some(3), v.pop());
|
||||
assert_eq!(Some(2), v.pop());
|
||||
assert_eq!(Some(1), v.pop());
|
||||
assert_eq!(None, v.pop());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resize_smaller() {
|
||||
let v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let mut v = v.resize::<2>();
|
||||
|
||||
assert_eq!(2, v.capacity());
|
||||
|
||||
assert_eq!(Some(2), v.pop());
|
||||
assert_eq!(Some(1), v.pop());
|
||||
assert_eq!(None, v.pop());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resize_bigger() {
|
||||
let v = Stack::from([1, 2, 3, 4]);
|
||||
let mut v: Stack<_, 10> = v.resize();
|
||||
|
||||
assert_eq!(Some(4), v.pop());
|
||||
assert_eq!(Some(3), v.pop());
|
||||
assert_eq!(Some(2), v.pop());
|
||||
assert_eq!(Some(1), v.pop());
|
||||
assert_eq!(None, v.pop());
|
||||
}
|
||||
#[test]
|
||||
fn dangle() {
|
||||
let mut v: Stack<_, 2> = Stack::new();
|
||||
let a = 0;
|
||||
let b = 1;
|
||||
v.push(&a);
|
||||
v.push(&b);
|
||||
println!("{v:?}");
|
||||
}
|
||||
#[test]
|
||||
fn remove() {
|
||||
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||
|
||||
assert_eq!(3, v.remove(3));
|
||||
assert_eq!(4, v.remove(3));
|
||||
assert_eq!(5, v.remove(3));
|
||||
assert_eq!(Some(2), v.pop());
|
||||
assert_eq!(Some(1), v.pop());
|
||||
assert_eq!(Some(0), v.pop());
|
||||
assert_eq!(None, v.pop());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swap_remove() {
|
||||
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||
assert_eq!(3, v.swap_remove(3));
|
||||
assert_eq!(&[0, 1, 2, 5, 4], v.as_slice());
|
||||
}
|
||||
#[test]
|
||||
fn swap_remove_last() {
|
||||
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
|
||||
assert_eq!(5, v.swap_remove(5));
|
||||
assert_eq!(&[0, 1, 2, 3, 4], v.as_slice())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert() {
|
||||
let mut v = Stack::from([0, 1, 2, 4, 5, 0x41414141]);
|
||||
v.pop();
|
||||
v.insert(3, 3);
|
||||
|
||||
assert_eq!(&[0, 1, 2, 3, 4, 5], v.as_slice())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Attempted to insert into full stack"]
|
||||
fn insert_overflow() {
|
||||
let mut v = Stack::from([0]);
|
||||
v.insert(0, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
let v = Stack::from([
|
||||
Box::new(0),
|
||||
Box::new(1),
|
||||
Box::new(2),
|
||||
Box::new(3),
|
||||
Box::new(4),
|
||||
]);
|
||||
std::mem::drop(std::hint::black_box(v));
|
||||
}
|
||||
}
|
||||
221
compiler/cl-structures/src/tree.rs
Normal file
221
compiler/cl-structures/src/tree.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
//! An insert-only unordered tree, backed by a [Vec]
|
||||
//!
|
||||
//! # Examples
|
||||
//! ```
|
||||
//! use cl_structures::tree::{Tree, Node};
|
||||
//! // A tree can be created
|
||||
//! let mut tree = Tree::new();
|
||||
//! // Provided with a root node
|
||||
//! let root = tree.root("This is the root node").unwrap();
|
||||
//!
|
||||
//! // Nodes can be accessed by indexing
|
||||
//! assert_eq!(*tree[root].as_ref(), "This is the root node");
|
||||
//! // Nodes' data can be accessed directly by calling `get`/`get_mut`
|
||||
//! assert_eq!(tree.get(root).unwrap(), &"This is the root node")
|
||||
//! ```
|
||||
|
||||
// TODO: implement an Entry-style API for doing traversal algorithms
|
||||
|
||||
pub use self::tree_ref::Ref;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
pub mod tree_ref;
|
||||
|
||||
/// An insert-only unordered tree, backed by a [Vec]
|
||||
#[derive(Debug)]
|
||||
pub struct Tree<T> {
|
||||
nodes: Vec<Node<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for Tree<T> {
|
||||
fn default() -> Self {
|
||||
Self { nodes: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Getters
|
||||
impl<T> Tree<T> {
|
||||
pub fn get(&self, index: Ref<T>) -> Option<&T> {
|
||||
self.get_node(index).map(|node| &node.value)
|
||||
}
|
||||
pub fn get_mut(&mut self, index: Ref<T>) -> Option<&mut T> {
|
||||
self.get_node_mut(index).map(|node| &mut node.value)
|
||||
}
|
||||
pub fn get_node(&self, index: Ref<T>) -> Option<&Node<T>> {
|
||||
self.nodes.get(usize::from(index))
|
||||
}
|
||||
pub fn get_node_mut(&mut self, index: Ref<T>) -> Option<&mut Node<T>> {
|
||||
self.nodes.get_mut(usize::from(index))
|
||||
}
|
||||
}
|
||||
|
||||
/// Tree operations
|
||||
impl<T> Tree<T> {
|
||||
pub fn new() -> Self {
|
||||
Self { nodes: Default::default() }
|
||||
}
|
||||
|
||||
/// Creates a new root for the tree.
|
||||
///
|
||||
/// If the tree already has a root, the value will be returned.
|
||||
pub fn root(&mut self, value: T) -> Result<Ref<T>, T> {
|
||||
if self.is_empty() {
|
||||
// Create an index for the new node
|
||||
let node = Ref::new_unchecked(self.nodes.len());
|
||||
// add child to tree
|
||||
self.nodes.push(Node::from(value));
|
||||
Ok(node)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root(&mut self) -> Option<Ref<T>> {
|
||||
match self.nodes.is_empty() {
|
||||
true => None,
|
||||
false => Some(Ref::new_unchecked(0)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a value into the tree as a child of the parent node
|
||||
///
|
||||
/// # Panics
|
||||
/// May panic if the node [Ref] is from a different tree
|
||||
pub fn insert(&mut self, value: T, parent: Ref<T>) -> Ref<T> {
|
||||
let child = Ref::new_unchecked(self.nodes.len());
|
||||
// add child to tree before parent
|
||||
self.nodes.push(Node::with_parent(value, parent));
|
||||
// add child to parent
|
||||
self[parent].children.push(child);
|
||||
|
||||
child
|
||||
}
|
||||
|
||||
/// Gets the depth of a node
|
||||
///
|
||||
/// # Panics
|
||||
/// May panic if the node [Ref] is from a different tree
|
||||
pub fn depth(&self, node: Ref<T>) -> usize {
|
||||
match self[node].parent {
|
||||
Some(node) => self.depth(node) + 1,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the number of branches in the tree
|
||||
pub fn branches(&self) -> usize {
|
||||
self.nodes.iter().fold(0, |edges, node| edges + node.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Standard data structure functions
|
||||
impl<T> Tree<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.nodes.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<Ref<T>> for Tree<T> {
|
||||
type Output = Node<T>;
|
||||
fn index(&self, index: Ref<T>) -> &Self::Output {
|
||||
self.get_node(index).expect("Ref should be inside Tree")
|
||||
}
|
||||
}
|
||||
impl<T> IndexMut<Ref<T>> for Tree<T> {
|
||||
fn index_mut(&mut self, index: Ref<T>) -> &mut Self::Output {
|
||||
self.get_node_mut(index).expect("Ref should be inside Tree")
|
||||
}
|
||||
}
|
||||
|
||||
/// A node in a [Tree]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Node<T> {
|
||||
value: T,
|
||||
/// The parent
|
||||
parent: Option<Ref<T>>,
|
||||
/// The children
|
||||
children: Vec<Ref<T>>,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
Self { value, parent: None, children: vec![] }
|
||||
}
|
||||
pub const fn with_parent(value: T, parent: Ref<T>) -> Self {
|
||||
Self { value, parent: Some(parent), children: vec![] }
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &T {
|
||||
self.as_ref()
|
||||
}
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.as_mut()
|
||||
}
|
||||
pub fn swap(&mut self, value: T) -> T {
|
||||
std::mem::replace(&mut self.value, value)
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<Ref<T>> {
|
||||
self.parent
|
||||
}
|
||||
pub fn children(&self) -> &[Ref<T>] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.children.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.children.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for Node<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
impl<T> AsMut<T> for Node<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Node<T> {
|
||||
#[inline]
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[allow(unused)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_children() {
|
||||
let mut tree = Tree::new();
|
||||
let root = tree.root(0).unwrap();
|
||||
let one = tree.insert(1, root);
|
||||
let two = tree.insert(2, root);
|
||||
assert_eq!([one, two].as_slice(), tree[root].children());
|
||||
}
|
||||
#[test]
|
||||
fn nest_children() {
|
||||
let mut tree = Tree::new();
|
||||
let root = tree.root(0).unwrap();
|
||||
let one = tree.insert(1, root);
|
||||
let two = tree.insert(2, one);
|
||||
assert_eq!(&[one], tree[root].children());
|
||||
assert_eq!(&[two], tree[one].children());
|
||||
assert_eq!(tree[two].children(), &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_equal() {}
|
||||
}
|
||||
70
compiler/cl-structures/src/tree/tree_ref.rs
Normal file
70
compiler/cl-structures/src/tree/tree_ref.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
//! An element in a [Tree](super::Tree)
|
||||
///
|
||||
/// Contains a niche, and as such, [`Option<TreeRef<T>>`] is free :D
|
||||
use std::{marker::PhantomData, num::NonZeroUsize};
|
||||
|
||||
/// An element of in a [Tree](super::Tree).
|
||||
//? The index of the node is stored as a [NonZeroUsize] for space savings
|
||||
//? Making Refs T-specific helps the user keep track of which Refs belong to which trees.
|
||||
//? This isn't bulletproof, of course, but it'll keep Ref<Foo> from being used on Tree<Bar>
|
||||
pub struct Ref<T: ?Sized>(NonZeroUsize, PhantomData<T>);
|
||||
|
||||
impl<T: ?Sized> Ref<T> {
|
||||
/// Constructs a new [Ref] with the given index
|
||||
pub fn new_unchecked(index: usize) -> Self {
|
||||
// Safety: index cannot be zero because we use saturating addition on unsigned type.
|
||||
Self(
|
||||
unsafe { NonZeroUsize::new_unchecked(index.saturating_add(1)) },
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<Ref<T>> for usize {
|
||||
fn from(value: Ref<T>) -> Self {
|
||||
usize::from(value.0) - 1
|
||||
}
|
||||
}
|
||||
|
||||
/* --- implementations of derivable traits, because we don't need bounds here --- */
|
||||
|
||||
impl<T: ?Sized> std::fmt::Debug for Ref<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("TreeRef").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> std::hash::Hash for Ref<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
self.1.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> PartialEq for Ref<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0 && self.1 == other.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Eq for Ref<T> {}
|
||||
|
||||
impl<T: ?Sized> PartialOrd for Ref<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Ord for Ref<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for Ref<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for Ref<T> {}
|
||||
Reference in New Issue
Block a user