Merge branch 'main' into when-clause-guards

This commit is contained in:
Lucas 2023-01-30 11:40:29 -05:00 committed by GitHub
commit b653714c0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 2099 additions and 1015 deletions

1
Cargo.lock generated
View File

@ -117,6 +117,7 @@ dependencies = [
"futures",
"hex",
"ignore",
"indexmap",
"itertools",
"miette",
"owo-colors",

View File

@ -1,5 +1,6 @@
use std::{collections::HashSet, sync::Arc};
use std::sync::Arc;
use indexmap::IndexSet;
use uplc::builtins::DefaultFunction;
use crate::{
@ -9,6 +10,7 @@ use crate::{
#[derive(Debug, Clone)]
pub enum Air {
// Primitives
Int {
scope: Vec<u64>,
value: String,
@ -29,17 +31,6 @@ pub enum Air {
value: bool,
},
Var {
scope: Vec<u64>,
constructor: ValueConstructor,
name: String,
variant_name: String,
},
Fn {
scope: Vec<u64>,
params: Vec<String>,
},
List {
scope: Vec<u64>,
count: usize,
@ -47,48 +38,30 @@ pub enum Air {
tail: bool,
},
ListAccessor {
Tuple {
scope: Vec<u64>,
tipo: Arc<Type>,
names: Vec<String>,
tail: bool,
count: usize,
},
ListExpose {
Void {
scope: Vec<u64>,
tipo: Arc<Type>,
tail_head_names: Vec<(String, String)>,
tail: Option<(String, String)>,
},
Var {
scope: Vec<u64>,
constructor: ValueConstructor,
name: String,
variant_name: String,
},
// Functions
Call {
scope: Vec<u64>,
count: usize,
},
Builtin {
scope: Vec<u64>,
func: DefaultFunction,
tipo: Arc<Type>,
},
BinOp {
scope: Vec<u64>,
name: BinOp,
count: usize,
tipo: Arc<Type>,
},
Assignment {
scope: Vec<u64>,
name: String,
},
Assert {
scope: Vec<u64>,
constr_index: usize,
},
DefineFunc {
scope: Vec<u64>,
func_name: String,
@ -98,11 +71,47 @@ pub enum Air {
variant_name: String,
},
Lam {
Fn {
scope: Vec<u64>,
params: Vec<String>,
},
Builtin {
scope: Vec<u64>,
func: DefaultFunction,
tipo: Arc<Type>,
},
// Operators
BinOp {
scope: Vec<u64>,
name: BinOp,
count: usize,
tipo: Arc<Type>,
},
UnOp {
scope: Vec<u64>,
op: UnOp,
},
// Assignment
Let {
scope: Vec<u64>,
name: String,
},
UnWrapData {
scope: Vec<u64>,
tipo: Arc<Type>,
},
AssertConstr {
scope: Vec<u64>,
constr_index: usize,
},
// When
When {
scope: Vec<u64>,
tipo: Arc<Type>,
@ -124,11 +133,15 @@ pub enum Air {
complex_clause: bool,
},
WrapClause {
scope: Vec<u64>,
},
TupleClause {
scope: Vec<u64>,
tipo: Arc<Type>,
indices: HashSet<(usize, String)>,
predefined_indices: HashSet<(usize, String)>,
indices: IndexSet<(usize, String)>,
predefined_indices: IndexSet<(usize, String)>,
subject_name: String,
count: usize,
complex_clause: bool,
@ -148,18 +161,17 @@ pub enum Air {
inverse: bool,
},
Discard {
scope: Vec<u64>,
},
Finally {
scope: Vec<u64>,
},
// If
If {
scope: Vec<u64>,
tipo: Arc<Type>,
},
// Record Creation
Record {
scope: Vec<u64>,
constr_index: usize,
@ -167,6 +179,14 @@ pub enum Air {
count: usize,
},
RecordUpdate {
scope: Vec<u64>,
highest_index: usize,
indices: Vec<(usize, Arc<Type>)>,
tipo: Arc<Type>,
},
// Field Access
RecordAccess {
scope: Vec<u64>,
record_index: u64,
@ -175,14 +195,32 @@ pub enum Air {
FieldsExpose {
scope: Vec<u64>,
count: usize,
indices: Vec<(usize, String, Arc<Type>)>,
check_last_item: bool,
},
Tuple {
// ListAccess
ListAccessor {
scope: Vec<u64>,
tipo: Arc<Type>,
count: usize,
names: Vec<String>,
tail: bool,
check_last_item: bool,
},
ListExpose {
scope: Vec<u64>,
tipo: Arc<Type>,
tail_head_names: Vec<(String, String)>,
tail: Option<(String, String)>,
},
// Tuple Access
TupleAccessor {
scope: Vec<u64>,
names: Vec<String>,
tipo: Arc<Type>,
check_last_item: bool,
},
TupleIndex {
@ -191,6 +229,7 @@ pub enum Air {
tuple_index: usize,
},
// Misc.
Todo {
scope: Vec<u64>,
label: Option<String>,
@ -208,23 +247,6 @@ pub enum Air {
text: Option<String>,
tipo: Arc<Type>,
},
RecordUpdate {
scope: Vec<u64>,
highest_index: usize,
indices: Vec<(usize, Arc<Type>)>,
},
UnOp {
scope: Vec<u64>,
op: UnOp,
},
TupleAccessor {
scope: Vec<u64>,
names: Vec<String>,
tipo: Arc<Type>,
},
}
impl Air {
@ -234,67 +256,142 @@ impl Air {
| Air::String { scope, .. }
| Air::ByteArray { scope, .. }
| Air::Bool { scope, .. }
| Air::Var { scope, .. }
| Air::List { scope, .. }
| Air::ListAccessor { scope, .. }
| Air::ListExpose { scope, .. }
| Air::Fn { scope, .. }
| Air::Tuple { scope, .. }
| Air::Void { scope }
| Air::Var { scope, .. }
| Air::Call { scope, .. }
| Air::DefineFunc { scope, .. }
| Air::Fn { scope, .. }
| Air::Builtin { scope, .. }
| Air::BinOp { scope, .. }
| Air::Assignment { scope, .. }
| Air::Assert { scope, .. }
| Air::DefineFunc { scope, .. }
| Air::Lam { scope, .. }
| Air::UnOp { scope, .. }
| Air::Let { scope, .. }
| Air::UnWrapData { scope, .. }
| Air::AssertConstr { scope, .. }
| Air::When { scope, .. }
| Air::Clause { scope, .. }
| Air::ListClause { scope, .. }
| Air::WrapClause { scope }
| Air::TupleClause { scope, .. }
| Air::ClauseGuard { scope, .. }
| Air::ListClauseGuard { scope, .. }
| Air::Discard { scope }
| Air::Finally { scope }
| Air::If { scope, .. }
| Air::Record { scope, .. }
| Air::RecordUpdate { scope, .. }
| Air::RecordAccess { scope, .. }
| Air::FieldsExpose { scope, .. }
| Air::Tuple { scope, .. }
| Air::Todo { scope, .. }
| Air::ErrorTerm { scope, .. }
| Air::RecordUpdate { scope, .. }
| Air::UnOp { scope, .. }
| Air::Trace { scope, .. }
| Air::ListAccessor { scope, .. }
| Air::ListExpose { scope, .. }
| Air::TupleAccessor { scope, .. }
| Air::TupleIndex { scope, .. }
| Air::TupleClause { scope, .. } => scope.to_vec(),
| Air::Todo { scope, .. }
| Air::ErrorTerm { scope, .. }
| Air::Trace { scope, .. } => scope.clone(),
}
}
pub fn tipo(&self) -> Option<Arc<Type>> {
match self {
Air::Int { .. } => Some(
Type::App {
public: true,
module: String::new(),
name: "Int".to_string(),
args: vec![],
}
.into(),
),
Air::String { .. } => Some(
Type::App {
public: true,
module: String::new(),
name: "String".to_string(),
args: vec![],
}
.into(),
),
Air::ByteArray { .. } => Some(
Type::App {
public: true,
module: String::new(),
name: "ByteArray".to_string(),
args: vec![],
}
.into(),
),
Air::Bool { .. } => Some(
Type::App {
public: true,
module: String::new(),
name: "Bool".to_string(),
args: vec![],
}
.into(),
),
Air::Void { .. } => Some(
Type::App {
public: true,
module: String::new(),
name: "Void".to_string(),
args: vec![],
}
.into(),
),
Air::Var { constructor, .. } => Some(constructor.tipo.clone()),
Air::List { tipo, .. }
| Air::ListAccessor { tipo, .. }
| Air::ListExpose { tipo, .. }
| Air::Tuple { tipo, .. }
| Air::Call { tipo, .. }
| Air::Builtin { tipo, .. }
| Air::BinOp { tipo, .. }
| Air::UnWrapData { tipo, .. }
| Air::When { tipo, .. }
| Air::Clause { tipo, .. }
| Air::ListClause { tipo, .. }
| Air::TupleClause { tipo, .. }
| Air::ClauseGuard { tipo, .. }
| Air::If { tipo, .. }
| Air::ListClauseGuard { tipo, .. }
| Air::Record { tipo, .. }
| Air::RecordUpdate { tipo, .. }
| Air::RecordAccess { tipo, .. }
| Air::Tuple { tipo, .. }
| Air::ListAccessor { tipo, .. }
| Air::ListExpose { tipo, .. }
| Air::TupleAccessor { tipo, .. }
| Air::TupleIndex { tipo, .. }
| Air::Todo { tipo, .. }
| Air::ErrorTerm { tipo, .. }
| Air::Trace { tipo, .. }
| Air::TupleAccessor { tipo, .. }
| Air::Var {
constructor: ValueConstructor { tipo, .. },
..
} => Some(tipo.clone()),
_ => None,
| Air::Trace { tipo, .. } => Some(tipo.clone()),
Air::DefineFunc { .. }
| Air::Fn { .. }
| Air::Let { .. }
| Air::WrapClause { .. }
| Air::AssertConstr { .. }
| Air::Finally { .. }
| Air::FieldsExpose { .. } => None,
Air::UnOp { op, .. } => match op {
UnOp::Not => Some(
Type::App {
public: true,
module: String::new(),
name: "Bool".to_string(),
args: vec![],
}
.into(),
),
UnOp::Negate => Some(
Type::App {
public: true,
module: String::new(),
name: "Int".to_string(),
args: vec![],
}
.into(),
),
},
}
}
}

View File

@ -1,14 +1,10 @@
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
sync::Arc,
};
use std::{cell::RefCell, sync::Arc};
use indexmap::IndexMap;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use uplc::{
ast::{
builder::{apply_wrap, if_else},
builder::{apply_wrap, delayed_choose_list, if_else},
Constant as UplcConstant, Name, Term, Type as UplcType,
},
builtins::DefaultFunction,
@ -19,7 +15,7 @@ use uplc::{
use crate::{
air::Air,
ast::{
BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg, TypedDataType,
AssignmentKind, BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg, TypedDataType,
UnOp,
},
expr::TypedExpr,
@ -55,6 +51,12 @@ pub struct FunctionAccessKey {
pub variant_name: String,
}
#[derive(Clone, Debug)]
pub struct AssignmentProperties {
pub value_is_data: bool,
pub kind: AssignmentKind,
}
#[derive(Clone, Debug)]
pub enum ClauseProperties {
ConstrClause {
@ -68,14 +70,14 @@ pub enum ClauseProperties {
needs_constr_var: bool,
is_complex_clause: bool,
original_subject_name: String,
current_index: usize,
current_index: i64,
},
TupleClause {
clause_var_name: String,
needs_constr_var: bool,
is_complex_clause: bool,
original_subject_name: String,
defined_tuple_indices: HashSet<(usize, String)>,
defined_tuple_indices: IndexSet<(usize, String)>,
},
}
@ -87,7 +89,7 @@ impl ClauseProperties {
needs_constr_var: false,
is_complex_clause: false,
original_subject_name: subject_name,
current_index: 0,
current_index: -1,
}
} else if t.is_tuple() {
ClauseProperties::TupleClause {
@ -95,7 +97,7 @@ impl ClauseProperties {
needs_constr_var: false,
is_complex_clause: false,
original_subject_name: subject_name,
defined_tuple_indices: HashSet::new(),
defined_tuple_indices: IndexSet::new(),
}
} else {
ClauseProperties::ConstrClause {
@ -482,82 +484,22 @@ pub fn list_access_to_uplc(
tail: bool,
current_index: usize,
term: Term<Name>,
tipo: &Type,
tipos: Vec<Arc<Type>>,
check_last_item: bool,
) -> Term<Name> {
let (first, names) = names.split_first().unwrap();
if let Some((first, names)) = names.split_first() {
let (current_tipo, tipos) = tipos.split_first().unwrap();
let head_list = if tipo.is_map() {
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
}),
)
} else {
convert_data_to_type(
let head_list = if current_tipo.is_map() {
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
}),
),
&tipo.clone().get_inner_types()[0],
)
};
if names.len() == 1 && tail {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: names[0].clone(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
} else if names.is_empty() {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: term.into(),
},
} else {
convert_data_to_type(
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
@ -565,40 +507,135 @@ pub fn list_access_to_uplc(
unique: 0.into(),
}),
),
&current_tipo.to_owned(),
)
.into(),
};
if names.len() == 1 && tail {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: names[0].clone(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
} else if names.is_empty() {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: if check_last_item {
delayed_choose_list(
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
term,
apply_wrap(
apply_wrap(
Term::Builtin(DefaultFunction::Trace).force_wrap(),
Term::Constant(UplcConstant::String(
"List/Tuple contains more items than it should"
.to_string(),
)),
),
Term::Delay(Term::Error.into()),
)
.force_wrap(),
)
.into()
} else {
term.into()
},
},
head_list,
)
.into(),
}
} else {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
list_access_to_uplc(
names,
id_list,
tail,
current_index + 1,
term,
tipos.to_owned(),
check_last_item,
),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
}
} else {
Term::Lambda {
parameter_name: Name {
text: format!("tail_index_{}_{}", current_index, id_list[current_index]),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: first.clone(),
unique: 0.into(),
},
body: apply_wrap(
list_access_to_uplc(names, id_list, tail, current_index + 1, term, tipo),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: format!(
"tail_index_{}_{}",
current_index, id_list[current_index]
),
unique: 0.into(),
}),
),
)
.into(),
},
head_list,
)
.into(),
}
term
}
}
@ -760,7 +797,7 @@ pub fn match_ir_for_recursion(
}
}
pub fn find_generics_to_replace(tipo: &mut Arc<Type>, generic_types: &HashMap<u64, Arc<Type>>) {
pub fn find_generics_to_replace(tipo: &mut Arc<Type>, generic_types: &IndexMap<u64, Arc<Type>>) {
if let Some(id) = tipo.get_generic() {
//If generic does not have a type we know of like a None in option then just use same type
*tipo = generic_types.get(&id).unwrap_or(tipo).clone();
@ -986,7 +1023,7 @@ pub fn wrap_validator_args(term: Term<Name>, arguments: Vec<TypedArg>) -> Term<N
pub fn monomorphize(
ir: Vec<Air>,
generic_types: HashMap<u64, Arc<Type>>,
generic_types: IndexMap<u64, Arc<Type>>,
full_type: &Arc<Type>,
) -> (String, Vec<Air>) {
let mut new_air = ir.clone();
@ -1053,6 +1090,7 @@ pub fn monomorphize(
tipo,
names,
tail,
check_last_item,
} => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
@ -1063,6 +1101,7 @@ pub fn monomorphize(
names,
tipo,
tail,
check_last_item,
};
needs_variant = true;
}
@ -1114,8 +1153,15 @@ pub fn monomorphize(
needs_variant = true;
}
}
// TODO check on assignment if type is needed
Air::Assignment { .. } => {}
Air::UnWrapData { scope, tipo } => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
find_generics_to_replace(&mut tipo, &generic_types);
new_air[index] = Air::UnWrapData { scope, tipo };
needs_variant = true;
}
}
Air::When {
scope,
tipo,
@ -1327,8 +1373,8 @@ pub fn monomorphize(
}
Air::FieldsExpose {
scope,
count,
indices,
check_last_item,
} => {
let mut new_indices = vec![];
for (ind, name, tipo) in indices {
@ -1343,16 +1389,18 @@ pub fn monomorphize(
}
new_air[index] = Air::FieldsExpose {
scope,
count,
indices: new_indices,
check_last_item,
};
}
Air::RecordUpdate {
scope,
highest_index,
indices,
tipo,
} => {
let mut new_indices = vec![];
let mut tipo = tipo.clone();
for (ind, tipo) in indices {
if tipo.is_generic() {
let mut tipo = tipo.clone();
@ -1363,18 +1411,51 @@ pub fn monomorphize(
new_indices.push((ind, tipo));
}
}
if tipo.is_generic() {
find_generics_to_replace(&mut tipo, &generic_types);
}
new_air[index] = Air::RecordUpdate {
scope,
highest_index,
indices: new_indices,
tipo,
};
}
Air::TupleAccessor { scope, names, tipo } => {
Air::TupleAccessor {
scope,
names,
tipo,
check_last_item,
} => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
find_generics_to_replace(&mut tipo, &generic_types);
new_air[index] = Air::TupleAccessor { scope, names, tipo };
new_air[index] = Air::TupleAccessor {
scope,
names,
tipo,
check_last_item,
};
needs_variant = true;
}
}
Air::Call { scope, count, tipo } => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
find_generics_to_replace(&mut tipo, &generic_types);
new_air[index] = Air::Call { scope, count, tipo };
needs_variant = true;
}
}
Air::If { scope, tipo } => {
if tipo.is_generic() {
let mut tipo = tipo.clone();
find_generics_to_replace(&mut tipo, &generic_types);
new_air[index] = Air::If { scope, tipo };
needs_variant = true;
}
}
@ -1393,14 +1474,14 @@ pub fn monomorphize(
(new_name, new_air)
}
pub fn handle_func_deps_ir(
dep_ir: &mut Vec<Air>,
pub fn handle_func_dependencies_ir(
dependencies_ir: &mut Vec<Air>,
funt_comp: &FuncComponents,
func_components: &IndexMap<FunctionAccessKey, FuncComponents>,
defined_functions: &mut HashMap<FunctionAccessKey, ()>,
defined_functions: &mut IndexMap<FunctionAccessKey, ()>,
func_index_map: &IndexMap<FunctionAccessKey, Vec<u64>>,
func_scope: &[u64],
to_be_defined: &mut HashMap<FunctionAccessKey, ()>,
to_be_defined: &mut IndexMap<FunctionAccessKey, ()>,
) {
let mut funt_comp = funt_comp.clone();
@ -1451,9 +1532,9 @@ pub fn handle_func_deps_ir(
temp_ir.append(&mut recursion_ir);
temp_ir.append(dep_ir);
temp_ir.append(dependencies_ir);
*dep_ir = temp_ir;
*dependencies_ir = temp_ir;
if get_common_ancestor(dep_scope, func_scope) == func_scope.to_vec() {
defined_functions.insert(dependency, ());
}
@ -1492,10 +1573,11 @@ pub fn handle_recursion_ir(
let current_call = recursion_ir[index - 1].clone();
match current_call {
Air::Call { scope, count } => {
Air::Call { scope, count, tipo } => {
recursion_ir[index - 1] = Air::Call {
scope,
count: count + 1,
tipo,
}
}
_ => unreachable!(),
@ -1504,7 +1586,7 @@ pub fn handle_recursion_ir(
}
pub fn lookup_data_type_by_tipo(
data_types: HashMap<DataTypeKey, &TypedDataType>,
data_types: IndexMap<DataTypeKey, &TypedDataType>,
tipo: &Type,
) -> Option<DataType<Arc<Type>>> {
match tipo {
@ -1539,7 +1621,7 @@ pub fn lookup_data_type_by_tipo(
pub fn check_replaceable_opaque_type(
t: &Arc<Type>,
data_types: &HashMap<DataTypeKey, &TypedDataType>,
data_types: &IndexMap<DataTypeKey, &TypedDataType>,
) -> bool {
let data_type = lookup_data_type_by_tipo(data_types.clone(), t);
@ -1551,12 +1633,12 @@ pub fn check_replaceable_opaque_type(
}
}
pub fn replace_opaque_type(t: &mut Arc<Type>, data_types: HashMap<DataTypeKey, &TypedDataType>) {
pub fn replace_opaque_type(t: &mut Arc<Type>, data_types: IndexMap<DataTypeKey, &TypedDataType>) {
if check_replaceable_opaque_type(t, &data_types) && matches!(&**t, Type::App { .. }) {
let data_type = lookup_data_type_by_tipo(data_types.clone(), t).unwrap();
let new_type_fields = data_type.typed_parameters.clone();
let mut generics_type_map: HashMap<u64, Arc<Type>> = HashMap::new();
let mut generics_type_map: IndexMap<u64, Arc<Type>> = IndexMap::new();
for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) {
let mut map = generics_type_map.into_iter().collect_vec();

View File

@ -52,8 +52,8 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
just('|').to(Token::Vbar),
just("&&").to(Token::AmperAmper),
just('#').to(Token::Hash),
just("\n\n").to(Token::EmptyLine),
just("\n").to(Token::NewLine),
choice((just("\n\n"), just("\r\n\r\n"))).to(Token::EmptyLine),
choice((just("\n"), just("\r\n"))).to(Token::NewLine),
));
let grouping = choice((

View File

@ -22,6 +22,22 @@ fn assert_definitions(code: &str, definitions: Vec<ast::UntypedDefinition>) {
)
}
#[test]
fn windows_newline() {
let code = "use aiken/list\r\n";
assert_definitions(
code,
vec![ast::UntypedDefinition::Use(Use {
location: Span::new((), 0..14),
module: vec!["aiken".to_string(), "list".to_string()],
as_name: None,
unqualified: vec![],
package: (),
})],
)
}
#[test]
fn import() {
let code = indoc! {r#"

View File

@ -173,6 +173,14 @@ impl Type {
}
}
pub fn is_data(&self) -> bool {
match self {
Self::App { module, name, .. } => "Data" == name && module.is_empty(),
Self::Var { tipo } => tipo.borrow().is_data(),
_ => false,
}
}
pub fn is_generic(&self) -> bool {
match self {
Type::App { args, .. } => {
@ -205,6 +213,7 @@ impl Type {
match self {
Self::Fn { args, .. } => Some(args.clone()),
Self::App { args, .. } => Some(args.clone()),
Self::Var { tipo } => tipo.borrow().arg_types(),
_ => None,
}
}
@ -467,6 +476,13 @@ impl TypeVar {
}
}
pub fn is_data(&self) -> bool {
match self {
Self::Link { tipo } => tipo.is_data(),
_ => false,
}
}
pub fn is_generic(&self) -> bool {
match self {
TypeVar::Generic { .. } => true,
@ -483,6 +499,13 @@ impl TypeVar {
}
}
pub fn arg_types(&self) -> Option<Vec<Arc<Type>>> {
match self {
Self::Link { tipo } => tipo.arg_types(),
_ => None,
}
}
pub fn get_inner_type(&self) -> Vec<Arc<Type>> {
match self {
Self::Link { tipo } => tipo.get_inner_types(),

View File

@ -1,7 +1,7 @@
use super::Type;
use crate::{
ast::{Annotation, BinOp, CallArg, Span, TodoKind, UntypedPattern},
expr,
expr::{self, UntypedExpr},
format::Formatter,
levenshtein,
pretty::Documentable,
@ -172,20 +172,7 @@ You can use '{discard}' and numbers to distinguish between similar names.
If you really meant to return that last expression, try to replace it with the following:
{sample}"#
, sample = Formatter::new()
.expr(expr)
.to_pretty_string(70)
.lines()
.enumerate()
.map(|(ix, line)| {
if ix == 0 {
format!("╰─▶ {}", line.yellow())
} else {
format!(" {line}").yellow().to_string()
}
})
.collect::<Vec<_>>()
.join("")
, sample = format_suggestion(expr)
))]
LastExpressionIsAssignment {
#[label("let-binding as last expression")]
@ -737,6 +724,15 @@ The best thing to do from here is to remove it."#))]
location: Span,
name: String,
},
#[error("I discovered a type cast from Data without an annotation")]
#[diagnostic(code("illegal::type_cast"))]
#[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
CastDataNoAnn {
#[label]
location: Span,
value: UntypedExpr,
},
}
impl Error {
@ -1034,14 +1030,12 @@ fn suggest_import_constructor() -> String {
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
pub enum Warning {
#[error("I found a todo left in the code.\n")]
#[diagnostic(help("You probably want to replace that one with real code... eventually."))]
#[diagnostic(code("todo"))]
Todo {
kind: TodoKind,
#[error("I found a record update using all fields; thus redundant.\n")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::all_fields"))]
AllFieldsRecordUpdate {
#[label]
location: Span,
tipo: Arc<Type>,
},
#[error(
@ -1056,9 +1050,10 @@ pub enum Warning {
location: Span,
},
#[error("I found a literal that is unused.\n")]
#[diagnostic(code("unused::literal"))]
UnusedLiteral {
#[error("I found a record update with no fields; effectively updating nothing.\n")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::no_fields"))]
NoFieldsRecordUpdate {
#[label]
location: Span,
},
@ -1070,29 +1065,25 @@ pub enum Warning {
location: Span,
},
#[error("I found a record update with no fields; effectively updating nothing.\n")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::no_fields"))]
NoFieldsRecordUpdate {
#[label]
#[error("I found a when expression with a single clause.")]
#[diagnostic(
code("single_when_clause"),
help("Prefer using a {} binding like so...\n\n{}", "let".purple(), format_suggestion(sample))
)]
SingleWhenClause {
#[label("use let")]
location: Span,
sample: UntypedExpr,
},
#[error("I found a record update using all fields; thus redundant.\n")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::all_fields"))]
AllFieldsRecordUpdate {
#[error("I found a todo left in the code.\n")]
#[diagnostic(help("You probably want to replace that one with real code... eventually."))]
#[diagnostic(code("todo"))]
Todo {
kind: TodoKind,
#[label]
location: Span,
},
#[error("I discovered an unused type: '{}'.\n", name.purple())]
#[diagnostic(code("unused::type"))]
UnusedType {
#[label]
location: Span,
imported: bool,
name: String,
tipo: Arc<Type>,
},
#[error("I discovered an unused constructor: '{}'.\n", name.purple())]
@ -1107,6 +1098,17 @@ pub enum Warning {
name: String,
},
#[error("I discovered an unused imported module: '{}'.\n", name.purple())]
#[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning."
))]
#[diagnostic(code("unused::import::module"))]
UnusedImportedModule {
#[label]
location: Span,
name: String,
},
#[error("I discovered an unused imported value: '{}'.\n", name.purple())]
#[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning."
@ -1118,12 +1120,21 @@ pub enum Warning {
name: String,
},
#[error("I discovered an unused imported module: '{}'.\n", name.purple())]
#[error("I found a literal that is unused.\n")]
#[diagnostic(code("unused::literal"))]
UnusedLiteral {
#[label]
location: Span,
},
#[error("I found an unused private function: '{}'.\n", name.purple())]
#[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning."
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\
Otherwise, you might want to get rid of it altogether."
, keyword_pub = "pub".bright_blue()
))]
#[diagnostic(code("unused::import::module"))]
UnusedImportedModule {
#[diagnostic(code("unused::function"))]
UnusedPrivateFunction {
#[label]
location: Span,
name: String,
@ -1142,16 +1153,12 @@ pub enum Warning {
name: String,
},
#[error("I found an unused private function: '{}'.\n", name.purple())]
#[diagnostic(help(
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\
Otherwise, you might want to get rid of it altogether."
, keyword_pub = "pub".bright_blue()
))]
#[diagnostic(code("unused::function"))]
UnusedPrivateFunction {
#[error("I discovered an unused type: '{}'.\n", name.purple())]
#[diagnostic(code("unused::type"))]
UnusedType {
#[label]
location: Span,
imported: bool,
name: String,
},
@ -1187,3 +1194,20 @@ pub enum UnknownRecordFieldSituation {
/// This unknown record field is being called as a function. i.e. `record.field()`
FunctionCall,
}
fn format_suggestion(sample: &UntypedExpr) -> String {
Formatter::new()
.expr(sample)
.to_pretty_string(70)
.lines()
.enumerate()
.map(|(ix, line)| {
if ix == 0 {
format!("╰─▶ {}", line.yellow())
} else {
format!(" {line}").yellow().to_string()
}
})
.collect::<Vec<_>>()
.join("")
}

View File

@ -807,8 +807,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
annotation: &Option<Annotation>,
location: Span,
) -> Result<TypedExpr, Error> {
let value = self.in_new_scope(|value_typer| value_typer.infer(value))?;
let mut value_typ = value.tipo();
let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?;
let mut value_typ = typed_value.tipo();
// Check that any type annotation is accurate.
let pattern = if let Some(ann) = annotation {
@ -819,7 +819,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
self.unify(
ann_typ.clone(),
value_typ.clone(),
value.type_defining_location(),
typed_value.type_defining_location(),
)?;
value_typ = ann_typ.clone();
@ -831,6 +831,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Some(ann_typ),
)?
} else {
if value_typ.is_data() {
return Err(Error::CastDataNoAnn {
location,
value: UntypedExpr::Assignment {
location,
value: value.into(),
pattern,
kind,
annotation: Some(Annotation::Constructor {
location: Span::empty(),
module: None,
name: "Type".to_string(),
arguments: vec![],
}),
},
});
}
// Ensure the pattern matches the type of the value
PatternTyper::new(self.environment, &self.hydrator).unify(
pattern,
@ -860,7 +878,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
tipo: value_typ,
kind,
pattern,
value: Box::new(value),
value: Box::new(typed_value),
})
}
@ -1906,6 +1924,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
clauses: Vec<UntypedClause>,
location: Span,
) -> Result<TypedExpr, Error> {
// if there is only one clause we want to present a warning
// that suggests that a `let` binding should be used instead.
if clauses.len() == 1 {
self.environment.warnings.push(Warning::SingleWhenClause {
location: clauses[0].pattern[0].location(),
sample: UntypedExpr::Assignment {
location: Span::empty(),
value: Box::new(subjects[0].clone()),
pattern: clauses[0].pattern[0].clone(),
kind: AssignmentKind::Let,
annotation: None,
},
});
}
let subjects_count = subjects.len();
let mut typed_subjects = Vec::with_capacity(subjects_count);

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@ fslock = "0.2.1"
futures = "0.3.25"
hex = "0.4.3"
ignore = "0.4.18"
indexmap = "1.9.1"
itertools = "0.10.1"
miette = { version = "5.3.0", features = ["fancy"] }
owo-colors = "3.5.0"

View File

@ -20,6 +20,7 @@ use aiken_lang::{
IdGenerator, MINT, PUBLISH, SPEND, VALIDATOR_NAMES, WITHDRAW,
};
use deps::UseManifest;
use indexmap::IndexMap;
use miette::NamedSource;
use options::{CodeGenMode, Options};
use package_name::PackageName;
@ -482,9 +483,9 @@ where
validators: Vec<(PathBuf, String, TypedFunction)>,
) -> Result<Vec<Script>, Error> {
let mut programs = Vec::new();
let mut functions = HashMap::new();
let mut type_aliases = HashMap::new();
let mut data_types = HashMap::new();
let mut functions = IndexMap::new();
let mut type_aliases = IndexMap::new();
let mut data_types = IndexMap::new();
let prelude_functions = builtins::prelude_functions(&self.id_gen);
for (access_key, func) in prelude_functions.iter() {
@ -539,11 +540,15 @@ where
..
} = func_def;
let mut modules_map = IndexMap::new();
modules_map.extend(self.module_types.clone());
let mut generator = CodeGenerator::new(
&functions,
// &type_aliases,
&data_types,
&self.module_types,
&modules_map,
);
self.event_listener.handle_event(Event::GeneratingUPLC {
@ -573,9 +578,9 @@ where
should_collect: fn(&TypedDefinition) -> bool,
) -> Result<Vec<Script>, Error> {
let mut programs = Vec::new();
let mut functions = HashMap::new();
let mut type_aliases = HashMap::new();
let mut data_types = HashMap::new();
let mut functions = IndexMap::new();
let mut type_aliases = IndexMap::new();
let mut data_types = IndexMap::new();
let prelude_functions = builtins::prelude_functions(&self.id_gen);
for (access_key, func) in prelude_functions.iter() {
@ -648,21 +653,25 @@ where
})
}
let mut modules_map = IndexMap::new();
modules_map.extend(self.module_types.clone());
let mut generator = CodeGenerator::new(
&functions,
// &type_aliases,
&data_types,
&self.module_types,
&modules_map,
);
let evaluation_hint = if let Some((bin_op, left_src, right_src)) = func_def.test_hint()
{
let left = CodeGenerator::new(&functions, &data_types, &self.module_types)
let left = CodeGenerator::new(&functions, &data_types, &modules_map)
.generate(*left_src, vec![], false)
.try_into()
.unwrap();
let right = CodeGenerator::new(&functions, &data_types, &self.module_types)
let right = CodeGenerator::new(&functions, &data_types, &modules_map)
.generate(*right_src, vec![], false)
.try_into()
.unwrap();

View File

@ -5,6 +5,7 @@ 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 const ASSERT_ON_LIST: &str = "__assert_on_list";
pub fn apply_wrap(function: Term<Name>, arg: Term<Name>) -> Term<Name> {
Term::Apply {
@ -31,6 +32,108 @@ pub fn final_wrapper(term: Term<Name>) -> Term<Name> {
)
}
pub fn assert_on_list(term: Term<Name>) -> Term<Name> {
apply_wrap(
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
),
)
.into(),
},
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: Term::Lambda {
parameter_name: Name {
text: "list_to_check".to_string(),
unique: 0.into(),
},
body: Term::Lambda {
parameter_name: Name {
text: "check_with".to_string(),
unique: 0.into(),
},
body: delayed_choose_list(
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
Term::Constant(Constant::Unit),
apply_wrap(
apply_wrap(
Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(),
apply_wrap(
Term::Var(Name {
text: "check_with".to_string(),
unique: 0.into(),
}),
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
),
),
),
apply_wrap(
apply_wrap(
apply_wrap(
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
),
),
Term::Var(Name {
text: "check_with".to_string(),
unique: 0.into(),
}),
),
),
)
.into(),
}
.into(),
}
.into(),
},
)
}
pub fn constr_fields_exposer(term: Term<Name>) -> Term<Name> {
Term::Apply {
function: Term::Lambda {

View File

@ -343,7 +343,7 @@ impl Machine {
Err(Error::UnexpectedBuiltinTermArgument(t.as_ref().clone()))
}
}
rest => Err(Error::NonFunctionalApplication(rest)),
rest => Err(Error::NonFunctionalApplication(rest, argument)),
}
}

View File

@ -16,8 +16,8 @@ pub enum Error {
EvaluationFailure,
#[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")]
NonPolymorphicInstantiation(Value),
#[error("Attempted to apply a non-function:\n\n{0:#?}")]
NonFunctionalApplication(Value),
#[error("Attempted to apply a non-function:\n\n{0:#?} to argument:\n\n{1:#?}")]
NonFunctionalApplication(Value, Value),
#[error("Type mismatch expected '{0}' got '{1}'")]
TypeMismatch(Type, Type),
#[error("Type mismatch expected '(list a)' got '{0}'")]

View File

@ -1 +1 @@
addr1w9zyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90s83wahe
addr1w8ehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssvegjnw

View File

@ -1,5 +1,5 @@
{
"type": "PlutusScriptV2",
"description": "Generated by Aiken",
"cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1"
"cborHex": "59015159014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1"
}

View File

@ -1 +1 @@
59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300c00130050013300500100237566601c601e00490011192999aab9f00114a2294000488c8c8cc0140052f5bded8c06600a002004004446464a666ae68cdc3800a40042006264640026eacd5d080098070011aab9d37540020044466006004002600200244464a666aae7c0044cdd2a400497ae01323232325333573466e3c0180044cdd2a400066ae80dd300125eb804ccc02002000c018dd71aab9d00337566aae78008d5d10011aba100100223335734002941289800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1
59014e01000022253335734646464646466002006464646464646464646600201291010500000000000022323232300600130060013300600100237566601c601e00490011199ab9a0014a09448c94ccd55cf8008a5114a00024464646600a00297adef6c60330050010020022232325333573466e1c005200210031323200137566ae84004c034008d55ce9baa001002223300300200130010012223253335573e002266e9520024bd700991919192999ab9a3371e00c002266e9520003357406e980092f5c0266601001000600c6eb8d55ce8019bab35573c0046ae88008d5d08008011800800911192999aab9f00114a026464a666ae68c01000852889998030030008021aba2002357420020046eb0cc004c008cc004c00800d20004801088c8ccc00400520000032223333573466e1c0100095d0919980200219b8000348008d5d100080091aab9e37540022930b1

View File

@ -1 +1 @@
addr_test1wpzyujfu4a23cltkm7xe2usj7wedtvqnsk9ecg2zwt6a90sue6pcu
addr_test1wrehrka3eyh8hqxt647qm56z0ju3x8fsyjv2f3cwp5kr5ssh3uwut

View File

@ -1,3 +1,9 @@
pub type Door{
angle: Int,
locked: Bool
}
pub type Car {
Honda { remote_connect: ByteArray, owner: ByteArray, wheels: Int }
Ford {
@ -5,18 +11,19 @@ pub type Car {
owner: ByteArray,
wheels: Int,
truck_bed_limit: Int,
car_doors: List<Door>
}
}
// test update_owner2_should_fail(){
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000}
// assert Honda{ owner, ..} = initial_car
// let initial_car: Data = Ford{remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: []}
// assert Honda{ owner, ..}: Car = initial_car
// owner == #[]
// }
test update_owner1() {
let initial_car: Data =
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000 }
assert Ford { owner, .. } = initial_car
Ford { remote_connect: #[], owner: #[], wheels: 4, truck_bed_limit: 10000, car_doors: [] }
assert Ford { owner, .. }: Car = initial_car
owner == #[]
}

View File

@ -0,0 +1,5 @@
# This file was generated by Aiken
# You typically do not need to edit this file
requirements = []
packages = []

View File

@ -0,0 +1,3 @@
name = 'aiken-lang/acceptance_test_046'
version = '0.0.0'
description = ''

View File

@ -0,0 +1,9 @@
test sort_by_1() {
let xs = [[4, 3, 2, 1], [2, 3, 4, 5]]
when xs is {
[[], ys] -> False
[xs, []] -> False
[[x, ..xs2], [y, ..ys2]] -> True
_ -> False
}
}

View File

@ -0,0 +1,9 @@
fn when_tuple(a: (Int, Int)) -> Int {
when a is {
(a, b) -> a
}
}
pub fn spend(a, b, c) -> Bool {
when_tuple((4, 1)) == 4
}