#![feature(debug_closure_helpers, box_as_ptr, allocator_api)] #[cfg(test)] mod tests; pub mod ffi { #![allow( non_camel_case_types, dead_code, non_upper_case_globals, improper_ctypes )] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } #[repr(C)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct FFISlice { pub ptr: *const u8, pub len: usize, } #[repr(transparent)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct MaybeFFISlice { inner: FFISlice, } impl MaybeFFISlice { pub fn is_none(&self) -> bool { self.inner.ptr.is_null() } pub fn into_option(self) -> Option { if self.is_none() { None } else { Some(self.inner) } } } impl FFISlice { /// # Safety /// The caller must ensure that the slice is valid for type T, and lasts for 'a. pub unsafe fn as_slice_unchecked<'a, T: Sized>(self) -> &'a [T] { // SAFETY: The caller ensures that the FFISlice is valid for type T. unsafe { core::slice::from_raw_parts(self.ptr.cast(), self.len) } } /// # Safety /// The caller ensures that the slice is valid byte slice. /// Namely, the pointer must be well-aligned and point to `len` bytes, and /// must last for at least 'a. pub fn as_u8s_unchecked<'a>(self) -> &'a [u8] { // SAFETY: The FFISlice is guaranteed to be a valid byte slice. unsafe { self.as_slice_unchecked() } } /// # Safety /// The caller must ensure that the slice is a valid utf8 string. /// Furthermore, the pointer must be well-aligned, point to `len` bytes, and /// must last for at least 'a. pub unsafe fn as_str_unchecked<'a>(self) -> &'a str { // SAFETY: The caller ensures that the FFISlice is a valid utf8 string. unsafe { core::str::from_utf8_unchecked(self.as_u8s_unchecked()) } } } pub mod vec { #![allow(dead_code)] impl Default for BlobVec { fn default() -> Self { Self { data: core::ptr::null_mut(), len: 0, cap: 0, elem_size: 0, drop: None, } } } unsafe impl Send for BlobVec {} unsafe impl Sync for BlobVec {} use super::ffi::*; #[repr(transparent)] #[derive(Debug)] pub struct Vec { pub vec: BlobVec, _marker: core::marker::PhantomData, } impl Default for Vec { fn default() -> Self { Self::new() } } impl Drop for Vec { fn drop(&mut self) { // SAFETY: The vec is valid and owned by self. unsafe { vec_drop(&mut self.vec); } } } impl Vec { pub fn new() -> Self { Self::new_with(32) } pub fn new_with(capacity: usize) -> Self { let mut vec = BlobVec { data: core::ptr::null_mut(), len: 0, cap: 0, elem_size: 0, drop: None, }; unsafe extern "C" fn drop_fn(ptr: *mut ()) { unsafe { core::ptr::drop_in_place::(ptr as *mut T); } } unsafe { vec_init_with( &mut vec, core::mem::size_of::(), Some(drop_fn::), capacity, ); } Self { vec, _marker: core::marker::PhantomData, } } pub fn as_slice(&self) -> &[T] { assert_eq!(self.vec.elem_size, core::mem::size_of::()); unsafe { core::slice::from_raw_parts(self.vec.data as *const T, self.vec.len) } } pub fn as_slice_mut(&mut self) -> &mut [T] { assert_eq!(self.vec.elem_size, core::mem::size_of::()); unsafe { core::slice::from_raw_parts_mut(self.vec.data as *mut T, self.vec.len) } } pub fn extend(&mut self, elements: Box<[T]>) { unsafe { let elements = core::mem::transmute::, Box<[core::mem::ManuallyDrop]>>(elements); vec_extend(&mut self.vec, elements.as_ptr() as *const _, elements.len()); } } pub fn push(&mut self, value: T) { let value = core::mem::ManuallyDrop::new(value); unsafe { vec_push(&mut self.vec, &raw const value as *const T as *const _); } } pub fn insert(&mut self, value: T, index: usize) { if index > self.vec.len { return; } let value = core::mem::ManuallyDrop::new(value); unsafe { vec_insert( &mut self.vec, index, &raw const value as *const T as *const _, ); } } pub fn insert_many(&mut self, index: usize, elements: Box<[T]>) { unsafe { let elements = core::mem::transmute::, Box<[core::mem::ManuallyDrop]>>(elements); vec_insert_many( &mut self.vec, index, elements.as_ptr() as *const _, elements.len(), ); } } pub fn pop(&mut self) -> Option { if self.vec.len == 0 { return None; } unsafe { let ptr = vec_get(&mut self.vec, self.vec.len - 1) as *mut T; let value = ptr.read(); vec_pop(&mut self.vec); Some(value) } } pub fn get(&self, index: usize) -> Option<&T> { if index >= self.vec.len { return None; } unsafe { let ptr = vec_get(&raw const self.vec as *mut _, index) as *mut T; Some(&*ptr) } } pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index >= self.vec.len { return None; } unsafe { let ptr = vec_get(&raw mut self.vec, index) as *mut T; Some(&mut *ptr) } } pub fn remove(&mut self, index: usize) { if index >= self.vec.len { return; } unsafe { vec_remove(&mut self.vec, index); } } pub fn len(&self) -> usize { self.vec.len } pub fn is_empty(&self) -> bool { self.vec.len == 0 } pub fn position(&self, elem: &T, mut cmp: F) -> Option where F: FnMut(&T, &T) -> bool, { extern "C" fn cmp_trampoline bool>( f: *const (), a: *const (), b: *const (), ) -> bool { let f = unsafe { &mut *(f as *mut F) }; let a = unsafe { &*(a as *const T) }; let b = unsafe { &*(b as *const T) }; f(a, b) } unsafe { let index = vec_find( &raw const self.vec as *mut _, elem as *const T as *const _, cmp_trampoline::, &raw mut cmp as *mut (), ); if index == usize::MAX { None } else { Some(index) } } } pub fn binary_search_by(&self, elem: &T, mut cmp: F) -> Result where F: FnMut(&T, &T) -> i32, { extern "C" fn cmp_trampoline i32>( f: *const (), a: *const (), b: *const (), ) -> i32 { let f = unsafe { &mut *(f as *mut F) }; let a = unsafe { &*(a as *const T) }; let b = unsafe { &*(b as *const T) }; f(a, b) } unsafe { let (index, vacant) = vec_binary_search_by( &raw const self.vec as *mut _, elem as *const T as *const _, cmp_trampoline::, &raw mut cmp as *mut (), ); if vacant { Err(index) } else { Ok(index) } } } pub fn insert_sorted(&mut self, elem: T, mut cmp: F) -> Result where F: FnMut(&T, &T) -> i32, { extern "C" fn cmp_trampoline i32>( f: *const (), a: *const (), b: *const (), ) -> i32 { let f = unsafe { &mut *(f as *mut F) }; let a = unsafe { &*(a as *const T) }; let b = unsafe { &*(b as *const T) }; f(a, b) } let mut elem = core::mem::ManuallyDrop::new(elem); unsafe { let (index, _inserted) = vec_insert_sorted( &raw const self.vec as *mut _, &raw mut elem as *const _, cmp_trampoline::, &raw mut cmp as *mut (), ); Ok(index) } } } } mod display { use super::ffi::{self, Ast, AstNode}; impl core::fmt::Display for AstNode { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use crate::ffi::{ AST_ADDRESS_OF, AST_ARG, AST_AS, AST_ASSIGNMENT, AST_BINARY_OP, AST_BLOCK, AST_CALL, AST_DEREF, AST_FUNCTION, AST_IF, AST_NUMBER, AST_PLACE_TO_VALUE, AST_RETURN_STATEMENT, AST_VALUE_TO_PLACE, AST_VAR_DECL, AST_VAR_REF, BinaryExpr, }; match self.kind { AST_NUMBER => { write!(f, "Number({})", self.data as usize) } AST_DEREF => { write!(f, "Deref(expr: {})", self.data as usize) } AST_ADDRESS_OF => { write!(f, "AddressOf(expr: {})", self.data as usize) } AST_CALL => { let call = unsafe { self.data.cast::().read() }; write!(f, "Call(callee: {}, params: {:?})", call.callee, unsafe { std::slice::from_raw_parts(call.params.cast::(), call.params_len) },) } AST_AS => { let as_expr = unsafe { self.data.cast::().read() }; write!(f, "As(expr: {}, target_type: {})", as_expr.expr, as_expr.ty) } AST_ARG => { let arg = unsafe { self.data.cast::().read() }; write!( f, "Arg(name: {:?}, arg_type: {})", unsafe { std::str::from_utf8(std::slice::from_raw_parts(arg.name, arg.name_len)) }, arg.arg_type, ) } AST_VAR_REF => { let var_ref = unsafe { self.data.cast::().read() }; if var_ref.resolved != u64::MAX { write!(f, "VarRef({})", var_ref.resolved) } else { write!(f, "VarRef(name: {:?})", unsafe { std::str::from_utf8(std::slice::from_raw_parts( var_ref.name, var_ref.name_len, )) },) } } AST_VAR_DECL => { let var_decl = unsafe { self.data.cast::().read() }; write!( f, "VarDecl(name: {:?}, var_type: {})", unsafe { std::str::from_utf8(std::slice::from_raw_parts( var_decl.name, var_decl.name_len, )) }, var_decl.var_type, ) } AST_ASSIGNMENT => { write!( f, "Assignment(dest: {}, src: {})", self.data as usize, self.extra ) } AST_BINARY_OP => { let BinaryExpr { left, operator, right, } = unsafe { self.data.cast::().read() }; write!( f, "BinaryOp(op: {}, left: {}, right: {})", operator, left, right ) } AST_RETURN_STATEMENT => { let return_expr_id = self.data as usize; write!(f, "ReturnStatement(expr: {})", return_expr_id) } AST_FUNCTION => { let func = unsafe { self.data.cast::().read() }; write!( f, "Function(name: {:?}, args: {:?}, return_type: {}, body: {})", unsafe { std::str::from_utf8(std::slice::from_raw_parts( func.name, func.name_len, )) }, unsafe { std::slice::from_raw_parts(func.args.cast::(), func.args_len) }, func.return_type, func.body ) } AST_BLOCK => { write!(f, "Block(statements: {:?})", unsafe { std::slice::from_raw_parts(self.data.cast::(), self.extra) }) } AST_IF => { let if_node = unsafe { self.data.cast::().read() }; write!( f, "If(cond: {}, then_branch: {}, else_branch: {:?})", if_node.condition, if_node.then, match if_node.else_ { u64::MAX => None, v => Some(v), } ) } AST_PLACE_TO_VALUE => { write!(f, "PlaceToValue(place: {})", self.data as usize) } AST_VALUE_TO_PLACE => { write!(f, "ValueToPlace(value: {})", self.data as usize) } kind => write!(f, "UnknownNode(kind: {kind})"), } } } impl core::fmt::Display for Ast { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { writeln!(f, "[")?; for (i, item) in self.nodes.as_slice().iter().enumerate() { if i > 0 { writeln!(f, ", ")?; } write!(f, "\t{i}: {}", item)?; } write!(f, "\n]") } } impl core::fmt::Display for ffi::SymEntry { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("SymEntry") .field_with("key", |f| { f.debug_struct("Key") .field_with("kind", |f| { f.write_str(match self.key.kind { ffi::SYM_KEY_SCOPE => "Scope", ffi::SYM_KEY_SCOPE_NAME => "ScopeName", ffi::SYM_KEY_PARENT_SCOPE => "ParentScope", ffi::SYM_KEY_ARG => "Argument", ffi::SYM_KEY_VAR => "Variable", ffi::SYM_KEY_FUNCTION => "Function", _ => "Unknown", }) }) .field("scope", &self.key.scope_index) .field("span", &self.key.span) .field_with("ident", |f| { f.write_str(unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts( self.key.ident, self.key.ident_len, )) }) }) .finish() }) .field_with("value", |f| { let stct = &mut f.debug_struct("Value"); if self.extra == 0 { stct.field("ast_index", &self.index).finish() } else if self.index != 0 { stct.field_with("ident", |f| { f.write_str(unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts( self.index as *const u8, self.extra as usize, )) }) }) .finish() } else { stct.field("index", &self.index) .field("extra", &self.extra) .finish() } }) .finish() } } impl core::fmt::Display for ffi::Type { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use ffi::{ TYPE_BOOL, TYPE_F32, TYPE_F64, TYPE_I8, TYPE_I16, TYPE_I32, TYPE_I64, TYPE_ISIZE, TYPE_POINTER, TYPE_STR, TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64, TYPE_USIZE, TYPE_VOID, }; match self.kind { TYPE_VOID => { write!(f, "void") } TYPE_BOOL => { write!(f, "bool") } TYPE_I32 => { write!(f, "i32") } TYPE_U32 => { write!(f, "u32") } TYPE_STR => { write!(f, "str") } TYPE_POINTER => { let pointee = unsafe { (self.data as *const ffi::Type).read() }; write!(f, "*{pointee}",) } TYPE_I8 => { write!(f, "i8") } TYPE_U8 => { write!(f, "u8") } TYPE_I16 => { write!(f, "i16") } TYPE_U16 => { write!(f, "u16") } TYPE_I64 => { write!(f, "i64") } TYPE_U64 => { write!(f, "u64") } TYPE_F32 => { write!(f, "f32") } TYPE_F64 => { write!(f, "f64") } TYPE_USIZE => { write!(f, "usize") } TYPE_ISIZE => { write!(f, "isize") } _ => { write!(f, "UnknownType") } } } } #[repr(transparent)] pub struct Displayed(pub T); impl core::fmt::Debug for Displayed { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.0) } } pub trait DisplayedSliceExt { type Displayed: core::fmt::Debug; fn displayed(self) -> Self::Displayed; } impl<'a, T: core::fmt::Display> DisplayedSliceExt for &'a [T] { type Displayed = &'a [Displayed]; fn displayed(self) -> Self::Displayed { unsafe { core::mem::transmute(self) } } } } pub use display::{Displayed, DisplayedSliceExt}; impl Default for ffi::Ast { fn default() -> Self { ffi::Ast { nodes: vec::Vec::default(), } } }