lemonlang 0.0.4

an experimental, modern, purely safe, programming language.
use crate::ast::{self};

use super::{
	diags::SyntaxErr,
	types::{Type, TypeId},
	Checker, TyResult,
};

impl Checker<'_> {
	pub fn check_assign_expr(&mut self, assign_expr: &mut ast::AssignExpr) -> TyResult<TypeId> {
		let found = self.check_expr(&mut assign_expr.right)?;
		let expected = self.assign_left_expr(&mut assign_expr.left, found)?;
		assign_expr.set_type_id(expected);
		Ok(TypeId::UNIT)
	}

	fn assign_left_expr(&mut self, expr: &mut ast::Expr, found_id: TypeId) -> TyResult<TypeId> {
		match expr {
			ast::Expr::Ident(ident) => self.assign_ident_expr(ident, found_id),
			ast::Expr::Deref(deref) => self.assign_deref_expr(deref, found_id),
			ast::Expr::Member(member) => self.assign_member_expr(member, found_id),
			_ => Err(SyntaxErr::left_hand_cannot_be_assigned(expr.get_range())),
		}
	}

	fn assign_ident_expr(&mut self, ident: &mut ast::Ident, found: TypeId) -> TyResult<TypeId> {
		let lexeme = ident.lexeme();
		if let Some(value) = self.ctx.get_value(lexeme) {
			if !value.is_mutable() {
				return Err(SyntaxErr::cannot_assign_immutable(lexeme, ident.get_range()));
			}
			let found = self.infer_type(value.type_id, found)?;
			self.equal_type_expected(value.type_id, found, ident.get_range())?;
			return Ok(value.type_id);
		}
		Err(SyntaxErr::not_found_value(lexeme, ident.get_range()))
	}

	fn assign_deref_expr(&mut self, deref: &mut ast::DerefExpr, found: TypeId) -> TyResult<TypeId> {
		let expected = self.check_deref_expr(deref)?;

		let (name, mutable) = self.try_mutate_expr(&deref.expr)?;
		if !mutable {
			return Err(SyntaxErr::cannot_assign_immutable(&name, deref.get_range()));
		}

		let found = self.infer_type(expected, found)?;
		self.equal_type_expected(expected, found, deref.get_range())?;
		Ok(expected)
	}

	fn assign_member_expr(
		&mut self,
		member: &mut ast::MemberExpr,
		found: TypeId,
	) -> TyResult<TypeId> {
		let self_type = self.check_expr(&mut member.left)?;
		let self_type = self.get_stored_type(self_type);
		if let Type::Struct(struct_type) = self_type {
			let lexeme = member.method.lexeme();
			let field = struct_type.get_field(lexeme);
			if let Some(field) = field {
				let (name, mutable) = self.try_mutate_expr(&member.left)?;
				if !mutable {
					return Err(SyntaxErr::cannot_assign_immutable(&name, member.get_range()));
				}
				let found = self.infer_type(field.type_id, found)?;
				member.method.set_type_id(found);
				self.equal_type_expected(field.type_id, found, member.get_range())?;
				return Ok(field.type_id);
			}

			let method = member.method.lexeme().to_owned();
			let found = self.display_type_value(self_type);
			return Err(SyntaxErr::not_found_method_named(method, found, member.get_range()));
		}
		todo!("assign member expr {:?}", self_type)
	}

	pub fn try_mutate_expr(&self, expr: &ast::Expr) -> TyResult<(String, bool)> {
		match expr {
			ast::Expr::Ident(ident) => self.try_mutate_ident_expr(ident),
			ast::Expr::Member(member) => self.try_mutate_expr(&member.left),
			ast::Expr::Deref(deref) => self.try_mutate_expr(&deref.expr),
			ast::Expr::Assign(assign) => self.try_mutate_expr(&assign.right),
			_ => todo!("code {:?}", expr),
		}
	}

	fn try_mutate_ident_expr(&self, ident: &ast::Ident) -> TyResult<(String, bool)> {
		let lexeme = ident.lexeme();
		if let Some(value) = self.ctx.get_value(lexeme) {
			return Ok((lexeme.to_owned(), value.is_mutable()));
		}
		Err(SyntaxErr::not_found_value(lexeme, ident.get_range()))
	}
}