From e7037cfd334773771e927af34ec0acf19412ee2a Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Sat, 2 Mar 2024 10:20:33 +0100 Subject: [PATCH] feat: add casts --- lib/edlang_ast/src/lib.rs | 1 + lib/edlang_codegen_llvm/src/codegen.rs | 97 ++++++++++++++++++++++++++ lib/edlang_ir/src/lib.rs | 9 +++ lib/edlang_lowering/src/lib.rs | 40 ++++++++++- lib/edlang_parser/src/grammar.lalrpop | 6 +- lib/edlang_parser/src/tokens.rs | 2 + 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/lib/edlang_ast/src/lib.rs b/lib/edlang_ast/src/lib.rs index a293ae8d5..ef92d59e3 100644 --- a/lib/edlang_ast/src/lib.rs +++ b/lib/edlang_ast/src/lib.rs @@ -176,6 +176,7 @@ pub enum Expression { Binary(Box, BinaryOp, Box), Deref(Box, Span), AsRef(Box, bool, Span), + Cast(PathExpr, Type, Span), } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/lib/edlang_codegen_llvm/src/codegen.rs b/lib/edlang_codegen_llvm/src/codegen.rs index d41f52491..6699f428b 100644 --- a/lib/edlang_codegen_llvm/src/codegen.rs +++ b/lib/edlang_codegen_llvm/src/codegen.rs @@ -1020,6 +1020,103 @@ fn compile_rvalue<'ctx>( ); compile_unary_op(ctx, fn_id, locals, *op, value)? } + ir::RValue::Cast(place, target_ty, span) => { + ctx.set_debug_loc( + ctx.builder + .get_current_debug_location() + .unwrap() + .get_scope(), + *span, + ); + + let target_ty = target_ty.clone(); + let target_llvm_ty = compile_basic_type(ctx, &target_ty); + let (value, ty) = compile_load_place(ctx, fn_id, locals, place, false)?; + let current_ty = compile_basic_type(ctx, &ty); + + if target_llvm_ty.is_pointer_type() { + // int to ptr + let target_llvm_ty = target_llvm_ty.into_pointer_type(); + if current_ty.is_int_type() { + let value = + ctx.builder + .build_int_to_ptr(value.into_int_value(), target_llvm_ty, "")?; + (value.as_basic_value_enum(), target_ty) + } else if current_ty.is_pointer_type() { + (value, target_ty.clone()) + } else { + unreachable!("cast from {:?} to ptr", current_ty) + } + } else if target_llvm_ty.is_int_type() { + let is_signed = target_ty.kind.is_signed_integer(); + let target_llvm_ty = target_llvm_ty.into_int_type(); + if current_ty.is_int_type() { + // int to int casts + let current_ty = current_ty.into_int_type(); + + match current_ty + .get_bit_width() + .cmp(&target_llvm_ty.get_bit_width()) + { + std::cmp::Ordering::Greater => { + let value = ctx.builder.build_int_truncate( + value.into_int_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } + std::cmp::Ordering::Equal => (value, target_ty.clone()), + std::cmp::Ordering::Less => { + if is_signed { + let value = ctx.builder.build_int_s_extend( + value.into_int_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } else { + let value = ctx.builder.build_int_z_extend( + value.into_int_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } + } + } + } else if current_ty.is_float_type() { + // float to int casts + if is_signed { + let value = ctx.builder.build_float_to_signed_int( + value.into_float_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } else { + let value = ctx.builder.build_float_to_unsigned_int( + value.into_float_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } + } else if current_ty.is_pointer_type() { + // ptr to int + let value = ctx.builder.build_ptr_to_int( + value.into_pointer_value(), + target_llvm_ty, + "", + )?; + (value.as_basic_value_enum(), target_ty) + } else { + todo!("cast {:?} to int", current_ty) + } + } else { + todo!("cast from {:?} to {:?}", current_ty, target_llvm_ty) + } + } }) } diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 59476ab37..78d56bd22 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -259,6 +259,14 @@ impl TypeKind { matches!(self, Self::Int(_) | Self::Uint(_)) } + pub const fn is_signed_integer(&self) -> bool { + matches!(self, Self::Int(_)) + } + + pub const fn is_float(&self) -> bool { + matches!(self, Self::Float(_)) + } + pub fn get_falsy_value(&self) -> ValueTree { match self { Self::Bool => ValueTree::Leaf(ConstValue::Bool(false)), @@ -410,6 +418,7 @@ pub enum RValue { BinOp(BinOp, Operand, Operand, Span), LogicOp(LogicalOp, Operand, Operand, Span), UnOp(UnOp, Operand, Span), + Cast(Place, TypeInfo, Span), } #[derive(Debug, Clone)] diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index aff8150f0..b212c495f 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -516,15 +516,35 @@ fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) -> Result<(), fn find_expr_type(builder: &mut BodyBuilder, info: &ast::Expression) -> Option { Some(match info { - ast::Expression::Value(x) => match x { + ast::Expression::Value(val) => match val { ast::ValueExpr::Bool { .. } => TypeKind::Bool, ast::ValueExpr::Char { .. } => TypeKind::Char, ast::ValueExpr::Int { .. } => return None, ast::ValueExpr::Float { .. } => return None, ast::ValueExpr::Str { .. } => todo!(), ast::ValueExpr::Path(path) => { - // todo: handle full path - builder.get_local(&path.first.name)?.ty.kind.clone() + let local = builder.get_local(&path.first.name)?; + let mut ty = local.ty.kind.clone(); + + for seg in &path.extra { + match seg { + ast::PathSegment::Field(field_name) => { + ty = match ty { + TypeKind::Struct(id, _struct_name) => { + let body = builder.ctx.body.structs.get(&id)?; + body.variants[*body.name_to_idx.get(&field_name.name)?] + .ty + .kind + .clone() + } + _ => unreachable!(), + } + } + ast::PathSegment::Index { .. } => todo!(), + } + } + + ty } }, ast::Expression::FnCall(info) => { @@ -569,6 +589,11 @@ fn find_expr_type(builder: &mut BodyBuilder, info: &ast::Expression) -> Option { + // checks? + + todo!() + } }) } @@ -705,6 +730,15 @@ fn lower_expr( (RValue::Use(Operand::Move(place), info.span), ty, info.span) } + ast::Expression::Cast(path, cast_ty, span) => { + let (place, _ty, _path_span) = lower_path(builder, path)?; + let new_ty = lower_type(&builder.ctx, cast_ty, builder.local_module)?; + let kind = new_ty.kind.clone(); + + // todo: some checks? + + (RValue::Cast(place, new_ty, *span), kind, *span) + } }) } diff --git a/lib/edlang_parser/src/grammar.lalrpop b/lib/edlang_parser/src/grammar.lalrpop index af8f15ca1..c46bec31e 100644 --- a/lib/edlang_parser/src/grammar.lalrpop +++ b/lib/edlang_parser/src/grammar.lalrpop @@ -27,6 +27,7 @@ extern { "use" => Token::KeywordUse, "in" => Token::KeywordIn, "extern" => Token::KeywordExtern, + "as" => Token::KeywordAs, // literals "identifier" => Token::Identifier(), @@ -308,7 +309,6 @@ pub(crate) Term: ast::Expression = { #[precedence(level="0")] => ast::Expression::Value(<>), => ast::Expression::FnCall(<>), - "(" ")" => ast::Expression::StructInit(<>), #[precedence(level="2")] #[assoc(side="left")] "(" ")", } @@ -324,7 +324,6 @@ pub(crate) Expression: ast::Expression = { op, Box::new(rhs) ), - #[precedence(level="2")] #[assoc(side="left")] => ast::Expression::Binary( Box::new(lhs), @@ -343,6 +342,9 @@ pub(crate) Expression: ast::Expression = { op, Box::new(rhs) ), + #[precedence(level="5")] #[assoc(side="left")] + "as" => ast::Expression::Cast(a, b, ast::Span::new(lo, hi)), + "(" ")" => ast::Expression::StructInit(<>), } pub BinaryFirstLvlOp: ast::BinaryOp = { diff --git a/lib/edlang_parser/src/tokens.rs b/lib/edlang_parser/src/tokens.rs index aaff73247..d7caa54f8 100644 --- a/lib/edlang_parser/src/tokens.rs +++ b/lib/edlang_parser/src/tokens.rs @@ -55,6 +55,8 @@ pub enum Token { KeywordIn, #[token("extern")] KeywordExtern, + #[token("as")] + KeywordAs, // Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/ #[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())]