use std::ops::Deref; use ecow::{EcoString, eco_format}; use im::HashSet; use num_bigint::BigInt; use vec1::Vec1; use crate::{ast, exhaustiveness, parse, type_}; #[derive(Debug, Default)] pub struct Module { functions: Vec, } #[derive(Debug)] pub struct Function { pub name: EcoString, pub return_type: Type, pub parameters: Vec, pub body: Expression, } #[derive(Debug)] pub struct FunctionParameter { pub type_: Type, pub name: Option, } #[derive(Debug, Default)] pub struct Translator { module: Module, } #[derive(Debug, Clone, Copy)] pub enum Type { Int, Float, Bool, Generic, } #[derive(Debug)] pub enum Expression { Block(Vec), Var { name: EcoString, }, Int { value: BigInt, }, Equals { lhs: Box, rhs: Box, }, IntAdd { lhs: Box, rhs: Box, }, Set { name: EcoString, value: Box, }, If { cond: Box, then: Box, else_: Box, }, } impl Translator { pub fn translate(mut self, module: &ast::TypedModule) -> Module { for definition in &module.definitions { match definition { ast::Definition::Function(function) => { self.module .functions .push(Self::translate_function(function)); } ast::Definition::TypeAlias(_type_alias) => todo!(), ast::Definition::CustomType(_custom_type) => todo!(), ast::Definition::Import(_import) => todo!(), ast::Definition::ModuleConstant(_module_constant) => todo!(), } } self.module } fn translate_function(function: &ast::TypedFunction) -> Function { let (_, name) = function.name.clone().expect("function should have a name"); let return_type = Self::translate_type(&function.return_type); let parameters = Iterator::collect(function.arguments.iter().map(|argument| { let name = argument.get_variable_name().cloned(); let type_ = Self::translate_type(&argument.type_); FunctionParameter { name, type_ } })); let body = BodyTranslator::default().translate(&function.body); Function { name, body, return_type, parameters, } } fn translate_type(type_: &type_::Type) -> Type { match type_ { type_::Type::Named { publicity: _, package: _, module, name, arguments: _, inferred_variant: _, } => match (module.as_str(), name.as_str()) { ("gleam", "Int") => Type::Int, ("gleam", "Float") => Type::Float, ("gleam", "Bool") => Type::Bool, (_, _) => todo!(), }, type_::Type::Fn { arguments: _, return_: _, } => todo!(), type_::Type::Var { type_ } => match type_.borrow().deref() { type_::TypeVar::Link { type_ } => Self::translate_type(type_), type_::TypeVar::Unbound { id: _ } | type_::TypeVar::Generic { id: _ } => { Type::Generic } }, type_::Type::Tuple { elements: _ } => todo!(), } } } #[derive(Debug, Default)] pub struct BodyTranslator { variables: HashSet, variable_count: usize, } impl BodyTranslator { fn translate(mut self, body: &Vec1) -> Expression { Expression::Block(Iterator::collect( body.iter().map(|stmt| self.translate_statement(stmt)), )) } fn translate_statement(&mut self, statement: &ast::TypedStatement) -> Expression { match statement { ast::Statement::Expression(expression) => self.translate_expression(expression), ast::Statement::Assignment(assignment) => self.translate_assignment(assignment), ast::Statement::Use(_) => todo!(), ast::Statement::Assert(_) => todo!(), } } fn translate_expression(&mut self, expression: &ast::TypedExpr) -> Expression { match expression { ast::TypedExpr::Int { location: _, type_: _, value: _, int_value, } => Expression::Int { value: int_value.clone(), }, ast::TypedExpr::Float { location: _, type_: _, value: _, } => todo!(), ast::TypedExpr::String { location: _, type_: _, value: _, } => todo!(), ast::TypedExpr::Block { location: _, statements: _, } => todo!(), ast::TypedExpr::Pipeline { location: _, first_value: _, assignments: _, finally: _, finally_kind: _, } => todo!(), ast::TypedExpr::Var { location: _, constructor: _, name, } => Expression::Var { name: name.clone() }, ast::TypedExpr::Fn { location: _, type_: _, kind: _, arguments: _, body: _, return_annotation: _, purity: _, } => todo!(), ast::TypedExpr::List { location: _, type_: _, elements: _, tail: _, } => todo!(), ast::TypedExpr::Call { location: _, type_: _, fun: _, arguments: _, } => todo!(), ast::TypedExpr::BinOp { location: _, type_: _, name, name_location: _, left, right, } => { let lhs = Box::new(self.translate_expression(left)); let rhs = Box::new(self.translate_expression(right)); match name { ast::BinOp::And => todo!(), ast::BinOp::Or => todo!(), ast::BinOp::Eq => Expression::Equals { lhs, rhs }, ast::BinOp::NotEq => todo!(), ast::BinOp::LtInt => todo!(), ast::BinOp::LtEqInt => todo!(), ast::BinOp::LtFloat => todo!(), ast::BinOp::LtEqFloat => todo!(), ast::BinOp::GtEqInt => todo!(), ast::BinOp::GtInt => todo!(), ast::BinOp::GtEqFloat => todo!(), ast::BinOp::GtFloat => todo!(), ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs }, ast::BinOp::AddFloat => todo!(), ast::BinOp::SubInt => todo!(), ast::BinOp::SubFloat => todo!(), ast::BinOp::MultInt => todo!(), ast::BinOp::MultFloat => todo!(), ast::BinOp::DivInt => todo!(), ast::BinOp::DivFloat => todo!(), ast::BinOp::RemainderInt => todo!(), ast::BinOp::Concatenate => todo!(), } } ast::TypedExpr::Case { location: _, type_: _, subjects, clauses, compiled_case, } => { let mut block = vec![]; let subject_vars: Vec<_> = Iterator::collect(subjects.iter().map(|subject| { let name = self.make_tmp_var(); let value = self.translate_expression(subject); block.push(Expression::Set { name: name.clone(), value: Box::new(value), }); return name; })); block.push(self.translate_decision(&subject_vars, clauses, &compiled_case.tree)); Expression::Block(block) } ast::TypedExpr::RecordAccess { location: _, field_start: _, type_: _, label: _, index: _, record: _, } => todo!(), ast::TypedExpr::ModuleSelect { location: _, field_start: _, type_: _, label: _, module_name: _, module_alias: _, constructor: _, } => todo!(), ast::TypedExpr::Tuple { location: _, type_: _, elements: _, } => todo!(), ast::TypedExpr::TupleIndex { location: _, type_: _, index: _, tuple: _, } => todo!(), ast::TypedExpr::Todo { location: _, message: _, kind: _, type_: _, } => todo!(), ast::TypedExpr::Panic { location: _, message: _, type_: _, } => todo!(), ast::TypedExpr::Echo { location: _, type_: _, expression: _, message: _, } => todo!(), ast::TypedExpr::BitArray { location: _, type_: _, segments: _, } => todo!(), ast::TypedExpr::RecordUpdate { location: _, type_: _, record_assignment: _, constructor: _, arguments: _, } => todo!(), ast::TypedExpr::NegateBool { location: _, value: _, } => todo!(), ast::TypedExpr::NegateInt { location: _, value: _, } => todo!(), ast::TypedExpr::Invalid { location: _, type_: _, } => todo!(), } } fn translate_assignment(&mut self, assignment: &ast::TypedAssignment) -> Expression { dbg!(&assignment.compiled_case); todo!() } fn make_tmp_var(&mut self) -> EcoString { let variable_count = self.variable_count; self.make_var(eco_format!("_tmp{variable_count}")) } fn make_var(&mut self, name: EcoString) -> EcoString { self.variable_count += 1; _ = self.variables.insert(name.clone()); name } fn translate_decision( &mut self, subjects: &[EcoString], clauses: &[ast::TypedClause], decision: &exhaustiveness::Decision, ) -> Expression { match decision { exhaustiveness::Decision::Run { body } => { let mut block = vec![]; for (binding, value) in &body.bindings { block.push(self.translate_binding(subjects, binding.clone(), value)); } block.push(self.translate_expression(&clauses[body.clause_index].then)); Expression::Block(block) } exhaustiveness::Decision::Guard { guard: _, if_true: _, if_false: _, } => { todo!() } exhaustiveness::Decision::Switch { var, choices, fallback, fallback_check: _, } => { let fallback = self.translate_decision(subjects, clauses, fallback); choices .iter() .rfold(fallback, |fallback, (check, then)| Expression::If { cond: Box::new(self.translate_runtime_check( check, Expression::Var { name: subjects[var.id].clone(), }, )), then: Box::new(self.translate_decision(subjects, clauses, then)), else_: Box::new(fallback), }) } exhaustiveness::Decision::Fail => todo!(), } } fn translate_binding( &mut self, subjects: &[EcoString], binding: EcoString, value: &exhaustiveness::BoundValue, ) -> Expression { let value = match value { exhaustiveness::BoundValue::Variable(variable) => Expression::Var { name: subjects[variable.id].clone(), }, exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(), exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int { value: big_int.clone(), }, exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(), exhaustiveness::BoundValue::BitArraySlice { bit_array: _, read_action: _, } => todo!(), }; Expression::Set { name: binding, value: Box::new(value), } } fn translate_runtime_check( &mut self, check: &exhaustiveness::RuntimeCheck, against: Expression, ) -> Expression { match check { exhaustiveness::RuntimeCheck::Int { value } => { let value = parse::parse_int_value(&value).expect("unable to parse integer value"); Expression::Equals { lhs: Box::new(against), rhs: Box::new(Expression::Int { value }), } } exhaustiveness::RuntimeCheck::Float { value: _ } => todo!(), exhaustiveness::RuntimeCheck::String { value: _ } => todo!(), exhaustiveness::RuntimeCheck::StringPrefix { prefix: _, rest: _ } => todo!(), exhaustiveness::RuntimeCheck::Tuple { size: _, elements: _, } => todo!(), exhaustiveness::RuntimeCheck::BitArray { test: _ } => todo!(), exhaustiveness::RuntimeCheck::Variant { match_: _, index: _, labels: _, fields: _, } => todo!(), exhaustiveness::RuntimeCheck::NonEmptyList { first: _, rest: _ } => todo!(), exhaustiveness::RuntimeCheck::EmptyList => todo!(), } } }