From 04c05f8d63c9e78e83dbc48a999554210df5242d Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 9 Dec 2022 18:47:21 +0100 Subject: [PATCH 01/39] Add some tests that make the compiler panick. --- examples/tests/a/aiken.toml | 2 ++ examples/tests/a/lib/main.ak | 14 ++++++++++++++ examples/tests/b/aiken.toml | 2 ++ examples/tests/b/lib/main.ak | 11 +++++++++++ examples/tests/c/aiken.toml | 2 ++ examples/tests/c/lib/main.ak | 14 ++++++++++++++ 6 files changed, 45 insertions(+) create mode 100644 examples/tests/a/aiken.toml create mode 100644 examples/tests/a/lib/main.ak create mode 100644 examples/tests/b/aiken.toml create mode 100644 examples/tests/b/lib/main.ak create mode 100644 examples/tests/c/aiken.toml create mode 100644 examples/tests/c/lib/main.ak diff --git a/examples/tests/a/aiken.toml b/examples/tests/a/aiken.toml new file mode 100644 index 00000000..748c732f --- /dev/null +++ b/examples/tests/a/aiken.toml @@ -0,0 +1,2 @@ +name = "test_a" +version = "0.0.0" diff --git a/examples/tests/a/lib/main.ak b/examples/tests/a/lib/main.ak new file mode 100644 index 00000000..bed4015a --- /dev/null +++ b/examples/tests/a/lib/main.ak @@ -0,0 +1,14 @@ +pub fn length(xs: List(a)) -> Int { + when xs is { + [] -> 0 + [_, ..rest] -> 1 + length(rest) + } +} + +test length_1() { + length([1, 2, 3]) == 3 +} + +test length_2() { + length([]) == 0 +} diff --git a/examples/tests/b/aiken.toml b/examples/tests/b/aiken.toml new file mode 100644 index 00000000..bf978117 --- /dev/null +++ b/examples/tests/b/aiken.toml @@ -0,0 +1,2 @@ +name = "test_b" +version = "0.0.0" diff --git a/examples/tests/b/lib/main.ak b/examples/tests/b/lib/main.ak new file mode 100644 index 00000000..c952a34c --- /dev/null +++ b/examples/tests/b/lib/main.ak @@ -0,0 +1,11 @@ +pub fn repeat(x: a, n: Int) -> List(a) { + if n <= 0 { + [] + } else { + [x, ..repeat(x, n - 1)] + } +} + +test repeat_1() { + repeat("aiken", 0) == [] +} diff --git a/examples/tests/c/aiken.toml b/examples/tests/c/aiken.toml new file mode 100644 index 00000000..48d12454 --- /dev/null +++ b/examples/tests/c/aiken.toml @@ -0,0 +1,2 @@ +name = "test_c" +version = "0.0.0" diff --git a/examples/tests/c/lib/main.ak b/examples/tests/c/lib/main.ak new file mode 100644 index 00000000..1331d981 --- /dev/null +++ b/examples/tests/c/lib/main.ak @@ -0,0 +1,14 @@ +pub fn foldr(xs: List(a), f: fn(a, b) -> b, zero: b) -> b { + when xs is { + [] -> zero + [x, ..rest] -> f(x, foldr(rest, f, zero)) + } +} + +pub fn concat(left: List(a), right: List(a)) -> List(a) { + foldr(left, fn(x, xs) { [x, ..xs] }, right) +} + +test concat_1() { + concat([1, 2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6] +} From 3d3b3d7e10fce9e5d168a54a5a36c3716316bb5f Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 10 Dec 2022 00:42:38 -0500 Subject: [PATCH 02/39] checkpoint --- crates/lang/src/tipo.rs | 10 +-- crates/lang/src/uplc.rs | 130 ++++++++++++++++++++++++++++++++++++-- crates/project/src/lib.rs | 4 +- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index def274c8..a63b4299 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -143,7 +143,7 @@ impl Type { if let Type::Tuple { elems } = &*args[0] { elems.len() == 2 } else if let Type::Var { tipo } = &*args[0] { - matches!(tipo.borrow().get_uplc_type(), UplcType::Pair(_, _)) + matches!(tipo.borrow().get_uplc_type(), Some(UplcType::Pair(_, _))) } else { false } @@ -388,10 +388,10 @@ impl TypeVar { } } - pub fn get_uplc_type(&self) -> UplcType { - match self { - Self::Link { tipo } => tipo.get_uplc_type(), - _ => unreachable!(), + pub fn get_uplc_type(&self) -> Option { + match dbg!(self) { + Self::Link { tipo } => Some(tipo.get_uplc_type()), + _ => None, } } } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 6cc2f72e..816759aa 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -101,8 +101,12 @@ impl<'a> CodeGenerator<'a> { self.build_ir(&body, &mut ir_stack, scope); + print!("{ir_stack:#?}"); + self.define_ir(&mut ir_stack); + print!("{ir_stack:#?}"); + let mut term = self.uplc_code_gen(&mut ir_stack); if self.needs_field_access { @@ -129,6 +133,8 @@ impl<'a> CodeGenerator<'a> { term, }; + println!("{}", program.to_pretty()); + let mut interner = Interner::new(); interner.program(&mut program); @@ -1877,6 +1883,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::EqualsData }; + println!("Equals Binop"); let term = match name { BinOp::And => Term::Apply { function: Term::Apply { @@ -1974,7 +1981,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); return; } else if tipo.is_tuple() - && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) + && matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::Pair(_, _)) { let term = Term::Apply { function: Term::Apply { @@ -2029,7 +2036,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); return; } else if tipo.is_list() - || matches!(tipo.get_uplc_type(), UplcType::List(_)) + || matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::List(_)) { let term = Term::Apply { function: Term::Apply { @@ -2146,7 +2153,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); return; } else if tipo.is_tuple() - && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) + && matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::Pair(_, _)) { // let term = Term::Apply { // function: Term::Apply { @@ -2198,7 +2205,7 @@ impl<'a> CodeGenerator<'a> { // return; todo!() } else if tipo.is_list() - || matches!(tipo.get_uplc_type(), UplcType::List(_)) + || matches!(dbg!(tipo).get_uplc_type(), UplcType::List(_)) { let term = Term::Apply { function: Term::Apply { @@ -3270,8 +3277,65 @@ impl<'a> CodeGenerator<'a> { params: depend_comp.args.clone(), recursive: depend_comp.recursive, }]; + let mut new_ir = depend_comp.ir.clone(); - temp_ir.extend(depend_comp.ir.clone()); + if depend_comp.recursive { + let mut insert_var_vec = vec![]; + println!("FOund HERE"); + for (index, air) in depend_comp.ir.clone().into_iter().enumerate().rev() { + if let Air::Var { + scope, + constructor, + name, + } = air + { + println!("found a var at index: {}", index); + if let ValueConstructorVariant::ModuleFn { + name: func_name, + module, + .. + } = constructor.clone().variant + { + println!( + "Func Name: {func_name}, Dependency Name: {}", + dependency.function_name + ); + println!( + "Module Name: {module}, Dependency Module: {}", + dependency.module_name + ); + if func_name.clone() == dependency.function_name.clone() + && module == dependency.module_name.clone() + { + insert_var_vec.push(( + index, + Air::Var { + scope: scope.clone(), + constructor: constructor.clone(), + name: func_name.clone(), + }, + )); + } + } + } + } + + for (index, ir) in insert_var_vec { + new_ir.insert(index, ir); + let current_call = new_ir[index - 1].clone(); + match current_call { + Air::Call { scope, count } => { + new_ir[index - 1] = Air::Call { + scope, + count: count + 1, + } + } + _ => unreachable!(), + } + } + } + + temp_ir.extend(new_ir); temp_ir.append(&mut dep_ir); @@ -3314,6 +3378,61 @@ impl<'a> CodeGenerator<'a> { recursive: funt_comp.recursive, }); + // let mut insert_var_vec = vec![]; + println!("FOund HERE"); + + // for (index, air) in depend_comp.ir.clone().into_iter().enumerate().rev() { + // if let Air::Var { + // scope, + // constructor, + // name, + // } = air + // { + // println!("found a var at index: {}", index); + // if let ValueConstructorVariant::ModuleFn { + // name: func_name, + // module, + // .. + // } = constructor.clone().variant + // { + // println!( + // "Func Name: {func_name}, Dependency Name: {}", + // dependency.function_name + // ); + // println!( + // "Module Name: {module}, Dependency Module: {}", + // dependency.module_name + // ); + // if func_name.clone() == dependency.function_name.clone() + // && module == dependency.module_name.clone() + // { + // insert_var_vec.push(( + // index, + // Air::Var { + // scope: scope.clone(), + // constructor: constructor.clone(), + // name: func_name.clone(), + // }, + // )); + // } + // } + // } + // } + + // for (index, ir) in insert_var_vec { + // new_ir.insert(index, ir); + // let current_call = new_ir[index - 1].clone(); + // match current_call { + // Air::Call { scope, count } => { + // new_ir[index - 1] = Air::Call { + // scope, + // count: count + 1, + // } + // } + // _ => unreachable!(), + // } + // } + full_func_ir.extend(funt_comp.ir.clone()); for ir in full_func_ir.into_iter().rev() { @@ -3981,6 +4100,7 @@ fn convert_type_to_data(term: Term, field_type: &Arc) -> Term .into(), } } else if field_type.is_tuple() { + println!("Type to data"); match field_type.get_uplc_type() { UplcType::List(_) => Term::Apply { function: DefaultFunction::ListData.into(), diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index 16192840..0588f4b9 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -548,7 +548,9 @@ where results.push(test_info); } - (Err(_), remaining_budget, _) => { + (Err(e), remaining_budget, _) => { + println!("ERROR:\n{}", e); + let test_info = TestInfo { is_passing: false, test, From 16fbf5bbcdff78abf759613c558ec673633fcc42 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 10 Dec 2022 01:07:33 -0500 Subject: [PATCH 03/39] feat: fix recursive functions --- crates/lang/src/uplc.rs | 133 +++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 816759aa..8e1d682d 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -3362,78 +3362,87 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(); - for item in to_insert.into_iter() { - func_index_map.remove(item.0); - self.defined_functions.insert(item.0.clone(), ()); + for (function_access_key, scopes) in to_insert.into_iter() { + func_index_map.remove(function_access_key); - let mut full_func_ir = final_func_dep_ir.get(item.0).unwrap().clone(); + self.defined_functions + .insert(function_access_key.clone(), ()); - let funt_comp = func_components.get(item.0).unwrap(); + let mut full_func_ir = + final_func_dep_ir.get(function_access_key).unwrap().clone(); + + let mut func_comp = + func_components.get(function_access_key).unwrap().clone(); + + dbg!(&func_comp); full_func_ir.push(Air::DefineFunc { - scope: item.1.clone(), - func_name: item.0.function_name.clone(), - module_name: item.0.module_name.clone(), - params: funt_comp.args.clone(), - recursive: funt_comp.recursive, + scope: scopes.clone(), + func_name: function_access_key.function_name.clone(), + module_name: function_access_key.module_name.clone(), + params: func_comp.args.clone(), + recursive: func_comp.recursive, }); - // let mut insert_var_vec = vec![]; + let mut insert_var_vec = vec![]; println!("FOund HERE"); - // for (index, air) in depend_comp.ir.clone().into_iter().enumerate().rev() { - // if let Air::Var { - // scope, - // constructor, - // name, - // } = air - // { - // println!("found a var at index: {}", index); - // if let ValueConstructorVariant::ModuleFn { - // name: func_name, - // module, - // .. - // } = constructor.clone().variant - // { - // println!( - // "Func Name: {func_name}, Dependency Name: {}", - // dependency.function_name - // ); - // println!( - // "Module Name: {module}, Dependency Module: {}", - // dependency.module_name - // ); - // if func_name.clone() == dependency.function_name.clone() - // && module == dependency.module_name.clone() - // { - // insert_var_vec.push(( - // index, - // Air::Var { - // scope: scope.clone(), - // constructor: constructor.clone(), - // name: func_name.clone(), - // }, - // )); - // } - // } - // } - // } + for (index, air) in func_comp.ir.clone().into_iter().enumerate().rev() { + if let Air::Var { + scope, + constructor, + name, + } = air + { + println!("found a var at index: {}", index); + if let ValueConstructorVariant::ModuleFn { + name: func_name, + module, + .. + } = constructor.clone().variant + { + println!( + "Func Name: {func_name}, Dependency Name: {}", + function_access_key.function_name + ); + println!( + "Module Name: {module}, Dependency Module: {}", + function_access_key.module_name + ); + if func_name.clone() + == function_access_key.function_name.clone() + && module == function_access_key.module_name.clone() + { + insert_var_vec.push(( + index, + Air::Var { + scope: scope.clone(), + constructor: constructor.clone(), + name: func_name.clone(), + }, + )); + } + } + } + } - // for (index, ir) in insert_var_vec { - // new_ir.insert(index, ir); - // let current_call = new_ir[index - 1].clone(); - // match current_call { - // Air::Call { scope, count } => { - // new_ir[index - 1] = Air::Call { - // scope, - // count: count + 1, - // } - // } - // _ => unreachable!(), - // } - // } + for (index, ir) in insert_var_vec { + func_comp.ir.insert(index, ir); - full_func_ir.extend(funt_comp.ir.clone()); + let current_call = func_comp.ir[index - 1].clone(); + + match current_call { + Air::Call { scope, count } => { + func_comp.ir[index - 1] = Air::Call { + scope, + count: count + 1, + } + } + _ => unreachable!(), + } + } + + full_func_ir.extend(func_comp.ir.clone()); for ir in full_func_ir.into_iter().rev() { ir_stack.insert(index, ir); From e4d9ca4586e6ada6f61ed842a62ec30d9696d0de Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 11 Dec 2022 14:09:20 -0500 Subject: [PATCH 04/39] support generics --- crates/lang/src/tipo.rs | 35 ++++++++ crates/lang/src/uplc.rs | 190 ++++++++++++++++++++++++---------------- 2 files changed, 151 insertions(+), 74 deletions(-) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index a63b4299..fe604429 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -157,6 +157,33 @@ impl Type { matches!(self, Self::Tuple { .. }) } + pub fn is_generic(&self) -> bool { + match self { + Type::App { + public, + module, + name, + args, + } => { + let mut is_a_generic = false; + for arg in args { + is_a_generic = is_a_generic || arg.is_generic(); + } + is_a_generic + } + + Type::Var { tipo } => tipo.borrow().is_generic(), + Type::Tuple { elems } => { + let mut is_a_generic = false; + for elem in elems { + is_a_generic = is_a_generic || elem.is_generic(); + } + is_a_generic + } + _ => todo!(), + } + } + pub fn get_inner_type(&self) -> Vec> { if self.is_list() { match self { @@ -381,6 +408,14 @@ impl TypeVar { } } + pub fn is_generic(&self) -> bool { + match self { + TypeVar::Generic { .. } => true, + TypeVar::Link { tipo } => tipo.is_generic(), + _ => false, + } + } + pub fn get_inner_type(&self) -> Vec> { match self { Self::Link { tipo } => tipo.get_inner_type(), diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 8e1d682d..1800a456 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -3277,65 +3277,8 @@ impl<'a> CodeGenerator<'a> { params: depend_comp.args.clone(), recursive: depend_comp.recursive, }]; - let mut new_ir = depend_comp.ir.clone(); - if depend_comp.recursive { - let mut insert_var_vec = vec![]; - println!("FOund HERE"); - for (index, air) in depend_comp.ir.clone().into_iter().enumerate().rev() { - if let Air::Var { - scope, - constructor, - name, - } = air - { - println!("found a var at index: {}", index); - if let ValueConstructorVariant::ModuleFn { - name: func_name, - module, - .. - } = constructor.clone().variant - { - println!( - "Func Name: {func_name}, Dependency Name: {}", - dependency.function_name - ); - println!( - "Module Name: {module}, Dependency Module: {}", - dependency.module_name - ); - if func_name.clone() == dependency.function_name.clone() - && module == dependency.module_name.clone() - { - insert_var_vec.push(( - index, - Air::Var { - scope: scope.clone(), - constructor: constructor.clone(), - name: func_name.clone(), - }, - )); - } - } - } - } - - for (index, ir) in insert_var_vec { - new_ir.insert(index, ir); - let current_call = new_ir[index - 1].clone(); - match current_call { - Air::Call { scope, count } => { - new_ir[index - 1] = Air::Call { - scope, - count: count + 1, - } - } - _ => unreachable!(), - } - } - } - - temp_ir.extend(new_ir); + temp_ir.extend(depend_comp.ir.clone()); temp_ir.append(&mut dep_ir); @@ -3374,8 +3317,6 @@ impl<'a> CodeGenerator<'a> { let mut func_comp = func_components.get(function_access_key).unwrap().clone(); - dbg!(&func_comp); - full_func_ir.push(Air::DefineFunc { scope: scopes.clone(), func_name: function_access_key.function_name.clone(), @@ -3385,13 +3326,9 @@ impl<'a> CodeGenerator<'a> { }); let mut insert_var_vec = vec![]; - println!("FOund HERE"); - for (index, air) in func_comp.ir.clone().into_iter().enumerate().rev() { if let Air::Var { - scope, - constructor, - name, + scope, constructor, .. } = air { println!("found a var at index: {}", index); @@ -3550,7 +3487,7 @@ impl<'a> CodeGenerator<'a> { } = &constructor.variant { if builtin.is_none() { - let function_key = FunctionAccessKey { + let mut function_key = FunctionAccessKey { module_name: module.clone(), function_name: name.clone(), }; @@ -3568,6 +3505,33 @@ impl<'a> CodeGenerator<'a> { self.build_ir(&function.body, &mut func_ir, scope.to_vec()); + let (param_types, _) = constructor.tipo.function_types().unwrap(); + + let mut param_name_types = vec![]; + + for (index, arg) in function.arguments.iter().enumerate() { + if arg.tipo.is_generic() { + param_name_types.push(( + arg.arg_name + .get_variable_name() + .unwrap_or_default() + .to_string(), + param_types[index].clone(), + )); + } + } + + let (variant_name, func_ir) = + self.monomorphize(func_ir, param_name_types); + + function_key = FunctionAccessKey { + module_name: module.clone(), + function_name: format!( + "{}_{variant_name}", + function_key.function_name + ), + }; + to_be_defined_map.insert(function_key.clone(), scope.to_vec()); let mut func_calls = vec![]; @@ -3588,8 +3552,8 @@ impl<'a> CodeGenerator<'a> { { func_calls.push(FunctionAccessKey { module_name: module.clone(), - function_name: func_name.clone(), - }) + function_name: format!("{func_name}_{variant_name}"), + }); } } @@ -3604,13 +3568,17 @@ impl<'a> CodeGenerator<'a> { _ => {} } } - let recursive = - if let Ok(index) = func_calls.binary_search(&function_key) { + let recursive = if let Ok(index) = + func_calls.binary_search(&function_key) + { + func_calls.remove(index); + while let Ok(index) = func_calls.binary_search(&function_key) { func_calls.remove(index); - true - } else { - false - }; + } + true + } else { + false + }; func_components.insert( function_key, @@ -3656,6 +3624,80 @@ impl<'a> CodeGenerator<'a> { func_index_map.insert(func.0.clone(), get_common_ancestor(func.1, index_scope)); } } + + fn monomorphize( + &mut self, + ir: Vec, + param_types: Vec<(String, Arc)>, + ) -> (String, Vec) { + let mut used_param_to_type = vec![]; + let mut new_air = ir.clone(); + + for (index, ir) in ir.into_iter().enumerate() { + if let Air::Var { + scope, + constructor: + ValueConstructor { + public, variant, .. + }, + name, + } = ir + { + let exists = param_types.iter().find(|(n, _)| n == &name); + + if let Some((n, t)) = exists { + used_param_to_type.push((n.clone(), t.clone())); + + new_air[index] = Air::Var { + scope, + constructor: ValueConstructor { + public, + variant, + tipo: t.clone(), + }, + name, + } + } + } + } + + let mut new_name = String::new(); + + for (_, t) in used_param_to_type { + get_variant_name(&mut new_name, t); + } + + (new_name, new_air) + } +} + +fn get_variant_name(new_name: &mut String, t: Arc) { + new_name.push_str(&format!( + "_{}", + if t.is_string() { + "string".to_string() + } else if t.is_int() { + "int".to_string() + } else if t.is_bool() { + "bool".to_string() + } else if t.is_map() { + let mut full_type = "map".to_string(); + let pair_type = &t.get_inner_type()[0]; + let fst_type = &pair_type.get_inner_type()[0]; + let snd_type = &pair_type.get_inner_type()[1]; + + get_variant_name(&mut full_type, fst_type.clone()); + get_variant_name(&mut full_type, snd_type.clone()); + full_type + } else if t.is_list() { + let mut full_type = "list".to_string(); + let list_type = &t.get_inner_type()[0]; + get_variant_name(&mut full_type, list_type.clone()); + full_type + } else { + "data".to_string() + } + )); } fn convert_constants_to_data(constants: Vec) -> Vec { From 15dc2028102dae104385a88292b84c1c56642a05 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 11 Dec 2022 19:40:28 -0500 Subject: [PATCH 05/39] Feat: generic function call tests work --- crates/lang/src/air.rs | 2 + crates/lang/src/tipo.rs | 7 +- crates/lang/src/uplc.rs | 162 +++++++++++++++++++++++++------------- crates/project/src/lib.rs | 2 + 4 files changed, 111 insertions(+), 62 deletions(-) diff --git a/crates/lang/src/air.rs b/crates/lang/src/air.rs index fde5981c..1740367e 100644 --- a/crates/lang/src/air.rs +++ b/crates/lang/src/air.rs @@ -28,6 +28,7 @@ pub enum Air { scope: Vec, constructor: ValueConstructor, name: String, + variant_name: String, }, // Fn { @@ -88,6 +89,7 @@ pub enum Air { module_name: String, params: Vec, recursive: bool, + variant_name: String, }, DefineConst { diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index fe604429..87e2e4a5 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -159,12 +159,7 @@ impl Type { pub fn is_generic(&self) -> bool { match self { - Type::App { - public, - module, - name, - args, - } => { + Type::App { args, .. } => { let mut is_a_generic = false; for arg in args { is_a_generic = is_a_generic || arg.is_generic(); diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 1800a456..3c3d5998 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -50,6 +50,7 @@ pub type ConstrUsageKey = String; pub struct FunctionAccessKey { pub module_name: String, pub function_name: String, + pub variant_name: String, } #[derive(Clone, Debug)] @@ -182,6 +183,7 @@ impl<'a> CodeGenerator<'a> { scope, constructor: constructor.clone(), name: name.clone(), + variant_name: String::new(), }); } } @@ -194,11 +196,7 @@ impl<'a> CodeGenerator<'a> { } => { ir_stack.push(Air::List { scope: scope.clone(), - count: if tail.is_some() { - elements.len() + 1 - } else { - elements.len() - }, + count: elements.len(), tipo: tipo.clone(), tail: tail.is_some(), }); @@ -219,7 +217,7 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Call { fun, args, .. } => { ir_stack.push(Air::Call { scope: scope.clone(), - count: args.len() + 1, + count: args.len(), }); let mut scope_fun = scope.clone(); scope_fun.push(self.id_gen.next()); @@ -412,6 +410,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: constr_var, + variant_name: String::new(), }) } else { ir_stack.push(Air::When { @@ -486,6 +485,7 @@ impl<'a> CodeGenerator<'a> { let func = self.functions.get(&FunctionAccessKey { module_name: module_name.clone(), function_name: name.clone(), + variant_name: String::new(), }); if let Some(func) = func { @@ -503,6 +503,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: format!("{module}_{name}"), + variant_name: String::new(), }); } else { let type_info = self.module_types.get(module_name).unwrap(); @@ -590,6 +591,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: clause_properties.original_subject_name.clone(), + variant_name: String::new(), }); pattern_vec.append(values); } @@ -609,6 +611,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: clause_properties.original_subject_name.clone(), + variant_name: String::new(), }); new_vec.append(values); @@ -706,6 +709,7 @@ impl<'a> CodeGenerator<'a> { ), name: clause_properties.clause_var_name.clone(), scope: scope.clone(), + variant_name: String::new(), }]; // if only one constructor, no need to check @@ -1171,6 +1175,7 @@ impl<'a> CodeGenerator<'a> { ), name: item_name, scope: scope.clone(), + variant_name: String::new(), }); self.pattern_ir( a, @@ -1277,6 +1282,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: constr_name.clone(), + variant_name: String::new(), }], tipo, scope.clone(), @@ -1342,6 +1348,7 @@ impl<'a> CodeGenerator<'a> { }, ), name: constr_name.clone(), + variant_name: String::new(), }], tipo, scope.clone(), @@ -1404,6 +1411,7 @@ impl<'a> CodeGenerator<'a> { ), name: item_name, scope: scope.clone(), + variant_name: String::new(), }); self.pattern_ir( a, @@ -1439,6 +1447,8 @@ impl<'a> CodeGenerator<'a> { } fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec>) { + // println!("IR IS {ir:#?} AND ARG STACK IS {arg_stack:#?}"); + match ir { Air::Int { value, .. } => { let integer = value.parse().unwrap(); @@ -1457,7 +1467,10 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } Air::Var { - name, constructor, .. + name, + constructor, + variant_name, + .. } => { match constructor.variant { ValueConstructorVariant::LocalVariable { .. } => { @@ -1475,10 +1488,11 @@ impl<'a> CodeGenerator<'a> { .. } => { let name = if func_name == name { - format!("{module}_{func_name}") + format!("{module}_{func_name}{variant_name}") } else { - name + format!("{func_name}{variant_name}") }; + arg_stack.push(Term::Var(Name { text: name, unique: 0.into(), @@ -1670,10 +1684,9 @@ impl<'a> CodeGenerator<'a> { }; term = Term::Apply { function: Term::Apply { - function: Term::Force( - Term::Builtin(DefaultFunction::MkCons).force_wrap().into(), - ) - .into(), + function: Term::Builtin(DefaultFunction::MkCons) + .force_wrap() + .into(), argument: list_item.into(), } .into(), @@ -1846,10 +1859,10 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } Air::Call { count, .. } => { - if count >= 2 { + if count >= 1 { let mut term = arg_stack.pop().unwrap(); - for _ in 0..count - 1 { + for _ in 0..count { let arg = arg_stack.pop().unwrap(); term = Term::Apply { @@ -1883,7 +1896,6 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::EqualsData }; - println!("Equals Binop"); let term = match name { BinOp::And => Term::Apply { function: Term::Apply { @@ -1981,7 +1993,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); return; } else if tipo.is_tuple() - && matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::Pair(_, _)) + && matches!(tipo.clone().get_uplc_type(), UplcType::Pair(_, _)) { let term = Term::Apply { function: Term::Apply { @@ -2035,9 +2047,7 @@ impl<'a> CodeGenerator<'a> { }; arg_stack.push(term); return; - } else if tipo.is_list() - || matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::List(_)) - { + } else if tipo.is_list() { let term = Term::Apply { function: Term::Apply { function: default_builtin.into(), @@ -2048,13 +2058,10 @@ impl<'a> CodeGenerator<'a> { .into(), } .into(), + argument: Term::Apply { - function: default_builtin.into(), - argument: Term::Apply { - function: DefaultFunction::ListData.into(), - argument: right.into(), - } - .into(), + function: DefaultFunction::ListData.into(), + argument: right.into(), } .into(), }; @@ -2153,7 +2160,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); return; } else if tipo.is_tuple() - && matches!(dbg!(tipo.clone()).get_uplc_type(), UplcType::Pair(_, _)) + && matches!(tipo.clone().get_uplc_type(), UplcType::Pair(_, _)) { // let term = Term::Apply { // function: Term::Apply { @@ -2205,7 +2212,7 @@ impl<'a> CodeGenerator<'a> { // return; todo!() } else if tipo.is_list() - || matches!(dbg!(tipo).get_uplc_type(), UplcType::List(_)) + || matches!(tipo.get_uplc_type(), UplcType::List(_)) { let term = Term::Apply { function: Term::Apply { @@ -2367,12 +2374,13 @@ impl<'a> CodeGenerator<'a> { params, recursive, module_name, + variant_name, .. } => { let func_name = if module_name.is_empty() { - func_name + format!("{func_name}{variant_name}") } else { - format!("{module_name}_{func_name}") + format!("{module_name}_{func_name}{variant_name}") }; let mut func_body = arg_stack.pop().unwrap(); @@ -3276,6 +3284,7 @@ impl<'a> CodeGenerator<'a> { module_name: dependency.module_name.clone(), params: depend_comp.args.clone(), recursive: depend_comp.recursive, + variant_name: func.0.variant_name.clone(), }]; temp_ir.extend(depend_comp.ir.clone()); @@ -3323,29 +3332,24 @@ impl<'a> CodeGenerator<'a> { module_name: function_access_key.module_name.clone(), params: func_comp.args.clone(), recursive: func_comp.recursive, + variant_name: function_access_key.variant_name.clone(), }); let mut insert_var_vec = vec![]; for (index, air) in func_comp.ir.clone().into_iter().enumerate().rev() { if let Air::Var { - scope, constructor, .. + scope, + constructor, + variant_name, + .. } = air { - println!("found a var at index: {}", index); if let ValueConstructorVariant::ModuleFn { name: func_name, module, .. } = constructor.clone().variant { - println!( - "Func Name: {func_name}, Dependency Name: {}", - function_access_key.function_name - ); - println!( - "Module Name: {module}, Dependency Module: {}", - function_access_key.module_name - ); if func_name.clone() == function_access_key.function_name.clone() && module == function_access_key.module_name.clone() @@ -3356,6 +3360,7 @@ impl<'a> CodeGenerator<'a> { scope: scope.clone(), constructor: constructor.clone(), name: func_name.clone(), + variant_name, }, )); } @@ -3392,7 +3397,7 @@ impl<'a> CodeGenerator<'a> { fn define_recurse_ir( &mut self, - ir_stack: &[Air], + ir_stack: &mut [Air], func_components: &mut IndexMap, func_index_map: &mut IndexMap>, recursion_func_map: IndexMap, @@ -3405,7 +3410,7 @@ impl<'a> CodeGenerator<'a> { let func = func_index.0; let function_components = func_components.get(func).unwrap(); - let function_ir = function_components.ir.clone(); + let mut function_ir = function_components.ir.clone(); for ir in function_ir.clone() { if let Air::Var { @@ -3419,12 +3424,14 @@ impl<'a> CodeGenerator<'a> { }, .. }, + variant_name, .. } = ir { if recursion_func_map.contains_key(&FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), + variant_name: variant_name.clone(), }) { return; } else { @@ -3432,6 +3439,7 @@ impl<'a> CodeGenerator<'a> { FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), + variant_name: variant_name.clone(), }, (), ); @@ -3444,7 +3452,7 @@ impl<'a> CodeGenerator<'a> { let mut inner_func_index_map = IndexMap::new(); self.define_recurse_ir( - &function_ir, + &mut function_ir, &mut inner_func_components, &mut inner_func_index_map, recursion_func_map.clone(), @@ -3469,12 +3477,12 @@ impl<'a> CodeGenerator<'a> { fn process_define_ir( &mut self, - ir_stack: &[Air], + ir_stack: &mut [Air], func_components: &mut IndexMap, func_index_map: &mut IndexMap>, ) { let mut to_be_defined_map: IndexMap> = IndexMap::new(); - for ir in ir_stack.iter().rev() { + for (index, ir) in ir_stack.to_vec().iter().enumerate().rev() { match ir { Air::Var { scope, constructor, .. @@ -3490,6 +3498,7 @@ impl<'a> CodeGenerator<'a> { let mut function_key = FunctionAccessKey { module_name: module.clone(), function_name: name.clone(), + variant_name: String::new(), }; if let Some(scope_prev) = to_be_defined_map.get(&function_key) { @@ -3521,21 +3530,19 @@ impl<'a> CodeGenerator<'a> { } } - let (variant_name, func_ir) = + let (variant_name, mut func_ir) = self.monomorphize(func_ir, param_name_types); function_key = FunctionAccessKey { module_name: module.clone(), - function_name: format!( - "{}_{variant_name}", - function_key.function_name - ), + function_name: function_key.function_name, + variant_name: variant_name.clone(), }; to_be_defined_map.insert(function_key.clone(), scope.to_vec()); let mut func_calls = vec![]; - for ir in func_ir.clone() { + for (index, ir) in func_ir.clone().into_iter().enumerate() { if let Air::Var { constructor: ValueConstructor { @@ -3543,17 +3550,52 @@ impl<'a> CodeGenerator<'a> { ValueConstructorVariant::ModuleFn { name: func_name, module, + field_map, + arity, + location, .. }, - .. + public, + tipo, }, + scope, + name, .. } = ir { - func_calls.push(FunctionAccessKey { + let current_func = FunctionAccessKey { module_name: module.clone(), - function_name: format!("{func_name}_{variant_name}"), - }); + function_name: func_name.clone(), + variant_name: String::new(), + }; + + let current_func_as_variant = FunctionAccessKey { + module_name: module.clone(), + function_name: func_name.clone(), + variant_name: variant_name.clone(), + }; + if function_key.clone() == current_func_as_variant { + func_ir[index] = Air::Var { + scope, + constructor: ValueConstructor { + public, + variant: ValueConstructorVariant::ModuleFn { + name: func_name, + field_map, + module, + arity, + location, + builtin: None, + }, + tipo, + }, + name, + variant_name: variant_name.clone(), + }; + func_calls.push(current_func_as_variant); + } else { + func_calls.push(current_func); + } } } @@ -3580,6 +3622,13 @@ impl<'a> CodeGenerator<'a> { false }; + ir_stack[index] = Air::Var { + scope: scope.clone(), + constructor: constructor.clone(), + name: name.clone(), + variant_name, + }; + func_components.insert( function_key, FuncComponents { @@ -3641,6 +3690,7 @@ impl<'a> CodeGenerator<'a> { public, variant, .. }, name, + .. } = ir { let exists = param_types.iter().find(|(n, _)| n == &name); @@ -3656,6 +3706,7 @@ impl<'a> CodeGenerator<'a> { tipo: t.clone(), }, name, + variant_name: String::new(), } } } @@ -4151,7 +4202,6 @@ fn convert_type_to_data(term: Term, field_type: &Arc) -> Term .into(), } } else if field_type.is_tuple() { - println!("Type to data"); match field_type.get_uplc_type() { UplcType::List(_) => Term::Apply { function: DefaultFunction::ListData.into(), diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index 0588f4b9..ba483096 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -382,6 +382,7 @@ where FunctionAccessKey { module_name: module.name.clone(), function_name: func.name.clone(), + variant_name: String::new(), }, func, ); @@ -456,6 +457,7 @@ where FunctionAccessKey { module_name: module.name.clone(), function_name: func.name.clone(), + variant_name: String::new(), }, func, ); From 77386893d75ee4229deebc1c4dc4c5b9b8239556 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 11 Dec 2022 20:19:54 -0500 Subject: [PATCH 06/39] test: actually repeat --- examples/tests/b/lib/main.ak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tests/b/lib/main.ak b/examples/tests/b/lib/main.ak index c952a34c..4e80e58e 100644 --- a/examples/tests/b/lib/main.ak +++ b/examples/tests/b/lib/main.ak @@ -7,5 +7,5 @@ pub fn repeat(x: a, n: Int) -> List(a) { } test repeat_1() { - repeat("aiken", 0) == [] + repeat("aiken", 1) == ["aiken"] } From e43063d44754e4e7a6a5c2b3d5a89ba82e9aa644 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 12 Dec 2022 04:58:03 -0500 Subject: [PATCH 07/39] overhaul monomorphize to cover all generic cases test b passes --- crates/lang/src/tipo.rs | 33 ++++- crates/lang/src/uplc.rs | 296 +++++++++++++++++++++++++++++++++------- 2 files changed, 280 insertions(+), 49 deletions(-) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index 87e2e4a5..f450c399 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -175,11 +175,24 @@ impl Type { } is_a_generic } - _ => todo!(), + Type::Fn { args, .. } => { + let mut is_a_generic = false; + for arg in args { + is_a_generic = is_a_generic || arg.is_generic(); + } + is_a_generic + } } } - pub fn get_inner_type(&self) -> Vec> { + pub fn get_generic(&self) -> Option { + match self { + Type::Var { tipo } => tipo.borrow().get_generic(), + _ => None, + } + } + + pub fn get_inner_types(&self) -> Vec> { if self.is_list() { match self { Self::App { args, .. } => args.clone(), @@ -191,6 +204,13 @@ impl Type { Self::Tuple { elems } => elems.to_vec(), _ => vec![], } + } else if matches!(self.get_uplc_type(), UplcType::Data) { + match self { + Type::App { args, .. } => args.clone(), + Type::Fn { args, .. } => args.clone(), + Type::Var { tipo } => tipo.borrow().get_inner_type(), + _ => unreachable!(), + } } else { vec![] } @@ -411,9 +431,16 @@ impl TypeVar { } } + pub fn get_generic(&self) -> Option { + match self { + TypeVar::Generic { id } => Some(*id), + _ => None, + } + } + pub fn get_inner_type(&self) -> Vec> { match self { - Self::Link { tipo } => tipo.get_inner_type(), + Self::Link { tipo } => tipo.get_inner_types(), _ => vec![], } } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 3c3d5998..30d69653 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ops::Deref, sync::Arc, vec}; +use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc, vec}; use indexmap::IndexMap; use itertools::Itertools; @@ -1181,7 +1181,7 @@ impl<'a> CodeGenerator<'a> { a, &mut elements_vec, &mut var_vec, - &tipo.get_inner_type()[0], + &tipo.get_inner_types()[0], scope.clone(), ); } @@ -1417,7 +1417,7 @@ impl<'a> CodeGenerator<'a> { a, &mut elements_vec, &mut var_vec, - &tipo.get_inner_type()[0], + &tipo.get_inner_types()[0], scope.clone(), ); } @@ -1623,7 +1623,7 @@ impl<'a> CodeGenerator<'a> { } } - let list_type = tipo.get_inner_type()[0].clone(); + let list_type = tipo.get_inner_types()[0].clone(); if constants.len() == args.len() && !tail { let list = if tipo.is_map() { @@ -1734,7 +1734,7 @@ impl<'a> CodeGenerator<'a> { }) .into(), }, - &tipo.get_inner_type()[0], + &tipo.get_inner_types()[0], ) }; @@ -1840,7 +1840,7 @@ impl<'a> CodeGenerator<'a> { }) .into(), }, - &tipo.get_inner_type()[0], + &tipo.get_inner_types()[0], ) }; term = Term::Apply { @@ -3037,7 +3037,7 @@ impl<'a> CodeGenerator<'a> { } } - let tuple_sub_types = tipo.get_inner_type(); + let tuple_sub_types = tipo.get_inner_types(); if constants.len() == args.len() { let data_constants = convert_constants_to_data(constants); @@ -3183,7 +3183,7 @@ impl<'a> CodeGenerator<'a> { }) .into(), }, - &tipo.get_inner_type()[0], + &tipo.get_inner_types()[0], ) }; @@ -3516,22 +3516,19 @@ impl<'a> CodeGenerator<'a> { let (param_types, _) = constructor.tipo.function_types().unwrap(); - let mut param_name_types = vec![]; + let mut generic_id_type_vec = vec![]; for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { - param_name_types.push(( - arg.arg_name - .get_variable_name() - .unwrap_or_default() - .to_string(), - param_types[index].clone(), + generic_id_type_vec.append(&mut get_generics_and_type( + &arg.tipo, + ¶m_types[index], )); } } let (variant_name, mut func_ir) = - self.monomorphize(func_ir, param_name_types); + self.monomorphize(func_ir, generic_id_type_vec); function_key = FunctionAccessKey { module_name: module.clone(), @@ -3677,44 +3674,156 @@ impl<'a> CodeGenerator<'a> { fn monomorphize( &mut self, ir: Vec, - param_types: Vec<(String, Arc)>, + generic_types: Vec<(u64, Arc)>, ) -> (String, Vec) { - let mut used_param_to_type = vec![]; let mut new_air = ir.clone(); for (index, ir) in ir.into_iter().enumerate() { - if let Air::Var { - scope, - constructor: - ValueConstructor { - public, variant, .. - }, - name, - .. - } = ir - { - let exists = param_types.iter().find(|(n, _)| n == &name); + match ir { + Air::Var { + constructor, + scope, + name, + variant_name, + } => { + if constructor.tipo.is_generic() { + let mut tipo = constructor.tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); - if let Some((n, t)) = exists { - used_param_to_type.push((n.clone(), t.clone())); + let mut constructor = constructor.clone(); + constructor.tipo = tipo; - new_air[index] = Air::Var { - scope, - constructor: ValueConstructor { - public, - variant, - tipo: t.clone(), - }, - name, - variant_name: String::new(), + new_air[index] = Air::Var { + scope, + constructor, + name, + variant_name, + }; } } + Air::List { + tipo, + scope, + count, + tail, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::List { + scope, + count, + tipo, + tail, + }; + } + } + Air::ListAccessor { + scope, + tipo, + names, + tail, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::ListAccessor { + scope, + names, + tipo, + tail, + }; + } + } + Air::ListExpose { + scope, + tipo, + tail_head_names, + tail, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::ListExpose { + scope, + tail_head_names, + tipo, + tail, + }; + } + } + + Air::BinOp { + scope, + name, + count, + tipo, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::BinOp { + scope, + name, + tipo, + count, + }; + } + } + Air::Assignment { .. } => {} + Air::When { + scope, + tipo, + subject_name, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::When { + scope, + subject_name, + tipo, + }; + } + } + Air::Clause { + scope, + tipo, + subject_name, + complex_clause, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::Clause { + scope, + tipo, + subject_name, + complex_clause, + }; + } + } + Air::ListClause { .. } => todo!(), + Air::ClauseGuard { .. } => todo!(), + Air::RecordAccess { .. } => todo!(), + Air::FieldsExpose { .. } => todo!(), + Air::Tuple { .. } => todo!(), + Air::Todo { .. } => todo!(), + Air::RecordUpdate { .. } => todo!(), + Air::TupleAccessor { .. } => todo!(), + _ => {} } } let mut new_name = String::new(); - for (_, t) in used_param_to_type { + for (_, t) in generic_types { get_variant_name(&mut new_name, t); } @@ -3722,6 +3831,101 @@ impl<'a> CodeGenerator<'a> { } } +fn find_generics_to_replace(tipo: &mut Arc, generic_types: &[(u64, Arc)]) { + if let Some(id) = tipo.get_generic() { + if let Some((_, t)) = generic_types + .iter() + .find(|(generic_id, _)| id == *generic_id) + { + *tipo = t.clone(); + } + } else if tipo.is_generic() { + match &**tipo { + Type::App { + args, + public, + module, + name, + } => { + let mut new_args = vec![]; + for arg in args { + let mut arg = arg.clone(); + find_generics_to_replace(&mut arg, generic_types); + new_args.push(arg); + } + let t = Type::App { + args: new_args, + public: *public, + module: module.clone(), + name: name.clone(), + }; + *tipo = t.into(); + } + Type::Fn { args, ret } => { + let mut new_args = vec![]; + for arg in args { + let mut arg = arg.clone(); + find_generics_to_replace(&mut arg, generic_types); + new_args.push(arg); + } + + let mut ret = ret.clone(); + find_generics_to_replace(&mut ret, generic_types); + + let t = Type::Fn { + args: new_args, + ret, + }; + *tipo = t.into(); + } + Type::Tuple { elems } => { + let mut new_elems = vec![]; + for elem in elems { + let mut elem = elem.clone(); + find_generics_to_replace(&mut elem, generic_types); + new_elems.push(elem); + } + let t = Type::Tuple { elems: new_elems }; + *tipo = t.into(); + } + Type::Var { tipo: var_tipo } => { + let var_type = var_tipo.as_ref().borrow().clone(); + let var_tipo = match var_type { + tipo::TypeVar::Unbound { .. } => todo!(), + tipo::TypeVar::Link { tipo } => { + let mut tipo = tipo; + find_generics_to_replace(&mut tipo, generic_types); + tipo + } + tipo::TypeVar::Generic { .. } => unreachable!(), + }; + + let t = Type::Var { + tipo: RefCell::from(tipo::TypeVar::Link { tipo: var_tipo }).into(), + }; + *tipo = t.into() + } + }; + } +} + +fn get_generics_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc)> { + let mut generics_ids = vec![]; + + if let Some(id) = tipo.get_generic() { + generics_ids.push((id, param.clone().into())); + } + + for (tipo, param_type) in tipo + .get_inner_types() + .iter() + .zip(param.get_inner_types().iter()) + { + generics_ids.append(&mut get_generics_and_type(tipo, param_type)); + } + generics_ids +} + fn get_variant_name(new_name: &mut String, t: Arc) { new_name.push_str(&format!( "_{}", @@ -3733,16 +3937,16 @@ fn get_variant_name(new_name: &mut String, t: Arc) { "bool".to_string() } else if t.is_map() { let mut full_type = "map".to_string(); - let pair_type = &t.get_inner_type()[0]; - let fst_type = &pair_type.get_inner_type()[0]; - let snd_type = &pair_type.get_inner_type()[1]; + let pair_type = &t.get_inner_types()[0]; + let fst_type = &pair_type.get_inner_types()[0]; + let snd_type = &pair_type.get_inner_types()[1]; get_variant_name(&mut full_type, fst_type.clone()); get_variant_name(&mut full_type, snd_type.clone()); full_type } else if t.is_list() { let mut full_type = "list".to_string(); - let list_type = &t.get_inner_type()[0]; + let list_type = &t.get_inner_types()[0]; get_variant_name(&mut full_type, list_type.clone()); full_type } else { @@ -3897,7 +4101,7 @@ fn list_access_to_uplc( }) .into(), }, - &tipo.clone().get_inner_type()[0], + &tipo.clone().get_inner_types()[0], ) }; From d78e2c9c6f63432cb3007d17abab5a168218dfe0 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 12 Dec 2022 21:34:32 -0500 Subject: [PATCH 08/39] feat: finish up generic match cases --- crates/lang/src/tipo.rs | 2 +- crates/lang/src/uplc.rs | 92 +++++++++++++++++++++++++++++++----- examples/tests/b/lib/main.ak | 2 +- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index f450c399..ec09c20a 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -446,7 +446,7 @@ impl TypeVar { } pub fn get_uplc_type(&self) -> Option { - match dbg!(self) { + match self { Self::Link { tipo } => Some(tipo.get_uplc_type()), _ => None, } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 30d69653..130208ce 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -102,12 +102,8 @@ impl<'a> CodeGenerator<'a> { self.build_ir(&body, &mut ir_stack, scope); - print!("{ir_stack:#?}"); - self.define_ir(&mut ir_stack); - print!("{ir_stack:#?}"); - let mut term = self.uplc_code_gen(&mut ir_stack); if self.needs_field_access { @@ -134,8 +130,6 @@ impl<'a> CodeGenerator<'a> { term, }; - println!("{}", program.to_pretty()); - let mut interner = Interner::new(); interner.program(&mut program); @@ -1447,8 +1441,6 @@ impl<'a> CodeGenerator<'a> { } fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec>) { - // println!("IR IS {ir:#?} AND ARG STACK IS {arg_stack:#?}"); - match ir { Air::Int { value, .. } => { let integer = value.parse().unwrap(); @@ -3809,11 +3801,85 @@ impl<'a> CodeGenerator<'a> { }; } } - Air::ListClause { .. } => todo!(), - Air::ClauseGuard { .. } => todo!(), - Air::RecordAccess { .. } => todo!(), - Air::FieldsExpose { .. } => todo!(), - Air::Tuple { .. } => todo!(), + Air::ListClause { + scope, + tipo, + tail_name, + complex_clause, + next_tail_name, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::ListClause { + scope, + tipo, + tail_name, + complex_clause, + next_tail_name, + }; + } + } + Air::ClauseGuard { + tipo, + scope, + subject_name, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::ClauseGuard { + scope, + subject_name, + tipo, + }; + } + } + Air::RecordAccess { + scope, + index: record_index, + tipo, + } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::RecordAccess { + scope, + index: record_index, + tipo, + }; + } + } + Air::FieldsExpose { + scope, + count, + indices, + } => { + let mut new_indices = vec![]; + for (ind, name, tipo) in indices { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + } + new_indices.push((ind, name, tipo)); + } + new_air[index] = Air::FieldsExpose { + scope, + count, + indices: new_indices, + }; + } + Air::Tuple { scope, tipo, count } => { + if tipo.is_generic() { + let mut tipo = tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + + new_air[index] = Air::Tuple { scope, count, tipo }; + } + } Air::Todo { .. } => todo!(), Air::RecordUpdate { .. } => todo!(), Air::TupleAccessor { .. } => todo!(), diff --git a/examples/tests/b/lib/main.ak b/examples/tests/b/lib/main.ak index 4e80e58e..1b70e682 100644 --- a/examples/tests/b/lib/main.ak +++ b/examples/tests/b/lib/main.ak @@ -7,5 +7,5 @@ pub fn repeat(x: a, n: Int) -> List(a) { } test repeat_1() { - repeat("aiken", 1) == ["aiken"] + repeat("aiken", 2) == ["aiken", "aiken"] } From 8f69a4b60034f8fcec7f2a4badda9435bbeb7283 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 11:19:16 +0100 Subject: [PATCH 09/39] Add another failing test example (d) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:3264:35 ╰─▶ called `Option::unwrap()` on a `None` value help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/tests/d/aiken.toml | 2 ++ examples/tests/d/lib/main.ak | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 examples/tests/d/aiken.toml create mode 100644 examples/tests/d/lib/main.ak diff --git a/examples/tests/d/aiken.toml b/examples/tests/d/aiken.toml new file mode 100644 index 00000000..762f80f1 --- /dev/null +++ b/examples/tests/d/aiken.toml @@ -0,0 +1,2 @@ +name = "test_d" +version = "0.0.0" diff --git a/examples/tests/d/lib/main.ak b/examples/tests/d/lib/main.ak new file mode 100644 index 00000000..f9359d1a --- /dev/null +++ b/examples/tests/d/lib/main.ak @@ -0,0 +1,18 @@ +pub fn foldr(xs: List(a), f: fn(a, b) -> b, zero: b) -> b { + when xs is { + [] -> zero + [x, ..rest] -> f(x, foldr(rest, f, zero)) + } +} + +pub fn prepend(x: a, xs: List(a)) -> List(a) { + [x, ..xs] +} + +pub fn concat(left: List(a), right: List(a)) -> List(a) { + foldr(left, prepend, right) +} + +test concat_1() { + concat([1, 2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6] +} From 572121974efcbd5121b36e45f2c081f91315923a Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 11:23:51 +0100 Subject: [PATCH 10/39] Update generic syntax in tests. --- examples/tests/a/lib/main.ak | 2 +- examples/tests/b/lib/main.ak | 2 +- examples/tests/c/lib/main.ak | 4 ++-- examples/tests/d/lib/main.ak | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/tests/a/lib/main.ak b/examples/tests/a/lib/main.ak index bed4015a..75355cc8 100644 --- a/examples/tests/a/lib/main.ak +++ b/examples/tests/a/lib/main.ak @@ -1,4 +1,4 @@ -pub fn length(xs: List(a)) -> Int { +pub fn length(xs: List) -> Int { when xs is { [] -> 0 [_, ..rest] -> 1 + length(rest) diff --git a/examples/tests/b/lib/main.ak b/examples/tests/b/lib/main.ak index 1b70e682..751281ed 100644 --- a/examples/tests/b/lib/main.ak +++ b/examples/tests/b/lib/main.ak @@ -1,4 +1,4 @@ -pub fn repeat(x: a, n: Int) -> List(a) { +pub fn repeat(x: a, n: Int) -> List { if n <= 0 { [] } else { diff --git a/examples/tests/c/lib/main.ak b/examples/tests/c/lib/main.ak index 1331d981..49b48533 100644 --- a/examples/tests/c/lib/main.ak +++ b/examples/tests/c/lib/main.ak @@ -1,11 +1,11 @@ -pub fn foldr(xs: List(a), f: fn(a, b) -> b, zero: b) -> b { +pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } -pub fn concat(left: List(a), right: List(a)) -> List(a) { +pub fn concat(left: List, right: List) -> List { foldr(left, fn(x, xs) { [x, ..xs] }, right) } diff --git a/examples/tests/d/lib/main.ak b/examples/tests/d/lib/main.ak index f9359d1a..169f2b3d 100644 --- a/examples/tests/d/lib/main.ak +++ b/examples/tests/d/lib/main.ak @@ -1,15 +1,15 @@ -pub fn foldr(xs: List(a), f: fn(a, b) -> b, zero: b) -> b { +pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } -pub fn prepend(x: a, xs: List(a)) -> List(a) { +pub fn prepend(x: a, xs: List) -> List { [x, ..xs] } -pub fn concat(left: List(a), right: List(a)) -> List(a) { +pub fn concat(left: List, right: List) -> List { foldr(left, prepend, right) } From 603f4a63851e802a0b2606602f32666335fb8b64 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 13:46:29 +0100 Subject: [PATCH 11/39] Add another failing example (e) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:1518:45 ╰─▶ called `Option::unwrap()` on a `None` value help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/tests/e/aiken.toml | 2 ++ examples/tests/e/lib/main.ak | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 examples/tests/e/aiken.toml create mode 100644 examples/tests/e/lib/main.ak diff --git a/examples/tests/e/aiken.toml b/examples/tests/e/aiken.toml new file mode 100644 index 00000000..eeade47c --- /dev/null +++ b/examples/tests/e/aiken.toml @@ -0,0 +1,2 @@ +name = "test_e" +version = "0.0.0" diff --git a/examples/tests/e/lib/main.ak b/examples/tests/e/lib/main.ak new file mode 100644 index 00000000..ce9644a7 --- /dev/null +++ b/examples/tests/e/lib/main.ak @@ -0,0 +1,16 @@ +use aiken/builtin.{head_list} + +pub fn head(xs: List) -> Option { + when xs is { + [] -> None + _ -> Some(head_list(xs)) + } +} + +test head_1() { + head([1, 2, 3]) == Some(1) +} + +test head_2() { + head([]) == None +} From b29406309728a4c5d491ba709f7658fd3b1bf68c Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 13:48:24 +0100 Subject: [PATCH 12/39] Add another acceptance test (f) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:4043:46 ╰─▶ not yet implemented help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/tests/f/aiken.toml | 2 ++ examples/tests/f/lib/main.ak | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 examples/tests/f/aiken.toml create mode 100644 examples/tests/f/lib/main.ak diff --git a/examples/tests/f/aiken.toml b/examples/tests/f/aiken.toml new file mode 100644 index 00000000..b151c124 --- /dev/null +++ b/examples/tests/f/aiken.toml @@ -0,0 +1,2 @@ +name = "test_f" +version = "0.0.0" diff --git a/examples/tests/f/lib/main.ak b/examples/tests/f/lib/main.ak new file mode 100644 index 00000000..4de15698 --- /dev/null +++ b/examples/tests/f/lib/main.ak @@ -0,0 +1,3 @@ +test foo() { + (1, []) == (1, []) +} From a62fb1905e90fb8ba414b4b1533ade47e8039d82 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 14:09:17 +0100 Subject: [PATCH 13/39] rename examples/tests/{a,b,c,d,e,f} into examples/acceptance_tests/00{1,2,3,4,5,6} Also added a little Makefile to run them all in one go. --- .editorconfig | 12 ++++++++---- examples/acceptance_tests/001/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/001/lib/test.ak} | 0 examples/acceptance_tests/002/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/002/lib/test.ak} | 0 examples/acceptance_tests/003/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/003/lib/test.ak} | 0 examples/acceptance_tests/004/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/004/lib/test.ak} | 0 examples/acceptance_tests/005/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/005/lib/test.ak} | 0 examples/acceptance_tests/006/aiken.toml | 2 ++ .../lib/main.ak => acceptance_tests/006/lib/test.ak} | 0 examples/acceptance_tests/Makefile | 5 +++++ examples/tests/a/aiken.toml | 2 -- examples/tests/b/aiken.toml | 2 -- examples/tests/c/aiken.toml | 2 -- examples/tests/d/aiken.toml | 2 -- examples/tests/e/aiken.toml | 2 -- examples/tests/f/aiken.toml | 2 -- 20 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 examples/acceptance_tests/001/aiken.toml rename examples/{tests/a/lib/main.ak => acceptance_tests/001/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/002/aiken.toml rename examples/{tests/b/lib/main.ak => acceptance_tests/002/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/003/aiken.toml rename examples/{tests/c/lib/main.ak => acceptance_tests/003/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/004/aiken.toml rename examples/{tests/d/lib/main.ak => acceptance_tests/004/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/005/aiken.toml rename examples/{tests/e/lib/main.ak => acceptance_tests/005/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/006/aiken.toml rename examples/{tests/f/lib/main.ak => acceptance_tests/006/lib/test.ak} (100%) create mode 100644 examples/acceptance_tests/Makefile delete mode 100644 examples/tests/a/aiken.toml delete mode 100644 examples/tests/b/aiken.toml delete mode 100644 examples/tests/c/aiken.toml delete mode 100644 examples/tests/d/aiken.toml delete mode 100644 examples/tests/e/aiken.toml delete mode 100644 examples/tests/f/aiken.toml diff --git a/.editorconfig b/.editorconfig index 0759674c..4f309038 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,13 @@ root = true - -[*.ak] -indent_style = space -indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.ak] +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tabs +indent_size = 4 diff --git a/examples/acceptance_tests/001/aiken.toml b/examples/acceptance_tests/001/aiken.toml new file mode 100644 index 00000000..69aeb069 --- /dev/null +++ b/examples/acceptance_tests/001/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_001" +version = "0.0.0" diff --git a/examples/tests/a/lib/main.ak b/examples/acceptance_tests/001/lib/test.ak similarity index 100% rename from examples/tests/a/lib/main.ak rename to examples/acceptance_tests/001/lib/test.ak diff --git a/examples/acceptance_tests/002/aiken.toml b/examples/acceptance_tests/002/aiken.toml new file mode 100644 index 00000000..333cd9d2 --- /dev/null +++ b/examples/acceptance_tests/002/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_002" +version = "0.0.0" diff --git a/examples/tests/b/lib/main.ak b/examples/acceptance_tests/002/lib/test.ak similarity index 100% rename from examples/tests/b/lib/main.ak rename to examples/acceptance_tests/002/lib/test.ak diff --git a/examples/acceptance_tests/003/aiken.toml b/examples/acceptance_tests/003/aiken.toml new file mode 100644 index 00000000..1d4e8952 --- /dev/null +++ b/examples/acceptance_tests/003/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_003" +version = "0.0.0" diff --git a/examples/tests/c/lib/main.ak b/examples/acceptance_tests/003/lib/test.ak similarity index 100% rename from examples/tests/c/lib/main.ak rename to examples/acceptance_tests/003/lib/test.ak diff --git a/examples/acceptance_tests/004/aiken.toml b/examples/acceptance_tests/004/aiken.toml new file mode 100644 index 00000000..3c7cbf0b --- /dev/null +++ b/examples/acceptance_tests/004/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_004" +version = "0.0.0" diff --git a/examples/tests/d/lib/main.ak b/examples/acceptance_tests/004/lib/test.ak similarity index 100% rename from examples/tests/d/lib/main.ak rename to examples/acceptance_tests/004/lib/test.ak diff --git a/examples/acceptance_tests/005/aiken.toml b/examples/acceptance_tests/005/aiken.toml new file mode 100644 index 00000000..58b7366b --- /dev/null +++ b/examples/acceptance_tests/005/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_005" +version = "0.0.0" diff --git a/examples/tests/e/lib/main.ak b/examples/acceptance_tests/005/lib/test.ak similarity index 100% rename from examples/tests/e/lib/main.ak rename to examples/acceptance_tests/005/lib/test.ak diff --git a/examples/acceptance_tests/006/aiken.toml b/examples/acceptance_tests/006/aiken.toml new file mode 100644 index 00000000..fc4d7722 --- /dev/null +++ b/examples/acceptance_tests/006/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_006" +version = "0.0.0" diff --git a/examples/tests/f/lib/main.ak b/examples/acceptance_tests/006/lib/test.ak similarity index 100% rename from examples/tests/f/lib/main.ak rename to examples/acceptance_tests/006/lib/test.ak diff --git a/examples/acceptance_tests/Makefile b/examples/acceptance_tests/Makefile new file mode 100644 index 00000000..4362b8ab --- /dev/null +++ b/examples/acceptance_tests/Makefile @@ -0,0 +1,5 @@ +all: + @for t in $(shell find . -regex ".*[0-9]\{3\}" -type d | sort); do \ + aiken check -d $${t}; \ + echo ""; \ + done diff --git a/examples/tests/a/aiken.toml b/examples/tests/a/aiken.toml deleted file mode 100644 index 748c732f..00000000 --- a/examples/tests/a/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_a" -version = "0.0.0" diff --git a/examples/tests/b/aiken.toml b/examples/tests/b/aiken.toml deleted file mode 100644 index bf978117..00000000 --- a/examples/tests/b/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_b" -version = "0.0.0" diff --git a/examples/tests/c/aiken.toml b/examples/tests/c/aiken.toml deleted file mode 100644 index 48d12454..00000000 --- a/examples/tests/c/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_c" -version = "0.0.0" diff --git a/examples/tests/d/aiken.toml b/examples/tests/d/aiken.toml deleted file mode 100644 index 762f80f1..00000000 --- a/examples/tests/d/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_d" -version = "0.0.0" diff --git a/examples/tests/e/aiken.toml b/examples/tests/e/aiken.toml deleted file mode 100644 index eeade47c..00000000 --- a/examples/tests/e/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_e" -version = "0.0.0" diff --git a/examples/tests/f/aiken.toml b/examples/tests/f/aiken.toml deleted file mode 100644 index b151c124..00000000 --- a/examples/tests/f/aiken.toml +++ /dev/null @@ -1,2 +0,0 @@ -name = "test_f" -version = "0.0.0" From 022fc36dd221d65934429e465691aa4e688a727c Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 14:14:28 +0100 Subject: [PATCH 14/39] Add new (failing) acceptance test (007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: aiken::check × Checking ╰─▶ Not a function ╭─[./007/lib/test.ak:4:1] 4 │ [(a, b), ..rest] -> { 5 │ let (a_tail, b_tail) = unzip(rest) · ────── 6 │ ([a, ..a_tail], [b, ..b_tail]) ╰──── ``` --- examples/acceptance_tests/007/aiken.toml | 2 ++ examples/acceptance_tests/007/lib/test.ak | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 examples/acceptance_tests/007/aiken.toml create mode 100644 examples/acceptance_tests/007/lib/test.ak diff --git a/examples/acceptance_tests/007/aiken.toml b/examples/acceptance_tests/007/aiken.toml new file mode 100644 index 00000000..a3ee5646 --- /dev/null +++ b/examples/acceptance_tests/007/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_007" +version = "0.0.0" diff --git a/examples/acceptance_tests/007/lib/test.ak b/examples/acceptance_tests/007/lib/test.ak new file mode 100644 index 00000000..82fc7f83 --- /dev/null +++ b/examples/acceptance_tests/007/lib/test.ak @@ -0,0 +1,9 @@ +pub fn unzip(xs: List<(a, b)>) -> (List, List) { + when xs is { + [] -> ([], []) + [(a, b), ..rest] -> { + let (a_tail, b_tail) = unzip(rest) + ([a, ..a_tail], [b, ..b_tail]) + } + } +} From adf34e9dc63b38bc6dda7668ee0f4e778839488f Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 14:39:36 +0100 Subject: [PATCH 15/39] Add new acceptance test scenario (008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:530:41 ╰─▶ not yet implemented help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/acceptance_tests/008/aiken.toml | 2 ++ examples/acceptance_tests/008/lib/test.ak | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 examples/acceptance_tests/008/aiken.toml create mode 100644 examples/acceptance_tests/008/lib/test.ak diff --git a/examples/acceptance_tests/008/aiken.toml b/examples/acceptance_tests/008/aiken.toml new file mode 100644 index 00000000..b8d13e78 --- /dev/null +++ b/examples/acceptance_tests/008/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_008" +version = "0.0.0" diff --git a/examples/acceptance_tests/008/lib/test.ak b/examples/acceptance_tests/008/lib/test.ak new file mode 100644 index 00000000..689fb805 --- /dev/null +++ b/examples/acceptance_tests/008/lib/test.ak @@ -0,0 +1,21 @@ +use aiken/builtin + +pub fn is_empty(bytes: ByteArray) -> Bool { + builtin.length_of_bytearray(bytes) == 0 +} + +test is_empty_1() { + is_empty(#[]) == True +} + +test is_empty_1_alt() { + is_empty(#[]) +} + +test is_empty_2() { + is_empty(#[1]) == False +} + +test is_empty_2_alt() { + !is_empty(#[1]) +} From 4217fd06b57ebf3eb32991b3eae5aa9b0f8cfa10 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 14:40:05 +0100 Subject: [PATCH 16/39] Add new acceptance test scenario (009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: the problem is only present when importing a builtin explicitly. When using a qualified import, it compiles fine. ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:3264:35 ╰─▶ called `Option::unwrap()` on a `None` value help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/acceptance_tests/009/aiken.toml | 2 ++ examples/acceptance_tests/009/lib/test.ak | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 examples/acceptance_tests/009/aiken.toml create mode 100644 examples/acceptance_tests/009/lib/test.ak diff --git a/examples/acceptance_tests/009/aiken.toml b/examples/acceptance_tests/009/aiken.toml new file mode 100644 index 00000000..575d56fb --- /dev/null +++ b/examples/acceptance_tests/009/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_009" +version = "0.0.0" diff --git a/examples/acceptance_tests/009/lib/test.ak b/examples/acceptance_tests/009/lib/test.ak new file mode 100644 index 00000000..b4ef2b0d --- /dev/null +++ b/examples/acceptance_tests/009/lib/test.ak @@ -0,0 +1,9 @@ +use aiken/builtin.{length_of_bytearray} + +pub fn is_empty(bytes: ByteArray) -> Bool { + length_of_bytearray(bytes) == 0 +} + +test is_empty_1() { + is_empty(#[]) == True +} From 37def91fc57ce8c1c5dc24d105c6f70f770736e5 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 16:08:33 +0100 Subject: [PATCH 17/39] Add new acceptance test scenario (010) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:688:33 ╰─▶ called `Option::unwrap()` on a `None` value help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` --- examples/acceptance_tests/010/aiken.toml | 2 ++ examples/acceptance_tests/010/lib/test.ak | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 examples/acceptance_tests/010/aiken.toml create mode 100644 examples/acceptance_tests/010/lib/test.ak diff --git a/examples/acceptance_tests/010/aiken.toml b/examples/acceptance_tests/010/aiken.toml new file mode 100644 index 00000000..7d91c9e7 --- /dev/null +++ b/examples/acceptance_tests/010/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_010" +version = "0.0.0" diff --git a/examples/acceptance_tests/010/lib/test.ak b/examples/acceptance_tests/010/lib/test.ak new file mode 100644 index 00000000..31260b9b --- /dev/null +++ b/examples/acceptance_tests/010/lib/test.ak @@ -0,0 +1,14 @@ +pub fn map(opt: Option, f: fn(a) -> b) -> Option { + when opt is { + None -> None + Some(a) -> Some(f(a)) + } +} + +fn add_one(n: Int) -> Int { + n + 1 +} + +test map_1() { + map(None, add_one) == None +} From 299cd1ac243735b6d979e345c506c55123e16de6 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 13 Dec 2022 13:13:58 -0500 Subject: [PATCH 18/39] fix: tuples --- examples/acceptance_tests/006/lib/test.ak | 2 +- examples/acceptance_tests/007/lib/test.ak | 10 +++++----- examples/acceptance_tests/Makefile | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/acceptance_tests/006/lib/test.ak b/examples/acceptance_tests/006/lib/test.ak index 4de15698..9b49e9d0 100644 --- a/examples/acceptance_tests/006/lib/test.ak +++ b/examples/acceptance_tests/006/lib/test.ak @@ -1,3 +1,3 @@ test foo() { - (1, []) == (1, []) + #(1, []) == #(1, []) } diff --git a/examples/acceptance_tests/007/lib/test.ak b/examples/acceptance_tests/007/lib/test.ak index 82fc7f83..570c7b2f 100644 --- a/examples/acceptance_tests/007/lib/test.ak +++ b/examples/acceptance_tests/007/lib/test.ak @@ -1,9 +1,9 @@ -pub fn unzip(xs: List<(a, b)>) -> (List, List) { +pub fn unzip(xs: List<#(a, b)>) -> #(List, List) { when xs is { - [] -> ([], []) - [(a, b), ..rest] -> { - let (a_tail, b_tail) = unzip(rest) - ([a, ..a_tail], [b, ..b_tail]) + [] -> #([], []) + [#(a, b), ..rest] -> { + let #(a_tail, b_tail) = unzip(rest) + #([a, ..a_tail], [b, ..b_tail]) } } } diff --git a/examples/acceptance_tests/Makefile b/examples/acceptance_tests/Makefile index 4362b8ab..a523fad9 100644 --- a/examples/acceptance_tests/Makefile +++ b/examples/acceptance_tests/Makefile @@ -1,5 +1,5 @@ all: @for t in $(shell find . -regex ".*[0-9]\{3\}" -type d | sort); do \ - aiken check -d $${t}; \ + cargo run --quiet -- check -d $${t}; \ echo ""; \ done From dc5ae296dba0a564b1789aee1e49bb2d4b186b48 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Tue, 13 Dec 2022 14:25:26 -0500 Subject: [PATCH 19/39] implement anonymous functions, more tests pass now --- crates/lang/src/air.rs | 13 +- crates/lang/src/tipo.rs | 1 + crates/lang/src/uplc.rs | 329 +++++++++++++++++++++++++++------------- 3 files changed, 228 insertions(+), 115 deletions(-) diff --git a/crates/lang/src/air.rs b/crates/lang/src/air.rs index 1740367e..d7863db3 100644 --- a/crates/lang/src/air.rs +++ b/crates/lang/src/air.rs @@ -31,14 +31,10 @@ pub enum Air { variant_name: String, }, - // Fn { - // scope: Vec, - // tipo: Arc, - // is_capture: bool, - // args: Vec>>, - // body: Box, - // return_annotation: Option, - // }, + Fn { + scope: Vec, + params: Vec, + }, List { scope: Vec, count: usize, @@ -239,6 +235,7 @@ impl Air { | Air::List { scope, .. } | Air::ListAccessor { scope, .. } | Air::ListExpose { scope, .. } + | Air::Fn { scope, .. } | Air::Call { scope, .. } | Air::Builtin { scope, .. } | Air::BinOp { scope, .. } diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index ec09c20a..4a16a4a0 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -434,6 +434,7 @@ impl TypeVar { pub fn get_generic(&self) -> Option { match self { TypeVar::Generic { id } => Some(*id), + TypeVar::Link { tipo } => tipo.get_generic(), _ => None, } } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 130208ce..c006d780 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -181,7 +181,27 @@ impl<'a> CodeGenerator<'a> { }); } } - TypedExpr::Fn { .. } => todo!(), + TypedExpr::Fn { args, body, .. } => { + let mut func_body = vec![]; + let mut func_scope = scope.clone(); + func_scope.push(self.id_gen.next()); + self.build_ir(body, &mut func_body, func_scope); + let mut arg_names = vec![]; + for arg in args { + let name = arg + .arg_name + .get_variable_name() + .unwrap_or_default() + .to_string(); + arg_names.push(name); + } + + ir_stack.push(Air::Fn { + scope, + params: arg_names, + }); + ir_stack.append(&mut func_body); + } TypedExpr::List { elements, tail, @@ -1850,6 +1870,22 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } + + Air::Fn { params, .. } => { + let mut term = arg_stack.pop().unwrap(); + + for param in params.iter().rev() { + term = Term::Lambda { + parameter_name: Name { + text: param.clone(), + unique: 0.into(), + }, + body: term.into(), + }; + } + + arg_stack.push(term); + } Air::Call { count, .. } => { if count >= 1 { let mut term = arg_stack.pop().unwrap(); @@ -3244,20 +3280,21 @@ impl<'a> CodeGenerator<'a> { recursion_func_map, ); + let mut insert_var_vec = vec![]; let mut final_func_dep_ir = IndexMap::new(); - for func in func_index_map.clone() { if self.defined_functions.contains_key(&func.0) { continue; } - let mut funt_comp = func_components.get(&func.0).unwrap().clone(); let func_scope = func_index_map.get(&func.0).unwrap(); let mut dep_ir = vec![]; - + // deal with function dependencies while let Some(dependency) = funt_comp.dependencies.pop() { - if self.defined_functions.contains_key(&dependency) { + if self.defined_functions.contains_key(&dependency) + || func_components.get(&dependency).is_none() + { continue; } @@ -3276,15 +3313,46 @@ impl<'a> CodeGenerator<'a> { module_name: dependency.module_name.clone(), params: depend_comp.args.clone(), recursive: depend_comp.recursive, - variant_name: func.0.variant_name.clone(), + variant_name: dependency.variant_name.clone(), }]; - temp_ir.extend(depend_comp.ir.clone()); + for (index, ir) in depend_comp.ir.iter().enumerate() { + match_ir_for_recursion( + ir.clone(), + &mut insert_var_vec, + &FunctionAccessKey { + function_name: dependency.function_name.clone(), + module_name: dependency.module_name.clone(), + variant_name: dependency.variant_name.clone(), + }, + index, + ); + } + + let mut recursion_ir = depend_comp.ir.clone(); + for (index, ir) in insert_var_vec.clone() { + recursion_ir.insert(index, ir); + + let current_call = recursion_ir[index - 1].clone(); + + match current_call { + Air::Call { scope, count } => { + recursion_ir[index - 1] = Air::Call { + scope, + count: count + 1, + } + } + _ => unreachable!(), + } + } + + temp_ir.append(&mut recursion_ir); temp_ir.append(&mut dep_ir); dep_ir = temp_ir; self.defined_functions.insert(dependency, ()); + insert_var_vec = vec![]; } } @@ -3292,95 +3360,65 @@ impl<'a> CodeGenerator<'a> { } for (index, ir) in ir_stack.clone().into_iter().enumerate().rev() { - match ir { - Air::Var { constructor, .. } => { - if let ValueConstructorVariant::ModuleFn { .. } = &constructor.variant {} - } - a => { - let temp_func_index_map = func_index_map.clone(); - let to_insert = temp_func_index_map - .iter() - .filter(|func| { - func.1.clone() == a.scope() - && !self.defined_functions.contains_key(func.0) - }) - .collect_vec(); + { + let temp_func_index_map = func_index_map.clone(); + let to_insert = temp_func_index_map + .iter() + .filter(|func| { + func.1.clone() == ir.scope() && !self.defined_functions.contains_key(func.0) + }) + .collect_vec(); - for (function_access_key, scopes) in to_insert.into_iter() { - func_index_map.remove(function_access_key); + for (function_access_key, scopes) in to_insert.into_iter() { + func_index_map.remove(function_access_key); - self.defined_functions - .insert(function_access_key.clone(), ()); + self.defined_functions + .insert(function_access_key.clone(), ()); - let mut full_func_ir = - final_func_dep_ir.get(function_access_key).unwrap().clone(); + let mut full_func_ir = + final_func_dep_ir.get(function_access_key).unwrap().clone(); - let mut func_comp = - func_components.get(function_access_key).unwrap().clone(); + let mut func_comp = func_components.get(function_access_key).unwrap().clone(); - full_func_ir.push(Air::DefineFunc { - scope: scopes.clone(), - func_name: function_access_key.function_name.clone(), - module_name: function_access_key.module_name.clone(), - params: func_comp.args.clone(), - recursive: func_comp.recursive, - variant_name: function_access_key.variant_name.clone(), - }); + full_func_ir.push(Air::DefineFunc { + scope: scopes.clone(), + func_name: function_access_key.function_name.clone(), + module_name: function_access_key.module_name.clone(), + params: func_comp.args.clone(), + recursive: func_comp.recursive, + variant_name: function_access_key.variant_name.clone(), + }); - let mut insert_var_vec = vec![]; - for (index, air) in func_comp.ir.clone().into_iter().enumerate().rev() { - if let Air::Var { - scope, - constructor, - variant_name, - .. - } = air - { - if let ValueConstructorVariant::ModuleFn { - name: func_name, - module, - .. - } = constructor.clone().variant - { - if func_name.clone() - == function_access_key.function_name.clone() - && module == function_access_key.module_name.clone() - { - insert_var_vec.push(( - index, - Air::Var { - scope: scope.clone(), - constructor: constructor.clone(), - name: func_name.clone(), - variant_name, - }, - )); - } + for (index, ir) in func_comp.ir.clone().iter().enumerate() { + match_ir_for_recursion( + ir.clone(), + &mut insert_var_vec, + function_access_key, + index, + ); + } + + for (index, ir) in insert_var_vec { + func_comp.ir.insert(index, ir); + + let current_call = func_comp.ir[index - 1].clone(); + + match current_call { + Air::Call { scope, count } => { + func_comp.ir[index - 1] = Air::Call { + scope, + count: count + 1, } } + _ => unreachable!("{current_call:#?}"), } + } + insert_var_vec = vec![]; - for (index, ir) in insert_var_vec { - func_comp.ir.insert(index, ir); + full_func_ir.extend(func_comp.ir.clone()); - let current_call = func_comp.ir[index - 1].clone(); - - match current_call { - Air::Call { scope, count } => { - func_comp.ir[index - 1] = Air::Call { - scope, - count: count + 1, - } - } - _ => unreachable!(), - } - } - - full_func_ir.extend(func_comp.ir.clone()); - - for ir in full_func_ir.into_iter().rev() { - ir_stack.insert(index, ir); - } + for ir in full_func_ir.into_iter().rev() { + ir_stack.insert(index, ir); } } } @@ -3492,7 +3530,6 @@ impl<'a> CodeGenerator<'a> { function_name: name.clone(), variant_name: String::new(), }; - if let Some(scope_prev) = to_be_defined_map.get(&function_key) { let new_scope = get_common_ancestor(scope, scope_prev); @@ -3508,19 +3545,25 @@ impl<'a> CodeGenerator<'a> { let (param_types, _) = constructor.tipo.function_types().unwrap(); - let mut generic_id_type_vec = vec![]; + let mut generics_type_map: HashMap> = HashMap::new(); for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { - generic_id_type_vec.append(&mut get_generics_and_type( + let mut map = generics_type_map.into_iter().collect_vec(); + map.append(&mut get_generics_and_type( &arg.tipo, ¶m_types[index], )); + + generics_type_map = map.into_iter().collect(); } } - let (variant_name, mut func_ir) = - self.monomorphize(func_ir, generic_id_type_vec); + let (variant_name, mut func_ir) = self.monomorphize( + func_ir, + generics_type_map, + &constructor.tipo, + ); function_key = FunctionAccessKey { module_name: module.clone(), @@ -3563,6 +3606,8 @@ impl<'a> CodeGenerator<'a> { function_name: func_name.clone(), variant_name: variant_name.clone(), }; + + let function = self.functions.get(¤t_func); if function_key.clone() == current_func_as_variant { func_ir[index] = Air::Var { scope, @@ -3582,6 +3627,26 @@ impl<'a> CodeGenerator<'a> { variant_name: variant_name.clone(), }; func_calls.push(current_func_as_variant); + } else if let (Some(function), Type::Fn { args, .. }) = + (function, &*tipo) + { + if function + .arguments + .iter() + .any(|arg| arg.tipo.is_generic()) + { + let mut new_name = String::new(); + for arg in args.iter() { + get_variant_name(&mut new_name, arg); + } + func_calls.push(FunctionAccessKey { + module_name: module, + function_name: func_name, + variant_name: new_name, + }); + } else { + func_calls.push(current_func); + } } else { func_calls.push(current_func); } @@ -3666,9 +3731,11 @@ impl<'a> CodeGenerator<'a> { fn monomorphize( &mut self, ir: Vec, - generic_types: Vec<(u64, Arc)>, + generic_types: HashMap>, + full_type: &Arc, ) -> (String, Vec) { let mut new_air = ir.clone(); + let mut new_name = String::new(); for (index, ir) in ir.into_iter().enumerate() { match ir { @@ -3676,20 +3743,33 @@ impl<'a> CodeGenerator<'a> { constructor, scope, name, - variant_name, + .. } => { if constructor.tipo.is_generic() { let mut tipo = constructor.tipo.clone(); + find_generics_to_replace(&mut tipo, &generic_types); + let mut variant = String::new(); + let mut constructor = constructor.clone(); constructor.tipo = tipo; + if let Type::Fn { args, .. } = &*constructor.tipo { + if matches!( + constructor.variant, + ValueConstructorVariant::ModuleFn { .. } + ) { + for arg in args { + get_variant_name(&mut variant, arg); + } + } + } new_air[index] = Air::Var { scope, constructor, name, - variant_name, + variant_name: variant, }; } } @@ -3887,24 +3967,59 @@ impl<'a> CodeGenerator<'a> { } } - let mut new_name = String::new(); - - for (_, t) in generic_types { - get_variant_name(&mut new_name, t); + if let Type::Fn { args, .. } = &**full_type { + for arg in args { + get_variant_name(&mut new_name, arg); + } } (new_name, new_air) } } -fn find_generics_to_replace(tipo: &mut Arc, generic_types: &[(u64, Arc)]) { - if let Some(id) = tipo.get_generic() { - if let Some((_, t)) = generic_types - .iter() - .find(|(generic_id, _)| id == *generic_id) +fn match_ir_for_recursion( + ir: Air, + insert_var_vec: &mut Vec<(usize, Air)>, + function_access_key: &FunctionAccessKey, + index: usize, +) { + if let Air::Var { + scope, + constructor, + variant_name, + .. + } = ir + { + if let ValueConstructorVariant::ModuleFn { + name: func_name, + module, + .. + } = constructor.clone().variant { - *tipo = t.clone(); + let var_func_access = FunctionAccessKey { + module_name: module, + function_name: func_name.clone(), + variant_name: variant_name.clone(), + }; + + if function_access_key.clone() == var_func_access { + insert_var_vec.push(( + index, + Air::Var { + scope, + constructor, + name: func_name, + variant_name, + }, + )); + } } + } +} + +fn find_generics_to_replace(tipo: &mut Arc, generic_types: &HashMap>) { + if let Some(id) = tipo.get_generic() { + *tipo = generic_types.get(&id).unwrap().clone(); } else if tipo.is_generic() { match &**tipo { Type::App { @@ -3992,7 +4107,7 @@ fn get_generics_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc)> { generics_ids } -fn get_variant_name(new_name: &mut String, t: Arc) { +fn get_variant_name(new_name: &mut String, t: &Arc) { new_name.push_str(&format!( "_{}", if t.is_string() { @@ -4007,13 +4122,13 @@ fn get_variant_name(new_name: &mut String, t: Arc) { let fst_type = &pair_type.get_inner_types()[0]; let snd_type = &pair_type.get_inner_types()[1]; - get_variant_name(&mut full_type, fst_type.clone()); - get_variant_name(&mut full_type, snd_type.clone()); + get_variant_name(&mut full_type, fst_type); + get_variant_name(&mut full_type, snd_type); full_type } else if t.is_list() { let mut full_type = "list".to_string(); let list_type = &t.get_inner_types()[0]; - get_variant_name(&mut full_type, list_type.clone()); + get_variant_name(&mut full_type, list_type); full_type } else { "data".to_string() From 8393d8555c58737c011dde8a32e4e3294cdb6d38 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Tue, 13 Dec 2022 14:43:08 -0500 Subject: [PATCH 20/39] fill constants to data so now test 006 passes --- crates/lang/src/uplc.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index c006d780..b4b20f52 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -10,7 +10,7 @@ use uplc::{ builtins::DefaultFunction, machine::runtime::convert_constr_to_tag, parser::interner::Interner, - BigInt, Constr, PlutusData, + BigInt, Constr, KeyValuePairs, PlutusData, }; use crate::{ @@ -4155,8 +4155,32 @@ fn convert_constants_to_data(constants: Vec) -> Vec any_constructor: None, fields: vec![], })), - UplcConstant::ProtoList(_, _) => todo!(), - UplcConstant::ProtoPair(_, _, _, _) => todo!(), + UplcConstant::ProtoList(_, constants) => { + let inner_constants = convert_constants_to_data(constants) + .into_iter() + .map(|constant| match constant { + UplcConstant::Data(d) => d, + _ => todo!(), + }) + .collect_vec(); + + UplcConstant::Data(PlutusData::Array(inner_constants)) + } + UplcConstant::ProtoPair(_, _, left, right) => { + let inner_constants = vec![*left, *right]; + let inner_constants = convert_constants_to_data(inner_constants) + .into_iter() + .map(|constant| match constant { + UplcConstant::Data(d) => d, + _ => todo!(), + }) + .collect_vec(); + + UplcConstant::Data(PlutusData::Map(KeyValuePairs::Def(vec![( + inner_constants[0].clone(), + inner_constants[1].clone(), + )]))) + } d @ UplcConstant::Data(_) => d, _ => unreachable!(), }; From 11c793dd2a15fc22e5768501a85d3909a4dbabb1 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 13 Dec 2022 15:02:46 -0500 Subject: [PATCH 21/39] feat(Type): add is_option method --- crates/lang/src/tipo.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index 4a16a4a0..6b457ff3 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -135,6 +135,14 @@ impl Type { } } + pub fn is_option(&self) -> bool { + match self { + Self::App { module, name, .. } if "Option" == name && module.is_empty() => true, + Self::Var { tipo } => tipo.borrow().is_option(), + _ => false, + } + } + pub fn is_map(&self) -> bool { match self { Self::App { @@ -416,6 +424,13 @@ impl TypeVar { } } + pub fn is_option(&self) -> bool { + match self { + Self::Link { tipo } => tipo.is_option(), + _ => false, + } + } + pub fn is_map(&self) -> bool { match self { Self::Link { tipo } => tipo.is_map(), From f53caff53830f9400641518a3c9cf9eeb22631f1 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 22:49:55 +0100 Subject: [PATCH 22/39] Add new acceptance test scenario (011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:4022:40 ╰─▶ called `Option::unwrap()` on a `None` value ``` --- examples/acceptance_tests/011/aiken.toml | 2 ++ examples/acceptance_tests/011/lib/test.ak | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 examples/acceptance_tests/011/aiken.toml create mode 100644 examples/acceptance_tests/011/lib/test.ak diff --git a/examples/acceptance_tests/011/aiken.toml b/examples/acceptance_tests/011/aiken.toml new file mode 100644 index 00000000..929d7fd5 --- /dev/null +++ b/examples/acceptance_tests/011/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_011" +version = "0.0.0" diff --git a/examples/acceptance_tests/011/lib/test.ak b/examples/acceptance_tests/011/lib/test.ak new file mode 100644 index 00000000..9c515622 --- /dev/null +++ b/examples/acceptance_tests/011/lib/test.ak @@ -0,0 +1,10 @@ +pub fn map(xs: List, f: fn(a) -> result) -> List { + when xs is { + [] -> [] + [x, ..rest] -> [f(x), ..map(rest, f)] + } +} + +test map_1() { + map([], fn(n) { n + 1 }) == [] +} From 9c902cdf8985a26ec75f3f688e0688426d0d8b5c Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 22:54:07 +0100 Subject: [PATCH 23/39] Add new acceptance test scenario (012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` Error: × Main thread panicked. ├─▶ at crates/lang/src/uplc.rs:3413:34 ╰─▶ internal error: entered unreachable code: Var { scope: [ 0, 1, 3, 24, 25, 29, 32, ], constructor: ValueConstructor { public: false, variant: LocalVariable { location: 46..62, }, tipo: Fn { args: [ Var { tipo: RefCell { value: Link { tipo: App { public: true, module: "", name: "Int", args: [], }, }, }, }, ], ret: App { public: true, module: "", name: "Bool", args: [], }, }, }, name: "f", variant_name: "", } ``` --- examples/acceptance_tests/012/aiken.toml | 2 ++ examples/acceptance_tests/012/lib/test.ak | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 examples/acceptance_tests/012/aiken.toml create mode 100644 examples/acceptance_tests/012/lib/test.ak diff --git a/examples/acceptance_tests/012/aiken.toml b/examples/acceptance_tests/012/aiken.toml new file mode 100644 index 00000000..ed8f7704 --- /dev/null +++ b/examples/acceptance_tests/012/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_012" +version = "0.0.0" diff --git a/examples/acceptance_tests/012/lib/test.ak b/examples/acceptance_tests/012/lib/test.ak new file mode 100644 index 00000000..b0a781e3 --- /dev/null +++ b/examples/acceptance_tests/012/lib/test.ak @@ -0,0 +1,18 @@ +use aiken/builtin + +pub fn filter(xs: List, f: fn(a) -> Bool) -> List { + when xs is { + [] -> [] + [x, ..rest] -> + if f(x) { + [x, ..filter(rest, f)] + } else { + filter(rest, f) + } + } +} + +test filter_1() { + filter([1, + 2, 3, 4, 5, 6], fn(x) { builtin.mod_integer(x, 2) == 0 }) == [2, 4, 6] +} From bc7c236b3b8393d6022105f8e97ba5a2d9ec9950 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 13 Dec 2022 23:02:49 +0100 Subject: [PATCH 24/39] Add new acceptance test scenario (013) ``` ERROR: The provided Plutus code called 'error' ``` --- examples/acceptance_tests/013/aiken.toml | 2 ++ examples/acceptance_tests/013/lib/test.ak | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 examples/acceptance_tests/013/aiken.toml create mode 100644 examples/acceptance_tests/013/lib/test.ak diff --git a/examples/acceptance_tests/013/aiken.toml b/examples/acceptance_tests/013/aiken.toml new file mode 100644 index 00000000..c86153a1 --- /dev/null +++ b/examples/acceptance_tests/013/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_013" +version = "0.0.0" diff --git a/examples/acceptance_tests/013/lib/test.ak b/examples/acceptance_tests/013/lib/test.ak new file mode 100644 index 00000000..8f0aa2bb --- /dev/null +++ b/examples/acceptance_tests/013/lib/test.ak @@ -0,0 +1,13 @@ +pub fn unzip(xs: List<#(a, b)>) -> #(List, List) { + when xs is { + [] -> #([], []) + [#(a, b), ..rest] -> { + let #(a_tail, b_tail) = unzip(rest) + #([a, ..a_tail], [b, ..b_tail]) + } + } +} + +test unzip_1() { + unzip([#(1, "a"), #(2, "b")]) == #([1, 2], ["a", "b"]) +} From 95df5f9137051b4078601605d24ec9dc9730de7c Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 14 Dec 2022 01:08:41 +0100 Subject: [PATCH 25/39] Add new acceptance test scenario (014) --- examples/acceptance_tests/014/aiken.toml | 2 ++ examples/acceptance_tests/014/lib/test.ak | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 examples/acceptance_tests/014/aiken.toml create mode 100644 examples/acceptance_tests/014/lib/test.ak diff --git a/examples/acceptance_tests/014/aiken.toml b/examples/acceptance_tests/014/aiken.toml new file mode 100644 index 00000000..2f582156 --- /dev/null +++ b/examples/acceptance_tests/014/aiken.toml @@ -0,0 +1,2 @@ +name = "acceptance_test_014" +version = "0.0.0" diff --git a/examples/acceptance_tests/014/lib/test.ak b/examples/acceptance_tests/014/lib/test.ak new file mode 100644 index 00000000..bac75f88 --- /dev/null +++ b/examples/acceptance_tests/014/lib/test.ak @@ -0,0 +1,17 @@ +pub fn range(from: Int, to: Int) -> List { + if from > to { + [] + } else { + [from, ..range(from + 1, to)] + } +} + +test range_1() { + range(0, 2) == [0, 1, 2] +} + +// NOTE: +// Somehow, the left-hand evaluates to: [#02, #01, #00, #32] +test range_2() { + range(0 - 1, 2) == [0 - 1, 0, 1, 2] +} From b6962ba9d335a0c958115352dfa1a280e6806094 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 14 Dec 2022 00:31:14 +0100 Subject: [PATCH 26/39] Add 'eval' command to evaluate target aiken function Pretty useful for debbugging. Though, on second-thoughts, this is something we may want to review later and maybe have that done by default for tests. At the moment, we expects tests to unify to `bool`, and treat `false` values as failing tests. Yet, on failures, this gives little information about what's wrong with the test. It'd be nice to either have better way to assert in tests, or, to simply accept non-bool tests, and show whatever the test evaluates to as a debug output. --- crates/cli/src/cmd/eval.rs | 27 +++++++++ crates/cli/src/cmd/mod.rs | 1 + crates/cli/src/lib.rs | 97 +++++++++++++++++++++++---------- crates/cli/src/main.rs | 4 +- crates/lang/src/uplc.rs | 13 ++++- crates/project/src/lib.rs | 90 ++++++++++++++++++------------ crates/project/src/options.rs | 1 + crates/project/src/telemetry.rs | 13 +++-- 8 files changed, 176 insertions(+), 70 deletions(-) create mode 100644 crates/cli/src/cmd/eval.rs diff --git a/crates/cli/src/cmd/eval.rs b/crates/cli/src/cmd/eval.rs new file mode 100644 index 00000000..15dfabb7 --- /dev/null +++ b/crates/cli/src/cmd/eval.rs @@ -0,0 +1,27 @@ +use aiken_project::options::{CodeGenMode, Options}; +use std::path::PathBuf; + +#[derive(clap::Args)] +/// Evaluate a chosen function with no argument. +pub struct Args { + /// Path to project + #[clap(short, long)] + directory: Option, + + /// Evaluate the given function + #[clap(short, long)] + function_name: String, +} + +pub fn exec( + Args { + directory, + function_name, + }: Args, +) -> miette::Result<()> { + crate::with_project(directory, |p| { + p.compile(Options { + code_gen_mode: CodeGenMode::Eval(function_name.clone()), + }) + }) +} diff --git a/crates/cli/src/cmd/mod.rs b/crates/cli/src/cmd/mod.rs index 75b5fe74..4fc06408 100644 --- a/crates/cli/src/cmd/mod.rs +++ b/crates/cli/src/cmd/mod.rs @@ -1,6 +1,7 @@ pub mod build; pub mod check; pub mod error; +pub mod eval; pub mod fmt; pub mod lsp; pub mod new; diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 0034bf3e..615fb7f7 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -2,7 +2,7 @@ use std::{env, path::PathBuf}; use aiken_project::{ config::Config, - telemetry::{self, TestInfo}, + telemetry::{self, EvalInfo}, Project, }; use miette::IntoDiagnostic; @@ -76,37 +76,30 @@ impl telemetry::EventListener for Terminal { output_path.to_str().unwrap_or("").bright_blue() ); } + telemetry::Event::EvaluatingFunction { results } => { + println!("{}\n", "...Evaluating function".bold().purple()); + + let (max_mem, max_cpu) = find_max_execution_units(&results); + + for eval_info in &results { + println!("{}", fmt_eval(eval_info, max_mem, max_cpu)) + } + } telemetry::Event::RunningTests => { println!("{}\n", "...Running tests".bold().purple()); } telemetry::Event::FinishedTests { tests } => { - let (max_mem, max_cpu) = tests.iter().fold( - (0, 0), - |(max_mem, max_cpu), TestInfo { spent_budget, .. }| { - if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu { - (spent_budget.mem, spent_budget.cpu) - } else if spent_budget.mem > max_mem { - (spent_budget.mem, max_cpu) - } else if spent_budget.cpu > max_cpu { - (max_mem, spent_budget.cpu) - } else { - (max_mem, max_cpu) - } - }, - ); + let (max_mem, max_cpu) = find_max_execution_units(&tests); - let max_mem = max_mem.to_string().len() as i32; - let max_cpu = max_cpu.to_string().len() as i32; - - for test_info in &tests { - println!("{}", fmt_test(test_info, max_mem, max_cpu)) + for eval_info in &tests { + println!("{}", fmt_test(eval_info, max_mem, max_cpu)) } let (n_passed, n_failed) = tests .iter() .fold((0, 0), |(n_passed, n_failed), test_info| { - if test_info.is_passing { + if test_info.success { (n_passed + 1, n_failed) } else { (n_passed, n_failed + 1) @@ -128,26 +121,72 @@ impl telemetry::EventListener for Terminal { } } -fn fmt_test(test_info: &TestInfo, max_mem: i32, max_cpu: i32) -> String { - let TestInfo { - is_passing, - test, +fn fmt_test(eval_info: &EvalInfo, max_mem: i32, max_cpu: i32) -> String { + let EvalInfo { + success, + script, spent_budget, - } = test_info; + .. + } = eval_info; let ExBudget { mem, cpu } = spent_budget; format!( " [{}] [mem: {}, cpu: {}] {}::{}", - if *is_passing { + if *success { "PASS".bold().green().to_string() } else { "FAIL".bold().red().to_string() }, pad_left(mem.to_string(), max_mem, " "), pad_left(cpu.to_string(), max_cpu, " "), - test.module.blue(), - test.name.bright_blue() + script.module.blue(), + script.name.bright_blue() + ) +} + +fn fmt_eval(eval_info: &EvalInfo, max_mem: i32, max_cpu: i32) -> String { + let EvalInfo { + output, + script, + spent_budget, + .. + } = eval_info; + + let ExBudget { mem, cpu } = spent_budget; + + format!( + " {}::{} [mem: {}, cpu: {}]\n │\n ╰─▶ {}", + script.module.blue(), + script.name.bright_blue(), + pad_left(mem.to_string(), max_mem, " "), + pad_left(cpu.to_string(), max_cpu, " "), + output + .as_ref() + .map(|x| format!("{}", x)) + .unwrap_or("Error.".to_string()), + ) +} + +fn find_max_execution_units(xs: &Vec) -> (i32, i32) { + let (max_mem, max_cpu) = xs.iter().fold( + (0, 0), + |(max_mem, max_cpu), EvalInfo { spent_budget, .. }| { + if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu { + (spent_budget.mem, spent_budget.cpu) + } else if spent_budget.mem > max_mem { + (spent_budget.mem, max_cpu) + } else if spent_budget.cpu > max_cpu { + (max_mem, spent_budget.cpu) + } else { + (max_mem, max_cpu) + } + }, + ); + + ( + max_mem.to_string().len() as i32, + max_cpu.to_string().len() as i32, ) } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d624ce6d..3cda6690 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,4 +1,4 @@ -use aiken::cmd::{build, check, fmt, lsp, new, tx, uplc}; +use aiken::cmd::{build, check, eval, fmt, lsp, new, tx, uplc}; use clap::Parser; /// Aiken: a smart-contract language and toolchain for Cardano @@ -11,6 +11,7 @@ pub enum Cmd { Fmt(fmt::Args), Build(build::Args), Check(check::Args), + Eval(eval::Args), #[clap(hide = true)] Lsp(lsp::Args), @@ -35,6 +36,7 @@ fn main() -> miette::Result<()> { Cmd::Fmt(args) => fmt::exec(args), Cmd::Build(args) => build::exec(args), Cmd::Check(args) => check::exec(args), + Cmd::Eval(args) => eval::exec(args), Cmd::Lsp(args) => lsp::exec(args), Cmd::Tx(sub_cmd) => tx::exec(sub_cmd), Cmd::Uplc(sub_cmd) => uplc::exec(sub_cmd), diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index b4b20f52..23054ff7 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -96,7 +96,12 @@ impl<'a> CodeGenerator<'a> { } } - pub fn generate(&mut self, body: TypedExpr, arguments: Vec) -> Program { + pub fn generate( + &mut self, + body: TypedExpr, + arguments: Vec, + wrap_as_validator: bool, + ) -> Program { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; @@ -113,7 +118,11 @@ impl<'a> CodeGenerator<'a> { } // Wrap the validator body if ifThenElse term unit error - term = builder::final_wrapper(term); + term = if wrap_as_validator { + builder::final_wrapper(term) + } else { + term + }; for arg in arguments.iter().rev() { term = Term::Lambda { diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index ba483096..16ca71ae 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -13,7 +13,7 @@ pub mod script; pub mod telemetry; use aiken_lang::{ - ast::{Definition, Function, ModuleKind, TypedFunction}, + ast::{Definition, Function, ModuleKind, TypedDefinition, TypedFunction}, builtins, tipo::TypeInfo, uplc::{CodeGenerator, DataTypeKey, FunctionAccessKey}, @@ -28,9 +28,9 @@ use pallas::{ use pallas_traverse::ComputeHash; use script::Script; use serde_json::json; -use telemetry::{EventListener, TestInfo}; +use telemetry::{EvalInfo, EventListener}; use uplc::{ - ast::{DeBruijn, Program}, + ast::{Constant, DeBruijn, Program, Term}, machine::cost_model::ExBudget, }; @@ -146,8 +146,25 @@ where self.write_build_outputs(programs, uplc_dump)?; } CodeGenMode::Test(match_tests) => { - let tests = self.test_gen(&checked_modules)?; - self.run_tests(tests, match_tests); + let tests = self.scripts_gen(&checked_modules, |def| match def { + Definition::Test(..) => true, + _ => false, + })?; + if !tests.is_empty() { + self.event_listener.handle_event(Event::RunningTests); + } + let results = self.eval_scripts(tests, match_tests); + self.event_listener + .handle_event(Event::FinishedTests { tests: results }); + } + CodeGenMode::Eval(func_name) => { + let scripts = self.scripts_gen(&checked_modules, |def| match def { + Definition::Fn(..) => true, + _ => false, + })?; + let results = self.eval_scripts(scripts, Some(func_name)); + self.event_listener + .handle_event(Event::EvaluatingFunction { results }); } CodeGenMode::NoOp => (), } @@ -427,7 +444,7 @@ where &self.module_types, ); - let program = generator.generate(body, arguments); + let program = generator.generate(body, arguments, true); let script = Script::new(module_name, name, program.try_into().unwrap()); @@ -438,7 +455,11 @@ where } // TODO: revisit ownership and lifetimes of data in this function - fn test_gen(&mut self, checked_modules: &CheckedModules) -> Result, Error> { + fn scripts_gen( + &mut self, + checked_modules: &CheckedModules, + should_collect: fn(&TypedDefinition) -> bool, + ) -> Result, Error> { let mut programs = Vec::new(); let mut functions = HashMap::new(); let mut type_aliases = HashMap::new(); @@ -447,7 +468,7 @@ where let mut constants = HashMap::new(); // let mut indices_to_remove = Vec::new(); - let mut tests = Vec::new(); + let mut scripts = Vec::new(); for module in checked_modules.values() { for (_index, def) in module.ast.definitions().enumerate() { @@ -461,9 +482,14 @@ where }, func, ); + if should_collect(def) { + scripts.push((module.name.clone(), func)); + } } Definition::Test(func) => { - tests.push((module.name.clone(), func)); + if should_collect(def) { + scripts.push((module.name.clone(), func)); + } // indices_to_remove.push(index); } Definition::TypeAlias(ta) => { @@ -492,7 +518,7 @@ where // } } - for (module_name, func_def) in tests { + for (module_name, func_def) in scripts { let Function { arguments, name, @@ -509,7 +535,7 @@ where &self.module_types, ); - let program = generator.generate(body.clone(), arguments.clone()); + let program = generator.generate(body.clone(), arguments.clone(), false); let script = Script::new(module_name, name.to_string(), program.try_into().unwrap()); @@ -519,7 +545,7 @@ where Ok(programs) } - fn run_tests(&self, tests: Vec