Structs and comptime reflection stuff
This commit is contained in:
@@ -14,9 +14,14 @@ pub enum Kind {
|
||||
SymbolNotFound = 0x0200,
|
||||
UninitializedSymbol = 0x0201,
|
||||
NotAType = 0x0202,
|
||||
NotAFunction = 0x0205,
|
||||
InvalidIntegerType = 0x0203,
|
||||
InvalidType = 0x0204,
|
||||
NotAFunction = 0x0205,
|
||||
NotAStruct = 0x0208,
|
||||
FieldNotFound = 0x0206,
|
||||
InvalidCast = 0x0207,
|
||||
|
||||
UninitializedField = 0x0300,
|
||||
|
||||
FunctionCompilationFailed = 0x0301,
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ use leaf_assembly::{
|
||||
assembly::{Assembly, AssemblyIdentifier, Context},
|
||||
functions::Function,
|
||||
types::Type,
|
||||
values::{AnyConst, Value},
|
||||
values::{AnyConst, AnyValue},
|
||||
};
|
||||
use leaf_parser::{SourceCode, ast};
|
||||
use std::{collections::VecDeque, ops::Deref, sync::Arc};
|
||||
@@ -66,7 +66,7 @@ impl<'l> CompilationContext<'l> {
|
||||
$(
|
||||
scope.insert(
|
||||
literal_substr!($id),
|
||||
Value::Constant(AnyConst::Type($ty.into())),
|
||||
AnyValue::Constant(AnyConst::Type($ty.into())),
|
||||
false,
|
||||
);
|
||||
)*
|
||||
|
||||
+317
-112
@@ -1,17 +1,24 @@
|
||||
use crate::{FuncQueue, error::*};
|
||||
use arcstr::Substr;
|
||||
use arcstr::{Substr, literal_substr};
|
||||
use leaf_assembly::{
|
||||
assembly::Assembly,
|
||||
functions::{
|
||||
Function,
|
||||
ir::{Cmp, FunctionBodyBuilder},
|
||||
},
|
||||
types::{Type, derivations::PtrT},
|
||||
values::{AnyConst, Int, Value, ValueFlags},
|
||||
types::{
|
||||
Type,
|
||||
compound::{Field, FieldMap, StructT},
|
||||
derivations::PtrT,
|
||||
},
|
||||
values::{AnyConst, AnyValue, Int, Value, ValueFlags},
|
||||
};
|
||||
use leaf_parser::{
|
||||
SourceCode,
|
||||
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, IndexingExpr, NamePattern, While},
|
||||
ast::{
|
||||
self, AccessExpr, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, IndexingExpr, NamePattern,
|
||||
While,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -26,7 +33,7 @@ struct ExpressionContext<'l, 'r> {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Variable<'l> {
|
||||
value: Arc<OnceLock<Value<'l>>>,
|
||||
value: Arc<OnceLock<AnyValue<'l>>>,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
@@ -46,7 +53,7 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: Substr, value: Value<'l>, mutable: bool) {
|
||||
pub fn insert(&mut self, name: Substr, value: AnyValue<'l>, mutable: bool) {
|
||||
self.values.insert(
|
||||
name,
|
||||
Variable {
|
||||
@@ -122,7 +129,7 @@ impl<'l> Scope<'l> {
|
||||
Type::Void => builder.ret(None).unwrap(),
|
||||
_ => {
|
||||
if let Some(expr) = last_expr.as_mut()
|
||||
&& expr.flags().contains(ValueFlags::LValue)
|
||||
&& expr.is_lvalue()
|
||||
{
|
||||
*expr = builder.load(*expr).unwrap();
|
||||
}
|
||||
@@ -139,7 +146,7 @@ impl<'l> Scope<'l> {
|
||||
&mut self,
|
||||
expr: &Expr,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<Value<'l>, CompilationError> {
|
||||
) -> Result<AnyValue<'l>, CompilationError> {
|
||||
match expr {
|
||||
Expr::Ident(Ident(name)) => match self.values.get(name) {
|
||||
None => Err(CompilationError {
|
||||
@@ -167,7 +174,7 @@ impl<'l> Scope<'l> {
|
||||
|
||||
Expr::Func(func) => self
|
||||
.make_function(func, ctx)
|
||||
.map(|f| Value::Constant(AnyConst::Function(f)))
|
||||
.map(|f| AnyValue::Constant(AnyConst::Function(f)))
|
||||
.map_err(|err| CompilationError {
|
||||
kind: Kind::FunctionCompilationFailed,
|
||||
message: "Could not compile function.".to_string(),
|
||||
@@ -189,7 +196,7 @@ impl<'l> Scope<'l> {
|
||||
Some(("0x", value)) => <$ty>::from_str_radix(value, 16),
|
||||
_ => n.text.parse::<$ty>(),
|
||||
}
|
||||
.map(|v| Value::Constant(AnyConst::Int(Int::$id(v))))
|
||||
.map(|v| AnyValue::Constant(AnyConst::Int(Int::$id(v))))
|
||||
.map_err(|_| CompilationError {
|
||||
kind: Kind::InvalidInteger,
|
||||
message: format!("`{}` is not a valid integer.", n.text),
|
||||
@@ -202,19 +209,19 @@ impl<'l> Scope<'l> {
|
||||
};
|
||||
}
|
||||
match n.r#type.as_ref().map(|v| v.as_str()) {
|
||||
None => parse_number!(i128, ISize),
|
||||
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!(i128, ISize),
|
||||
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!(u128, USize),
|
||||
Some("usize") => parse_number!(u64, USize),
|
||||
Some(ty) => Err(CompilationError {
|
||||
kind: Kind::InvalidIntegerType,
|
||||
message: format!("`{ty}` is not a valid integer type."),
|
||||
@@ -227,6 +234,40 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Access(expr) => {
|
||||
let AccessExpr {
|
||||
value: value_expr,
|
||||
field,
|
||||
} = &**expr;
|
||||
let value = self.compile_expression(value_expr, ctx)?;
|
||||
if let Some(value) = value.get_associated_value(&field.0) {
|
||||
return Ok(value);
|
||||
}
|
||||
match value.ty() {
|
||||
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();
|
||||
return Ok(builder
|
||||
.get_element_value(value, field.name.as_any_value())
|
||||
.unwrap()
|
||||
.as_any_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
return Err(CompilationError {
|
||||
kind: Kind::FieldNotFound,
|
||||
message: format!("Value does not contain field `{}`.", field.0),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: value_expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
|
||||
Expr::Binary(bin_expr) => {
|
||||
let BinaryExpr {
|
||||
lhs: lhs_expr,
|
||||
@@ -235,50 +276,98 @@ impl<'l> Scope<'l> {
|
||||
} = &**bin_expr;
|
||||
|
||||
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
|
||||
if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) {
|
||||
if lhs.is_lvalue() && !matches!(op, BinaryOp::Assign(_)) {
|
||||
lhs = ctx.builder.as_mut().unwrap().load(lhs).unwrap();
|
||||
}
|
||||
|
||||
let mut rhs = self.compile_expression(rhs_expr, ctx)?;
|
||||
if rhs.flags().contains(ValueFlags::LValue) {
|
||||
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 op {
|
||||
$(
|
||||
$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 op {
|
||||
$(
|
||||
$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] BinaryOp::Add(_) => |a, b| a + b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Sub(_) => |a, b| a - b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Mul(_) => |a, b| a - b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Div(_) => |a, b| a - b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Mod(_) => |a, b| a - b,
|
||||
}
|
||||
int_bin_ops! {
|
||||
const auto
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Eq(_) => |a, b| a == b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Ne(_) => |a, b| a == b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Lt(_) => |a, b| a == b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Gt(_) => |a, b| a == b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Le(_) => |a, b| a == b,
|
||||
[I8, I16, I32, I64, I128, U8, U16, U32, U64, U128, ISize, USize] BinaryOp::Ge(_) => |a, b| a == b,
|
||||
}
|
||||
}
|
||||
|
||||
if match (lhs.ty(), rhs.ty()) {
|
||||
(Type::Int(a_ty), Type::Int(b_ty)) => a_ty == b_ty,
|
||||
_ => false,
|
||||
} {
|
||||
return Ok(match op {
|
||||
BinaryOp::Add(_) => builder.add(lhs, rhs).unwrap(),
|
||||
BinaryOp::Sub(_) => builder.sub(lhs, rhs).unwrap(),
|
||||
BinaryOp::Mul(_) => builder.mul(lhs, rhs).unwrap(),
|
||||
BinaryOp::Div(_) => builder.div(lhs, rhs).unwrap(),
|
||||
BinaryOp::Mod(_) => builder.modulo(lhs, rhs).unwrap(),
|
||||
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::Int(a_ty), Type::Int(b_ty), BinaryOp::Add(_)) if a_ty == b_ty => {
|
||||
Ok(builder.add(lhs, rhs).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Sub(_)) if a_ty == b_ty => {
|
||||
Ok(builder.sub(lhs, rhs).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mul(_)) if a_ty == b_ty => {
|
||||
Ok(builder.mul(lhs, rhs).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Div(_)) if a_ty == b_ty => {
|
||||
Ok(builder.div(lhs, rhs).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mod(_)) if a_ty == b_ty => {
|
||||
Ok(builder.modulo(lhs, rhs).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Eq(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Eq).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ne(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Ne).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Lt(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Gt(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Gt).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Le(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Le).unwrap())
|
||||
}
|
||||
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ge(_)) if a_ty == b_ty => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Ge).unwrap())
|
||||
}
|
||||
(Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty {
|
||||
true => Ok(builder.store(lhs, rhs).unwrap()),
|
||||
false => Err(CompilationError {
|
||||
@@ -294,25 +383,20 @@ impl<'l> Scope<'l> {
|
||||
}),
|
||||
},
|
||||
(src_ty, Type::Type, BinaryOp::Cast(_)) => {
|
||||
let Value::Constant(AnyConst::Type(dst_ty)) = rhs else {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::NotAType,
|
||||
message: "Cannot perform cast.".to_string(),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: expr.range(),
|
||||
},
|
||||
cause: Some(Box::new(CompilationError {
|
||||
kind: Kind::NotAType,
|
||||
message: "Cast target is not a type.".to_string(),
|
||||
let dst_ty =
|
||||
self.assert_ty(rhs, rhs_expr)
|
||||
.map_err(|err| CompilationError {
|
||||
kind: Kind::InvalidCast,
|
||||
message: "Cannot perform cast.".to_string(),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: rhs_expr.range(),
|
||||
range: expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
})),
|
||||
});
|
||||
};
|
||||
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 {
|
||||
@@ -320,6 +404,9 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
todo!("{src_ty} as {dst_ty}");
|
||||
}
|
||||
(Type::Int(_), Type::Ptr(dst_ty)) => {
|
||||
return Ok(builder.int_to_ptr(lhs, dst_ty).unwrap());
|
||||
}
|
||||
_ => todo!("{src_ty} as {dst_ty}"),
|
||||
}
|
||||
}
|
||||
@@ -356,7 +443,7 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
builder.jump(cond_block).unwrap();
|
||||
builder.set_current_block(exit_block);
|
||||
Ok(last_expr.unwrap_or(Value::Constant(AnyConst::Void)))
|
||||
Ok(last_expr.unwrap_or(AnyValue::Constant(AnyConst::Void)))
|
||||
}
|
||||
|
||||
Expr::Call {
|
||||
@@ -364,7 +451,7 @@ impl<'l> Scope<'l> {
|
||||
args: args_exprs,
|
||||
} => {
|
||||
let func = match self.compile_expression(func, ctx)? {
|
||||
Value::Constant(AnyConst::Function(func)) => func,
|
||||
AnyValue::Constant(AnyConst::Function(func)) => func,
|
||||
_ => {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::NotAFunction,
|
||||
@@ -380,7 +467,7 @@ impl<'l> Scope<'l> {
|
||||
let mut args = Vec::with_capacity(args_exprs.len());
|
||||
for expr in args_exprs {
|
||||
let mut arg = self.compile_expression(expr, ctx)?;
|
||||
if arg.flags().contains(ValueFlags::LValue) {
|
||||
if arg.is_lvalue() {
|
||||
arg = ctx.builder.as_mut().unwrap().load(arg).unwrap();
|
||||
}
|
||||
args.push(arg);
|
||||
@@ -390,20 +477,58 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
|
||||
Expr::Type(ty_expr) => match &**ty_expr {
|
||||
ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? {
|
||||
Value::Constant(AnyConst::Type(ty)) => {
|
||||
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into())
|
||||
ast::Type::Struct(ast::Struct { fields }) => {
|
||||
let name = match &ctx.decl_names {
|
||||
Some(NamePattern::Single(func_name)) => func_name.0.as_str(),
|
||||
_ => "",
|
||||
};
|
||||
let struct_ty = self.assembly.create_struct(name);
|
||||
let mut scope = self.clone();
|
||||
let mut expr_ctx = ExpressionContext {
|
||||
builder: None,
|
||||
decl_names: None,
|
||||
fn_queue: ctx.fn_queue,
|
||||
};
|
||||
let ctx = self.assembly.ctx();
|
||||
|
||||
scope.insert(literal_substr!("Self"), struct_ty.as_any_value(), false);
|
||||
let mut field_map = FieldMap::default();
|
||||
for 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(&name.0);
|
||||
field_map.insert(
|
||||
name,
|
||||
Field {
|
||||
name,
|
||||
ty: self.assert_ty(ty, ty_expr)?,
|
||||
public: public.is_some(),
|
||||
mutable: mutable.is_some(),
|
||||
},
|
||||
);
|
||||
}
|
||||
Value::Instruction(inst) if inst.value_flags().contains(ValueFlags::LValue) => {
|
||||
struct_ty.fields.set(field_map).unwrap();
|
||||
Ok(struct_ty.as_any_value())
|
||||
}
|
||||
ast::Type::Ptr { base, mutable } => 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.value_ty()
|
||||
}) = inst.ty()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
if *mutable && !*is_mut {
|
||||
if mutable.is_some() && !*is_mut {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::NotAFunction,
|
||||
message: "Cannot obtain a mutable pointer to an immutable value."
|
||||
@@ -415,27 +540,28 @@ impl<'l> Scope<'l> {
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
let mut flags = inst.value_flags();
|
||||
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));
|
||||
let ptr = Type::Ptr(base.make_ptr(mutable.is_some()));
|
||||
unsafe {
|
||||
Ok(builder
|
||||
.reinterpret(Value::Instruction(inst), ptr, flags)
|
||||
.reinterpret(AnyValue::Instruction(inst), ptr, flags)
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
v => todo!("{v:?}"),
|
||||
},
|
||||
v => todo!("{v:#?}"),
|
||||
},
|
||||
|
||||
Expr::List(expr) => {
|
||||
let mut expr = expr.iter();
|
||||
let mut values = Vec::with_capacity(expr.len());
|
||||
match expr.next() {
|
||||
None => return Ok(Value::Constant(AnyConst::Array(&[]))),
|
||||
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
|
||||
@@ -445,28 +571,14 @@ impl<'l> Scope<'l> {
|
||||
let element_ty = values[0].ty();
|
||||
for expr in expr {
|
||||
let value = self.compile_expression(expr, ctx)?;
|
||||
if value.ty() != element_ty {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::InvalidType,
|
||||
message: format!(
|
||||
"Expected type `{}`, found `{}`",
|
||||
element_ty,
|
||||
value.ty()
|
||||
),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
self.assert_ty_eq(&value, expr, &element_ty)?;
|
||||
values.push(value);
|
||||
}
|
||||
if values.iter().all(|v| matches!(v, Value::Constant(_))) {
|
||||
if values.iter().all(|v| matches!(v, AnyValue::Constant(_))) {
|
||||
let alloc = self.assembly.ctx().alloc();
|
||||
return Ok(Value::Constant(AnyConst::Array(alloc.alloc_slice(
|
||||
return Ok(AnyValue::Constant(AnyConst::Array(alloc.alloc_slice(
|
||||
values.into_iter().map(|v| match v {
|
||||
Value::Constant(c) => c,
|
||||
AnyValue::Constant(c) => c,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
))));
|
||||
@@ -480,7 +592,7 @@ impl<'l> Scope<'l> {
|
||||
let mut index = self.compile_expression(index, ctx)?;
|
||||
let builder = ctx.builder.as_mut().unwrap();
|
||||
|
||||
if index.flags().contains(ValueFlags::LValue) {
|
||||
if index.is_lvalue() {
|
||||
index = builder.load(index).unwrap();
|
||||
}
|
||||
|
||||
@@ -497,48 +609,111 @@ impl<'l> Scope<'l> {
|
||||
});
|
||||
}
|
||||
|
||||
if value.flags().contains(ValueFlags::LValue) {
|
||||
let gep = builder.gep(value, index).unwrap();
|
||||
if value.is_lvalue() {
|
||||
let gep = builder.get_element_ptr(value, index).unwrap();
|
||||
return Ok(gep);
|
||||
}
|
||||
|
||||
todo!("{:#?}", value.ty());
|
||||
}
|
||||
|
||||
Expr::Struct(ctor) => {
|
||||
let ty = self.compile_expression(&ctor.r#type, ctx)?;
|
||||
let AnyValue::Constant(AnyConst::Type(Type::Struct(
|
||||
struct_ty @ StructT { name, fields, .. },
|
||||
))) = ty
|
||||
else {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::NotAStruct,
|
||||
message: format!("Expected struct type, got value of type `{}`.", ty.ty()),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: ctor.r#type.range(),
|
||||
},
|
||||
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.get(*fld_name) else {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::UninitializedField,
|
||||
message: format!("Uninitialized field `{fld_name}`."),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
};
|
||||
let value = self.compile_expression(&name_value_pair.value, 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: &ast::Function,
|
||||
ast: &Arc<ast::Function>,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<&'l Function<'l>, CompilationError> {
|
||||
let ret_ty = match ast.ret.as_ref() {
|
||||
None => Value::Constant(AnyConst::Type(Type::Void)),
|
||||
None => AnyValue::Constant(AnyConst::Type(Type::Void)),
|
||||
Some(ty) => self.compile_expression(ty, ctx)?,
|
||||
};
|
||||
let ret_ty = self.assert_type(ret_ty)?;
|
||||
let ast_as_expr = 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.r#type, ctx)?;
|
||||
let ty = self.assert_type(ty)?;
|
||||
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 func = self.assembly.create_function(fn_ty);
|
||||
if let Some(NamePattern::Single(func_name)) = &ctx.decl_names {
|
||||
func.name.set(func.ctx().intern_str(&func_name.0)).unwrap();
|
||||
let name = match &ctx.decl_names {
|
||||
Some(NamePattern::Single(func_name)) => func_name.0.as_str(),
|
||||
_ => "",
|
||||
};
|
||||
|
||||
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() {
|
||||
scope.insert(arg.name.0.clone(), Value::Parameter(i, func), false);
|
||||
scope.insert(arg.name.0.clone(), AnyValue::Parameter(i, func), false);
|
||||
}
|
||||
|
||||
ctx.fn_queue.push_back((func, block.clone(), scope));
|
||||
@@ -552,7 +727,7 @@ impl<'l> Scope<'l> {
|
||||
value: &Expr,
|
||||
mutable: bool,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<Value<'l>, CompilationError> {
|
||||
) -> Result<AnyValue<'l>, CompilationError> {
|
||||
let mut sub_ctx = ExpressionContext {
|
||||
decl_names: Some(names),
|
||||
builder: ctx.builder.as_deref_mut(),
|
||||
@@ -581,15 +756,45 @@ impl<'l> Scope<'l> {
|
||||
Ok(AnyConst::Void.into())
|
||||
}
|
||||
|
||||
fn assert_type(&self, val: Value<'l>) -> Result<Type<'l>, CompilationError> {
|
||||
fn assert_ty(
|
||||
&self,
|
||||
val: AnyValue<'l>,
|
||||
value_expr: &Expr,
|
||||
) -> Result<Type<'l>, CompilationError> {
|
||||
match val {
|
||||
Value::Constant(AnyConst::Type(ty)) => Ok(ty),
|
||||
AnyValue::Constant(AnyConst::Type(ty)) => Ok(ty),
|
||||
_ => Err(CompilationError {
|
||||
kind: Kind::NotAType,
|
||||
message: "Value is not a type.".to_string(),
|
||||
location: Location::None,
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: value_expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_ty_eq(
|
||||
&self,
|
||||
value: &AnyValue<'l>,
|
||||
value_expr: &Expr,
|
||||
expected: &Type<'l>,
|
||||
) -> Result<Type<'l>, CompilationError> {
|
||||
let value_ty = value.ty();
|
||||
match value_ty == *expected {
|
||||
true => Ok(value_ty),
|
||||
false => {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::InvalidType,
|
||||
message: format!("Expected value of type `{expected}`, found `{value_ty}`."),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: value_expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user