From 168a12b4fc7a8194786a78db43189bdc9e4cf31d Mon Sep 17 00:00:00 2001 From: Mia Date: Sat, 7 Mar 2026 16:24:32 +0100 Subject: [PATCH] Ifs and Phis --- .gitignore | 4 +- assembly/src/functions/ir.rs | 182 +++++++++++++++++--- assembly/src/types/mod.rs | 41 +++-- assembly/src/values/constants.rs | 6 + assembly/src/values/mod.rs | 2 +- backends/llvm/src/lib.rs | 127 +++++++++++--- compiler/src/error.rs | 1 + compiler/src/main.rs | 12 +- compiler/src/scope.rs | 278 ++++++++++++++++++++++++++----- parser/src/ast.rs | 40 +++-- parser/src/parser.rs | 69 ++++---- 11 files changed, 612 insertions(+), 150 deletions(-) diff --git a/.gitignore b/.gitignore index ed6a96a..2f127e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ target/ -test.leaf -out.asm +*.leaf +*.asm a.out .zed/ diff --git a/assembly/src/functions/ir.rs b/assembly/src/functions/ir.rs index e248424..a402846 100644 --- a/assembly/src/functions/ir.rs +++ b/assembly/src/functions/ir.rs @@ -5,7 +5,13 @@ use crate::{ values::{AnyConst, AnyValue, Value, ValueFlags, default_associated_values}, }; use derive_more::{Debug, Display}; -use std::{borrow::Cow, cell::UnsafeCell, hash::Hash, ops::Deref, sync::OnceLock}; +use std::{ + borrow::Cow, + cell::UnsafeCell, + hash::Hash, + ops::Deref, + sync::{OnceLock, atomic::AtomicU16}, +}; // Maybe unsafe but honestly it's extremely unlikely that this will go wrong and it won't cause any issues. struct Id(UnsafeCell); @@ -17,6 +23,8 @@ unsafe impl Sync for Id {} #[display("%{}", unsafe { *id.0.get() })] pub struct Instruction<'l> { id: Id, + ty: OnceLock>, + flags: AtomicU16, pub parent_block: &'l Block<'l>, pub variant: InstructionVariant<'l>, } @@ -56,11 +64,19 @@ impl<'l> Instruction<'l> { pub fn id(&self) -> u32 { unsafe { *self.id.0.get() } } + + #[inline] + pub unsafe fn edit_flags(&self, edit: impl FnOnce(ValueFlags) -> ValueFlags) { + let flags = self.flags.load(std::sync::atomic::Ordering::Relaxed); + let flags = edit(ValueFlags::from_bits_retain(flags)); + self.flags + .store(flags.bits(), std::sync::atomic::Ordering::Relaxed); + } } impl<'l> Value<'l> for &'l Instruction<'l> { fn ty(&self) -> Type<'l> { - match self.variant { + *self.ty.get_or_init(|| match self.variant { InstructionVariant::Return(_) => Type::Void, InstructionVariant::Store(_, _) => Type::Void, InstructionVariant::StackAlloc(ty) => ty.make_ptr(true).into(), @@ -68,7 +84,7 @@ impl<'l> Value<'l> for &'l Instruction<'l> { Type::Ptr(PtrT { base, .. }) => *base, _ => unreachable!(), }, - InstructionVariant::GetElementPtr(v, _) => match v.ty() { + InstructionVariant::GetElementPtr(v, i) => match v.ty() { Type::Ptr(PtrT { base: Type::Array(ArrayT { base, .. }), mutable, @@ -77,6 +93,26 @@ impl<'l> Value<'l> for &'l Instruction<'l> { base: Type::Array(ArrayT { base, .. }), mutable, }) => base.make_ref(*mutable).into(), + Type::Ptr(PtrT { + base: Type::Struct(StructT { fields, .. }), + mutable, + }) => { + let fields = fields.get().unwrap(); + let AnyValue::Constant(AnyConst::Str(name)) = i else { + unreachable!(); + }; + fields[name].ty.make_ptr(*mutable).into() + } + Type::Ref(RefT { + base: Type::Struct(StructT { fields, .. }), + mutable, + }) => { + let fields = fields.get().unwrap(); + let AnyValue::Constant(AnyConst::Str(name)) = i else { + unreachable!(); + }; + fields[name].ty.make_ref(*mutable).into() + } _ => unreachable!(), }, InstructionVariant::GetElementVal(v, i) => match v.ty() { @@ -108,18 +144,13 @@ impl<'l> Value<'l> for &'l Instruction<'l> { InstructionVariant::Call(f, _) => f.ty.ret_t, InstructionVariant::Jump(_) => Type::Void, InstructionVariant::Branch { .. } => Type::Void, - InstructionVariant::Reinterpret(_, t, _) => t, + InstructionVariant::Reinterpret(_, t) => t, _ => todo!("{self:?}"), - } + }) } fn flags(&self) -> ValueFlags { - match self.variant { - InstructionVariant::StackAlloc(_) => ValueFlags::LValue, - InstructionVariant::GetElementPtr(v, _) if v.is_lvalue() => ValueFlags::LValue, - InstructionVariant::Reinterpret(_, _, f) => f, - _ => ValueFlags::empty(), - } + ValueFlags::from_bits_retain(self.flags.load(std::sync::atomic::Ordering::Relaxed)) } fn get_associated_value(&self, name: &str) -> Option> { @@ -141,6 +172,13 @@ pub enum Cmp { Ge, } +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] +#[display("[{value}, #{block}]")] +pub struct PhiValue<'l> { + pub block: u32, + pub value: AnyValue<'l>, +} + #[derive(PartialEq, Eq)] pub enum InstructionVariant<'l> { StackAlloc(Type<'l>), @@ -171,6 +209,7 @@ pub enum InstructionVariant<'l> { FCmp(AnyValue<'l>, AnyValue<'l>, Cmp), MakeStruct(&'l StructT<'l>, &'l [AnyValue<'l>]), + Phi(&'l [PhiValue<'l>]), Call(&'l Function<'l>, Vec>), Jump(&'l Block<'l>), @@ -181,7 +220,7 @@ pub enum InstructionVariant<'l> { }, Return(Option>), - Reinterpret(AnyValue<'l>, Type<'l>, ValueFlags), + Reinterpret(AnyValue<'l>, Type<'l>), } impl InstructionVariant<'_> { @@ -217,6 +256,15 @@ impl std::fmt::Debug for InstructionVariant<'_> { Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"), Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"), + Self::Phi(values) => { + write!(f, "phi")?; + let mut separator = " "; + for val in *values { + write!(f, "{separator}{val}")?; + separator = ", "; + } + Ok(()) + } Self::MakeStruct(ty, vals) => { write!( f, @@ -258,7 +306,7 @@ impl std::fmt::Debug for InstructionVariant<'_> { Self::Jump(b) => write!(f, "jump #{}", b.id), Self::Return(None) => write!(f, "return"), Self::Return(Some(v)) => write!(f, "return {v}"), - Self::Reinterpret(v, t, fl) => write!(f, "reinterpret {v} as {t} {fl:?}"), + Self::Reinterpret(v, t) => write!(f, "reinterpret {v} as {t}"), } } } @@ -288,7 +336,7 @@ impl PartialEq for Block<'_> { } pub struct BlockBuilder<'l> { - block: &'l Block<'l>, + pub block: &'l Block<'l>, instructions: Vec<&'l Instruction<'l>>, } @@ -298,6 +346,10 @@ pub type BlockBuilderResult<'l, T> = Result>; impl<'l> BlockBuilder<'l> { pub fn stack_alloc(&mut self, ty: Type<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> { let inst = self.push_instruction(InstructionVariant::StackAlloc(ty))?; + inst.flags.store( + ValueFlags::LValue.bits(), + std::sync::atomic::Ordering::Relaxed, + ); Ok(inst.into()) } @@ -477,6 +529,24 @@ impl<'l> BlockBuilder<'l> { } } + pub fn ptr_to_int( + &mut self, + val: AnyValue<'l>, + target: IntT, + ) -> BlockBuilderResult<'l, AnyValue<'l>> { + let ty = val.ty(); + match ty { + Type::Ptr(_) => { + let inst = self.push_instruction(InstructionVariant::PtrToInt(val, target))?; + Ok(inst.into()) + } + _ => Err(format!( + "Cannot convert non-pointer value of type `{ty}` to integer `{target}`." + ) + .into()), + } + } + pub fn cmp( &mut self, a: AnyValue<'l>, @@ -506,23 +576,48 @@ impl<'l> BlockBuilder<'l> { let v_ty = value.ty(); let i_ty = index.ty(); - let _ = match v_ty { + match v_ty { Type::Ptr(PtrT { - base: Type::Array(ArrayT { base, .. }), + base: Type::Array(ArrayT { .. }), .. - }) => *base, - Type::Ref(RefT { - base: Type::Array(ArrayT { base, .. }), + }) + | Type::Ref(RefT { + base: Type::Array(ArrayT { .. }), .. - }) => *base, + }) => { + if i_ty != Type::USIZE { + return Err(format!("Expeted index type `usize`, found {i_ty}.").into()); + } + } + Type::Ptr(PtrT { + base: Type::Struct(StructT { fields, .. }), + .. + }) + | Type::Ref(RefT { + base: Type::Struct(StructT { fields, .. }), + .. + }) => { + if i_ty != Type::ConstStr { + return Err(format!("Expeted index type `const str`, found {i_ty}.").into()); + } + let AnyValue::Constant(AnyConst::Str(name)) = index else { + unreachable!(); + }; + let fields = fields.get().unwrap(); + if !fields.contains_key(name) { + return Err(format!("Type `{v_ty}` does not contain field `{name}`.").into()); + } + } _ => return Err(format!("Cannot index a value of type `{}`.", v_ty).into()), }; - if i_ty != Type::USIZE { - return Err(format!("Expeted index type `usize`, found {}.", i_ty).into()); - } - let inst = self.push_instruction(InstructionVariant::GetElementPtr(value, index))?; + if value.is_lvalue() { + inst.flags.store( + ValueFlags::LValue.bits(), + std::sync::atomic::Ordering::Relaxed, + ); + } Ok(inst.into()) } @@ -571,7 +666,7 @@ impl<'l> BlockBuilder<'l> { .find(|(_, (a, b))| a.ty != b.ty()) { return Err(format!( - "Invalid valua at position {i}. Expected type `{}`, found `{}`.", + "Invalid value at position {i}. Expected type `{}`, found `{}`.", a.ty, b.ty() ) @@ -581,6 +676,22 @@ impl<'l> BlockBuilder<'l> { return Ok(inst.into()); } + pub fn phi( + &mut self, + values: impl ExactSizeIterator>, + ) -> BlockBuilderResult<'l, AnyValue<'l>> { + if values.len() < 2 { + return Err("Instruction requires at least two values.".into()); + } + let values = self.block.func.ctx().alloc.alloc_slice(values); + let ty = values[0].value.ty(); + if values.iter().any(|v| v.value.ty() != ty) { + return Err("All values must have the same type.".into()); + } + let inst = self.push_instruction(InstructionVariant::Phi(values))?; + Ok(inst.into()) + } + pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> { if !std::ptr::eq(block.func, self.block.func) { return Err("Block does not belong to this function.".into()); @@ -667,7 +778,9 @@ impl<'l> BlockBuilder<'l> { ty: Type<'l>, flags: ValueFlags, ) -> BlockBuilderResult<'l, AnyValue<'l>> { - let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty, flags))?; + let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty))?; + inst.flags + .store(flags.bits(), std::sync::atomic::Ordering::Relaxed); Ok(inst.into()) } @@ -697,6 +810,8 @@ impl<'l> BlockBuilder<'l> { let instruction = &*self.block.func.ctx().alloc.alloc(Instruction { id: Id(UnsafeCell::new(u32::MAX)), parent_block: self.block, + ty: OnceLock::new(), + flags: AtomicU16::new(0), variant, }); self.instructions.push(instruction); @@ -841,6 +956,14 @@ impl<'l> FunctionBodyBuilder<'l> { self.current_builder().int_to_ptr(val, target) } + pub fn ptr_to_int( + &mut self, + val: AnyValue<'l>, + target: IntT, + ) -> BlockBuilderResult<'l, AnyValue<'l>> { + self.current_builder().ptr_to_int(val, target) + } + pub fn cmp( &mut self, a: AnyValue<'l>, @@ -874,6 +997,13 @@ impl<'l> FunctionBodyBuilder<'l> { self.current_builder().make_struct(struct_ty, values) } + pub fn phi( + &mut self, + values: impl ExactSizeIterator>, + ) -> BlockBuilderResult<'l, AnyValue<'l>> { + self.current_builder().phi(values) + } + pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> { self.current_builder().jump(block) } diff --git a/assembly/src/types/mod.rs b/assembly/src/types/mod.rs index e61f94c..bb9c47c 100644 --- a/assembly/src/types/mod.rs +++ b/assembly/src/types/mod.rs @@ -49,6 +49,23 @@ impl<'l> Value<'l> for IntT { } } +#[rustfmt::skip] +impl IntT { + pub const I8: Self = IntT { signed: true, precision: 8 }; + pub const I16: Self = IntT { signed: true, precision: 16 }; + pub const I32: Self = IntT { signed: true, precision: 32 }; + pub const I64: Self = IntT { signed: true, precision: 64 }; + pub const I128: Self = IntT { signed: true, precision: 128 }; + pub const ISIZE: Self = IntT { signed: true, precision: u32::MAX }; + + pub const U8: Self = IntT { signed: false, precision: 8 }; + pub const U16: Self = IntT { signed: false, precision: 16 }; + pub const U32: Self = IntT { signed: false, precision: 32 }; + pub const U64: Self = IntT { signed: false, precision: 64 }; + pub const U128: Self = IntT { signed: false, precision: 128 }; + pub const USIZE: Self = IntT { signed: false, precision: u32::MAX }; +} + #[non_exhaustive] #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] #[display("f{}", precision)] @@ -198,19 +215,19 @@ impl<'l> Value<'l> for Type<'l> { #[rustfmt::skip] impl Type<'_> { - pub const I8: Self = Type::Int(IntT { signed: true, precision: 8 }); - pub const I16: Self = Type::Int(IntT { signed: true, precision: 16 }); - pub const I32: Self = Type::Int(IntT { signed: true, precision: 32 }); - pub const I64: Self = Type::Int(IntT { signed: true, precision: 64 }); - pub const I128: Self = Type::Int(IntT { signed: true, precision: 128 }); - pub const ISIZE: Self = Type::Int(IntT { signed: true, precision: u32::MAX }); + pub const I8: Self = Type::Int(IntT::I8); + pub const I16: Self = Type::Int(IntT::I16); + pub const I32: Self = Type::Int(IntT::I32); + pub const I64: Self = Type::Int(IntT::I64); + pub const I128: Self = Type::Int(IntT::I128); + pub const ISIZE: Self = Type::Int(IntT::ISIZE); - pub const U8: Self = Type::Int(IntT { signed: false, precision: 8 }); - pub const U16: Self = Type::Int(IntT { signed: false, precision: 16 }); - pub const U32: Self = Type::Int(IntT { signed: false, precision: 32 }); - pub const U64: Self = Type::Int(IntT { signed: false, precision: 64 }); - pub const U128: Self = Type::Int(IntT { signed: false, precision: 128 }); - pub const USIZE: Self = Type::Int(IntT { signed: false, precision: u32::MAX }); + pub const U8: Self = Type::Int(IntT::U8); + pub const U16: Self = Type::Int(IntT::U16); + pub const U32: Self = Type::Int(IntT::U32); + pub const U64: Self = Type::Int(IntT::U64); + pub const U128: Self = Type::Int(IntT::U128); + pub const USIZE: Self = Type::Int(IntT::USIZE); pub const F16: Self = Type::Float(FloatT { precision: 16 }); pub const F32: Self = Type::Float(FloatT { precision: 32 }); diff --git a/assembly/src/values/constants.rs b/assembly/src/values/constants.rs index bcb9d82..57b7694 100644 --- a/assembly/src/values/constants.rs +++ b/assembly/src/values/constants.rs @@ -176,6 +176,10 @@ pub enum AnyConst<'l> { #[display("{}", StructDisplay(_0, _1))] Struct(&'l StructT<'l>, &'l [AnyConst<'l>]), Type(Type<'l>), + #[from(ignore)] + #[debug("SizeOf({:?})", _0)] + #[display("size_of({_0})")] + SizeOf(Type<'l>), } impl<'l> Value<'l> for AnyConst<'l> { @@ -194,6 +198,7 @@ impl<'l> Value<'l> for AnyConst<'l> { Self::Array(a @ [v, ..]) => Type::Array(v.ty().make_array(Some(a.len() as u32))), Self::Str(s) => s.ty(), Self::Struct(t, _) => Type::Struct(t), + Self::SizeOf(_) => Type::USIZE, _ => todo!("{self:?}"), } } @@ -218,6 +223,7 @@ impl<'l> Value<'l> for AnyConst<'l> { AnyConst::Array(_) => default_associated_values(self, name), AnyConst::Struct(t, _) => t.get_associated_value(name), AnyConst::Type(t) => t.get_associated_value(name), + AnyConst::SizeOf(_) => Type::USIZE.get_associated_value(name), } } diff --git a/assembly/src/values/mod.rs b/assembly/src/values/mod.rs index 077d860..9aeb1d9 100644 --- a/assembly/src/values/mod.rs +++ b/assembly/src/values/mod.rs @@ -77,7 +77,7 @@ impl<'l> Value<'l> for AnyValue<'l> { fn get_associated_value(&self, name: &str) -> Option> { match self { AnyValue::Constant(v) => v.get_associated_value(name), - AnyValue::Instruction(v) => todo!(), + AnyValue::Instruction(_) => default_associated_values(self, name), AnyValue::Parameter(_, _) => default_associated_values(self, name), } } diff --git a/backends/llvm/src/lib.rs b/backends/llvm/src/lib.rs index f4cb661..87f2dfc 100644 --- a/backends/llvm/src/lib.rs +++ b/backends/llvm/src/lib.rs @@ -5,7 +5,7 @@ use inkwell::{ module::Module, targets::TargetMachine, types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicTypeEnum, IntType}, - values::{AggregateValueEnum, AnyValue as LlvmAnyValue, BasicValue, BasicValueEnum, IntValue}, + values::{AnyValue as LlvmAnyValue, BasicValue, BasicValueEnum}, }; use leaf_assembly::{ assembly::Assembly, @@ -45,11 +45,11 @@ impl<'l> CompilationContext<'l> { functions: HashMap::default(), fields: HashMap::default(), native_int_ty: match target.get_target_data().get_pointer_byte_size(None) { - 8 => ctx.i8_type(), - 16 => ctx.i16_type(), - 32 => ctx.i32_type(), - 64 => ctx.i64_type(), - 128 => ctx.i128_type(), + 1 => ctx.i8_type(), + 2 => ctx.i16_type(), + 4 => ctx.i32_type(), + 8 => ctx.i64_type(), + 16 => ctx.i128_type(), _ => unreachable!(), }, } @@ -146,20 +146,61 @@ impl<'l> CompilationContext<'l> { None } InstructionVariant::GetElementPtr(ptr, idx) => 'val: { - let pointee_ty = self.get_type(match inst.ty() { - Type::Ptr(PtrT { base, .. }) => *base, - Type::Ref(RefT { base, .. }) => *base, - _ => unreachable!(), - }); - let Ok(pointee_ty): Result = pointee_ty.try_into() - else { - break 'val None; - }; - let ptr = self.get_value(&values, ptr).unwrap().into_pointer_value(); - let idx = self.get_value(&values, idx).unwrap().into_int_value(); - unsafe { - let ptr = builder.build_gep(pointee_ty, ptr, &[idx], "").unwrap(); - Some(ptr.into()) + let ptr_val = + self.get_value(&values, ptr).unwrap().into_pointer_value(); + + match ptr.ty() { + Type::Ptr(PtrT { + base: Type::Array(_), + .. + }) + | Type::Ref(RefT { + base: Type::Array(_), + .. + }) => unsafe { + let pointee_ty = match inst.ty() { + Type::Ptr(PtrT { base, .. }) => *base, + Type::Ref(RefT { base, .. }) => *base, + _ => unreachable!(), + }; + let Ok(pointee): Result = + self.get_type(pointee_ty).try_into() + else { + break 'val None; + }; + + let idx = + self.get_value(&values, idx).unwrap().into_int_value(); + let ptr = + builder.build_gep(pointee, ptr_val, &[idx], "").unwrap(); + Some(ptr.into()) + }, + Type::Ptr(PtrT { + base: Type::Struct(ty), + .. + }) + | Type::Ref(RefT { + base: Type::Struct(ty), + .. + }) => { + let Ok(pointee): Result = + self.get_type(Type::Struct(ty)).try_into() + else { + break 'val None; + }; + let AnyValue::Constant(AnyConst::Str(name)) = idx else { + unreachable!() + }; + let idx = match self.fields.get_sync(&(*ty, *name)) { + Some(idx) => *idx, + None => break 'val None, + }; + let ptr = builder + .build_struct_gep(pointee, ptr_val, idx, "") + .unwrap(); + Some(ptr.into()) + } + _ => unreachable!("{}", ptr.ty()), } } InstructionVariant::GetElementVal( @@ -225,6 +266,11 @@ impl<'l> CompilationContext<'l> { let ptr = self.get_type(Type::Ptr(ty)).into_pointer_type(); Some(builder.build_int_to_ptr(val, ptr, "").unwrap().into()) } + InstructionVariant::PtrToInt(v, ty) => { + let val = self.get_value(&values, v).unwrap().into_pointer_value(); + let int = self.get_type(Type::Int(*ty)).into_int_type(); + Some(builder.build_ptr_to_int(val, int, "").unwrap().into()) + } InstructionVariant::ICmp(lhs, rhs, cmp) => { let u = !is_signed(lhs.ty()); let cmp = match (cmp, u) { @@ -261,6 +307,26 @@ impl<'l> CompilationContext<'l> { Some(val.into()) } + InstructionVariant::Phi(phi_values) => 'val: { + let Ok(ty): Result = + self.get_type(phi_values[0].value.ty()).try_into() + else { + break 'val None; + }; + let llvm_vals: Vec<_> = phi_values + .iter() + .map(|v| self.get_value(&values, &v.value).unwrap()) + .collect(); + let llvm_vals: Vec<_> = phi_values + .iter() + .zip(&llvm_vals) + .map(|(p, v)| (v as &dyn BasicValue, blocks[p.block as usize])) + .collect(); + let phi = builder.build_phi(ty, "").unwrap(); + phi.add_incoming(&llvm_vals); + Some(phi.as_basic_value()) + } + InstructionVariant::Call(func, args) => { // TODO This will fail with external assemblies. Fix this. let func = *self.functions.get_sync(&(assembly, *func)).unwrap(); @@ -307,7 +373,7 @@ impl<'l> CompilationContext<'l> { None } - InstructionVariant::Reinterpret(v, t, _) => match (v.ty(), t) { + InstructionVariant::Reinterpret(v, t) => match (v.ty(), t) { (Type::Ptr(_), Type::Ptr(_)) => { Some(self.get_value(&values, v).unwrap()) } @@ -356,6 +422,7 @@ impl<'l> CompilationContext<'l> { AnyTypeEnum::VoidType(ty) => ty.fn_type(&par_t, false).into(), AnyTypeEnum::IntType(ty) => ty.fn_type(&par_t, false).into(), AnyTypeEnum::StructType(ty) => ty.fn_type(&par_t, false).into(), + AnyTypeEnum::PointerType(ty) => ty.fn_type(&par_t, false).into(), _ => todo!("{ret_t:?}"), } } @@ -454,6 +521,15 @@ impl<'l> CompilationContext<'l> { .collect(); Some(ty.const_named_struct(&vals).into()) } + + AnyConst::SizeOf(Type::Int(ty)) => { + let ty = self.get_type(Type::Int(*ty)).into_int_type(); + Some( + ty.size_of() + .const_truncate_or_bit_cast(self.native_int_ty) + .into(), + ) + } _ => todo!("{val:?}"), }, _ => unreachable!("{val:#?}"), @@ -464,7 +540,14 @@ impl<'l> CompilationContext<'l> { #[inline] fn is_signed(ty: Type) -> bool { match ty { - Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 | Type::USIZE => false, + Type::U8 + | Type::U16 + | Type::U32 + | Type::U64 + | Type::U128 + | Type::USIZE + | Type::Ptr(_) + | Type::Ref(_) => false, Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 | Type::ISIZE => true, _ => unreachable!(), } diff --git a/compiler/src/error.rs b/compiler/src/error.rs index a3618f8..0bcac6c 100644 --- a/compiler/src/error.rs +++ b/compiler/src/error.rs @@ -20,6 +20,7 @@ pub enum Kind { NotAStruct = 0x0208, FieldNotFound = 0x0206, InvalidCast = 0x0207, + CannotDereference = 0x0209, UninitializedField = 0x0300, diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 8ab3002..a632f3e 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -10,7 +10,10 @@ use leaf_backend_llvm::{ }; use leaf_compiler::CompilationContext; use leaf_parser::SourceCode; -use std::{path::PathBuf, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; fn main() { let alloc = SyncArenaAllocator::default(); @@ -54,10 +57,7 @@ fn main() { module.print_to_stderr(); module.verify().unwrap(); - let asm = target_machine - .write_to_memory_buffer(&module, FileType::Assembly) + target_machine + .write_to_file(&module, FileType::Assembly, Path::new("out.asm")) .unwrap(); - let asm = std::str::from_utf8(asm.as_slice()).unwrap(); - eprintln!("{asm}"); - std::fs::write("out.asm", asm).unwrap(); } diff --git a/compiler/src/scope.rs b/compiler/src/scope.rs index dbf7bb4..ccc2c68 100644 --- a/compiler/src/scope.rs +++ b/compiler/src/scope.rs @@ -4,20 +4,20 @@ use leaf_assembly::{ assembly::Assembly, functions::{ Function, - ir::{Cmp, FunctionBodyBuilder}, + ir::{Cmp, FunctionBodyBuilder, PhiValue}, }, types::{ - Type, + IntT, Type, compound::{Field, FieldMap, StructT}, - derivations::PtrT, + derivations::{PtrT, RefT}, }, values::{AnyConst, AnyValue, Int, Value, ValueFlags}, }; use leaf_parser::{ SourceCode, ast::{ - self, AccessExpr, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, IndexingExpr, NamePattern, - While, + self, AccessExpr, BinaryExpr, BinaryOp, Block, ConstDecl, Else, Expr, Ident, If, + IndexingExpr, NamePattern, While, }, }; use std::{ @@ -109,31 +109,30 @@ impl<'l> Scope<'l> { pub fn compile_function( &mut self, func: &'l Function<'l>, - block: &ast::Block, + block: &Arc, fn_queue: &mut FuncQueue<'l>, ) -> Result<(), CompilationError> { let mut builder = func.create_body().unwrap(); - let mut ctx = ExpressionContext { - builder: Some(&mut builder), - decl_names: None, - fn_queue: fn_queue, - }; - - let mut last_expr = None; - for expr in &block.0 { - last_expr = Some(self.compile_expression(expr, &mut ctx)?); - } + let mut ret = self.compile_block( + block, + &mut ExpressionContext { + builder: Some(&mut builder), + decl_names: None, + fn_queue: fn_queue, + }, + )?; if !builder.current_block().has_termination() { match func.ty.ret_t { - Type::Void => builder.ret(None).unwrap(), + Type::Void => { + builder.ret(None).unwrap(); + } _ => { - if let Some(expr) = last_expr.as_mut() - && expr.is_lvalue() - { - *expr = builder.load(*expr).unwrap(); + if ret.is_lvalue() { + ret = builder.load(ret).unwrap(); } - builder.ret(last_expr).unwrap() + self.assert_ty_eq(&ret, &Expr::Block(block.clone()), &func.ty.ret_t)?; + builder.ret(Some(ret)).unwrap(); } }; } @@ -255,6 +254,28 @@ impl<'l> Scope<'l> { } } } + Type::Ptr(PtrT { + base: Type::Struct(StructT { fields, .. }), + .. + }) => { + if let Some(fields) = fields.get() { + if let Some(field) = fields.get(field.0.as_str()) { + let builder = ctx.builder.as_mut().unwrap(); + let inst = builder + .get_element_ptr(value, field.name.as_any_value()) + .unwrap() + .as_any_value(); + + unsafe { + if let AnyValue::Instruction(inst) = inst { + inst.edit_flags(|f| f | ValueFlags::LValue); + } + } + + return Ok(inst); + } + } + } _ => {} }; return Err(CompilationError { @@ -347,7 +368,9 @@ impl<'l> Scope<'l> { } } - if match (lhs.ty(), rhs.ty()) { + let (lhs_ty, rhs_ty) = (lhs.ty(), rhs.ty()); + + if match (lhs_ty, rhs_ty) { (Type::Int(a_ty), Type::Int(b_ty)) => a_ty == b_ty, _ => false, } { @@ -367,7 +390,31 @@ impl<'l> Scope<'l> { }); } - match (lhs.ty(), rhs.ty(), op) { + if match (lhs_ty, rhs_ty) { + (Type::Ptr(a_ty), Type::Ptr(b_ty)) => a_ty == b_ty, + _ => false, + } { + let lhs = builder.ptr_to_int(lhs, IntT::USIZE).unwrap(); + let rhs = builder.ptr_to_int(rhs, IntT::USIZE).unwrap(); + return Ok(match op { + BinaryOp::Eq(_) => builder.cmp(lhs, rhs, Cmp::Eq).unwrap(), + BinaryOp::Ne(_) => builder.cmp(lhs, rhs, Cmp::Ne).unwrap(), + BinaryOp::Lt(_) => builder.cmp(lhs, rhs, Cmp::Lt).unwrap(), + BinaryOp::Gt(_) => builder.cmp(lhs, rhs, Cmp::Gt).unwrap(), + BinaryOp::Le(_) => builder.cmp(lhs, rhs, Cmp::Le).unwrap(), + BinaryOp::Ge(_) => builder.cmp(lhs, rhs, Cmp::Ge).unwrap(), + _ => todo!("{lhs:?} {op:?} {rhs:?}"), + }); + } + + match (lhs_ty, rhs_ty, op) { + (Type::Ptr(ptr @ PtrT { base, .. }), Type::USIZE, BinaryOp::Add(_)) => { + let mut value = builder.ptr_to_int(lhs, IntT::USIZE).unwrap(); + let add = builder.mul(rhs, AnyConst::SizeOf(*base).into()).unwrap(); + value = builder.add(value, add).unwrap(); + value = builder.int_to_ptr(value, ptr).unwrap(); + Ok(value) + } (Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty { true => Ok(builder.store(lhs, rhs).unwrap()), false => Err(CompilationError { @@ -407,6 +454,9 @@ impl<'l> Scope<'l> { (Type::Int(_), Type::Ptr(dst_ty)) => { return Ok(builder.int_to_ptr(lhs, dst_ty).unwrap()); } + (Type::Ptr(_), dst_ty @ Type::Ptr(_)) => unsafe { + return Ok(builder.reinterpret(lhs, dst_ty, lhs.flags()).unwrap()); + }, _ => todo!("{src_ty} as {dst_ty}"), } } @@ -414,8 +464,10 @@ impl<'l> Scope<'l> { } } + Expr::If(expr) => self.compile_if(expr, ctx), + Expr::While(expr) => { - let While { value, block } = &**expr; + let While { cond, block } = &**expr; let mut builder = ctx.builder.as_mut().unwrap(); let cond_block = builder.create_block(); @@ -424,26 +476,18 @@ impl<'l> Scope<'l> { builder.jump(cond_block).unwrap(); builder.set_current_block(cond_block); - let condition = self.compile_expression(value, ctx)?; + let condition = self.compile_expression(cond, 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), - decl_names: None, - fn_queue: ctx.fn_queue, - }; + let ret = self.compile_block(block, ctx)?; + builder = ctx.builder.as_mut().unwrap(); - 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(AnyValue::Constant(AnyConst::Void))) + Ok(ret) } Expr::Call { @@ -586,9 +630,63 @@ impl<'l> Scope<'l> { todo!() } + Expr::Deref(expr) => { + let value = self.compile_expression(expr, ctx)?; + let builder = ctx.builder.as_mut().unwrap(); + let ty = value.ty(); + + match value.is_lvalue() { + false => unsafe { + if !matches!(ty, Type::Ptr(_)) { + return Err(CompilationError { + kind: Kind::CannotDereference, + message: format!("Cannot dereference a value of type `{ty}`."), + location: Location::Range { + file: self.source.clone(), + range: expr.range(), + }, + cause: None, + }); + } + + Ok(builder + .reinterpret(value, value.ty(), value.flags() | ValueFlags::LValue) + .unwrap()) + }, + true => unsafe { + if !matches!( + ty, + Type::Ptr(PtrT { + base: Type::Ptr(_), + .. + }) + ) { + let Type::Ptr(PtrT { base, .. }) = ty else { + unreachable!() + }; + return Err(CompilationError { + kind: Kind::CannotDereference, + message: format!("Cannot dereference a value of type `{base}`."), + location: Location::Range { + file: self.source.clone(), + range: expr.range(), + }, + cause: None, + }); + } + let AnyValue::Instruction(value) = builder.load(value).unwrap() else { + unreachable!() + }; + value.edit_flags(|v| v | ValueFlags::LValue); + Ok(AnyValue::Instruction(value)) + }, + _ => unimplemented!("{}", value.is_lvalue()), + } + } + Expr::Index(expr) => { let IndexingExpr { value, index } = &**expr; - let value = self.compile_expression(value, ctx)?; + let mut value = self.compile_expression(value, ctx)?; let mut index = self.compile_expression(index, ctx)?; let builder = ctx.builder.as_mut().unwrap(); @@ -609,6 +707,21 @@ impl<'l> Scope<'l> { }); } + // TODO This is probably wrong, make it better. + while value.is_lvalue() + && !matches!( + value.ty(), + Type::Ptr(PtrT { + base: Type::Array(_), + .. + }) | Type::Ref(RefT { + base: Type::Array(_), + .. + }) + ) { + value = builder.load(value).unwrap(); + } + if value.is_lvalue() { let gep = builder.get_element_ptr(value, index).unwrap(); return Ok(gep); @@ -620,7 +733,7 @@ impl<'l> Scope<'l> { Expr::Struct(ctor) => { let ty = self.compile_expression(&ctor.r#type, ctx)?; let AnyValue::Constant(AnyConst::Type(Type::Struct( - struct_ty @ StructT { name, fields, .. }, + struct_ty @ StructT { fields, .. }, ))) = ty else { return Err(CompilationError { @@ -734,8 +847,11 @@ impl<'l> Scope<'l> { fn_queue: ctx.fn_queue, }; let mut value = self.compile_expression(value, &mut sub_ctx)?; + let builder = sub_ctx.builder.unwrap(); + if value.is_lvalue() { + value = builder.load(value).unwrap(); + } if mutable { - let builder = sub_ctx.builder.unwrap(); let variable = builder.stack_alloc(value.ty()).unwrap(); builder.store(variable, value).unwrap(); value = variable; @@ -756,6 +872,88 @@ impl<'l> Scope<'l> { Ok(AnyConst::Void.into()) } + fn compile_block( + &mut self, + expr: &Block, + ctx: &mut ExpressionContext<'l, '_>, + ) -> Result, CompilationError> { + let mut scope = self.clone(); + let builder = ctx.builder.as_mut().unwrap(); + let mut ctx = ExpressionContext { + builder: Some(builder), + decl_names: None, + fn_queue: ctx.fn_queue, + }; + + let mut last_expr = None; + for expr in &expr.0 { + last_expr = Some(scope.compile_expression(expr, &mut ctx)?); + } + Ok(last_expr.unwrap_or(AnyValue::Constant(AnyConst::Void))) + } + + fn compile_if( + &mut self, + expr: &If, + ctx: &mut ExpressionContext<'l, '_>, + ) -> Result, CompilationError> { + let If { cond, block, else_ } = expr; + let condition = self.compile_expression(cond, ctx)?; + self.assert_ty_eq(&condition, cond, &Type::Bool)?; + + let builder = ctx.builder.as_mut().unwrap(); + let then_block = builder.create_block(); + let else_block = builder.create_block(); + + builder.branch(condition, then_block, else_block).unwrap(); + builder.set_current_block(then_block); + let then_val = self.compile_block(block, ctx)?; + let builder = ctx.builder.as_mut().unwrap(); + + let else_ = match else_ { + None => { + builder.jump(else_block).unwrap(); + builder.set_current_block(else_block); + return Ok(then_val); + } + Some(else_) => else_, + }; + + let continue_block = builder.create_block(); + builder.jump(continue_block).unwrap(); + builder.set_current_block(else_block); + + let else_val = match &**else_ { + Else::Block(block) => self.compile_block(block, ctx)?, + Else::If(if_) => self.compile_if(if_, ctx)?, + }; + + let builder = ctx.builder.as_mut().unwrap(); + builder.jump(continue_block).unwrap(); + builder.set_current_block(continue_block); + + if then_val.ty() != else_val.ty() { + todo!() + } + + Ok(builder + .phi( + [ + PhiValue { + value: then_val, + block: then_block.id, + }, + PhiValue { + value: else_val, + block: else_block.id, + }, + ] + .into_iter(), + ) + .unwrap() + .into()) + } + fn assert_ty( &self, val: AnyValue<'l>, diff --git a/parser/src/ast.rs b/parser/src/ast.rs index a37a279..6606fae 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -26,30 +26,33 @@ pub enum Expr { Number(Number), String(Substr), #[debug("{_0:?}")] - Binary(Arc), - Index(Arc), - Access(Arc), + Binary(Box), + Index(Box), + Access(Box), + Deref(Box), Tuple(Vec), List(Vec), - Struct(Arc), + Struct(Box), #[debug("{_0:?}")] - Block(Block), + Block(Arc), #[debug("{_0:?}")] Func(Arc), #[debug("{_0:?}")] - Type(Arc), + Type(Box), #[debug("{_0:?}")] - ConstDecl(Arc), + ConstDecl(Box), #[debug("{_0:?}")] - VarDecl(Arc), + VarDecl(Box), #[debug("{_0:?}")] - For(Arc), + For(Box), #[debug("{_0:?}")] - While(Arc), + While(Box), + #[debug("{_0:?}")] + If(Box), Call { - func: Arc, + func: Box, args: Vec, }, } @@ -191,10 +194,23 @@ pub struct For { #[derive(Debug)] pub struct While { - pub value: Expr, + pub cond: Expr, pub block: Block, } +#[derive(Debug)] +pub struct If { + pub cond: Expr, + pub block: Block, + pub else_: Option>, +} + +#[derive(Debug)] +pub enum Else { + If(If), + Block(Block), +} + #[derive(Debug)] pub struct CompilationUnit { pub imports: Vec, diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 9e8ce08..740dbb9 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -73,44 +73,48 @@ peg::parser! { // ### EXPRESSIONS #### rule expr() -> Expr = precedence! { - lhs:(@) __ op:$("as") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Cast(op), rhs })) } + lhs:(@) __ op:$("as") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Cast(op), rhs }.into()) } -- - lhs:@ __ op:$("=") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs })) } + lhs:@ __ op:$("=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs }.into()) } value:@ __ op:$(".") __ field:ident2() { Expr::Access(AccessExpr { value, field }.into()) } - lhs:@ "(" __ args:(expr() ** list_separator()) __ ")" { Expr::Call { func: Arc::new(lhs), args } } - value:@ "[" __ index:expr() __ "]" { Expr::Index(Arc::new(IndexingExpr { value, index })) } + value:@ __ op:$(".^") { Expr::Deref(value.into()) } + lhs:@ "(" __ args:(expr() ** list_separator()) __ ")" { Expr::Call { func: lhs.into(), args } } + value:@ "[" __ index:expr() __ "]" { Expr::Index(IndexingExpr { value, index }.into()) } - r#type:@ __ "{" __ values:name_value_pairs() __ "}" { Expr::Struct(Arc::new(StructCtor { - r#type, values: values.into_iter().map(|v| (v.name.0.clone(), v)).collect() - })) } + r#type:@ __ "#{" __ values:name_value_pairs() __ "}" { Expr::Struct( + StructCtor { + r#type, values: values.into_iter().map(|v| (v.name.0.clone(), v)).collect() + }.into() + ) } -- - lhs:@ __ op:$("+") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Add(op), rhs })) } - lhs:@ __ op:$("-") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Sub(op), rhs })) } + lhs:@ __ op:$("+") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Add(op), rhs }.into()) } + lhs:@ __ op:$("-") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Sub(op), rhs }.into()) } -- - lhs:@ __ op:$("*") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Mul(op), rhs })) } - lhs:@ __ op:$("/") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Div(op), rhs })) } - lhs:@ __ op:$("%") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Mod(op), rhs })) } + lhs:@ __ op:$("*") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Mul(op), rhs }.into()) } + lhs:@ __ op:$("/") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Div(op), rhs }.into()) } + lhs:@ __ op:$("%") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Mod(op), rhs }.into()) } -- - lhs:@ __ op:$("..") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) } + lhs:@ __ op:$("..") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs }.into()) } -- - lhs:@ __ op:$("==") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Eq(op), rhs })) } - lhs:@ __ op:$("!=") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Ne(op), rhs })) } - lhs:@ __ op:$("<") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs })) } - lhs:@ __ op:$(">") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs })) } - lhs:@ __ op:$("<=") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs })) } - lhs:@ __ op:$(">=") __ rhs:expr() { Expr::Binary(Arc::new(BinaryExpr { lhs, op: BinaryOp::Ge(op), rhs })) } + lhs:@ __ op:$("==") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Eq(op), rhs }.into()) } + lhs:@ __ op:$("!=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Ne(op), rhs }.into()) } + lhs:@ __ op:$("<") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs }.into()) } + lhs:@ __ op:$(">") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs }.into()) } + lhs:@ __ op:$("<=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs }.into()) } + lhs:@ __ op:$(">=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Ge(op), rhs }.into()) } -- - block:block() { Expr::Block(block)} - for_loop:for_loop() { Expr::For(Arc::new(for_loop))} - while_loop:while_loop() { Expr::While(Arc::new(while_loop))} + block:block() { Expr::Block(block.into())} + for_loop:for_loop() { Expr::For(for_loop.into())} + while_loop:while_loop() { Expr::While(while_loop.into())} + if_statement:if_statement() { Expr::If(if_statement.into())} func:func() { Expr::Func(Arc::new(func))} - var_decl:var_decl() { Expr::VarDecl(Arc::new(var_decl)) } - const_decl:const_decl() { Expr::ConstDecl(Arc::new(const_decl)) } + var_decl:var_decl() { Expr::VarDecl(var_decl.into()) } + const_decl:const_decl() { Expr::ConstDecl(const_decl.into()) } "(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) } "[" __ list:(expr() ** ("," __)) __ "]" { Expr::List(list) } "(" __ v:expr() __ ")" { v } - "*" __ m:$"mut"? __ v:expr() { Expr::Type(Arc::new(Type::Ptr { base:v, mutable: m })) } - v:struct_t() { Expr::Type(Arc::new(Type::Struct(v))) } + "*" __ m:$"mut"? __ v:expr() { Expr::Type(Type::Ptr { base:v, mutable: m }.into()) } + v:struct_t() { Expr::Type(Type::Struct(v).into()) } v:string() { Expr::String(v) } v:number() { Expr::Number(v) } v:ident() { Expr::Ident(v) } @@ -121,7 +125,7 @@ peg::parser! { rule func() -> Function = s:position!() t:$"fn" __ "(" __ args:name_type_pairs() __ ")" __ ret:("->" __ e:expr() {e})? __ block:block()? e:position!() - { Function { args, ret, block: block.map(Arc::new), text: t.parent().substr(s..e), } } + { Function { args, ret, block: block.map(Into::into), text: t.parent().substr(s..e), } } rule name_type_pair() -> NameValuePair = name:ident() __ ":" __ value:expr() { NameValuePair { name, value } } @@ -168,8 +172,15 @@ peg::parser! { { For { names, value, block } } rule while_loop() -> While = - "while" _ value:expr() _ block:block() - { While { value, block } } + "while" _ cond:expr() _ block:block() + { While { cond, block } } + + rule if_statement() -> If = + "if" _ cond:expr() __ block:block() e:(__ e:else_statement() {e})? { If { cond, block, else_: e.map(Box::new) } } + + rule else_statement() -> Else + = "else" _ i:if_statement() { Else::If(i) } + / "else" __ b:block() { Else::Block(b) } pub rule compilation_unit() -> CompilationUnit = __ imports:(i:import() statement_separator() {i})*