1127 lines
32 KiB
Rust
Executable File
1127 lines
32 KiB
Rust
Executable File
use crate::{CompilationContext, FuncQueue, diagnostics::*, events::Event, metadata::CodePosition};
|
|
use arcstr::{Substr, literal_substr};
|
|
use leaf_assembly::{
|
|
assembly::Assembly,
|
|
functions::{
|
|
Function,
|
|
ir::{Cmp, FunctionBodyBuilder, PhiValue},
|
|
},
|
|
types::{
|
|
IntT, Type,
|
|
compound::{Field, FieldMap, StructT},
|
|
derivations::{PtrT, RefT},
|
|
},
|
|
values::{AnyConst, AnyValue, Int, Value, ValueFlags},
|
|
};
|
|
use leaf_parser::{
|
|
SourceCode,
|
|
ast::{self, *},
|
|
};
|
|
use std::{
|
|
borrow::Cow,
|
|
collections::HashMap,
|
|
ops::Range,
|
|
sync::{Arc, OnceLock},
|
|
};
|
|
|
|
struct ExpressionContext<'l, 'r> {
|
|
decl_names: Option<&'r NamePattern>,
|
|
type_hint: Option<Type<'l>>,
|
|
builder: Option<&'r mut FunctionBodyBuilder<'l>>,
|
|
fn_queue: &'r mut FuncQueue<'l>,
|
|
}
|
|
|
|
impl<'l, 'r, 'a> ExpressionContext<'l, 'r> {
|
|
pub fn with_type_hit(&'a mut self, type_hint: Option<Type<'l>>) -> ExpressionContext<'l, 'a> {
|
|
ExpressionContext {
|
|
decl_names: self.decl_names,
|
|
type_hint,
|
|
builder: self.builder.as_mut().map(|b| &mut **b),
|
|
fn_queue: self.fn_queue,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct Variable<'l> {
|
|
value: Arc<OnceLock<AnyValue<'l>>>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Scope<'l> {
|
|
ctx: &'l CompilationContext<'l>,
|
|
assembly: &'l Assembly<'l>,
|
|
source: Arc<SourceCode>,
|
|
values: HashMap<Substr, Variable<'l>>,
|
|
}
|
|
|
|
impl<'l> Scope<'l> {
|
|
pub fn new(
|
|
ctx: &'l CompilationContext<'l>,
|
|
assembly: &'l Assembly<'l>,
|
|
source: Arc<SourceCode>,
|
|
) -> Self {
|
|
Self {
|
|
ctx,
|
|
assembly,
|
|
source,
|
|
values: HashMap::default(),
|
|
}
|
|
}
|
|
|
|
pub fn insert(&mut self, name: Substr, value: AnyValue<'l>) {
|
|
self.values.insert(
|
|
name,
|
|
Variable {
|
|
value: Arc::new(OnceLock::from(value)),
|
|
},
|
|
);
|
|
}
|
|
|
|
pub fn declare_constants(&mut self, decl: &[AstNode<ConstDecl>]) {
|
|
for val in decl {
|
|
for range in val.names.as_slice() {
|
|
let name = self.get_text_arc(range);
|
|
self.values.insert(
|
|
name,
|
|
Variable {
|
|
value: Arc::default(),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn define_constants(
|
|
&mut self,
|
|
decl: &[AstNode<ConstDecl>],
|
|
fn_queue: &mut FuncQueue<'l>,
|
|
) -> Result<(), Diagnostic> {
|
|
for val in decl {
|
|
let expr = self.compile_expression(
|
|
&val.value,
|
|
&mut ExpressionContext {
|
|
decl_names: Some(&val.names),
|
|
builder: None,
|
|
type_hint: None,
|
|
fn_queue,
|
|
},
|
|
)?;
|
|
match &*val.names {
|
|
NamePattern::Single(ident) => {
|
|
let name = self.get_text(ident).to_string();
|
|
|
|
self.values
|
|
.get_mut(&*name)
|
|
.unwrap()
|
|
.value
|
|
.set(expr)
|
|
.unwrap();
|
|
|
|
self.ctx.emit_event(Event::Definition {
|
|
value: expr,
|
|
position: CodePosition::new(self.source.clone(), ident.clone()),
|
|
});
|
|
}
|
|
NamePattern::Tuple(_) => todo!(),
|
|
NamePattern::List(_) => todo!(),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn compile_function(
|
|
&mut self,
|
|
func: &'l Function<'l>,
|
|
block: &Arc<AstNode<ast::Block>>,
|
|
fn_queue: &mut FuncQueue<'l>,
|
|
) -> Result<(), Diagnostic> {
|
|
let mut builder = func.create_body().unwrap();
|
|
let mut ret = self.compile_block(
|
|
block,
|
|
&mut ExpressionContext {
|
|
builder: Some(&mut builder),
|
|
decl_names: None,
|
|
type_hint: Some(func.ty.ret_t),
|
|
fn_queue: fn_queue,
|
|
},
|
|
)?;
|
|
|
|
if !builder.current_block().has_termination() {
|
|
match func.ty.ret_t {
|
|
Type::Void => {
|
|
builder.ret(None).unwrap();
|
|
}
|
|
_ => {
|
|
if ret.is_lvalue() {
|
|
ret = builder.load(ret).unwrap();
|
|
}
|
|
self.assert_ty_eq(
|
|
&ret,
|
|
&AstNode {
|
|
range: block.range.clone(),
|
|
node: Expr::Block(block.clone()),
|
|
},
|
|
&func.ty.ret_t,
|
|
)?;
|
|
builder.ret(Some(ret)).unwrap();
|
|
}
|
|
};
|
|
}
|
|
|
|
builder.build().unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
fn compile_expression(
|
|
&mut self,
|
|
expr: &AstNode<Expr>,
|
|
ctx: &mut ExpressionContext<'l, '_>,
|
|
) -> Result<AnyValue<'l>, Diagnostic> {
|
|
match &**expr {
|
|
Expr::Ident(range) => {
|
|
let name = self.get_text(range);
|
|
match self.values.get(&*name) {
|
|
None => Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::SymbolNotFound,
|
|
message: format!("Symbol `{name}` does not exist in the current scope."),
|
|
position: CodePosition::new(self.source.clone(), range.clone()),
|
|
cause: None,
|
|
}),
|
|
Some(Variable { value, .. }) => match value.get() {
|
|
None => Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::UninitializedSymbol,
|
|
message: format!("Symbol `{name}` is not initialized at this time."),
|
|
position: CodePosition::new(self.source.clone(), range.clone()),
|
|
cause: None,
|
|
}),
|
|
Some(value) => {
|
|
self.ctx.emit_event(Event::Symbol {
|
|
value: *value,
|
|
position: CodePosition::new(self.source.clone(), range.clone()),
|
|
});
|
|
Ok(*value)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
Expr::Func(func) => self
|
|
.make_function(func, ctx)
|
|
.map(|f| AnyValue::Constant(AnyConst::Function(f)))
|
|
.map_err(|err| Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::FunctionCompilationFailed,
|
|
message: "Could not compile function.".to_string(),
|
|
position: CodePosition::new(self.source.clone(), expr.range.clone()),
|
|
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) => {{
|
|
let text = self.get_text(&n.number);
|
|
match text.split_at_checked(2) {
|
|
Some(("0b", value)) => <$ty>::from_str_radix(value, 2),
|
|
Some(("0x", value)) => <$ty>::from_str_radix(value, 16),
|
|
_ => text.parse::<$ty>(),
|
|
}
|
|
.map(|v| AnyValue::Constant(AnyConst::Int(Int::$id(v))))
|
|
.map_err(|_| Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidInteger,
|
|
message: format!("`{}` is not a valid integer.", text),
|
|
position: CodePosition::new(self.source.clone(), n.number.clone()),
|
|
cause: None,
|
|
})
|
|
}};
|
|
}
|
|
let ty = n.ty.as_ref().map(|v| self.get_text(v));
|
|
let value = match ty.as_ref().map(|ty| &**ty) {
|
|
None if ctx.type_hint == Some(Type::I8) => parse_number!(i8, I8),
|
|
None if ctx.type_hint == Some(Type::I16) => parse_number!(i16, I16),
|
|
None if ctx.type_hint == Some(Type::I32) => parse_number!(i32, I32),
|
|
None if ctx.type_hint == Some(Type::I64) => parse_number!(i64, I64),
|
|
None if ctx.type_hint == Some(Type::I128) => parse_number!(i128, I128),
|
|
None if ctx.type_hint == Some(Type::ISIZE) => parse_number!(i64, ISize),
|
|
None if ctx.type_hint == Some(Type::U8) => parse_number!(u8, U8),
|
|
None if ctx.type_hint == Some(Type::U16) => parse_number!(u16, U16),
|
|
None if ctx.type_hint == Some(Type::U32) => parse_number!(u32, U32),
|
|
None if ctx.type_hint == Some(Type::U64) => parse_number!(u64, U64),
|
|
None if ctx.type_hint == Some(Type::U128) => parse_number!(u128, U128),
|
|
None if ctx.type_hint == Some(Type::USIZE) => parse_number!(u64, USize),
|
|
None => parse_number!(i64, 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!(i64, 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!(u64, USize),
|
|
Some(ty) => Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidIntegerType,
|
|
message: format!("`{ty}` is not a valid integer type."),
|
|
position: CodePosition::new(self.source.clone(), n.ty.clone().unwrap()),
|
|
cause: None,
|
|
}),
|
|
}?;
|
|
self.ctx.emit_event(Event::Symbol {
|
|
value: value,
|
|
position: CodePosition::new(self.source.clone(), n.number.clone()),
|
|
});
|
|
Ok(value)
|
|
}
|
|
|
|
Expr::Access {
|
|
value: value_expr,
|
|
operator,
|
|
field,
|
|
} => {
|
|
let value = self.compile_expression(value_expr, ctx)?;
|
|
let name = self.get_text(field);
|
|
if let Some(value) = value.get_associated_value(&*name) {
|
|
return Ok(value);
|
|
}
|
|
|
|
let value = 'value: {
|
|
match value.ty() {
|
|
Type::Struct(StructT { fields, .. }) => {
|
|
if let Some(fields) = fields.get() {
|
|
if let Some(field) = fields.get(&*name) {
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
break 'value Some(
|
|
builder
|
|
.get_element_value(value, field.name.as_any_value())
|
|
.unwrap()
|
|
.as_any_value(),
|
|
);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
Type::Ptr(PtrT {
|
|
base: Type::Struct(StructT { fields, .. }),
|
|
..
|
|
}) => {
|
|
if let Some(fields) = fields.get() {
|
|
if let Some(field) = fields.get(&*name) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
break 'value Some(inst);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
_ => None,
|
|
}
|
|
};
|
|
|
|
if let Some(value) = value {
|
|
self.ctx.emit_event(Event::Symbol {
|
|
value: value,
|
|
position: CodePosition::new(self.source.clone(), field.clone()),
|
|
});
|
|
return Ok(value);
|
|
}
|
|
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::FieldNotFound,
|
|
message: format!("Value does not contain field `{name}`."),
|
|
position: CodePosition::new(self.source.clone(), field.clone()),
|
|
cause: None,
|
|
});
|
|
}
|
|
|
|
Expr::Binary {
|
|
lhs: lhs_expr,
|
|
rhs: rhs_expr,
|
|
operator,
|
|
} => {
|
|
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
|
|
let type_hint = if lhs.is_lvalue() {
|
|
let Type::Ptr(PtrT { base, .. }) = lhs.ty() else {
|
|
unreachable!();
|
|
};
|
|
if !matches!(operator.node, BinaryOperator::Assign) {
|
|
lhs = ctx.builder.as_mut().unwrap().load(lhs).unwrap();
|
|
}
|
|
*base
|
|
} else {
|
|
lhs.ty()
|
|
};
|
|
|
|
let mut rhs =
|
|
self.compile_expression(rhs_expr, &mut ctx.with_type_hit(Some(type_hint)))?;
|
|
if rhs.is_lvalue() {
|
|
rhs = ctx.builder.as_mut().unwrap().load(rhs).unwrap();
|
|
}
|
|
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
macro_rules! int_bin_ops {
|
|
(
|
|
const exact
|
|
$([$($ty:ident),*] $op:pat => |$a:ident, $b:ident| $expr:expr,)*
|
|
) => {
|
|
match operator.node {
|
|
$(
|
|
$op => match (lhs, rhs) {
|
|
$(
|
|
(
|
|
AnyValue::Constant(AnyConst::Int(Int::$ty($a))),
|
|
AnyValue::Constant(AnyConst::Int(Int::$ty($b))),
|
|
) => return Ok(AnyValue::Constant(AnyConst::Int(Int::$ty($expr)))),
|
|
)*
|
|
_ => {}
|
|
}
|
|
)*
|
|
_ => {}
|
|
}
|
|
};
|
|
(
|
|
const auto
|
|
$([$($ty:ident),*] $op:pat => |$a:ident, $b:ident| $expr:expr,)*
|
|
) => {
|
|
match operator.node {
|
|
$(
|
|
$op => match (lhs, rhs) {
|
|
$(
|
|
(
|
|
AnyValue::Constant(AnyConst::Int(Int::$ty($a))),
|
|
AnyValue::Constant(AnyConst::Int(Int::$ty($b))),
|
|
) => return Ok($expr.as_any_value()),
|
|
)*
|
|
_ => {}
|
|
}
|
|
)*
|
|
_ => {}
|
|
}
|
|
};
|
|
}
|
|
|
|
if lhs.is_const() && rhs.is_const() {
|
|
int_bin_ops! {
|
|
const exact
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Add => |a, b| a + b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Sub => |a, b| a - b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Mul => |a, b| a - b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Div => |a, b| a - b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Mod => |a, b| a - b,
|
|
}
|
|
int_bin_ops! {
|
|
const auto
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Eq => |a, b| a == b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Ne => |a, b| a == b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Lt => |a, b| a == b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Gt => |a, b| a == b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Le => |a, b| a == b,
|
|
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOperator::Ge => |a, b| a == b,
|
|
}
|
|
}
|
|
|
|
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,
|
|
} {
|
|
return Ok(match operator.node {
|
|
BinaryOperator::Add => builder.add(lhs, rhs).unwrap(),
|
|
BinaryOperator::Sub => builder.sub(lhs, rhs).unwrap(),
|
|
BinaryOperator::Mul => builder.mul(lhs, rhs).unwrap(),
|
|
BinaryOperator::Div => builder.div(lhs, rhs).unwrap(),
|
|
BinaryOperator::Mod => builder.modulo(lhs, rhs).unwrap(),
|
|
BinaryOperator::Eq => builder.cmp(lhs, rhs, Cmp::Eq).unwrap(),
|
|
BinaryOperator::Ne => builder.cmp(lhs, rhs, Cmp::Ne).unwrap(),
|
|
BinaryOperator::Lt => builder.cmp(lhs, rhs, Cmp::Lt).unwrap(),
|
|
BinaryOperator::Gt => builder.cmp(lhs, rhs, Cmp::Gt).unwrap(),
|
|
BinaryOperator::Le => builder.cmp(lhs, rhs, Cmp::Le).unwrap(),
|
|
BinaryOperator::Ge => builder.cmp(lhs, rhs, Cmp::Ge).unwrap(),
|
|
_ => todo!("{lhs:?} {:?} {rhs:?}", operator.node),
|
|
});
|
|
}
|
|
|
|
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 operator.node {
|
|
BinaryOperator::Eq => builder.cmp(lhs, rhs, Cmp::Eq).unwrap(),
|
|
BinaryOperator::Ne => builder.cmp(lhs, rhs, Cmp::Ne).unwrap(),
|
|
BinaryOperator::Lt => builder.cmp(lhs, rhs, Cmp::Lt).unwrap(),
|
|
BinaryOperator::Gt => builder.cmp(lhs, rhs, Cmp::Gt).unwrap(),
|
|
BinaryOperator::Le => builder.cmp(lhs, rhs, Cmp::Le).unwrap(),
|
|
BinaryOperator::Ge => builder.cmp(lhs, rhs, Cmp::Ge).unwrap(),
|
|
_ => todo!("{lhs:?} {:?} {rhs:?}", operator.node),
|
|
});
|
|
}
|
|
|
|
match (lhs_ty, rhs_ty, operator.node) {
|
|
(Type::Ptr(ptr @ PtrT { base, .. }), Type::USIZE, BinaryOperator::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, BinaryOperator::Assign) => match *base == ty
|
|
{
|
|
true => Ok(builder.store(lhs, rhs).unwrap()),
|
|
false => Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidType,
|
|
message: format!(
|
|
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
|
|
),
|
|
position: CodePosition::new(self.source.clone(), expr.range.clone()),
|
|
cause: None,
|
|
}),
|
|
},
|
|
(src_ty, Type::Type, BinaryOperator::Cast) => {
|
|
let dst_ty = self.assert_ty(rhs, rhs_expr).map_err(|err| Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidCast,
|
|
message: "Cannot perform cast.".to_string(),
|
|
position: CodePosition::new(self.source.clone(), expr.range.clone()),
|
|
cause: Some(Box::new(err)),
|
|
})?;
|
|
if src_ty == dst_ty {
|
|
return Ok(lhs);
|
|
}
|
|
match (src_ty, dst_ty) {
|
|
(Type::Int(src_ty), Type::Int(dst_ty)) => {
|
|
if dst_ty.precision < src_ty.precision {
|
|
return Ok(builder.trunc(lhs, dst_ty).unwrap());
|
|
}
|
|
todo!("{src_ty} as {dst_ty}");
|
|
}
|
|
(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}"),
|
|
}
|
|
}
|
|
(a, b, _) => todo!(
|
|
"{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}",
|
|
op = operator.node,
|
|
),
|
|
}
|
|
}
|
|
|
|
Expr::If(expr) => self.compile_if(expr, ctx),
|
|
|
|
Expr::While(expr) => {
|
|
let While { cond, 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(cond, ctx)?;
|
|
|
|
builder = ctx.builder.as_mut().unwrap();
|
|
builder.branch(condition, exec_block, exit_block).unwrap();
|
|
builder.set_current_block(exec_block);
|
|
|
|
let ret = self.compile_block(block, ctx)?;
|
|
builder = ctx.builder.as_mut().unwrap();
|
|
|
|
builder.jump(cond_block).unwrap();
|
|
builder.set_current_block(exit_block);
|
|
Ok(ret)
|
|
}
|
|
|
|
Expr::Call {
|
|
func,
|
|
args: args_exprs,
|
|
} => {
|
|
let func = match self.compile_expression(func, ctx)? {
|
|
AnyValue::Constant(AnyConst::Function(func)) => func,
|
|
_ => {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::NotAFunction,
|
|
message: "Value is not a function".into(),
|
|
position: CodePosition::new(self.source.clone(), func.range.clone()),
|
|
cause: None,
|
|
});
|
|
}
|
|
};
|
|
let mut arg_ty = func.ty.par_t.iter().cloned();
|
|
let mut args = Vec::with_capacity(args_exprs.len());
|
|
for expr in args_exprs {
|
|
let mut arg =
|
|
self.compile_expression(expr, &mut ctx.with_type_hit(arg_ty.next()))?;
|
|
if arg.is_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::Ptr { mutable, base, .. } => match self.compile_expression(base, ctx)? {
|
|
AnyValue::Constant(AnyConst::Type(ty)) => {
|
|
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(mutable.is_some()))).into())
|
|
}
|
|
AnyValue::Instruction(inst) if inst.is_lvalue() => {
|
|
let Type::Ptr(PtrT {
|
|
base,
|
|
mutable: is_mut,
|
|
..
|
|
}) = inst.ty()
|
|
else {
|
|
unreachable!()
|
|
};
|
|
if mutable.is_some() && !*is_mut {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::NotAFunction,
|
|
message: "Cannot obtain a mutable pointer to an immutable value."
|
|
.into(),
|
|
position: CodePosition::new(self.source.clone(), expr.range.clone()),
|
|
cause: None,
|
|
});
|
|
}
|
|
let mut flags = inst.flags();
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
flags.remove(ValueFlags::Mutable | ValueFlags::Volatile | ValueFlags::LValue);
|
|
let ptr = Type::Ptr(base.make_ptr(mutable.is_some()));
|
|
unsafe {
|
|
Ok(builder
|
|
.reinterpret(AnyValue::Instruction(inst), ptr, flags)
|
|
.unwrap())
|
|
}
|
|
}
|
|
v => todo!("{v:?}"),
|
|
},
|
|
Expr::Struct { fields, .. } => {
|
|
let name = match &ctx.decl_names {
|
|
Some(NamePattern::Single(func_name)) => &*self.get_text(func_name),
|
|
_ => "",
|
|
};
|
|
let struct_ty = self.assembly.create_struct(name);
|
|
let mut scope = self.clone();
|
|
let mut expr_ctx = ExpressionContext {
|
|
builder: None,
|
|
decl_names: None,
|
|
type_hint: Some(Type::Type),
|
|
fn_queue: ctx.fn_queue,
|
|
};
|
|
let ctx = self.assembly.ctx();
|
|
|
|
scope.insert(literal_substr!("Self"), struct_ty.as_any_value());
|
|
let mut field_map = FieldMap::default();
|
|
for AstNode {
|
|
range,
|
|
node:
|
|
ast::Field {
|
|
name,
|
|
ty: ty_expr,
|
|
public,
|
|
mutable,
|
|
},
|
|
} in fields
|
|
{
|
|
let ty = scope.compile_expression(ty_expr, &mut expr_ctx)?;
|
|
let name = ctx.intern_str(&self.get_text(name));
|
|
field_map.insert(
|
|
name,
|
|
Field {
|
|
name,
|
|
ty: self.assert_ty(ty, ty_expr)?,
|
|
public: public.is_some(),
|
|
mutable: mutable.is_some(),
|
|
},
|
|
);
|
|
}
|
|
struct_ty.fields.set(field_map).unwrap();
|
|
Ok(struct_ty.as_any_value())
|
|
}
|
|
|
|
Expr::List(expr) => {
|
|
let mut expr = expr.iter();
|
|
let mut values = Vec::with_capacity(expr.len());
|
|
match expr.next() {
|
|
None => return Ok(AnyValue::Constant(AnyConst::Array(&[]))),
|
|
Some(expr) => {
|
|
let value = self.compile_expression(expr, ctx)?;
|
|
// TODO Check if it matches the ctx type hint
|
|
values.push(value);
|
|
}
|
|
};
|
|
let element_ty = values[0].ty();
|
|
for expr in expr {
|
|
let value = self.compile_expression(expr, ctx)?;
|
|
self.assert_ty_eq(&value, expr, &element_ty)?;
|
|
values.push(value);
|
|
}
|
|
if values.iter().all(|v| matches!(v, AnyValue::Constant(_))) {
|
|
let alloc = self.assembly.ctx().alloc();
|
|
return Ok(AnyValue::Constant(AnyConst::Array(alloc.alloc_slice(
|
|
values.into_iter().map(|v| match v {
|
|
AnyValue::Constant(c) => c,
|
|
_ => unreachable!(),
|
|
}),
|
|
))));
|
|
}
|
|
todo!()
|
|
}
|
|
|
|
Expr::Deref { value: 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(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::CannotDereference,
|
|
message: format!("Cannot dereference a value of type `{ty}`."),
|
|
position: CodePosition::new(
|
|
self.source.clone(),
|
|
expr.range.clone(),
|
|
),
|
|
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(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::CannotDereference,
|
|
message: format!("Cannot dereference a value of type `{base}`."),
|
|
position: CodePosition::new(
|
|
self.source.clone(),
|
|
expr.range.clone(),
|
|
),
|
|
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 {
|
|
value,
|
|
index: index_expr,
|
|
} if index_expr.len() == 1 => {
|
|
let mut value = self.compile_expression(value, ctx)?;
|
|
let mut index = self.compile_expression(&index_expr[0], ctx)?;
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
|
|
if index.is_lvalue() {
|
|
index = builder.load(index).unwrap();
|
|
}
|
|
|
|
// TODO Add support for custom indexing operations
|
|
if index.ty() != Type::USIZE {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidType,
|
|
message: "Value is not of type `usize`".into(),
|
|
position: CodePosition::new(
|
|
self.source.clone(),
|
|
index_expr[0].range.clone(),
|
|
),
|
|
cause: None,
|
|
});
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
todo!("{:#?}", value.ty());
|
|
}
|
|
|
|
Expr::StructCtor {
|
|
ty: ctor_ty,
|
|
values: ctor_values,
|
|
} => {
|
|
let ty = match ctor_ty {
|
|
Some(ty) => self.compile_expression(ty, ctx)?,
|
|
None => match ctx.type_hint {
|
|
Some(ty) => AnyValue::Constant(AnyConst::Type(ty)),
|
|
None => {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::CannotInferType,
|
|
message: "Type cannot be inferred.".into(),
|
|
position: CodePosition::new(
|
|
self.source.clone(),
|
|
expr.range.clone(),
|
|
),
|
|
cause: None,
|
|
});
|
|
}
|
|
},
|
|
};
|
|
let AnyValue::Constant(AnyConst::Type(Type::Struct(
|
|
struct_ty @ StructT { fields, .. },
|
|
))) = ty
|
|
else {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::NotAStruct,
|
|
message: format!("Expected struct type, got value of type `{}`.", ty.ty()),
|
|
position: CodePosition::new(
|
|
self.source.clone(),
|
|
match ctor_ty {
|
|
None => expr.range.clone(),
|
|
Some(ty) => ty.range.clone(),
|
|
},
|
|
),
|
|
cause: None,
|
|
});
|
|
};
|
|
|
|
let mut non_const = false;
|
|
let fields = fields.get().unwrap();
|
|
let mut values = Vec::with_capacity(fields.len());
|
|
for Field {
|
|
name: fld_name, ty, ..
|
|
} in fields.values()
|
|
{
|
|
let Some(name_value_pair) = ctor_values.iter().find_map(|v| {
|
|
let name = self.get_text(&v.name);
|
|
match name == *fld_name {
|
|
true => Some(&v.node),
|
|
false => None,
|
|
}
|
|
}) else {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::UninitializedField,
|
|
message: format!("Uninitialized field `{fld_name}`."),
|
|
position: CodePosition::new(self.source.clone(), expr.range.clone()),
|
|
cause: None,
|
|
});
|
|
};
|
|
let mut ctx = ctx.with_type_hit(Some(*ty));
|
|
let value = self.compile_expression(&name_value_pair.value, &mut ctx)?;
|
|
self.assert_ty_eq(&value, &name_value_pair.value, ty)?;
|
|
non_const |= !value.flags().contains(ValueFlags::Const);
|
|
values.push(value);
|
|
}
|
|
|
|
Ok(match non_const {
|
|
true => {
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
let values = self.assembly.ctx().alloc().alloc_slice(values.into_iter());
|
|
builder.make_struct(struct_ty, values).unwrap()
|
|
}
|
|
false => AnyValue::Constant(AnyConst::Struct(
|
|
struct_ty,
|
|
self.assembly
|
|
.ctx()
|
|
.alloc()
|
|
.alloc_slice(values.into_iter().map(|v| {
|
|
let AnyValue::Constant(c) = v else {
|
|
unreachable!()
|
|
};
|
|
c
|
|
})),
|
|
)),
|
|
})
|
|
}
|
|
|
|
_ => todo!("{expr:#?}"),
|
|
}
|
|
}
|
|
|
|
fn make_function(
|
|
&mut self,
|
|
ast: &Arc<AstNode<ast::Function>>,
|
|
ctx: &mut ExpressionContext<'l, '_>,
|
|
) -> Result<&'l Function<'l>, Diagnostic> {
|
|
let ret_ty = match ast.ret.as_ref() {
|
|
None => AnyValue::Constant(AnyConst::Type(Type::Void)),
|
|
Some(ty) => self.compile_expression(ty, ctx)?,
|
|
};
|
|
let ast_as_expr = AstNode {
|
|
range: ast.range.clone(),
|
|
node: Expr::Func(ast.clone()),
|
|
};
|
|
let ret_ty = self.assert_ty(ret_ty, ast.ret.as_ref().unwrap_or(&ast_as_expr))?;
|
|
let mut par_ty = Vec::with_capacity(ast.args.len());
|
|
for arg in &ast.args {
|
|
let ty = self.compile_expression(&arg.value, ctx)?;
|
|
let ty = self.assert_ty(ty, &arg.value)?;
|
|
par_ty.push(ty);
|
|
}
|
|
|
|
let fn_ty = ret_ty.make_fn(par_ty);
|
|
let name = match &ctx.decl_names {
|
|
Some(NamePattern::Single(func_name)) => &*self.get_text(func_name),
|
|
_ => "",
|
|
};
|
|
let func = self.assembly.create_function(fn_ty, name);
|
|
let Some(block) = &ast.block else {
|
|
return Ok(func);
|
|
};
|
|
|
|
let mut scope = self.clone();
|
|
for (i, arg) in ast.args.iter().enumerate() {
|
|
let name = self.get_text_arc(&arg.name);
|
|
scope.insert(name, AnyValue::Parameter(i, func));
|
|
}
|
|
|
|
ctx.fn_queue.push_back((func, block.clone(), scope));
|
|
|
|
Ok(func)
|
|
}
|
|
|
|
fn compile_decl(
|
|
&mut self,
|
|
names: &NamePattern,
|
|
value: &AstNode<Expr>,
|
|
mutable: bool,
|
|
ctx: &mut ExpressionContext<'l, '_>,
|
|
) -> Result<AnyValue<'l>, Diagnostic> {
|
|
let mut sub_ctx = ExpressionContext {
|
|
decl_names: Some(names),
|
|
builder: ctx.builder.as_deref_mut(),
|
|
type_hint: None, //TODO
|
|
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 variable = builder.stack_alloc(value.ty()).unwrap();
|
|
builder.store(variable, value).unwrap();
|
|
value = variable;
|
|
}
|
|
match names {
|
|
NamePattern::Single(ident) => {
|
|
let name = self.get_text_arc(ident);
|
|
self.values.insert(
|
|
name,
|
|
Variable {
|
|
value: Arc::new(OnceLock::from(value)),
|
|
},
|
|
);
|
|
|
|
self.ctx.emit_event(Event::Definition {
|
|
value: value,
|
|
position: CodePosition::new(self.source.clone(), ident.clone()),
|
|
});
|
|
}
|
|
NamePattern::Tuple(_) => todo!(),
|
|
NamePattern::List(_) => todo!(),
|
|
}
|
|
Ok(AnyConst::Void.into())
|
|
}
|
|
|
|
fn compile_block(
|
|
&mut self,
|
|
block: &Block,
|
|
ctx: &mut ExpressionContext<'l, '_>,
|
|
) -> Result<AnyValue<'l>, Diagnostic> {
|
|
let mut scope = self.clone();
|
|
let builder = ctx.builder.as_mut().unwrap();
|
|
|
|
let mut last_expr = None;
|
|
for (i, expr) in block.0.iter().enumerate() {
|
|
let mut ctx = ExpressionContext {
|
|
builder: Some(builder),
|
|
decl_names: None,
|
|
type_hint: match i == block.0.len() - 1 {
|
|
true => ctx.type_hint,
|
|
false => None,
|
|
},
|
|
fn_queue: ctx.fn_queue,
|
|
};
|
|
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>, Diagnostic> {
|
|
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 { expr, .. } => self.compile_block(expr, ctx)?,
|
|
Else::If { expr, .. } => self.compile_if(expr, 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>,
|
|
value_expr: &AstNode<Expr>,
|
|
) -> Result<Type<'l>, Diagnostic> {
|
|
match val {
|
|
AnyValue::Constant(AnyConst::Type(ty)) => Ok(ty),
|
|
_ => Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::NotAType,
|
|
message: "Value is not a type.".to_string(),
|
|
position: CodePosition::new(self.source.clone(), value_expr.range.clone()),
|
|
cause: None,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn assert_ty_eq(
|
|
&self,
|
|
value: &AnyValue<'l>,
|
|
value_expr: &AstNode<Expr>,
|
|
expected: &Type<'l>,
|
|
) -> Result<Type<'l>, Diagnostic> {
|
|
let value_ty = value.ty();
|
|
match value_ty == *expected {
|
|
true => Ok(value_ty),
|
|
false => {
|
|
return Err(Diagnostic {
|
|
kind: Kind::Error,
|
|
code: Code::InvalidType,
|
|
message: format!("Expected value of type `{expected}`, found `{value_ty}`."),
|
|
position: CodePosition::new(self.source.clone(), value_expr.range.clone()),
|
|
cause: None,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_text(&self, range: &Range<usize>) -> Cow<'_, str> {
|
|
match &self.source.text {
|
|
leaf_parser::Text::ArcStr(arc_str, _) => {
|
|
Cow::Borrowed(&arc_str.as_str()[range.clone()])
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_text_arc(&self, range: &Range<usize>) -> Substr {
|
|
match &self.source.text {
|
|
leaf_parser::Text::ArcStr(arc_str, _) => arc_str.substr(range.clone()),
|
|
}
|
|
}
|
|
}
|