Ifs and Phis
This commit is contained in:
+2
-2
@@ -1,5 +1,5 @@
|
||||
target/
|
||||
test.leaf
|
||||
out.asm
|
||||
*.leaf
|
||||
*.asm
|
||||
a.out
|
||||
.zed/
|
||||
|
||||
+156
-26
@@ -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<u32>);
|
||||
@@ -17,6 +23,8 @@ unsafe impl Sync for Id {}
|
||||
#[display("%{}", unsafe { *id.0.get() })]
|
||||
pub struct Instruction<'l> {
|
||||
id: Id,
|
||||
ty: OnceLock<Type<'l>>,
|
||||
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<AnyValue<'l>> {
|
||||
@@ -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<AnyValue<'l>>),
|
||||
Jump(&'l Block<'l>),
|
||||
@@ -181,7 +220,7 @@ pub enum InstructionVariant<'l> {
|
||||
},
|
||||
Return(Option<AnyValue<'l>>),
|
||||
|
||||
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<T, BlockBuilderError<'l>>;
|
||||
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<Item = PhiValue<'l>>,
|
||||
) -> 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<Item = PhiValue<'l>>,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
+29
-12
@@ -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 });
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ impl<'l> Value<'l> for AnyValue<'l> {
|
||||
fn get_associated_value(&self, name: &str) -> Option<AnyValue<'l>> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
+98
-15
@@ -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() {
|
||||
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_ty): Result<BasicTypeEnum, _> = pointee_ty.try_into()
|
||||
};
|
||||
let Ok(pointee): Result<BasicTypeEnum, _> =
|
||||
self.get_type(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();
|
||||
|
||||
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<BasicTypeEnum, _> =
|
||||
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<BasicTypeEnum, _> =
|
||||
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!(),
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum Kind {
|
||||
NotAStruct = 0x0208,
|
||||
FieldNotFound = 0x0206,
|
||||
InvalidCast = 0x0207,
|
||||
CannotDereference = 0x0209,
|
||||
|
||||
UninitializedField = 0x0300,
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+236
-38
@@ -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<ast::Block>,
|
||||
fn_queue: &mut FuncQueue<'l>,
|
||||
) -> Result<(), CompilationError> {
|
||||
let mut builder = func.create_body().unwrap();
|
||||
let mut ctx = ExpressionContext {
|
||||
let mut ret = self.compile_block(
|
||||
block,
|
||||
&mut 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)?);
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
if !builder.current_block().has_termination() {
|
||||
match func.ty.ret_t {
|
||||
Type::Void => builder.ret(None).unwrap(),
|
||||
_ => {
|
||||
if let Some(expr) = last_expr.as_mut()
|
||||
&& expr.is_lvalue()
|
||||
{
|
||||
*expr = builder.load(*expr).unwrap();
|
||||
Type::Void => {
|
||||
builder.ret(None).unwrap();
|
||||
}
|
||||
builder.ret(last_expr).unwrap()
|
||||
_ => {
|
||||
if ret.is_lvalue() {
|
||||
ret = builder.load(ret).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)?;
|
||||
if mutable {
|
||||
let builder = sub_ctx.builder.unwrap();
|
||||
if value.is_lvalue() {
|
||||
value = builder.load(value).unwrap();
|
||||
}
|
||||
if mutable {
|
||||
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<AnyValue<'l>, 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<AnyValue<'l>, 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>,
|
||||
|
||||
+28
-12
@@ -26,30 +26,33 @@ pub enum Expr {
|
||||
Number(Number),
|
||||
String(Substr),
|
||||
#[debug("{_0:?}")]
|
||||
Binary(Arc<BinaryExpr>),
|
||||
Index(Arc<IndexingExpr>),
|
||||
Access(Arc<AccessExpr>),
|
||||
Binary(Box<BinaryExpr>),
|
||||
Index(Box<IndexingExpr>),
|
||||
Access(Box<AccessExpr>),
|
||||
Deref(Box<Expr>),
|
||||
Tuple(Vec<Expr>),
|
||||
List(Vec<Expr>),
|
||||
Struct(Arc<StructCtor>),
|
||||
Struct(Box<StructCtor>),
|
||||
#[debug("{_0:?}")]
|
||||
Block(Block),
|
||||
Block(Arc<Block>),
|
||||
#[debug("{_0:?}")]
|
||||
Func(Arc<Function>),
|
||||
#[debug("{_0:?}")]
|
||||
Type(Arc<Type>),
|
||||
Type(Box<Type>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
ConstDecl(Arc<ConstDecl>),
|
||||
ConstDecl(Box<ConstDecl>),
|
||||
#[debug("{_0:?}")]
|
||||
VarDecl(Arc<VarDecl>),
|
||||
VarDecl(Box<VarDecl>),
|
||||
#[debug("{_0:?}")]
|
||||
For(Arc<For>),
|
||||
For(Box<For>),
|
||||
#[debug("{_0:?}")]
|
||||
While(Arc<While>),
|
||||
While(Box<While>),
|
||||
#[debug("{_0:?}")]
|
||||
If(Box<If>),
|
||||
|
||||
Call {
|
||||
func: Arc<Expr>,
|
||||
func: Box<Expr>,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
}
|
||||
@@ -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<Box<Else>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Else {
|
||||
If(If),
|
||||
Block(Block),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompilationUnit {
|
||||
pub imports: Vec<Import>,
|
||||
|
||||
+39
-28
@@ -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: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})*
|
||||
|
||||
Reference in New Issue
Block a user