From 0b4f169c6f6ea00341edf0ebb9f1d23acdded728 Mon Sep 17 00:00:00 2001 From: Mia Date: Fri, 27 Feb 2026 15:48:16 +0100 Subject: [PATCH] More stuff compiles --- Cargo.lock | 5 + allocators/src/lib.rs | 14 ++ assembly/Cargo.toml | 1 + assembly/src/assembly.rs | 41 +++- assembly/src/functions/ir.rs | 44 +++- assembly/src/functions/mod.rs | 1 + assembly/src/values/constants.rs | 16 +- assembly/src/values/mod.rs | 22 +- compiler/Cargo.toml | 2 + compiler/src/error.rs | 7 + compiler/src/lib.rs | 13 +- compiler/src/main.rs | 2 +- compiler/src/scope.rs | 337 ++++++++++++++++++++++++--- interpreter/Cargo.toml | 2 + interpreter/src/instruction_cache.rs | 4 +- interpreter/src/main.rs | 88 +++---- parser/src/ast.rs | 47 +++- parser/src/parser.rs | 13 +- rust-toolchain.toml | 1 - test.leaf | 26 +-- 20 files changed, 552 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5648ac..8d0ca76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,7 @@ dependencies = [ "bitflags", "boxcar", "derive_more", + "fxhash", "half", "leaf_allocators", "scc", @@ -121,6 +122,8 @@ name = "leaf_compiler" version = "0.1.0" dependencies = [ "arcstr", + "derive_more", + "fxhash", "leaf_allocators", "leaf_assembly", "leaf_parser", @@ -133,6 +136,8 @@ dependencies = [ "fxhash", "leaf_allocators", "leaf_assembly", + "leaf_compiler", + "leaf_parser", "scc", "smallvec", ] diff --git a/allocators/src/lib.rs b/allocators/src/lib.rs index 791c65f..9abf99f 100644 --- a/allocators/src/lib.rs +++ b/allocators/src/lib.rs @@ -37,10 +37,24 @@ impl<'l> dyn Allocator + 'l { &mut *(self.alloc_unsafe(data, layout, drop) as *mut T) } } + + pub fn alloc_str(&'l self, str: &str) -> &'l str { + unsafe { + let ptr = + self.alloc_unsafe(str.as_ptr(), Layout::array::(str.len()).unwrap(), None); + let slice = std::slice::from_raw_parts_mut(ptr, str.len()); + slice.copy_from_slice(str.as_bytes()); + std::str::from_utf8_unchecked(slice) + } + } } impl<'l> dyn SyncAllocator + 'l { pub fn alloc(&'l self, value: T) -> &'l mut T { ::alloc(self, value) } + + pub fn alloc_str(&'l self, value: &str) -> &'l str { + ::alloc_str(self, value) + } } diff --git a/assembly/Cargo.toml b/assembly/Cargo.toml index 6423dd3..25da544 100644 --- a/assembly/Cargo.toml +++ b/assembly/Cargo.toml @@ -11,3 +11,4 @@ half = "2.7.1" scc = "3.3.7" leaf_allocators = { path = "../allocators" } +fxhash = "0.2.1" diff --git a/assembly/src/assembly.rs b/assembly/src/assembly.rs index 73a41f3..c7ce21c 100644 --- a/assembly/src/assembly.rs +++ b/assembly/src/assembly.rs @@ -1,11 +1,12 @@ use crate::{ functions::Function, types::derivations::{FuncT, TypeDerivations}, - values::AnyConst, + values::Const, }; use derive_more::Debug; +use fxhash::FxBuildHasher; use leaf_allocators::SyncAllocator; -use scc::{HashMap, HashSet}; +use scc::HashMap; use std::{borrow::Cow, hash::Hash, sync::OnceLock}; #[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -28,6 +29,7 @@ pub type Ctx<'l> = &'l Context<'l>; pub struct Context<'l> { pub(crate) alloc: &'l dyn SyncAllocator, pub(crate) derivations: TypeDerivations<'l>, + strings: HashMap<&'l str, &'l str, FxBuildHasher>, assemblies: HashMap>, } @@ -49,6 +51,7 @@ impl<'l> Context<'l> { pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Self { alloc.alloc(Self { alloc, + strings: HashMap::default(), derivations: TypeDerivations::new(alloc), assemblies: Default::default(), }) @@ -64,6 +67,15 @@ impl<'l> Context<'l> { .entry_sync(ident.clone()) .or_insert_with(|| Assembly::new(self, ident)) } + + pub fn intern_str(&'l self, str: &str) -> &'l str { + if let Some(value) = self.strings.get_sync(str) { + return (*value).into(); + } + let str = self.alloc.alloc_str(str); + let _ = self.strings.insert_sync(str, str); + str + } } #[derive(Debug)] @@ -71,12 +83,7 @@ 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: HashSet>, + functions: boxcar::Vec<&'l Function<'l>>, } impl Eq for Assembly<'_> {} @@ -98,7 +105,7 @@ impl<'l> Assembly<'l> { let assembly = ctx.alloc.alloc(Self { ctx, ident, - constants: HashSet::new(), + functions: boxcar::vec![], }); assembly } @@ -114,9 +121,19 @@ impl<'l> Assembly<'l> { body: OnceLock::new(), declaring_assembly: self, }); - self.constants - .insert_sync(AnyConst::Function(func)) - .unwrap(); + self.functions.push(func); func } + + pub fn find_function( + &'l self, + filter: impl Fn(&'l Function<'l>) -> bool, + ) -> Option<&'l Function<'l>> { + for (_, func) in self.functions.iter() { + if filter(func) { + return Some(func); + } + } + None + } } diff --git a/assembly/src/functions/ir.rs b/assembly/src/functions/ir.rs index 2dc4632..b514bf2 100644 --- a/assembly/src/functions/ir.rs +++ b/assembly/src/functions/ir.rs @@ -59,7 +59,8 @@ impl<'l> Instruction<'l> { pub fn value_flags(&self) -> ValueFlags { match self.variant { - _ => todo!(), + InstructionVariant::StackAlloc(_) => ValueFlags::LValue, + _ => ValueFlags::empty(), } } @@ -73,8 +74,11 @@ impl<'l> Instruction<'l> { _ => unreachable!(), }, InstructionVariant::IAdd(a, _) => a.ty(), + InstructionVariant::IMul(a, _) => a.ty(), InstructionVariant::FAdd(a, _) => a.ty(), + InstructionVariant::FMul(a, _) => a.ty(), InstructionVariant::ICmp(_, _, _) => Type::Bool, + InstructionVariant::Call(f, _) => f.ty.ret_t, _ => todo!("{self:?}"), } } @@ -98,7 +102,9 @@ pub enum InstructionVariant<'l> { Store(Value<'l>, Value<'l>), IAdd(Value<'l>, Value<'l>), + IMul(Value<'l>, Value<'l>), FAdd(Value<'l>, Value<'l>), + FMul(Value<'l>, Value<'l>), ICmp(Value<'l>, Value<'l>, Cmp), FCmp(Value<'l>, Value<'l>, Cmp), @@ -132,11 +138,17 @@ impl std::fmt::Debug for InstructionVariant<'_> { Self::Load(v) => write!(f, "load {v}"), Self::Store(t, v) => write!(f, "store {t}, {v}"), Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"), + Self::IMul(a, b) => write!(f, "imul {a}, {b}"), Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"), + Self::FMul(a, b) => write!(f, "fmul {a}, {b}"), Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"), Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"), Self::Call(func, args) => { - write!(f, "call {:#?}(", *func as *const Function)?; + write!( + f, + "call {}(", + func.name.get().unwrap_or(&"".into()) + )?; let mut separator = ""; for arg in args { write!(f, "{separator}{arg}")?; @@ -252,6 +264,22 @@ impl<'l> BlockBuilder<'l> { } } + pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> { + let [a_ty, b_ty] = [a.ty(), b.ty()]; + + match (a_ty, b_ty) { + (Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => { + let inst = self.push_instruction(InstructionVariant::IMul(a, b))?; + Ok(inst.into()) + } + (Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => { + let inst = self.push_instruction(InstructionVariant::FMul(a, b))?; + Ok(inst.into()) + } + _ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()), + } + } + pub fn cmp( &mut self, a: Value<'l>, @@ -358,7 +386,7 @@ impl<'l> BlockBuilder<'l> { pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> { if !self.has_termination() { - return Err(format!("Block #{} has not termination.", self.block.id).into()); + return Err(format!("Block #{} has no termination.", self.block.id).into()); } self.block.instructions.set(self.instructions).unwrap(); Ok(self.block) @@ -398,15 +426,15 @@ impl<'l> FunctionBodyBuilder<'l> { builder } - pub fn current_block(&self) -> &'l Block<'l> { - self.blocks[self.current_block].block + pub fn current_block(&self) -> &BlockBuilder<'l> { + &self.blocks[self.current_block] } pub fn set_current_block(&mut self, block: &'l Block<'l>) -> Option<&'l Block<'l>> { match std::ptr::eq(block.func, self.func) { false => None, true => { - let current = self.current_block(); + let current = &self.blocks[self.current_block].block; self.current_block = block.id as usize; Some(current) } @@ -466,6 +494,10 @@ impl<'l> FunctionBodyBuilder<'l> { self.current_builder().add(a, b) } + pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> { + self.current_builder().mul(a, b) + } + pub fn cmp( &mut self, a: Value<'l>, diff --git a/assembly/src/functions/mod.rs b/assembly/src/functions/mod.rs index da5aef4..f96bcc6 100644 --- a/assembly/src/functions/mod.rs +++ b/assembly/src/functions/mod.rs @@ -62,6 +62,7 @@ impl FmtDebug for Function<'_> { f.debug_struct("Function") .field("ty", &format_args!("{}", self.ty)) + .field("name", &self.name.get()) .field("body", body) .finish_non_exhaustive() } diff --git a/assembly/src/values/constants.rs b/assembly/src/values/constants.rs index 2910848..83595bc 100644 --- a/assembly/src/values/constants.rs +++ b/assembly/src/values/constants.rs @@ -1,4 +1,4 @@ -use crate::{functions::Function, types::Type}; +use crate::{functions::Function, types::Type, values::ValueFlags}; use derive_more::{Debug, *}; use half::f16; use std::hash::Hash; @@ -9,13 +9,17 @@ pub enum Int { I16(i16), I32(i32), I64(i64), + #[from] I128(i128), + ISize(i128), U8(u8), U16(u16), U32(u32), U64(u64), + #[from] U128(u128), + USize(u128), } #[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq)] @@ -90,15 +94,25 @@ impl<'l> AnyConst<'l> { Self::Int(Int::I32(_)) => Type::I32, Self::Int(Int::I64(_)) => Type::I64, Self::Int(Int::I128(_)) => Type::I128, + Self::Int(Int::ISize(_)) => Type::ISIZE, Self::Int(Int::U8(_)) => Type::U8, Self::Int(Int::U16(_)) => Type::U16, Self::Int(Int::U32(_)) => Type::U32, Self::Int(Int::U64(_)) => Type::U64, Self::Int(Int::U128(_)) => Type::U128, + Self::Int(Int::USize(_)) => Type::USIZE, Self::Float(Float::F16(_)) => Type::F16, Self::Float(Float::F32(_)) => Type::F32, Self::Float(Float::F64(_)) => Type::F64, _ => todo!("{self:?}"), } } + + pub fn flags(&self) -> super::ValueFlags { + match self { + AnyConst::Function(function) => ValueFlags::Function, + AnyConst::Type(_) => ValueFlags::Type, + _ => ValueFlags::Const, + } + } } diff --git a/assembly/src/values/mod.rs b/assembly/src/values/mod.rs index 6cb8211..f72d1d1 100644 --- a/assembly/src/values/mod.rs +++ b/assembly/src/values/mod.rs @@ -1,4 +1,7 @@ -use crate::{functions::ir::Instruction, types::Type}; +use crate::{ + functions::{Function, ir::Instruction}, + types::Type, +}; use bitflags::bitflags; use derive_more::{Debug, Display, *}; use std::hash::Hash; @@ -11,6 +14,7 @@ bitflags! { pub struct ValueFlags: u16 { const Mutable = 0b00000000_00000001; const Volatile = 0b00000000_00000010; + const LValue = 0b00000000_00000100; const Const = 0b00000001_00000000; const ConstOnly = 0b00000011_00000000; const Type = 0b00000111_00000000; @@ -20,10 +24,15 @@ bitflags! { #[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)] pub enum Value<'l> { + Uninit, #[display("{_0}")] Constant(AnyConst<'l>), #[display("{_0}")] Instruction(&'l Instruction<'l>), + + #[debug("${_0}")] + #[display("${_0}")] + Parameter(usize, &'l Function<'l>), } impl From for Value<'_> { @@ -37,6 +46,17 @@ impl<'l> Value<'l> { match self { Value::Constant(v) => v.ty(), Value::Instruction(v) => v.value_ty(), + Value::Parameter(i, f) => f.ty.par_t[*i], + Value::Uninit => unreachable!(), + } + } + + pub fn flags(&self) -> ValueFlags { + match self { + Value::Instruction(instruction) => instruction.value_flags(), + Value::Parameter(_, _) => ValueFlags::empty(), + Value::Constant(c) => c.flags(), + _ => todo!("{self:?}"), } } } diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 16b4b79..707ef95 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -8,3 +8,5 @@ leaf_parser = { path = "../parser" } leaf_assembly = { path = "../assembly" } leaf_allocators = { path = "../allocators" } arcstr = "1.2.0" +derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] } +fxhash = "0.2.1" diff --git a/compiler/src/error.rs b/compiler/src/error.rs index 79fbb2b..b2bbf44 100644 --- a/compiler/src/error.rs +++ b/compiler/src/error.rs @@ -1,3 +1,4 @@ +use derive_more::Debug; use leaf_parser::{LineCol, ParseError}; use std::ops::Range; @@ -8,10 +9,14 @@ pub enum Kind { Unknown = 0x0000, Parsing = 0x0100, + InvalidInteger = 0x0101, SymbolNotFound = 0x0200, UninitializedSymbol = 0x0201, NotAType = 0x0202, + NotAFunction = 0x0205, + InvalidIntegerType = 0x0203, + InvalidType = 0x0204, FunctionCompilationFailed = 0x0301, } @@ -20,10 +25,12 @@ pub enum Kind { pub enum Location { None, Range { + #[debug("{:?}", file.file)] file: SourceFile, range: Range, }, Position { + #[debug("{:?}", file.file)] file: SourceFile, position: LineCol, }, diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 232aeeb..e920644 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -3,11 +3,11 @@ use arcstr::literal_substr; use leaf_allocators::SyncAllocator; use leaf_assembly::{ assembly::{Assembly, AssemblyIdentifier, Context}, - types::{FloatT, IntT, Type}, + types::Type, values::{AnyConst, Value}, }; use leaf_parser::SourceCode; -use std::{collections::HashMap, sync::Arc, u32}; +use std::{ops::Deref, sync::Arc}; mod error; mod scope; @@ -19,6 +19,14 @@ pub struct CompilationContext<'l> { alloc: &'l dyn SyncAllocator, } +impl<'l> Deref for CompilationContext<'l> { + type Target = Context<'l>; + + fn deref(&self) -> &Self::Target { + self.ctx + } +} + impl<'l> CompilationContext<'l> { pub fn new(alloc: &'l dyn SyncAllocator) -> Self { Self { @@ -56,6 +64,7 @@ impl<'l> CompilationContext<'l> { scope.insert( literal_substr!($id), Value::Constant(AnyConst::Type($ty.into())), + false, ); )* }; diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 27433e1..8b1a559 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -19,7 +19,7 @@ fn main() { file: PathBuf::from("../test.leaf"), })], ) { - println!("{}", err.message); + println!("{:#?}", err); } else { println!("{:#?}", context.get_assembly(&ident)); } diff --git a/compiler/src/scope.rs b/compiler/src/scope.rs index a9ba2f5..4cdf6c4 100644 --- a/compiler/src/scope.rs +++ b/compiler/src/scope.rs @@ -1,22 +1,38 @@ use arcstr::Substr; use leaf_assembly::{ assembly::Assembly, - types::Type, - values::{AnyConst, Value}, + functions::{ + Function, + ir::{Cmp, FunctionBodyBuilder}, + }, + types::{Type, derivations::PtrT}, + values::{AnyConst, Int, Value, ValueFlags}, }; use leaf_parser::{ SourceCode, - ast::{self, ConstDecl, Expr, Ident}, + ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, NamePattern, While}, }; use std::{collections::HashMap, sync::Arc}; use crate::error::*; +#[derive(Default)] +struct ExpressionContext<'l, 'r> { + decl_names: Option<&'r NamePattern>, + builder: Option<&'r mut FunctionBodyBuilder<'l>>, +} + +#[derive(Clone)] +struct Variable<'l> { + value: Value<'l>, + mutable: bool, +} + #[derive(Clone)] pub struct Scope<'l> { assembly: &'l Assembly<'l>, source: Arc, - values: HashMap>>, + values: HashMap>, } impl<'l> Scope<'l> { @@ -28,26 +44,49 @@ impl<'l> Scope<'l> { } } - pub fn insert(&mut self, name: Substr, value: Value<'l>) { - self.values.insert(name, Some(value)); + pub fn insert(&mut self, name: Substr, value: Value<'l>, mutable: bool) { + self.values.insert(name, Variable { value, mutable }); } 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); + self.values.insert( + name.clone(), + Variable { + value: Value::Uninit, + mutable: false, + }, + ); } } } pub fn define_constants(&mut self, decl: &[ConstDecl]) -> Result<(), CompilationError> { for val in decl { - let expr = self.compile_expression(&val.value)?; + let expr = self.compile_expression( + &val.value, + &mut ExpressionContext { + decl_names: Some(&val.names), + ..Default::default() + }, + )?; + match &val.names { + NamePattern::Single(ident) => { + self.values.get_mut(&ident.0).unwrap().value = expr; + } + NamePattern::Tuple(_) => todo!(), + NamePattern::List(_) => todo!(), + } } Ok(()) } - pub fn compile_expression(&self, expr: &Expr) -> Result, CompilationError> { + fn compile_expression( + &mut self, + expr: &Expr, + ctx: &mut ExpressionContext<'l, '_>, + ) -> Result, CompilationError> { match expr { Expr::Ident(Ident(name)) => match self.values.get(name) { None => Err(CompilationError { @@ -59,7 +98,10 @@ impl<'l> Scope<'l> { }, cause: None, }), - Some(None) => Err(CompilationError { + Some(Variable { + value: Value::Uninit, + .. + }) => Err(CompilationError { kind: Kind::UninitializedSymbol, message: format!("Symbol `{name}` is not initialized at this time."), location: Location::Range { @@ -68,32 +110,273 @@ impl<'l> Scope<'l> { }, cause: None, }), - Some(Some(value)) => Ok(*value), + Some(Variable { 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(), + + Expr::Func(func) => self + .compile_function(func, ctx) + .map(|f| Value::Constant(AnyConst::Function(f))) + .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)), + }), + + Expr::ConstDecl(decl) => self.compile_decl(&decl.names, &decl.value, false, ctx), + Expr::VarDecl(decl) => self.compile_decl(&decl.names, &decl.value, true, ctx), + + Expr::Number(n) => { + macro_rules! parse_number { + ($ty: ty, $id: ident) => { + match n.text.split_at_checked(2) { + Some(("0b", value)) => <$ty>::from_str_radix(value, 2), + Some(("0x", value)) => <$ty>::from_str_radix(value, 16), + _ => <$ty>::from_str_radix(&n.text, 10), + } + .map(|v| Value::Constant(AnyConst::Int(Int::$id(v)))) + .map_err(|_| CompilationError { + kind: Kind::InvalidInteger, + message: format!("`{}` is not a valid integer.", n.text), + location: Location::Range { + file: self.source.clone(), + range: n.text.range(), + }, + cause: None, + }) + }; + } + match n.r#type.as_ref().map(|v| v.as_str()) { + None => parse_number!(i128, ISize), + Some("i8") => parse_number!(i8, I8), + Some("i16") => parse_number!(i16, I16), + Some("i32") => parse_number!(i32, I32), + Some("i64") => parse_number!(i64, I64), + Some("i128") => parse_number!(i128, I128), + Some("isize") => parse_number!(i128, ISize), + Some("u8") => parse_number!(u8, U8), + Some("u16") => parse_number!(u16, U16), + Some("u32") => parse_number!(u32, U32), + Some("u64") => parse_number!(u64, U64), + Some("u128") => parse_number!(u128, U128), + Some("usize") => parse_number!(u128, USize), + Some(ty) => { + return Err(CompilationError { + kind: Kind::InvalidIntegerType, + message: format!("`{ty}` is not a valid integer type."), + location: Location::Range { + file: self.source.clone(), + range: n.text.range(), + }, + cause: None, + }); + } + } + } + + Expr::Binary(expr) => { + let BinaryExpr { + lhs: lhs_expr, + rhs: rhs_expr, + op, + } = &**expr; + + let mut lhs = self.compile_expression(lhs_expr, ctx)?; + if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) { + lhs = ctx.builder.as_mut().unwrap().load(lhs).unwrap(); + } + + let mut rhs = self.compile_expression(rhs_expr, ctx)?; + if rhs.flags().contains(ValueFlags::LValue) { + rhs = ctx.builder.as_mut().unwrap().load(rhs).unwrap(); + } + + let builder = ctx.builder.as_mut().unwrap(); + match (lhs.ty(), rhs.ty(), op) { + (Type::U32, Type::U32, BinaryOp::Add(_)) => Ok(builder.add(lhs, rhs).unwrap()), + (Type::U32, Type::U32, BinaryOp::Mul(_)) => Ok(builder.mul(lhs, rhs).unwrap()), + (Type::U32, Type::U32, BinaryOp::Lt(_)) => { + Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap()) + } + (Type::Ptr(PtrT { base, mutable, .. }), ty, BinaryOp::Assign(_)) => { + match *base == ty { + true => Ok(builder.store(lhs, rhs).unwrap()), + false => Err(CompilationError { + kind: Kind::InvalidType, + message: format!( + "Cannot assign a value of type `{ty}` to a value of type `{base}`." + ), + location: Location::Range { + file: self.source.clone(), + range: rhs_expr.range(), + }, + cause: None, + }), + } + } + (a, b, _) => unimplemented!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"), + } + } + + Expr::While(expr) => { + let While { value, block } = &**expr; + + let mut builder = ctx.builder.as_mut().unwrap(); + let cond_block = builder.create_block(); + let exec_block = builder.create_block(); + let exit_block = builder.create_block(); + + builder.jump(cond_block).unwrap(); + builder.set_current_block(cond_block); + let condition = self.compile_expression(value, ctx)?; + + builder = ctx.builder.as_mut().unwrap(); + builder.branch(condition, exec_block, exit_block).unwrap(); + builder.set_current_block(exec_block); + + let mut scope = self.clone(); + let mut ctx = ExpressionContext { + builder: Some(builder), + ..Default::default() + }; + + let mut last_expr = None; + for expr in &block.0 { + last_expr = Some(scope.compile_expression(expr, &mut ctx)?); + } + builder.jump(cond_block).unwrap(); + builder.set_current_block(exit_block); + Ok(last_expr.unwrap_or(Value::Constant(AnyConst::Void))) + } + + Expr::Call { + func, + args: args_exprs, + } => { + let func = match self.compile_expression(func, ctx)? { + Value::Constant(AnyConst::Function(func)) => func, + _ => { + return Err(CompilationError { + kind: Kind::NotAFunction, + message: "Value is not a function".into(), + location: Location::Range { + file: self.source.clone(), + range: func.range(), + }, + cause: None, + }); + } + }; + let mut args = Vec::with_capacity(args_exprs.len()); + for expr in args_exprs { + let mut arg = self.compile_expression(expr, ctx)?; + if arg.flags().contains(ValueFlags::LValue) { + arg = ctx.builder.as_mut().unwrap().load(arg).unwrap(); + } + args.push(arg); + } + let builder = ctx.builder.as_mut().unwrap(); + Ok(builder.call(func, args).unwrap()) + } + + Expr::Type(expr) => match &**expr { + ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? { + Value::Constant(AnyConst::Type(ty)) => { + Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into()) + } + _ => todo!(), }, - 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)?)?; + fn compile_function( + &mut self, + ast: &ast::Function, + ctx: &mut ExpressionContext<'l, '_>, + ) -> Result<&'l Function<'l>, CompilationError> { + let ret_ty = match ast.ret.as_ref() { + None => Value::Constant(AnyConst::Type(Type::Void)), + Some(ty) => self.compile_expression(ty, ctx)?, + }; + let ret_ty = self.assert_type(ret_ty)?; + let mut par_ty = Vec::with_capacity(ast.args.len()); + for arg in &ast.args { + let ty = self.compile_expression(&arg.r#type, ctx)?; + let ty = self.assert_type(ty)?; par_ty.push(ty); } + let fn_ty = ret_ty.make_fn(par_ty); - Ok(Value::Constant(AnyConst::Function( - self.assembly.create_function(fn_ty), - ))) + let func = self.assembly.create_function(fn_ty); + if let Some(NamePattern::Single(func_name)) = &ctx.decl_names { + func.name.set(func.ctx().intern_str(&func_name.0)).unwrap(); + if let Some(variable) = self.values.get_mut(func_name.as_str()) { + variable.value = Value::Constant(AnyConst::Function(func)); + } + }; + + let Some(block) = &ast.block else { + return Ok(func); + }; + + let mut scope = self.clone(); + for (i, arg) in ast.args.iter().enumerate() { + scope.insert(arg.name.0.clone(), Value::Parameter(i, func), false); + } + + let mut builder = func.create_body().unwrap(); + let mut ctx = ExpressionContext { + builder: Some(&mut builder), + ..Default::default() + }; + + let mut last_expr = None; + for expr in &block.0 { + last_expr = Some(scope.compile_expression(expr, &mut ctx)?); + } + + if !builder.current_block().has_termination() { + builder.ret(last_expr).unwrap(); + } + + builder.build().unwrap(); + + Ok(func) + } + + fn compile_decl( + &mut self, + names: &NamePattern, + value: &Expr, + mutable: bool, + ctx: &mut ExpressionContext<'l, '_>, + ) -> Result, CompilationError> { + let mut sub_ctx = ExpressionContext { + decl_names: Some(names), + builder: ctx.builder.as_mut().map(|v| &mut **v), + }; + let mut value = self.compile_expression(value, &mut sub_ctx)?; + if mutable { + let builder = sub_ctx.builder.unwrap(); + let variable = builder.stack_alloc(value.ty()).unwrap(); + builder.store(variable, value).unwrap(); + value = variable; + } + match names { + NamePattern::Single(ident) => { + self.values + .insert(ident.0.clone(), Variable { value, mutable }); + } + NamePattern::Tuple(_) => todo!(), + NamePattern::List(_) => todo!(), + } + Ok(AnyConst::Void.into()) } fn assert_type(&self, val: Value<'l>) -> Result, CompilationError> { diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 21759e3..c353cb5 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -7,5 +7,7 @@ edition = "2024" fxhash = "0.2.1" leaf_allocators = { path = "../allocators" } leaf_assembly = { path = "../assembly" } +leaf_compiler = { path = "../compiler" } +leaf_parser = { path = "../parser" } scc = "3.3.7" smallvec = "1.15.1" diff --git a/interpreter/src/instruction_cache.rs b/interpreter/src/instruction_cache.rs index 031e5d9..ed54a6a 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::{IntT, Type}, - values::{AnyConst, Const, Int, Value}, + values::{AnyConst, Int, Value}, }; use scc::HashMap; use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc}; @@ -246,7 +246,7 @@ impl<'l> InstructionCacheEntry<'l> { Value::Instruction(i) => { ranges.push(memory_ranges[&i.id()].clone()); } - _ => todo!("Unimplemented variant `{arg}`."), + _ => todo!("Unimplemented variant `{arg:?}`."), } } opcodes.push(OpCode::Call( diff --git a/interpreter/src/main.rs b/interpreter/src/main.rs index fa58839..086d0cf 100644 --- a/interpreter/src/main.rs +++ b/interpreter/src/main.rs @@ -1,66 +1,44 @@ use leaf_allocators::SyncArenaAllocator; -use leaf_assembly::assembly::{AssemblyIdentifier, Context, Version}; -use leaf_assembly::functions::ir::Cmp; -use leaf_assembly::types::Type; -use leaf_assembly::values::Int; +use leaf_assembly::assembly::{AssemblyIdentifier, Version}; +use leaf_compiler::CompilationContext; use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction}; +use leaf_parser::SourceCode; use std::ffi::CStr; +use std::path::PathBuf; +use std::sync::Arc; use std::time::{Duration, Instant}; fn main() { let allocator = SyncArenaAllocator::default(); - let context = Context::new(&allocator); - let assembly = context.get_or_create_assembly(AssemblyIdentifier { + let context = CompilationContext::new(&allocator); + let ident = AssemblyIdentifier { version: Version::default(), - name: "test".into(), - }); - - let puts = assembly.create_function(Type::Void.make_fn([Type::U8.make_ptr(false).into()])); - - let print_u32 = assembly.create_function(Type::Void.make_fn([Type::U32.into()])); - - let random_func = { - let func = assembly.create_function(Type::U32.make_fn([])); - let mut builder = func.create_body().unwrap(); - - let loop_check = builder.create_block(); - let loop_body = builder.create_block(); - let loop_end = builder.create_block(); - - let var0 = builder.stack_alloc(Type::U32).unwrap(); - let var1 = builder.stack_alloc(Type::U32).unwrap(); - - builder.store(var0, Int::U32(42).into()).unwrap(); - - builder.store(var1, Int::U32(0u32).into()).unwrap(); - - builder.jump(loop_check).unwrap(); - builder.set_current_block(loop_check); - - let a = builder.load(var0).unwrap(); - let b = builder.load(var1).unwrap(); - let c = builder.add(a, b).unwrap(); - builder.call(print_u32, vec![c]).unwrap(); - - let cond = builder.cmp(c, Int::U32(69u32).into(), Cmp::Lt).unwrap(); - builder.branch(cond, loop_body, loop_end).unwrap(); - - builder.set_current_block(loop_body); - let v = builder.load(var1).unwrap(); - let v = builder.add(v, Int::U32(1u32).into()).unwrap(); - builder.store(var1, v).unwrap(); - builder.jump(loop_check).unwrap(); - - builder.set_current_block(loop_end); - let v = builder.load(var1).unwrap(); - builder.ret(Some(v)).unwrap(); - - let body = builder.build().unwrap(); - println!("{body:#?}"); - func + name: "interpreter.il".into(), }; + context + .extend( + ident.clone(), + &[Arc::new(SourceCode { + text: std::fs::read_to_string("../test.leaf").unwrap().into(), + file: PathBuf::from("../test.leaf"), + })], + ) + .unwrap(); - let mut interpreter = Interpreter::new(context); + let mut interpreter = Interpreter::new(&context); + let assembly = context.get_assembly(&ident).unwrap(); + + let puts = assembly + .find_function(|f| f.name.get() == Some(&"puts")) + .unwrap(); + + let print_u32 = assembly + .find_function(|f| f.name.get() == Some(&"print_u32")) + .unwrap(); + + let main = assembly + .find_function(|f| f.name.get() == Some(&"main")) + .unwrap(); interpreter.register_function( puts, @@ -91,7 +69,7 @@ fn main() { { let now = Instant::now(); - let result = interpreter.run(random_func, vec![]); + let result = interpreter.run(main, vec![]); let elapsed = now.elapsed(); println!("Ret Value: {result:?}"); println!("Cold Time: {:?}", elapsed) @@ -101,7 +79,7 @@ fn main() { let mut duration = Duration::default(); for _ in 0..LOOPS { let now = Instant::now(); - let _ = interpreter.run(random_func, vec![]); + let _ = interpreter.run(main, vec![]); duration += now.elapsed(); } println!("Warm Time: {:?}", duration / LOOPS); diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 4d30b7e..b653e87 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use arcstr::Substr; use derive_more::Deref; @@ -32,6 +34,8 @@ pub enum Expr { Block(Block), #[debug("{_0:?}")] Func(Box), + #[debug("{_0:?}")] + Type(Box), #[debug("{_0:?}")] ConstDecl(Box), @@ -39,6 +43,8 @@ pub enum Expr { VarDecl(Box), #[debug("{_0:?}")] For(Box), + #[debug("{_0:?}")] + While(Box), Call { func: Box, @@ -46,6 +52,14 @@ pub enum Expr { }, } +impl Expr { + pub fn range(&self) -> Range { + match self { + _ => todo!(), + } + } +} + #[derive(Debug)] pub struct BinaryExpr { pub lhs: Expr, @@ -62,14 +76,23 @@ pub struct IndexingExpr { #[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), + #[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}")] Lt(Substr), + #[debug("{_0}")] Gt(Substr), + #[debug("{_0}")] Le(Substr), + #[debug("{_0}")] Ge(Substr), + #[debug("{_0}")] Range(Substr), + #[debug("{_0}")] Assign(Substr), +} + +#[derive(Debug)] +pub enum Type { + Ptr { base: Expr, mutable: bool }, } #[derive(Debug)] @@ -107,7 +130,7 @@ impl NamePattern { pub struct Function { pub text: Substr, pub args: Vec, - pub ret: Expr, + pub ret: Option, pub block: Option, } @@ -133,6 +156,12 @@ pub struct For { pub block: Block, } +#[derive(Debug)] +pub struct While { + pub value: Expr, + pub block: Block, +} + #[derive(Debug)] pub struct CompilationUnit { pub imports: Vec, diff --git a/parser/src/parser.rs b/parser/src/parser.rs index d77ee96..6044ce4 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -84,14 +84,21 @@ peg::parser! { -- lhs:(@) __ op:$("..") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) } -- + lhs:(@) __ op:$("<") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs })) } + lhs:(@) __ op:$(">") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs })) } + lhs:(@) __ op:$("<=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs })) } + lhs:(@) __ op:$(">=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Ge(op), rhs })) } + -- block:block() { Expr::Block(block)} for_loop:for_loop() { Expr::For(Box::new(for_loop))} + while_loop:while_loop() { Expr::While(Box::new(while_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 } + "*" __ m:"mut"? __ v:expr() { Expr::Type(Box::new(Type::Ptr { base:v, mutable: m.is_some() })) } v:string() { Expr::String(v) } v:number() { Expr::Number(v) } v:ident() { Expr::Ident(v) } @@ -101,7 +108,7 @@ peg::parser! { = "{" __ 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!() + = s:position!() t:$"fn" __ "(" __ args:name_value_pairs() __ ")" __ ret:("->" __ e:expr() {e})? __ block:block()? e:position!() { Function { args, ret, block, text: t.parent().substr(s..e), } } rule name_value_pair() -> NameValuePair @@ -130,6 +137,10 @@ peg::parser! { "for" _ names:name_pattern() _ "in" _ value:expr() _ block:block() { For { names, value, block } } + rule while_loop() -> While = + "while" _ value:expr() _ block:block() + { While { value, block } } + pub rule compilation_unit() -> CompilationUnit = __ imports:(i:import() statement_separator() {i})* __ decls:(d:const_decl() statement_separator() {d})* diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9a2357f..1857fe2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,3 @@ [toolchain] -channel = "1.91.0" components = ["clippy", "rustfmt"] profile = "default" diff --git a/test.leaf b/test.leaf index 4404712..84d2746 100644 --- a/test.leaf +++ b/test.leaf @@ -1,20 +1,14 @@ -import leaf.reflection.comptime +puts :: fn(v: *u8) -> i32; +print_u32 :: fn(v: u32); -add :: fn(a: u32, b: u32) u32 { - a + b +print_up_to :: fn(max: u32) { + i := 0u32 + while i < max { + print_u32(i) + i = i + 1u32 + } } -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() +main :: fn() { + print_up_to(42u32) }