From 33a3c5dc137b195e428b99b9d2ca73af6473b909 Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 22 Mar 2023 22:33:49 -0400 Subject: [PATCH] feat(gen_uplc): introduce scope new type * new module scope which holds some ancestor logic * rework some things to truly hide scope increments Co-authored-by: Kasey White --- crates/aiken-lang/src/gen_uplc.rs | 447 ++++++++-------------- crates/aiken-lang/src/gen_uplc/air.rs | 82 ++-- crates/aiken-lang/src/gen_uplc/builder.rs | 23 -- crates/aiken-lang/src/gen_uplc/scope.rs | 65 ++++ crates/aiken-lang/src/gen_uplc/stack.rs | 280 ++++++++++++-- crates/uplc/src/builder.rs | 14 +- 6 files changed, 519 insertions(+), 392 deletions(-) create mode 100644 crates/aiken-lang/src/gen_uplc/scope.rs diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index a6854ec2..f8b789ad 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -4,7 +4,7 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType}, - builder::{ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, CONSTR_INDEX_EXPOSER}, + builder::{CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST}, builtins::DefaultFunction, machine::cost_model::ExBudget, optimize::aiken_optimize_and_intern, @@ -27,6 +27,7 @@ use crate::{ pub mod air; pub mod builder; +pub mod scope; pub mod stack; use air::Air; @@ -185,14 +186,15 @@ impl<'a> CodeGenerator<'a> { TypedExpr::String { value, .. } => ir_stack.string(value.to_string()), TypedExpr::ByteArray { bytes, .. } => ir_stack.byte_array(bytes.to_vec()), TypedExpr::Pipeline { expressions, .. } | TypedExpr::Sequence { expressions, .. } => { - let stacks = Vec::new(); + let mut stacks = Vec::new(); for (index, expr) in expressions.iter().enumerate() { if index == 0 { self.build_ir(expr, ir_stack); } else { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); self.build_ir(expr, &mut stack); + stacks.push(stack); } } @@ -215,7 +217,7 @@ impl<'a> CodeGenerator<'a> { } }, TypedExpr::Fn { args, body, .. } => { - let mut body_stack = ir_stack.in_new_scope(); + let mut body_stack = ir_stack.empty_with_scope(); self.build_ir(body, &mut body_stack); @@ -234,7 +236,7 @@ impl<'a> CodeGenerator<'a> { } => { let stacks = Vec::new(); for element in elements { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); self.build_ir(element, &mut stack); @@ -242,7 +244,7 @@ impl<'a> CodeGenerator<'a> { } let tail = tail.as_ref().map(|tail| { - let mut tail_stack = ir_stack.in_new_scope(); + let mut tail_stack = ir_stack.empty_with_scope(); self.build_ir(tail, &mut tail_stack); @@ -276,7 +278,7 @@ impl<'a> CodeGenerator<'a> { let stacks = Vec::new(); for (arg, func_type) in args.iter().zip(fun_arg_types) { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); if func_type.is_data() && !arg.value.tipo().is_data() { stack.wrap_data(arg.value.tipo()); @@ -301,7 +303,7 @@ impl<'a> CodeGenerator<'a> { let stacks = Vec::new(); for (arg, func_type) in args.iter().zip(fun_arg_types) { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); if func_type.is_data() && !arg.value.tipo().is_data() { stack.wrap_data(arg.value.tipo()); @@ -341,7 +343,7 @@ impl<'a> CodeGenerator<'a> { let mut stacks = Vec::new(); for (arg, func_type) in args.iter().zip(fun_arg_types) { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); if func_type.is_data() && !arg.value.tipo().is_data() { stack.wrap_data(arg.value.tipo()); @@ -366,7 +368,7 @@ impl<'a> CodeGenerator<'a> { let mut stacks = Vec::new(); for (arg, func_type) in args.iter().zip(fun_arg_types) { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); if func_type.is_data() && !arg.value.tipo().is_data() { stack.wrap_data(arg.value.tipo()); @@ -385,7 +387,7 @@ impl<'a> CodeGenerator<'a> { _ => {} } - let mut fun_stack = ir_stack.in_new_scope(); + let mut fun_stack = ir_stack.empty_with_scope(); self.build_ir(fun, &mut fun_stack); @@ -393,7 +395,7 @@ impl<'a> CodeGenerator<'a> { let mut stacks = Vec::new(); for (arg, func_type) in args.iter().zip(fun_arg_types) { - let mut stack = ir_stack.in_new_scope(); + let mut stack = ir_stack.empty_with_scope(); if func_type.is_data() && !arg.value.tipo().is_data() { stack.wrap_data(arg.value.tipo()); @@ -407,8 +409,8 @@ impl<'a> CodeGenerator<'a> { TypedExpr::BinOp { name, left, right, .. } => { - let mut left_stack = ir_stack.in_new_scope(); - let mut right_stack = ir_stack.in_new_scope(); + let mut left_stack = ir_stack.empty_with_scope(); + let mut right_stack = ir_stack.empty_with_scope(); self.build_ir(left, &mut left_stack); self.build_ir(right, &mut right_stack); @@ -422,8 +424,8 @@ impl<'a> CodeGenerator<'a> { tipo, .. } => { - let mut value_stack = ir_stack.in_new_scope(); - let mut pattern_stack = ir_stack.in_new_scope(); + let mut value_stack = ir_stack.empty_with_scope(); + let mut pattern_stack = ir_stack.empty_with_scope(); let mut replaced_type = tipo.clone(); builder::replace_opaque_type(&mut replaced_type, self.data_types.clone()); @@ -1756,17 +1758,17 @@ impl<'a> CodeGenerator<'a> { && !tipo.is_data() && !pattern.is_discard() { - let mut wrap_stack = pattern_stack.in_new_scope(); + let mut wrap_stack = pattern_stack.empty_with_scope(); wrap_stack.un_wrap_data(tipo.clone().into()); - wrap_stack.merge(value_stack); + wrap_stack.merge_child(value_stack); wrap_stack } else if !assignment_properties.value_type.is_data() && tipo.is_data() && !pattern.is_discard() { - let mut wrap_stack = pattern_stack.in_new_scope(); + let mut wrap_stack = pattern_stack.empty_with_scope(); wrap_stack.wrap_data(assignment_properties.value_type.clone()); - wrap_stack.merge(value_stack); + wrap_stack.merge_child(value_stack); wrap_stack } else { value_stack @@ -1775,24 +1777,24 @@ impl<'a> CodeGenerator<'a> { match pattern { Pattern::Int { .. } => todo!(), Pattern::Var { name, .. } => { - let assert_value_stack = value_stack.in_new_scope(); + let expect_value_stack = value_stack.empty_with_scope(); pattern_stack.let_assignment(name, value_stack); if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { - let mut assert_stack = pattern_stack.in_new_scope(); + let mut expect_stack = pattern_stack.empty_with_scope(); - self.recursive_assert_pattern( + self.expect_pattern( pattern, - &mut assert_stack, - assert_value_stack, + &mut expect_stack, + expect_value_stack, tipo, assignment_properties, ); - pattern_stack.merge(assert_stack); + pattern_stack.merge(expect_stack); } } Pattern::Assign { .. } => todo!("Assign not yet implemented"), @@ -1804,7 +1806,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( list, pattern_vec, value_vec, @@ -1828,7 +1830,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( constr, pattern_vec, value_vec, @@ -1852,7 +1854,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( tuple, pattern_vec, value_vec, @@ -2165,10 +2167,10 @@ impl<'a> CodeGenerator<'a> { } } - pub fn recursive_assert_pattern( + pub fn expect_pattern( &mut self, pattern: &Pattern>, - assert_stack: &mut AirStack, + expect_stack: &mut AirStack, value_stack: AirStack, tipo: &Type, assignment_properties: AssignmentProperties, @@ -2176,51 +2178,52 @@ impl<'a> CodeGenerator<'a> { match pattern { Pattern::Int { .. } => unreachable!(), Pattern::Var { name, .. } => { - assert_stack.merge(value_stack); + expect_stack.merge(value_stack); - self.recursive_assert_tipo(tipo, assert_stack, name); + self.expect_type(tipo, expect_stack, name); } Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => unreachable!(), Pattern::List { elements, tail, .. } => { - let mut assert_list_vec = vec![]; let inner_list_type = &tipo.get_inner_types()[0]; let mut names = vec![]; + + let mut expect_list_stacks = vec![]; + for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } Pattern::Assign { .. } => todo!(), - l @ (Pattern::List { .. } + element_pattern @ (Pattern::List { .. } | Pattern::Constructor { .. } | Pattern::Tuple { .. }) => { let name = format!("list_item_id_{}", self.id_gen.next()); + names.push(name.clone()); - self.recursive_assert_pattern( - l, - &mut assert_list_vec, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone().into(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name, - variant_name: String::new(), - }], + let mut element_stack = expect_stack.empty_with_scope(); + let mut value_stack = element_stack.empty_with_scope(); + + value_stack.local_var(tipo.clone().into(), name); + + self.expect_pattern( + element_pattern, + &mut element_stack, + value_stack, inner_list_type, assignment_properties.clone(), - scope.clone(), ); + + expect_list_stacks.push(element_stack); } _ => {} } } + let mut tail_stack = expect_stack.empty_with_scope(); + let name = if let Some(tail) = tail { match &**tail { Pattern::Var { name, .. } => name.clone(), @@ -2230,21 +2233,13 @@ impl<'a> CodeGenerator<'a> { format!("__tail_{}", self.id_gen.next()) }; - self.recursive_assert_tipo(tipo, &mut assert_list_vec, &name, scope.clone()); + self.expect_type(tipo, &mut tail_stack, &name); names.push(name); - pattern_vec.push(Air::ListAccessor { - scope, - tipo: tipo.clone().into(), - names, - tail: true, - check_last_item: false, - }); + expect_stack.list_accessor(tipo.clone().into(), names, true, false, value_stack); - pattern_vec.append(value_vec); - - pattern_vec.append(&mut assert_list_vec); + expect_stack.merge_children(expect_list_stacks); } Pattern::Constructor { arguments, @@ -2306,17 +2301,19 @@ impl<'a> CodeGenerator<'a> { current_index += 1; } else { let id_next = self.id_gen.next(); + final_args.push((format!("__field_{index}_{id_next}"), index)); - self.recursive_assert_tipo( + + self.expect_type( type_map.get(&index).unwrap(), &mut nested_pattern, &format!("__field_{index}_{id_next}"), - scope.clone(), ) } } let constr_var = format!("__constr_{}", self.id_gen.next()); + pattern_vec.push(Air::Let { scope: scope.clone(), name: constr_var.clone(), @@ -2411,11 +2408,10 @@ impl<'a> CodeGenerator<'a> { } else { let id_next = self.id_gen.next(); final_args.push((format!("__tuple_{index}_{id_next}"), index)); - self.recursive_assert_tipo( + self.expect_type( type_map.get(&index).unwrap(), &mut nested_pattern, &format!("__tuple_{index}_{id_next}"), - scope.clone(), ) } } @@ -2436,7 +2432,7 @@ impl<'a> CodeGenerator<'a> { } } - fn recursive_assert_tipo(&mut self, tipo: &Type, assert_vec: &mut AirStack, name: &str) { + fn expect_type(&mut self, tipo: &Type, expect_stack: &mut AirStack, name: &str) { let mut tipo = tipo.clone().into(); builder::replace_opaque_type(&mut tipo, self.data_types.clone()); @@ -2455,231 +2451,103 @@ impl<'a> CodeGenerator<'a> { let inner_list_type = &tipo.get_inner_types()[0]; let inner_pair_types = inner_list_type.get_inner_types(); - assert_vec.push(Air::Builtin { - scope: scope.clone(), - func: DefaultFunction::ChooseUnit, - tipo: tipo.clone(), - count: DefaultFunction::ChooseUnit.arity(), - }); + let mut unwrap_function_stack = expect_stack.empty_with_scope(); + let mut pair_access_stack = unwrap_function_stack.empty_with_scope(); + let mut local_var_stack = pair_access_stack.empty_with_scope(); - assert_vec.push(Air::Call { - scope: scope.clone(), - count: 2, - tipo: tipo.clone(), - }); + local_var_stack.local_var(inner_list_type.clone(), format!("__pair_{new_id}")); - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: ASSERT_ON_LIST.to_string(), - variant_name: String::new(), - }); - - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: name.to_owned(), - variant_name: String::new(), - }); - - assert_vec.push(Air::Fn { - scope: scope.clone(), - params: vec![format!("__pair_{new_id}")], - }); - - assert_vec.push(Air::TupleAccessor { - scope: scope.clone(), - names: vec![ + pair_access_stack.tuple_accessor( + inner_list_type.clone(), + vec![ format!("__pair_fst_{}", id_pair.0), format!("__pair_snd_{}", id_pair.1), ], - tipo: inner_list_type.clone(), - check_last_item: false, - }); + false, + local_var_stack, + ); - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__pair_{new_id}"), - variant_name: String::new(), - }); - - self.recursive_assert_tipo( + self.expect_type( &inner_pair_types[0], - assert_vec, + &mut pair_access_stack, &format!("__pair_fst_{}", id_pair.0), - scope.clone(), ); - self.recursive_assert_tipo( + self.expect_type( &inner_pair_types[1], - assert_vec, + &mut pair_access_stack, &format!("__pair_snd_{}", id_pair.1), - scope.clone(), ); - assert_vec.push(Air::Void { scope }); + unwrap_function_stack + .anonymous_function(vec![format!("__pair_{new_id}")], pair_access_stack); + + expect_stack.expect_list_from_data(tipo.clone(), name, unwrap_function_stack); + + expect_stack.void(); } else if tipo.is_list() { self.used_data_assert_on_list = true; let new_id = self.id_gen.next(); let inner_list_type = &tipo.get_inner_types()[0]; - assert_vec.push(Air::Builtin { - scope: scope.clone(), - func: DefaultFunction::ChooseUnit, - tipo: tipo.clone(), - count: DefaultFunction::ChooseUnit.arity(), - }); + let mut unwrap_function_stack = expect_stack.empty_with_scope(); + let mut list_access_stack = unwrap_function_stack.empty_with_scope(); + let mut local_var_stack = list_access_stack.empty_with_scope(); - assert_vec.push(Air::Call { - scope: scope.clone(), - count: 2, - tipo: tipo.clone(), - }); + local_var_stack.un_wrap_data(inner_list_type.clone()); + local_var_stack.local_var(inner_list_type.clone(), format!("__list_item_{new_id}")); - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: ASSERT_ON_LIST.to_string(), - variant_name: String::new(), - }); + list_access_stack.let_assignment(format!("__list_item_{new_id}"), local_var_stack); - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: name.to_owned(), - variant_name: String::new(), - }); - - assert_vec.push(Air::Fn { - scope: scope.clone(), - params: vec![format!("__list_item_{new_id}")], - }); - - assert_vec.push(Air::Let { - scope: scope.clone(), - name: format!("__list_item_{new_id}"), - }); - - assert_vec.push(Air::UnWrapData { - scope: scope.clone(), - tipo: inner_list_type.clone(), - }); - - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__list_item_{new_id}"), - variant_name: String::new(), - }); - - self.recursive_assert_tipo( + self.expect_type( inner_list_type, - assert_vec, + &mut list_access_stack, &format!("__list_item_{new_id}"), - scope.clone(), ); - assert_vec.push(Air::Void { scope }); + unwrap_function_stack + .anonymous_function(vec![format!("__list_item_{new_id}")], list_access_stack); + + expect_stack.expect_list_from_data(tipo.clone(), name, unwrap_function_stack); + + expect_stack.void(); } else if tipo.is_tuple() { let tuple_inner_types = tipo.get_inner_types(); let mut new_id_list = vec![]; + for (index, _) in tuple_inner_types.iter().enumerate() { new_id_list.push((index, self.id_gen.next())); } - assert_vec.push(Air::TupleAccessor { - scope: scope.clone(), - names: new_id_list - .iter() - .map(|(index, id)| format!("__tuple_index_{index}_{id}")) - .collect_vec(), - tipo: tipo.clone(), - check_last_item: true, - }); + let mut local_var_stack = expect_stack.empty_with_scope(); - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: name.to_owned(), - variant_name: String::new(), - }); + local_var_stack.local_var(tipo, name); + + let names = new_id_list + .iter() + .map(|(index, id)| format!("__tuple_index_{index}_{id}")) + .collect(); + + expect_stack.tuple_accessor(tipo, names, true, local_var_stack); for (index, name) in new_id_list .into_iter() .map(|(index, id)| (index, format!("__tuple_index_{index}_{id}"))) { - self.recursive_assert_tipo( - &tuple_inner_types[index], - assert_vec, - &name, - scope.clone(), - ); + self.expect_type(&tuple_inner_types[index], expect_stack, &name); } } else { let data_type = builder::lookup_data_type_by_tipo(self.data_types.clone(), &tipo).unwrap(); + let new_id = self.id_gen.next(); - assert_vec.push(Air::Builtin { - scope: scope.clone(), - func: DefaultFunction::ChooseUnit, - tipo: tipo.clone(), - count: DefaultFunction::ChooseUnit.arity(), - }); - - assert_vec.push(Air::When { - scope: scope.clone(), - tipo: tipo.clone(), - subject_name: format!("__subject_{new_id}"), - }); - - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: name.to_owned(), - variant_name: String::new(), - }); + // START HERE + let mut arg_stack = expect_stack.empty_with_scope(); + let mut clause_stack = expect_stack.empty_with_scope(); + let mut when_stack = expect_stack.empty_with_scope(); + let mut trace_stack = expect_stack.empty_with_scope(); + let mut subject_stack = expect_stack.empty_with_scope(); for (index, constr) in data_type.constructors.iter().enumerate() { let arg_indices = constr @@ -2691,65 +2559,50 @@ impl<'a> CodeGenerator<'a> { .label .clone() .unwrap_or(format!("__field_{index}_{new_id}")); + (index, arg_name, arg.tipo.clone()) }) .collect_vec(); - assert_vec.push(Air::Clause { - scope: scope.clone(), - tipo: tipo.clone(), - subject_name: format!("__subject_{new_id}"), - complex_clause: false, - }); - - assert_vec.push(Air::Int { - scope: scope.clone(), - value: index.to_string(), - }); - - if !arg_indices.is_empty() { - assert_vec.push(Air::FieldsExpose { - scope: scope.clone(), - indices: arg_indices.clone(), - check_last_item: true, - }); - - assert_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: name.to_owned(), - variant_name: String::new(), - }); + for (index, name, tipo) in arg_indices { + self.expect_type(&tipo, &mut arg_stack, &name); } - for (_, name, tipo) in arg_indices { - self.recursive_assert_tipo(&tipo, assert_vec, &name, scope.clone()); - } + arg_stack = if !arg_indices.is_empty() { + arg_stack.local_var(tipo, name); - assert_vec.push(Air::Void { - scope: scope.clone(), - }); + let field_expose_stack = expect_stack.empty_with_scope(); + + field_expose_stack.fields_expose(arg_indices.clone(), true, arg_stack); + + field_expose_stack + } else { + arg_stack + }; + + arg_stack.void(); + + clause_stack.clause(tipo, format!("__subject_{new_id}"), index, false, arg_stack); } - assert_vec.push(Air::Trace { - scope: scope.clone(), - tipo: tipo.clone(), - }); + trace_stack.trace(tipo.clone()); - assert_vec.push(Air::String { - scope: scope.clone(), - value: "Constr index did not match any type variant".to_string(), - }); + trace_stack.string("Constr index did not match any type variant"); - assert_vec.push(Air::ErrorTerm { - scope, - tipo: tipo.clone(), - }); + trace_stack.error(tipo.clone()); + + subject_stack.local_var(tipo, name); + + when_stack.when( + tipo, + format!("__subject_{new_id}"), + subject_stack, + clause_stack, + trace_stack, + ); + + // Only used here + expect_stack.expect_constr_from_data(tipo.clone(), when_stack); } } @@ -2774,7 +2627,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( a, nested_pattern, &mut vec![Air::Var { @@ -2827,7 +2680,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( a, nested_pattern, &mut vec![Air::Var { @@ -2876,7 +2729,7 @@ impl<'a> CodeGenerator<'a> { && assignment_properties.value_type.is_data() && !tipo.is_data() { - self.recursive_assert_pattern( + self.expect_pattern( a, nested_pattern, &mut vec![Air::Var { diff --git a/crates/aiken-lang/src/gen_uplc/air.rs b/crates/aiken-lang/src/gen_uplc/air.rs index 6532f946..e38e5300 100644 --- a/crates/aiken-lang/src/gen_uplc/air.rs +++ b/crates/aiken-lang/src/gen_uplc/air.rs @@ -7,53 +7,55 @@ use crate::{ tipo::{Type, ValueConstructor}, }; +use super::scope::Scope; + #[derive(Debug, Clone)] pub enum Air { // Primitives Int { - scope: Vec, + scope: Scope, value: String, }, String { - scope: Vec, + scope: Scope, value: String, }, ByteArray { - scope: Vec, + scope: Scope, bytes: Vec, }, Bool { - scope: Vec, + scope: Scope, value: bool, }, List { - scope: Vec, + scope: Scope, count: usize, tipo: Arc, tail: bool, }, Tuple { - scope: Vec, + scope: Scope, tipo: Arc, count: usize, }, Void { - scope: Vec, + scope: Scope, }, Var { - scope: Vec, + scope: Scope, constructor: ValueConstructor, name: String, variant_name: String, }, // Functions Call { - scope: Vec, + scope: Scope, count: usize, tipo: Arc, }, DefineFunc { - scope: Vec, + scope: Scope, func_name: String, module_name: String, params: Vec, @@ -61,70 +63,70 @@ pub enum Air { variant_name: String, }, Fn { - scope: Vec, + scope: Scope, params: Vec, }, Builtin { - scope: Vec, + scope: Scope, count: usize, func: DefaultFunction, tipo: Arc, }, // Operators BinOp { - scope: Vec, + scope: Scope, name: BinOp, tipo: Arc, }, UnOp { - scope: Vec, + scope: Scope, op: UnOp, }, // Assignment Let { - scope: Vec, + scope: Scope, name: String, }, UnWrapData { - scope: Vec, + scope: Scope, tipo: Arc, }, WrapData { - scope: Vec, + scope: Scope, tipo: Arc, }, AssertConstr { - scope: Vec, + scope: Scope, constr_index: usize, }, AssertBool { - scope: Vec, + scope: Scope, is_true: bool, }, // When When { - scope: Vec, + scope: Scope, tipo: Arc, subject_name: String, }, Clause { - scope: Vec, + scope: Scope, tipo: Arc, subject_name: String, complex_clause: bool, }, ListClause { - scope: Vec, + scope: Scope, tipo: Arc, tail_name: String, next_tail_name: Option, complex_clause: bool, }, WrapClause { - scope: Vec, + scope: Scope, }, TupleClause { - scope: Vec, + scope: Scope, tipo: Arc, indices: IndexSet<(usize, String)>, predefined_indices: IndexSet<(usize, String)>, @@ -133,88 +135,88 @@ pub enum Air { complex_clause: bool, }, ClauseGuard { - scope: Vec, + scope: Scope, subject_name: String, tipo: Arc, }, ListClauseGuard { - scope: Vec, + scope: Scope, tipo: Arc, tail_name: String, next_tail_name: Option, inverse: bool, }, Finally { - scope: Vec, + scope: Scope, }, // If If { - scope: Vec, + scope: Scope, tipo: Arc, }, // Record Creation Record { - scope: Vec, + scope: Scope, tag: usize, tipo: Arc, count: usize, }, RecordUpdate { - scope: Vec, + scope: Scope, highest_index: usize, indices: Vec<(usize, Arc)>, tipo: Arc, }, // Field Access RecordAccess { - scope: Vec, + scope: Scope, record_index: u64, tipo: Arc, }, FieldsExpose { - scope: Vec, + scope: Scope, indices: Vec<(usize, String, Arc)>, check_last_item: bool, }, // ListAccess ListAccessor { - scope: Vec, + scope: Scope, tipo: Arc, names: Vec, tail: bool, check_last_item: bool, }, ListExpose { - scope: Vec, + scope: Scope, tipo: Arc, tail_head_names: Vec<(String, String)>, tail: Option<(String, String)>, }, // Tuple Access TupleAccessor { - scope: Vec, + scope: Scope, names: Vec, tipo: Arc, check_last_item: bool, }, TupleIndex { - scope: Vec, + scope: Scope, tipo: Arc, tuple_index: usize, }, // Misc. ErrorTerm { - scope: Vec, + scope: Scope, tipo: Arc, }, Trace { - scope: Vec, + scope: Scope, tipo: Arc, }, } impl Air { - pub fn scope(&self) -> Vec { + pub fn scope(&self) -> Scope { match self { Air::Int { scope, .. } | Air::String { scope, .. } @@ -256,7 +258,7 @@ impl Air { | Air::Trace { scope, .. } => scope.clone(), } } - pub fn scope_mut(&mut self) -> &mut Vec { + pub fn scope_mut(&mut self) -> &mut Scope { match self { Air::Int { scope, .. } | Air::String { scope, .. } diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 1a06be7a..8d0c0a58 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -636,29 +636,6 @@ pub fn list_access_to_uplc( } } -pub fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec { - let longest_length = if scope.len() >= scope_prev.len() { - scope.len() - } else { - scope_prev.len() - }; - - if *scope == *scope_prev { - return scope.to_vec(); - } - - for index in 0..longest_length { - if scope.get(index).is_none() { - return scope.to_vec(); - } else if scope_prev.get(index).is_none() { - return scope_prev.to_vec(); - } else if scope[index] != scope_prev[index] { - return scope[0..index].to_vec(); - } - } - vec![] -} - pub fn check_when_pattern_needs( pattern: &Pattern>, clause_properties: &mut ClauseProperties, diff --git a/crates/aiken-lang/src/gen_uplc/scope.rs b/crates/aiken-lang/src/gen_uplc/scope.rs new file mode 100644 index 00000000..4f09291b --- /dev/null +++ b/crates/aiken-lang/src/gen_uplc/scope.rs @@ -0,0 +1,65 @@ +#[derive(Debug, Clone, Default)] +pub struct Scope(Vec); + +impl Scope { + pub fn push(&mut self, value: u64) { + self.0.push(value); + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn replace(&mut self, pattern: &Scope, replacement: Scope) { + let mut result = Vec::new(); + + let mut index = 0; + let mut pattern_index = 0; + + let mut no_matches = true; + + while index < self.0.len() { + if self.0[index] == pattern.0[pattern_index] { + if pattern_index == pattern.0.len() - 1 { + no_matches = false; + result.extend(replacement.0.clone()); + pattern_index = 0; + } else { + pattern_index += 1; + } + } else { + result.push(self.0[index]); + pattern_index = 0; + } + + index += 1; + } + + if no_matches { + replacement.0.extend(self.0); + self.0 = replacement.0; + } else { + self.0 = result; + } + } + + pub fn common_ancestor(&self, other: &Self) -> Scope { + let longest_length = self.0.len().max(other.0.len()); + + if *self.0 == *other.0 { + return self.clone(); + } + + for index in 0..longest_length { + if self.0.get(index).is_none() { + return self.clone(); + } else if other.0.get(index).is_none() { + return other.clone(); + } else if self.0[index] != other.0[index] { + return Scope(self.0[0..index].to_vec()); + } + } + + Scope::default() + } +} diff --git a/crates/aiken-lang/src/gen_uplc/stack.rs b/crates/aiken-lang/src/gen_uplc/stack.rs index 02657f9d..171920ce 100644 --- a/crates/aiken-lang/src/gen_uplc/stack.rs +++ b/crates/aiken-lang/src/gen_uplc/stack.rs @@ -1,30 +1,34 @@ use std::sync::Arc; -use uplc::builtins::DefaultFunction; +use uplc::{builder::EXPECT_ON_LIST, builtins::DefaultFunction}; use crate::{ - tipo::{Type, ValueConstructor}, + ast::Span, + tipo::{Type, ValueConstructor, ValueConstructorVariant}, IdGenerator, }; -use super::air::Air; +use super::{air::Air, scope::Scope}; +/// A builder for [`Air`]. pub struct AirStack<'a> { pub id_gen: &'a mut IdGenerator, - pub scope: Vec, + pub scope: Scope, pub air: Vec, } impl<'a> AirStack<'a> { + /// Create a new [`AirStack`] with an [`IdGenerator`] pub fn new(id_gen: &'a mut IdGenerator) -> Self { AirStack { id_gen, - scope: vec![id_gen.next()], + scope: Scope::default(), air: vec![], } } - pub fn with_scope(id_gen: &'a mut IdGenerator, scope: Vec) -> Self { + /// Create a new [`AirStack`] with an [`IdGenerator`] and [`Scope`]. + pub fn with_scope(id_gen: &'a mut IdGenerator, scope: Scope) -> Self { AirStack { id_gen, scope, @@ -32,22 +36,37 @@ impl<'a> AirStack<'a> { } } - pub fn in_new_scope(&mut self) -> Self { - let mut new_stack = AirStack::with_scope(&mut self.id_gen, self.scope.clone()); - - new_stack.new_scope(); - - new_stack + /// Create a new empty [`AirStack`] with the current stack's scope. + pub fn empty_with_scope(&mut self) -> Self { + AirStack::with_scope(&mut self.id_gen, self.scope.clone()) } - pub fn new_scope(&mut self) { + /// Increment the [`Scope`] + fn new_scope(&mut self) { self.scope.push(self.id_gen.next()); } + /// Merge two [`AirStack`]'s together while maintaining the current stack's [`Scope`] pub fn merge(&mut self, mut other: AirStack) { self.air.append(&mut other.air); } + pub fn merge_child(&mut self, mut other: AirStack) { + let pattern = self.scope.common_ancestor(&other.scope); + + for ir in other.air.iter_mut() { + ir.scope_mut().replace(&pattern, self.scope.clone()); + } + + self.merge(other); + } + + pub fn merge_children(&mut self, stacks: Vec) { + for stack in stacks { + self.merge_child(stack) + } + } + pub fn sequence(&mut self, stacks: Vec) { for stack in stacks { self.merge(stack) @@ -55,20 +74,26 @@ impl<'a> AirStack<'a> { } pub fn integer(&mut self, value: String) { + self.new_scope(); + self.air.push(Air::Int { scope: self.scope.clone(), value, }); } - pub fn string(&mut self, value: String) { + pub fn string(&mut self, value: impl ToString) { + self.new_scope(); + self.air.push(Air::String { scope: self.scope.clone(), - value, + value: value.to_string(), }); } pub fn byte_array(&mut self, bytes: Vec) { + self.new_scope(); + self.air.push(Air::ByteArray { scope: self.scope.clone(), bytes, @@ -76,6 +101,8 @@ impl<'a> AirStack<'a> { } pub fn builtin(&mut self, func: DefaultFunction, tipo: Arc, args: Vec) { + self.new_scope(); + self.air.push(Air::Builtin { scope: self.scope.clone(), count: args.len(), @@ -83,7 +110,7 @@ impl<'a> AirStack<'a> { tipo, }); - self.sequence(args); + self.merge_children(args); } pub fn var( @@ -92,6 +119,8 @@ impl<'a> AirStack<'a> { name: impl ToString, variant_name: impl ToString, ) { + self.new_scope(); + self.air.push(Air::Var { scope: self.scope.clone(), constructor, @@ -100,16 +129,36 @@ impl<'a> AirStack<'a> { }); } + pub fn local_var(&mut self, tipo: Arc, name: impl ToString) { + self.new_scope(); + + self.air.push(Air::Var { + scope: self.scope.clone(), + constructor: ValueConstructor::public( + tipo, + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_string(), + variant_name: String::new(), + }); + } + pub fn anonymous_function(&mut self, params: Vec, body: AirStack) { + self.new_scope(); + self.air.push(Air::Fn { scope: self.scope.clone(), params, }); - self.merge(body); + self.merge_child(body); } pub fn list(&mut self, tipo: Arc, elements: Vec, tail: Option) { + self.new_scope(); + self.air.push(Air::List { scope: self.scope.clone(), count: elements.len(), @@ -117,14 +166,16 @@ impl<'a> AirStack<'a> { tail: tail.is_some(), }); - self.sequence(elements); + self.merge_children(elements); if let Some(tail) = tail { - self.merge(tail); + self.merge_child(tail); } } pub fn record(&mut self, tipo: Arc, tag: usize, fields: Vec) { + self.new_scope(); + self.air.push(Air::Record { scope: self.scope.clone(), tag, @@ -132,19 +183,21 @@ impl<'a> AirStack<'a> { count: fields.len(), }); - self.sequence(fields); + self.merge_children(fields); } pub fn call(&mut self, tipo: Arc, fun: AirStack, args: Vec) { + self.new_scope(); + self.air.push(Air::Call { scope: self.scope.clone(), count: args.len(), tipo, }); - self.merge(fun); + self.merge_child(fun); - self.sequence(args); + self.merge_children(args); } pub fn binop( @@ -154,26 +207,62 @@ impl<'a> AirStack<'a> { left: AirStack, right: AirStack, ) { + self.new_scope(); + self.air.push(Air::BinOp { scope: self.scope.clone(), name, tipo, }); - self.merge(left); - self.merge(right); + self.merge_child(left); + self.merge_child(right); } pub fn let_assignment(&mut self, name: impl ToString, value: AirStack) { + self.new_scope(); + self.air.push(Air::Let { scope: self.scope.clone(), name: name.to_string(), }); - self.merge(value); + self.merge_child(value); + } + + pub fn expect_list_from_data( + &mut self, + tipo: Arc, + name: impl ToString, + unwrap_function: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::Builtin { + scope: self.scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone(), + count: DefaultFunction::ChooseUnit.arity(), + }); + + self.new_scope(); + + self.air.push(Air::Call { + scope: self.scope.clone(), + count: 2, + tipo, + }); + + self.local_var(tipo.clone(), EXPECT_ON_LIST); + + self.local_var(tipo, name); + + self.merge_child(unwrap_function); } pub fn wrap_data(&mut self, tipo: Arc) { + self.new_scope(); + self.air.push(Air::WrapData { scope: self.scope.clone(), tipo, @@ -181,9 +270,150 @@ impl<'a> AirStack<'a> { } pub fn un_wrap_data(&mut self, tipo: Arc) { + self.new_scope(); + self.air.push(Air::UnWrapData { scope: self.scope.clone(), tipo, }) } + + pub fn void(&mut self) { + self.new_scope(); + + self.air.push(Air::Void { + scope: self.scope.clone(), + }) + } + + pub fn tuple_accessor( + &mut self, + tipo: Arc, + names: Vec, + check_last_item: bool, + value: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::TupleAccessor { + scope: self.scope.clone(), + names, + tipo, + check_last_item, + }); + + self.merge_child(value); + } + + pub fn fields_expose( + &mut self, + indices: Vec<(usize, String, Arc)>, + check_last_item: bool, + value: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::FieldsExpose { + scope: self.scope.clone(), + indices, + check_last_item, + }); + + self.merge_child(value); + } + + pub fn clause( + &mut self, + tipo: Arc, + subject_name: impl ToString, + tag: usize, + complex_clause: bool, + body: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::Clause { + scope: self.scope.clone(), + subject_name: subject_name.to_string(), + complex_clause, + tipo, + }); + + self.integer(tag.to_string()); + + self.merge_child(body); + } + + pub fn trace(&mut self, tipo: Arc) { + self.new_scope(); + + self.air.push(Air::Trace { + scope: self.scope.clone(), + tipo, + }) + } + + pub fn error(&mut self, tipo: Arc) { + self.new_scope(); + + self.air.push(Air::ErrorTerm { + scope: self.scope.clone(), + tipo, + }) + } + + pub fn expect_constr_from_data(&mut self, tipo: Arc, when_stack: AirStack) { + self.new_scope(); + + self.air.push(Air::Builtin { + scope: self.scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone(), + count: DefaultFunction::ChooseUnit.arity(), + }); + + self.merge_child(when_stack); + } + + pub fn when( + &mut self, + tipo: Arc, + subject_name: impl ToString, + subject_stack: AirStack, + clauses_stack: AirStack, + else_stack: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::When { + scope: self.scope.clone(), + subject_name: subject_name.to_string(), + tipo, + }); + + self.merge_child(subject_stack); + self.merge_child(clauses_stack); + self.merge_child(else_stack); + } + + pub fn list_accessor( + &mut self, + tipo: Arc, + names: Vec, + tail: bool, + check_last_item: bool, + value: AirStack, + ) { + self.new_scope(); + + self.air.push(Air::ListAccessor { + scope: self.scope.clone(), + names, + tail, + check_last_item, + tipo, + }); + + self.merge_child(value); + } } diff --git a/crates/uplc/src/builder.rs b/crates/uplc/src/builder.rs index 35eaa174..d30d8c4c 100644 --- a/crates/uplc/src/builder.rs +++ b/crates/uplc/src/builder.rs @@ -6,7 +6,7 @@ use crate::{ pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer"; pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; -pub const ASSERT_ON_LIST: &str = "__assert_on_list"; +pub const EXPECT_ON_LIST: &str = "__expect_on_list"; impl Term { pub fn apply(self, arg: Self) -> Self { @@ -213,11 +213,11 @@ impl Term { } pub fn assert_on_list(self) -> Term { - self.lambda(ASSERT_ON_LIST.to_string()) + self.lambda(EXPECT_ON_LIST.to_string()) .apply( - Term::var(ASSERT_ON_LIST.to_string()).apply(Term::var(ASSERT_ON_LIST.to_string())), + Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())), ) - .lambda(ASSERT_ON_LIST.to_string()) + .lambda(EXPECT_ON_LIST.to_string()) .apply( Term::var("__list_to_check".to_string()) .delayed_choose_list( @@ -227,8 +227,8 @@ impl Term { Term::head_list().apply(Term::var("__list_to_check".to_string())), ) .choose_unit( - Term::var(ASSERT_ON_LIST.to_string()) - .apply(Term::var(ASSERT_ON_LIST.to_string())) + Term::var(EXPECT_ON_LIST.to_string()) + .apply(Term::var(EXPECT_ON_LIST.to_string())) .apply( Term::tail_list() .apply(Term::var("__list_to_check".to_string())), @@ -238,7 +238,7 @@ impl Term { ) .lambda("__check_with".to_string()) .lambda("__list_to_check".to_string()) - .lambda(ASSERT_ON_LIST), + .lambda(EXPECT_ON_LIST), ) }