diff --git a/Cargo.lock b/Cargo.lock index 14ed619a..1c243db9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,7 @@ dependencies = [ "futures", "hex", "ignore", + "indexmap", "itertools", "miette", "owo-colors", diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index 52791762..1b5d1262 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -1,5 +1,6 @@ -use std::{collections::HashSet, sync::Arc}; +use std::sync::Arc; +use indexmap::IndexSet; use uplc::builtins::DefaultFunction; use crate::{ @@ -9,6 +10,7 @@ use crate::{ #[derive(Debug, Clone)] pub enum Air { + // Primitives Int { scope: Vec, value: String, @@ -29,17 +31,6 @@ pub enum Air { value: bool, }, - Var { - scope: Vec, - constructor: ValueConstructor, - name: String, - variant_name: String, - }, - - Fn { - scope: Vec, - params: Vec, - }, List { scope: Vec, count: usize, @@ -47,48 +38,30 @@ pub enum Air { tail: bool, }, - ListAccessor { + Tuple { scope: Vec, tipo: Arc, - names: Vec, - tail: bool, + count: usize, }, - ListExpose { + Void { scope: Vec, - tipo: Arc, - tail_head_names: Vec<(String, String)>, - tail: Option<(String, String)>, }, + Var { + scope: Vec, + constructor: ValueConstructor, + name: String, + variant_name: String, + }, + + // Functions Call { scope: Vec, count: usize, - }, - - Builtin { - scope: Vec, - func: DefaultFunction, tipo: Arc, }, - BinOp { - scope: Vec, - name: BinOp, - count: usize, - tipo: Arc, - }, - - Assignment { - scope: Vec, - name: String, - }, - - Assert { - scope: Vec, - constr_index: usize, - }, - DefineFunc { scope: Vec, func_name: String, @@ -98,11 +71,47 @@ pub enum Air { variant_name: String, }, - Lam { + Fn { + scope: Vec, + params: Vec, + }, + + Builtin { + scope: Vec, + func: DefaultFunction, + tipo: Arc, + }, + + // Operators + BinOp { + scope: Vec, + name: BinOp, + count: usize, + tipo: Arc, + }, + + UnOp { + scope: Vec, + op: UnOp, + }, + + // Assignment + Let { scope: Vec, name: String, }, + UnWrapData { + scope: Vec, + tipo: Arc, + }, + + AssertConstr { + scope: Vec, + constr_index: usize, + }, + + // When When { scope: Vec, tipo: Arc, @@ -124,11 +133,15 @@ pub enum Air { complex_clause: bool, }, + WrapClause { + scope: Vec, + }, + TupleClause { scope: Vec, tipo: Arc, - indices: HashSet<(usize, String)>, - predefined_indices: HashSet<(usize, String)>, + indices: IndexSet<(usize, String)>, + predefined_indices: IndexSet<(usize, String)>, subject_name: String, count: usize, complex_clause: bool, @@ -148,18 +161,17 @@ pub enum Air { inverse: bool, }, - Discard { - scope: Vec, - }, - Finally { scope: Vec, }, + // If If { scope: Vec, + tipo: Arc, }, + // Record Creation Record { scope: Vec, constr_index: usize, @@ -167,6 +179,14 @@ pub enum Air { count: usize, }, + RecordUpdate { + scope: Vec, + highest_index: usize, + indices: Vec<(usize, Arc)>, + tipo: Arc, + }, + + // Field Access RecordAccess { scope: Vec, record_index: u64, @@ -175,14 +195,32 @@ pub enum Air { FieldsExpose { scope: Vec, - count: usize, indices: Vec<(usize, String, Arc)>, + check_last_item: bool, }, - Tuple { + // ListAccess + ListAccessor { scope: Vec, tipo: Arc, - count: usize, + names: Vec, + tail: bool, + check_last_item: bool, + }, + + ListExpose { + scope: Vec, + tipo: Arc, + tail_head_names: Vec<(String, String)>, + tail: Option<(String, String)>, + }, + + // Tuple Access + TupleAccessor { + scope: Vec, + names: Vec, + tipo: Arc, + check_last_item: bool, }, TupleIndex { @@ -191,6 +229,7 @@ pub enum Air { tuple_index: usize, }, + // Misc. Todo { scope: Vec, label: Option, @@ -208,23 +247,6 @@ pub enum Air { text: Option, tipo: Arc, }, - - RecordUpdate { - scope: Vec, - highest_index: usize, - indices: Vec<(usize, Arc)>, - }, - - UnOp { - scope: Vec, - op: UnOp, - }, - - TupleAccessor { - scope: Vec, - names: Vec, - tipo: Arc, - }, } impl Air { @@ -234,67 +256,142 @@ impl Air { | Air::String { scope, .. } | Air::ByteArray { scope, .. } | Air::Bool { scope, .. } - | Air::Var { scope, .. } | Air::List { scope, .. } - | Air::ListAccessor { scope, .. } - | Air::ListExpose { scope, .. } - | Air::Fn { scope, .. } + | Air::Tuple { scope, .. } + | Air::Void { scope } + | Air::Var { scope, .. } | Air::Call { scope, .. } + | Air::DefineFunc { scope, .. } + | Air::Fn { scope, .. } | Air::Builtin { scope, .. } | Air::BinOp { scope, .. } - | Air::Assignment { scope, .. } - | Air::Assert { scope, .. } - | Air::DefineFunc { scope, .. } - | Air::Lam { scope, .. } + | Air::UnOp { scope, .. } + | Air::Let { scope, .. } + | Air::UnWrapData { scope, .. } + | Air::AssertConstr { scope, .. } | Air::When { scope, .. } | Air::Clause { scope, .. } | Air::ListClause { scope, .. } + | Air::WrapClause { scope } + | Air::TupleClause { scope, .. } | Air::ClauseGuard { scope, .. } | Air::ListClauseGuard { scope, .. } - | Air::Discard { scope } | Air::Finally { scope } | Air::If { scope, .. } | Air::Record { scope, .. } + | Air::RecordUpdate { scope, .. } | Air::RecordAccess { scope, .. } | Air::FieldsExpose { scope, .. } - | Air::Tuple { scope, .. } - | Air::Todo { scope, .. } - | Air::ErrorTerm { scope, .. } - | Air::RecordUpdate { scope, .. } - | Air::UnOp { scope, .. } - | Air::Trace { scope, .. } + | Air::ListAccessor { scope, .. } + | Air::ListExpose { scope, .. } | Air::TupleAccessor { scope, .. } | Air::TupleIndex { scope, .. } - | Air::TupleClause { scope, .. } => scope.to_vec(), + | Air::Todo { scope, .. } + | Air::ErrorTerm { scope, .. } + | Air::Trace { scope, .. } => scope.clone(), } } pub fn tipo(&self) -> Option> { match self { + Air::Int { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Int".to_string(), + args: vec![], + } + .into(), + ), + Air::String { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "String".to_string(), + args: vec![], + } + .into(), + ), + Air::ByteArray { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "ByteArray".to_string(), + args: vec![], + } + .into(), + ), + Air::Bool { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Bool".to_string(), + args: vec![], + } + .into(), + ), + Air::Void { .. } => Some( + Type::App { + public: true, + module: String::new(), + name: "Void".to_string(), + args: vec![], + } + .into(), + ), + Air::Var { constructor, .. } => Some(constructor.tipo.clone()), Air::List { tipo, .. } - | Air::ListAccessor { tipo, .. } - | Air::ListExpose { tipo, .. } + | Air::Tuple { tipo, .. } + | Air::Call { tipo, .. } | Air::Builtin { tipo, .. } | Air::BinOp { tipo, .. } + | Air::UnWrapData { tipo, .. } | Air::When { tipo, .. } | Air::Clause { tipo, .. } | Air::ListClause { tipo, .. } | Air::TupleClause { tipo, .. } | Air::ClauseGuard { tipo, .. } + | Air::If { tipo, .. } | Air::ListClauseGuard { tipo, .. } | Air::Record { tipo, .. } + | Air::RecordUpdate { tipo, .. } | Air::RecordAccess { tipo, .. } - | Air::Tuple { tipo, .. } + | Air::ListAccessor { tipo, .. } + | Air::ListExpose { tipo, .. } + | Air::TupleAccessor { tipo, .. } | Air::TupleIndex { tipo, .. } | Air::Todo { tipo, .. } | Air::ErrorTerm { tipo, .. } - | Air::Trace { tipo, .. } - | Air::TupleAccessor { tipo, .. } - | Air::Var { - constructor: ValueConstructor { tipo, .. }, - .. - } => Some(tipo.clone()), - _ => None, + | Air::Trace { tipo, .. } => Some(tipo.clone()), + + Air::DefineFunc { .. } + | Air::Fn { .. } + | Air::Let { .. } + | Air::WrapClause { .. } + | Air::AssertConstr { .. } + | Air::Finally { .. } + | Air::FieldsExpose { .. } => None, + + Air::UnOp { op, .. } => match op { + UnOp::Not => Some( + Type::App { + public: true, + module: String::new(), + name: "Bool".to_string(), + args: vec![], + } + .into(), + ), + UnOp::Negate => Some( + Type::App { + public: true, + module: String::new(), + name: "Int".to_string(), + args: vec![], + } + .into(), + ), + }, } } } diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 3b3d69a3..4cc07d25 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -1,14 +1,10 @@ -use std::{ - cell::RefCell, - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::{cell::RefCell, sync::Arc}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ - builder::{apply_wrap, if_else}, + builder::{apply_wrap, delayed_choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -19,7 +15,7 @@ use uplc::{ use crate::{ air::Air, ast::{ - BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg, TypedDataType, + AssignmentKind, BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg, TypedDataType, UnOp, }, expr::TypedExpr, @@ -55,6 +51,12 @@ pub struct FunctionAccessKey { pub variant_name: String, } +#[derive(Clone, Debug)] +pub struct AssignmentProperties { + pub value_is_data: bool, + pub kind: AssignmentKind, +} + #[derive(Clone, Debug)] pub enum ClauseProperties { ConstrClause { @@ -68,14 +70,14 @@ pub enum ClauseProperties { needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, - current_index: usize, + current_index: i64, }, TupleClause { clause_var_name: String, needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, - defined_tuple_indices: HashSet<(usize, String)>, + defined_tuple_indices: IndexSet<(usize, String)>, }, } @@ -87,7 +89,7 @@ impl ClauseProperties { needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, - current_index: 0, + current_index: -1, } } else if t.is_tuple() { ClauseProperties::TupleClause { @@ -95,7 +97,7 @@ impl ClauseProperties { needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, - defined_tuple_indices: HashSet::new(), + defined_tuple_indices: IndexSet::new(), } } else { ClauseProperties::ConstrClause { @@ -482,82 +484,22 @@ pub fn list_access_to_uplc( tail: bool, current_index: usize, term: Term, - tipo: &Type, + tipos: Vec>, + check_last_item: bool, ) -> Term { - let (first, names) = names.split_first().unwrap(); + if let Some((first, names)) = names.split_first() { + let (current_tipo, tipos) = tipos.split_first().unwrap(); - let head_list = if tipo.is_map() { - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - Term::Var(Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }), - ) - } else { - convert_data_to_type( + let head_list = if current_tipo.is_map() { apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var(Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), }), - ), - &tipo.clone().get_inner_types()[0], - ) - }; - - if names.len() == 1 && tail { - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: names[0].clone(), - unique: 0.into(), - }, - body: term.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var(Name { - text: format!( - "tail_index_{}_{}", - current_index, id_list[current_index] - ), - unique: 0.into(), - }), - ), - ) - .into(), - }, - head_list, ) - .into(), - } - } else if names.is_empty() { - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: term.into(), - }, + } else { + convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var(Name { @@ -565,40 +507,135 @@ pub fn list_access_to_uplc( unique: 0.into(), }), ), + ¤t_tipo.to_owned(), ) - .into(), + }; + + if names.len() == 1 && tail { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: names[0].clone(), + unique: 0.into(), + }, + body: term.into(), + }, + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + ) + .into(), + }, + head_list, + ) + .into(), + } + } else if names.is_empty() { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: if check_last_item { + delayed_choose_list( + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + term, + apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Constant(UplcConstant::String( + "List/Tuple contains more items than it should" + .to_string(), + )), + ), + Term::Delay(Term::Error.into()), + ) + .force_wrap(), + ) + .into() + } else { + term.into() + }, + }, + head_list, + ) + .into(), + } + } else { + Term::Lambda { + parameter_name: Name { + text: format!("tail_index_{}_{}", current_index, id_list[current_index]), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first.clone(), + unique: 0.into(), + }, + body: apply_wrap( + list_access_to_uplc( + names, + id_list, + tail, + current_index + 1, + term, + tipos.to_owned(), + check_last_item, + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!( + "tail_index_{}_{}", + current_index, id_list[current_index] + ), + unique: 0.into(), + }), + ), + ) + .into(), + }, + head_list, + ) + .into(), + } } } else { - Term::Lambda { - parameter_name: Name { - text: format!("tail_index_{}_{}", current_index, id_list[current_index]), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first.clone(), - unique: 0.into(), - }, - body: apply_wrap( - list_access_to_uplc(names, id_list, tail, current_index + 1, term, tipo), - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var(Name { - text: format!( - "tail_index_{}_{}", - current_index, id_list[current_index] - ), - unique: 0.into(), - }), - ), - ) - .into(), - }, - head_list, - ) - .into(), - } + term } } @@ -760,7 +797,7 @@ pub fn match_ir_for_recursion( } } -pub fn find_generics_to_replace(tipo: &mut Arc, generic_types: &HashMap>) { +pub fn find_generics_to_replace(tipo: &mut Arc, generic_types: &IndexMap>) { if let Some(id) = tipo.get_generic() { //If generic does not have a type we know of like a None in option then just use same type *tipo = generic_types.get(&id).unwrap_or(tipo).clone(); @@ -986,7 +1023,7 @@ pub fn wrap_validator_args(term: Term, arguments: Vec) -> Term, - generic_types: HashMap>, + generic_types: IndexMap>, full_type: &Arc, ) -> (String, Vec) { let mut new_air = ir.clone(); @@ -1053,6 +1090,7 @@ pub fn monomorphize( tipo, names, tail, + check_last_item, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); @@ -1063,6 +1101,7 @@ pub fn monomorphize( names, tipo, tail, + check_last_item, }; needs_variant = true; } @@ -1114,8 +1153,15 @@ pub fn monomorphize( needs_variant = true; } } - // TODO check on assignment if type is needed - Air::Assignment { .. } => {} + Air::UnWrapData { scope, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::UnWrapData { scope, tipo }; + needs_variant = true; + } + } Air::When { scope, tipo, @@ -1327,8 +1373,8 @@ pub fn monomorphize( } Air::FieldsExpose { scope, - count, indices, + check_last_item, } => { let mut new_indices = vec![]; for (ind, name, tipo) in indices { @@ -1343,16 +1389,18 @@ pub fn monomorphize( } new_air[index] = Air::FieldsExpose { scope, - count, indices: new_indices, + check_last_item, }; } Air::RecordUpdate { scope, highest_index, indices, + tipo, } => { let mut new_indices = vec![]; + let mut tipo = tipo.clone(); for (ind, tipo) in indices { if tipo.is_generic() { let mut tipo = tipo.clone(); @@ -1363,18 +1411,51 @@ pub fn monomorphize( new_indices.push((ind, tipo)); } } + if tipo.is_generic() { + find_generics_to_replace(&mut tipo, &generic_types); + } new_air[index] = Air::RecordUpdate { scope, highest_index, indices: new_indices, + tipo, }; } - Air::TupleAccessor { scope, names, tipo } => { + Air::TupleAccessor { + scope, + names, + tipo, + check_last_item, + } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); - new_air[index] = Air::TupleAccessor { scope, names, tipo }; + new_air[index] = Air::TupleAccessor { + scope, + names, + tipo, + check_last_item, + }; + needs_variant = true; + } + } + + Air::Call { scope, count, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::Call { scope, count, tipo }; + needs_variant = true; + } + } + Air::If { scope, tipo } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::If { scope, tipo }; needs_variant = true; } } @@ -1393,14 +1474,14 @@ pub fn monomorphize( (new_name, new_air) } -pub fn handle_func_deps_ir( - dep_ir: &mut Vec, +pub fn handle_func_dependencies_ir( + dependencies_ir: &mut Vec, funt_comp: &FuncComponents, func_components: &IndexMap, - defined_functions: &mut HashMap, + defined_functions: &mut IndexMap, func_index_map: &IndexMap>, func_scope: &[u64], - to_be_defined: &mut HashMap, + to_be_defined: &mut IndexMap, ) { let mut funt_comp = funt_comp.clone(); @@ -1451,9 +1532,9 @@ pub fn handle_func_deps_ir( temp_ir.append(&mut recursion_ir); - temp_ir.append(dep_ir); + temp_ir.append(dependencies_ir); - *dep_ir = temp_ir; + *dependencies_ir = temp_ir; if get_common_ancestor(dep_scope, func_scope) == func_scope.to_vec() { defined_functions.insert(dependency, ()); } @@ -1492,10 +1573,11 @@ pub fn handle_recursion_ir( let current_call = recursion_ir[index - 1].clone(); match current_call { - Air::Call { scope, count } => { + Air::Call { scope, count, tipo } => { recursion_ir[index - 1] = Air::Call { scope, count: count + 1, + tipo, } } _ => unreachable!(), @@ -1504,7 +1586,7 @@ pub fn handle_recursion_ir( } pub fn lookup_data_type_by_tipo( - data_types: HashMap, + data_types: IndexMap, tipo: &Type, ) -> Option>> { match tipo { @@ -1539,7 +1621,7 @@ pub fn lookup_data_type_by_tipo( pub fn check_replaceable_opaque_type( t: &Arc, - data_types: &HashMap, + data_types: &IndexMap, ) -> bool { let data_type = lookup_data_type_by_tipo(data_types.clone(), t); @@ -1551,12 +1633,12 @@ pub fn check_replaceable_opaque_type( } } -pub fn replace_opaque_type(t: &mut Arc, data_types: HashMap) { +pub fn replace_opaque_type(t: &mut Arc, data_types: IndexMap) { if check_replaceable_opaque_type(t, &data_types) && matches!(&**t, Type::App { .. }) { let data_type = lookup_data_type_by_tipo(data_types.clone(), t).unwrap(); let new_type_fields = data_type.typed_parameters.clone(); - let mut generics_type_map: HashMap> = HashMap::new(); + let mut generics_type_map: IndexMap> = IndexMap::new(); for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) { let mut map = generics_type_map.into_iter().collect_vec(); diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 913e27d4..1349ae39 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -52,8 +52,8 @@ pub fn lexer() -> impl Parser, Error = ParseError> { just('|').to(Token::Vbar), just("&&").to(Token::AmperAmper), just('#').to(Token::Hash), - just("\n\n").to(Token::EmptyLine), - just("\n").to(Token::NewLine), + choice((just("\n\n"), just("\r\n\r\n"))).to(Token::EmptyLine), + choice((just("\n"), just("\r\n"))).to(Token::NewLine), )); let grouping = choice(( diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index de8b06c5..6caa7e3f 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -22,6 +22,22 @@ fn assert_definitions(code: &str, definitions: Vec) { ) } +#[test] +fn windows_newline() { + let code = "use aiken/list\r\n"; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Use(Use { + location: Span::new((), 0..14), + module: vec!["aiken".to_string(), "list".to_string()], + as_name: None, + unqualified: vec![], + package: (), + })], + ) +} + #[test] fn import() { let code = indoc! {r#" diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index 1ddbcac5..fa8f31e3 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -173,6 +173,14 @@ impl Type { } } + pub fn is_data(&self) -> bool { + match self { + Self::App { module, name, .. } => "Data" == name && module.is_empty(), + Self::Var { tipo } => tipo.borrow().is_data(), + _ => false, + } + } + pub fn is_generic(&self) -> bool { match self { Type::App { args, .. } => { @@ -205,6 +213,7 @@ impl Type { match self { Self::Fn { args, .. } => Some(args.clone()), Self::App { args, .. } => Some(args.clone()), + Self::Var { tipo } => tipo.borrow().arg_types(), _ => None, } } @@ -467,6 +476,13 @@ impl TypeVar { } } + pub fn is_data(&self) -> bool { + match self { + Self::Link { tipo } => tipo.is_data(), + _ => false, + } + } + pub fn is_generic(&self) -> bool { match self { TypeVar::Generic { .. } => true, @@ -483,6 +499,13 @@ impl TypeVar { } } + pub fn arg_types(&self) -> Option>> { + match self { + Self::Link { tipo } => tipo.arg_types(), + _ => None, + } + } + pub fn get_inner_type(&self) -> Vec> { match self { Self::Link { tipo } => tipo.get_inner_types(), diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index edef1333..7166b7e0 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1,7 +1,7 @@ use super::Type; use crate::{ ast::{Annotation, BinOp, CallArg, Span, TodoKind, UntypedPattern}, - expr, + expr::{self, UntypedExpr}, format::Formatter, levenshtein, pretty::Documentable, @@ -172,20 +172,7 @@ You can use '{discard}' and numbers to distinguish between similar names. If you really meant to return that last expression, try to replace it with the following: {sample}"# - , sample = Formatter::new() - .expr(expr) - .to_pretty_string(70) - .lines() - .enumerate() - .map(|(ix, line)| { - if ix == 0 { - format!("╰─▶ {}", line.yellow()) - } else { - format!(" {line}").yellow().to_string() - } - }) - .collect::>() - .join("") + , sample = format_suggestion(expr) ))] LastExpressionIsAssignment { #[label("let-binding as last expression")] @@ -737,6 +724,15 @@ The best thing to do from here is to remove it."#))] location: Span, name: String, }, + + #[error("I discovered a type cast from Data without an annotation")] + #[diagnostic(code("illegal::type_cast"))] + #[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))] + CastDataNoAnn { + #[label] + location: Span, + value: UntypedExpr, + }, } impl Error { @@ -1034,14 +1030,12 @@ fn suggest_import_constructor() -> String { #[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)] pub enum Warning { - #[error("I found a todo left in the code.\n")] - #[diagnostic(help("You probably want to replace that one with real code... eventually."))] - #[diagnostic(code("todo"))] - Todo { - kind: TodoKind, + #[error("I found a record update using all fields; thus redundant.\n")] + #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] + #[diagnostic(code("record_update::all_fields"))] + AllFieldsRecordUpdate { #[label] location: Span, - tipo: Arc, }, #[error( @@ -1056,9 +1050,10 @@ pub enum Warning { location: Span, }, - #[error("I found a literal that is unused.\n")] - #[diagnostic(code("unused::literal"))] - UnusedLiteral { + #[error("I found a record update with no fields; effectively updating nothing.\n")] + #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] + #[diagnostic(code("record_update::no_fields"))] + NoFieldsRecordUpdate { #[label] location: Span, }, @@ -1070,29 +1065,25 @@ pub enum Warning { location: Span, }, - #[error("I found a record update with no fields; effectively updating nothing.\n")] - #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] - #[diagnostic(code("record_update::no_fields"))] - NoFieldsRecordUpdate { - #[label] + #[error("I found a when expression with a single clause.")] + #[diagnostic( + code("single_when_clause"), + help("Prefer using a {} binding like so...\n\n{}", "let".purple(), format_suggestion(sample)) + )] + SingleWhenClause { + #[label("use let")] location: Span, + sample: UntypedExpr, }, - #[error("I found a record update using all fields; thus redundant.\n")] - #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] - #[diagnostic(code("record_update::all_fields"))] - AllFieldsRecordUpdate { + #[error("I found a todo left in the code.\n")] + #[diagnostic(help("You probably want to replace that one with real code... eventually."))] + #[diagnostic(code("todo"))] + Todo { + kind: TodoKind, #[label] location: Span, - }, - - #[error("I discovered an unused type: '{}'.\n", name.purple())] - #[diagnostic(code("unused::type"))] - UnusedType { - #[label] - location: Span, - imported: bool, - name: String, + tipo: Arc, }, #[error("I discovered an unused constructor: '{}'.\n", name.purple())] @@ -1107,6 +1098,17 @@ pub enum Warning { name: String, }, + #[error("I discovered an unused imported module: '{}'.\n", name.purple())] + #[diagnostic(help( + "No big deal, but you might want to remove it to get rid of that warning." + ))] + #[diagnostic(code("unused::import::module"))] + UnusedImportedModule { + #[label] + location: Span, + name: String, + }, + #[error("I discovered an unused imported value: '{}'.\n", name.purple())] #[diagnostic(help( "No big deal, but you might want to remove it to get rid of that warning." @@ -1118,12 +1120,21 @@ pub enum Warning { name: String, }, - #[error("I discovered an unused imported module: '{}'.\n", name.purple())] + #[error("I found a literal that is unused.\n")] + #[diagnostic(code("unused::literal"))] + UnusedLiteral { + #[label] + location: Span, + }, + + #[error("I found an unused private function: '{}'.\n", name.purple())] #[diagnostic(help( - "No big deal, but you might want to remove it to get rid of that warning." + "Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ + Otherwise, you might want to get rid of it altogether." + , keyword_pub = "pub".bright_blue() ))] - #[diagnostic(code("unused::import::module"))] - UnusedImportedModule { + #[diagnostic(code("unused::function"))] + UnusedPrivateFunction { #[label] location: Span, name: String, @@ -1142,16 +1153,12 @@ pub enum Warning { name: String, }, - #[error("I found an unused private function: '{}'.\n", name.purple())] - #[diagnostic(help( - "Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ - Otherwise, you might want to get rid of it altogether." - , keyword_pub = "pub".bright_blue() - ))] - #[diagnostic(code("unused::function"))] - UnusedPrivateFunction { + #[error("I discovered an unused type: '{}'.\n", name.purple())] + #[diagnostic(code("unused::type"))] + UnusedType { #[label] location: Span, + imported: bool, name: String, }, @@ -1187,3 +1194,20 @@ pub enum UnknownRecordFieldSituation { /// This unknown record field is being called as a function. i.e. `record.field()` FunctionCall, } + +fn format_suggestion(sample: &UntypedExpr) -> String { + Formatter::new() + .expr(sample) + .to_pretty_string(70) + .lines() + .enumerate() + .map(|(ix, line)| { + if ix == 0 { + format!("╰─▶ {}", line.yellow()) + } else { + format!(" {line}").yellow().to_string() + } + }) + .collect::>() + .join("") +} diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 45f7132a..3e48878f 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -807,8 +807,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { annotation: &Option, location: Span, ) -> Result { - let value = self.in_new_scope(|value_typer| value_typer.infer(value))?; - let mut value_typ = value.tipo(); + let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?; + let mut value_typ = typed_value.tipo(); // Check that any type annotation is accurate. let pattern = if let Some(ann) = annotation { @@ -819,7 +819,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { self.unify( ann_typ.clone(), value_typ.clone(), - value.type_defining_location(), + typed_value.type_defining_location(), )?; value_typ = ann_typ.clone(); @@ -831,6 +831,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> { Some(ann_typ), )? } else { + if value_typ.is_data() { + return Err(Error::CastDataNoAnn { + location, + value: UntypedExpr::Assignment { + location, + value: value.into(), + pattern, + kind, + annotation: Some(Annotation::Constructor { + location: Span::empty(), + module: None, + name: "Type".to_string(), + arguments: vec![], + }), + }, + }); + } + // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( pattern, @@ -860,7 +878,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { tipo: value_typ, kind, pattern, - value: Box::new(value), + value: Box::new(typed_value), }) } @@ -1906,6 +1924,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> { clauses: Vec, location: Span, ) -> Result { + // if there is only one clause we want to present a warning + // that suggests that a `let` binding should be used instead. + if clauses.len() == 1 { + self.environment.warnings.push(Warning::SingleWhenClause { + location: clauses[0].pattern[0].location(), + sample: UntypedExpr::Assignment { + location: Span::empty(), + value: Box::new(subjects[0].clone()), + pattern: clauses[0].pattern[0].clone(), + kind: AssignmentKind::Let, + annotation: None, + }, + }); + } + let subjects_count = subjects.len(); let mut typed_subjects = Vec::with_capacity(subjects_count); diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 13f34b18..bce6585e 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1,16 +1,13 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, - vec, -}; +use std::sync::Arc; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ builder::{ - self, apply_wrap, choose_list, constr_index_exposer, delayed_choose_list, - delayed_if_else, if_else, repeat_tail_list, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, + self, apply_wrap, assert_on_list, choose_list, constr_index_exposer, + delayed_choose_list, delayed_if_else, final_wrapper, if_else, repeat_tail_list, + ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, }, Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType, }, @@ -28,47 +25,49 @@ use crate::{ builder::{ check_replaceable_opaque_type, check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, - get_generics_and_type, handle_clause_guard, handle_func_deps_ir, handle_recursion_ir, + get_generics_and_type, handle_clause_guard, handle_func_dependencies_ir, handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, - replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, FuncComponents, + replace_opaque_type, wrap_validator_args, AssignmentProperties, ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey, }, builtins::bool, expr::TypedExpr, tipo::{ - self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, + ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, }; pub struct CodeGenerator<'a> { - defined_functions: HashMap, - functions: &'a HashMap, - // type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, - data_types: &'a HashMap, - module_types: &'a HashMap, + defined_functions: IndexMap, + functions: &'a IndexMap, + // type_aliases: &'a IndexMap<(String, String), &'a TypeAlias>>, + data_types: &'a IndexMap, + module_types: &'a IndexMap, id_gen: IdGenerator, needs_field_access: bool, - zero_arg_functions: HashMap>, + used_data_assert_on_list: bool, + zero_arg_functions: IndexMap>, } impl<'a> CodeGenerator<'a> { pub fn new( - functions: &'a HashMap, - // type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, - data_types: &'a HashMap, - module_types: &'a HashMap, + functions: &'a IndexMap, + // type_aliases: &'a IndexMap<(String, String), &'a TypeAlias>>, + data_types: &'a IndexMap, + module_types: &'a IndexMap, ) -> Self { CodeGenerator { - defined_functions: HashMap::new(), + defined_functions: IndexMap::new(), functions, // type_aliases, data_types, module_types, id_gen: IdGenerator::new(), needs_field_access: false, - zero_arg_functions: HashMap::new(), + used_data_assert_on_list: false, + zero_arg_functions: IndexMap::new(), } } @@ -97,13 +96,19 @@ impl<'a> CodeGenerator<'a> { // Wrap the validator body if ifThenElse term unit error term = if wrap_as_validator { - builder::final_wrapper(term) + final_wrapper(term) } else { term }; term = wrap_validator_args(term, arguments); + term = if wrap_as_validator || self.used_data_assert_on_list { + assert_on_list(term) + } else { + term + }; + let mut program = Program { version: (1, 0, 0), term, @@ -247,6 +252,7 @@ impl<'a> CodeGenerator<'a> { ir_stack.push(Air::Call { scope: scope.clone(), count: args.len(), + tipo: tipo.clone(), }); let mut scope_fun = scope.clone(); scope_fun.push(self.id_gen.next()); @@ -289,6 +295,8 @@ impl<'a> CodeGenerator<'a> { let mut value_scope = scope.clone(); value_scope.push(self.id_gen.next()); + let value_is_data = value.tipo().is_data(); + self.build_ir(value, &mut value_vec, value_scope); self.assignment_ir( @@ -296,7 +304,10 @@ impl<'a> CodeGenerator<'a> { &mut pattern_vec, &mut value_vec, tipo, - *kind, + AssignmentProperties { + value_is_data, + kind: *kind, + }, scope, ); @@ -310,104 +321,129 @@ impl<'a> CodeGenerator<'a> { // assuming one subject at the moment let subject = subjects[0].clone(); + if clauses.len() <= 1 { + let mut value_vec: Vec = vec![]; + let mut pattern_vec: Vec = vec![]; + let mut subject_vec: Vec = vec![]; - let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) { - rearrange_clauses(clauses.clone()) - } else { - clauses.clone() - }; + self.build_ir(&clauses[0].then, &mut value_vec, scope.clone()); - if let Some((last_clause, clauses)) = clauses.split_last() { - let mut pattern_vec = vec![]; + self.build_ir(&subject, &mut value_vec, scope.clone()); - let mut clause_properties = ClauseProperties::init( - &subject.tipo(), - constr_var.clone(), - subject_name.clone(), - ); - - self.handle_each_clause( + self.assignment_ir( + &clauses[0].pattern[0], &mut pattern_vec, - &mut clause_properties, - clauses, + &mut subject_vec, &subject.tipo(), - scope.clone(), + AssignmentProperties { + value_is_data: false, + kind: AssignmentKind::Let, + }, + scope, ); - let last_pattern = &last_clause.pattern[0]; - - let mut final_scope = scope.clone(); - - final_scope.push(self.id_gen.next()); - - pattern_vec.push(Air::Finally { - scope: final_scope.clone(), - }); - - let mut final_clause_vec = vec![]; - - self.build_ir( - &last_clause.then, - &mut final_clause_vec, - final_scope.clone(), - ); - - self.when_ir( - last_pattern, - &mut pattern_vec, - &mut final_clause_vec, - &subject.tipo(), - &mut clause_properties, - final_scope, - ); - - if *clause_properties.needs_constr_var() { - ir_stack.push(Air::Lam { - scope: scope.clone(), - name: constr_var.clone(), - }); - - self.build_ir(&subject, ir_stack, scope.clone()); - - ir_stack.push(Air::When { - scope: scope.clone(), - subject_name, - tipo: subject.tipo(), - }); - - let mut scope = scope; - scope.push(self.id_gen.next()); - - ir_stack.push(Air::Var { - scope, - constructor: ValueConstructor::public( - subject.tipo(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_var, - variant_name: String::new(), - }) - } else { - ir_stack.push(Air::When { - scope: scope.clone(), - subject_name, - tipo: subject.tipo(), - }); - - let mut scope = scope; - scope.push(self.id_gen.next()); - - self.build_ir(&subject, ir_stack, scope); - } - ir_stack.append(&mut pattern_vec); - }; + ir_stack.append(&mut value_vec); + } else { + let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) { + rearrange_clauses(clauses.clone()) + } else { + clauses.clone() + }; + + if let Some((last_clause, clauses)) = clauses.split_last() { + let mut pattern_vec = vec![]; + + let mut clause_properties = ClauseProperties::init( + &subject.tipo(), + constr_var.clone(), + subject_name.clone(), + ); + + self.handle_each_clause( + &mut pattern_vec, + &mut clause_properties, + clauses, + &subject.tipo(), + scope.clone(), + ); + + let last_pattern = &last_clause.pattern[0]; + + let mut final_scope = scope.clone(); + + final_scope.push(self.id_gen.next()); + + pattern_vec.push(Air::Finally { + scope: final_scope.clone(), + }); + + let mut final_clause_vec = vec![]; + + self.build_ir( + &last_clause.then, + &mut final_clause_vec, + final_scope.clone(), + ); + + self.when_ir( + last_pattern, + &mut pattern_vec, + &mut final_clause_vec, + &subject.tipo(), + &mut clause_properties, + final_scope, + ); + + if *clause_properties.needs_constr_var() { + ir_stack.push(Air::Let { + scope: scope.clone(), + name: constr_var.clone(), + }); + + self.build_ir(&subject, ir_stack, scope.clone()); + + ir_stack.push(Air::When { + scope: scope.clone(), + subject_name, + tipo: subject.tipo(), + }); + + let mut scope = scope; + scope.push(self.id_gen.next()); + + ir_stack.push(Air::Var { + scope, + constructor: ValueConstructor::public( + subject.tipo(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var, + variant_name: String::new(), + }) + } else { + ir_stack.push(Air::When { + scope: scope.clone(), + subject_name, + tipo: subject.tipo(), + }); + + let mut scope = scope; + scope.push(self.id_gen.next()); + + self.build_ir(&subject, ir_stack, scope); + } + + ir_stack.append(&mut pattern_vec); + } + } } TypedExpr::If { branches, final_else, + tipo, .. } => { let mut if_ir = vec![]; @@ -419,10 +455,12 @@ impl<'a> CodeGenerator<'a> { if index == 0 { if_ir.push(Air::If { scope: scope.clone(), + tipo: tipo.clone(), }); } else { if_ir.push(Air::If { scope: branch_scope.clone(), + tipo: tipo.clone(), }); } self.build_ir(&branch.condition, &mut if_ir, branch_scope.clone()); @@ -458,7 +496,9 @@ impl<'a> CodeGenerator<'a> { tipo, .. } => match constructor { - ModuleValueConstructor::Record { .. } => todo!(), + ModuleValueConstructor::Record { .. } => { + todo!("Records from modules not yet implemented.") + } ModuleValueConstructor::Fn { name, module, .. } => { let func = self.functions.get(&FunctionAccessKey { module_name: module_name.clone(), @@ -511,7 +551,9 @@ impl<'a> CodeGenerator<'a> { tipo: tipo.clone(), }); } - TypedExpr::RecordUpdate { spread, args, .. } => { + TypedExpr::RecordUpdate { + spread, args, tipo, .. + } => { let mut update_ir = vec![]; let mut spread_scope = scope.clone(); let mut index_types = vec![]; @@ -536,6 +578,7 @@ impl<'a> CodeGenerator<'a> { scope, highest_index, indices: index_types, + tipo: tipo.clone(), }); ir_stack.append(&mut update_ir); @@ -677,7 +720,14 @@ impl<'a> CodeGenerator<'a> { current_index, .. } => { - let current_clause_index = *current_index; + let current_clause_index = + if let Pattern::List { elements, .. } = &clause.pattern[0] { + elements.len() + } else { + unreachable!() + }; + + let prev_index = *current_index; let subject_name = if current_clause_index == 0 { original_subject_name.clone() @@ -697,20 +747,36 @@ impl<'a> CodeGenerator<'a> { let next_tail = if index == clauses.len() - 1 { None } else { - Some(format!("__tail_{}", current_clause_index)) + let next_list_size = if let Pattern::List { elements, .. } = + &clauses[index + 1].pattern[0] + { + elements.len() + } else { + unreachable!() + }; + + if next_list_size == current_clause_index { + None + } else { + Some(format!("__tail_{}", current_clause_index)) + } }; - ir_stack.push(Air::ListClause { - scope, - tipo: subject_type.clone(), - tail_name: subject_name, - next_tail_name: next_tail, - complex_clause: *clause_properties.is_complex_clause(), - }); + if current_clause_index as i64 == prev_index { + ir_stack.push(Air::WrapClause { scope }); + } else { + ir_stack.push(Air::ListClause { + scope, + tipo: subject_type.clone(), + tail_name: subject_name, + next_tail_name: next_tail, + complex_clause: *clause_properties.is_complex_clause(), + }); + } match clause_properties { ClauseProperties::ListClause { current_index, .. } => { - *current_index += 1; + *current_index = current_clause_index as i64; } _ => unreachable!(), } @@ -762,7 +828,7 @@ impl<'a> CodeGenerator<'a> { fn when_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, @@ -778,12 +844,12 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(values); } - Pattern::String { .. } => todo!(), + Pattern::String { .. } => todo!("String matching in whens not yet implemented"), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); - pattern_vec.push(Air::Lam { + pattern_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); @@ -801,10 +867,10 @@ impl<'a> CodeGenerator<'a> { }); pattern_vec.append(values); } - Pattern::VarUsage { .. } => todo!(), + Pattern::VarUsage { .. } => unreachable!(), Pattern::Assign { name, pattern, .. } => { let mut new_vec = vec![]; - new_vec.push(Air::Lam { + new_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); @@ -833,7 +899,7 @@ impl<'a> CodeGenerator<'a> { ); } Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } Pattern::List { elements, tail, .. } => { @@ -846,7 +912,7 @@ impl<'a> CodeGenerator<'a> { } *clause_properties.needs_constr_var() = false; - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -960,7 +1026,7 @@ impl<'a> CodeGenerator<'a> { fn when_recursive_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, clause_properties: &mut ClauseProperties, @@ -968,13 +1034,13 @@ impl<'a> CodeGenerator<'a> { scope: Vec, ) { match pattern { - Pattern::Int { .. } => todo!(), - Pattern::String { .. } => todo!(), - Pattern::Var { .. } => todo!(), - Pattern::VarUsage { .. } => todo!(), - Pattern::Assign { .. } => todo!(), + Pattern::Int { .. } => unreachable!(), + Pattern::String { .. } => unreachable!(), + Pattern::Var { .. } => unreachable!(), + Pattern::VarUsage { .. } => unreachable!(), + Pattern::Assign { .. } => todo!("Nested assign not yet implemented"), Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } @@ -1002,7 +1068,7 @@ impl<'a> CodeGenerator<'a> { tail_name = name.clone(); } Pattern::Discard { .. } => {} - _ => todo!(), + _ => unreachable!("Patterns in tail of list should not allow this"), } } @@ -1069,7 +1135,7 @@ impl<'a> CodeGenerator<'a> { PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), }; - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let label = constructor_type.arguments[index].label.clone().unwrap(); @@ -1109,7 +1175,6 @@ impl<'a> CodeGenerator<'a> { if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, indices: arguments_index .iter() .map(|(label, var_name, index)| { @@ -1118,10 +1183,11 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } else { - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); @@ -1146,7 +1212,6 @@ impl<'a> CodeGenerator<'a> { if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, indices: arguments_index .iter() .map(|(name, index)| { @@ -1156,6 +1221,7 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(), scope, + check_last_item: false, }); } } @@ -1207,7 +1273,7 @@ impl<'a> CodeGenerator<'a> { let pattern_type = &tipo.get_inner_types()[index]; - pattern_vec.push(Air::Lam { + pattern_vec.push(Air::Let { scope: scope.clone(), name: new_name.clone(), }); @@ -1242,7 +1308,7 @@ impl<'a> CodeGenerator<'a> { fn nested_pattern_ir_and_label( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, pattern_type: &Arc, scope: Vec, @@ -1263,7 +1329,7 @@ impl<'a> CodeGenerator<'a> { inverse: false, }); - pattern_vec.push(Air::Discard { scope }); + pattern_vec.push(Air::Void { scope }); } else { for (index, _) in elements.iter().enumerate() { let prev_tail_name = if index == 0 { @@ -1277,24 +1343,18 @@ impl<'a> CodeGenerator<'a> { needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), - current_index: index, + current_index: index as i64, }; let tail_name = format!("{}_{}", new_tail_name, index); if elements.len() - 1 == index { if tail.is_some() { - let tail_name = match *tail.clone().unwrap() { - Pattern::Var { name, .. } => name, - Pattern::Discard { .. } => "_".to_string(), - _ => unreachable!(), - }; - pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: prev_tail_name, - next_tail_name: Some(tail_name), + next_tail_name: None, inverse: true, }); @@ -1315,7 +1375,7 @@ impl<'a> CodeGenerator<'a> { inverse: true, }); - pattern_vec.push(Air::Discard { + pattern_vec.push(Air::Void { scope: scope.clone(), }); @@ -1363,7 +1423,6 @@ impl<'a> CodeGenerator<'a> { } } - // self.when_recursive_ir(a); Some(item_name) } a @ Pattern::Constructor { @@ -1409,7 +1468,7 @@ impl<'a> CodeGenerator<'a> { needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), - defined_tuple_indices: HashSet::new(), + defined_tuple_indices: IndexSet::new(), }; let mut inner_pattern_vec = vec![]; @@ -1435,7 +1494,7 @@ impl<'a> CodeGenerator<'a> { scope, tipo: pattern_type.clone(), indices: defined_indices, - predefined_indices: HashSet::new(), + predefined_indices: IndexSet::new(), subject_name: clause_properties.original_subject_name().to_string(), count: elems.len(), complex_clause: false, @@ -1445,33 +1504,61 @@ impl<'a> CodeGenerator<'a> { Some(item_name) } - _ => todo!(), + Pattern::Assign { .. } => todo!("Nested assign is not yet done"), + Pattern::Int { .. } => unimplemented!(), + Pattern::String { .. } => unimplemented!(), + Pattern::VarUsage { .. } => unreachable!(), } } fn assignment_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, - kind: AssignmentKind, + assignment_properties: AssignmentProperties, scope: Vec, ) { + if assignment_properties.value_is_data && !tipo.is_data() && !pattern.is_discard() { + value_vec.insert( + 0, + Air::UnWrapData { + scope: scope.clone(), + tipo: tipo.clone().into(), + }, + ); + } match pattern { Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Var { name, .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: name.clone(), - scope, + scope: scope.clone(), }); pattern_vec.append(value_vec); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + let mut assert_vec = vec![]; + self.recursive_assert_pattern( + pattern, + &mut assert_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + pattern_vec.append(&mut assert_vec); + } } - Pattern::VarUsage { .. } => todo!(), - Pattern::Assign { .. } => todo!(), + Pattern::VarUsage { .. } => unreachable!(), + Pattern::Assign { .. } => todo!("Assign not yet implemented yet"), Pattern::Discard { .. } => { - pattern_vec.push(Air::Assignment { + pattern_vec.push(Air::Let { name: "_".to_string(), scope, }); @@ -1479,85 +1566,87 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(value_vec); } list @ Pattern::List { .. } => { - self.pattern_ir(list, pattern_vec, value_vec, tipo, scope); - } - Pattern::Constructor { - name: constr_name, - tipo, - .. - } => { - let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo); - - let (index, _) = data_type - .unwrap() - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == constr_name) - .unwrap(); - match kind { - AssignmentKind::Let => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); - } - AssignmentKind::Assert => { - let name_id = self.id_gen.next(); - pattern_vec.push(Air::Lam { - scope: scope.clone(), - name: format!("__constr_{}", name_id), - }); - - pattern_vec.append(value_vec); - - pattern_vec.push(Air::Assert { - scope: scope.clone(), - constr_index: index, - }); - - pattern_vec.push(Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__constr_{}", name_id), - variant_name: String::new(), - }); - - self.pattern_ir( - pattern, - pattern_vec, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: format!("__constr_{}", name_id), - variant_name: String::new(), - }], - tipo, - scope, - ); - } - AssignmentKind::Check => todo!(), + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + list, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + list, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); } } - Pattern::Tuple { .. } => { - self.pattern_ir(pattern, pattern_vec, value_vec, tipo, scope); + constr @ Pattern::Constructor { .. } => { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + constr, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + constr, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } + } + tuple @ Pattern::Tuple { .. } => { + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + tuple, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } else { + self.pattern_ir( + tuple, + pattern_vec, + value_vec, + tipo, + assignment_properties, + scope, + ); + } } } } fn pattern_ir( &mut self, - pattern: &Pattern>, + pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, + assignment_properties: AssignmentProperties, scope: Vec, ) { match pattern { @@ -1566,21 +1655,19 @@ impl<'a> CodeGenerator<'a> { Pattern::Var { .. } => todo!(), Pattern::VarUsage { .. } => todo!(), Pattern::Assign { .. } => todo!(), - Pattern::Discard { .. } => { - pattern_vec.push(Air::Discard { scope }); - - pattern_vec.append(values); - } + Pattern::Discard { .. } => todo!(), Pattern::List { elements, tail, .. } => { let mut elements_vec = vec![]; - let mut names = vec![]; + for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } - a @ Pattern::List { .. } => { + a @ (Pattern::List { .. } + | Pattern::Constructor { .. } + | Pattern::Tuple { .. }) => { let mut var_vec = vec![]; let item_name = format!("list_item_id_{}", self.id_gen.next()); names.push(item_name.clone()); @@ -1606,10 +1693,17 @@ impl<'a> CodeGenerator<'a> { &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], + assignment_properties.clone(), scope.clone(), ); } - _ => todo!(), + Pattern::Int { .. } => todo!(), + Pattern::String { .. } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + Pattern::Discard { .. } => { + names.push("_".to_string()); + } } } @@ -1621,230 +1715,883 @@ impl<'a> CodeGenerator<'a> { } } - pattern_vec.push(Air::ListAccessor { - names, - tail: tail.is_some(), - scope, - tipo: tipo.clone().into(), - }); + if matches!(&assignment_properties.kind, AssignmentKind::Assert) { + } else { + pattern_vec.push(Air::ListAccessor { + names, + tail: tail.is_some(), + scope, + tipo: tipo.clone().into(), + check_last_item: false, + }); + } pattern_vec.append(values); pattern_vec.append(&mut elements_vec); } Pattern::Constructor { - is_record, - name: constr_name, arguments, constructor, tipo, .. } => { - let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); - let (_, constructor_type) = data_type - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == constr_name) - .unwrap(); let mut nested_pattern = vec![]; - if *is_record { - let field_map = match constructor { - tipo::PatternConstructor::Record { field_map, .. } => { - field_map.clone().unwrap() - } - }; - let mut type_map: HashMap> = HashMap::new(); + let field_map = match constructor { + PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), + }; - for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { - let label = constructor_type.arguments[index].label.clone().unwrap(); - let field_type = arg.clone(); + let mut type_map: IndexMap> = IndexMap::new(); - type_map.insert(label, field_type); - } + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } - let arguments_index = arguments - .iter() - .map(|item| { - let label = item.label.clone().unwrap_or_default(); - let field_index = - field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); - let (discard, var_name) = match &item.value { - Pattern::Var { name, .. } => (false, name.clone()), - Pattern::Discard { .. } => (true, "".to_string()), - Pattern::List { .. } => todo!(), - a @ Pattern::Constructor { - tipo, - name: constr_name, - .. - } => { - let id = self.id_gen.next(); - let constr_name = format!("{constr_name}_{id}"); - self.pattern_ir( - a, - &mut nested_pattern, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_name.clone(), - variant_name: String::new(), - }], - tipo, - scope.clone(), - ); + let arguments_index = arguments + .iter() + .filter_map(|item| { + let label = item.label.clone().unwrap_or_default(); + let field_index = field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); + self.extract_arg_and_index( + &item.value, + *field_index, + &mut nested_pattern, + type_map.get(field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); - (false, constr_name) - } - _ => todo!(), - }; - - (label, var_name, *field_index, discard) - }) - .filter(|(_, _, _, discard)| !discard) - .sorted_by(|item1, item2| item1.2.cmp(&item2.2)) - .collect::>(); - - if !arguments_index.is_empty() { - pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, - indices: arguments_index - .iter() - .map(|(label, var_name, index, _)| { - let field_type = type_map.get(label).unwrap(); - (*index, var_name.clone(), field_type.clone()) - }) - .collect_vec(), - scope, - }); - } - } else { - let mut type_map: HashMap> = HashMap::new(); - - for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { - let field_type = arg.clone(); - - type_map.insert(index, field_type); - } - - let arguments_index = arguments - .iter() - .enumerate() - .map(|(index, item)| { - let (discard, var_name) = match &item.value { - Pattern::Var { name, .. } => (false, name.clone()), - Pattern::Discard { .. } => (true, "".to_string()), - Pattern::List { .. } => todo!(), - a @ Pattern::Constructor { - tipo, - name: constr_name, - .. - } => { - let id = self.id_gen.next(); - let constr_name = format!("{constr_name}_{id}"); - self.pattern_ir( - a, - &mut nested_pattern, - &mut vec![Air::Var { - scope: scope.clone(), - constructor: ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: constr_name.clone(), - variant_name: String::new(), - }], - tipo, - scope.clone(), - ); - - (false, constr_name) - } - _ => todo!(), - }; - - (var_name, index, discard) - }) - .filter(|(_, _, discard)| !discard) - .collect::>(); - - if !arguments_index.is_empty() { - pattern_vec.push(Air::FieldsExpose { - count: arguments_index.len() + 2, - indices: arguments_index - .iter() - .map(|(name, index, _)| { - let field_type = type_map.get(index).unwrap(); - - (*index, name.clone(), field_type.clone()) - }) - .collect_vec(), - scope, - }); - } + if !arguments_index.is_empty() { + pattern_vec.push(Air::FieldsExpose { + indices: arguments_index + .iter() + .map(|(var_name, index)| { + let field_type = type_map.get(index).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) + .collect_vec(), + scope, + check_last_item: false, + }); } pattern_vec.append(values); pattern_vec.append(&mut nested_pattern); } Pattern::Tuple { elems, .. } => { - let mut elements_vec = vec![]; + let mut nested_pattern = vec![]; + let mut type_map: IndexMap> = IndexMap::new(); + for (index, arg) in tipo.get_inner_types().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + + let arguments_index = elems + .iter() + .enumerate() + .filter_map(|(tuple_index, item)| { + self.extract_arg_and_index( + item, + tuple_index, + &mut nested_pattern, + type_map.get(&tuple_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + if !arguments_index.is_empty() { + pattern_vec.push(Air::TupleAccessor { + scope, + names: arguments_index + .into_iter() + .map(|(item, _)| item) + .collect_vec(), + tipo: tipo.clone().into(), + check_last_item: true, + }); + } + + pattern_vec.append(values); + + pattern_vec.append(&mut nested_pattern); + } + } + } + + pub fn recursive_assert_pattern( + &mut self, + pattern: &Pattern>, + pattern_vec: &mut Vec, + value_vec: &mut Vec, + tipo: &Type, + assignment_properties: AssignmentProperties, + scope: Vec, + ) { + match pattern { + Pattern::Int { .. } => unreachable!(), + Pattern::String { .. } => unreachable!(), + Pattern::Var { name, .. } => { + pattern_vec.push(Air::Let { + scope: scope.clone(), + name: name.clone(), + }); + + pattern_vec.append(value_vec); + + self.recursive_assert_tipo(tipo, pattern_vec, name, scope); + } + Pattern::VarUsage { .. } => todo!(), + 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![]; - for element in elems { + for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } - Pattern::Discard { .. } => { - names.push("_".to_string()); + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + l @ (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(), + }], + inner_list_type, + assignment_properties.clone(), + scope.clone(), + ); } - a @ Pattern::List { .. } => { - let mut var_vec = vec![]; - let item_name = format!("list_item_id_{}", self.id_gen.next()); - names.push(item_name.clone()); - var_vec.push(Air::Var { + _ => {} + } + } + + let name = if let Some(tail) = tail { + match &**tail { + Pattern::Var { name, .. } => name.clone(), + _ => format!("__tail_{}", self.id_gen.next()), + } + } else { + format!("__tail_{}", self.id_gen.next()) + }; + + self.recursive_assert_tipo(tipo, &mut assert_list_vec, &name, scope.clone()); + + pattern_vec.push(Air::ListAccessor { + scope, + tipo: tipo.clone().into(), + names, + tail: true, + check_last_item: false, + }); + + pattern_vec.append(value_vec); + + pattern_vec.append(&mut assert_list_vec); + } + Pattern::Constructor { + arguments, + constructor, + name: constr_name, + tipo, + .. + } => { + let mut nested_pattern = vec![]; + + let field_map = match constructor { + PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), + }; + + let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); + + let (index, data_type_constr) = data_type + .constructors + .iter() + .enumerate() + .find(|(_, constr)| &constr.name == constr_name) + .unwrap(); + + let mut type_map: IndexMap> = IndexMap::new(); + + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + + let arguments_index = arguments + .iter() + .filter_map(|item| { + let label = item.label.clone().unwrap_or_default(); + let field_index = field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); + self.extract_arg_and_index( + &item.value, + *field_index, + &mut nested_pattern, + type_map.get(field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + let total_fields = data_type_constr.arguments.len(); + let mut final_args = vec![]; + let mut current_index = 0; + for index in 0..total_fields { + if arguments_index.get(current_index).is_some() + && arguments_index[current_index].1 == index + { + final_args.push(arguments_index.get(current_index).unwrap().clone()); + current_index += 1; + } else { + let id_next = self.id_gen.next(); + final_args.push((format!("__field_{index}_{}", id_next), index)); + self.recursive_assert_tipo( + 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(), + }); + + pattern_vec.append(value_vec); + + pattern_vec.push(Air::AssertConstr { + scope: scope.clone(), + constr_index: index, + }); + + pattern_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var.clone(), + variant_name: String::new(), + }); + + if !arguments_index.is_empty() { + pattern_vec.push(Air::FieldsExpose { + indices: final_args + .iter() + .map(|(var_name, index)| { + let field_type = type_map.get(index).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) + .collect_vec(), + scope: scope.clone(), + check_last_item: true, + }); + + pattern_vec.push(Air::Var { + scope, + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_var, + variant_name: String::new(), + }); + } + + pattern_vec.append(&mut nested_pattern); + } + Pattern::Tuple { elems, .. } => { + let mut type_map: IndexMap> = IndexMap::new(); + + for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { + let field_type = arg.clone(); + type_map.insert(index, field_type); + } + let mut nested_pattern = vec![]; + let arguments_index = elems + .iter() + .enumerate() + .filter_map(|(index, item)| { + let field_index = index; + self.extract_arg_and_index( + item, + field_index, + &mut nested_pattern, + type_map.get(&field_index).unwrap(), + &assignment_properties, + &scope, + ) + }) + .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) + .collect::>(); + + let total_fields = type_map.len(); + let mut final_args = vec![]; + let mut current_index = 0; + for index in 0..total_fields { + if arguments_index.get(current_index).is_some() + && arguments_index[current_index].1 == index + { + final_args.push(arguments_index.get(index).unwrap().clone()); + current_index += 1; + } else { + let id_next = self.id_gen.next(); + final_args.push((format!("__tuple_{index}_{}", id_next), index)); + self.recursive_assert_tipo( + type_map.get(&index).unwrap(), + &mut nested_pattern, + &format!("__tuple_{index}_{}", id_next), + scope.clone(), + ) + } + } + + if !final_args.is_empty() { + pattern_vec.push(Air::TupleAccessor { + scope, + names: final_args.into_iter().map(|(item, _)| item).collect_vec(), + tipo: tipo.clone().into(), + check_last_item: true, + }); + } + + pattern_vec.append(value_vec); + + pattern_vec.append(&mut nested_pattern); + } + } + } + + fn recursive_assert_tipo( + &mut self, + tipo: &Type, + assert_vec: &mut Vec, + name: &str, + scope: Vec, + ) { + if tipo.is_bool() + || tipo.is_bytearray() + || tipo.is_int() + || tipo.is_string() + || tipo.is_void() + || tipo.get_generic().is_some() + { + } else if tipo.is_map() { + self.used_data_assert_on_list = true; + let new_id = self.id_gen.next(); + let id_pair = (self.id_gen.next(), self.id_gen.next()); + let inner_list_type = &tipo.get_inner_types()[0]; + let inner_pair_types = inner_list_type.get_inner_types(); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + 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().into(), + 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![ + format!("__pair_fst_{}", id_pair.0), + format!("__pair_snd_{}", id_pair.1), + ], + tipo: inner_list_type.clone(), + check_last_item: false, + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: format!("__pair_{}", new_id), + variant_name: String::new(), + }); + + self.recursive_assert_tipo( + &inner_pair_types[0], + assert_vec, + &format!("__pair_fst_{}", id_pair.0), + scope.clone(), + ); + + self.recursive_assert_tipo( + &inner_pair_types[1], + assert_vec, + &format!("__pair_snd_{}", id_pair.1), + scope.clone(), + ); + + assert_vec.push(Air::Void { scope }); + } 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::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + 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().into(), + 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().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: format!("__list_item_{}", new_id), + variant_name: String::new(), + }); + + self.recursive_assert_tipo( + inner_list_type, + assert_vec, + &format!("__list_item_{}", new_id), + scope.clone(), + ); + + assert_vec.push(Air::Void { scope }); + } 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().into(), + check_last_item: true, + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + 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(), + ); + } + } else { + let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); + let new_id = self.id_gen.next(); + + assert_vec.push(Air::Call { + scope: scope.clone(), + count: 2, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::Builtin { + scope: scope.clone(), + func: DefaultFunction::ChooseUnit, + tipo: tipo.clone().into(), + }); + + assert_vec.push(Air::When { + scope: scope.clone(), + tipo: tipo.clone().into(), + subject_name: format!("__subject_{}", new_id), + }); + + assert_vec.push(Air::Var { + scope: scope.clone(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + for (index, constr) in data_type.constructors.iter().enumerate() { + let arg_indices = constr + .arguments + .iter() + .enumerate() + .map(|(index, arg)| { + let arg_name = arg + .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().into(), + subject_name: format!("__subject_{}", new_id), + complex_clause: false, + }); + + assert_vec.push(Air::Int { + scope: scope.clone(), + value: index.to_string(), + }); + + 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().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: name.to_owned(), + variant_name: String::new(), + }); + + for (_, name, tipo) in arg_indices { + self.recursive_assert_tipo(&tipo, assert_vec, &name, scope.clone()); + } + + assert_vec.push(Air::Void { + scope: scope.clone(), + }); + } + + assert_vec.push(Air::ErrorTerm { + scope, + tipo: tipo.clone().into(), + label: Some("Constr index did not match any type variant".to_string()), + }); + } + } + + fn extract_arg_and_index( + &mut self, + item: &Pattern>, + field_index: usize, + nested_pattern: &mut Vec, + tipo: &Type, + assignment_properties: &AssignmentProperties, + scope: &[u64], + ) -> Option<(String, usize)> { + { + let (discard, var_name) = match item { + Pattern::Var { name, .. } => (false, name.clone()), + Pattern::Discard { .. } => (true, "".to_string()), + a @ Pattern::List { .. } => { + let id = self.id_gen.next(); + let list_name = format!("__list_{id}"); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), constructor: ValueConstructor::public( - Type::App { - public: true, - module: String::new(), - name: String::new(), - args: vec![], - } - .into(), + tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), - name: item_name, - scope: scope.clone(), + name: list_name.clone(), variant_name: String::new(), - }); - self.pattern_ir( - a, - &mut elements_vec, - &mut var_vec, - &tipo.get_inner_types()[0], - scope.clone(), - ); - } - _ => todo!(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: list_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); } - } - pattern_vec.push(Air::TupleAccessor { - names, - scope, - tipo: tipo.clone().into(), - }); - pattern_vec.append(values); - pattern_vec.append(&mut elements_vec); + (false, list_name) + } + a @ Pattern::Constructor { + tipo, + name: constr_name, + .. + } => { + let id = self.id_gen.next(); + let constr_name = format!("{constr_name}_{id}"); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: constr_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } + + (false, constr_name) + } + a @ Pattern::Tuple { .. } => { + let id = self.id_gen.next(); + let tuple_name = format!("__tuple_name_{id}"); + + if matches!(assignment_properties.kind, AssignmentKind::Assert) + && assignment_properties.value_is_data + && !tipo.is_data() + { + self.recursive_assert_pattern( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: tuple_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } else { + self.pattern_ir( + a, + nested_pattern, + &mut vec![Air::Var { + scope: scope.to_owned(), + constructor: ValueConstructor::public( + tipo.clone().into(), + ValueConstructorVariant::LocalVariable { + location: Span::empty(), + }, + ), + name: tuple_name.clone(), + variant_name: String::new(), + }], + tipo, + assignment_properties.clone(), + scope.to_owned(), + ); + } + + (false, tuple_name) + } + Pattern::Int { .. } => todo!(), + Pattern::String { .. } => todo!(), + Pattern::VarUsage { .. } => todo!(), + Pattern::Assign { .. } => todo!(), + }; + + if discard { + None + } else { + Some((var_name, field_index)) } } } @@ -1863,8 +2610,8 @@ impl<'a> CodeGenerator<'a> { ); let mut final_func_dep_ir = IndexMap::new(); - let mut zero_arg_defined_functions = HashMap::new(); - let mut to_be_defined = HashMap::new(); + let mut zero_arg_defined_functions = IndexMap::new(); + let mut to_be_defined = IndexMap::new(); let mut dependency_map = IndexMap::new(); let mut dependency_vec = vec![]; @@ -1894,7 +2641,7 @@ impl<'a> CodeGenerator<'a> { if !funt_comp.args.is_empty() { // deal with function dependencies - handle_func_deps_ir( + handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, @@ -1906,16 +2653,17 @@ impl<'a> CodeGenerator<'a> { final_func_dep_ir.insert(func, dep_ir); } else { // since zero arg functions are run at compile time we need to pull all deps - let mut defined_functions = HashMap::new(); + // note anon functions are not included in the above. They exist in a function anyway + let mut defined_functions = IndexMap::new(); // deal with function dependencies in zero arg functions - handle_func_deps_ir( + handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, &mut defined_functions, &func_index_map, func_scope, - &mut HashMap::new(), + &mut IndexMap::new(), ); let mut final_zero_arg_ir = dep_ir; @@ -1924,7 +2672,8 @@ impl<'a> CodeGenerator<'a> { self.convert_opaque_type_to_inner_ir(&mut final_zero_arg_ir); self.zero_arg_functions.insert(func, final_zero_arg_ir); - + // zero arg functions don't contain the dependencies since they are pre-evaluated + // As such we add functions to defined only after dependencies for all other functions are calculated for (key, val) in defined_functions.into_iter() { zero_arg_defined_functions.insert(key, val); } @@ -2124,7 +2873,7 @@ impl<'a> CodeGenerator<'a> { let param_types = constructor.tipo.arg_types().unwrap(); - let mut generics_type_map: HashMap> = HashMap::new(); + let mut generics_type_map: IndexMap> = IndexMap::new(); for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { @@ -2162,7 +2911,7 @@ impl<'a> CodeGenerator<'a> { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); } else { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); - let mut func_calls = HashMap::new(); + let mut func_calls = IndexMap::new(); for ir in func_ir.clone().into_iter() { if let Air::Var { @@ -2198,8 +2947,8 @@ impl<'a> CodeGenerator<'a> { } else if let (Some(function), Type::Fn { .. }) = (function, &*tipo) { - let mut generics_type_map: HashMap> = - HashMap::new(); + let mut generics_type_map: IndexMap> = + IndexMap::new(); let param_types = tipo.arg_types().unwrap(); @@ -2353,6 +3102,7 @@ impl<'a> CodeGenerator<'a> { scope, names, tail, + check_last_item, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); @@ -2362,6 +3112,7 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, names, tail, + check_last_item, }; } Air::ListExpose { @@ -2562,7 +3313,12 @@ impl<'a> CodeGenerator<'a> { tipo: replaced_type, }; } - Air::TupleAccessor { tipo, scope, names } => { + Air::TupleAccessor { + tipo, + scope, + names, + check_last_item, + } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); @@ -2570,12 +3326,14 @@ impl<'a> CodeGenerator<'a> { scope, names, tipo: replaced_type, + check_last_item, }; } Air::RecordUpdate { highest_index, indices, scope, + tipo, } => { let mut new_indices = vec![]; for (ind, tipo) in indices { @@ -2588,6 +3346,7 @@ impl<'a> CodeGenerator<'a> { scope, indices: new_indices, highest_index, + tipo, }; } Air::Record { @@ -2642,15 +3401,15 @@ impl<'a> CodeGenerator<'a> { } } Air::FieldsExpose { - count, indices, scope, + check_last_item, } => { let record = ir_stack[index + 1].clone(); let record_type = record.tipo(); if let Some(record_type) = record_type { if check_replaceable_opaque_type(&record_type, self.data_types) { - ir_stack[index] = Air::Lam { + ir_stack[index] = Air::Let { scope, name: indices[0].1.clone(), }; @@ -2665,7 +3424,7 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } else { @@ -2679,10 +3438,38 @@ impl<'a> CodeGenerator<'a> { ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, - count, + check_last_item, }; } } + Air::Call { scope, count, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::Call { + scope, + tipo: replaced_type, + count, + }; + } + Air::If { scope, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::If { + scope, + tipo: replaced_type, + }; + } + Air::UnWrapData { scope, tipo } => { + let mut replaced_type = tipo.clone(); + replace_opaque_type(&mut replaced_type, self.data_types.clone()); + + ir_stack[index] = Air::UnWrapData { + scope, + tipo: replaced_type, + }; + } _ => {} } } @@ -2760,10 +3547,7 @@ impl<'a> CodeGenerator<'a> { })); } ValueConstructorVariant::Record { - name: constr_name, - field_map, - arity, - .. + name: constr_name, .. } => { if constructor.tipo.is_bool() { arg_stack @@ -2784,59 +3568,10 @@ impl<'a> CodeGenerator<'a> { .find(|(_, x)| x.name == *constr_name) .unwrap(); - let mut fields = + let fields = Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![])); - let tipo = constructor.tipo; - - let args_type = tipo.arg_types().unwrap(); - - if let Some(field_map) = field_map.clone() { - for field in field_map - .fields - .iter() - .sorted_by(|item1, item2| { - let (a, _) = item1.1; - let (b, _) = item2.1; - a.cmp(b) - }) - .zip(&args_type) - .rev() - { - // TODO revisit - fields = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), - convert_type_to_data( - Term::Var(Name { - text: field.0 .0.clone(), - unique: 0.into(), - }), - field.1, - ), - ), - fields, - ); - } - } else { - for (index, arg) in args_type.iter().enumerate().take(*arity) { - fields = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), - convert_type_to_data( - Term::Var(Name { - text: format!("__arg_{}", index), - unique: 0.into(), - }), - arg, - ), - ), - fields, - ); - } - } - - let mut term = apply_wrap( + let term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::ConstrData), Term::Constant(UplcConstant::Integer( @@ -2846,43 +3581,12 @@ impl<'a> CodeGenerator<'a> { fields, ); - if let Some(field_map) = field_map { - for field in field_map - .fields - .iter() - .sorted_by(|item1, item2| { - let (a, _) = item1.1; - let (b, _) = item2.1; - a.cmp(b) - }) - .rev() - { - term = Term::Lambda { - parameter_name: Name { - text: field.0.clone(), - unique: 0.into(), - }, - body: term.into(), - }; - } - } else { - for (index, _) in args_type.iter().enumerate().take(*arity) { - term = Term::Lambda { - parameter_name: Name { - text: format!("__arg_{}", index), - unique: 0.into(), - }, - body: term.into(), - }; - } - } - arg_stack.push(term); } } }; } - Air::Discard { .. } => { + Air::Void { .. } => { arg_stack.push(Term::Constant(UplcConstant::Unit)); } Air::List { @@ -2972,7 +3676,11 @@ impl<'a> CodeGenerator<'a> { } } Air::ListAccessor { - names, tail, tipo, .. + names, + tail, + tipo, + check_last_item, + .. } => { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -3009,6 +3717,13 @@ impl<'a> CodeGenerator<'a> { ) }; + let inner_types = tipo + .get_inner_types() + .into_iter() + .cycle() + .take(names.len()) + .collect_vec(); + term = apply_wrap( Term::Lambda { parameter_name: Name { @@ -3028,7 +3743,8 @@ impl<'a> CodeGenerator<'a> { tail, current_index, term, - &tipo, + inner_types, + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), @@ -3475,49 +4191,6 @@ impl<'a> CodeGenerator<'a> { }; arg_stack.push(term); } - Air::Assignment { name, .. } => { - let right_hand = arg_stack.pop().unwrap(); - let lam_body = arg_stack.pop().unwrap(); - - let term = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: name, - unique: 0.into(), - }, - body: lam_body.into(), - }, - right_hand, - ); - - arg_stack.push(term); - } - Air::Assert { constr_index, .. } => { - let constr = arg_stack.pop().unwrap(); - - let mut term = arg_stack.pop().unwrap(); - - let trace_error = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::Trace).force_wrap(), - Term::Constant(UplcConstant::String("Assert Failed".to_string())), - ), - Term::Delay(Term::Error.into()), - ) - .force_wrap(); - - let condition = apply_wrap( - apply_wrap( - DefaultFunction::EqualsInteger.into(), - Term::Constant(UplcConstant::Integer(constr_index as i128)), - ), - constr_index_exposer(constr), - ); - - term = delayed_if_else(condition, term, trace_error); - - arg_stack.push(term); - } Air::DefineFunc { func_name, params, @@ -3600,7 +4273,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } } - Air::Lam { name, .. } => { + Air::Let { name, .. } => { let arg = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -3618,6 +4291,43 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + Air::UnWrapData { tipo, .. } => { + let mut term = arg_stack.pop().unwrap(); + + term = convert_data_to_type(term, &tipo); + + arg_stack.push(term); + } + Air::AssertConstr { constr_index, .. } => { + let constr = arg_stack.pop().unwrap(); + + let mut term = arg_stack.pop().unwrap(); + + let error_term = apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Constant(UplcConstant::String( + "Asserted on incorrect constructor variant.".to_string(), + )), + ), + Term::Delay(Term::Error.into()), + ) + .force_wrap(); + + term = delayed_if_else( + apply_wrap( + apply_wrap( + DefaultFunction::EqualsInteger.into(), + Term::Constant(UplcConstant::Integer(constr_index as i128)), + ), + constr_index_exposer(constr), + ), + term, + error_term, + ); + + arg_stack.push(term); + } Air::When { subject_name, tipo, .. } => { @@ -3866,6 +4576,25 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + + Air::WrapClause { .. } => { + let _ = arg_stack.pop().unwrap(); + + let mut term = arg_stack.pop().unwrap(); + let arg = arg_stack.pop().unwrap(); + + term = apply_wrap( + Term::Lambda { + parameter_name: Name { + text: "__other_clauses_delayed".into(), + unique: 0.into(), + }, + body: term.into(), + }, + Term::Delay(arg.into()), + ); + arg_stack.push(term); + } Air::ClauseGuard { subject_name, tipo, .. } => { @@ -4101,162 +4830,89 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::FieldsExpose { indices, .. } => { + Air::FieldsExpose { + indices, + check_last_item, + .. + } => { self.needs_field_access = true; - - let constr_var = arg_stack.pop().unwrap(); - let mut body = arg_stack.pop().unwrap(); - - let mut indices = indices.into_iter().rev(); - let highest = indices.next().unwrap(); let mut id_list = vec![]; + let value = arg_stack.pop().unwrap(); + let mut term = arg_stack.pop().unwrap(); + let list_id = self.id_gen.next(); - for _ in 0..highest.0 { + for _ in 0..indices.len() { id_list.push(self.id_gen.next()); } - let constr_name_lam = format!("__constr_fields_{}", self.id_gen.next()); - let highest_loop_index = highest.0 as i32 - 1; - let last_prev_tail = Term::Var(Name { - text: if highest_loop_index == -1 { - constr_name_lam.clone() - } else { - format!( - "__tail_{}_{}", - highest_loop_index, id_list[highest_loop_index as usize] - ) - }, - unique: 0.into(), - }); + let current_index = 0; + let (first_name, indices) = indices.split_first().unwrap(); - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: highest.1, + let head_list = convert_data_to_type( + apply_wrap( + Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Var(Name { + text: format!("__constr_fields_{}", list_id), unique: 0.into(), - }, - body: body.into(), - }, - convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - last_prev_tail, - ), - &highest.2, + }), ), + &first_name.2, ); - let mut current_field = None; - for index in (0..highest.0).rev() { - let current_tail_index = index; - let previous_tail_index = if index == 0 { 0 } else { index - 1 }; - let current_tail_id = id_list[index]; - let previous_tail_id = if index == 0 { 0 } else { id_list[index - 1] }; - if current_field.is_none() { - current_field = indices.next(); - } + let names = indices.iter().cloned().map(|item| item.1).collect_vec(); + let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); - let prev_tail = if index == 0 { - Term::Var(Name { - text: constr_name_lam.clone(), - unique: 0.into(), - }) - } else { - Term::Var(Name { - text: format!("__tail_{previous_tail_index}_{previous_tail_id}"), - unique: 0.into(), - }) - }; + let tail_list = if !indices.is_empty() { + apply_wrap( + list_access_to_uplc( + &names, + &id_list, + false, + current_index, + term, + inner_types, + check_last_item, + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: format!("__constr_fields_{}", list_id), + unique: 0.into(), + }), + ), + ) + } else { + term + }; - if let Some(ref field) = current_field { - if field.0 == index { - let unwrapper = convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - prev_tail.clone(), - ), - &field.2, - ); - - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: field.1.clone(), - unique: 0.into(), - }, - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!( - "__tail_{current_tail_index}_{current_tail_id}" - ), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ) - .into(), - }, - unwrapper, - ); - - current_field = None; - } else { - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!( - "__tail_{current_tail_index}_{current_tail_id}" - ), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ); - } - } else { - body = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!("__tail_{current_tail_index}_{current_tail_id}"), - unique: 0.into(), - }, - body: body.into(), - }, - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - prev_tail, - ), - ); - } - } - - body = apply_wrap( + term = apply_wrap( Term::Lambda { parameter_name: Name { - text: constr_name_lam, + text: format!("__constr_fields_{}", list_id), unique: 0.into(), }, - body: body.into(), + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: first_name.1.clone(), + unique: 0.into(), + }, + body: tail_list.into(), + }, + head_list, + ) + .into(), }, apply_wrap( Term::Var(Name { text: CONSTR_FIELDS_EXPOSER.to_string(), unique: 0.into(), }), - constr_var, + value, ), ); - arg_stack.push(body); + arg_stack.push(term); } Air::Tuple { tipo, count, .. } => { let mut args = vec![]; @@ -4336,7 +4992,7 @@ impl<'a> CodeGenerator<'a> { let record = arg_stack.pop().unwrap(); - let mut args = HashMap::new(); + let mut args = IndexMap::new(); let mut unchanged_field_indices = vec![]; let mut prev_index = 0; for (index, tipo) in indices @@ -4541,7 +5197,12 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - Air::TupleAccessor { tipo, names, .. } => { + Air::TupleAccessor { + tipo, + names, + check_last_item, + .. + } => { let inner_types = tipo.get_inner_types(); let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); @@ -4640,7 +5301,8 @@ impl<'a> CodeGenerator<'a> { false, current_index, term, - &tipo, + tipo.get_inner_types(), + check_last_item, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), diff --git a/crates/aiken-project/Cargo.toml b/crates/aiken-project/Cargo.toml index e10ead1b..5bc164e9 100644 --- a/crates/aiken-project/Cargo.toml +++ b/crates/aiken-project/Cargo.toml @@ -16,6 +16,7 @@ fslock = "0.2.1" futures = "0.3.25" hex = "0.4.3" ignore = "0.4.18" +indexmap = "1.9.1" itertools = "0.10.1" miette = { version = "5.3.0", features = ["fancy"] } owo-colors = "3.5.0" diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index b6013b9f..86c3ca7c 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -20,6 +20,7 @@ use aiken_lang::{ IdGenerator, MINT, PUBLISH, SPEND, VALIDATOR_NAMES, WITHDRAW, }; use deps::UseManifest; +use indexmap::IndexMap; use miette::NamedSource; use options::{CodeGenMode, Options}; use package_name::PackageName; @@ -482,9 +483,9 @@ where validators: Vec<(PathBuf, String, TypedFunction)>, ) -> Result, Error> { let mut programs = Vec::new(); - let mut functions = HashMap::new(); - let mut type_aliases = HashMap::new(); - let mut data_types = HashMap::new(); + let mut functions = IndexMap::new(); + let mut type_aliases = IndexMap::new(); + let mut data_types = IndexMap::new(); let prelude_functions = builtins::prelude_functions(&self.id_gen); for (access_key, func) in prelude_functions.iter() { @@ -539,11 +540,15 @@ where .. } = func_def; + let mut modules_map = IndexMap::new(); + + modules_map.extend(self.module_types.clone()); + let mut generator = CodeGenerator::new( &functions, // &type_aliases, &data_types, - &self.module_types, + &modules_map, ); self.event_listener.handle_event(Event::GeneratingUPLC { @@ -573,9 +578,9 @@ where should_collect: fn(&TypedDefinition) -> bool, ) -> Result, Error> { let mut programs = Vec::new(); - let mut functions = HashMap::new(); - let mut type_aliases = HashMap::new(); - let mut data_types = HashMap::new(); + let mut functions = IndexMap::new(); + let mut type_aliases = IndexMap::new(); + let mut data_types = IndexMap::new(); let prelude_functions = builtins::prelude_functions(&self.id_gen); for (access_key, func) in prelude_functions.iter() { @@ -648,21 +653,25 @@ where }) } + let mut modules_map = IndexMap::new(); + + modules_map.extend(self.module_types.clone()); + let mut generator = CodeGenerator::new( &functions, // &type_aliases, &data_types, - &self.module_types, + &modules_map, ); let evaluation_hint = if let Some((bin_op, left_src, right_src)) = func_def.test_hint() { - let left = CodeGenerator::new(&functions, &data_types, &self.module_types) + let left = CodeGenerator::new(&functions, &data_types, &modules_map) .generate(*left_src, vec![], false) .try_into() .unwrap(); - let right = CodeGenerator::new(&functions, &data_types, &self.module_types) + let right = CodeGenerator::new(&functions, &data_types, &modules_map) .generate(*right_src, vec![], false) .try_into() .unwrap(); diff --git a/crates/uplc/src/ast/builder.rs b/crates/uplc/src/ast/builder.rs index 251db3ca..1b11d16f 100644 --- a/crates/uplc/src/ast/builder.rs +++ b/crates/uplc/src/ast/builder.rs @@ -5,6 +5,7 @@ use super::{Constant, Name, Term}; 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 fn apply_wrap(function: Term, arg: Term) -> Term { Term::Apply { @@ -31,6 +32,108 @@ pub fn final_wrapper(term: Term) -> Term { ) } +pub fn assert_on_list(term: Term) -> Term { + apply_wrap( + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: apply_wrap( + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: term.into(), + }, + apply_wrap( + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + ), + ) + .into(), + }, + Term::Lambda { + parameter_name: Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }, + body: Term::Lambda { + parameter_name: Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }, + body: Term::Lambda { + parameter_name: Name { + text: "check_with".to_string(), + unique: 0.into(), + }, + body: delayed_choose_list( + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + Term::Constant(Constant::Unit), + apply_wrap( + apply_wrap( + Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(), + apply_wrap( + Term::Var(Name { + text: "check_with".to_string(), + unique: 0.into(), + }), + apply_wrap( + Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + ), + ), + ), + apply_wrap( + apply_wrap( + apply_wrap( + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + Term::Var(Name { + text: ASSERT_ON_LIST.to_string(), + unique: 0.into(), + }), + ), + apply_wrap( + Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Var(Name { + text: "list_to_check".to_string(), + unique: 0.into(), + }), + ), + ), + Term::Var(Name { + text: "check_with".to_string(), + unique: 0.into(), + }), + ), + ), + ) + .into(), + } + .into(), + } + .into(), + }, + ) +} + pub fn constr_fields_exposer(term: Term) -> Term { Term::Apply { function: Term::Lambda { diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 196fdb25..89262cbe 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -343,7 +343,7 @@ impl Machine { Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone())) } } - rest => Err(Error::NonFunctionalApplication(rest)), + rest => Err(Error::NonFunctionalApplication(rest, argument)), } } diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 7132db2c..5749a38f 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -16,8 +16,8 @@ pub enum Error { EvaluationFailure, #[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")] NonPolymorphicInstantiation(Value), - #[error("Attempted to apply a non-function:\n\n{0:#?}")] - NonFunctionalApplication(Value), + #[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")] + NonFunctionalApplication(Value, Value), #[error("Type mismatch expected '{0}' got '{1}'")] TypeMismatch(Type, Type), #[error("Type mismatch expected '(list a)' got '{0}'")] diff --git a/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr b/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr index ec36701e..1f30832e 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr +++ b/examples/acceptance_tests/036/assets/spend/spend/mainnet.addr @@ -1 +1 @@ -addr1w9zyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90s83wahe \ No newline at end of file +addr1w8ehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssvegjnw \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/payment_script.json b/examples/acceptance_tests/036/assets/spend/spend/payment_script.json index 78c4254d..26f7ac73 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/payment_script.json +++ b/examples/acceptance_tests/036/assets/spend/spend/payment_script.json @@ -1,5 +1,5 @@ { "type": "PlutusScriptV2", "description": "Generated by Aiken", - "cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1" + "cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1" } \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/script.cbor b/examples/acceptance_tests/036/assets/spend/spend/script.cbor index 0d8bf202..8663ae49 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/script.cbor +++ b/examples/acceptance_tests/036/assets/spend/spend/script.cbor @@ -1 +1 @@ -59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1 \ No newline at end of file +59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1 \ No newline at end of file diff --git a/examples/acceptance_tests/036/assets/spend/spend/testnet.addr b/examples/acceptance_tests/036/assets/spend/spend/testnet.addr index 4fcad1d6..54c31f14 100644 --- a/examples/acceptance_tests/036/assets/spend/spend/testnet.addr +++ b/examples/acceptance_tests/036/assets/spend/spend/testnet.addr @@ -1 +1 @@ -addr_test1wpzyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90sue6pcu \ No newline at end of file +addr_test1wrehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssh3uwut \ No newline at end of file diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index 554bde33..249423ce 100644 --- a/examples/acceptance_tests/040/lib/tests.ak +++ b/examples/acceptance_tests/040/lib/tests.ak @@ -1,3 +1,9 @@ +pub type Door{ + angle: Int, + locked: Bool +} + + pub type Car { Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int } Ford { @@ -5,18 +11,19 @@ pub type Car { owner: ByteArray, wheels: Int, truck_bed_limit: Int, + car_doors: List } } // test update_owner2_should_fail(){ -// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000} -// assert Honda{ owner, ..} = initial_car +// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: []} +// assert Honda{ owner, ..}: Car = initial_car // owner == #[] // } test update_owner1() { let initial_car: Data = - Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 } - assert Ford { owner, .. } = initial_car + Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] } + assert Ford { owner, .. }: Car = initial_car owner == #[] } diff --git a/examples/acceptance_tests/046/aiken.lock b/examples/acceptance_tests/046/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/046/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/046/aiken.toml b/examples/acceptance_tests/046/aiken.toml new file mode 100644 index 00000000..8101704f --- /dev/null +++ b/examples/acceptance_tests/046/aiken.toml @@ -0,0 +1,3 @@ +name = 'aiken-lang/acceptance_test_046' +version = '0.0.0' +description = '' diff --git a/examples/acceptance_tests/046/lib/tests.ak b/examples/acceptance_tests/046/lib/tests.ak new file mode 100644 index 00000000..71276553 --- /dev/null +++ b/examples/acceptance_tests/046/lib/tests.ak @@ -0,0 +1,9 @@ +test sort_by_1() { + let xs = [[4, 3, 2, 1], [2, 3, 4, 5]] + when xs is { + [[], ys] -> False + [xs, []] -> False + [[x, ..xs2], [y, ..ys2]] -> True + _ -> False + } +} diff --git a/examples/acceptance_tests/048/validators/foo.ak b/examples/acceptance_tests/048/validators/foo.ak new file mode 100644 index 00000000..487266c9 --- /dev/null +++ b/examples/acceptance_tests/048/validators/foo.ak @@ -0,0 +1,9 @@ +fn when_tuple(a: (Int, Int)) -> Int { + when a is { + (a, b) -> a + } +} + +pub fn spend(a, b, c) -> Bool { + when_tuple((4, 1)) == 4 +}