feat: implement most of airtree build

This commit is contained in:
microproofs 2023-06-27 17:42:57 -04:00 committed by Kasey
parent 5e097d42ba
commit 83ade9335f
4 changed files with 375 additions and 5 deletions

View File

@ -153,6 +153,7 @@ pub enum AirTree {
// If // If
If { If {
tipo: Arc<Type>, tipo: Arc<Type>,
pattern: Box<AirTree>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>, otherwise: Box<AirTree>,
}, },
@ -216,6 +217,9 @@ pub enum AirTree {
then: Box<AirTree>, then: Box<AirTree>,
}, },
NoOp, NoOp,
Sequence {
expressions: Vec<AirTree>,
},
FieldsEmpty { FieldsEmpty {
constr: Box<AirTree>, constr: Box<AirTree>,
}, },
@ -241,8 +245,22 @@ impl AirTree {
pub fn bool(value: bool) -> AirTree { pub fn bool(value: bool) -> AirTree {
AirTree::Bool { value } AirTree::Bool { value }
} }
pub fn list(items: Vec<AirTree>, tipo: Arc<Type>, tail: bool) -> AirTree { pub fn list(mut items: Vec<AirTree>, tipo: Arc<Type>, tail: Option<AirTree>) -> AirTree {
AirTree::List { tipo, tail, items } 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<AirTree>, tipo: Arc<Type>) -> AirTree { pub fn tuple(items: Vec<AirTree>, tipo: Arc<Type>) -> AirTree {
AirTree::Tuple { tipo, items } AirTree::Tuple { tipo, items }
@ -450,20 +468,26 @@ impl AirTree {
then: then.into(), then: then.into(),
} }
} }
pub fn if_branches(mut branches: Vec<AirTree>, tipo: Arc<Type>, otherwise: AirTree) -> AirTree { pub fn if_branches(
mut branches: Vec<(AirTree, AirTree)>,
tipo: Arc<Type>,
otherwise: AirTree,
) -> AirTree {
assert!(branches.len() > 0); assert!(branches.len() > 0);
let last_if = branches.pop().unwrap(); let last_if = branches.pop().unwrap();
let mut final_if = AirTree::If { let mut final_if = AirTree::If {
tipo: tipo.clone(), tipo: tipo.clone(),
then: last_if.into(), pattern: last_if.0.into(),
then: last_if.1.into(),
otherwise: otherwise.into(), otherwise: otherwise.into(),
}; };
while let Some(branch) = branches.pop() { while let Some(branch) = branches.pop() {
final_if = AirTree::If { final_if = AirTree::If {
tipo: tipo.clone(), tipo: tipo.clone(),
then: branch.into(), pattern: branch.0.into(),
then: branch.1.into(),
otherwise: final_if.into(), otherwise: final_if.into(),
}; };
} }
@ -569,6 +593,9 @@ impl AirTree {
pub fn no_op() -> AirTree { pub fn no_op() -> AirTree {
AirTree::NoOp AirTree::NoOp
} }
pub fn sequence(expressions: Vec<AirTree>) -> AirTree {
AirTree::Sequence { expressions }
}
pub fn fields_empty(constr: AirTree) -> AirTree { pub fn fields_empty(constr: AirTree) -> AirTree {
AirTree::FieldsEmpty { AirTree::FieldsEmpty {
constr: constr.into(), constr: constr.into(),
@ -672,6 +699,7 @@ impl AirTree {
AirTree::Finally { pattern, then } => todo!(), AirTree::Finally { pattern, then } => todo!(),
AirTree::If { AirTree::If {
tipo, tipo,
pattern,
then, then,
otherwise, otherwise,
} => todo!(), } => todo!(),
@ -720,6 +748,7 @@ impl AirTree {
AirTree::ErrorTerm { tipo } => todo!(), AirTree::ErrorTerm { tipo } => todo!(),
AirTree::Trace { tipo, msg, then } => todo!(), AirTree::Trace { tipo, msg, then } => todo!(),
AirTree::NoOp => todo!(), AirTree::NoOp => todo!(),
AirTree::Sequence { .. } => todo!(),
AirTree::FieldsEmpty { constr } => todo!(), AirTree::FieldsEmpty { constr } => todo!(),
AirTree::ListEmpty { list } => todo!(), AirTree::ListEmpty { list } => todo!(),
} }

View File

@ -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<FunctionAccessKey, ()>,
functions: IndexMap<FunctionAccessKey, &'a TypedFunction>,
data_types: IndexMap<DataTypeKey, &'a TypedDataType>,
module_types: IndexMap<&'a String, &'a TypeInfo>,
needs_field_access: bool,
code_gen_functions: IndexMap<String, CodeGenFunction>,
zero_arg_functions: IndexMap<FunctionAccessKey, Vec<Air>>,
tracing: bool,
}
impl<'a> CodeGenerator<'a> {
pub fn new(
functions: IndexMap<FunctionAccessKey, &'a TypedFunction>,
data_types: IndexMap<DataTypeKey, &'a TypedDataType>,
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<Name> {
todo!()
}
pub fn generate_test(&mut self, test_body: &TypedExpr) -> Program<Name> {
todo!()
}
fn finalize(&mut self, term: Term<Name>) -> Program<Name> {
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)),
}
}
}

View File

@ -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<PatternConstructor, Arc<Type>>,
value: AirTree,
tipo: &Arc<Type>,
props: AssignmentProperties,
) -> AirTree {
todo!()
}

View File

@ -8,6 +8,7 @@ pub mod builtins;
pub mod expr; pub mod expr;
pub mod format; pub mod format;
pub mod gen_uplc; pub mod gen_uplc;
pub mod gen_uplc2;
pub mod levenshtein; pub mod levenshtein;
pub mod parser; pub mod parser;
pub mod pretty; pub mod pretty;