diff --git a/Cargo.lock b/Cargo.lock index 816a75c..f5648ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,42 +2,12 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "ar_archive_writer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" -dependencies = [ - "object", -] - [[package]] name = "arcstr" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - [[package]] name = "bitflags" version = "2.10.0" @@ -62,16 +32,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" -dependencies = [ - "find-msvc-tools", - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -79,16 +39,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "chumsky" -version = "0.11.2" +name = "convert_case" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc17a6284abccac6e50db35c1cee87f605474a72939b959a3a67d9371800efd" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ - "hashbrown", - "regex-automata", - "serde", - "stacker", - "unicode-ident", "unicode-segmentation", ] @@ -100,49 +55,27 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", "unicode-xid", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "fxhash" version = "0.2.1" @@ -163,23 +96,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "leaf_allocators" version = "0.1.0" @@ -200,6 +116,16 @@ dependencies = [ "scc", ] +[[package]] +name = "leaf_compiler" +version = "0.1.0" +dependencies = [ + "arcstr", + "leaf_allocators", + "leaf_assembly", + "leaf_parser", +] + [[package]] name = "leaf_interpreter" version = "0.1.0" @@ -212,68 +138,40 @@ dependencies = [ ] [[package]] -name = "leaf_parsing" +name = "leaf_parser" version = "0.1.0" dependencies = [ "arcstr", - "chumsky", - "logos", + "derive_more", + "peg", ] [[package]] -name = "libc" -version = "0.2.177" +name = "peg" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "logos" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" +checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" dependencies = [ - "logos-derive", + "peg-macros", + "peg-runtime", ] [[package]] -name = "logos-codegen" -version = "0.15.1" +name = "peg-macros" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" +checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" dependencies = [ - "beef", - "fnv", - "lazy_static", + "peg-runtime", "proc-macro2", "quote", - "regex-syntax 0.8.8", - "rustc_version", - "syn", ] [[package]] -name = "logos-derive" -version = "0.15.1" +name = "peg-runtime" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] +checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" [[package]] name = "proc-macro2" @@ -284,16 +182,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "psm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" -dependencies = [ - "ar_archive_writer", - "cc", -] - [[package]] name = "quote" version = "1.0.42" @@ -303,29 +191,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex-automata" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - [[package]] name = "rustc_version" version = "0.4.1" @@ -363,61 +228,12 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "stacker" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys", -] - [[package]] name = "syn" version = "2.0.110" @@ -447,79 +263,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "zerocopy" version = "0.8.27" diff --git a/Cargo.toml b/Cargo.toml index 9344244..e180ace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "3" -members = ["allocators","assembly", "interpreter", "parsing"] +members = ["allocators","assembly", "compiler", "interpreter", "parser"] diff --git a/allocators/src/lib.rs b/allocators/src/lib.rs index ee559e8..791c65f 100644 --- a/allocators/src/lib.rs +++ b/allocators/src/lib.rs @@ -8,16 +8,16 @@ use std::{alloc::Layout, mem::MaybeUninit}; pub type DropFn = Option; pub struct AllocationEntry { - ptr: *mut u8, - layout: Layout, - drop_fn: DropFn, + ptr: *mut u8, + layout: Layout, + drop_fn: DropFn, } unsafe impl Send for AllocationEntry {} unsafe impl Sync for AllocationEntry {} pub trait Allocator { - unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop: DropFn) -> *mut u8; + unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop: DropFn) -> *mut u8; } pub trait SyncAllocator: Allocator + Send + Sync {} @@ -25,22 +25,22 @@ pub trait SyncAllocator: Allocator + Send + Sync {} impl SyncAllocator for T {} impl<'l> dyn Allocator + 'l { - pub fn alloc(&'l self, value: T) -> &'l mut T { - unsafe { - let value = MaybeUninit::new(value); - let data = value.as_ptr() as *const u8; - let layout = Layout::new::(); - let drop: DropFn = match std::mem::needs_drop::() { - false => None, - true => Some(|ptr: *mut u8| std::ptr::drop_in_place(ptr as *mut T)), - }; - &mut *(self.alloc_unsafe(data, layout, drop) as *mut T) - } - } + pub fn alloc(&'l self, value: T) -> &'l mut T { + unsafe { + let value = MaybeUninit::new(value); + let data = value.as_ptr() as *const u8; + let layout = Layout::new::(); + let drop: DropFn = match std::mem::needs_drop::() { + false => None, + true => Some(|ptr: *mut u8| std::ptr::drop_in_place(ptr as *mut T)), + }; + &mut *(self.alloc_unsafe(data, layout, drop) as *mut T) + } + } } impl<'l> dyn SyncAllocator + 'l { - pub fn alloc(&'l self, value: T) -> &'l mut T { - ::alloc(self, value) - } + pub fn alloc(&'l self, value: T) -> &'l mut T { + ::alloc(self, value) + } } diff --git a/assembly/src/context.rs b/assembly/src/assembly.rs similarity index 54% rename from assembly/src/context.rs rename to assembly/src/assembly.rs index 4ae1671..c864d61 100644 --- a/assembly/src/context.rs +++ b/assembly/src/assembly.rs @@ -1,24 +1,41 @@ use crate::{ + assembly::intrinsic_types::make_intrinsic_types, functions::Function, types::{ Type, derivations::{FuncT, TypeDerivations}, intrinsics::*, }, - values::{Const, Int}, + values::{Const, ConstData, Int}, }; +use derive_more::Debug; use leaf_allocators::SyncAllocator; use scc::HashMap; -use std::{hash::Hash, sync::OnceLock}; +use std::{borrow::Cow, hash::Hash, sync::OnceLock}; + +#[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[debug("{major}.{minor}.{patch}.{build}")] +pub struct Version { + pub major: u16, + pub minor: u16, + pub patch: u16, + pub build: u16, +} + +#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct AssemblyIdentifier { + pub version: Version, + pub name: Cow<'static, str>, +} pub type Ctx<'l> = &'l Context<'l>; pub struct Context<'l> { pub(crate) alloc: &'l dyn SyncAllocator, pub(crate) derivations: TypeDerivations<'l>, - - constants: HashMap<&'l Const<'l>, &'l Const<'l>>, + constants: HashMap<&'l ConstData<'l>, &'l Const<'l>>, intrinsics: OnceLock>>, + assemblies: HashMap>, } impl Eq for Context<'_> {} @@ -31,30 +48,91 @@ impl PartialEq for Context<'_> { impl Hash for Context<'_> { fn hash(&self, state: &mut H) { - (self as *const Self).hash(state); + std::ptr::hash(self, state); } } impl<'l> Context<'l> { - pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Context<'l> { + pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Self { let ctx = alloc.alloc(Self { alloc, constants: HashMap::new(), derivations: TypeDerivations::new(alloc), intrinsics: OnceLock::new(), + assemblies: Default::default(), }); ctx.intrinsics - .set(Box::new(intrinsic_types::make_intrinsic_types(ctx))) + .set(Box::new(make_intrinsic_types(ctx))) .unwrap(); ctx } + pub fn get_assembly(&'l self, ident: &AssemblyIdentifier) -> Option<&'l Assembly<'l>> { + self.assemblies.get_sync(ident).map(|v| *v) + } + + pub fn get_or_create_assembly(&'l self, ident: AssemblyIdentifier) -> &'l Assembly<'l> { + &*self + .assemblies + .entry_sync(ident.clone()) + .or_insert_with(|| Assembly::new(self, ident)) + } +} + +#[derive(Debug)] +pub struct Assembly<'l> { + #[debug(skip)] + ctx: &'l Context<'l>, + ident: AssemblyIdentifier, + #[debug("{:#?}", { + let mut c = vec![]; + constants.iter_sync(|k, _| {c.push(*k); true}); + c + })] + constants: HashMap<&'l Const<'l>, &'l Const<'l>>, +} + +impl Eq for Assembly<'_> {} + +impl PartialEq for Assembly<'_> { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self, other) + } +} + +impl Hash for Assembly<'_> { + fn hash(&self, state: &mut H) { + (self as *const Self).hash(state); + } +} + +impl<'l> Assembly<'l> { + pub fn new(ctx: &'l Context<'l>, ident: AssemblyIdentifier) -> &'l Self { + let assembly = ctx.alloc.alloc(Self { + ctx, + ident, + constants: HashMap::new(), + }); + assembly + } + + pub fn ctx(&self) -> Ctx<'l> { + self.ctx + } + pub fn create_function(&'l self, ty: &'l FuncT<'l>) -> &'l Function<'l> { - self.alloc.alloc(Function { + let func = self.ctx.alloc.alloc(Function { ty, + name: OnceLock::new(), body: OnceLock::new(), - ctx: self, - }) + declaring_assembly: self, + }); + let constant = self.ctx.alloc.alloc(Const { + ctx: self.ctx, + data: ConstData::Function(func), + }); + self.constants.insert_sync(constant, constant).unwrap(); + func } } @@ -68,11 +146,11 @@ macro_rules! create_const { impl<'l> CreateConst<'l, $ty> for Context<'l> { fn create_const(&'l self, value: $ty) -> &'l Const<'l> { let constant = ($expr)(self, value); - if let Some(existing) = self.constants.get_sync(&constant) { + if let Some(existing) = self.constants.get_sync(&constant.data) { return *existing; } let constant = self.alloc.alloc(constant); - *self.constants.entry_sync(constant).or_insert(constant) + *self.constants.entry_sync(&constant.data).or_insert(constant) } } )* @@ -80,22 +158,22 @@ macro_rules! create_const { } create_const! { - impl i8: |ctx, val| Const::Int(Int::I8(val), ctx), - impl i16: |ctx, val| Const::Int(Int::I16(val), ctx), - impl i32: |ctx, val| Const::Int(Int::I32(val), ctx), - impl i64: |ctx, val| Const::Int(Int::I64(val), ctx), - impl i128: |ctx, val| Const::Int(Int::I128(val), ctx), + impl i8: |ctx, val| Const { ctx, data: ConstData::Int(Int::I8(val)) }, + impl i16: |ctx, val| Const { ctx, data: ConstData::Int(Int::I16(val)) }, + impl i32: |ctx, val| Const { ctx, data: ConstData::Int(Int::I32(val)) }, + impl i64: |ctx, val| Const { ctx, data: ConstData::Int(Int::I64(val)) }, + impl i128: |ctx, val| Const { ctx, data: ConstData::Int(Int::I128(val)) }, - impl u8: |ctx, val| Const::Int(Int::U8(val), ctx), - impl u16: |ctx, val| Const::Int(Int::U16(val), ctx), - impl u32: |ctx, val| Const::Int(Int::U32(val), ctx), - impl u64: |ctx, val| Const::Int(Int::U64(val), ctx), - impl u128: |ctx, val| Const::Int(Int::U128(val), ctx), + impl u8: |ctx, val| Const { ctx, data: ConstData::Int(Int::U8(val)) }, + impl u16: |ctx, val| Const { ctx, data: ConstData::Int(Int::U16(val)) }, + impl u32: |ctx, val| Const { ctx, data: ConstData::Int(Int::U32(val)) }, + impl u64: |ctx, val| Const { ctx, data: ConstData::Int(Int::U64(val)) }, + impl u128: |ctx, val| Const { ctx, data: ConstData::Int(Int::U128(val)) }, - impl (): |ctx, _| Const::Void(ctx), - impl char: |ctx, val| Const::Char(val, ctx), - impl bool: |ctx, val| Const::Bool(val, ctx), - impl &'l str: |ctx, val| Const::Str(val, ctx), + impl (): |ctx, _| Const { ctx, data: ConstData::Void }, + impl char: |ctx, val| Const { ctx, data: ConstData::Char(val) }, + impl bool: |ctx, val| Const { ctx, data: ConstData::Bool(val) }, + impl &'l str: |ctx, val| Const { ctx, data: ConstData::Str(val) }, } mod intrinsic_types { @@ -111,7 +189,7 @@ mod intrinsic_types { pub type IntrinsicTypesArray<'l> = [Type<'l>; IntrinsicTypeKind::COUNT as usize]; - pub fn make_intrinsic_types<'l>(ctx: Ctx<'l>) -> IntrinsicTypesArray<'l> { + pub fn make_intrinsic_types<'l>(ctx: &'l Context<'l>) -> IntrinsicTypesArray<'l> { unsafe { const C: usize = IntrinsicTypeKind::COUNT as usize; let mut intrinsics = [MaybeUninit::::uninit(); C]; diff --git a/assembly/src/functions/ir.rs b/assembly/src/functions/ir.rs index 339de97..0602fcb 100644 --- a/assembly/src/functions/ir.rs +++ b/assembly/src/functions/ir.rs @@ -1,5 +1,5 @@ use crate::{ - context::Ctx, + assembly::Ctx, functions::{Function, FunctionBody}, types::{Type, derivations::*}, values::{Value, ValueFlags}, @@ -55,7 +55,7 @@ impl<'l> Into> for &'l Instruction<'l> { impl<'l> Instruction<'l> { #[inline] pub fn ctx(&self) -> Ctx<'l> { - self.parent_block.func.ctx + self.parent_block.func.declaring_assembly.ctx() } #[inline] @@ -342,7 +342,7 @@ impl<'l> BlockBuilder<'l> { let ret_t = self.block.func.ty.ret_t; let value_ty = match value { Some(v) => v.ty(), - None => self.block.func.ctx.void_t(), + None => self.block.func.ctx().void_t(), }; if value_ty != ret_t { return Err(format!( @@ -377,7 +377,7 @@ impl<'l> BlockBuilder<'l> { if self.has_termination() { return Err(format!("Block #{} has already terminated", self.block.id).into()); } - let instruction = &*self.block.func.ctx.alloc.alloc(Instruction { + let instruction = &*self.block.func.ctx().alloc.alloc(Instruction { id: Id(UnsafeCell::new(u32::MAX)), parent_block: self.block, variant, @@ -420,7 +420,7 @@ impl<'l> FunctionBodyBuilder<'l> { } pub fn create_block(&mut self) -> &'l Block<'l> { - let block = &*self.func.ctx.alloc.alloc(Block { + let block = &*self.func.ctx().alloc.alloc(Block { id: self.blocks.len() as u32, func: self.func, instructions: OnceLock::new(), diff --git a/assembly/src/functions/mod.rs b/assembly/src/functions/mod.rs index 42b64a3..da5aef4 100644 --- a/assembly/src/functions/mod.rs +++ b/assembly/src/functions/mod.rs @@ -1,23 +1,27 @@ use crate::{ - context::Ctx, + assembly::{Assembly, Ctx}, functions::ir::{Block, FunctionBodyBuilder}, types::derivations::FuncT, }; -use std::{fmt::Debug as FmtDebug, sync::OnceLock}; +use std::{ + fmt::{Debug as FmtDebug, Display}, + hash::Hash, + sync::OnceLock, +}; pub mod ir; #[non_exhaustive] pub struct Function<'l> { pub ty: &'l FuncT<'l>, + pub name: OnceLock<&'l str>, pub(crate) body: OnceLock>, - - pub(crate) ctx: Ctx<'l>, + pub(crate) declaring_assembly: &'l Assembly<'l>, } impl<'l> Function<'l> { pub fn ctx(&self) -> Ctx<'l> { - self.ctx + self.declaring_assembly.ctx() } pub fn body(&self) -> Option<&FunctionBody<'l>> { @@ -40,6 +44,15 @@ impl PartialEq for Function<'_> { } } +impl Display for Function<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.name.get() { + Some(n) => write!(f, "{} @ {}", self.ty, n), + None => Display::fmt(&self.ty, f), + } + } +} + impl FmtDebug for Function<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let body: &dyn FmtDebug = match self.body() { @@ -54,6 +67,12 @@ impl FmtDebug for Function<'_> { } } +impl Hash for Function<'_> { + fn hash(&self, state: &mut H) { + std::ptr::hash(self, state); + } +} + #[non_exhaustive] pub struct FunctionBody<'l> { pub blocks: Vec<&'l Block<'l>>, diff --git a/assembly/src/lib.rs b/assembly/src/lib.rs index d26d103..38a85ab 100644 --- a/assembly/src/lib.rs +++ b/assembly/src/lib.rs @@ -1,4 +1,4 @@ -pub mod context; +pub mod assembly; pub mod functions; pub mod types; pub mod values; diff --git a/assembly/src/types/derivations.rs b/assembly/src/types/derivations.rs index 5cb7898..b676c13 100644 --- a/assembly/src/types/derivations.rs +++ b/assembly/src/types/derivations.rs @@ -122,7 +122,7 @@ impl Display for FuncT<'_> { write!(f, "{separator}{ty}")?; separator = ", "; } - write!(f, ") -> {}", self.ret_t)?; + write!(f, ") {}", self.ret_t)?; Ok(()) } } diff --git a/assembly/src/types/intrinsics.rs b/assembly/src/types/intrinsics.rs index 2562bb8..b475a82 100644 --- a/assembly/src/types/intrinsics.rs +++ b/assembly/src/types/intrinsics.rs @@ -1,5 +1,6 @@ -use crate::{context::Ctx, types::Type}; +use crate::{assembly::Ctx, types::Type}; use derive_more::{Debug, Display}; +use std::fmt::Display; #[derive(Debug, Display, PartialEq, Eq, Hash)] #[display("type")] @@ -68,7 +69,11 @@ impl<'l> Into> for &'l ConstStrT<'l> { #[non_exhaustive] #[derive(Debug, Display, PartialEq, Eq, Hash)] -#[display("{}{}", if *signed { "i" } else { "u" }, precision)] +#[display( + "{}{}", + if *signed { "i" } else { "u" }, + if *precision == u32::MAX { &"size" as &dyn Display } else { precision }) +] #[debug("{self}")] pub struct IntT<'l> { pub signed: bool, diff --git a/assembly/src/types/mod.rs b/assembly/src/types/mod.rs index 7501cec..6d2e7d8 100644 --- a/assembly/src/types/mod.rs +++ b/assembly/src/types/mod.rs @@ -2,7 +2,7 @@ pub mod derivations; pub mod intrinsics; use crate::{ - context::Ctx, + assembly::Ctx, types::{derivations::*, intrinsics::*}, }; use derive_more::{Debug, Display}; diff --git a/assembly/src/values/mod.rs b/assembly/src/values/mod.rs index f4724e9..0e89a0e 100644 --- a/assembly/src/values/mod.rs +++ b/assembly/src/values/mod.rs @@ -1,4 +1,8 @@ -use crate::{context::Ctx, functions::ir::Instruction, types::Type}; +use crate::{ + assembly::Context, + functions::{Function, ir::Instruction}, + types::Type, +}; use bitflags::bitflags; use derive_more::{Debug, Display}; use half::f16; @@ -12,6 +16,7 @@ bitflags! { const Const = 0b00000001_00000000; const ConstOnly = 0b00000011_00000000; const Type = 0b00000111_00000000; + const Function = 0b00001011_00000000; } } @@ -50,51 +55,51 @@ impl Hash for Float { } #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Const<'l> { +pub enum ConstData<'l> { #[display("()")] #[debug("()")] - Void(Ctx<'l>), - + Void, #[debug("{:?}", _0)] #[display("{}", _0)] - Bool(bool, Ctx<'l>), - + Bool(bool), #[debug("{:?}", _0)] #[display("{}", _0)] - Char(char, Ctx<'l>), - + Char(char), #[debug("{:?}", _0)] #[display("\"{}\"", _0)] - Str(&'l str, Ctx<'l>), - + Str(&'l str), #[debug("{:?}", _0)] #[display("{}", _0)] - Int(Int, Ctx<'l>), - + Int(Int), #[debug("{:?}", _0)] #[display("{}", _0)] - Float(Float, Ctx<'l>), + Float(Float), + #[debug("{:?}", _0)] + #[display("{}", _0)] + Function(&'l Function<'l>), +} + +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] +#[debug("{:?}", data)] +#[display("{}", data)] +pub struct Const<'l> { + pub ctx: &'l Context<'l>, + pub data: ConstData<'l>, } impl<'l> Const<'l> { pub fn flags(&self) -> ValueFlags { - match self { - Const::Void(_) => ValueFlags::Const, - Const::Char(_, _) => ValueFlags::Const, - Const::Bool(_, _) => ValueFlags::Const, - Const::Str(_, _) => ValueFlags::Const, - Const::Int(_, _) => ValueFlags::Const, - Const::Float(_, _) => ValueFlags::Const, - } + ValueFlags::Const } pub fn ty(&self) -> Type<'l> { - match self { - Const::Void(ctx) => ctx.void_t(), - Const::Char(_, ctx) => ctx.char_t(), - Const::Bool(_, ctx) => ctx.bool_t(), - Const::Str(_, ctx) => ctx.const_str_t(), - Const::Int(v, ctx) => match v { + let ctx = self.ctx; + match self.data { + ConstData::Void => ctx.void_t(), + ConstData::Char(_) => ctx.char_t(), + ConstData::Bool(_) => ctx.bool_t(), + ConstData::Str(_) => ctx.const_str_t(), + ConstData::Int(v) => match v { Int::I8(_) => ctx.i8_t(), Int::I16(_) => ctx.i16_t(), Int::I32(_) => ctx.i32_t(), @@ -106,11 +111,12 @@ impl<'l> Const<'l> { Int::U64(_) => ctx.u64_t(), Int::U128(_) => ctx.u128_t(), }, - Const::Float(v, ctx) => match v { + ConstData::Float(v) => match v { Float::F16(_) => ctx.f16_t(), Float::F32(_) => ctx.f32_t(), Float::F64(_) => ctx.f64_t(), }, + ConstData::Function(v) => Type::Func(v.ty), } } } @@ -121,12 +127,14 @@ impl<'l> Into> for &'l Const<'l> { } } -#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)] pub enum Value<'l> { #[display("{}", _0)] Const(&'l Const<'l>), #[display("{}", _0)] Type(Type<'l>), + #[display("")] + Func(&'l Function<'l>), #[display("%{}", _0.id())] Instruction(&'l Instruction<'l>), } @@ -136,6 +144,7 @@ impl<'l> Value<'l> { match self { Value::Const(c) => c.flags(), Value::Type(_) => ValueFlags::Type, + Value::Func(_) => ValueFlags::Function, Value::Instruction(i) => i.value_flags(), } } @@ -144,6 +153,7 @@ impl<'l> Value<'l> { match self { Value::Const(v) => v.ty(), Value::Type(v) => v.ctx().type_t(), + Value::Func(v) => Type::Func(v.ty), Value::Instruction(v) => v.value_ty(), } } diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml new file mode 100644 index 0000000..16b4b79 --- /dev/null +++ b/compiler/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "leaf_compiler" +version = "0.1.0" +edition = "2024" + +[dependencies] +leaf_parser = { path = "../parser" } +leaf_assembly = { path = "../assembly" } +leaf_allocators = { path = "../allocators" } +arcstr = "1.2.0" diff --git a/compiler/src/error.rs b/compiler/src/error.rs new file mode 100644 index 0000000..79fbb2b --- /dev/null +++ b/compiler/src/error.rs @@ -0,0 +1,52 @@ +use leaf_parser::{LineCol, ParseError}; +use std::ops::Range; + +use crate::SourceFile; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Kind { + Unknown = 0x0000, + + Parsing = 0x0100, + + SymbolNotFound = 0x0200, + UninitializedSymbol = 0x0201, + NotAType = 0x0202, + + FunctionCompilationFailed = 0x0301, +} + +#[derive(Debug, Clone)] +pub enum Location { + None, + Range { + file: SourceFile, + range: Range, + }, + Position { + file: SourceFile, + position: LineCol, + }, +} + +#[derive(Debug)] +pub struct CompilationError { + pub kind: Kind, + pub message: String, + pub location: Location, + pub cause: Option>, +} + +impl CompilationError { + pub fn parsing(file: SourceFile, err: ParseError) -> Self { + Self { + kind: Kind::Parsing, + message: err.to_string(), + location: Location::Position { + file, + position: err.location, + }, + cause: None, + } + } +} diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs new file mode 100644 index 0000000..d76dd33 --- /dev/null +++ b/compiler/src/lib.rs @@ -0,0 +1,87 @@ +use crate::{error::CompilationError, scope::Scope}; +use arcstr::literal_substr; +use leaf_allocators::SyncAllocator; +use leaf_assembly::{ + assembly::{Assembly, AssemblyIdentifier, Context}, + values::Value, +}; +use leaf_parser::SourceCode; +use std::{collections::HashMap, sync::Arc}; + +mod error; +mod scope; + +pub type SourceFile = Arc; + +pub struct CompilationContext<'l> { + ctx: &'l Context<'l>, + alloc: &'l dyn SyncAllocator, +} + +impl<'l> CompilationContext<'l> { + pub fn new(alloc: &'l dyn SyncAllocator) -> Self { + Self { + alloc, + ctx: Context::new(alloc), + } + } + + pub fn get_assembly(&self, ident: &AssemblyIdentifier) -> Option<&'l Assembly<'l>> { + self.ctx.get_assembly(ident) + } + + pub fn extend( + &'l self, + ident: AssemblyIdentifier, + sources: &[Arc], + ) -> Result<(), CompilationError> { + let assembly = self.ctx.get_or_create_assembly(ident.clone()); + + let mut units = Vec::with_capacity(sources.len()); + for src in sources { + match leaf_parser::parse(src) { + Ok(unit) => units.push(unit), + Err(err) => return Err(CompilationError::parsing(src.clone(), err)), + } + } + + let mut scopes: Vec<_> = sources + .iter() + .map(|src| { + let mut scope = Scope::new(assembly, src.clone()); + scope.insert(literal_substr!("void"), Value::Type(self.ctx.void_t())); + scope.insert(literal_substr!("i8"), Value::Type(self.ctx.i8_t())); + scope.insert(literal_substr!("i16"), Value::Type(self.ctx.i16_t())); + scope.insert(literal_substr!("i32"), Value::Type(self.ctx.i32_t())); + scope.insert(literal_substr!("i64"), Value::Type(self.ctx.i64_t())); + scope.insert(literal_substr!("i128"), Value::Type(self.ctx.i128_t())); + scope.insert(literal_substr!("isize"), Value::Type(self.ctx.isize_t())); + scope.insert(literal_substr!("u8"), Value::Type(self.ctx.u8_t())); + scope.insert(literal_substr!("u16"), Value::Type(self.ctx.u16_t())); + scope.insert(literal_substr!("u32"), Value::Type(self.ctx.u32_t())); + scope.insert(literal_substr!("u64"), Value::Type(self.ctx.u64_t())); + scope.insert(literal_substr!("u128"), Value::Type(self.ctx.u128_t())); + scope.insert(literal_substr!("usize"), Value::Type(self.ctx.usize_t())); + scope.insert(literal_substr!("f16"), Value::Type(self.ctx.f16_t())); + scope.insert(literal_substr!("f32"), Value::Type(self.ctx.f32_t())); + scope.insert(literal_substr!("f64"), Value::Type(self.ctx.f64_t())); + scope.insert(literal_substr!("type"), Value::Type(self.ctx.type_t())); + scope + }) + .collect(); + + for i in 0..sources.len() { + let unit = &units[i]; + let scope = &mut scopes[i]; + scope.declare_constants(&unit.decls); + } + + for i in 0..sources.len() { + let unit = &units[i]; + let scope = &mut scopes[i]; + scope.define_constants(&unit.decls)?; + } + + Ok(()) + } +} diff --git a/compiler/src/main.rs b/compiler/src/main.rs new file mode 100644 index 0000000..27433e1 --- /dev/null +++ b/compiler/src/main.rs @@ -0,0 +1,26 @@ +use arcstr::ArcStr; +use leaf_allocators::SyncArenaAllocator; +use leaf_assembly::assembly::{AssemblyIdentifier, Version}; +use leaf_compiler::CompilationContext; +use leaf_parser::SourceCode; +use std::{path::PathBuf, sync::Arc}; + +fn main() { + let alloc = SyncArenaAllocator::default(); + let context = CompilationContext::new(&alloc); + let ident = AssemblyIdentifier { + version: Version::default(), + name: std::borrow::Cow::Borrowed("leaf_test"), + }; + if let Err(err) = context.extend( + ident.clone(), + &[Arc::new(SourceCode { + text: ArcStr::from(std::fs::read_to_string("../test.leaf").unwrap()), + file: PathBuf::from("../test.leaf"), + })], + ) { + println!("{}", err.message); + } else { + println!("{:#?}", context.get_assembly(&ident)); + } +} diff --git a/compiler/src/scope.rs b/compiler/src/scope.rs new file mode 100644 index 0000000..662061a --- /dev/null +++ b/compiler/src/scope.rs @@ -0,0 +1,108 @@ +use arcstr::Substr; +use leaf_assembly::{ + assembly::Assembly, + types::{Type, derivations::MakeTypeDerivations}, + values::Value, +}; +use leaf_parser::{ + SourceCode, + ast::{self, ConstDecl, Expr, Ident}, +}; +use std::{collections::HashMap, sync::Arc}; + +use crate::error::*; + +#[derive(Clone)] +pub struct Scope<'l> { + assembly: &'l Assembly<'l>, + source: Arc, + values: HashMap>>, +} + +impl<'l> Scope<'l> { + pub fn new(assembly: &'l Assembly<'l>, source: Arc) -> Self { + Self { + assembly, + source, + values: HashMap::default(), + } + } + + pub fn insert(&mut self, name: Substr, value: Value<'l>) { + self.values.insert(name, Some(value)); + } + + pub fn declare_constants(&mut self, decl: &[ConstDecl]) { + for val in decl { + for Ident(name) in val.names.as_slice() { + self.values.insert(name.clone(), None); + } + } + } + + pub fn define_constants(&mut self, decl: &[ConstDecl]) -> Result<(), CompilationError> { + for val in decl { + let expr = self.compile_expression(&val.value)?; + } + Ok(()) + } + + pub fn compile_expression(&self, expr: &Expr) -> Result, CompilationError> { + match expr { + Expr::Ident(Ident(name)) => match self.values.get(name) { + None => Err(CompilationError { + kind: Kind::SymbolNotFound, + message: format!("Symbol `{name}` does not exist in the current scope."), + location: Location::Range { + file: self.source.clone(), + range: name.range(), + }, + cause: None, + }), + Some(None) => Err(CompilationError { + kind: Kind::UninitializedSymbol, + message: format!("Symbol `{name}` is not initialized at this time."), + location: Location::Range { + file: self.source.clone(), + range: name.range(), + }, + cause: None, + }), + Some(Some(value)) => Ok(*value), + }, + Expr::Func(func) => self.compile_function(func).map_err(|err| CompilationError { + kind: Kind::FunctionCompilationFailed, + message: format!("Could not compile function."), + location: Location::Range { + file: self.source.clone(), + range: func.text.range(), + }, + cause: Some(Box::new(err)), + }), + _ => todo!("{expr:#?}"), + } + } + + fn compile_function(&self, func: &ast::Function) -> Result, CompilationError> { + let ret_ty = self.assert_type(self.compile_expression(&func.ret)?)?; + let mut par_ty = Vec::with_capacity(func.args.len()); + for arg in &func.args { + let ty = self.assert_type(self.compile_expression(&arg.r#type)?)?; + par_ty.push(ty); + } + let fn_ty = ret_ty.make_fn(par_ty); + Ok(Value::Func(self.assembly.create_function(fn_ty))) + } + + fn assert_type(&self, val: Value<'l>) -> Result, CompilationError> { + match val { + Value::Type(ty) => Ok(ty), + _ => Err(CompilationError { + kind: Kind::NotAType, + message: format!("Value is not a type."), + location: Location::None, + cause: None, + }), + } + } +} diff --git a/interpreter/src/instruction_cache.rs b/interpreter/src/instruction_cache.rs index b374741..4f8dad9 100644 --- a/interpreter/src/instruction_cache.rs +++ b/interpreter/src/instruction_cache.rs @@ -6,7 +6,7 @@ use leaf_assembly::{ ir::{Cmp, Instruction, InstructionVariant}, }, types::{Type, intrinsics::IntT}, - values::{Const, Int, Value}, + values::{Const, ConstData, Int, Value}, }; use scc::HashMap; use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc}; @@ -112,7 +112,10 @@ impl<'l> InstructionCacheEntry<'l> { .. }, ), - Value::Const(Const::Int(Int::U8(v), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U8(v)), + ctx, + }), ) => { assert_eq!(ctx.u8_t(), *ty); opcodes.push(OpCode::Store_CL_U8(*v, memory_ranges[&t.id()].start)); @@ -125,7 +128,10 @@ impl<'l> InstructionCacheEntry<'l> { .. }, ), - Value::Const(Const::Int(Int::U16(v), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U16(v)), + ctx, + }), ) => { assert_eq!(ctx.u16_t(), *ty); opcodes.push(OpCode::Store_CL_U16(*v, memory_ranges[&t.id()].start)); @@ -138,7 +144,10 @@ impl<'l> InstructionCacheEntry<'l> { .. }, ), - Value::Const(Const::Int(Int::U32(v), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U32(v)), + ctx, + }), ) => { assert_eq!(ctx.u32_t(), *ty); opcodes.push(OpCode::Store_CL_U32(*v, memory_ranges[&t.id()].start)); @@ -151,7 +160,10 @@ impl<'l> InstructionCacheEntry<'l> { .. }, ), - Value::Const(Const::Int(Int::U64(v), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U64(v)), + ctx, + }), ) => { assert_eq!(ctx.u64_t(), *ty); opcodes.push(OpCode::Store_CL_U64(*v, memory_ranges[&t.id()].start)); @@ -200,7 +212,10 @@ impl<'l> InstructionCacheEntry<'l> { InstructionVariant::IAdd( Value::Instruction(a), - Value::Const(Const::Int(Int::U32(b), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U32(b)), + ctx, + }), ) => { assert!(matches!( a.value_ty(), @@ -217,7 +232,10 @@ impl<'l> InstructionCacheEntry<'l> { InstructionVariant::ICmp( Value::Instruction(a), - Value::Const(Const::Int(Int::U32(b), ctx)), + Value::Const(Const { + data: ConstData::Int(Int::U32(b)), + ctx, + }), cmp, ) => { assert!(matches!( diff --git a/interpreter/src/interpreter.rs b/interpreter/src/interpreter.rs index 9869871..633c33a 100644 --- a/interpreter/src/interpreter.rs +++ b/interpreter/src/interpreter.rs @@ -4,10 +4,10 @@ use crate::{ }; use fxhash::FxHashMap; use leaf_assembly::{ - context::Ctx, + assembly::Ctx, functions::Function, types::{Type, intrinsics::IntT}, - values::{Const, Int, Value}, + values::{Const, ConstData, Int, Value}, }; use smallvec::SmallVec; use std::{ops::Range, sync::Arc}; @@ -211,7 +211,10 @@ impl<'l> Interpreter<'l> { fn push_ctx_val(&mut self, sp: &mut usize, value: &Value) -> Range { let ptr = &self.stack[*sp] as *const u8; match value { - Value::Const(Const::Int(Int::U32(v), _)) => { + Value::Const(Const { + data: ConstData::Int(Int::U32(v)), + .. + }) => { *sp += ptr.align_offset(align_of::()); let range = *sp..*sp + size_of::(); self.stack[range.clone()].copy_from_slice(&v.to_ne_bytes()); diff --git a/interpreter/src/main.rs b/interpreter/src/main.rs index e6b6e9f..98be31f 100644 --- a/interpreter/src/main.rs +++ b/interpreter/src/main.rs @@ -1,5 +1,5 @@ use leaf_allocators::SyncArenaAllocator; -use leaf_assembly::context::{Context, CreateConst}; +use leaf_assembly::assembly::{Assembly, AssemblyIdentifier, Context, CreateConst, Version}; use leaf_assembly::functions::ir::Cmp; use leaf_assembly::types::derivations::MakeTypeDerivations; use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction}; @@ -9,17 +9,21 @@ use std::time::{Duration, Instant}; fn main() { let allocator = SyncArenaAllocator::default(); let context = Context::new(&allocator); + let assembly = context.get_or_create_assembly(AssemblyIdentifier { + version: Version::default(), + name: "test".into(), + }); - let puts = context.create_function( + let puts = assembly.create_function( context .void_t() .make_fn([context.u8_t().make_ptr(false).into()]), ); - let print_u32 = context.create_function(context.void_t().make_fn([context.u32_t().into()])); + let print_u32 = assembly.create_function(context.void_t().make_fn([context.u32_t().into()])); let random_func = { - let func = context.create_function(context.u32_t().make_fn([])); + let func = assembly.create_function(context.u32_t().make_fn([])); let mut builder = func.create_body().unwrap(); let loop_check = builder.create_block(); diff --git a/parsing/.gitignore b/parser/.gitignore similarity index 100% rename from parsing/.gitignore rename to parser/.gitignore diff --git a/parser/Cargo.toml b/parser/Cargo.toml new file mode 100644 index 0000000..15c2704 --- /dev/null +++ b/parser/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "leaf_parser" +version = "0.1.0" +edition = "2024" + +[dependencies] +arcstr = "1.2.0" +derive_more = { version = "2.1.0", features = ["deref", "debug", "display"] } +peg = "0.8.5" diff --git a/parser/src/ast.rs b/parser/src/ast.rs new file mode 100644 index 0000000..4d30b7e --- /dev/null +++ b/parser/src/ast.rs @@ -0,0 +1,140 @@ +use arcstr::Substr; +use derive_more::Deref; + +#[derive(Debug, Deref)] +pub struct Ident(pub Substr); + +#[derive(Debug, Deref)] +pub struct Import(pub Expr); + +#[derive(Debug)] +pub struct Number { + pub text: Substr, + pub r#type: Option, +} + +#[derive(derive_more::Debug)] +pub enum Expr { + #[debug("uninit")] + Uninit(Substr), + #[debug("Ident({:?})", _0.0)] + Ident(Ident), + #[debug("{_0:?}")] + Number(Number), + String(Substr), + #[debug("{_0:?}")] + Binary(Box), + Index(Box), + Tuple(Vec), + List(Vec), + Struct(Box), + #[debug("{_0:?}")] + Block(Block), + #[debug("{_0:?}")] + Func(Box), + + #[debug("{_0:?}")] + ConstDecl(Box), + #[debug("{_0:?}")] + VarDecl(Box), + #[debug("{_0:?}")] + For(Box), + + Call { + func: Box, + args: Vec, + }, +} + +#[derive(Debug)] +pub struct BinaryExpr { + pub lhs: Expr, + pub op: BinaryOp, + pub rhs: Expr, +} + +#[derive(Debug)] +pub struct IndexingExpr { + pub value: Expr, + pub index: Expr, +} + +#[rustfmt::skip] +#[derive(derive_more::Debug)] +pub enum BinaryOp { + #[debug("{_0:?}")] Add(Substr), + #[debug("{_0:?}")] Sub(Substr), + #[debug("{_0:?}")] Mul(Substr), + #[debug("{_0:?}")] Div(Substr), + #[debug("{_0:?}")] Mod(Substr), + #[debug("{_0:?}")] Dot(Substr), + #[debug("{_0:?}")] Range(Substr), + #[debug("{_0:?}")] Assign(Substr), +} + +#[derive(Debug)] +pub struct ConstDecl { + pub names: NamePattern, + pub r#type: Option, + pub value: Expr, +} + +#[derive(Debug)] +pub struct VarDecl { + pub names: NamePattern, + pub r#type: Option, + pub value: Expr, +} + +#[derive(Debug)] +pub enum NamePattern { + Single(Ident), + Tuple(Vec), + List(Vec), +} + +impl NamePattern { + pub fn as_slice(&self) -> &[Ident] { + match self { + NamePattern::Single(ident) => std::slice::from_ref(ident), + NamePattern::Tuple(idents) => idents.as_slice(), + NamePattern::List(idents) => idents.as_slice(), + } + } +} + +#[derive(Debug)] +pub struct Function { + pub text: Substr, + pub args: Vec, + pub ret: Expr, + pub block: Option, +} + +#[derive(Debug)] +pub struct NameValuePair { + pub name: Ident, + pub r#type: Expr, +} + +#[derive(Debug)] +pub struct StructCtor { + pub r#type: Expr, + pub r#values: Vec, +} + +#[derive(Debug)] +pub struct Block(pub Vec); + +#[derive(Debug)] +pub struct For { + pub names: NamePattern, + pub value: Expr, + pub block: Block, +} + +#[derive(Debug)] +pub struct CompilationUnit { + pub imports: Vec, + pub decls: Vec, +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs new file mode 100644 index 0000000..6ff1a7d --- /dev/null +++ b/parser/src/lib.rs @@ -0,0 +1,19 @@ +use arcstr::ArcStr; +pub use parser::{compilation_unit as parse, *}; +use std::{fmt::Debug, path::PathBuf}; + +pub mod ast; +mod parser; + +pub struct SourceCode { + pub text: ArcStr, + pub file: PathBuf, +} + +impl Debug for SourceCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SourceCode") + .field("text", &self.text) + .finish_non_exhaustive() + } +} diff --git a/parser/src/main.rs b/parser/src/main.rs new file mode 100644 index 0000000..3874fa3 --- /dev/null +++ b/parser/src/main.rs @@ -0,0 +1,12 @@ +use arcstr::ArcStr; +use leaf_parser::SourceCode; +use std::path::PathBuf; + +fn main() { + let source = SourceCode { + text: ArcStr::from(std::fs::read_to_string("../test.leaf").unwrap()), + file: PathBuf::from("../test.leaf"), + }; + let unit = leaf_parser::parse(&source); + let _ = dbg!(unit); +} diff --git a/parser/src/parser.rs b/parser/src/parser.rs new file mode 100644 index 0000000..d77ee96 --- /dev/null +++ b/parser/src/parser.rs @@ -0,0 +1,146 @@ +use crate::SourceCode; +use crate::ast::*; +use arcstr::Substr; +use peg::{Parse, ParseElem, ParseLiteral, ParseSlice}; +pub use peg::{error::*, str::LineCol}; + +impl Parse for SourceCode { + type PositionRepr = LineCol; + + #[inline] + fn start<'input>(&'input self) -> usize { + self.text.as_str().start() + } + + #[inline] + fn is_eof<'input>(&'input self, p: usize) -> bool { + self.text.as_str().is_eof(p) + } + + #[inline] + fn position_repr<'input>(&'input self, p: usize) -> Self::PositionRepr { + self.text.as_str().position_repr(p) + } +} + +impl<'input> ParseElem<'input> for SourceCode { + type Element = char; + + #[inline] + fn parse_elem(&'input self, pos: usize) -> peg::RuleResult { + self.text.as_str().parse_elem(pos) + } +} + +impl ParseLiteral for SourceCode { + #[inline] + fn parse_string_literal(&self, pos: usize, literal: &str) -> peg::RuleResult<()> { + self.text.as_str().parse_string_literal(pos, literal) + } +} + +impl<'input> ParseSlice<'input> for SourceCode { + type Slice = Substr; + + #[inline] + fn parse_slice(&'input self, p1: usize, p2: usize) -> Self::Slice { + self.text.substr(p1..p2) + } +} + +peg::parser! { + grammar leaf_parser() for SourceCode { + // #### ATOMS #### + rule number() -> Number + = text:$(['0'..='9']+) r#type:ident()? { Number { text, r#type } } + / "0x" text:$(['0'..='9'|'a'..='f'|'A'..'F']+) r#type:ident()? { Number { text, r#type } } + / "0b" text:$(['0'|'1']+) r#type:ident()? { Number { text, r#type } } + + rule ident() -> Ident + = text:$(['_'|'a'..='z'|'A'..='Z']['_'|'a'..='z'|'A'..='Z'|'0'..='9']*) { Ident(text) } + + rule string() -> Substr + = str:$("\"" char()* "\"") { str } + + rule char() -> char = normal() + + rule normal() -> char = [^'\\'|'"'] + + // ### EXPRESSIONS #### + + rule expr() -> Expr = precedence! { + lhs:(@) __ op:$("=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs })) } + lhs:(@) __ op:$(".") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Dot(op), rhs })) } + lhs:(@) "(" __ args:(expr() ** ("," __)) __ ")" { Expr::Call { func: Box::new(lhs), args } } + value:(@) "[" __ index:expr() __ "]" { Expr::Index(Box::new(IndexingExpr { value, index })) } + r#type:(@) _ "{" __ values:name_value_pairs() __ "}" { Expr::Struct(Box::new(StructCtor { r#type, values })) } + -- + lhs:(@) __ op:$("+") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Add(op), rhs })) } + lhs:(@) __ op:$("-") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Sub(op), rhs })) } + -- + lhs:(@) __ op:$("*") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Mul(op), rhs })) } + lhs:(@) __ op:$("/") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Div(op), rhs })) } + lhs:(@) __ op:$("%") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Mod(op), rhs })) } + -- + lhs:(@) __ op:$("..") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) } + -- + block:block() { Expr::Block(block)} + for_loop:for_loop() { Expr::For(Box::new(for_loop))} + func:func() { Expr::Func(Box::new(func))} + var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) } + const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) } + "(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) } + "[" __ list:(expr() **<0,> ("," __)) __ "]" { Expr::List(list) } + "(" __ v:expr() __ ")" { v } + v:string() { Expr::String(v) } + v:number() { Expr::Number(v) } + v:ident() { Expr::Ident(v) } + } + + rule block() -> Block + = "{" __ exprs:(i:expr() statement_separator() {i})* __ "}" { Block(exprs) } + + rule func() -> Function + = s:position!() t:$"fn" __ "(" __ args:name_value_pairs() __ ")" __ ret:expr() __ block:block()? e:position!() + { Function { args, ret, block, text: t.parent().substr(s..e), } } + + rule name_value_pair() -> NameValuePair + = name:ident() __ ":" __ r#type:expr() { NameValuePair { name, r#type } } + + rule name_value_pairs() -> Vec + = v:(name_value_pair() ** ("," __)) { v } + + rule import() -> Import + = "import" _ expr:expr() { Import(expr) } + + rule name_pattern() -> NamePattern + = "(" __ tuple:(ident() ** ("," __)) __ ")" { NamePattern::Tuple(tuple) } + / "[" __ slice:(ident() ** ("," __)) __ "]" { NamePattern::List(slice) } + / ident:ident() { NamePattern::Single(ident) } + + rule const_decl() -> ConstDecl = + names:name_pattern() _? ":" r#type:(_ t:expr() _ {t})? ":" _ value:expr() + { ConstDecl { names, r#type, value } } + + rule var_decl() -> VarDecl = + names:name_pattern() _? ":" r#type:(_ t:expr() _ {t})? "=" _ value:expr() + { VarDecl { names, r#type, value } } + + rule for_loop() -> For = + "for" _ names:name_pattern() _ "in" _ value:expr() _ block:block() + { For { names, value, block } } + + pub rule compilation_unit() -> CompilationUnit = + __ imports:(i:import() statement_separator() {i})* + __ decls:(d:const_decl() statement_separator() {d})* + __ + { CompilationUnit { imports, decls } } + + // #### MISC #### + rule _ = quiet! { [' '|'\t']+ } + rule __ = quiet! { [' '|'\t'|'\n']* } + rule statement_separator() = quiet! { [';'|'\n'] __ } + } +} + +pub use leaf_parser::compilation_unit; diff --git a/parsing/Cargo.toml b/parsing/Cargo.toml deleted file mode 100644 index 7e68ae6..0000000 --- a/parsing/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "leaf_parsing" -version = "0.1.0" -edition = "2024" - -[dependencies] -arcstr = "1.2.0" -chumsky = { version = "0.11.1", features = ["pratt"] } -logos = "0.15.1" diff --git a/parsing/src/ast.rs b/parsing/src/ast.rs deleted file mode 100644 index a5d6078..0000000 --- a/parsing/src/ast.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::tokenizer::Token; - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct AstRoot { - pub imports: Vec, - pub decls: Vec, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Ident { - pub tok_ident: Token, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Import { - pub tok_pub: Option, - pub tok_import: Token, - pub value: Value, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Struct { - pub tok_struct: Token, - pub tok_open_curly: Token, - pub fields: Vec, - pub tok_close_curly: Token, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct StructField { - pub ident: Ident, - pub tok_colon: Token, - pub r#type: Value, -} - -pub enum Number { - Integer { value: i128, token: Token }, - Decimal { value: f64, token: Token }, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub enum Value { - Ident(Ident), - Struct(Struct), - Statement(Statement), - - BinaryOp { - lhs: Box, - tok_op: Token, - rhs: Box, - }, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub enum Statement { - While(While), - Return(Return), - ConstDecl(ConstDecl), -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct ConstDecl { - pub name: Ident, - pub tok_colon_t: Token, - pub r#type: Option>, - pub tok_colon_v: Token, - pub value: Box, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct While { - pub tok_while: Token, - pub condition: Box, - pub tok_open_curly: Token, - pub statements: Vec, - pub tok_close_curly: Token, -} - -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Return { - pub tok_return: Token, - pub value: Box, -} diff --git a/parsing/src/lib.rs b/parsing/src/lib.rs deleted file mode 100644 index 1abcb7c..0000000 --- a/parsing/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -use arcstr::Substr; -use std::path::PathBuf; - -pub mod ast; -pub mod parser; -pub mod tokenizer; - -pub struct SourceCode { - pub text: Substr, - pub file: PathBuf, -} diff --git a/parsing/src/main.rs b/parsing/src/main.rs deleted file mode 100644 index 40dba9e..0000000 --- a/parsing/src/main.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::path::PathBuf; - -use arcstr::literal_substr; -use chumsky::{Parser, input::Stream}; -use leaf_parsing::{SourceCode, tokenizer::tokenize}; - -fn main() { - let source = SourceCode { - text: literal_substr!( - " - import leaf.intrinsics; - import leaf.values; - - Vector3 :: struct { - x: 2, - } - " - ), - file: PathBuf::from("test.leaf"), - }; - let tokens = tokenize(&source).unwrap(); - let stream = Stream::from_iter(tokens.iter().cloned()); - let ast = leaf_parsing::parser::ast_root().parse(stream); - println!("{ast:#?}") -} diff --git a/parsing/src/parser.rs b/parsing/src/parser.rs deleted file mode 100644 index cfd2f54..0000000 --- a/parsing/src/parser.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::{ast::*, tokenizer::*}; -use chumsky::{ - extra::Err, - input::Stream, - pratt::{infix, left}, - prelude::*, - util::Maybe, -}; - -pub type Tokens = Stream; - -pub fn parse<'l>(tokens: Vec) -> ParseResult> { - let stream = Stream::from_iter(tokens); - ast_root().parse(stream) -} - -macro_rules! make_parsers { - ( - $( - $vis:vis fn $name:ident() -> $type:ty { - $($tt:tt)* - } - )* - ) => { - $( - $vis fn $name<'l, T: Iterator + 'l>() -> impl Parser<'l, Tokens, $type, Err>> { - $($tt)* - } - )* - }; -} - -make_parsers! { - pub fn ident() -> Ident { - tok(TokenKind::Ident).map(|tok_ident| Ident { tok_ident }) - } - - pub fn value() -> Value { - let atom = choice(( - choice(( - ident().map(Value::Ident), - type_struct().map(Value::Struct), - )), - statement_val(), - )); - atom.pratt(( - infix(left(1), tok(TokenKind::Add), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - infix(left(1), tok(TokenKind::Sub), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - infix(left(2), tok(TokenKind::Mul), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - infix(left(2), tok(TokenKind::Div), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - infix(left(2), tok(TokenKind::Mod), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - infix(left(3), tok(TokenKind::Period), |lhs, tok_op, rhs, _| Value::BinaryOp { lhs: Box::new(lhs), tok_op, rhs: Box::new(rhs) }), - )).boxed() - } - - pub fn type_struct() -> Struct { - let struct_fld = ident() - .then(tok(TokenKind::Colon)) - .then(value()) - .map(|((ident, tok_colon), r#type)| StructField { ident, tok_colon, r#type }); - - tok(TokenKind::Struct) - .then(tok(TokenKind::OpenCurly)) - .then(struct_fld.separated_by(tok(TokenKind::Comma)).allow_trailing().collect()) - .then(tok(TokenKind::CloseCurly)) - .map(|(((tok_struct, tok_open_curly), fields), tok_close_curly)| { - Struct { tok_struct, tok_open_curly, fields, tok_close_curly } - }) - } - - pub fn statement() -> Statement { - choice(( - const_decl().map(Statement::ConstDecl), - statement_while().map(Statement::While), - statement_return().map(Statement::Return), - )).boxed() - } - - pub fn statement_val() -> Value { - choice(( - statement_while().map(Statement::While), - statement_return().map(Statement::Return), - )).map(Value::Statement) - } - - pub fn statement_return() -> Return { - tok(TokenKind::Return) - .then(value()) - .map(|(tok_return, value)| Return { tok_return, value: Box::new(value) }) - } - - pub fn statement_while() -> While { - tok(TokenKind::While) - .then(value()) - .then(tok(TokenKind::OpenCurly)) - .then(statement().repeated().collect()) - .then(tok(TokenKind::CloseCurly)) - .map(| ((((tok_while, condition), tok_open_curly), statements), tok_close_curly)| While { - tok_while, condition: Box::new(condition), tok_open_curly, statements, tok_close_curly - }) - } - - pub fn const_decl() -> ConstDecl { - ident() - .then(tok(TokenKind::Colon)) - .then(value().or_not()) - .then(tok(TokenKind::Colon)) - .then(value()) - .map(|((((name, tok_colon_t), r#type), tok_colon_v), value)| ConstDecl { - name, - tok_colon_t, - r#type: r#type.map(Box::new), - tok_colon_v, - value: Box::new(value), - }) - } - - pub fn import() -> Import { - tok(TokenKind::Pub).or_not() - .then(tok(TokenKind::Import)) - .then(value()) - .map(|((tok_pub, tok_import), value)| Import { tok_pub, tok_import, value }) - } - - pub fn ast_root() -> AstRoot { - let imports = import().then_ignore(tok(TokenKind::Semicolon)).repeated().collect::>(); - let decls = const_decl().repeated().collect::>(); - imports.then(decls).map(|(imports, decls)| AstRoot { imports, decls }) - } -} - -#[inline(always)] -fn tok<'l, T: Iterator + 'l>( - k: TokenKind, -) -> impl Parser<'l, Tokens, Token, Err>> { - custom(move |inp| { - let before = inp.cursor(); - match inp.next() { - Some(token @ Token { kind, .. }) if kind == k => Ok(token), - None => Err(Simple::new(None, inp.span_since(&before))), - Some(token) => Err(Simple::new( - Some(Maybe::Val(token)), - inp.span_since(&before), - )), - } - }) -} diff --git a/parsing/src/tokenizer.rs b/parsing/src/tokenizer.rs deleted file mode 100644 index 770da6a..0000000 --- a/parsing/src/tokenizer.rs +++ /dev/null @@ -1,161 +0,0 @@ -use arcstr::Substr; -use logos::{Logos, Source, source::Chunk}; -use std::ops::Range; - -use crate::SourceCode; - -#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[logos(source = SourceCode)] -pub enum TokenKind { - #[regex(r"[ \t\n\f\r]+", logos::skip)] - Whitespace, - - #[token("fn")] - Fn, - #[token("enum")] - Enum, - #[token("union")] - Union, - #[token("struct")] - Struct, - #[token("interface")] - Interface, - - #[token("pub")] - Pub, - #[token("mut")] - Mut, - #[token("for")] - For, - #[token("while")] - While, - #[token("yield")] - Yield, - #[token("import")] - Import, - #[token("return")] - Return, - #[token("continue")] - Continue, - - #[token("+")] - Add, - #[token("-")] - Sub, - #[token("*")] - Mul, - #[token("/")] - Div, - #[token("%")] - Mod, - #[token(":")] - Colon, - #[token(",")] - Comma, - #[token(".")] - Period, - #[token(";")] - Semicolon, - - #[token("=")] - Eq, - #[token("==")] - CmpEq, - #[token("!=")] - CmpNe, - #[token("<")] - CmpLt, - #[token("<=")] - CmpLe, - #[token(">")] - CmpGt, - #[token(">=")] - CmpGe, - - #[token("(")] - OpenRound, - #[token("[")] - OpenSquare, - #[token("{")] - OpenCurly, - #[token(")")] - CloseRound, - #[token("]")] - CloseSquare, - #[token("}")] - CloseCurly, - - #[regex(r"[a-zA-Z_][a-zA-Z0-9_]*")] - Ident, - #[regex(r"[0-9]+")] - #[regex(r"[0-9]+\.[0-9]+")] - Number, -} - -impl Source for SourceCode { - type Slice<'a> = Substr; - - #[inline] - fn len(&self) -> usize { - Source::len(self.text.as_str()) - } - - #[inline] - fn read<'a, Chunk>(&'a self, offset: usize) -> Option - where - Chunk: self::Chunk<'a>, - { - Source::read(self.text.as_str(), offset) - } - - #[inline] - unsafe fn read_byte_unchecked(&self, offset: usize) -> u8 { - unsafe { Source::read_byte_unchecked(self.text.as_str(), offset) } - } - - #[inline] - fn slice(&self, range: std::ops::Range) -> Option> { - _ = self.text.get(range.clone())?; - Some(self.text.substr(range)) - } - - #[inline] - unsafe fn slice_unchecked(&self, range: std::ops::Range) -> Self::Slice<'_> { - self.text.substr(range) - } - - #[inline] - fn is_boundary(&self, index: usize) -> bool { - Source::is_boundary(self.text.as_str(), index) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Token { - pub text: Substr, - pub kind: TokenKind, -} - -impl PartialEq for Token { - #[inline] - fn eq(&self, other: &TokenKind) -> bool { - self.kind == *other - } -} - -pub fn tokenize(source: &SourceCode) -> Result, Range> { - let mut tokens = Vec::with_capacity(1024); - let mut lexer = TokenKind::lexer(&source).spanned(); - while let Some((token, span)) = lexer.next() { - match token { - Ok(kind) => tokens.push(Token { - kind, - text: source.text.substr(span), - }), - Err(()) => { - return Err(lexer.slice().range()); - } - } - } - Ok(tokens) -} diff --git a/test.leaf b/test.leaf new file mode 100644 index 0000000..4404712 --- /dev/null +++ b/test.leaf @@ -0,0 +1,20 @@ +import leaf.reflection.comptime + +add :: fn(a: u32, b: u32) u32 { + a + b +} + +Vector :: fn(T: type, count: usize) type { + NAMES :: ["x", "y", "z", "w"] + + ty := TypeBuilder.struct() + for i in 0..count { + ty.push(Field { + name: NAMES[i], + type: T, + flags: FieldFlags.Public + }) + } + + ty.build() +}