small edge case to fix and some ordering to go

This commit is contained in:
Kasey White 2022-11-23 05:42:52 -05:00 committed by Lucas
parent e496591ded
commit e6f6d8a42d
1 changed files with 665 additions and 124 deletions

View File

@ -88,7 +88,7 @@ pub struct ConstrFieldKey {
pub field_name: String, pub field_name: String,
} }
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct DataTypeKey { pub struct DataTypeKey {
pub module_name: String, pub module_name: String,
pub defined_type: String, pub defined_type: String,
@ -238,6 +238,8 @@ impl<'a> CodeGenerator<'a> {
term, term,
}; };
println!("{}", program.to_pretty());
let mut interner = Interner::new(); let mut interner = Interner::new();
interner.program(&mut program); interner.program(&mut program);
@ -246,7 +248,7 @@ impl<'a> CodeGenerator<'a> {
} }
pub(crate) fn recurse_scope_level(&mut self, body: &TypedExpr, scope_level: ScopeLevels) { pub(crate) fn recurse_scope_level(&mut self, body: &TypedExpr, scope_level: ScopeLevels) {
match body { match dbg!(body) {
TypedExpr::Int { .. } => {} TypedExpr::Int { .. } => {}
TypedExpr::String { .. } => {} TypedExpr::String { .. } => {}
TypedExpr::ByteArray { .. } => {} TypedExpr::ByteArray { .. } => {}
@ -535,15 +537,17 @@ impl<'a> CodeGenerator<'a> {
scope_level: ScopeLevels, scope_level: ScopeLevels,
vars: &[TypedExpr], vars: &[TypedExpr],
) { ) {
match pattern { match dbg!(pattern) {
Pattern::Int { .. } | Pattern::String { .. } | Pattern::Var { .. } => { Pattern::Int { .. }
| Pattern::String { .. }
| Pattern::Var { .. }
| Pattern::List { .. }
| Pattern::Discard { .. } => {
self.recurse_scope_level(value, scope_level); self.recurse_scope_level(value, scope_level);
} }
Pattern::VarUsage { .. } => todo!(), Pattern::VarUsage { .. } => todo!(),
Pattern::Assign { .. } => todo!(), Pattern::Assign { .. } => todo!(),
Pattern::Discard { .. } => todo!(),
Pattern::List { .. } => todo!(),
Pattern::Constructor { Pattern::Constructor {
name: constructor_name, name: constructor_name,
tipo, tipo,
@ -1300,7 +1304,7 @@ impl<'a> CodeGenerator<'a> {
let mut total_constr_length = 0; let mut total_constr_length = 0;
let pattern = &clauses[0].pattern[0]; let pattern = &clauses[0].pattern[0];
let key = match pattern { let key = match dbg!(pattern) {
Pattern::Constructor { tipo, .. } => { Pattern::Constructor { tipo, .. } => {
let mut is_app = false; let mut is_app = false;
let mut tipo = &**tipo; let mut tipo = &**tipo;
@ -1322,11 +1326,14 @@ impl<'a> CodeGenerator<'a> {
}; };
} }
key Some(key)
} }
Pattern::List { .. } => None,
Pattern::Discard { .. } => None,
_ => todo!(), _ => todo!(),
}; };
if let Some(key) = key {
let dt = self.data_types.get(&key).unwrap(); let dt = self.data_types.get(&key).unwrap();
let data_type = &dt.name; let data_type = &dt.name;
let mut new_current_clauses: Vec<(usize, Term<Name>)> = current_clauses let mut new_current_clauses: Vec<(usize, Term<Name>)> = current_clauses
@ -1337,7 +1344,6 @@ impl<'a> CodeGenerator<'a> {
Pattern::Constructor { name, module, .. } => { Pattern::Constructor { name, module, .. } => {
let index = let index =
dt.constructors.iter().position(|c| name.clone() == c.name); dt.constructors.iter().position(|c| name.clone() == c.name);
let mut current_term = self.recurse_code_gen( let mut current_term = self.recurse_code_gen(
&clause.then, &clause.then,
scope_level.scope_increment_sequence(1), scope_level.scope_increment_sequence(1),
@ -1352,10 +1358,12 @@ impl<'a> CodeGenerator<'a> {
if let Some(ScopedExpr { if let Some(ScopedExpr {
expr: TypedExpr::Assignment { pattern, .. }, expr: TypedExpr::Assignment { pattern, .. },
.. ..
}) = self.uplc_data_holder_lookup.get(&ConstrFieldKey { }) =
self.uplc_data_holder_lookup.get(&ConstrFieldKey {
local_var: current_var_name.to_string(), local_var: current_var_name.to_string(),
field_name: label.clone(), field_name: label.clone(),
}) { })
{
let var_name = match pattern { let var_name = match pattern {
Pattern::Var { name, .. } => name, Pattern::Var { name, .. } => name,
_ => todo!(), _ => todo!(),
@ -1478,6 +1486,486 @@ impl<'a> CodeGenerator<'a> {
} }
term term
} else {
let mut type_list = vec![];
let mut is_final_type = false;
// TODO use lifetimes instead of clone
// Skip first type since we know we have a list
let tipo = match subject {
TypedExpr::Var { constructor, .. } => &constructor.tipo,
_ => todo!(),
};
let mut current_tipo = match (&**tipo).clone() {
Type::App { args, .. } => (*args[0]).clone(),
Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(),
};
while !is_final_type {
match current_tipo.clone() {
Type::App { name, args, .. } => {
if args.is_empty() {
type_list.push(name);
is_final_type = true;
} else {
type_list.push(name);
current_tipo = (&*args[0]).clone();
}
}
Type::Fn { .. } => todo!(),
Type::Var { tipo } => match (*tipo).borrow().clone() {
tipo::TypeVar::Unbound { .. } => todo!(),
tipo::TypeVar::Link { tipo } => {
current_tipo = (&*tipo).clone();
}
tipo::TypeVar::Generic { .. } => todo!(),
},
};
}
let mut new_current_clauses: Vec<(Option<usize>, bool, Term<Name>)> =
current_clauses
.iter()
.map(|clause| {
let pattern = &clause.pattern[0];
let mut current_term = self.recurse_code_gen(
&clause.then,
scope_level.scope_increment_sequence(1),
);
let triplet = match dbg!(pattern) {
Pattern::List { elements, tail, .. } => {
let element_names: Vec<String> = elements
.clone()
.iter()
.map(|element| match element {
Pattern::Var { name, .. } => name.to_string(),
_ => todo!(),
})
.collect();
let tail_name: Option<String> = if let Some(tail) = tail {
match &**tail {
Pattern::Var { name, .. } => Some(name.to_string()),
_ => todo!(),
}
} else {
None
};
for (index, var_name) in element_names.iter().enumerate() {
current_term = Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: var_name.to_string(),
unique: 0.into(),
},
body: current_term.into(),
}
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_item_{index}"
),
unique: 0.into(),
})
.into(),
};
}
if let Some(tail_name) = tail_name {
current_term = Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: tail_name,
unique: 0.into(),
},
body: current_term.into(),
}
.into(),
argument: Term::Var(Name {
text: format!("{current_var_name}_rest"),
unique: 0.into(),
})
.into(),
};
}
(Some(elements.len()), tail.is_some(), current_term)
}
Pattern::Discard { .. } => (None, false, current_term),
_ => todo!(),
};
triplet
})
.collect();
new_current_clauses.sort_by(|item1, item2| {
if item1.0.is_none() && item2.0.is_some() {
Ordering::Greater
} else if item2.0.is_none() && item1.0.is_some() {
Ordering::Less
} else {
match item1.0.cmp(&item2.0) {
Ordering::Less => Ordering::Less,
Ordering::Equal => item1.1.cmp(&item2.1),
Ordering::Greater => Ordering::Greater,
}
}
});
println!("{new_current_clauses:?}");
let (last, new_current_clauses) = new_current_clauses.split_last().unwrap();
println!("{new_current_clauses:?}");
let mut new_current_clauses = new_current_clauses.to_vec();
new_current_clauses.reverse();
let mut current_term: Term<Name> = last.2.clone();
let last_term = last.2.clone();
//if last clause had a tail then we need the lambda to expose rest
if last.1 && last.0.is_some() {
let last_index = last.0.unwrap() - 1;
current_term = Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!("{current_var_name}_rest"),
unique: 0.into(),
},
body: current_term.into(),
}
.into(),
argument: Term::Var(Name {
text: format!("{current_var_name}_tail_{last_index}"),
unique: 0.into(),
})
.into(),
};
}
for (index, (array_length, has_tail, then)) in
new_current_clauses.iter().enumerate()
{
let prev_length: Option<usize> = if index == 0 {
None
} else {
new_current_clauses
.get(index - 1)
.and_then(|(index_opt, _, _)| *index_opt)
};
match dbg!((*array_length, prev_length)) {
(Some(length), Some(prev_length)) => {
let check_length = if prev_length == length {
length + 2
} else {
length + 1
};
// Go index by index to create cases for each possible len
for expose_index in (prev_length + 1..check_length).rev() {
let prev_exposed = expose_index - 1;
let list_var_name =
format!("{current_var_name}_tail_{prev_exposed}");
// For a given list length if we encounter a tail and we are checking a clause length = current index
// then expose a var for tail and run clause then
current_term = if *has_tail && expose_index == length - 1 {
Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!("{current_var_name}_rest"),
unique: 0.into(),
},
body: then.clone().into(),
}
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
// we are checking a clause length = current index so check empty tail list and run clause then if tail list is empty
} else if expose_index == length - 1 {
Term::Force(
Term::Apply {
function: Term::Apply {
function: Term::Apply {
function: Term::Force(
Term::Force(
Term::Builtin(
DefaultFunction::ChooseList,
)
.into(),
)
.into(),
)
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
.into(),
argument: Term::Delay(then.clone().into())
.into(),
}
.into(),
argument: Term::Delay(current_term.into()).into(),
}
.into(),
)
// We are not checking for a list of this length, so fallback to last clause then if tail list is empty
} else {
Term::Force(
Term::Apply {
function: Term::Apply {
function: Term::Apply {
function: Term::Force(
Term::Force(
Term::Builtin(
DefaultFunction::ChooseList,
)
.into(),
)
.into(),
)
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
.into(),
argument: Term::Delay(last_term.clone().into())
.into(),
}
.into(),
argument: Term::Delay(current_term.into()).into(),
}
.into(),
)
};
if prev_length != length {
// Just expose head list and tail list. Check for empty list happens above
current_term = Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!(
"{current_var_name}_item_{expose_index}"
),
unique: 0.into(),
},
body: Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
},
body: current_term.into(),
}
.into(),
argument: Term::Apply {
function: Term::Force(
Term::Builtin(
DefaultFunction::TailList,
)
.into(),
)
.into(),
argument: Term::Var(Name {
text: list_var_name.to_string(),
unique: 0.into(),
})
.into(),
}
.into(),
}
.into(),
}
.into(),
argument: Term::Apply {
function: Term::Force(
Term::Builtin(DefaultFunction::HeadList).into(),
)
.into(),
argument: Term::Var(Name {
text: list_var_name.to_string(),
unique: 0.into(),
})
.into(),
}
.into(),
};
}
}
}
(Some(length), None) => {
for expose_index in (0..length + 1).rev() {
let list_var_name = if expose_index == 0 {
current_var_name.clone()
} else {
let prev_exposed = expose_index - 1;
format!("{current_var_name}_tail_{prev_exposed}")
};
// For a given list length if we encounter a tail and we are checking a clause length = current index
// then expose a var for tail and run clause then
current_term = if *has_tail && expose_index == length - 1 {
Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!("{current_var_name}_rest"),
unique: 0.into(),
},
body: then.clone().into(),
}
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
// we are checking a clause length = current index so check empty tail list and run clause then if tail list is empty
} else if expose_index == length - 1 {
Term::Force(
Term::Apply {
function: Term::Apply {
function: Term::Apply {
function: Term::Force(
Term::Force(
Term::Builtin(
DefaultFunction::ChooseList,
)
.into(),
)
.into(),
)
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
.into(),
argument: Term::Delay(then.clone().into())
.into(),
}
.into(),
argument: Term::Delay(current_term.into()).into(),
}
.into(),
)
// We are not checking for a list of this length, so fallback to last clause then if tail list is empty
} else {
Term::Force(
Term::Apply {
function: Term::Apply {
function: Term::Apply {
function: Term::Force(
Term::Force(
Term::Builtin(
DefaultFunction::ChooseList,
)
.into(),
)
.into(),
)
.into(),
argument: Term::Var(Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
})
.into(),
}
.into(),
argument: Term::Delay(last_term.clone().into())
.into(),
}
.into(),
argument: Term::Delay(current_term.into()).into(),
}
.into(),
)
};
// Just expose head list and tail list. Check for empty list happens above
current_term = Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!(
"{current_var_name}_item_{expose_index}"
),
unique: 0.into(),
},
body: Term::Apply {
function: Term::Lambda {
parameter_name: Name {
text: format!(
"{current_var_name}_tail_{expose_index}"
),
unique: 0.into(),
},
body: current_term.into(),
}
.into(),
argument: Term::Apply {
function: Term::Force(
Term::Builtin(DefaultFunction::TailList)
.into(),
)
.into(),
argument: Term::Var(Name {
text: list_var_name.to_string(),
unique: 0.into(),
})
.into(),
}
.into(),
}
.into(),
}
.into(),
argument: Term::Apply {
function: Term::Force(
Term::Builtin(DefaultFunction::HeadList).into(),
)
.into(),
argument: Term::Var(Name {
text: list_var_name.to_string(),
unique: 0.into(),
})
.into(),
}
.into(),
};
}
}
(None, None) => todo!(),
(None, Some(_)) => todo!(),
}
}
current_term
}
} }
// if statements increase scope due to branching. // if statements increase scope due to branching.
TypedExpr::If { TypedExpr::If {
@ -2145,4 +2633,57 @@ impl<'a> CodeGenerator<'a> {
.into(), .into(),
} }
} }
// fn add_field_length_check(&self, term: Term<Name>) -> Term<Name> {
// Term::Apply {
// function: Term::Lambda {
// parameter_name: Name {
// text: "field_length_check".to_string(),
// unique: 0.into(),
// },
// body: term.into(),
// }
// .into(),
// argument: Term::Lambda {
// parameter_name: Name {
// text: "expected_field_length".to_string(),
// unique: 0.into(),
// },
// body: Term::Lambda {
// parameter_name: Name {
// text: "type_to_check".to_string(),
// unique: 0.into(),
// },
// body: Term::Apply {
// function: Term::Apply {
// function: Term::Apply {
// function: Term::Force(
// Term::Builtin(DefaultFunction::IfThenElse).into(),
// )
// .into(),
// argument: Term::Apply {
// function: Term::Apply {
// function: Term::Apply {
// function: Term::Builtin(DefaultFunction::EqualsInteger),
// argument: Term::Apply { function: , argument: () },
// },
// argument: (),
// },
// argument: (),
// },
// }
// .into(),
// argument: (),
// }
// .into(),
// argument: (),
// }
// .into(),
// }
// .into(),
// }
// .into(),
// }
// todo!()
// }
} }