diff --git a/crates/aiken-lang/src/gen_uplc2.rs b/crates/aiken-lang/src/gen_uplc2.rs index d8ceedf4..b059f302 100644 --- a/crates/aiken-lang/src/gen_uplc2.rs +++ b/crates/aiken-lang/src/gen_uplc2.rs @@ -1217,7 +1217,34 @@ impl<'a> CodeGenerator<'a> { defined_tails, } => { let Pattern::List { elements, .. } = &clause.pattern - else { unreachable!() }; + else { + let mut next_clause_props = ClauseProperties { + clause_var_name: props.clause_var_name.clone(), + complex_clause: false, + needs_constr_var: false, + original_subject_name: props.original_subject_name.clone(), + final_clause: false, + specific_clause: SpecificClause::ListClause { + current_index: *current_index, + defined_tails: defined_tails.clone(), + }, + }; + + let (_, clause_assign) = + self.clause_pattern(&clause.pattern, subject_tipo, props); + + let clause_assign_hoisted = clause_assign.hoist_over(clause_then); + + return AirTree::wrap_clause( + clause_assign_hoisted, + self.handle_each_clause( + rest_clauses, + final_clause, + subject_tipo, + &mut next_clause_props, + ), + ); + }; let tail_name = if *current_index == 0 { props.original_subject_name.clone() @@ -1255,7 +1282,7 @@ impl<'a> CodeGenerator<'a> { let mut use_wrap_clause = false; - if elements.len() > *current_index as usize { + if elements.len() >= *current_index as usize { *current_index += 1; defined_tails.push(tail_name.clone()); } else if next_tail_name.is_none() { @@ -1396,8 +1423,13 @@ impl<'a> CodeGenerator<'a> { elem_name.clone(), ); - let statement = - self.nested_clause_condition(elem, list_elem_type, &mut elem_props); + let statement = self.nested_clause_condition( + elem, + AirTree::local_var(&elem_name, list_elem_type.clone()), + list_elem_type, + &mut elem_props, + ); + *complex_clause = *complex_clause || elem_props.complex_clause; (tail, elem_name, statement) @@ -1419,7 +1451,7 @@ impl<'a> CodeGenerator<'a> { tail.iter().for_each(|elem| { let tail = defined_tails .last() - .unwrap_or_else(|| panic!("WHERE IS ME TAIL???")); + .unwrap_or_else(|| panic!("WHERE IS THE TAIL???")); let elem_name = match elem.as_ref() { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), @@ -1440,8 +1472,12 @@ impl<'a> CodeGenerator<'a> { specific_clause: props.specific_clause.clone(), }; - let statement = - self.nested_clause_condition(elem, subject_tipo, &mut elem_props); + let statement = self.nested_clause_condition( + elem, + AirTree::local_var(&elem_name, subject_tipo.clone()), + subject_tipo, + &mut elem_props, + ); *complex_clause = *complex_clause || elem_props.complex_clause; air_elems.push(statement); @@ -1542,6 +1578,7 @@ impl<'a> CodeGenerator<'a> { let statement = self.nested_clause_condition( &arg.value, + AirTree::local_var(&field_name, arg_type.clone()), arg_type, &mut field_props, ); @@ -1585,9 +1622,37 @@ impl<'a> CodeGenerator<'a> { fn nested_clause_condition( &mut self, pattern: &Pattern>, + value: AirTree, subject_tipo: &Arc, props: &mut ClauseProperties, ) -> AirTree { - todo!() + match pattern { + Pattern::Int { value, .. } => { + todo!(); + } + Pattern::Var { location, name } => todo!(), + Pattern::Assign { + name, + location, + pattern, + } => todo!(), + Pattern::Discard { name, location } => todo!(), + Pattern::List { + location, + elements, + tail, + } => todo!(), + Pattern::Constructor { + is_record, + location, + name, + arguments, + module, + constructor, + with_spread, + tipo, + } => todo!(), + Pattern::Tuple { location, elems } => todo!(), + } } } diff --git a/crates/aiken-lang/src/gen_uplc2/tree.rs b/crates/aiken-lang/src/gen_uplc2/tree.rs index 2c28bb06..da50f8ce 100644 --- a/crates/aiken-lang/src/gen_uplc2/tree.rs +++ b/crates/aiken-lang/src/gen_uplc2/tree.rs @@ -45,6 +45,20 @@ pub enum AirStatement { is_true: bool, value: Box, }, + // Clause Guards + ClauseGuard { + subject_name: String, + tipo: Arc, + pattern: Box, + then: Box, + }, + ListClauseGuard { + tipo: Arc, + tail_name: String, + next_tail_name: Option, + inverse: bool, + then: Box, + }, // Field Access FieldsExpose { indices: Vec<(usize, String, Arc)>, @@ -180,19 +194,7 @@ pub enum AirExpression { then: Box, otherwise: Box, }, - ClauseGuard { - subject_name: String, - tipo: Arc, - pattern: Box, - then: Box, - }, - ListClauseGuard { - tipo: Arc, - tail_name: String, - next_tail_name: Option, - inverse: bool, - then: Box, - }, + Finally { pattern: Box, then: Box, @@ -479,12 +481,15 @@ impl AirTree { tipo: Arc, then: AirTree, ) -> AirTree { - AirTree::Expression(AirExpression::ClauseGuard { - subject_name: subject_name.to_string(), - tipo, - pattern: pattern.into(), - then: then.into(), - }) + AirTree::Statement { + statement: AirStatement::ClauseGuard { + subject_name: subject_name.to_string(), + tipo, + pattern: pattern.into(), + then: then.into(), + }, + hoisted_over: None, + } } pub fn list_clause_guard( tail_name: impl ToString, @@ -493,13 +498,16 @@ impl AirTree { then: AirTree, next_tail_name: Option, ) -> AirTree { - AirTree::Expression(AirExpression::ListClauseGuard { - tipo, - tail_name: tail_name.to_string(), - next_tail_name, - inverse, - then: then.into(), - }) + AirTree::Statement { + statement: AirStatement::ListClauseGuard { + tipo, + tail_name: tail_name.to_string(), + next_tail_name, + inverse, + then: then.into(), + }, + hoisted_over: None, + } } pub fn finally(pattern: AirTree, then: AirTree) -> AirTree { AirTree::Expression(AirExpression::Finally { @@ -768,6 +776,36 @@ impl AirTree { air_vec.push(Air::AssertBool { is_true: *is_true }); value.create_air_vec(air_vec); } + AirStatement::ClauseGuard { + subject_name, + tipo, + pattern, + then, + } => { + air_vec.push(Air::ClauseGuard { + subject_name: subject_name.clone(), + tipo: tipo.clone(), + }); + + pattern.create_air_vec(air_vec); + then.create_air_vec(air_vec); + } + AirStatement::ListClauseGuard { + tipo, + tail_name, + next_tail_name, + inverse, + then, + } => { + air_vec.push(Air::ListClauseGuard { + tipo: tipo.clone(), + tail_name: tail_name.clone(), + next_tail_name: next_tail_name.clone(), + inverse: *inverse, + }); + + then.create_air_vec(air_vec); + } AirStatement::FieldsExpose { indices, check_last_item, @@ -991,36 +1029,6 @@ impl AirTree { then.create_air_vec(air_vec); otherwise.create_air_vec(air_vec); } - AirExpression::ClauseGuard { - subject_name, - tipo, - pattern, - then, - } => { - air_vec.push(Air::ClauseGuard { - subject_name: subject_name.clone(), - tipo: tipo.clone(), - }); - - pattern.create_air_vec(air_vec); - then.create_air_vec(air_vec); - } - AirExpression::ListClauseGuard { - tipo, - tail_name, - next_tail_name, - inverse, - then, - } => { - air_vec.push(Air::ListClauseGuard { - tipo: tipo.clone(), - tail_name: tail_name.clone(), - next_tail_name: next_tail_name.clone(), - inverse: *inverse, - }); - - then.create_air_vec(air_vec); - } AirExpression::Finally { pattern, then } => { air_vec.push(Air::Finally); pattern.create_air_vec(air_vec); diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index a390b446..57dbd9ba 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -419,6 +419,103 @@ fn acceptance_test_4_concat_no_anon_func() { ); } +#[test] +fn acceptance_test_5_direct_head() { + let src = r#" + use aiken/builtin.{head_list} + + test head_1() { + let head = fn(xs){ + when xs is { + [] -> None + _ -> Some(head_list(xs)) + } + } + + head([1, 2, 3]) == Some(1) + } + "#; + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::var("head") + .lambda("head") + .apply( + Term::var("xs") + .delayed_choose_list( + Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), + Term::constr_data().apply(Term::integer(0.into())).apply( + Term::mk_cons() + .apply(Term::head_list().apply(Term::var("xs"))) + .apply(Term::empty_list()), + ), + ) + .lambda("xs"), + ) + .apply(Term::list_values(vec![ + Constant::Data(Data::integer(1.into())), + Constant::Data(Data::integer(2.into())), + Constant::Data(Data::integer(3.into())), + ])), + ) + .apply(Term::Constant( + Constant::Data(Data::constr(0, vec![Data::integer(1.into())])).into(), + )), + false, + ); +} + +#[test] +fn acceptance_test_5_direct_2_heads() { + let src = r#" + use aiken/builtin.{head_list} + + test head_2() { + let head = fn(xs: List){ + when xs is { + [] -> None + [a] -> Some(xs) + [a, b, ..c] -> Some([a,b]) + } + } + + head([1, 2, 3]) == Some([1, 2]) + } + "#; + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::var("head") + .lambda("head") + .apply( + Term::var("xs") + .delayed_choose_list( + Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), + Term::constr_data().apply(Term::integer(0.into())).apply( + Term::mk_cons() + .apply(Term::head_list().apply(Term::var("xs"))) + .apply(Term::empty_list()), + ), + ) + .lambda("xs"), + ) + .apply(Term::list_values(vec![ + Constant::Data(Data::integer(1.into())), + Constant::Data(Data::integer(2.into())), + Constant::Data(Data::integer(3.into())), + ])), + ) + .apply(Term::Constant( + Constant::Data(Data::constr(0, vec![Data::integer(1.into())])).into(), + )), + false, + ); +} + #[test] fn acceptance_test_5_head_not_empty() { let src = r#"