From 29a30aa61ff864209ad551a31aa819413a256c36 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 28 Nov 2022 02:49:39 -0500 Subject: [PATCH] feat: finished when constr is for IR and code gen --- crates/lang/src/ir.rs | 2 +- crates/lang/src/uplc_two.rs | 406 +++++++++++++++++++++++++---- crates/uplc/src/ast/builder.rs | 13 + examples/sample/validators/swap.ak | 4 +- 4 files changed, 369 insertions(+), 56 deletions(-) diff --git a/crates/lang/src/ir.rs b/crates/lang/src/ir.rs index 199dab98..2def574e 100644 --- a/crates/lang/src/ir.rs +++ b/crates/lang/src/ir.rs @@ -141,7 +141,7 @@ pub enum IR { FieldsExpose { count: usize, - indices: Vec, + indices: Vec<(usize, String, Arc)>, }, // ModuleSelect { diff --git a/crates/lang/src/uplc_two.rs b/crates/lang/src/uplc_two.rs index 905055f9..5bdc7aa3 100644 --- a/crates/lang/src/uplc_two.rs +++ b/crates/lang/src/uplc_two.rs @@ -1,9 +1,9 @@ -use std::{collections::HashMap, ops::Deref, sync::Arc}; +use std::{collections::HashMap, ops::Deref, sync::Arc, vec}; use itertools::Itertools; use uplc::{ ast::{ - builder::{self, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD}, + builder::{self, constr_index_exposer, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD}, Constant, Name, Program, Term, }, builtins::DefaultFunction, @@ -515,26 +515,35 @@ impl<'a> CodeGenerator<'a> { tipo, .. } => { - if *is_record { - let data_type_key = match tipo.as_ref() { - Type::Fn { ret, .. } => match &**ret { - Type::App { module, name, .. } => DataTypeKey { - module_name: module.clone(), - defined_type: name.clone(), - }, - _ => unreachable!(), + let data_type_key = match tipo.as_ref() { + Type::Fn { ret, .. } => match &**ret { + Type::App { module, name, .. } => DataTypeKey { + module_name: module.clone(), + defined_type: name.clone(), }, _ => unreachable!(), - }; + }, + Type::App { module, name, .. } => DataTypeKey { + module_name: module.clone(), + defined_type: name.clone(), + }, + _ => unreachable!(), + }; - let data_type = self.data_types.get(&data_type_key).unwrap(); - let (index, constructor_type) = data_type - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == constr_name) - .unwrap(); + let data_type = self.data_types.get(&data_type_key).unwrap(); + let (index, constructor_type) = data_type + .constructors + .iter() + .enumerate() + .find(|(_, dt)| &dt.name == constr_name) + .unwrap(); + // push constructor Index + pattern_vec.push(IR::Int { + value: index.to_string(), + }); + + if *is_record { let field_map = match constructor { tipo::PatternConstructor::Record { field_map, .. } => { field_map.clone().unwrap() @@ -569,43 +578,64 @@ impl<'a> CodeGenerator<'a> { .sorted_by(|item1, item2| item1.2.cmp(&item2.2)) .collect::>(); - // push constructor Index - pattern_vec.push(IR::Int { - value: index.to_string(), - }); if !arguments_index.is_empty() { pattern_vec.push(IR::FieldsExpose { count: arguments_index.len() + 2, indices: arguments_index .iter() - .map(|(_, _, index, _)| *index) + .map(|(label, var_name, index, _)| { + let field_type = type_map.get(label).unwrap(); + (*index, var_name.clone(), field_type.clone()) + }) .collect_vec(), }); - - for arg in arguments_index { - let field_label = arg.0; - let field_type = type_map.get(&field_label).unwrap(); - let field_var = arg.1; - pattern_vec.push(IR::Var { - constructor: ValueConstructor::public( - field_type.clone(), - ValueConstructorVariant::LocalVariable { - location: Span::empty(), - }, - ), - name: field_var, - }) - } } - pattern_vec.append(values); } else { - println!("todo"); + let mut type_map: HashMap> = HashMap::new(); + + for (index, arg) in constructor_type.arguments.iter().enumerate() { + let field_type = arg.tipo.clone(); + + type_map.insert(index, field_type); + } + + let arguments_index = arguments + .iter() + .enumerate() + .map(|(index, item)| { + let (discard, var_name) = match &item.value { + Pattern::Var { name, .. } => (false, name.clone()), + Pattern::Discard { .. } => (true, "".to_string()), + Pattern::List { .. } => todo!(), + Pattern::Constructor { .. } => todo!(), + _ => todo!(), + }; + + (var_name, index, discard) + }) + .filter(|(_, _, discard)| !discard) + .collect::>(); + + if !arguments_index.is_empty() { + pattern_vec.push(IR::FieldsExpose { + count: arguments_index.len() + 2, + indices: arguments_index + .iter() + .map(|(name, index, _)| { + let field_type = type_map.get(index).unwrap(); + + (*index, name.clone(), field_type.clone()) + }) + .collect_vec(), + }); + } } + pattern_vec.append(values); } } } - fn uplc_code_gen(&self, ir_stack: &mut Vec) -> Term { + fn uplc_code_gen(&mut self, ir_stack: &mut Vec) -> Term { let mut arg_stack: Vec> = vec![]; while let Some(ir_element) = ir_stack.pop() { @@ -615,7 +645,7 @@ impl<'a> CodeGenerator<'a> { arg_stack[0].clone() } - fn gen_uplc(&self, ir: IR, arg_stack: &mut Vec>) { + fn gen_uplc(&mut self, ir: IR, arg_stack: &mut Vec>) { match ir { IR::Int { value } => { let integer = value.parse().unwrap(); @@ -924,7 +954,14 @@ impl<'a> CodeGenerator<'a> { } } BinOp::NotEq => todo!(), - BinOp::LtInt => todo!(), + BinOp::LtInt => Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::LessThanInteger).into(), + argument: left.into(), + } + .into(), + argument: right.into(), + }, BinOp::LtEqInt => todo!(), BinOp::GtEqInt => todo!(), BinOp::GtInt => Term::Apply { @@ -976,7 +1013,24 @@ impl<'a> CodeGenerator<'a> { IR::DefineConst { .. } => todo!(), IR::DefineConstrFields { .. } => todo!(), IR::DefineConstrFieldAccess { .. } => todo!(), - IR::Lam { .. } => todo!(), + IR::Lam { name } => { + let arg = arg_stack.pop().unwrap(); + + let mut term = arg_stack.pop().unwrap(); + + term = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: name, + unique: 0.into(), + }, + body: term.into(), + } + .into(), + argument: arg.into(), + }; + arg_stack.push(term); + } IR::When { subject_name, tipo, .. } => { @@ -984,7 +1038,8 @@ impl<'a> CodeGenerator<'a> { let mut term = arg_stack.pop().unwrap(); - term = if tipo.is_int() { + term = if tipo.is_int() || tipo.is_bytearray() || tipo.is_string() || tipo.is_list() + { Term::Apply { function: Term::Lambda { parameter_name: Name { @@ -997,7 +1052,17 @@ impl<'a> CodeGenerator<'a> { argument: subject.into(), } } else { - todo!() + Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: subject_name, + unique: 0.into(), + }, + body: term.into(), + } + .into(), + argument: constr_index_exposer(subject).into(), + } }; arg_stack.push(term); @@ -1077,12 +1142,7 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } IR::Finally => { - let clause = arg_stack.pop().unwrap(); - - if !clause.is_unit() { - let _body = arg_stack.pop().unwrap(); - todo!(); - } + let _clause = arg_stack.pop().unwrap(); } IR::If { .. } => todo!(), IR::Constr { .. } => todo!(), @@ -1130,7 +1190,247 @@ impl<'a> CodeGenerator<'a> { arg_stack.push(term); } - IR::FieldsExpose { .. } => todo!(), + IR::FieldsExpose { + count: _count, + indices, + } => { + self.needs_field_access = true; + + let constr_var = arg_stack.pop().unwrap(); + let mut body = arg_stack.pop().unwrap(); + + let mut indices = indices.into_iter().rev(); + let highest = indices.next().unwrap(); + let mut id_list = vec![]; + + for _ in 0..highest.0 { + id_list.push(self.id_gen.next()); + } + + let constr_name_lam = format!("__constr_fields_{}", self.id_gen.next()); + let highest_loop_index = highest.0 as i32 - 1; + let last_prev_tail = Term::Var(Name { + text: if highest_loop_index == -1 { + constr_name_lam.clone() + } else { + format!( + "__tail_{}_{}", + highest_loop_index, id_list[highest_loop_index as usize] + ) + }, + unique: 0.into(), + }); + + let unwrapper = if highest.2.is_int() { + Term::Apply { + function: DefaultFunction::UnIData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList).force_wrap().into(), + argument: last_prev_tail.into(), + } + .into(), + } + } else if highest.2.is_bytearray() { + Term::Apply { + function: DefaultFunction::UnBData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList).force_wrap().into(), + argument: last_prev_tail.into(), + } + .into(), + } + } else if highest.2.is_list() { + Term::Apply { + function: DefaultFunction::UnListData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList).force_wrap().into(), + argument: last_prev_tail.into(), + } + .into(), + } + } else { + Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList).force_wrap().into(), + argument: last_prev_tail.into(), + } + }; + + body = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: highest.1, + unique: 0.into(), + }, + body: body.into(), + } + .into(), + argument: unwrapper.into(), + }; + + let mut current_field = None; + for index in (0..highest.0).rev() { + let current_tail_index = index; + let previous_tail_index = if index == 0 { 0 } else { index - 1 }; + let current_tail_id = id_list[index]; + let previous_tail_id = if index == 0 { 0 } else { id_list[index - 1] }; + if current_field.is_none() { + current_field = indices.next(); + } + + let prev_tail = if index == 0 { + Term::Var(Name { + text: constr_name_lam.clone(), + unique: 0.into(), + }) + } else { + Term::Var(Name { + text: format!("__tail_{previous_tail_index}_{previous_tail_id}"), + unique: 0.into(), + }) + }; + + if let Some(ref field) = current_field { + if field.0 == index { + let unwrapper = if field.2.is_int() { + Term::Apply { + function: DefaultFunction::UnIData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList) + .force_wrap() + .into(), + argument: prev_tail.clone().into(), + } + .into(), + } + } else if field.2.is_bytearray() { + Term::Apply { + function: DefaultFunction::UnBData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList) + .force_wrap() + .into(), + argument: prev_tail.clone().into(), + } + .into(), + } + } else if field.2.is_list() { + Term::Apply { + function: DefaultFunction::UnListData.into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList) + .force_wrap() + .into(), + argument: prev_tail.clone().into(), + } + .into(), + } + } else { + Term::Apply { + function: Term::Builtin(DefaultFunction::HeadList) + .force_wrap() + .into(), + argument: prev_tail.clone().into(), + } + }; + + body = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: field.1.clone(), + unique: 0.into(), + }, + body: Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: format!( + "__tail_{current_tail_index}_{current_tail_id}" + ), + unique: 0.into(), + }, + body: body.into(), + } + .into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::TailList) + .force_wrap() + .into(), + argument: prev_tail.into(), + } + .into(), + } + .into(), + } + .into(), + argument: unwrapper.into(), + }; + + current_field = None; + } else { + body = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: format!( + "__tail_{current_tail_index}_{current_tail_id}" + ), + unique: 0.into(), + }, + body: body.into(), + } + .into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::TailList) + .force_wrap() + .force_wrap() + .into(), + argument: prev_tail.into(), + } + .into(), + } + } + } else { + body = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: format!("__tail_{current_tail_index}_{current_tail_id}"), + unique: 0.into(), + }, + body: body.into(), + } + .into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::TailList) + .force_wrap() + .force_wrap() + .into(), + argument: prev_tail.into(), + } + .into(), + } + } + } + + body = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: constr_name_lam, + unique: 0.into(), + }, + body: body.into(), + } + .into(), + argument: Term::Apply { + function: Term::Var(Name { + text: CONSTR_FIELDS_EXPOSER.to_string(), + unique: 0.into(), + }) + .into(), + argument: constr_var.into(), + } + .into(), + }; + + arg_stack.push(body); + } IR::Todo { .. } => todo!(), IR::RecordUpdate { .. } => todo!(), IR::Negate { .. } => todo!(), diff --git a/crates/uplc/src/ast/builder.rs b/crates/uplc/src/ast/builder.rs index b6d13f65..08aa990f 100644 --- a/crates/uplc/src/ast/builder.rs +++ b/crates/uplc/src/ast/builder.rs @@ -3,6 +3,7 @@ use crate::builtins::DefaultFunction; use super::{Constant, Name, Term}; pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer"; +pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; pub fn final_wrapper(term: Term) -> Term { @@ -59,6 +60,18 @@ pub fn constr_fields_exposer(term: Term) -> Term { } } +pub fn constr_index_exposer(term: Term) -> Term { + Term::Apply { + function: Term::Force(Term::Force(Term::Builtin(DefaultFunction::FstPair).into()).into()) + .into(), + argument: Term::Apply { + function: Term::Builtin(DefaultFunction::UnConstrData).into(), + argument: term.into(), + } + .into(), + } +} + pub fn constr_get_field(term: Term) -> Term { Term::Apply { function: Term::Lambda { diff --git a/examples/sample/validators/swap.ak b/examples/sample/validators/swap.ak index 8ec4eb27..992142d5 100644 --- a/examples/sample/validators/swap.ak +++ b/examples/sample/validators/swap.ak @@ -44,14 +44,14 @@ pub fn who(a: ByteArray) -> ByteArray { } pub type Datum { - Offer { price: Int, asset_class: ByteArray } + Offer { price: Int, asset_class: ByteArray, thing: Int } Sell Hold(Int) } pub fn spend(datum: Datum, _rdmr: Nil, _ctx: Nil) -> Bool { when datum is { - Offer { price, .. } -> price > 0 + Offer { price, thing: t, .. } -> price > 0 Hold(less) -> less < 0 Sell -> False }