From 83ade9335fe3a673e5a7fa2df2b98473ee2974cf Mon Sep 17 00:00:00 2001 From: microproofs Date: Tue, 27 Jun 2023 17:42:57 -0400 Subject: [PATCH] feat: implement most of airtree build --- crates/aiken-lang/src/gen_uplc/tree.rs | 39 ++- crates/aiken-lang/src/gen_uplc2.rs | 321 +++++++++++++++++++++ crates/aiken-lang/src/gen_uplc2/builder.rs | 19 ++ crates/aiken-lang/src/lib.rs | 1 + 4 files changed, 375 insertions(+), 5 deletions(-) create mode 100644 crates/aiken-lang/src/gen_uplc2.rs create mode 100644 crates/aiken-lang/src/gen_uplc2/builder.rs diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index 54605630..3f4a0c77 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -153,6 +153,7 @@ pub enum AirTree { // If If { tipo: Arc, + pattern: Box, then: Box, otherwise: Box, }, @@ -216,6 +217,9 @@ pub enum AirTree { then: Box, }, NoOp, + Sequence { + expressions: Vec, + }, FieldsEmpty { constr: Box, }, @@ -241,8 +245,22 @@ impl AirTree { pub fn bool(value: bool) -> AirTree { AirTree::Bool { value } } - pub fn list(items: Vec, tipo: Arc, tail: bool) -> AirTree { - AirTree::List { tipo, tail, items } + pub fn list(mut items: Vec, tipo: Arc, tail: Option) -> AirTree { + if let Some(tail) = tail { + items.push(tail); + + AirTree::List { + tipo, + tail: true, + items, + } + } else { + AirTree::List { + tipo, + tail: false, + items, + } + } } pub fn tuple(items: Vec, tipo: Arc) -> AirTree { AirTree::Tuple { tipo, items } @@ -450,20 +468,26 @@ impl AirTree { then: then.into(), } } - pub fn if_branches(mut branches: Vec, tipo: Arc, otherwise: AirTree) -> AirTree { + pub fn if_branches( + mut branches: Vec<(AirTree, AirTree)>, + tipo: Arc, + otherwise: AirTree, + ) -> AirTree { assert!(branches.len() > 0); let last_if = branches.pop().unwrap(); let mut final_if = AirTree::If { tipo: tipo.clone(), - then: last_if.into(), + pattern: last_if.0.into(), + then: last_if.1.into(), otherwise: otherwise.into(), }; while let Some(branch) = branches.pop() { final_if = AirTree::If { tipo: tipo.clone(), - then: branch.into(), + pattern: branch.0.into(), + then: branch.1.into(), otherwise: final_if.into(), }; } @@ -569,6 +593,9 @@ impl AirTree { pub fn no_op() -> AirTree { AirTree::NoOp } + pub fn sequence(expressions: Vec) -> AirTree { + AirTree::Sequence { expressions } + } pub fn fields_empty(constr: AirTree) -> AirTree { AirTree::FieldsEmpty { constr: constr.into(), @@ -672,6 +699,7 @@ impl AirTree { AirTree::Finally { pattern, then } => todo!(), AirTree::If { tipo, + pattern, then, otherwise, } => todo!(), @@ -720,6 +748,7 @@ impl AirTree { AirTree::ErrorTerm { tipo } => todo!(), AirTree::Trace { tipo, msg, then } => todo!(), AirTree::NoOp => todo!(), + AirTree::Sequence { .. } => todo!(), AirTree::FieldsEmpty { constr } => todo!(), AirTree::ListEmpty { list } => todo!(), } diff --git a/crates/aiken-lang/src/gen_uplc2.rs b/crates/aiken-lang/src/gen_uplc2.rs new file mode 100644 index 00000000..21ba492d --- /dev/null +++ b/crates/aiken-lang/src/gen_uplc2.rs @@ -0,0 +1,321 @@ +mod builder; + +use std::{rc::Rc, sync::Arc}; + +use indexmap::{IndexMap, IndexSet}; +use itertools::Itertools; +use uplc::ast::{Name, Program, Term}; + +use crate::{ + ast::{TypedDataType, TypedFunction, TypedValidator}, + expr::TypedExpr, + gen_uplc::{ + air::Air, + builder::{self as build, DataTypeKey, FunctionAccessKey}, + tree::AirTree, + CodeGenFunction, + }, + tipo::{ModuleValueConstructor, TypeInfo, ValueConstructor, ValueConstructorVariant}, +}; + +#[derive(Clone)] +pub struct CodeGenerator<'a> { + defined_functions: IndexMap, + functions: IndexMap, + data_types: IndexMap, + module_types: IndexMap<&'a String, &'a TypeInfo>, + needs_field_access: bool, + code_gen_functions: IndexMap, + zero_arg_functions: IndexMap>, + tracing: bool, +} + +impl<'a> CodeGenerator<'a> { + pub fn new( + functions: IndexMap, + data_types: IndexMap, + module_types: IndexMap<&'a String, &'a TypeInfo>, + tracing: bool, + ) -> Self { + CodeGenerator { + defined_functions: IndexMap::new(), + functions, + data_types, + module_types, + needs_field_access: false, + code_gen_functions: IndexMap::new(), + zero_arg_functions: IndexMap::new(), + tracing, + } + } + + pub fn reset(&mut self) { + self.code_gen_functions = IndexMap::new(); + self.zero_arg_functions = IndexMap::new(); + self.needs_field_access = false; + self.defined_functions = IndexMap::new(); + } + + pub fn generate( + &mut self, + TypedValidator { + fun, + other_fun, + params, + .. + }: &TypedValidator, + ) -> Program { + todo!() + } + + pub fn generate_test(&mut self, test_body: &TypedExpr) -> Program { + todo!() + } + + fn finalize(&mut self, term: Term) -> Program { + todo!() + } + + fn build(&mut self, body: &TypedExpr) -> AirTree { + match body { + TypedExpr::Int { value, .. } => AirTree::int(value), + TypedExpr::String { value, .. } => AirTree::string(value), + TypedExpr::ByteArray { bytes, .. } => AirTree::byte_array(bytes.clone()), + TypedExpr::Sequence { expressions, .. } | TypedExpr::Pipeline { expressions, .. } => { + AirTree::sequence( + expressions + .iter() + .map(|expression| self.build(expression)) + .collect_vec(), + ) + } + + TypedExpr::Var { + constructor, name, .. + } => AirTree::var(constructor.clone(), name, ""), + + TypedExpr::Fn { args, body, .. } => AirTree::anon_func( + args.iter() + .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) + .collect_vec(), + self.build(body), + ), + + TypedExpr::List { + tipo, + elements, + tail, + .. + } => AirTree::list( + elements.iter().map(|elem| self.build(elem)).collect_vec(), + tipo.clone(), + tail.as_ref().map(|tail| self.build(tail)), + ), + + TypedExpr::Call { + tipo, fun, args, .. + } => match fun.as_ref() { + TypedExpr::Var { + constructor: + ValueConstructor { + variant: + ValueConstructorVariant::Record { + name: constr_name, .. + }, + .. + }, + .. + } + | TypedExpr::ModuleSelect { + constructor: + ModuleValueConstructor::Record { + name: constr_name, .. + }, + .. + } => { + let Some(data_type) = build::lookup_data_type_by_tipo(&self.data_types, tipo) + else {unreachable!("Creating a record with no record definition.")}; + + let (constr_index, _) = data_type + .constructors + .iter() + .enumerate() + .find(|(_, dt)| &dt.name == constr_name) + .unwrap(); + + let constr_args = args.iter().map(|arg| self.build(&arg.value)).collect_vec(); + + AirTree::create_constr(constr_index, tipo.clone(), constr_args) + } + + TypedExpr::Var { + constructor: + ValueConstructor { + variant: ValueConstructorVariant::ModuleFn { builtin, .. }, + .. + }, + .. + } => { + let Some(fun_arg_types) = fun.tipo().arg_types() else {unreachable!("Expected a function type with arguments")}; + + let func_args = args + .iter() + .zip(fun_arg_types) + .map(|(arg, arg_tipo)| { + let mut arg_val = self.build(&arg.value); + + if arg_tipo.is_data() && !arg.value.tipo().is_data() { + arg_val = AirTree::wrap_data(arg_val, arg.value.tipo()) + } + arg_val + }) + .collect_vec(); + + if let Some(func) = builtin { + AirTree::builtin(*func, tipo.clone(), func_args) + } else { + AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) + } + } + + TypedExpr::ModuleSelect { + tipo, + module_name, + constructor: ModuleValueConstructor::Fn { name, .. }, + .. + } => { + let type_info = self.module_types.get(module_name).unwrap(); + let value = type_info.values.get(name).unwrap(); + + let ValueConstructorVariant::ModuleFn { builtin, .. } = &value.variant else {unreachable!("Missing module function definition")}; + + let Some(fun_arg_types) = fun.tipo().arg_types() else {unreachable!("Expected a function type with arguments")}; + + let func_args = args + .iter() + .zip(fun_arg_types) + .map(|(arg, arg_tipo)| { + let mut arg_val = self.build(&arg.value); + + if arg_tipo.is_data() && !arg.value.tipo().is_data() { + arg_val = AirTree::wrap_data(arg_val, arg.value.tipo()) + } + arg_val + }) + .collect_vec(); + + if let Some(func) = builtin { + AirTree::builtin(*func, tipo.clone(), func_args) + } else { + AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) + } + } + _ => todo!("IS THIS REACHABLE?"), + }, + TypedExpr::BinOp { + name, + tipo, + left, + right, + .. + } => AirTree::binop(*name, tipo.clone(), self.build(left), self.build(right)), + + TypedExpr::Assignment { + tipo, + value, + pattern, + kind, + .. + } => { + let mut replaced_type = tipo.clone(); + build::replace_opaque_type(&mut replaced_type, &self.data_types); + + let air_value = self.build(value); + + builder::assignment_air_tree( + pattern, + air_value, + tipo, + build::AssignmentProperties { + value_type: value.tipo(), + kind: *kind, + }, + ) + } + + TypedExpr::Trace { + tipo, then, text, .. + } => AirTree::trace(self.build(text), tipo.clone(), self.build(then)), + + TypedExpr::When { .. } => todo!(), + + TypedExpr::If { + branches, + final_else, + tipo, + .. + } => AirTree::if_branches( + branches + .iter() + .map(|branch| (self.build(&branch.condition), self.build(&branch.body))) + .collect_vec(), + tipo.clone(), + self.build(final_else), + ), + + TypedExpr::RecordAccess { + tipo, + index, + record, + .. + } => AirTree::record_access(*index, tipo.clone(), self.build(record)), + + TypedExpr::ModuleSelect { .. } => todo!(), + + TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple( + elems.iter().map(|elem| self.build(elem)).collect_vec(), + tipo.clone(), + ), + + TypedExpr::TupleIndex { + tipo, index, tuple, .. + } => AirTree::tuple_index(*index, tipo.clone(), self.build(tuple)), + + TypedExpr::ErrorTerm { tipo, .. } => AirTree::error(tipo.clone()), + + TypedExpr::RecordUpdate { + tipo, spread, args, .. + } => { + let mut index_types = vec![]; + let mut update_args = vec![]; + + let mut highest_index = 0; + let record = self.build(spread); + + for arg in args + .iter() + .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) + { + let arg_val = self.build(&arg.value); + + if arg.index > highest_index { + highest_index = arg.index; + } + + index_types.push((arg.index, arg.value.tipo())); + update_args.push(arg_val); + } + + AirTree::record_update( + index_types, + highest_index, + tipo.clone(), + record, + update_args, + ) + } + + TypedExpr::UnOp { value, op, .. } => AirTree::unop(*op, self.build(value)), + } + } +} diff --git a/crates/aiken-lang/src/gen_uplc2/builder.rs b/crates/aiken-lang/src/gen_uplc2/builder.rs new file mode 100644 index 00000000..d487f09e --- /dev/null +++ b/crates/aiken-lang/src/gen_uplc2/builder.rs @@ -0,0 +1,19 @@ +use std::sync::Arc; + +use crate::{ + ast::Pattern, + gen_uplc::{builder::AssignmentProperties, tree::AirTree}, + tipo::{PatternConstructor, Type}, +}; + +pub fn assignment_air_tree( + pattern: &Pattern>, + value: AirTree, + tipo: &Arc, + props: AssignmentProperties, +) -> AirTree { + todo!() +} + + + diff --git a/crates/aiken-lang/src/lib.rs b/crates/aiken-lang/src/lib.rs index 3b4eb8de..1b4b598e 100644 --- a/crates/aiken-lang/src/lib.rs +++ b/crates/aiken-lang/src/lib.rs @@ -8,6 +8,7 @@ pub mod builtins; pub mod expr; pub mod format; pub mod gen_uplc; +pub mod gen_uplc2; pub mod levenshtein; pub mod parser; pub mod pretty;